Prefer a MAIN-bearing prediction template so EPC-less dwellings predict 🟥

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-06-24 13:16:54 +00:00
parent fc7cfc70e8
commit abb822c1c7

View file

@ -9,6 +9,7 @@ from datetime import date
from typing import Optional, Union
from datatypes.epc.domain.epc_property_data import (
BuildingPartIdentifier,
EpcPropertyData,
MainHeatingDetail,
SapBuildingPart,
@ -32,6 +33,7 @@ from domain.epc_prediction.prediction_target import PredictionTarget
def _epc(
*,
building_parts: int = 1,
identifier: BuildingPartIdentifier = BuildingPartIdentifier.MAIN,
floor_area: float = 80.0,
wall_construction: Union[int, str] = 1,
wall_insulation_type: Union[int, str] = 1,
@ -57,6 +59,7 @@ def _epc(
parts: list[SapBuildingPart] = []
for _ in range(building_parts):
part: SapBuildingPart = object.__new__(SapBuildingPart)
part.identifier = identifier
part.wall_construction = wall_construction
part.wall_insulation_type = wall_insulation_type
part.construction_age_band = construction_age_band
@ -148,6 +151,31 @@ def test_template_is_the_member_closest_to_the_cohort_median_size() -> None:
assert predicted.total_floor_area_m2 == 80.0
def test_template_skips_a_main_less_member_so_the_prediction_has_a_main_part() -> None:
# Arrange — the size-median member is OTHER-only (the gov API lodged its part
# with a null identifier), but the cohort holds MAIN-bearing neighbours at
# other sizes. The structural template must be a MAIN-bearing member so the
# predicted dwelling presents a main dwelling — else the modelling handler's
# MAIN-part guard rejects an otherwise-rich cohort as "not predictable".
cohort = _cohort(
_epc(floor_area=80.0, identifier=BuildingPartIdentifier.OTHER),
_epc(floor_area=30.0, identifier=BuildingPartIdentifier.MAIN),
_epc(floor_area=200.0, identifier=BuildingPartIdentifier.MAIN),
)
# Act
predicted: EpcPropertyData = EpcPrediction().predict(
PredictionTarget(postcode="LS6 1AA", property_type="2"), cohort
)
# Assert — the predicted dwelling has a MAIN part (seeded from a MAIN-bearing
# neighbour), not the OTHER-only median member.
assert any(
part.identifier is BuildingPartIdentifier.MAIN
for part in predicted.sap_building_parts
)
def test_sets_main_wall_construction_to_the_cohort_mode() -> None:
# Arrange — the template (members[0]) is solid brick (2), but the cohort
# majority is cavity (1). The homogeneous categorical should follow the mode,