From 4fe59b8e78b3bc5407f88187f96b3e471b23534c Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 24 Jun 2026 07:42:34 +0000 Subject: [PATCH] Default RdSAP 21.0.1 wet_rooms_count to 0 (fix NOT-NULL violation) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 37 modelling_e2e properties failed on the 2026-06-23 run with `NotNullViolation: null value in column "wet_rooms_count" of relation "epc_property"`. Root cause: 21.0.1 lodges `wet_rooms_count` as Optional, and `from_rdsap_schema_21_0_1` passed it straight through (`wet_rooms_count=schema.wet_rooms_count`). A cert omitting it mapped to `EpcPropertyData.wet_rooms_count=None`. When a predicted EPC (which deep-copies a comparable template's EpcPropertyData) inherited that None and was persisted, it violated the `epc_property.wet_rooms_count` NOT-NULL column — and the calc's `wet_rooms_count > 0` check would also raise `TypeError` on None. Fix: coalesce to 0, matching every other mapper (RdSAP "not lodged" → the calc's minimum 1 wet room). Regression test added. Co-Authored-By: Claude Opus 4.8 (1M context) --- datatypes/epc/domain/mapper.py | 5 ++++- .../epc/domain/tests/test_from_rdsap_schema.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/datatypes/epc/domain/mapper.py b/datatypes/epc/domain/mapper.py index 2f2bd00b..1658383f 100644 --- a/datatypes/epc/domain/mapper.py +++ b/datatypes/epc/domain/mapper.py @@ -2150,7 +2150,10 @@ class EpcPropertyDataMapper: door_count=schema.door_count, habitable_rooms_count=schema.habitable_room_count, heated_rooms_count=schema.heated_room_count, - wet_rooms_count=schema.wet_rooms_count, + # 21.0.1 lodges wet_rooms_count as Optional; None violates the + # epc_property NOT-NULL column (and the calc's `> 0` check). Coalesce + # to 0 like every other mapper (RdSAP "not lodged" → calc minimum 1). + wet_rooms_count=schema.wet_rooms_count or 0, extensions_count=schema.extensions_count, open_chimneys_count=schema.open_chimneys_count, insulated_door_count=schema.insulated_door_count, diff --git a/datatypes/epc/domain/tests/test_from_rdsap_schema.py b/datatypes/epc/domain/tests/test_from_rdsap_schema.py index e0c6b1ba..023f7c60 100644 --- a/datatypes/epc/domain/tests/test_from_rdsap_schema.py +++ b/datatypes/epc/domain/tests/test_from_rdsap_schema.py @@ -379,6 +379,22 @@ class TestFromRdSapSchema21_0_1: # --- general --- + def test_omitted_wet_rooms_count_defaults_to_zero_not_none(self) -> None: + # 21.0.1 lodges wet_rooms_count as Optional, so a cert that omits it + # mapped to EpcPropertyData.wet_rooms_count=None. That None then + # violated the epc_property.wet_rooms_count NOT-NULL constraint when a + # predicted EPC (which deep-copies a comparable template) was persisted, + # and would crash the calc's `wet_rooms_count > 0` check. 37 modelling_e2e + # properties failed on the 2026-06-23 run for this reason. Default to 0, + # matching every other mapper (RdSAP 0 → calc's Table-?? minimum 1 fan). + data = load("21_0_1.json") + data.pop("wet_rooms_count", None) + schema = from_dict(RdSapSchema21_0_1, data) + + result = EpcPropertyDataMapper.from_rdsap_schema_21_0_1(schema) + + assert result.wet_rooms_count == 0 + def test_uprn(self, result: EpcPropertyData) -> None: assert result.uprn == 12457