mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
feat(repositories): expose the spatial cache repo on the Unit of Work
Slice 3c.3. Ingestion writes the OS spatial reference cache through the same unit it persists the EPC/solar enrichments with, so `UnitOfWork` declares a `spatial` repo, `PostgresUnitOfWork` binds a `SpatialPostgresRepository` to the session, and `FakeUnitOfWork` gains a `FakeSpatialRepo` (seedable for read tests, recording writes for ingestion-side assertions). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a1c60d2fba
commit
234c4ae947
4 changed files with 40 additions and 0 deletions
|
|
@ -21,6 +21,7 @@ from repositories.scenario.scenario_postgres_repository import (
|
|||
ScenarioPostgresRepository,
|
||||
)
|
||||
from repositories.solar.solar_postgres_repository import SolarPostgresRepository
|
||||
from repositories.spatial.spatial_postgres_repository import SpatialPostgresRepository
|
||||
from repositories.unit_of_work import UnitOfWork
|
||||
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ class PostgresUnitOfWork(UnitOfWork):
|
|||
self.property = PropertyPostgresRepository(self._session, epc_repo)
|
||||
self.epc = epc_repo
|
||||
self.solar = SolarPostgresRepository(self._session)
|
||||
self.spatial = SpatialPostgresRepository(self._session)
|
||||
self.property_baseline = PropertyBaselinePostgresRepository(self._session)
|
||||
self.scenario = ScenarioPostgresRepository(self._session)
|
||||
self.product = ProductPostgresRepository(self._session)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from repositories.product.product_repository import ProductRepository
|
|||
from repositories.property.property_repository import PropertyRepository
|
||||
from repositories.scenario.scenario_repository import ScenarioRepository
|
||||
from repositories.solar.solar_repository import SolarRepository
|
||||
from repositories.spatial.spatial_repository import SpatialRepository
|
||||
|
||||
|
||||
class UnitOfWork(ABC):
|
||||
|
|
@ -28,6 +29,9 @@ class UnitOfWork(ABC):
|
|||
property: PropertyRepository
|
||||
epc: EpcRepository
|
||||
solar: SolarRepository
|
||||
# Per-UPRN cache of the OS spatial reference data, written in Ingestion
|
||||
# alongside the EPC/solar enrichments (ADR-0020).
|
||||
spatial: SpatialRepository
|
||||
property_baseline: PropertyBaselineRepository
|
||||
# Modelling-stage repos (ADR-0017): read the Scenario, read the Product
|
||||
# catalogue, write the Plan + its Plan Measures — all on the one session.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ from types import TracebackType
|
|||
from typing import Any, Optional
|
||||
|
||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||
from domain.geospatial.planning_restrictions import PlanningRestrictions
|
||||
from domain.geospatial.spatial_reference import SpatialReference
|
||||
from domain.modelling.plan import Plan
|
||||
from domain.modelling.scenario import Scenario
|
||||
from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance
|
||||
|
|
@ -22,6 +24,7 @@ from repositories.epc.epc_repository import EpcRepository
|
|||
from repositories.property.property_repository import PropertyRepository
|
||||
from repositories.scenario.scenario_repository import ScenarioRepository
|
||||
from repositories.solar.solar_repository import SolarRepository
|
||||
from repositories.spatial.spatial_repository import SpatialRepository
|
||||
from repositories.unit_of_work import UnitOfWork
|
||||
from domain.modelling.product import Product
|
||||
|
||||
|
|
@ -101,6 +104,24 @@ class FakeSolarRepo(SolarRepository):
|
|||
raise NotImplementedError
|
||||
|
||||
|
||||
class FakeSpatialRepo(SpatialRepository):
|
||||
"""In-memory per-UPRN spatial cache. Seed `by_uprn` to hydrate Properties in
|
||||
a read test; `saved` records writes for an Ingestion-side assertion."""
|
||||
|
||||
def __init__(
|
||||
self, by_uprn: Optional[dict[int, PlanningRestrictions]] = None
|
||||
) -> None:
|
||||
self._by_uprn: dict[int, PlanningRestrictions] = dict(by_uprn or {})
|
||||
self.saved: list[tuple[int, SpatialReference]] = []
|
||||
|
||||
def save(self, uprn: int, reference: SpatialReference) -> None:
|
||||
self.saved.append((uprn, reference))
|
||||
self._by_uprn[uprn] = reference.restrictions
|
||||
|
||||
def get_for_uprns(self, uprns: list[int]) -> dict[int, PlanningRestrictions]:
|
||||
return {uprn: self._by_uprn[uprn] for uprn in uprns if uprn in self._by_uprn}
|
||||
|
||||
|
||||
class FakePropertyBaselineRepo(PropertyBaselineRepository):
|
||||
def __init__(self) -> None:
|
||||
self.saved: list[tuple[PropertyBaselinePerformance, int]] = []
|
||||
|
|
@ -170,6 +191,7 @@ class FakeUnitOfWork(UnitOfWork):
|
|||
property: FakePropertyRepo,
|
||||
epc: Optional[FakeEpcRepo] = None,
|
||||
solar: Optional[FakeSolarRepo] = None,
|
||||
spatial: Optional[FakeSpatialRepo] = None,
|
||||
property_baseline: Optional[FakePropertyBaselineRepo] = None,
|
||||
scenario: Optional[FakeScenarioRepository] = None,
|
||||
product: Optional[ProductRepository] = None,
|
||||
|
|
@ -178,6 +200,7 @@ class FakeUnitOfWork(UnitOfWork):
|
|||
self.property = property
|
||||
self.epc = epc or FakeEpcRepo()
|
||||
self.solar = solar or FakeSolarRepo()
|
||||
self.spatial = spatial or FakeSpatialRepo()
|
||||
self.property_baseline = property_baseline or FakePropertyBaselineRepo()
|
||||
self.scenario = scenario or FakeScenarioRepository()
|
||||
self.product = product or _UnsetProductRepo()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from repositories.plan.plan_repository import PlanRepository
|
|||
from repositories.postgres_unit_of_work import PostgresUnitOfWork
|
||||
from repositories.product.product_repository import ProductRepository
|
||||
from repositories.scenario.scenario_repository import ScenarioRepository
|
||||
from repositories.spatial.spatial_repository import SpatialRepository
|
||||
|
||||
|
||||
def _session_factory(db_engine: Engine) -> Callable[[], Session]:
|
||||
|
|
@ -75,6 +76,16 @@ def test_unit_exposes_the_modelling_repos_bound_to_its_session(
|
|||
assert isinstance(uow.plan, PlanRepository)
|
||||
|
||||
|
||||
def test_unit_exposes_the_spatial_cache_repo_bound_to_its_session(
|
||||
db_engine: Engine,
|
||||
) -> None:
|
||||
# Arrange / Act
|
||||
with PostgresUnitOfWork(_session_factory(db_engine)) as uow:
|
||||
# Assert — Ingestion writes the OS spatial reference cache through the
|
||||
# same unit it persists the EPC/solar with (ADR-0020).
|
||||
assert isinstance(uow.spatial, SpatialRepository)
|
||||
|
||||
|
||||
def test_leaving_the_block_without_commit_persists_nothing(db_engine: Engine) -> None:
|
||||
# Arrange
|
||||
new_unit = lambda: PostgresUnitOfWork(_session_factory(db_engine))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue