Model/domain/epc/override_code_mapping.py
Jun-te Kim a1ce8ece50 Map landlord-override property type and built form to gov EPC codes 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 15:07:09 +00:00

56 lines
2.4 KiB
Python

"""Map a Landlord-Override enum *value* to the gov-EPC API *code* space.
The `property_overrides` fact layer stores resolved overrides as enum-value
strings ("House", "Detached"); the EPC-API cohort certs carry numeric codes
("0", "2") — `EpcPropertyData.property_type = str(schema.property_type)`. EPC
Prediction filters `comparable.epc.property_type == target.property_type`, so a
target attribute sourced from overrides must be translated into the code space
or no comparable ever matches (ADR-0031, the wiring-handover "every cohort
comes back empty" gotcha).
Codes are the gov RdSAP/SAP table values in `datatypes/epc/domain/epc_codes.csv`.
This module owns "unresolvable": a value that maps to no code returns None.
"""
from __future__ import annotations
from typing import Optional
# property_type codes (epc_codes.csv, `property_type` rows — stable across the
# RdSAP/SAP schemas that carry each member). "Park home" exists only from
# SAP-17.0 / RdSAP-17.0 onward; the code itself is stable where present.
_PROPERTY_TYPE_CODES: dict[str, str] = {
"House": "0",
"Bungalow": "1",
"Flat": "2",
"Maisonette": "3",
"Park home": "4",
}
# built_form codes (epc_codes.csv, `built_form` rows). "Not Recorded" lodges as
# the non-numeric "NR", but cohort comparables carry `str(int)` for built_form,
# so an "NR" target could never match — and built_form is the SOFT filter, so a
# non-match only widens the cohort. We therefore treat "Not Recorded" (and the
# classifier "Unknown") as "no usable built-form signal" → None.
_BUILT_FORM_CODES: dict[str, str] = {
"Detached": "1",
"Semi-Detached": "2",
"End-Terrace": "3",
"Mid-Terrace": "4",
"Enclosed End-Terrace": "5",
"Enclosed Mid-Terrace": "6",
}
def property_type_to_code(override_value: str) -> Optional[str]:
"""The gov-EPC `property_type` code for a Landlord-Override value, or None
when it has no code ("Unknown", or any unmapped value) — which gates the
Property out of prediction, as `property_type` is the hard cohort filter."""
return _PROPERTY_TYPE_CODES.get(override_value)
def built_form_to_code(override_value: str) -> Optional[str]:
"""The gov-EPC `built_form` code for a Landlord-Override value, or None when
it has no usable code ("Unknown", "Not Recorded", or any unmapped value).
built_form is the soft filter, so None simply leaves it unconditioned."""
return _BUILT_FORM_CODES.get(override_value)