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" ), )