mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
feat: rebaseliner adopts calc output on physical-state change 🟩
CalculatorRebaseliner uses the calculator output as Effective Performance whenever a Rebaselining trigger fired — pre-SAP10 (a) OR overrides/prediction moved the physical state (b)/(c) — tagging pre_sap10 / physical_state_changed / both. Only a pristine lodged >=10.2 cert keeps its accredited figure (the sole case the calculator runs purely to validate). Divergence is logged only in that pristine case. ABC + StubRebaseliner take the new keyword-only flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1e019ea3b3
commit
2f7cfbf446
2 changed files with 54 additions and 7 deletions
|
|
@ -4,7 +4,11 @@ import logging
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from domain.property_baseline.performance import Performance
|
from domain.property_baseline.performance import Performance
|
||||||
from domain.property_baseline.rebaseliner import Rebaseliner, RebaselineResult
|
from domain.property_baseline.rebaseliner import (
|
||||||
|
Rebaseliner,
|
||||||
|
RebaselineReason,
|
||||||
|
RebaselineResult,
|
||||||
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||||
|
|
@ -50,7 +54,12 @@ class CalculatorRebaseliner(Rebaseliner):
|
||||||
self._calculator = calculator
|
self._calculator = calculator
|
||||||
|
|
||||||
def rebaseline(
|
def rebaseline(
|
||||||
self, property_id: int, effective_epc: "EpcPropertyData", lodged: Performance
|
self,
|
||||||
|
property_id: int,
|
||||||
|
effective_epc: "EpcPropertyData",
|
||||||
|
lodged: Performance,
|
||||||
|
*,
|
||||||
|
physical_state_changed: bool = False,
|
||||||
) -> RebaselineResult:
|
) -> RebaselineResult:
|
||||||
# A raise (UnmappedSapCode, etc.) propagates: the calculator is
|
# A raise (UnmappedSapCode, etc.) propagates: the calculator is
|
||||||
# load-bearing, so the batch aborts and the cert is fixed at once. The
|
# load-bearing, so the batch aborts and the cert is fixed at once. The
|
||||||
|
|
@ -58,10 +67,24 @@ class CalculatorRebaseliner(Rebaseliner):
|
||||||
# regardless of whether lodged or calculated figures win (ADR-0013/0014).
|
# regardless of whether lodged or calculated figures win (ADR-0013/0014).
|
||||||
result: SapResult = self._calculator.calculate(effective_epc)
|
result: SapResult = self._calculator.calculate(effective_epc)
|
||||||
sap_version: Optional[float] = effective_epc.sap_version
|
sap_version: Optional[float] = effective_epc.sap_version
|
||||||
if sap_version is not None and sap_version < _MIN_TRUSTED_SAP_VERSION:
|
pre_sap10 = sap_version is not None and sap_version < _MIN_TRUSTED_SAP_VERSION
|
||||||
|
# The calculator output IS Effective Performance whenever a Rebaselining
|
||||||
|
# trigger fired: (a) a superseded methodology (pre-SAP10), or (b)/(c) the
|
||||||
|
# physical state was changed by Landlord Overrides / Prediction. Only a
|
||||||
|
# pristine lodged SAP >= 10.2 cert keeps its accredited figure — that is
|
||||||
|
# the *only* case where the calculator runs purely to validate (and where
|
||||||
|
# its known divergence from the accredited register would mislead).
|
||||||
|
if pre_sap10 or physical_state_changed:
|
||||||
|
reason: RebaselineReason = (
|
||||||
|
"both"
|
||||||
|
if pre_sap10 and physical_state_changed
|
||||||
|
else "pre_sap10"
|
||||||
|
if pre_sap10
|
||||||
|
else "physical_state_changed"
|
||||||
|
)
|
||||||
return RebaselineResult(
|
return RebaselineResult(
|
||||||
effective=Performance.from_sap_result(result),
|
effective=Performance.from_sap_result(result),
|
||||||
reason="pre_sap10",
|
reason=reason,
|
||||||
sap_result=result,
|
sap_result=result,
|
||||||
)
|
)
|
||||||
self._log_divergence(
|
self._log_divergence(
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,20 @@ class Rebaseliner(ABC):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def rebaseline(
|
def rebaseline(
|
||||||
self, property_id: int, effective_epc: EpcPropertyData, lodged: Performance
|
self,
|
||||||
) -> RebaselineResult: ...
|
property_id: int,
|
||||||
|
effective_epc: EpcPropertyData,
|
||||||
|
lodged: Performance,
|
||||||
|
*,
|
||||||
|
physical_state_changed: bool = False,
|
||||||
|
) -> RebaselineResult:
|
||||||
|
"""Produce 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
|
||||||
|
figure no longer describes the dwelling and the calculator output wins
|
||||||
|
even at SAP >= 10.2."""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class StubRebaseliner(Rebaseliner):
|
class StubRebaseliner(Rebaseliner):
|
||||||
|
|
@ -76,7 +88,12 @@ class StubRebaseliner(Rebaseliner):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def rebaseline(
|
def rebaseline(
|
||||||
self, property_id: int, effective_epc: EpcPropertyData, lodged: Performance
|
self,
|
||||||
|
property_id: int,
|
||||||
|
effective_epc: EpcPropertyData,
|
||||||
|
lodged: Performance,
|
||||||
|
*,
|
||||||
|
physical_state_changed: bool = False,
|
||||||
) -> RebaselineResult:
|
) -> RebaselineResult:
|
||||||
sap_version = effective_epc.sap_version
|
sap_version = effective_epc.sap_version
|
||||||
if sap_version is not None and sap_version < _SAP10_FLOOR:
|
if sap_version is not None and sap_version < _SAP10_FLOOR:
|
||||||
|
|
@ -84,4 +101,11 @@ class StubRebaseliner(Rebaseliner):
|
||||||
f"Property needs rebaselining (pre-SAP10 cert, sap_version="
|
f"Property needs rebaselining (pre-SAP10 cert, sap_version="
|
||||||
f"{sap_version}); this stub does not run the calculator"
|
f"{sap_version}); this stub does not run the calculator"
|
||||||
)
|
)
|
||||||
|
# A physical-state change needs the calculator this stub does not run;
|
||||||
|
# raise rather than fabricate a "none" that ignores the overrides.
|
||||||
|
if physical_state_changed:
|
||||||
|
raise RebaselineNotImplemented(
|
||||||
|
"Property needs rebaselining (physical state changed by overrides "
|
||||||
|
"/ prediction); this stub does not run the calculator"
|
||||||
|
)
|
||||||
return RebaselineResult(effective=lodged, reason="none", sap_result=None)
|
return RebaselineResult(effective=lodged, reason="none", sap_result=None)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue