mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Two-pass org_ref-matched builder for property_overrides (classify via ChatGPT into the landlord ledger, validate+apply user edits, write idempotently); ephemeral-Postgres smoke proves the one-property chain without creds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
95 lines
4.8 KiB
Python
95 lines
4.8 KiB
Python
"""End-to-end smoke of the Hyde override script for ONE property, against a real
|
|
(ephemeral) Postgres. Seeds the landlord vocab (simulating post-classify, so no
|
|
ChatGPT) + a minimal ``property`` row, then runs the script's real
|
|
``write`` + ``verify`` paths and asserts property_overrides + overlays land.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from typing import Any
|
|
|
|
from sqlalchemy import Engine, text
|
|
from sqlmodel import Session
|
|
|
|
import scripts.hyde.build_property_overrides as b
|
|
from domain.epc.property_overrides.built_form_type import BuiltFormType
|
|
from domain.epc.property_overrides.construction_age_band import ConstructionAgeBand
|
|
from domain.epc.property_overrides.glazing_type import GlazingType
|
|
from domain.epc.property_overrides.main_fuel_type import MainFuelType
|
|
from domain.epc.property_overrides.main_heating_system_type import MainHeatingSystemType
|
|
from domain.epc.property_overrides.property_type import PropertyType
|
|
from domain.epc.property_overrides.roof_type import RoofType
|
|
from domain.epc.property_overrides.wall_type import WallType
|
|
from domain.epc.property_overrides.water_heating_type import WaterHeatingType
|
|
from infrastructure.landlord_overrides.landlord_overrides_postgres_repository import (
|
|
LandlordOverridesRepository,
|
|
)
|
|
from repositories.property.landlord_override_overlays import overlays_from
|
|
from repositories.property.property_overrides_postgres_reader import (
|
|
PropertyOverridesPostgresReader,
|
|
)
|
|
|
|
PORTFOLIO = 795
|
|
ORG_REF = "55180004001"
|
|
EXCEL = "scripts/hyde/hyde_property_overrides.xlsx"
|
|
|
|
# What ChatGPT WOULD resolve this property's 9 descriptions to (component ->
|
|
# (raw Excel entry, enum member)). Seeded into the landlord ledger.
|
|
SEED = {
|
|
"property_type": ("House: MidTerrace", PropertyType.HOUSE),
|
|
"built_form_type": ("House: MidTerrace", BuiltFormType.MID_TERRACE),
|
|
"wall_type": ("TimberFrame: AsBuilt", WallType.TIMBER_FRAME_AS_BUILT_NO_INSULATION_ASSUMED),
|
|
"roof_type": ("PitchedNormalLoftAccess: 300mm", RoofType.PITCHED_LOFT_300MM),
|
|
"construction_age_band": ("L: 2012-2022", ConstructionAgeBand.L_2012_2022),
|
|
"main_fuel": ("Gas: Mains Gas", MainFuelType.MAINS_GAS),
|
|
"glazing": ("100% Double glazing 2002 or later", GlazingType.DOUBLE_POST_2002),
|
|
"water_heating": ("From main heating system: Mains Gas", WaterHeatingType.FROM_MAIN_MAINS_GAS),
|
|
"main_heating_system": ("Boiler: C rated Combi", MainHeatingSystemType.GAS_COMBI),
|
|
}
|
|
|
|
|
|
def test_one_property_end_to_end(db_engine: Engine, monkeypatch: Any) -> None:
|
|
specs = b._specs_by_component() # pyright: ignore[reportPrivateUsage]
|
|
|
|
# minimal FE-owned `property` table + the one row we'll match by org_ref
|
|
with Session(db_engine) as s:
|
|
s.execute(text( # pyright: ignore[reportDeprecated]
|
|
"CREATE TABLE property (id bigint PRIMARY KEY, portfolio_id bigint, "
|
|
"landlord_property_id text)"))
|
|
s.execute(text("INSERT INTO property VALUES (1, :p, :ref)"), # pyright: ignore[reportDeprecated]
|
|
{"p": PORTFOLIO, "ref": ORG_REF})
|
|
# seed the classifier ledger (keyed on normalised description)
|
|
for comp, (raw, member) in SEED.items():
|
|
repo: LandlordOverridesRepository[Any] = LandlordOverridesRepository(
|
|
s, specs[comp].row_type)
|
|
repo.upsert_all(PORTFOLIO, {b._norm(raw): member}) # pyright: ignore[reportPrivateUsage]
|
|
s.commit()
|
|
|
|
# point the script at the ephemeral engine
|
|
monkeypatch.setattr(b, "_db_session", lambda: Session(db_engine))
|
|
|
|
# --- run the real write() for this one property ---
|
|
b.write(argparse.Namespace(excel=EXCEL, sheet="AddressProfilingResults",
|
|
portfolio_id=PORTFOLIO, org_ref=ORG_REF, limit=None, apply=True))
|
|
|
|
with Session(db_engine) as s:
|
|
rows = list(s.execute(text( # pyright: ignore[reportDeprecated]
|
|
"SELECT override_component, building_part, override_value "
|
|
"FROM property_overrides WHERE property_id = 1 ORDER BY override_component")))
|
|
got = {c: v for c, _, v in rows}
|
|
# every seeded component produced a property_overrides row with the resolved value
|
|
assert got["main_fuel"] == "mains gas"
|
|
assert got["glazing"] == "Double glazing, 2002 or later"
|
|
assert got["construction_age_band"] == "L"
|
|
assert got["main_heating_system"] == "Gas boiler, combi"
|
|
assert got["water_heating"] == "From main system, mains gas"
|
|
assert len(rows) == 9 # all 9 components
|
|
|
|
# --- the overrides reach the SAP overlay surface ---
|
|
b.verify(argparse.Namespace(portfolio_id=PORTFOLIO, org_ref=ORG_REF)) # exercises verify()
|
|
overlays = overlays_from(
|
|
PropertyOverridesPostgresReader(lambda: Session(db_engine)).overrides_for(1))
|
|
assert len(overlays) == 9
|
|
assert any(o.heating is not None and o.heating.main_fuel_type == 26 for o in overlays)
|
|
assert any(o.glazing is not None and o.glazing.glazing_type == 2 for o in overlays)
|