From 518c682793647237a95cef29cdca60b3f71b8113 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 14 Jul 2026 22:05:31 +0000 Subject: [PATCH] =?UTF-8?q?Extract=20the=20PasHub=20detailed=20room-in-roo?= =?UTF-8?q?f=20block=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../documents_parser/tests/test_extractor.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/backend/documents_parser/tests/test_extractor.py b/backend/documents_parser/tests/test_extractor.py index 947c6e44d..00275b820 100644 --- a/backend/documents_parser/tests/test_extractor.py +++ b/backend/documents_parser/tests/test_extractor.py @@ -22,6 +22,7 @@ from datatypes.epc.surveys.pashub_rdsap_site_notes import ( MainBuildingMeasurements, MainHeating, PvArrayDetail, + RoomInRoofSurfaceDetail, Renewables, RoomCountElements, RoofSpace, @@ -705,6 +706,116 @@ class TestRenewablesPvArrays: assert renewables.pv_arrays is None +class TestRoofSpaceRoomInRoof: + """"Are there rooms in the roof? Yes" lodges an RdSAP §3.10 detailed RIR + block (age, floor area, gables/slopes/common walls/flat ceiling as + length × height). The extractor must capture it — dropped, ~32 m² of RIR + surfaces are billed as well-insulated loft (issue #1590 bug 5; lines + verbatim from accuracy fixture deal 499617935574, incl. the mid-block + "Page 7" break). + """ + + _RIR_BLOCK = [ + "Are there rooms in the roof?", + "Yes", + "Roof room age range:", + "B: 1900 - 1929", + "Floor area of room in roof:", + "32.35 m²", + "Are details of the room in roof known?", + "Yes", + "Are details of the gable walls known?", + "Gable wall 1 & 2", + "Gable wall 1 length:", + "6.64 m", + "Gable wall 1 height:", + "2.74 m", + "Page 7", + "Gable wall 1 U-Value:", + "Unknown", + "Gable wall 1 type:", + "Exposed", + "Gable wall 2 length:", + "6.64 m", + "Gable wall 2 height:", + "2.74 m", + "Gable wall 2 type:", + "Party", + "Are details of the stud walls known?", + "None", + "Are details of the slope walls known?", + "Slope 1 & 2", + "Slope 1 length:", + "4.94 m", + "Slope 1 height:", + "1.73 m", + "Slope 2 length:", + "5 m", + "Slope 2 height:", + "2.95 m", + "Are details of the common walls known?", + "Common wall 1 & 2", + "Common wall 1 length:", + "4.94 m", + "Common wall 1 height:", + "1.45 m", + "Common wall 2 length:", + "4.94 m", + "Common wall 2 height:", + "1.3 m", + "Are details of the flat ceiling known?", + "Flat ceiling 1", + "Flat ceiling 1 length:", + "4.94 m", + "Flat ceiling 1 height:", + "5.17 m", + ] + + def test_room_in_roof_block_is_captured(self) -> None: + # Arrange — fixture 1's roof space with the RIR block spliced in place + # of its "rooms in the roof? No" lodging. + lines = load_text_fixture() + idx = lines.index("Are there rooms in the roof?") + spliced = lines[:idx] + self._RIR_BLOCK + lines[idx + 2 :] + + # Act + detail = PasHubRdSapSiteNotesExtractor( + spliced + ).extract_roof_space().main_building + + # Assert + assert detail.rooms_in_roof is True + rir = detail.room_in_roof + assert rir is not None + assert rir.age_range == "B: 1900 - 1929" + assert rir.floor_area_m2 == 32.35 + assert rir.gables == [ + RoomInRoofSurfaceDetail(length_m=6.64, height_m=2.74, gable_type="Exposed"), + RoomInRoofSurfaceDetail(length_m=6.64, height_m=2.74, gable_type="Party"), + ] + assert rir.slopes == [ + RoomInRoofSurfaceDetail(length_m=4.94, height_m=1.73), + RoomInRoofSurfaceDetail(length_m=5.0, height_m=2.95), + ] + assert rir.common_walls == [ + RoomInRoofSurfaceDetail(length_m=4.94, height_m=1.45), + RoomInRoofSurfaceDetail(length_m=4.94, height_m=1.3), + ] + assert rir.flat_ceilings == [ + RoomInRoofSurfaceDetail(length_m=4.94, height_m=5.17), + ] + + def test_no_rooms_in_roof_leaves_detail_absent(self) -> None: + # Arrange / Act — fixture 1 as lodged ("No"). + detail = PasHubRdSapSiteNotesExtractor( + load_text_fixture() + ).extract_roof_space().main_building + + # Assert + assert detail.rooms_in_roof is False + assert detail.room_in_roof is None + + class TestRoomCountElements: @pytest.fixture def rce(self) -> RoomCountElements: