mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Wholesale rename of the Baseline aggregate to PropertyBaseline for clarity /
to disambiguate from baselines that appear elsewhere in Modelling. Scoped to
this aggregate only — the distinct Rebaselining term (rebaseline_reason,
StubRebaseliner, RebaselineNotImplemented) is deliberately untouched.
- domain/baseline → domain/property_baseline; BaselinePerformance →
PropertyBaselinePerformance.
- repositories/baseline → repositories/property_baseline; BaselineRepository
/ BaselinePostgresRepository → PropertyBaseline*.
- orchestration/baseline_orchestrator.py → property_baseline_orchestrator.py;
BaselineOrchestrator → PropertyBaselineOrchestrator. BaselineStage →
PropertyBaselineStage.
- infrastructure/postgres: baseline_performance_table.py →
property_baseline_performance_table.py; table `baseline_performance` →
`property_baseline_performance`; Model renamed.
- UnitOfWork attribute `.baseline` → `.property_baseline`.
- Docs: ADR-0004 references + migration doc (renamed to
property-baseline-performance-table.md) updated.
CONTEXT.md glossary term ("Baseline Performance") left as-is pending a
ubiquitous-language call (raised on the PR). 123 tests pass; pyright strict
clean (only the unrelated pre-existing moto import errors remain).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.7 KiB
Python
67 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
|
|
from datatypes.epc.domain.epc_property_data import (
|
|
EpcPropertyData,
|
|
RenewableHeatIncentive,
|
|
)
|
|
from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance
|
|
from domain.property_baseline.performance import lodged_performance
|
|
from domain.property_baseline.rebaseliner import Rebaseliner
|
|
from repositories.unit_of_work import UnitOfWork
|
|
|
|
|
|
class PropertyBaselineOrchestrator:
|
|
"""Stage 2: establish each Property's Baseline Performance and persist it.
|
|
|
|
Runs the whole batch in **one** Unit of Work and commits once (ADR-0012):
|
|
for each property it hydrates the Property via the unit's PropertyRepo,
|
|
resolves the Effective EPC, reads Lodged Performance off it, runs the
|
|
Rebaseliner to produce Effective Performance, and persists the pair plus the
|
|
deterministic kWh. Any property raising aborts the batch — the unit is left
|
|
uncommitted, so nothing persists and the subtask fails noisily.
|
|
|
|
Reads only from repos — never a Fetcher or HTTP (ADR-0003) — so it is
|
|
byte-identical whether Ingestion ran milliseconds ago (First Run) or last
|
|
week. The injected Rebaseliner is the re-score-on-override seam (ADR-0011).
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
unit_of_work: Callable[[], UnitOfWork],
|
|
rebaseliner: Rebaseliner,
|
|
) -> None:
|
|
self._unit_of_work = unit_of_work
|
|
self._rebaseliner = rebaseliner
|
|
|
|
def run(self, property_ids: list[int]) -> None:
|
|
with self._unit_of_work() as uow:
|
|
properties = uow.property.get_many(property_ids)
|
|
for property_id, prop in zip(property_ids, properties, strict=True):
|
|
effective_epc = prop.effective_epc
|
|
lodged = lodged_performance(effective_epc)
|
|
effective, reason = self._rebaseliner.rebaseline(
|
|
effective_epc, lodged
|
|
)
|
|
rhi = _require_rhi(effective_epc)
|
|
baseline = PropertyBaselinePerformance(
|
|
lodged=lodged,
|
|
effective=effective,
|
|
rebaseline_reason=reason,
|
|
space_heating_kwh=rhi.space_heating_kwh,
|
|
water_heating_kwh=rhi.water_heating_kwh,
|
|
)
|
|
uow.property_baseline.save(baseline, property_id)
|
|
uow.commit()
|
|
|
|
|
|
def _require_rhi(epc: EpcPropertyData) -> RenewableHeatIncentive:
|
|
rhi = epc.renewable_heat_incentive
|
|
if rhi is None:
|
|
raise ValueError(
|
|
"Effective EPC is missing renewable_heat_incentive; cannot read "
|
|
"baseline space-heating / hot-water kWh"
|
|
)
|
|
return rhi
|