Model/tests/domain/epc_prediction/test_prediction_target.py
Khalim Conn-Kowlessar 7ca1f815f6 refactor(epc-prediction): PR review — rename ComparableProperty, relocate PredictionTarget
Two review points from @dancafc:

1) Rename the `Comparable` dataclass → `ComparableProperty` (it models one
   comparable *property*; the collection stays `ComparableProperties`). Applied
   across domain, repositories, orchestration, harness, scripts, and tests with a
   word-boundary rename so `ComparableProperties` is untouched.

2) Move `PredictionTarget` out of comparable_properties.py into prediction_target.py
   (where `PredictionTargetAttributes` + `build_prediction_target` already live).
   comparable_properties.py now imports it; no import cycle (prediction_target no
   longer depends on comparable_properties). Importers updated.

92 tests pass across the touched suites; pyright strict clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:34:44 +00:00

56 lines
1.9 KiB
Python

"""Assembling an EPC-less Property's PredictionTarget, and the eligibility gate:
a Property whose property type is unknown is not predicted (ADR-0031 slice-5d)."""
from __future__ import annotations
from typing import Optional
from domain.epc_prediction.prediction_target import (
PredictionTarget,
PredictionTargetAttributes,
build_prediction_target,
)
from domain.geospatial.coordinates import Coordinates
from domain.property.property import PropertyIdentity
def _identity(postcode: str = "LS6 1AA") -> PropertyIdentity:
return PropertyIdentity(
portfolio_id=1, postcode=postcode, address="1 Some Street", uprn=12345
)
def test_target_is_assembled_from_identity_coords_and_overrides() -> None:
# Arrange — a known property type + built form + wall (Landlord Overrides),
# and resolved coordinates.
here = Coordinates(longitude=-1.55, latitude=53.81)
attributes = PredictionTargetAttributes(
property_type="2", built_form="3", wall_construction=1
)
# Act
target: Optional[PredictionTarget] = build_prediction_target(
_identity(), here, attributes
)
# Assert — every known input is threaded onto the target.
assert target is not None
assert target.postcode == "LS6 1AA"
assert target.property_type == "2"
assert target.built_form == "3"
assert target.wall_construction == 1
assert target.coordinates is here
def test_an_unknown_property_type_gates_the_property_out() -> None:
# Arrange — property type is the hard cohort filter; without it the Property
# must not be predicted from a mixed-type cohort (ADR-0031).
attributes = PredictionTargetAttributes(property_type=None)
# Act
target: Optional[PredictionTarget] = build_prediction_target(
_identity(), None, attributes
)
# Assert — gated out: no target to predict from.
assert target is None