mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
Persist no Lodged Performance for a predicted Property 🟩
The PropertyBaselineOrchestrator now reads no Lodged Performance off a predicted Property's neighbour-synthesised EPC: lodged is None when source_path is 'predicted', so no phantom lodged figure is manufactured. The Effective half is unchanged. Rebaseliner port + PropertyBaselinePerformance.lodged widen to Optional; the pristine-cert paths assert non-None (lodged is None only for a predicted Property, which is always physical_state_changed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5d63118d5a
commit
da805bce21
5 changed files with 34 additions and 9 deletions
|
|
@ -57,7 +57,7 @@ class CalculatorRebaseliner(Rebaseliner):
|
|||
self,
|
||||
property_id: int,
|
||||
effective_epc: "EpcPropertyData",
|
||||
lodged: Performance,
|
||||
lodged: Optional[Performance],
|
||||
*,
|
||||
physical_state_changed: bool = False,
|
||||
) -> RebaselineResult:
|
||||
|
|
@ -87,6 +87,11 @@ class CalculatorRebaseliner(Rebaseliner):
|
|||
reason=reason,
|
||||
sap_result=result,
|
||||
)
|
||||
# The pristine-lodged path: not pre-SAP10 and not physical-state-changed, so
|
||||
# there is a real lodged cert to validate against. ``lodged is None`` only
|
||||
# happens for a predicted Property, which is physical_state_changed and so
|
||||
# took the branch above — it never reaches here.
|
||||
assert lodged is not None
|
||||
self._log_divergence(
|
||||
property_id=property_id, sap_version=sap_version, result=result, lodged=lodged
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ class PropertyBaselinePerformance:
|
|||
Holds both halves — ``lodged`` (what the gov register says) and
|
||||
``effective`` (what the modelling pipeline scored against) — plus the
|
||||
``rebaseline_reason`` recording *why* they differ (``"none"`` when equal).
|
||||
Both halves are always populated, even when equal.
|
||||
Both halves are populated for a Property with a real record of itself; the
|
||||
``lodged`` half is ``None`` for a predicted Property, which has no lodged cert
|
||||
to read a Lodged Performance off — only the Effective half is established then
|
||||
(ADR-0004 amendment, #1361).
|
||||
|
||||
Carries the part of the energy block that needs no derivation: annual
|
||||
``space_heating_kwh`` / ``water_heating_kwh`` read off the EPC's RHI.
|
||||
|
|
@ -25,7 +28,7 @@ class PropertyBaselinePerformance:
|
|||
ran (the stub path produced no ``SapResult`` to price).
|
||||
"""
|
||||
|
||||
lodged: Performance
|
||||
lodged: Optional[Performance]
|
||||
effective: Performance
|
||||
rebaseline_reason: RebaselineReason
|
||||
space_heating_kwh: float
|
||||
|
|
|
|||
|
|
@ -62,11 +62,15 @@ class Rebaseliner(ABC):
|
|||
self,
|
||||
property_id: int,
|
||||
effective_epc: EpcPropertyData,
|
||||
lodged: Performance,
|
||||
lodged: Optional[Performance],
|
||||
*,
|
||||
physical_state_changed: bool = False,
|
||||
) -> RebaselineResult:
|
||||
"""Produce Effective Performance. ``physical_state_changed`` is True when
|
||||
"""Produce Effective Performance. ``lodged`` is ``None`` for a predicted
|
||||
Property — it has no lodged cert, so there is no Lodged Performance to
|
||||
compare against (which is also why ``physical_state_changed`` is always
|
||||
True there: the calculator output IS the Effective Performance).
|
||||
``physical_state_changed`` is True when
|
||||
the Effective EPC was assembled from something other than a pristine
|
||||
lodged cert — Landlord Overrides, Site Notes, or EPC Prediction moved the
|
||||
physical picture (Rebaselining trigger (b)/(c)) — so the accredited lodged
|
||||
|
|
@ -91,7 +95,7 @@ class StubRebaseliner(Rebaseliner):
|
|||
self,
|
||||
property_id: int,
|
||||
effective_epc: EpcPropertyData,
|
||||
lodged: Performance,
|
||||
lodged: Optional[Performance],
|
||||
*,
|
||||
physical_state_changed: bool = False,
|
||||
) -> RebaselineResult:
|
||||
|
|
@ -108,4 +112,8 @@ class StubRebaseliner(Rebaseliner):
|
|||
"Property needs rebaselining (physical state changed by overrides "
|
||||
"/ prediction); this stub does not run the calculator"
|
||||
)
|
||||
# Only a pristine SAP10 cert reaches here, and that always has a Lodged
|
||||
# Performance — ``lodged is None`` implies a predicted Property, which is
|
||||
# ``physical_state_changed`` and so raised above.
|
||||
assert lodged is not None
|
||||
return RebaselineResult(effective=lodged, reason="none", sap_result=None)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from domain.billing.bill import EnergyBreakdown
|
|||
from domain.sap10_calculator.calculator import SapResult
|
||||
from domain.billing.bill_derivation import BillDerivation
|
||||
from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance
|
||||
from domain.property_baseline.performance import lodged_performance
|
||||
from domain.property_baseline.performance import Performance, lodged_performance
|
||||
from domain.property_baseline.rebaseliner import Rebaseliner
|
||||
from repositories.fuel_rates.fuel_rates_repository import FuelRatesRepository
|
||||
from repositories.unit_of_work import UnitOfWork
|
||||
|
|
@ -50,7 +50,15 @@ class PropertyBaselineOrchestrator:
|
|||
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)
|
||||
# A predicted Property has no lodged cert — its Effective EPC is a
|
||||
# neighbour-synthesised picture, so its recorded performance fields
|
||||
# are a different dwelling's. There is no Lodged Performance to read
|
||||
# (ADR-0004 amendment, #1361); only the Effective half is established.
|
||||
lodged: Optional[Performance] = (
|
||||
None
|
||||
if prop.source_path == "predicted"
|
||||
else lodged_performance(effective_epc)
|
||||
)
|
||||
rebaselined = self._rebaseliner.rebaseline(
|
||||
property_id,
|
||||
effective_epc,
|
||||
|
|
|
|||
|
|
@ -161,10 +161,11 @@ class _ScoringRebaseliner(Rebaseliner):
|
|||
self,
|
||||
property_id: int,
|
||||
effective_epc: EpcPropertyData,
|
||||
lodged: Performance,
|
||||
lodged: Optional[Performance],
|
||||
*,
|
||||
physical_state_changed: bool = False,
|
||||
) -> RebaselineResult:
|
||||
assert lodged is not None # this stub is only used on the real-cert path
|
||||
return RebaselineResult(
|
||||
effective=lodged, reason="none", sap_result=self._result
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue