Merge pull request #1398 from Hestia-Homes/fix/rdsap-insulated-door-u-value

Normalize RdSAP insulated_door_u_value on the 20.0.0 + 21.0.0 paths
This commit is contained in:
Jun-te Kim 2026-07-01 15:49:24 +01:00 committed by GitHub
commit 78da4f390e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 0 deletions

View file

@ -1913,6 +1913,10 @@ class EpcPropertyDataMapper:
insulated_door_count=schema.insulated_door_count,
draughtproofed_door_count=None,
percent_draughtproofed=schema.percent_draughtproofed,
# 20.0.0 lodges insulated_door_u_value but the mapper dropped it,
# unlike 21.0.1. heat_transmission reads it as the measured door
# U-value override; without it every door falls to the RdSAP default.
insulated_door_u_value=schema.insulated_door_u_value,
# ADR-0027: 20.0.0 has no flue/fan/vent counts (calculator defaults
# them via RdSAP Table 5), but sheltered_sides must come from
# built_form — else the calculator assumes mid-terrace (2) for all.
@ -2187,6 +2191,10 @@ class EpcPropertyDataMapper:
schema.mechanical_ventilation_index_number
),
mechanical_vent_duct_type=schema.mechanical_vent_duct_type,
# 21.0.0 lodges insulated_door_u_value but the mapper dropped it,
# unlike 21.0.1. heat_transmission reads it as the measured door
# U-value override; without it every door falls to the RdSAP default.
insulated_door_u_value=schema.insulated_door_u_value,
led_fixed_lighting_bulbs_count=schema.led_fixed_lighting_bulbs_count or 0,
cfl_fixed_lighting_bulbs_count=schema.cfl_fixed_lighting_bulbs_count or 0,
incandescent_fixed_lighting_bulbs_count=schema.incandescent_fixed_lighting_bulbs_count,

View file

@ -0,0 +1,49 @@
"""Regression: the 20.0.0 and 21.0.0 mappers must carry the lodged
insulated_door_u_value, like the 21.0.1 path.
PRD #1385 phase 2. Both dropped `insulated_door_u_value` (top-level Optional
float). heat_transmission reads it as the measured door U-value override
(`cert_to_inputs` `heat_transmission`), so dropping it forced every door onto
the RdSAP default U-value.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
# 21.0.0 sample lodges insulated_door_u_value=3.
_FIXTURE_21_0_0 = Path("backend/epc_api/json_samples/RdSAP-Schema-21.0.0/epc.json")
# 20.0.0 certs don't lodge it in the corpus (latent) — inject to prove the path.
_FIXTURE_20_0_0 = Path("tests/fixtures/epc_prediction/PE71NT/cert-460c2d58495c.json")
def test_21_0_0_carries_insulated_door_u_value() -> None:
data: dict[str, Any] = json.loads(_FIXTURE_21_0_0.read_text())
assert data["schema_type"] == "RdSAP-Schema-21.0.0"
assert data["insulated_door_u_value"] == 3
epc = EpcPropertyDataMapper.from_api_response(data)
assert epc.insulated_door_u_value == 3
def test_20_0_0_carries_injected_insulated_door_u_value() -> None:
data: dict[str, Any] = json.loads(_FIXTURE_20_0_0.read_text())
assert data["schema_type"] == "RdSAP-Schema-20.0.0"
data["insulated_door_u_value"] = 1.5
epc = EpcPropertyDataMapper.from_api_response(data)
# Was hardcoded away (None); now carried through.
assert epc.insulated_door_u_value == 1.5
def test_20_0_0_absent_insulated_door_u_value_maps_to_none() -> None:
# The real cert lodges no value → None (no phantom U-value).
data: dict[str, Any] = json.loads(_FIXTURE_20_0_0.read_text())
assert data.get("insulated_door_u_value") is None
epc = EpcPropertyDataMapper.from_api_response(data)
assert epc.insulated_door_u_value is None