mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
refactor(baseline): Performance.from_sap_result replaces the loose mapper
PR feedback: the SapResult -> Performance mapping should be a method, not a free function you must know exists in the rebaseliner. Put the factory on the target as `Performance.from_sap_result`, beside its sibling `lodged_performance` and mirroring `Epc.from_sap_score` (the factory this mapping already calls). Not a `SapResult.to_performance()`: that would make the SAP calculator import `Performance` (a property_baseline type), re-introducing the engine->consumer coupling removed by the SapCalculator ABC. SapResult is a TYPE_CHECKING-only import in performance.py (the body only reads attributes), so the calculator module is not pulled in at runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8d2ff23e2f
commit
23f01685f9
2 changed files with 20 additions and 14 deletions
|
|
@ -3,7 +3,6 @@ from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from datatypes.epc.domain.epc import Epc
|
|
||||||
from domain.property_baseline.performance import Performance
|
from domain.property_baseline.performance import Performance
|
||||||
from domain.property_baseline.rebaseliner import Rebaseliner, RebaselineReason
|
from domain.property_baseline.rebaseliner import Rebaseliner, RebaselineReason
|
||||||
|
|
||||||
|
|
@ -22,17 +21,6 @@ _REL_TOL = 0.01
|
||||||
_KG_PER_TONNE = 1000.0
|
_KG_PER_TONNE = 1000.0
|
||||||
|
|
||||||
|
|
||||||
def performance_from_sap_result(result: "SapResult") -> Performance:
|
|
||||||
"""The four rated quantities, read off a `SapResult`: band derived from the
|
|
||||||
score, CO2 converted kg→tonnes, PEUI rounded to the lodged integer scale."""
|
|
||||||
return Performance(
|
|
||||||
sap_score=result.sap_score,
|
|
||||||
epc_band=Epc.from_sap_score(result.sap_score),
|
|
||||||
co2_emissions=result.co2_kg_per_yr / _KG_PER_TONNE,
|
|
||||||
primary_energy_intensity=round(result.primary_energy_kwh_per_m2),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _relative_diff(calculated: float, lodged: float) -> float:
|
def _relative_diff(calculated: float, lodged: float) -> float:
|
||||||
if lodged == 0:
|
if lodged == 0:
|
||||||
return 0.0 if calculated == 0 else float("inf")
|
return 0.0 if calculated == 0 else float("inf")
|
||||||
|
|
@ -62,7 +50,7 @@ class CalculatorRebaseliner(Rebaseliner):
|
||||||
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 < _SAP10_2_FLOOR:
|
if sap_version is not None and sap_version < _SAP10_2_FLOOR:
|
||||||
return performance_from_sap_result(result), "pre_sap10"
|
return Performance.from_sap_result(result), "pre_sap10"
|
||||||
self._log_divergence(
|
self._log_divergence(
|
||||||
property_id=property_id, sap_version=sap_version, result=result, lodged=lodged
|
property_id=property_id, sap_version=sap_version, result=result, lodged=lodged
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional, TypeVar
|
from typing import Optional, TYPE_CHECKING, TypeVar
|
||||||
|
|
||||||
from datatypes.epc.domain.epc import Epc
|
from datatypes.epc.domain.epc import Epc
|
||||||
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from domain.sap10_calculator.calculator import SapResult
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
|
_KG_PER_TONNE = 1000.0
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -24,6 +28,20 @@ class Performance:
|
||||||
co2_emissions: float
|
co2_emissions: float
|
||||||
primary_energy_intensity: int
|
primary_energy_intensity: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_sap_result(cls, result: "SapResult") -> "Performance":
|
||||||
|
"""The four rated quantities, read off a calculator `SapResult`
|
||||||
|
(ADR-0013): band derived from the score, CO2 converted kg→tonnes, PEUI
|
||||||
|
rounded to the lodged integer scale. The `from_*` factory mirrors
|
||||||
|
`Epc.from_sap_score`; living on the target keeps the SAP calculator
|
||||||
|
free of any `property_baseline` dependency."""
|
||||||
|
return cls(
|
||||||
|
sap_score=result.sap_score,
|
||||||
|
epc_band=Epc.from_sap_score(result.sap_score),
|
||||||
|
co2_emissions=result.co2_kg_per_yr / _KG_PER_TONNE,
|
||||||
|
primary_energy_intensity=round(result.primary_energy_kwh_per_m2),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _require(value: Optional[_T], field: str) -> _T:
|
def _require(value: Optional[_T], field: str) -> _T:
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue