"""Step-through inspector: did a Property's Landlord Overrides map correctly? Run cell-by-cell in VS Code (each `# %%` is a cell — ▶ Run Cell / Shift+Enter), or top-to-bottom with `PYTHONPATH=. python -m scripts.inspect_overlay`. For one PROPERTY_ID it shows: the `property_overrides` rows, whether each mapped to a Simulation Overlay (or is a silent no-op), the lodged-vs-effective main wall codes the calculator scores, and the SAP delta the overlay produces. EPC is fetched LIVE from the gov API by UPRN (same as run_modelling_e2e); nothing is written. Edit PROPERTY_ID in the second cell and re-run. """ # %% 1 — setup: env, DB engine, gov-EPC client from __future__ import annotations import os import sys from pathlib import Path _REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(_REPO_ROOT)) for _raw in (_REPO_ROOT / "backend" / ".env").read_text(encoding="utf-8").splitlines(): _line = _raw.strip() if _line and not _line.startswith("#") and "=" in _line: _k, _v = _line.split("=", 1) os.environ.setdefault(_k.strip(), _v.strip().strip('"').strip("'")) from sqlalchemy import create_engine, text from sqlmodel import Session from datatypes.epc.domain.epc_property_data import ( BuildingPartIdentifier, EpcPropertyData, ) from domain.epc.property_overlays.wall_type_overlay import wall_overlay_for from domain.property.property import Property, PropertyIdentity from domain.sap10_calculator.calculator import Sap10Calculator from infrastructure.epc_client.epc_client_service import EpcClientService from repositories.property.landlord_override_overlays import overlays_from from repositories.property.property_overrides_postgres_reader import ( PropertyOverridesPostgresReader, ) _engine = create_engine( f"postgresql+psycopg2://{os.environ['DB_USERNAME']}:{os.environ['DB_PASSWORD']}" f"@{os.environ['DB_HOST']}:{os.environ['DB_PORT']}/{os.environ['DB_NAME']}" ) _epc_client = EpcClientService(os.environ["OPEN_EPC_API_TOKEN"]) _reader = PropertyOverridesPostgresReader(lambda: Session(_engine)) def _main_wall(epc: EpcPropertyData) -> object: """The MAIN building part — its wall_construction / wall_insulation_type are what the calculator turns into the wall U-value.""" return next( p for p in epc.sap_building_parts if p.identifier is BuildingPartIdentifier.MAIN ) # %% 2 — pick the property, resolve its UPRN PROPERTY_ID = 709672 # <-- edit me with _engine.connect() as _conn: _row = _conn.execute( text("SELECT uprn, address FROM property WHERE id = :id"), {"id": PROPERTY_ID}, ).fetchone() assert _row is not None, f"property {PROPERTY_ID} not found" uprn, address = int(_row[0]), _row[1] print(f"property {PROPERTY_ID} · uprn {uprn} · {address}") # %% 3 — fetch the lodged EPC live from the gov API epc = _epc_client.get_by_uprn(uprn) assert epc is not None, f"no EPC found for uprn {uprn}" print(f"lodged EPC: {epc.property_type=} {epc.built_form=}") print(f"lodged main wall: {_main_wall(epc)!r}") # %% 4 — the property_overrides rows the finaliser wrote overrides = _reader.overrides_for(PROPERTY_ID) print(f"{len(overrides.rows)} override row(s):") for r in overrides.rows: print(f" part {r.building_part} · {r.override_component} = {r.override_value!r}") # %% 5 — per-row mapping: did each override produce an overlay, or is it a no-op? for r in overrides.rows: if r.override_component == "wall_type": sim = wall_overlay_for(r.override_value, r.building_part) if sim is None: print( f" wall_type {r.override_value!r} -> NO-OP (material/state unmapped)" ) else: bp = next(iter(sim.building_parts.values())) print( f" wall_type {r.override_value!r} -> " f"wall_construction={bp.wall_construction} " f"wall_insulation_type={bp.wall_insulation_type}" ) else: print( f" {r.override_component} {r.override_value!r} -> not overlaid (tracer is wall-only)" ) # %% 6 — fold the overrides into the Effective EPC overlays = overlays_from(overrides) prop = Property( identity=PropertyIdentity(portfolio_id=0, postcode="", address="", uprn=uprn), epc=epc, landlord_overrides=overlays, ) effective = prop.effective_epc print(f"{len(overlays)} overlay(s) folded · source_path={prop.source_path}") # %% 7 — lodged vs effective: the codes the calculator scores print(f"lodged main wall: {_main_wall(epc)!r}") print(f"effective main wall: {_main_wall(effective)!r}") # %% 8 — the SAP delta the overlay produces (the whole point) lodged_sap = Sap10Calculator().calculate(epc).sap_score effective_sap = Sap10Calculator().calculate(effective).sap_score print( f"SAP lodged={lodged_sap} effective={effective_sap} delta={effective_sap - lodged_sap:+d}" ) # %%