mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
294 lines
11 KiB
Python
294 lines
11 KiB
Python
import json
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from backend.documents_parser.extractor import PasHubRdSapSiteNotesExtractor
|
|
from datatypes.epc.surveys.pashub_rdsap_site_notes import (
|
|
BuildingConstruction,
|
|
BuildingMeasurements,
|
|
ExtensionConstruction,
|
|
ExtensionMeasurements,
|
|
ExtensionRoofSpace,
|
|
FloorConstruction,
|
|
FloorMeasurement,
|
|
General,
|
|
MainBuildingConstruction,
|
|
MainBuildingMeasurements,
|
|
PasHubRdSapSiteNotes,
|
|
RoofSpace,
|
|
RoofSpaceDetail,
|
|
)
|
|
|
|
FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")
|
|
|
|
|
|
def load_text_fixture() -> list[str]:
|
|
with open(os.path.join(FIXTURES, "site_notes_example_text.json")) as f:
|
|
return json.load(f)
|
|
|
|
|
|
class TestGeneral:
|
|
@pytest.fixture
|
|
def general(self) -> General:
|
|
return PasHubRdSapSiteNotesExtractor(load_text_fixture()).extract_general()
|
|
|
|
def test_epc_checked_before_assessment(self, general: General) -> None:
|
|
assert general.epc_checked_before_assessment is True
|
|
|
|
def test_epc_exists_at_point_of_assessment(self, general: General) -> None:
|
|
assert general.epc_exists_at_point_of_assessment is False
|
|
|
|
def test_inspection_date(self, general: General) -> None:
|
|
assert general.inspection_date == "2025-09-25"
|
|
|
|
def test_transaction_type(self, general: General) -> None:
|
|
assert general.transaction_type == "Grant-Scheme (ECO, RHI, etc.)"
|
|
|
|
def test_tenure(self, general: General) -> None:
|
|
assert general.tenure == "Rented Social"
|
|
|
|
def test_property_type(self, general: General) -> None:
|
|
assert general.property_type == "House"
|
|
|
|
def test_detachment_type(self, general: General) -> None:
|
|
assert general.detachment_type == "Mid-terrace"
|
|
|
|
def test_number_of_storeys(self, general: General) -> None:
|
|
assert general.number_of_storeys == 2
|
|
|
|
def test_number_of_extensions(self, general: General) -> None:
|
|
assert general.number_of_extensions == 1
|
|
|
|
def test_electricity_smart_meter(self, general: General) -> None:
|
|
assert general.electricity_smart_meter is True
|
|
|
|
def test_mains_gas_available(self, general: General) -> None:
|
|
assert general.mains_gas_available is True
|
|
|
|
def test_measurements_location(self, general: General) -> None:
|
|
assert general.measurements_location == "Internal"
|
|
|
|
def test_full_general(self, general: General) -> None:
|
|
assert general == General(
|
|
epc_checked_before_assessment=True,
|
|
epc_exists_at_point_of_assessment=False,
|
|
inspection_date="2025-09-25",
|
|
transaction_type="Grant-Scheme (ECO, RHI, etc.)",
|
|
tenure="Rented Social",
|
|
property_type="House",
|
|
detachment_type="Mid-terrace",
|
|
number_of_storeys=2,
|
|
terrain_type="Suburban",
|
|
number_of_extensions=1,
|
|
electricity_smart_meter=True,
|
|
electric_meter_type="Single",
|
|
dwelling_export_capable=True,
|
|
mains_gas_available=True,
|
|
gas_smart_meter=True,
|
|
gas_meter_accessible=True,
|
|
measurements_location="Internal",
|
|
)
|
|
|
|
|
|
class TestBuildingConstruction:
|
|
@pytest.fixture
|
|
def construction(self) -> BuildingConstruction:
|
|
return PasHubRdSapSiteNotesExtractor(
|
|
load_text_fixture()
|
|
).extract_building_construction()
|
|
|
|
def test_main_building_wall_u_value_known_is_false(
|
|
self, construction: BuildingConstruction
|
|
) -> None:
|
|
assert construction.main_building.wall_u_value_known is False
|
|
|
|
def test_main_building_wall_thickness_mm(
|
|
self, construction: BuildingConstruction
|
|
) -> None:
|
|
assert construction.main_building.wall_thickness_mm == 310
|
|
|
|
def test_main_building_filled_cavity_indicators_present(
|
|
self, construction: BuildingConstruction
|
|
) -> None:
|
|
assert (
|
|
construction.main_building.filled_cavity_indicators
|
|
== "evidence of cavity fill drill holes"
|
|
)
|
|
|
|
def test_extension_filled_cavity_indicators_absent(
|
|
self, construction: BuildingConstruction
|
|
) -> None:
|
|
assert construction.extensions is not None
|
|
assert construction.extensions[0].filled_cavity_indicators is None
|
|
|
|
def test_one_extension(self, construction: BuildingConstruction) -> None:
|
|
assert construction.extensions is not None
|
|
assert len(construction.extensions) == 1
|
|
|
|
def test_extension_id(self, construction: BuildingConstruction) -> None:
|
|
assert construction.extensions is not None
|
|
assert construction.extensions[0].id == 1
|
|
|
|
def test_full_building_construction(
|
|
self, construction: BuildingConstruction
|
|
) -> None:
|
|
assert construction == BuildingConstruction(
|
|
main_building=MainBuildingConstruction(
|
|
age_range="1950-1966",
|
|
age_indicators="local knowledge, enquiries of owner",
|
|
walls_construction_type="Cavity",
|
|
cavity_construction_indicators="wall thickness over 270 mm",
|
|
walls_insulation_type="Filled Cavity",
|
|
filled_cavity_indicators="evidence of cavity fill drill holes",
|
|
thermal_conductivity_of_wall_insulation="Unknown",
|
|
wall_u_value_known=False,
|
|
wall_thickness_mm=310,
|
|
party_wall_construction_type="Cavity Masonry, Filled",
|
|
),
|
|
floor=FloorConstruction(
|
|
floor_type="Ground Floor",
|
|
floor_construction="Solid",
|
|
floor_insulation_type="As Built",
|
|
floor_u_value_known=False,
|
|
),
|
|
extensions=[
|
|
ExtensionConstruction(
|
|
id=1,
|
|
age_range="2003-2006",
|
|
age_indicators="local knowledge, enquiries of owner",
|
|
walls_construction_type="Cavity",
|
|
cavity_construction_indicators="wall thickness over 270 mm",
|
|
walls_insulation_type="As built",
|
|
thermal_conductivity_of_wall_insulation="Unknown",
|
|
wall_u_value_known=False,
|
|
wall_thickness_mm=310,
|
|
party_wall_construction_type="Cavity Masonry, Filled",
|
|
filled_cavity_indicators=None,
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
class TestBuildingMeasurements:
|
|
@pytest.fixture
|
|
def measurements(self) -> BuildingMeasurements:
|
|
return PasHubRdSapSiteNotesExtractor(
|
|
load_text_fixture()
|
|
).extract_building_measurements()
|
|
|
|
def test_main_building_has_two_floors(
|
|
self, measurements: BuildingMeasurements
|
|
) -> None:
|
|
assert len(measurements.main_building.floors) == 2
|
|
|
|
def test_main_building_floor_area(
|
|
self, measurements: BuildingMeasurements
|
|
) -> None:
|
|
assert measurements.main_building.floors[0].area_m2 == 35.68
|
|
|
|
def test_integer_token_parses_to_float(
|
|
self, measurements: BuildingMeasurements
|
|
) -> None:
|
|
# "11" in the PDF (no decimal) should parse to 11.0
|
|
assert measurements.main_building.floors[1].heat_loss_perimeter_m == 11.0
|
|
|
|
def test_extension_measurements_present(
|
|
self, measurements: BuildingMeasurements
|
|
) -> None:
|
|
assert measurements.extensions is not None
|
|
assert len(measurements.extensions) == 1
|
|
|
|
def test_extension_id(self, measurements: BuildingMeasurements) -> None:
|
|
assert measurements.extensions is not None
|
|
assert measurements.extensions[0].id == 1
|
|
|
|
def test_full_building_measurements(
|
|
self, measurements: BuildingMeasurements
|
|
) -> None:
|
|
assert measurements == BuildingMeasurements(
|
|
main_building=MainBuildingMeasurements(
|
|
floors=[
|
|
FloorMeasurement(
|
|
name="Floor 1",
|
|
area_m2=35.68,
|
|
height_m=2.19,
|
|
heat_loss_perimeter_m=13.44,
|
|
pwl_m=10.62,
|
|
),
|
|
FloorMeasurement(
|
|
name="Floor 0",
|
|
area_m2=35.68,
|
|
height_m=2.17,
|
|
heat_loss_perimeter_m=11.0,
|
|
pwl_m=10.62,
|
|
),
|
|
]
|
|
),
|
|
extensions=[
|
|
ExtensionMeasurements(
|
|
id=1,
|
|
floors=[
|
|
FloorMeasurement(
|
|
name="Floor 0",
|
|
area_m2=3.8,
|
|
height_m=2.0,
|
|
heat_loss_perimeter_m=5.7,
|
|
pwl_m=0.0,
|
|
)
|
|
],
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
class TestRoofSpace:
|
|
@pytest.fixture
|
|
def roof_space(self) -> RoofSpace:
|
|
return PasHubRdSapSiteNotesExtractor(load_text_fixture()).extract_roof_space()
|
|
|
|
def test_main_building_insulation_thickness_mm(
|
|
self, roof_space: RoofSpace
|
|
) -> None:
|
|
assert roof_space.main_building.insulation_thickness_mm == 100
|
|
|
|
def test_main_building_insulation_thickness_string_absent(
|
|
self, roof_space: RoofSpace
|
|
) -> None:
|
|
assert roof_space.main_building.insulation_thickness is None
|
|
|
|
def test_main_building_rooms_in_roof(self, roof_space: RoofSpace) -> None:
|
|
assert roof_space.main_building.rooms_in_roof is False
|
|
|
|
def test_main_building_roof_u_value_known(self, roof_space: RoofSpace) -> None:
|
|
assert roof_space.main_building.roof_u_value_known is False
|
|
|
|
def test_extension_uses_string_thickness(self, roof_space: RoofSpace) -> None:
|
|
assert roof_space.extensions is not None
|
|
assert roof_space.extensions[0].insulation_thickness == "As built"
|
|
assert roof_space.extensions[0].insulation_thickness_mm is None
|
|
|
|
def test_full_roof_space(self, roof_space: RoofSpace) -> None:
|
|
assert roof_space == RoofSpace(
|
|
main_building=RoofSpaceDetail(
|
|
construction_type="Pitched roof (Slates or tiles), Access to loft",
|
|
insulation_at="Joists",
|
|
roof_u_value_known=False,
|
|
cavity_wall_construction_indicators="cavity visible in roof space",
|
|
rooms_in_roof=False,
|
|
insulation_thickness_mm=100,
|
|
insulation_thickness=None,
|
|
),
|
|
extensions=[
|
|
ExtensionRoofSpace(
|
|
id=1,
|
|
construction_type="Pitched roof, Sloping ceiling",
|
|
insulation_at="Sloping ceiling insulation",
|
|
roof_u_value_known=False,
|
|
cavity_wall_construction_indicators="No indicator of construction visible",
|
|
rooms_in_roof=False,
|
|
insulation_thickness_mm=None,
|
|
insulation_thickness="As built",
|
|
)
|
|
],
|
|
)
|