Model/domain/bulk_document_download/package_plan.py
Khalim Conn-Kowlessar bd928828e3 Skip and report a document with no Document Type 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:06:26 +00:00

92 lines
3 KiB
Python

"""The Download Package plan (ADR-0060).
Pure logic: given the documents resolved for a set of properties, decide what
goes where in the ZIP — one folder per property, the latest file of each
Document Type — and which rows are skipped and reported. No I/O: the
orchestrator does the S3/DB work; this module only lays out the archive.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, Sequence
@dataclass(frozen=True)
class ResolvedDocument:
"""One `uploaded_files` row resolved for packaging: its property identity
(matched by `landlord_property_id`, ADR-0060), the display address enriched
from the hubspot deals data, its Document Type (`file_type`, nullable), the
S3 location to read it from, and when it was uploaded (for latest-per-type).
"""
landlord_property_id: str
address: str
document_type: Optional[str]
s3_bucket: str
s3_key: str
uploaded_at: datetime
@dataclass(frozen=True)
class PackageEntry:
"""One file to place in the Download Package: where it lands in the ZIP and
the S3 object to stream in from."""
zip_path: str
s3_bucket: str
s3_key: str
@dataclass(frozen=True)
class SkippedDocument:
"""A resolved row left out of the package, with the reason (surfaced in the
run's `sub_task.outputs`, ADR-0060)."""
landlord_property_id: str
s3_key: str
reason: str
@dataclass(frozen=True)
class DownloadPackagePlan:
"""The laid-out archive: the files to include and the rows skipped."""
entries: tuple[PackageEntry, ...]
skipped: tuple[SkippedDocument, ...]
def plan_download_package(
documents: Sequence[ResolvedDocument],
) -> DownloadPackagePlan:
"""Lay out a Download Package from resolved documents (ADR-0060): one folder
per property, the latest file of each Document Type, null-Document-Type rows
skipped and reported."""
latest_by_group: dict[tuple[str, str], ResolvedDocument] = {}
skipped: list[SkippedDocument] = []
for document in documents:
if document.document_type is None:
skipped.append(
SkippedDocument(
landlord_property_id=document.landlord_property_id,
s3_key=document.s3_key,
reason="null_document_type",
)
)
continue
group = (document.landlord_property_id, document.document_type)
incumbent = latest_by_group.get(group)
if incumbent is None or document.uploaded_at > incumbent.uploaded_at:
latest_by_group[group] = document
entries: list[PackageEntry] = [
PackageEntry(
zip_path=f"{document.address} ({document.landlord_property_id})/"
f"{document.document_type}",
s3_bucket=document.s3_bucket,
s3_key=document.s3_key,
)
for document in latest_by_group.values()
]
return DownloadPackagePlan(entries=tuple(entries), skipped=tuple(skipped))