Predict ventilation kind from the cohort mode 🟥

Prediction never synthesises ventilation — it keeps the size-template's
sap_ventilation, so a predicted dwelling in an MEV/MVHR neighbourhood is scored
+ displayed as natural (predicted property 721167 follow-up). Mode the
mechanical_ventilation_kind across the cohort like glazing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-26 10:49:37 +00:00
parent 44726469d4
commit 80c5ad0c6c

View file

@ -17,6 +17,7 @@ from datatypes.epc.domain.epc_property_data import (
SapEnergySource,
SapFloorDimension,
SapHeating,
SapVentilation,
SapWindow,
)
from domain.geospatial.coordinates import Coordinates
@ -54,6 +55,7 @@ def _epc(
meter_type: str = "2",
main_heating_label: str = "Boiler and radiators, mains gas",
main_heating_controls_label: Optional[str] = None,
mechanical_ventilation_kind: Optional[str] = None,
) -> EpcPropertyData:
epc: EpcPropertyData = object.__new__(EpcPropertyData)
epc.property_type = "2"
@ -108,6 +110,10 @@ def _epc(
)
epc.has_hot_water_cylinder = has_hot_water_cylinder
epc.solar_water_heating = solar_water_heating
epc.sap_ventilation = SapVentilation(
mechanical_ventilation_kind=mechanical_ventilation_kind,
sheltered_sides=1,
)
energy: SapEnergySource = object.__new__(SapEnergySource)
energy.meter_type = meter_type
epc.sap_energy_source = energy
@ -557,6 +563,31 @@ def test_heating_is_a_coherent_donor_not_the_structural_template() -> None:
assert predicted.has_hot_water_cylinder is True
def test_ventilation_kind_follows_the_cohort_mode() -> None:
# Mechanical ventilation (MEV/MVHR) is a new-build / retrofit feature that
# clusters by era and street — like glazing — so the predicted ventilation
# kind takes the recency/geo-weighted cohort mode, not the size-template's.
# The size-closest template here is natural (None); the cohort is
# predominantly MVHR, so the prediction must reflect the MVHR neighbourhood
# rather than leave the template's empty ventilation (predicted property
# 721167 follow-up). Natural-vent cohorts mode to None and stay natural.
cohort = _cohort(
_epc(mechanical_ventilation_kind=None), # template (size tie → first)
_epc(mechanical_ventilation_kind="MVHR"),
_epc(mechanical_ventilation_kind="MVHR"),
_epc(mechanical_ventilation_kind="MVHR"),
)
# Act
predicted: EpcPropertyData = EpcPrediction().predict(
PredictionTarget(postcode="LS6 1AA", property_type="2"), cohort
)
# Assert — the predicted kind is the cohort's MVHR mode, not the template's None.
assert predicted.sap_ventilation is not None
assert predicted.sap_ventilation.mechanical_ventilation_kind == "MVHR"
def test_glazing_follows_the_recency_weighted_cohort_mode() -> None:
# Arrange — an old majority single-glazed (type 1, 2015) and a recent
# minority double-glazed (type 3, 2025). Glazing is retrofitted over time