Resolve a historic EPC's stable attributes into cohort code spaces 🟥

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-06 08:21:41 +00:00
parent ece4b736d9
commit 992950bd9f
2 changed files with 113 additions and 0 deletions

View file

@ -0,0 +1,43 @@
"""Resolve an expired Historic EPC's *stable* attributes into the cohort code
spaces that condition EPC Prediction (ADR-0054).
The historic dump carries display text ("Semi-Detached", "Cavity wall, as
built, no insulation (assumed)"); the Comparable Properties cohort carries
gov-EPC codes. This module is the anti-corruption layer between the two: one
resolver per whitelisted stable attribute, each returning None on an
unresolvable value so its cohort filter is simply inactive. Volatile
attributes (heating system, hot water, glazing, PV, insulation states,
lighting) are deliberately absent a 14+-year-old observation of them is not
evidence about today.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
from datatypes.epc.domain.historic_epc import HistoricEpc
@dataclass(frozen=True)
class HistoricConditioning:
"""The expired cert's stable attributes, in cohort code space; None means
"unresolved — do not condition on this"."""
property_type: Optional[str]
built_form: Optional[str]
wall_construction: Optional[int]
construction_age_band: Optional[str]
main_fuel: Optional[int]
total_floor_area_m2: Optional[float]
def conditioning_from_historic(record: HistoricEpc) -> HistoricConditioning:
return HistoricConditioning(
property_type=None,
built_form=None,
wall_construction=None,
construction_age_band=None,
main_fuel=None,
total_floor_area_m2=None,
)

View file

@ -0,0 +1,70 @@
"""HistoricConditioning resolves an expired Historic EPC's stable attributes
into the cohort's code spaces (ADR-0054).
Only stable attributes are resolved volatile ones (heating system, glazing,
PV, insulation states) never condition prediction. Every resolver degrades to
None on an unresolvable value, which leaves its cohort filter inactive.
"""
from __future__ import annotations
import dataclasses
from datatypes.epc.domain.historic_epc import HistoricEpc
from domain.epc_prediction.historic_conditioning import (
HistoricConditioning,
conditioning_from_historic,
)
def _hist(**overrides: str) -> HistoricEpc:
fields = {f.name: "" for f in dataclasses.fields(HistoricEpc)}
fields.update(overrides)
return HistoricEpc(**fields)
def test_resolves_stable_attributes_into_cohort_code_spaces():
# Arrange — display-text values exactly as the old register lodged them.
record = _hist(
property_type="House",
built_form="Semi-Detached",
walls_description="Cavity wall, as built, no insulation (assumed)",
construction_age_band="England and Wales: 1930-1949",
main_fuel="mains gas (not community)",
total_floor_area="84",
)
# Act
conditioning = conditioning_from_historic(record)
# Assert — each attribute lands in the code space its cohort filter compares.
assert isinstance(conditioning, HistoricConditioning)
assert conditioning.property_type == "0"
assert conditioning.built_form == "2"
assert conditioning.wall_construction == 4
assert conditioning.construction_age_band == "C"
assert conditioning.main_fuel == 26
assert conditioning.total_floor_area_m2 == 84.0
def test_unresolvable_values_degrade_to_none():
# Arrange — junk and blanks must never guess a code.
record = _hist(
property_type="Castle",
built_form="Not Recorded",
walls_description="Average thermal transmittance 0.3 W/m²K",
construction_age_band="INVALID!",
main_fuel="To be used only when there is no heating system",
total_floor_area="",
)
# Act
conditioning = conditioning_from_historic(record)
# Assert
assert conditioning.property_type is None
assert conditioning.built_form is None
assert conditioning.wall_construction is None
assert conditioning.construction_age_band is None
assert conditioning.main_fuel is None
assert conditioning.total_floor_area_m2 is None