UploadedFile, FileTypeEnum, FileSourceEnum importable from infrastructure.postgres.uploaded_file_table 🟩

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Daniel Roth 2026-06-09 11:50:51 +00:00
parent 41b282042f
commit 5178cd02c5
14 changed files with 146 additions and 81 deletions

View file

@ -3,7 +3,7 @@ from typing import Optional
from sqlalchemy import select
from backend.app.db.connection import db_read_session
from backend.app.db.models.uploaded_file import (
from infrastructure.postgres.uploaded_file_table import (
FileSourceEnum,
FileTypeEnum,
UploadedFile,

View file

@ -1,69 +0,0 @@
import enum
from sqlalchemy import TIMESTAMP, BigInteger, Column, Text, Enum as SqlEnum
from backend.app.db.base import Base
class FileTypeEnum(enum.Enum):
PHOTO_PACK = "photo_pack"
SITE_NOTE = "site_note"
RD_SAP_SITE_NOTE = "rd_sap_site_note"
PAS_2023_VENTILATION = "pas_2023_ventilation"
PAS_2023_CONDITION = "pas_2023_condition"
PAS_SIGNIFICANCE = "pas_significance"
PAR_PHOTO_PACK = "par_photo_pack"
PAS_2023_PROPERTY = "pas_2023_property"
PAS_2023_OCCUPANCY = "pas_2023_occupancy"
ECMK_SITE_NOTE = "ecmk_site_note"
ECMK_RD_SAP_SITE_NOTE = "ecmk_rd_sap_site_note"
ECMK_SURVEY_XML = "ecmk_survey_xml"
MAGIC_PLAN_JSON = "magic_plan_json"
IMPROVEMENT_OPTION_EVALUATION = "improvement_option_evaluation"
MEDIUM_TERM_IMPROVEMENT_PLAN = "medium_term_improvement_plan"
RETROFIT_DESIGN_DOC = "retrofit_design_doc"
MCS_COMPLIANCE_CERTIFICATE = "mcs_compliance_certificate"
OTHER = "other"
class FileSourceEnum(enum.Enum):
PAS_HUB = "pas hub"
COORDINATION_HUB = "coordination_hub"
SHAREPOINT = "sharepoint"
HUBSPOT = "hubspot"
ECMK = "ecmk"
MAGIC_PLAN = "magic_plan"
class UploadedFile(Base):
__tablename__ = "uploaded_files"
id = Column(BigInteger, primary_key=True, autoincrement=True)
s3_file_bucket = Column(Text, nullable=False)
s3_file_key = Column(Text, nullable=False)
s3_upload_timestamp = Column(TIMESTAMP(timezone=True), nullable=False)
landlord_property_id = Column(Text, nullable=True)
uprn = Column(BigInteger, nullable=True)
hubspot_listing_id = Column(BigInteger, nullable=True)
hubspot_deal_id = Column(Text, nullable=True)
file_type = Column(
SqlEnum(
FileTypeEnum,
name="file_type",
create_type=False,
values_callable=lambda enum_cls: [e.value for e in enum_cls],
),
nullable=True,
)
file_source = Column(
SqlEnum(
FileSourceEnum,
name="file_source",
create_type=False,
values_callable=lambda enum_cls: [e.value for e in enum_cls],
),
nullable=True,
)

View file

@ -7,7 +7,7 @@ from backend.app.db.connection import db_session
from backend.app.db.functions.uploaded_files_functions import (
get_uploaded_file_by_listing_type_and_source,
)
from backend.app.db.models.uploaded_file import FileSourceEnum, FileTypeEnum
from infrastructure.postgres.uploaded_file_table import FileSourceEnum, FileTypeEnum
from backend.documents_parser.db_writer import save_epc_property_data
from backend.documents_parser.parser import parse_site_notes_pdf
from backend.ecmk_fetcher.address_list import (

View file

@ -1,6 +1,6 @@
from enum import Enum
from backend.app.db.models.uploaded_file import FileTypeEnum
from infrastructure.postgres.uploaded_file_table import FileTypeEnum
class FileDownloadButtonType(Enum):

View file

@ -1,7 +1,7 @@
from typing import Dict
from unittest.mock import MagicMock, call, patch
from backend.app.db.models.uploaded_file import FileTypeEnum
from infrastructure.postgres.uploaded_file_table import FileTypeEnum
from backend.ecmk_fetcher.address_list import PropertyRow
from backend.ecmk_fetcher.ecmk_service import EcmkService
from backend.ecmk_fetcher.reports import FileDownloadButtonType

View file

@ -3,7 +3,7 @@ from unittest.mock import MagicMock, call, patch
import pytest
from backend.app.db.models.uploaded_file import FileTypeEnum
from infrastructure.postgres.uploaded_file_table import FileTypeEnum
from backend.ecmk_fetcher.upload import upload_file_to_s3_and_record

View file

@ -3,7 +3,7 @@ import os
from typing import cast
from backend.app.db.connection import db_session
from backend.app.db.models.uploaded_file import (
from infrastructure.postgres.uploaded_file_table import (
FileSourceEnum,
FileTypeEnum,
UploadedFile,

View file

@ -1,7 +1,7 @@
from enum import Enum
from typing import Optional
from backend.app.db.models.uploaded_file import FileTypeEnum
from infrastructure.postgres.uploaded_file_table import FileTypeEnum
class CoreFiles(Enum):

View file

@ -3,7 +3,7 @@ from datetime import datetime, timezone
from typing import Callable, List, NamedTuple, Optional, cast
from backend.app.db.connection import db_session
from backend.app.db.models.uploaded_file import (
from infrastructure.postgres.uploaded_file_table import (
FileSourceEnum,
FileTypeEnum,
UploadedFile,

View file

@ -4,7 +4,7 @@ from typing import Any, Callable, Optional
from unittest.mock import MagicMock, call, patch
from backend.app.db.models.uploaded_file import FileSourceEnum, FileTypeEnum
from infrastructure.postgres.uploaded_file_table import FileSourceEnum, FileTypeEnum
from backend.pashub_fetcher.pashub_client import (
DownloadedFile,
DownloadedFiles,

View file

@ -0,0 +1,96 @@
from __future__ import annotations
import enum
from typing import ClassVar, Optional
from sqlalchemy import TIMESTAMP, BigInteger, Column, Text
from sqlalchemy import Enum as SqlEnum
from sqlmodel import Field, SQLModel
class FileTypeEnum(enum.Enum):
PHOTO_PACK = "photo_pack"
SITE_NOTE = "site_note"
RD_SAP_SITE_NOTE = "rd_sap_site_note"
PAS_2023_VENTILATION = "pas_2023_ventilation"
PAS_2023_CONDITION = "pas_2023_condition"
PAS_SIGNIFICANCE = "pas_significance"
PAR_PHOTO_PACK = "par_photo_pack"
PAS_2023_PROPERTY = "pas_2023_property"
PAS_2023_OCCUPANCY = "pas_2023_occupancy"
ECMK_SITE_NOTE = "ecmk_site_note"
ECMK_RD_SAP_SITE_NOTE = "ecmk_rd_sap_site_note"
ECMK_SURVEY_XML = "ecmk_survey_xml"
MAGIC_PLAN_JSON = "magic_plan_json"
IMPROVEMENT_OPTION_EVALUATION = "improvement_option_evaluation"
MEDIUM_TERM_IMPROVEMENT_PLAN = "medium_term_improvement_plan"
RETROFIT_DESIGN_DOC = "retrofit_design_doc"
MCS_COMPLIANCE_CERTIFICATE = "mcs_compliance_certificate"
OTHER = "other"
VENTILATION_AUDIT = "ventilation_audit"
class FileSourceEnum(enum.Enum):
PAS_HUB = "pas hub"
COORDINATION_HUB = "coordination_hub"
SHAREPOINT = "sharepoint"
HUBSPOT = "hubspot"
ECMK = "ecmk"
MAGIC_PLAN = "magic_plan"
AUDIT_GENERATOR = "audit_generator"
def _enum_values(enum_cls: type[enum.Enum]) -> list[str]:
return [e.value for e in enum_cls]
class UploadedFile(SQLModel, table=True):
__tablename__: ClassVar[str] = "uploaded_files" # pyright: ignore[reportIncompatibleVariableOverride]
id: Optional[int] = Field(
default=None,
sa_column=Column(BigInteger, primary_key=True, autoincrement=True),
)
s3_file_bucket: str = Field(sa_column=Column(Text, nullable=False))
s3_file_key: str = Field(sa_column=Column(Text, nullable=False))
s3_upload_timestamp: object = Field(
sa_column=Column(TIMESTAMP(timezone=True), nullable=False)
)
landlord_property_id: Optional[str] = Field(
default=None, sa_column=Column(Text, nullable=True)
)
uprn: Optional[int] = Field(
default=None, sa_column=Column(BigInteger, nullable=True)
)
hubspot_listing_id: Optional[int] = Field(
default=None, sa_column=Column(BigInteger, nullable=True)
)
hubspot_deal_id: Optional[str] = Field(
default=None, sa_column=Column(Text, nullable=True)
)
file_type: Optional[str] = Field(
default=None,
sa_column=Column(
SqlEnum(
FileTypeEnum,
name="file_type",
create_type=False,
values_callable=_enum_values,
),
nullable=True,
),
)
file_source: Optional[str] = Field(
default=None,
sa_column=Column(
SqlEnum(
FileSourceEnum,
name="file_source",
create_type=False,
values_callable=_enum_values,
),
nullable=True,
),
)

View file

@ -8,7 +8,7 @@ from domain.magicplan.api.response import MagicPlanPlan, PlanSummary
from domain.magicplan.mapper import map_plan
from domain.magicplan.models import Plan
from backend.app.db.models.uploaded_file import (
from infrastructure.postgres.uploaded_file_table import (
FileSourceEnum,
FileTypeEnum,
UploadedFile,

View file

@ -17,12 +17,13 @@ from typing import Any
import pytest
from psycopg import Connection
from pytest_postgresql import factories
from sqlalchemy import Engine
from sqlalchemy import Engine, text
from sqlmodel import SQLModel, create_engine
# Importing the SQLModel row modules registers their tables on
# SQLModel.metadata so ``create_all`` builds the full schema. Imports look
# unused; they aren't.
import infrastructure.postgres.uploaded_file_table as _uf_table # pyright: ignore[reportUnusedImport]
# pg_ctl ships under a versioned path and is not on PATH in the dev container.
@ -34,12 +35,49 @@ postgresql_proc = factories.postgresql_proc(
postgresql = factories.postgresql("postgresql_proc")
def _create_pg_enum_types(engine: Engine) -> None:
"""Emit CREATE TYPE for PostgreSQL enum types used by UploadedFile.
SQLModel.metadata.create_all uses create_type=False for these enums
(they are normally created by Alembic migrations). Tests need them upfront.
A DO block swallows duplicate_object so the fixture is safe to call on a
pre-seeded database.
"""
from infrastructure.postgres.uploaded_file_table import FileSourceEnum, FileTypeEnum
ft_values = ", ".join(f"'{e.value}'" for e in FileTypeEnum)
fs_values = ", ".join(f"'{e.value}'" for e in FileSourceEnum)
with engine.connect() as conn:
conn.execute(
text(
f"""
DO $$ BEGIN
CREATE TYPE file_type AS ENUM ({ft_values});
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
"""
)
)
conn.execute(
text(
f"""
DO $$ BEGIN
CREATE TYPE file_source AS ENUM ({fs_values});
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
"""
)
)
conn.commit()
@pytest.fixture
def db_engine(postgresql: Connection[Any]) -> Iterator[Engine]:
"""A SQLModel engine bound to a fresh, ephemeral PostgreSQL database."""
info = postgresql.info
url = f"postgresql+psycopg://{info.user}:@{info.host}:{info.port}/{info.dbname}"
engine = create_engine(url)
_create_pg_enum_types(engine)
SQLModel.metadata.create_all(engine)
try:
yield engine

View file

@ -9,7 +9,7 @@ from domain.magicplan.api.response import MagicPlanPlan, PlanSummary
from domain.magicplan.mapper import map_plan
from domain.magicplan.models import Plan
from backend.app.db.models.uploaded_file import (
from infrastructure.postgres.uploaded_file_table import (
FileSourceEnum,
FileTypeEnum,
UploadedFile,