From a169280b28dee5dd22b462d0ea50d29a57a84036 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Thu, 16 Apr 2026 14:22:26 +0000 Subject: [PATCH] =?UTF-8?q?Load=20HeatingAndHotWater=20from=20SiteNotes=20?= =?UTF-8?q?JSON=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/documents_parser/extractor.py | 59 +++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/backend/documents_parser/extractor.py b/backend/documents_parser/extractor.py index dd19746c..1e5531d4 100644 --- a/backend/documents_parser/extractor.py +++ b/backend/documents_parser/extractor.py @@ -47,10 +47,14 @@ class PasHubRdSapSiteNotesExtractor: except (ValueError, IndexError): return None - def _bool_in(self, lst: List[str], key: str) -> bool: - val = self._get_in(lst, key) + def _bool_in(self, lst: List[str], key: str, offset: int = 1) -> bool: + val = self._get_in(lst, key, offset) return val is not None and val.lower() == "yes" + def _optional_bool_in(self, lst: List[str], key: str) -> Optional[bool]: + val = self._get_in(lst, key) + return None if val is None else val.lower() == "yes" + def _is_known_in(self, lst: List[str], key: str) -> bool: val = self._get_in(lst, key) return val is not None and val.lower() != "not known" @@ -283,7 +287,56 @@ class PasHubRdSapSiteNotesExtractor: return windows def extract_heating_and_hot_water(self) -> HeatingAndHotWater: - raise NotImplementedError + hhw_section = self._section("Heating & Hot Water", "Ventilation") + return HeatingAndHotWater( + main_heating=self._parse_main_heating(hhw_section), + secondary_heating=self._parse_secondary_heating(hhw_section), + water_heating=self._parse_water_heating(hhw_section), + ) + + def _parse_main_heating(self, data: List[str]) -> MainHeating: + return MainHeating( + selection_method=self._get_in(data, "How would you like to select the Heating System?") or "", + system_type=self._get_in(data, "System type:") or "", + product_id=int(self._get_in(data, "Product Id") or 0), + manufacturer=self._get_in(data, "Manufacturer") or "", + model=self._get_in(data, "Model") or "", + orig_manufacturer=self._get_in(data, "Orig Manuf") or "", + fuel=self._get_in(data, "Fuel") or "", + summer_efficiency=float(self._get_in(data, "S. Efficiency") or 0), + type=self._get_in(data, "Type") or "", + condensing=self._bool_in(data, "Condensing"), + year=self._get_in(data, "Year") or "", + mount=self._get_in(data, "Mount") or "", + open_flue=self._get_in(data, "Open Flue") or "", + fan_assist=self._bool_in(data, "Fan Assist"), + status=self._get_in(data, "Status") or "", + central_heating_pump_age=self._get_in(data, "Central heating pump age:") or "", + controls=self._get_in(data, "Controls:") or "", + flue_gas_heat_recovery_system=self._bool_in( + data, "Does the boiler have a Flue Gas Heat Recover", offset=2 + ), + weather_compensator=self._bool_in(data, "Is there a weather compensator?"), + emitter=self._get_in(data, "Emitter:") or "", + emitter_temperature=self._get_in(data, "Emitter Temperature:") or "", + ) + + def _parse_secondary_heating(self, data: List[str]) -> SecondaryHeating: + return SecondaryHeating( + secondary_fuel=self._get_in(data, "Secondary Fuel") or "", + ) + + def _parse_water_heating(self, data: List[str]) -> WaterHeating: + thickness_raw = self._get_in(data, "Insulation Thickness (mm):") + return WaterHeating( + type=self._get_in(data, "Water Heating Type:") or "", + system=self._get_in(data, "Water Heating System:") or "", + cylinder_size=self._get_in(data, "Cylinder Size:") or "", + cylinder_measured_heat_loss=self._get_in(data, "Cylinder Measured Heat Loss:"), + insulation_type=self._get_in(data, "Insulation Type:"), + insulation_thickness_mm=int(thickness_raw) if thickness_raw else None, + has_thermostat=self._optional_bool_in(data, "Cylinder Thermostat:"), + ) def _parse_window(self, window_id: int, data: List[str]) -> Window: height_raw = self._get_in(data, "Window height:")