extract window frame details from elmhurst site notes 🟥

This commit is contained in:
Daniel Roth 2026-04-27 15:50:25 +00:00
parent 268576e345
commit 8f94bb5435
4 changed files with 95 additions and 20 deletions

Binary file not shown.

View file

@ -0,0 +1,6 @@
[
"Summary Information\nSurveyor:\nBW22-0001\nName:\nIan Marsh\nTitle:\nTel Number: 07709266472\nSurvey Reference:\n001233\nMy Reference:\nCurrent SAP rating:\nD 68\nPotential SAP rating: A 92\nEmissions (t/year):\n2.812 tonnes\nCurrent EI rating:\nD 68\nPotential EI rating:\nC 76\nFuel Bill:\n\u00a31098\nProperty Details:\nRdSAP version:\nRdSAP10\nReference Number:\nBW22-0001-001233\nMy Reference:\nLodgement Required:\nNo\nRegs Region:\nEngland\nEPC Language:\nEnglish\nUPRN:\nPostcode:\nBB11 2NU\nRegion:\nWest Pennines\nHouse Name:\nHouse No:\n39\nStreet:\nConstable Avenue\nLocality:\nTown:\nBURNLEY\nCounty:\nProperty Tenure:\nRented (social)\nTransaction Type:\nGrant scheme\nInspection Date:\n06/03/2026\nProcess date:\n06/03/2026\nCheck for the existence of\nan EPC:\nNo\nDoes an EPC exist at the\npoint of carrying out this\nenergy assessment:\nNo\nReason why another energy\nassessment needs to be\nundertaken:\nRdSAP Inputs\nProperty Description:\n1.0 Property type:\nH House\nS Semi-Detached\n2.0 Number of\nStoreys:\n2\nHabitable Rooms:\n4\nHeated Habitable Rooms:\n4\n3.0 Date Built:\nMain Property\nD 1950-1966\n4.0 Dimensions:\nDimension type:\nInternal\nMain Property\nFloor\nArea\n[m2]\nRoom\nHeight\n[m]\nHeat Loss\nWall Perimeter\n[m]\nParty Wall\nLength\n[m]\n1st Floor:\n35.88\n2.51\n17.46\n6.62\nLowest Floor:\n35.88\n2.67\n17.46\n6.62\nNo\n5.0 Conservatory:\nIs there a conservatory?\nNo\n7.0 Walls:\nMain Property\nType\nCA Cavity\nInsulation\nF Filled Cavity\nWall Thickness Unknown\nNo\nWall Thickness\n300 mm\nU-value Known\nNo\nParty Wall Type\nCU Cavity masonry unfilled\n\u00a9 Elmhurst Energy Systems Limited Registered Office Unit 16, St Johns Business Park, Lutterworth, Leicestershire LE17 4HB\n",
"Summary Information\n8.0 Roofs:\nMain Property\nType\nPA Pitched (slates/tiles), access to loft\nInsulation\nJ Joists\nInsulation Thickness\n200 mm\nU-value Known\nNo\n8.1 Rooms in Roof:\n9.0 Floors:\nMain Property\nLocation\nG Ground floor\nType\nT Suspended timber\nInsulation\nA As built\nDefault U-value\n0.72\nU-value Known\nNo\n10.0 Doors:\nTotal Number of Doors\n2\nNumber of Insulated Doors\n0\n11.0 Windows:\nW\nH\nArea Glazing Type\nFrame \nType\nFrame \nFactor\nGlazing \nGap\nBuilding \nPart\nLocation\nOrient. Data-Source\nU \nvalue\ng \nvalue\nDraught \nProofed\nPermanent \nShutters\n1.59\n1.36\n2.16\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nEast\nManufacturer\n2.70\n0.76\nYes\nNone\n1.27\n0.43\n0.55\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nEast\nManufacturer\n2.70\n0.76\nYes\nNone\n1.54\n1.06\n1.63\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nEast\nManufacturer\n2.70\n0.76\nYes\nNone\n0.61\n1.07\n0.65\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nSouth\nManufacturer\n2.70\n0.76\nYes\nNone\n1.07\n1.05\n1.12\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nWest\nManufacturer\n2.70\n0.76\nYes\nNone\n1.07\n1.08\n1.16\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nWest\nManufacturer\n2.70\n0.76\nYes\nNone\n1.10\n1.06\n1.17\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nWest\nManufacturer\n2.70\n0.76\nYes\nNone\n1.12\n1.06\n1.19\nDouble with unknown \ninstall date\nPVC\n0.70\n16 mm or \nmore\nMain\nExternal wall\nWest\nManufacturer\n2.70\n0.76\nYes\nNone\nDraught Proofing\n90 %\n12.0 Ventilation & Cooling\nNo. of open chimneys\n0\nNo. of open flues\n0\nNo. of open chimneys/open flues attached to closed fire\n0\nNo. of flues attached to solid fuel boiler\n0\nNo. of open flues attached to other heater\n0\nNo. of blocked chimneys\n0\nNo. of intermittent extract fans\n2\nNo. of passive vents\n2\nNo. of flueless gas fires\n0\nFixed Space Cooling\nNo\nDraught Lobby\nNot present\n12.1 Mechanical Ventilation\nMechanical Ventilation\nNo\n12.2 Air Pressure Test\nTest Method\nNot available\n13.0 Lighting\nTotal number of bulbs\n10\nNumber of LED and CFL Known\nNo\nTotal number of Low Energy\n5\nTotal number of incandescents\n5\n\u00a9 Elmhurst Energy Systems Limited Registered Office Unit 16, St Johns Business Park, Lutterworth, Leicestershire LE17 4HB\n",
"Summary Information\n14.0 Main Heating1\nPCDF boiler Reference\n18737 Baxi, ASSURE, 88.40%\nHeat Emitter\nRadiators\nHeat pump age\nUnknown\nFuel Type\nMains gas\nFlue Type\nBalanced\nFan Assisted Flue\nYes\nDesign flow temperature\nUnknown\nPCDF Heating Controls\n0 \nMain Heating Controls EES\nCBE\nMain Heating Controls Sap\nSAP code 2106, Programmer, room thermostat and TRVs\nPCDF Compensator\n0 \nPercentage of Heat\n100 %\n14.1 Main Heating2\nPCDF boiler Reference\n0 \nMain Heating EES Code\nMain Heating SAP Code\n0\nPercentage of Heat\n0 %\n14.1 Community Heating/Heat Network\nHeating Type\nNone\n14.2 Meters\nElectricity meter type\nSingle\nMain gas\nYes\nElectricity Smart Meter Present\nNo\nGas Smart Meter Present\nNo\n15.0 Water Heating\nWater Heating Code\nHWP\nWater Heating SapCode\n901\nWater Heating Fuel Type\nMains gas\n15.1 Hot Water Cylinder\nHot Water Cylinder Present\nNo\n15.2 Community Hot Water\nPCDF boiler Reference\n0\n16.0 Solar water heating\nSolar Water Heating\nNo\n17.0 Waste Water Heat Recovery System\nIs WWHRS present in the property?\nNo / Unknown\n1x.0 Baths and Showers\nTotal Number of Baths\n1\nNumber of Baths Connected\n0\nDescription\nType\nConnected\n1\nNon-electric shower\nNone\n18.0 Flue Gas Heat Recovery System\nPresent\nNo\n19.0 Photovoltaic Panel\nPhotovoltaic Panel\nNone\nExport capable meter\nNo\n20.0 Wind Turbine\nTerrain Type\nRural\nWind turbine present?\nNo\n22.0 Special Features\n21.0 Small-Scale Hydro\nElectricity generated [kWh/year]\n0.00\n\u00a9 Elmhurst Energy Systems Limited Registered Office Unit 16, St Johns Business Park, Lutterworth, Leicestershire LE17 4HB\n",
"Summary Information\nRecommendations\nLoft insulation (Already installed)\nFlat roof insulation (Not applicable)\nRoom-in-roof insulation (Not applicable)\nCavity wall insulation (Already installed)\nSolid wall insulation (Not applicable)\nFloor insulation (suspended floor) (Recommended)\nHot water cylinder insulation (Not applicable)\nDraught proofing (SAP increase too small)\nLow energy lighting (Recommended)\nCylinder thermostat (Not applicable)\nHeating controls for wet central heating system (Already installed)\nUpgrade boiler, same fuel (Already installed)\nChange heating to condensing gas condensing boiler (fuel switch) (Not applicable)\nFlue gas heat recovery in conjunction with new boiler (Not applicable)\nSolar water heating (SAP increase too small)\nHeat recovery system for mixer showers (SAP increase too small)\nDouble glazed windows (Already installed)\nInsulated doors (SAP increase too small)\nSolar photovoltaic panels (Recommended)\nWind turbine (Recommended)\nPV diverter (Not applicable)\nPV battery (Not applicable)\nWater heating controls (Not applicable)\nAlternative Recommendations\nExternal wall insulation with cavity insulation (Not applicable)\nBiomass boiler (alternative) (Not applicable)\nMicro CHP (alternative) (Not applicable)\nRelated Party Disclosure\nAddenda\n\u00a9 Elmhurst Energy Systems Limited Registered Office Unit 16, St Johns Business Park, Lutterworth, Leicestershire LE17 4HB\n"
]

View file

@ -28,6 +28,9 @@ from datatypes.epc.surveys.elmhurst_site_notes import (
FIXTURE_PATH = os.path.join(
os.path.dirname(__file__), "fixtures", "elmhurst_site_notes_1_text.json"
)
FIXTURE_PATH_2 = os.path.join(
os.path.dirname(__file__), "fixtures", "elmhurst_site_notes_2_text.json"
)
@pytest.fixture(scope="module")
@ -37,6 +40,13 @@ def result() -> ElmhurstSiteNotes:
return ElmhurstSiteNotesExtractor(pages).extract()
@pytest.fixture(scope="module")
def result2() -> ElmhurstSiteNotes:
with open(FIXTURE_PATH_2) as f:
pages = json.load(f)
return ElmhurstSiteNotesExtractor(pages).extract()
class TestSurveyorInfo:
def test_surveyor_code(self, result: ElmhurstSiteNotes) -> None:
assert result.surveyor_info.surveyor_code == "P960-0001"
@ -448,3 +458,58 @@ class TestEnergyPerformance:
def test_co2_emissions_current_t(self, result: ElmhurstSiteNotes) -> None:
assert result.co2_emissions_current_t == 1.683
class TestWindowsWithFrameDetails:
def test_window_count(self, result2: ElmhurstSiteNotes) -> None:
assert len(result2.windows) == 8
def test_draught_proofing_percent(self, result2: ElmhurstSiteNotes) -> None:
assert result2.draught_proofing_percent == 90
def test_first_window_glazing_type_excludes_frame_type(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[0].glazing_type == "Double with unknown install date"
def test_first_window_frame_type(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[0].frame_type == "PVC"
def test_first_window_frame_factor(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[0].frame_factor == 0.70
def test_first_window_glazing_gap(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[0].glazing_gap == "16 mm or more"
def test_first_window_location(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[0].building_part == "Main"
assert result2.windows[0].location == "External wall"
assert result2.windows[0].orientation == "East"
def test_first_window_performance(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[0].data_source == "Manufacturer"
assert result2.windows[0].u_value == 2.70
assert result2.windows[0].g_value == 0.76
assert result2.windows[0].draught_proofed is True
assert result2.windows[0].permanent_shutters == "None"
def test_fourth_window_orientation(self, result2: ElmhurstSiteNotes) -> None:
assert result2.windows[3].orientation == "South"
class TestLightingLedCflUnknown:
def test_total_bulbs(self, result2: ElmhurstSiteNotes) -> None:
assert result2.lighting.total_bulbs == 10
def test_led_cfl_count_known_false(self, result2: ElmhurstSiteNotes) -> None:
assert result2.lighting.led_cfl_count_known is False
def test_low_energy_count(self, result2: ElmhurstSiteNotes) -> None:
assert result2.lighting.low_energy_count == 5
def test_incandescent_count(self, result2: ElmhurstSiteNotes) -> None:
assert result2.lighting.incandescent_count == 5
def test_led_count_zero_when_unknown(self, result2: ElmhurstSiteNotes) -> None:
assert result2.lighting.led_count == 0
def test_cfl_count_zero_when_unknown(self, result2: ElmhurstSiteNotes) -> None:
assert result2.lighting.cfl_count == 0

View file

@ -53,27 +53,27 @@ class BuildingPartDimensions:
@dataclass
class WallDetails:
wall_type: str # e.g. "CA Cavity"
insulation: str # e.g. "F Filled Cavity"
wall_type: str # e.g. "CA Cavity"
insulation: str # e.g. "F Filled Cavity"
thickness_unknown: bool
u_value_known: bool
party_wall_type: str # e.g. "U Unable to determine"
party_wall_type: str # e.g. "U Unable to determine"
thickness_mm: Optional[int] = None
@dataclass
class RoofDetails:
roof_type: str # e.g. "PA Pitched (slates/tiles), access to loft"
insulation: str # e.g. "J Joists"
roof_type: str # e.g. "PA Pitched (slates/tiles), access to loft"
insulation: str # e.g. "J Joists"
u_value_known: bool
insulation_thickness_mm: Optional[int] = None
@dataclass
class FloorDetails:
location: str # e.g. "G Ground floor"
floor_type: str # e.g. "N Suspended, not timber"
insulation: str # e.g. "A As built"
location: str # e.g. "G Ground floor"
floor_type: str # e.g. "N Suspended, not timber"
insulation: str # e.g. "A As built"
u_value_known: bool
default_u_value: Optional[float] = None
@ -109,7 +109,7 @@ class VentilationAndCooling:
passive_vents_count: int
flueless_gas_fires_count: int
fixed_space_cooling: bool
draught_lobby: str # e.g. "Not present"
draught_lobby: str # e.g. "Not present"
mechanical_ventilation: bool
pressure_test_method: str # e.g. "Not available"
@ -125,15 +125,19 @@ class Lighting:
@dataclass
class MainHeating:
heat_emitter: str # e.g. "Radiators"
fuel_type: str # e.g. "Mains gas"
flue_type: str # e.g. "Balanced"
heat_emitter: str # e.g. "Radiators"
fuel_type: str # e.g. "Mains gas"
flue_type: str # e.g. "Balanced"
fan_assisted_flue: bool
design_flow_temperature: str # e.g. "Unknown"
heating_controls_ees: str # e.g. "CBE"
heating_controls_sap: str # e.g. "SAP code 2106, Programmer, room thermostat and TRVs"
heating_controls_ees: str # e.g. "CBE"
heating_controls_sap: (
str # e.g. "SAP code 2106, Programmer, room thermostat and TRVs"
)
percentage_of_heat: int
pcdf_boiler_reference: Optional[str] = None # e.g. "17742 Potterton, Promax 33 Combi ErP, 88.30%"
pcdf_boiler_reference: Optional[str] = (
None # e.g. "17742 Potterton, Promax 33 Combi ErP, 88.30%"
)
heat_pump_age: Optional[str] = None
@ -147,7 +151,7 @@ class Meters:
@dataclass
class WaterHeating:
water_heating_code: str # e.g. "HWP"
water_heating_code: str # e.g. "HWP"
water_heating_sap_code: int
water_heating_fuel_type: str
hot_water_cylinder_present: bool
@ -157,7 +161,7 @@ class WaterHeating:
class Shower:
shower_number: int
outlet_type: str
connected: str # e.g. "None"
connected: str # e.g. "None"
@dataclass
@ -172,7 +176,7 @@ class Renewables:
solar_water_heating: bool
wwhrs_present: bool
flue_gas_heat_recovery_present: bool
photovoltaic_panel: str # e.g. "None"
photovoltaic_panel: str # e.g. "None"
export_capable_meter: bool
wind_turbine_present: bool
wind_turbines_terrain_type: str
@ -192,8 +196,8 @@ class ElmhurstSiteNotes:
co2_emissions_current_t: float
# Section 1.0
property_type: str # e.g. "B Bungalow"
attachment: str # e.g. "E End-Terrace"
property_type: str # e.g. "B Bungalow"
attachment: str # e.g. "E End-Terrace"
# Section 2.0
number_of_storeys: int