Read a scenario's default-plan properties as export rows 🟩

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-13 23:26:51 +00:00
parent d70c1b65cc
commit 1bb1e605e2

View file

@ -1,7 +1,8 @@
from __future__ import annotations
from typing import Sequence
from typing import Any, Optional, Sequence
from sqlalchemy import text
from sqlmodel import Session
from domain.scenario_export.scenario_sheet import PropertyScenarioData
@ -9,6 +10,35 @@ from repositories.scenario_export.scenario_export_repository import (
ScenarioExportRepository,
)
# The default Plan per (scenario, property), scoped to the requested Properties.
# The JOIN to `plan` is the model-A scenario-scoping (ADR-0065): a Property with
# no default Plan under the Scenario yields no row and drops out.
_ROWS_SQL = text(
"SELECT p.id, p.landlord_property_id, p.uprn, p.address, p.postcode,"
" pl.post_sap_points, pl.post_epc_rating, pl.cost_of_works,"
" pl.contingency_cost, pl.co2_savings, pl.energy_bill_savings,"
" pl.energy_consumption_savings, pl.valuation_increase"
" FROM property p"
" JOIN plan pl ON pl.property_id = p.id"
" WHERE p.portfolio_id = :portfolio_id"
" AND pl.scenario_id = :scenario_id"
" AND pl.is_default = TRUE"
" AND p.id = ANY(:property_ids)"
" ORDER BY p.id"
)
def _str(value: Any) -> Optional[str]:
"""Render a persisted scalar (int UPRN, Epc enum band) as its string form,
preserving absence as ``None``."""
if value is None:
return None
return str(getattr(value, "value", value))
def _float(value: Any) -> Optional[float]:
return None if value is None else float(value)
class ScenarioExportPostgresRepository(ScenarioExportRepository):
"""Reads Scenario Export rows from Postgres (ADR-0065). Hand-rolled SQL over
@ -25,4 +55,31 @@ class ScenarioExportPostgresRepository(ScenarioExportRepository):
scenario_id: int,
property_ids: Sequence[int],
) -> list[PropertyScenarioData]:
raise NotImplementedError
if not property_ids:
return []
result = self._session.connection().execute(
_ROWS_SQL,
{
"portfolio_id": portfolio_id,
"scenario_id": scenario_id,
"property_ids": list(property_ids),
},
)
return [
PropertyScenarioData(
property_id=row[0],
landlord_property_id=_str(row[1]),
uprn=_str(row[2]),
address=_str(row[3]),
postcode=_str(row[4]),
post_sap_points=_float(row[5]),
post_epc_rating=_str(row[6]),
cost_of_works=_float(row[7]),
contingency_cost=_float(row[8]),
co2_savings=_float(row[9]),
energy_bill_savings=_float(row[10]),
energy_consumption_savings=_float(row[11]),
valuation_increase=_float(row[12]),
)
for row in result.all()
]