diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index c4a33a514..fe947bf9c 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -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), diff --git a/tests/datatypes/epc/domain/test_mapper_lighting_parity.py b/tests/datatypes/epc/domain/test_mapper_lighting_parity.py new file mode 100644 index 000000000..c3e3316fd --- /dev/null +++ b/tests/datatypes/epc/domain/test_mapper_lighting_parity.py @@ -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