mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
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>
71 lines
2.6 KiB
Python
71 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Optional, TYPE_CHECKING, TypeVar
|
|
|
|
from datatypes.epc.domain.epc import Epc
|
|
from datatypes.epc.domain.epc_property_data import EpcPropertyData
|
|
|
|
if TYPE_CHECKING:
|
|
from domain.sap10_calculator.calculator import SapResult
|
|
|
|
_T = TypeVar("_T")
|
|
_KG_PER_TONNE = 1000.0
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Performance:
|
|
"""One half of a Baseline Performance — a single set of SAP10 figures.
|
|
|
|
The four quantities a Property is rated on (CONTEXT.md: Lodged / Effective
|
|
Performance): SAP score, EPC Band, carbon emissions, and Primary Energy
|
|
Intensity. Used for both the Lodged half (off the gov register) and the
|
|
Effective half (what the modelling pipeline scored against).
|
|
"""
|
|
|
|
sap_score: int
|
|
epc_band: Epc
|
|
co2_emissions: float
|
|
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:
|
|
if value is None:
|
|
raise ValueError(
|
|
f"EPC is missing recorded performance field {field!r}; "
|
|
"cannot establish Lodged Performance"
|
|
)
|
|
return value
|
|
|
|
|
|
def lodged_performance(epc: EpcPropertyData) -> Performance:
|
|
"""The Lodged Performance recorded on an EPC — what the gov register says.
|
|
|
|
Reads the four rated quantities straight off the EPC's recorded fields
|
|
(CONTEXT.md: Primary Energy Intensity is recorded as `energy_consumption_current`).
|
|
Unmodified by modelling.
|
|
"""
|
|
return Performance(
|
|
sap_score=_require(epc.energy_rating_current, "energy_rating_current"),
|
|
epc_band=_require(
|
|
epc.current_energy_efficiency_band, "current_energy_efficiency_band"
|
|
),
|
|
co2_emissions=_require(epc.co2_emissions_current, "co2_emissions_current"),
|
|
primary_energy_intensity=_require(
|
|
epc.energy_consumption_current, "energy_consumption_current"
|
|
),
|
|
)
|