mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
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:
parent
ece4b736d9
commit
992950bd9f
2 changed files with 113 additions and 0 deletions
43
domain/epc_prediction/historic_conditioning.py
Normal file
43
domain/epc_prediction/historic_conditioning.py
Normal 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,
|
||||
)
|
||||
70
tests/domain/epc_prediction/test_historic_conditioning.py
Normal file
70
tests/domain/epc_prediction/test_historic_conditioning.py
Normal 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
|
||||
Loading…
Add table
Reference in a new issue