mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
The new pipeline left no per-Property record of a run (the old engine set property.has_recommendations and populated property_details_epc). Restore the marker: PropertyRepository.mark_modelled sets has_recommendations (true when the Plan carries measures, mirroring the old engine) and bumps updated_at, so a first-run under the new process is identifiable as updated_at >= 2026-06-01. ModellingOrchestrator marks each Property after its Scenarios (true if any Scenario yielded a measure); run_modelling_e2e's --persist path marks it too (its compute runs on in-memory fakes, so the DB UoW sets it directly). Adds the has_recommendations/updated_at columns to the PropertyRow mirror. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
142 lines
4.9 KiB
Python
142 lines
4.9 KiB
Python
"""PropertyRepository hydrates the aggregate whole from the property row + EPC slice."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from sqlalchemy import Engine
|
|
from sqlmodel import Session
|
|
|
|
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
|
|
from domain.geospatial.planning_restrictions import PlanningRestrictions
|
|
from domain.geospatial.spatial_reference import SpatialReference
|
|
from infrastructure.postgres.property_table import PropertyRow
|
|
from repositories.epc.epc_postgres_repository import EpcPostgresRepository
|
|
from repositories.property.property_postgres_repository import (
|
|
PropertyPostgresRepository,
|
|
)
|
|
from repositories.spatial.spatial_postgres_repository import SpatialPostgresRepository
|
|
|
|
_JSON_SAMPLES = Path(__file__).resolve().parents[3] / "backend/epc_api/json_samples"
|
|
|
|
|
|
def test_get_hydrates_identity_and_epc_slice(db_engine: Engine) -> None:
|
|
# Arrange
|
|
raw: dict[str, Any] = json.loads(
|
|
(_JSON_SAMPLES / "RdSAP-Schema-21.0.0" / "epc.json").read_text()
|
|
)
|
|
epc = EpcPropertyDataMapper.from_api_response(raw)
|
|
with Session(db_engine) as session:
|
|
row = PropertyRow(
|
|
portfolio_id=7, postcode="A0 0AA", address="1 Some Street", uprn=12345
|
|
)
|
|
session.add(row)
|
|
session.commit()
|
|
property_id = row.id
|
|
assert property_id is not None
|
|
EpcPostgresRepository(session).save(epc, property_id=property_id)
|
|
session.commit()
|
|
|
|
# Act
|
|
with Session(db_engine) as session:
|
|
repo = PropertyPostgresRepository(
|
|
session, EpcPostgresRepository(session), SpatialPostgresRepository(session)
|
|
)
|
|
prop = repo.get(property_id)
|
|
|
|
# Assert
|
|
assert prop.identity.portfolio_id == 7
|
|
assert prop.identity.uprn == 12345
|
|
assert prop.epc == epc
|
|
assert prop.source_path == "epc_with_overlay"
|
|
assert prop.effective_epc == epc
|
|
|
|
|
|
def test_get_many_hydrates_planning_restrictions_from_the_spatial_cache(
|
|
db_engine: Engine,
|
|
) -> None:
|
|
# Arrange — a property whose UPRN has a cached listed-building flag.
|
|
with Session(db_engine) as session:
|
|
row = PropertyRow(
|
|
portfolio_id=7, postcode="A0 0AA", address="1 Some Street", uprn=12345
|
|
)
|
|
session.add(row)
|
|
session.commit()
|
|
property_id = row.id
|
|
assert property_id is not None
|
|
SpatialPostgresRepository(session).save(
|
|
uprn=12345,
|
|
reference=SpatialReference(
|
|
coordinates=None,
|
|
restrictions=PlanningRestrictions(is_listed=True),
|
|
),
|
|
)
|
|
session.commit()
|
|
|
|
# Act
|
|
with Session(db_engine) as session:
|
|
repo = PropertyPostgresRepository(
|
|
session, EpcPostgresRepository(session), SpatialPostgresRepository(session)
|
|
)
|
|
properties = repo.get_many([property_id])
|
|
|
|
# Assert — the protections are hydrated onto the Property (ADR-0020).
|
|
assert properties.items[0].planning_restrictions == PlanningRestrictions(
|
|
is_listed=True
|
|
)
|
|
|
|
|
|
def test_get_many_defaults_to_unrestricted_when_uprn_has_no_spatial_row(
|
|
db_engine: Engine,
|
|
) -> None:
|
|
# Arrange — a property whose UPRN is not in the spatial cache.
|
|
with Session(db_engine) as session:
|
|
row = PropertyRow(
|
|
portfolio_id=7, postcode="A0 0AA", address="1 Some Street", uprn=999
|
|
)
|
|
session.add(row)
|
|
session.commit()
|
|
property_id = row.id
|
|
assert property_id is not None
|
|
|
|
# Act
|
|
with Session(db_engine) as session:
|
|
repo = PropertyPostgresRepository(
|
|
session, EpcPostgresRepository(session), SpatialPostgresRepository(session)
|
|
)
|
|
properties = repo.get_many([property_id])
|
|
|
|
# Assert — an uncovered UPRN means unrestricted, not blocked (per legacy
|
|
# `empty_spatial_df`; ADR-0020).
|
|
assert properties.items[0].planning_restrictions == PlanningRestrictions()
|
|
|
|
|
|
def test_mark_modelled_sets_has_recommendations_and_bumps_updated_at(
|
|
db_engine: Engine,
|
|
) -> None:
|
|
# Arrange — a freshly-inserted property with no run recorded yet.
|
|
with Session(db_engine) as session:
|
|
row = PropertyRow(portfolio_id=7, uprn=12345)
|
|
session.add(row)
|
|
session.commit()
|
|
property_id = row.id
|
|
assert property_id is not None
|
|
assert row.has_recommendations is None
|
|
assert row.updated_at is None
|
|
|
|
# Act — record a run that produced recommendations.
|
|
with Session(db_engine) as session:
|
|
PropertyPostgresRepository(session).mark_modelled(
|
|
property_id, has_recommendations=True
|
|
)
|
|
session.commit()
|
|
|
|
# Assert — the marker is set and updated_at is stamped, so the run is datable
|
|
# against the 2026-06-01 new-process cutoff.
|
|
with Session(db_engine) as session:
|
|
refreshed = session.get(PropertyRow, property_id)
|
|
assert refreshed is not None
|
|
assert refreshed.has_recommendations is True
|
|
assert refreshed.updated_at is not None
|