mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-19 17:03:02 +00:00
EpcSource widens to "expired" (ADR-0054; the column is TEXT — no migration). "predicted"/"expired" form one slot family: _slot_sources routes every slot read and slot-clearing delete through the family, so a re-ingestion flipping the flavour replaces the row instead of stranding its sibling. FakeEpcRepo mirrors the family. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
2.8 KiB
Python
74 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import TYPE_CHECKING, Literal, Optional
|
|
|
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
|
|
|
if TYPE_CHECKING:
|
|
from repositories.epc.epc_postgres_repository import EpcSaveRequest
|
|
|
|
# Provenance of a persisted EPC picture (ADR-0031): a real "lodged" EPC, or a
|
|
# "predicted" one synthesised by EPC Prediction — "expired" is a prediction
|
|
# enhanced by an expired Historic EPC's stable attributes (ADR-0054). A
|
|
# property holds one lodged picture and one predicted-slot picture.
|
|
EpcSource = Literal["lodged", "predicted", "expired"]
|
|
|
|
# The predicted slot's source family: "predicted" and "expired" occupy the SAME
|
|
# slot (a re-ingestion may flip the flavour), so slot reads and slot-clearing
|
|
# deletes must always address the family, never one member (ADR-0054).
|
|
PREDICTED_SLOT_SOURCES: tuple[EpcSource, ...] = ("predicted", "expired")
|
|
|
|
|
|
class EpcRepository(ABC):
|
|
"""Persists and loads the structured EPC Property Data slice.
|
|
|
|
`save` writes the `EpcPropertyData` to the `epc_property` parent row and its
|
|
child tables; `get` reconstructs the persisted projection back into an
|
|
`EpcPropertyData`. Round-trip fidelity over that projection is pinned by the
|
|
Slice-1 round-trip test (Hestia-Homes/Model#1129). Each EPC carries a
|
|
`source` so a lodged and a predicted picture coexist per property (ADR-0031).
|
|
"""
|
|
|
|
@abstractmethod
|
|
def save(
|
|
self,
|
|
data: EpcPropertyData,
|
|
property_id: int | None = None,
|
|
portfolio_id: int | None = None,
|
|
source: EpcSource = "lodged",
|
|
) -> int: ...
|
|
|
|
@abstractmethod
|
|
def save_batch(self, requests: "list[EpcSaveRequest]") -> list[int]: ...
|
|
|
|
@abstractmethod
|
|
def get(self, epc_property_id: int) -> EpcPropertyData: ...
|
|
|
|
@abstractmethod
|
|
def get_for_property(self, property_id: int) -> Optional[EpcPropertyData]:
|
|
"""The property's LODGED EPC (the predicted slot is read separately)."""
|
|
...
|
|
|
|
@abstractmethod
|
|
def get_predicted_for_property(
|
|
self, property_id: int
|
|
) -> Optional[EpcPropertyData]:
|
|
"""The property's PREDICTED EPC (EPC Prediction gap-fill), or None."""
|
|
...
|
|
|
|
@abstractmethod
|
|
def get_for_properties(
|
|
self, property_ids: list[int]
|
|
) -> dict[int, EpcPropertyData]:
|
|
"""Bulk-hydrate a batch's LODGED EPCs, keyed by property_id (only those
|
|
with one are present). A handful of per-table queries, not N per property."""
|
|
...
|
|
|
|
@abstractmethod
|
|
def get_predicted_for_properties(
|
|
self, property_ids: list[int]
|
|
) -> dict[int, EpcPropertyData]:
|
|
"""Bulk-hydrate a batch's PREDICTED EPCs (ADR-0031), keyed by property_id
|
|
(only those with one are present)."""
|
|
...
|