diff --git a/backend/documents_parser/tests/test_extractor.py b/backend/documents_parser/tests/test_extractor.py index 934488614..21a68c24b 100644 --- a/backend/documents_parser/tests/test_extractor.py +++ b/backend/documents_parser/tests/test_extractor.py @@ -985,6 +985,92 @@ class TestRoofSpaceRoomInRoof: assert rir.flat_ceilings is not None assert [f.insulation_known for f in rir.flat_ceilings] == [False] + # The Simplified-approach RIR block ("Are details of the room in roof + # known? No" → "Roof room type: RR type 2"), gables + common walls only, + # no slopes/flat ceilings. Verbatim from accuracy fixture 499538003156 + # (78 North Road), including the mid-block "Page 8" break and the noise + # lines the surveyor tool interleaves between a key and its value. + _RIR_SIMPLIFIED_TYPE2_BLOCK = [ + "Are there rooms in the roof?", + "Yes", + "Roof room age range:", + "C: 1930 - 1949", + "Floor area of room in roof:", + "25.43 m²", + "Are details of the room in roof known?", + "No", + "Roof room type:", + "RR type 2 (With the Accessible areas of continuous common walls)", + "Gable wall 1 length:", + "5.82 m", + "Gable wall 1 height:", + "2.72 m", + "Gable wall 1 type:", + "Party", + "Gable wall 2 length:", + "5.82 m", + "Gable wall 2 height:", + "2.72 m", + "Gable wall 2 type:", + "Party", + "Common wall 1 length:", + "4.37 m", + "Page 8", + "", + "Common wall 1 height:", + "2.34 m", + "Common wall 2 length:", + "4.37 m", + "Common wall 2 height:", + "2.34 m", + ] + + def test_roof_room_type_label_is_captured(self) -> None: + # Arrange — fixture 1's roof space with the Simplified 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_SIMPLIFIED_TYPE2_BLOCK + lines[idx + 2 :] + + # Act + rir = ( + PasHubRdSapSiteNotesExtractor(spliced) + .extract_roof_space() + .main_building.room_in_roof + ) + + # Assert + assert rir is not None + assert ( + rir.roof_room_type + == "RR type 2 (With the Accessible areas of continuous common walls)" + ) + # Simplified lodgement: gables + common walls, no roof-going surfaces. + assert rir.slopes is None + assert rir.flat_ceilings is None + assert rir.common_walls == [ + RoomInRoofSurfaceDetail(length_m=4.37, height_m=2.34), + RoomInRoofSurfaceDetail(length_m=4.37, height_m=2.34), + ] + + def test_detailed_lodgement_has_no_roof_room_type(self) -> None: + # Arrange — the Detailed §3.10 block (slopes/flat ceilings lodged) + # never carries a "Roof room type:" line. + lines = load_text_fixture() + idx = lines.index("Are there rooms in the roof?") + spliced = lines[:idx] + self._RIR_BLOCK + lines[idx + 2 :] + + # Act + rir = ( + PasHubRdSapSiteNotesExtractor(spliced) + .extract_roof_space() + .main_building.room_in_roof + ) + + # Assert + assert rir is not None + assert rir.roof_room_type is None + def test_no_rooms_in_roof_leaves_detail_absent(self) -> None: # Arrange / Act — fixture 1 as lodged ("No"). detail = PasHubRdSapSiteNotesExtractor( diff --git a/datatypes/epc/surveys/pashub_rdsap_site_notes.py b/datatypes/epc/surveys/pashub_rdsap_site_notes.py index 33c691e5a..8822800d8 100644 --- a/datatypes/epc/surveys/pashub_rdsap_site_notes.py +++ b/datatypes/epc/surveys/pashub_rdsap_site_notes.py @@ -138,6 +138,10 @@ class RoomInRoofDetail: slopes: Optional[List[RoomInRoofSurfaceDetail]] = None common_walls: Optional[List[RoomInRoofSurfaceDetail]] = None flat_ceilings: Optional[List[RoomInRoofSurfaceDetail]] = None + # The "Roof room type:" label, lodged only on the Simplified approach + # ("Are details of the room in roof known? No" → RdSAP §3.9.2 Type 1/2/3). + # None on a Detailed §3.10 lodgement (per-surface slopes/flat ceilings). + roof_room_type: Optional[str] = None @dataclass