from __future__ import annotations from typing import Optional, Sequence from sqlalchemy import select, text from sqlmodel import Session, col from domain.bulk_document_download.property_document import PropertyDocument from infrastructure.postgres.uploaded_file_table import FileTypeEnum, UploadedFile class UploadedFilePostgresRepository: def __init__(self, session: Session) -> None: self._session = session def get_latest_by_hubspot_deal_id( self, hubspot_deal_id: str, file_type: FileTypeEnum ) -> Optional[UploadedFile]: stmt = ( select(UploadedFile) .where(col(UploadedFile.hubspot_deal_id) == hubspot_deal_id) .where(col(UploadedFile.file_type) == file_type.value) .order_by(col(UploadedFile.s3_upload_timestamp).desc()) .limit(1) ) return self._session.execute(stmt).scalars().one_or_none() # pyright: ignore[reportDeprecated] def by_landlord_property_ids( self, landlord_property_ids: Sequence[str] ) -> list[PropertyDocument]: """Every uploaded file held for the given properties (ADR-0060), across all Document Types, as a small domain read-model. Files are matched by **`hubspot_deal_id`**, bridged to the property through `hubspot_deal_data` — `uploaded_files.landlord_property_id` is unpopulated in practice (no upload source sets it), so the property identity comes from the deal bridge, not the file row. Coverage is therefore limited to deal-id-linked sources (pashub, magic plan, audit generator); files keyed only by `hubspot_listing_id` or `uprn` are not reached — see the ADR-0060 matching decision. Latest-per-Document-Type selection and null-type skipping are the Download Package plan's job, not the query's.""" rows = self._session.connection().execute( text( "SELECT h.landlord_property_id, u.file_type," " u.s3_file_bucket, u.s3_file_key, u.s3_upload_timestamp" " FROM uploaded_files u" " JOIN hubspot_deal_data h ON h.deal_id = u.hubspot_deal_id" " WHERE h.landlord_property_id = ANY(:lpids)" ), {"lpids": list(landlord_property_ids)}, ) return [ PropertyDocument( landlord_property_id=row[0], document_type=row[1], s3_bucket=row[2], s3_key=row[3], uploaded_at=row[4], ) for row in rows.all() ] def insert(self, uploaded_file: UploadedFile) -> None: self._session.add(uploaded_file)