Merge pull request #1387 from Hestia-Homes/fix/rdsap-mapper-lighting-parity

Normalize RdSAP lighting mapping on the degraded 17.0/19.0 paths
This commit is contained in:
Jun-te Kim 2026-07-01 13:48:20 +01:00 committed by GitHub
commit 366adaf8c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 77 additions and 2 deletions

View file

@ -603,9 +603,18 @@ class EpcPropertyDataMapper:
insulated_door_count=schema.insulated_door_count,
draughtproofed_door_count=None,
percent_draughtproofed=schema.percent_draughtproofed,
# ADR-0028: the schema lodges total + low-energy OUTLET counts, not a
# bulb split — mirror the 17.1/18.0/20.0 paths (low-energy outlets →
# low_energy bulbs; the remainder → incandescent). Hardcoding all
# bulb counts to 0 dropped the lodged lighting mix, understating the
# low-energy lighting credit in the SAP lighting-energy calc.
led_fixed_lighting_bulbs_count=0,
cfl_fixed_lighting_bulbs_count=0,
incandescent_fixed_lighting_bulbs_count=0,
incandescent_fixed_lighting_bulbs_count=(
schema.fixed_lighting_outlets_count
- schema.low_energy_fixed_lighting_outlets_count
),
low_energy_fixed_lighting_bulbs_count=schema.low_energy_fixed_lighting_outlets_count,
roofs=EpcPropertyDataMapper._map_energy_elements(schema.roofs),
walls=EpcPropertyDataMapper._map_energy_elements(schema.walls),
floors=EpcPropertyDataMapper._map_energy_elements(schema.floors),
@ -1577,9 +1586,18 @@ class EpcPropertyDataMapper:
insulated_door_count=schema.insulated_door_count,
draughtproofed_door_count=None,
percent_draughtproofed=schema.percent_draughtproofed,
# ADR-0028: the schema lodges total + low-energy OUTLET counts, not a
# bulb split — mirror the 17.1/18.0/20.0 paths (low-energy outlets →
# low_energy bulbs; the remainder → incandescent). Hardcoding all
# bulb counts to 0 dropped the lodged lighting mix, understating the
# low-energy lighting credit in the SAP lighting-energy calc.
led_fixed_lighting_bulbs_count=0,
cfl_fixed_lighting_bulbs_count=0,
incandescent_fixed_lighting_bulbs_count=0,
incandescent_fixed_lighting_bulbs_count=(
schema.fixed_lighting_outlets_count
- schema.low_energy_fixed_lighting_outlets_count
),
low_energy_fixed_lighting_bulbs_count=schema.low_energy_fixed_lighting_outlets_count,
roofs=EpcPropertyDataMapper._map_energy_elements(schema.roofs),
walls=EpcPropertyDataMapper._map_energy_elements(schema.walls),
floors=EpcPropertyDataMapper._map_energy_elements(schema.floors),

View file

@ -0,0 +1,57 @@
"""Regression: the degraded RdSAP paths (17.0, 19.0) must carry the lodged
fixed-lighting outlet counts, like the reference-complete 17.1/18.0/20.0 paths.
PRD #1385 (mapper normalization), lighting family. Both paths hardcoded every
bulb count to 0, dropping the lodged lighting mix. The schema lodges total +
low-energy OUTLET counts (ADR-0028); the reference paths split them as
low-energy low_energy bulbs and the remainder incandescent, feeding the SAP
lighting-energy calc. (21.0.x use a different bulb-based lighting schema and are
out of scope for this slice.)
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
# 17.0 cert: fixed_lighting_outlets_count=10, low_energy=5 → incandescent 5.
_FIXTURE_17_0 = Path("tests/fixtures/epc_prediction/PE71NT/cert-26619d8e7f8e.json")
# 19.0 cert: fixed_lighting_outlets_count=8, low_energy=5 → incandescent 3.
_FIXTURE_19_0 = Path("tests/fixtures/epc_prediction/PE71NT/cert-21b634bc4b59.json")
def _load(path: Path, schema_type: str) -> dict[str, Any]:
data: dict[str, Any] = json.loads(path.read_text())
assert data["schema_type"] == schema_type
return data
def test_17_0_path_carries_lighting_outlet_counts() -> None:
data = _load(_FIXTURE_17_0, "RdSAP-Schema-17.0")
assert data["fixed_lighting_outlets_count"] == 10
assert data["low_energy_fixed_lighting_outlets_count"] == 5
epc = EpcPropertyDataMapper.from_api_response(data)
# Was hardcoded 0/0/0. Low-energy outlets carry through; the remainder is
# incandescent; led/cfl stay 0 (reduced-field certs don't split those).
assert epc.low_energy_fixed_lighting_bulbs_count == 5
assert epc.incandescent_fixed_lighting_bulbs_count == 5
assert epc.led_fixed_lighting_bulbs_count == 0
assert epc.cfl_fixed_lighting_bulbs_count == 0
def test_19_0_path_carries_lighting_outlet_counts() -> None:
data = _load(_FIXTURE_19_0, "RdSAP-Schema-19.0")
assert data["fixed_lighting_outlets_count"] == 8
assert data["low_energy_fixed_lighting_outlets_count"] == 5
epc = EpcPropertyDataMapper.from_api_response(data)
assert epc.low_energy_fixed_lighting_bulbs_count == 5
assert epc.incandescent_fixed_lighting_bulbs_count == 3
assert epc.led_fixed_lighting_bulbs_count == 0
assert epc.cfl_fixed_lighting_bulbs_count == 0