survey-extraction/etl/fileReader/sitenotes.py
Jun-te Kim 50ea324ca5 test
2026-03-30 18:31:05 +00:00

1965 lines
No EOL
88 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from etl.fileReader.reportType import ReportType
from etl.transform.preSiteNoteTypes import (
CompanyInfo, PreSiteNotesSummaryInfo, AssessorInfo,
PropertyDescription, PropertyDetail, Dimension,
Walls, Roofs, Floors, Door, VentilationAndCooling,
Lighting, WaterHeating, HotWaterCylinder, SolarWaterHeating,
ShowerAndBaths, FlueGasHeatRecoverySystem, PhotovoltaicPanel,
WindTurbine, OtherDetails, Windows, HeatingFromPreSiteNotes, HeatingSystemControls,
HeatingType, Insulation
)
from etl.transform.conditionReportTypes import (
ConditionReportModel, AssessorDetails, InspectionAndProject, TheProperty,
MainElevation, Elevation, ElevationInfo, PropertyAccess, ExternalElevation, ExternalElevationFront,
ExternalElevationGableOne, ExternalElevationGableTwo, ExternalElevationRear, ConservatoryOrOutbuilding,
AccessAndElevations, Hallway, RoomInfo, WindowsInfo, VentilationInfo, LivingRoom, DiningRoom, Kitchen, Rooms,
Utility, WC, Landing, Bedroom, Bathroom, LoftSpace, RoomInRoof, HeatingSystem, GeneralConditionHeatingSystem,
MainHeatingOne, MainHeatingTwo, SecondaryHeating, HeatingByRoom, Renewables, Occupant, EnergyUse, HeatingFromConditionReport, ShowerAndBath, FridgeAndFreezers, Cooker, TumbleDryer,
GeneralInformation, OccupantAssessment
)
from datetime import datetime
from pprint import pprint
from etl.transform.smartEpcSiteNoteTypes import (
SmartEpcHeader, SmartEpcGeneral, SmartEpcBuildingConstruction,
SmartEpcFloorMeasurement, SmartEpcRoofSpace, SmartEpcWindow,
SmartEpcMainHeating, SmartEpcSecondaryHeating, SmartEpcWaterHeating,
SmartEpcVentilation, SmartEpcRenewables, SmartEpcRoomCount,
SmartEpcMisc, SmartEpcCustomerResponse, SmartEpcAddendum,
SmartEpcSiteNoteModel,
)
class SiteNotesExtractor():
def __init__(self, data_list):
self.raw_data = data_list
def get_x_occurance(self, lst, value, x=1):
try:
return [i for i, v in enumerate(lst) if v == value][x]
except IndexError:
return None # Return None if the value does not occur twice
def get_x_value(self, lst, value, x=1):
index = self.get_x_occurance(lst, value, x)
return lst[index+1]
def get_next_value(self, lst, value, avoid=[]):
get_value = lambda key: None if lst[lst.index(value) + 1] in avoid else lst[lst.index(key) + 1]
return get_value(value)
def get_next_value_greedy(self, lst, value, upto):
index = lst.index(value) + 1
str = ""
for i in range(upto):
str += lst[index+i]
str += " "
return str[:-1]
def two_columns_processor(self, data, sub_titles_to_gather, avoid, indexAdd = 1):
def get_value(key):
try:
index = data.index(key)
value = data[index + indexAdd]
return None if value in avoid else value
except (ValueError, IndexError):
return None
dict_ = {}
for items in data:
if items in avoid:
continue
elif items in sub_titles_to_gather:
dict_.update({f"{items.lower().replace('-', '_').replace(' ','_')}":get_value(items)})
return dict_
def get_data_between(self, a, b):
return self.raw_data[self.raw_data.index(a):self.raw_data.index(b)]
class CSR(SiteNotesExtractor):
def __init__(self, data_list):
super().__init__(data_list)
self.type = ReportType.CHARTED_SURVEYOR_REPORT
self.insulation_info = None
self.setup()
def setup(self):
self.get_materials()
def get_materials(self):
lst = self.get_data_between("Detailed description of existing Cavity Wall Insulation ", "Detailed description of Defects in existing Cavity Wall Insulation")
if len(lst) > 2:
self.insulation_info = Insulation(type=lst[-1])
else:
dict_ = self.two_columns_processor(lst, ["Detailed description of existing Cavity Wall Insulation "], ["Detailed description of Defects in existing Cavity Wall Insulation"])
self.insulation_info = Insulation(
type=dict_.get('detailed_description_of_existing_cavity_wall_insulation_', "")
) if dict_ is not None else None
class ECOConditionReport(SiteNotesExtractor):
def __init__(self, data_list):
super().__init__(data_list)
self.type = ReportType.ECO_CONDITION_REPORT
self.master_obj = self.setup_condition_report()
def setup_condition_report(self):
pass
class WarmHomesConditionReport(SiteNotesExtractor):
def __init__(self, data_list, debug = False):
super().__init__(data_list)
self.type = ReportType.WARM_HOMES_CONDITION_REPORT
if debug is False:
room, heating_system, occupant, access_and_elevation = self.setup_condition_report()
self.master_obj = room, heating_system, occupant, access_and_elevation
def setup_condition_report(self):
# general_information = self.get_section_1()
access_and_elevations = self.get_section_2()
rooms = self.get_section_3()
heating_system = self.get_section_4()
occupant_assessment = self.get_section_5()
# site_name, reference_code, address, postcode = self.get_section_0()
return rooms, heating_system, occupant_assessment, access_and_elevations
def get_section_0(self):
data = self.get_data_between("Project Site Name", "1. General Information")
site_name = self.get_next_value(data, "Project Site Name")
# reference_code = self.get_next_value(data, "Property Reference Code")
reference_code = "Place holder, please delete me for prod"
address = self.get_next_value(data, "Property Address")
postcode = self.get_data_between("Postcode", "Main Image")[1:]
postcode = " ".join(postcode)
return site_name, reference_code, address, postcode
def get_section_1(self):
assessor_details = self.get_assessor_details()
inspection_and_project = self.get_inspection_and_project()
the_property = self.get_the_property()
main_elevation = self.get_main_elevation()
elevations = self.get_all_elevations()
return GeneralInformation(
assessor_details=assessor_details,
inspection_and_project=inspection_and_project,
the_property=the_property,
main_elevation=main_elevation,
elevations=elevations
)
def get_assessor_details(self):
data = self.get_data_between("1.1 Assessor details","1.2 Inspection & Project")
assessor_name = self.get_next_value(data, "Assessor Name & ID")
# elmhirst_id = self.get_next_value(data, "Property Reference Code")
elmhirst_id = "please remove me in prod"
return AssessorDetails(assessor_name_and_id=assessor_name, elmhurst_id=elmhirst_id)
def get_inspection_and_project(self):
data = self.get_data_between("1.2 Inspection & Project", "1.3 The property")
date = self.get_next_value(data, "Inspection Date")
return InspectionAndProject(inspection_date=date)
def get_the_property(self):
data = self.get_data_between("1.3 The property", "3. Rooms")
return TheProperty(
house_type=self.get_next_value(data, "House Type"),
on_which_floor_is_the_flat_located = self.get_next_value(data, "On which floor is the flat located?"),
is_there_a_corridor = True if self.get_next_value(data, "Is there a corridor?").lower() == "yes" else False,
is_it_heated = True if self.get_next_value(data, "Is it heated?").lower() == "yes" else False,
it_there_a_balcony = True if self.get_next_value(data, "Is there a balcony?").lower() == "yes" else False,
classification_type = self.get_next_value(data, "Classification Type"),
orientation_front_elevation = self.get_next_value(data, "Orientation (front elevation)"),
orientation_in_degrees_front_elevation = self.get_next_value(data, "Orientation in degrees (front elevation)"),
exposure_zone = self.get_next_value(data, "Exposure Zone"),
main_wall_construction = self.get_next_value(data, "Main Wall Construction")
)
def get_main_elevation(self):
data = self.get_data_between("Main elevation", "Elevation")
elevation = ElevationInfo(
elevation_type=self.get_next_value(data, "Elevation type"),
cavity_wall_depth=self.get_next_value(data, "Cavity wall depth (in mm)"),
is_insulation_present=True if self.get_next_value(data, "Is insulation present?").lower() == "yes" else False,
insulation_type= self.get_next_value(data, "Insulation type"),
)
return MainElevation(
elevation_info=elevation
)
def get_all_elevations(self):
elevations = []
i = 1
while(f"Elevation {i}" in self.raw_data):
i += 1
no_of_elevation = i-1
print(f"There are {no_of_elevation} elevation")
for i in range(no_of_elevation):
data = self.get_data_between(f"Elevation {i+1}", "2. Access & Elevations")
elevations.append(ElevationInfo(
elevation_type=self.get_next_value(data, "Elevation type"),
cavity_wall_depth=self.get_next_value(data, "Cavity wall depth (in mm)"),
is_insulation_present=True if self.get_next_value(data, "Is insulation present?").lower() == "yes" else False,
insulation_type= self.get_next_value(data, "Insulation type"),
))
return Elevation(
info=elevations,
protected_conservatory_or_aonb=self.get_next_value(data, "the Significance Survey below is required."),
material_type=self.get_next_value(data, "Material Type"),
are_there_any_visible_signs_of_existing_wall_insulation=self.get_next_value(data, "Are there any visible signs of existing wall insulation?"),
does_the_existing_ground_level_on_any_elevation_bridge_the_dpc=True if self.get_next_value(data, "DPC?").lower() == "yes" else False,
)
def get_section_2(self):
pa = self.get_property_access()
front,one,back, two = self.get_external_elevation()
co = self.get_conservatory_or_outbuilding()
return AccessAndElevations(
property_access=pa,
external_elevation_front=front,
external_elevation_back=back,
external_elevation_gable_one=one,
external_elevation_gable_two=two,
conservatory_or_out_building=co,
)
def get_property_access(self):
data = self.get_data_between("2.1 Property Access", "2.2 External Elevations")
property_access = PropertyAccess(
are_there_any_road_restriction_in_the_locality=True if self.get_next_value(data, "locality?").lower() == "yes" else False,
is_on_street_parking_available=True if self.get_next_value(data, "Is on-street parking available?").lower() == "yes" else False,
are_there_any_overhead_wires_or_cables=True if self.get_next_value(data, "Are there any overhead wires or cables?") == "yes" else False,
is_the_access_gated=True if self.get_next_value(data, "Is the access gated?").lower() == "yes" else False,
is_there_restricted_space_for_contractors_to_access_the_wall_area=True if self.get_next_value(data, "area?").lower() == "yes" else False,
is_there_restricted_space_for_contractors_to_access_the_roof_area=True if self.get_x_value(data, "area?", 1).lower() == "yes" else False,
is_there_more_than_1_point_5_meters_in_width_to_fence_or_neighbouring_boundary_along_the_full_gable_elevation=True if self.get_next_value(data, "boundary along the full gable elevation?").lower() == "yes" else False,
is_access_to_the_rear_provided_by_use_of_a_ginnel=True if self.get_next_value(data, "Is access to the rear provided by use of a ginnel?").lower() == "yes" else False,
is_access_to_the_rear_provided_by_use_of_a_secured_alleyway=True if self.get_next_value(data, "Is access to the rear provided by use of a secured alleyway?").lower() == "yes" else False,
)
return property_access
def get_external_elevation(self):
# Front
data = self.get_data_between("2.2.1. External Elevation - Front", '2.2.2. External Elevation - Gable 1')
ee = ExternalElevation(
structural_defects_of_elevation=self.get_next_value(data, "Structural defects of elevation"),
does_any_structural_defect_need_resolving_before_retrofit=True if self.get_next_value(data,"Does any structural defect need resolving before retrofit?").lower() == "yes" else False,
are_there_any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework=True if self.get_next_value(data, "rainwater goods or pipework?").lower() == "yes" else False,
are_there_any_visible_signs_of_movement=True if self.get_next_value(data, "Are there any visible signs of movement?").lower() == "yes" else False,
are_there_any_visible_signs_of_cracking_to_the_existing_external_finish=True if self.get_next_value(data, "external finish?").lower() == "yes" else False,
)
front = ExternalElevationFront(
external_elevation=ee
)
# Gable 1
data = self.get_data_between("2.2.2. External Elevation - Gable 1", "2.2.3. External Elevation - Rear")
state = True if self.get_next_value(data, "cracking)").lower() == "yes" else False
if state:
gable_one = ExternalElevationGableOne(
do_all_answers_for_the_front_elevation_apply_to_this_wall=state
)
else:
gable_one_elevation = ExternalElevation(
structural_defects_of_elevation=self.get_next_value(data, "Structural defects of elevation"),
does_any_structural_defect_need_resolving_before_retrofit=True if self.get_next_value(data,"Does any structural defect need resolving before retrofit?").lower() == "yes" else False,
are_there_any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework=True if self.get_next_value(data, "rainwater goods or pipework?").lower() == "yes" else False,
are_there_any_visible_signs_of_movement=True if self.get_next_value(data, "Are there any visible signs of movement?").lower() == "yes" else False,
are_there_any_visible_signs_of_cracking_to_the_existing_external_finish=True if self.get_next_value(data, "external finish?").lower() == "yes" else False,
)
gable_one = ExternalElevationGableOne(
do_all_answers_for_the_front_elevation_apply_to_this_wall=state,
external_elevation=gable_one_elevation
)
# Rear
data = self.get_data_between("2.2.3. External Elevation - Rear", "2.2.4. External Elevation - Gable 2")
state = True if self.get_next_value(data, "cracking)").lower() == "yes" else False
if state:
rear = ExternalElevationRear(do_all_answers_for_the_front_elevation_apply_to_this_wall=state)
else:
elevation = ExternalElevation(
structural_defects_of_elevation=self.get_next_value(data, "Structural defects of elevation"),
does_any_structural_defect_need_resolving_before_retrofit=True if self.get_next_value(data,"Does any structural defect need resolving before retrofit?").lower() == "yes" else False,
are_there_any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework=True if self.get_next_value(data, "rainwater goods or pipework?").lower() == "yes" else False,
are_there_any_visible_signs_of_movement=True if self.get_next_value(data, "Are there any visible signs of movement?").lower() == "yes" else False,
are_there_any_visible_signs_of_cracking_to_the_existing_external_finish=True if self.get_next_value(data, "external finish?").lower() == "yes" else False,
)
rear = ExternalElevationRear(
do_all_answers_for_the_front_elevation_apply_to_this_wall=state,
external_elevation=elevation
)
# Gable 2
data = self.get_data_between("2.2.4. External Elevation - Gable 2", "2.3. Conservatory or Outbuilding")
state = False # TODO quick demo for waltham forest, set to always false
if state is False:
gable_two = ExternalElevationGableTwo(is_there_a_fourth_external_elevation=state)
else:
gable_two_elevation = ExternalElevation(
structural_defects_of_elevation=self.get_next_value(data, "Structural defects of elevation"),
does_any_structural_defect_need_resolving_before_retrofit=True if self.get_next_value(data,"Does any structural defect need resolving before retrofit?").lower() == "yes" else False,
are_there_any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework=True if self.get_next_value(data, "rainwater goods or pipework?").lower() == "yes" else False,
are_there_any_visible_signs_of_movement=True if self.get_next_value(data, "Are there any visible signs of movement?").lower() == "yes" else False,
are_there_any_visible_signs_of_cracking_to_the_existing_external_finish=True if self.get_next_value(data, "external finish?").lower() == "yes" else False,
)
gable_two = ExternalElevationGableTwo(
is_there_a_fourth_external_elevation=state,
external_elevation=gable_two_elevation
)
return front, gable_one, rear, gable_two
def get_conservatory_or_outbuilding(self):
data = self.get_data_between("2.3. Conservatory or Outbuilding", "3. Rooms")
return ConservatoryOrOutbuilding(
is_there_a_conservatory=True if self.get_next_value(data, "Is there a Conservatory?").lower() == "yes" else False,
is_there_a_cellar_present=True if self.get_next_value(data, "Is there a cellar present?").lower() == "yes" else False,
is_there_an_outbuilding=True if self.get_next_value(data, "Is there an Outbuilding?").lower() == "yes" else False,
)
def get_section_3(self):
hallway = self.get_hallway()
living_room = self.get_living_room()
dining_room = self.get_dining_room()
kitchen = self.get_kitchen()
utility = self.get_utility()
wc = self.get_wc()
landing = self.get_landing()
bedrooms = self.get_bedrooms()
bathrooms = self.get_bathroom()
loft_space = self.get_loft_space()
room_in_roof = self.get_room_in_roof()
return Rooms(
hallway=hallway,
living_room=living_room,
dining_room=dining_room,
kitchen=kitchen,
utility=utility,
wash_chamber=wc,
landing=landing,
bedrooms=bedrooms,
bathrooms=bathrooms,
loft_space=loft_space,
room_in_roof=room_in_roof,
)
def get_room_info(self, data):
state = True if self.get_next_value(data, "excessive condensation within the room?").lower() == "yes" else False
if state is False:
ventilation = VentilationInfo(
is_there_a_ventilation_system_present_in_the_room=True if self.get_next_value(data, "Is there a ventilation system present in the room?").lower() == "yes" else False,
are_there_any_visible_or_reported_signs_of_damp_mould_or_excessive_condensation_within_the_room=True if self.get_next_value(data, "excessive condensation within the room?").lower() == "yes" else False,
are_there_sufficient_undercuts_on_the_closed_door=self.get_next_value(data, "- min 10mm)?"),
is_there_any_open_flue_heating_appliances_within_the_room=True if self.get_next_value(data, "Is there any open flue heating appliances within the room?").lower() == "yes" else False,
)
else:
ventilation = VentilationInfo(
is_there_a_ventilation_system_present_in_the_room=True if self.get_next_value(data, "Is there a ventilation system present in the room?").lower() == "yes" else False,
are_there_any_visible_or_reported_signs_of_damp_mould_or_excessive_condensation_within_the_room=True if self.get_next_value(data, "excessive condensation within the room?").lower() == "yes" else False,
are_there_sufficient_undercuts_on_the_closed_door=self.get_next_value(data, "- min 10mm)?"),
is_there_any_open_flue_heating_appliances_within_the_room=True if self.get_next_value(data, "Is there any open flue heating appliances within the room?").lower() == "yes" else False,
location_of_any_damp_or_mould=self.get_next_value(data, "Location of any damp/mould"),
what_severity_of_damp_mould_would_you_consider_this_to_be=self.get_next_value(data, "What severity of damp/mould would you consider this to be?"),
)
windows_state = True if self.get_next_value(data, "Does the room have any windows?").lower() == "yes" else False
if windows_state:
windows = WindowsInfo(
does_the_room_have_any_windows=windows_state,
condition_of_the_windows=self.get_next_value(data, "Condition of the windows"),
do_the_windows_have_trickle_vents=True if self.get_next_value(data, "Do the windows have trickle vents?").lower() == "yes" else False,
input_trickle_vent_product_code_or_measurement=self.get_next_value(data, "in both cases."),
are_the_windows_openable=True if self.get_next_value(data, "Are the windows openable?").lower() == "yes" else False,
)
else:
windows = WindowsInfo(
does_the_room_have_any_windows=windows_state
)
state = True if self.get_next_value(data, "cracking in walls, etc.)").lower()=="yes" else False
if state:
return RoomInfo(
overall_condition_of_the_room=self.get_next_value(data, "Overall condition of the room"),
does_the_room_have_any_defects=self.get_next_value(data, "cracking in walls, etc.)"),
description_of_defect=self.get_next_value(data, "Description of the defects", avoid=["Any additional recommendations?"]),
windows_info=windows,
ventilation_info=ventilation,
)
else:
return RoomInfo(
overall_condition_of_the_room=self.get_next_value(data, "Overall condition of the room"),
does_the_room_have_any_defects=self.get_next_value(data, "cracking in walls, etc.)"),
windows_info=windows,
ventilation_info=ventilation,
)
def get_hallway(self):
data = self.get_data_between("Hallway", "Living Room")
hallway_state = True if self.get_next_value(data, "Is there a hallway?").lower() == "yes" else False
if hallway_state:
room = self.get_room_info(data)
else:
room = None
return Hallway(
is_there_a_hallway=hallway_state,
room_info=room,
)
def get_living_room(self):
data = self.get_data_between("Living Room", "Dining Room")
room = self.get_room_info(data)
return LivingRoom(
room_info=room,
)
def get_dining_room(self):
data = self.get_data_between("Dining Room", "Kitchen")
dining_room_state = True if self.get_next_value(data, "Is there a dining room?").lower() == "yes" else False
if dining_room_state:
room = self.get_room_info(data)
else:
room = None
return DiningRoom(
is_there_a_dining_room=dining_room_state,
room_info=room,
)
def get_kitchen(self):
data = self.get_data_between("Kitchen", "Utility")
room = self.get_room_info(data)
return Kitchen(
room_info=room,
is_there_a_cooker_hood_present_in_the_room=True if self.get_next_value(data, "Is there a cooker hood present in the room?").lower() == "yes" else False,
)
def get_utility(self):
data = self.get_data_between("Utility", "WC")
utility_state = True if self.get_next_value(data, "Is there a utility room?").lower() == "yes" else False
if utility_state:
room = self.get_room_info(data)
else:
room = None
return Utility(
is_there_a_utility_room=utility_state,
room_info=room
)
def get_wc(self):
data = self.get_data_between("WC", "Landing")
wc_state = True if self.get_next_value(data, "Is there a separated WC?").lower() == "yes" else False
if wc_state:
room = self.get_room_info(data)
else:
room = None
return WC(
is_there_a_seperated_wc=wc_state,
room_info=room
)
def get_landing(self):
data = self.get_data_between("Landing", "Bedroom")
landing_state = True if self.get_next_value(data, "Is there a landing?").lower() == "yes" else False
if landing_state:
room_info = self.get_room_info(data)
else:
room_info = None
return Landing(
is_there_a_landing=landing_state,
room_info=room_info
)
def get_bedrooms(self):
bedrooms = []
i = 1
while(f"Bedroom {i}" in self.raw_data):
i += 1
no_of_bedrooms = i-1
print(f"There are {no_of_bedrooms} bedrooms")
for i in range(no_of_bedrooms):
data = self.get_data_between(f"Bedroom {i+1}", "Bathroom")
roominfo = self.get_room_info(data)
bedrooms.append(Bedroom(
double_or_single_bedroom=self.get_next_value(data, "Double or Single Bedroom?"),
room_info=roominfo
))
return bedrooms
def get_bathroom(self):
bathrooms = []
i = 1
while(f"Bathroom {i}" in self.raw_data):
i += 1
no_of_bathrooms = i-1
print(f"There are {no_of_bathrooms} bathrooms")
for i in range(no_of_bathrooms):
data = self.get_data_between(f"Bathroom {i+1}", "Loft Space")
roominfo = self.get_room_info(data)
bathrooms.append(Bathroom(
is_this_an_ensuite_bathroom=self.get_next_value(data, "Is this an ensuite bathroom?"),
room_info=roominfo
))
return bathrooms
def get_loft_space(self):
data = self.get_data_between("Loft Space", "Room in Roof")
state = True if self.get_next_value(data, "Is the main loft space accessible?").lower() == "yes" else False
if state:
return LoftSpace(
is_the_main_loft_space_accessible=self.get_next_value(data, "Is the main loft space accessible?"),
is_there_more_than_one_loft_space=True if self.get_next_value(data, "extension)?").lower() == "yes" else False,
overall_condition_of_the_loft_space=self.get_next_value(data, "Overall condition of the loft space"),
are_there_visible_signs_of_condesnation_on_the_roof_lining_or_insualtion_layer=self.get_next_value(data, "insulation layer?"),
does_the_loft_space_have_any_defects=self.get_next_value(data, "cracking in walls, etc.)"),
existing_depth_of_loft_insulation=self.get_next_value(data, "Existing Depth of Loft Insulation"),
is_the_insulation_layer_even_across_the_loft=self.get_next_value(data, "Is the insulation layer even across the loft?"),
is_the_loft_boarded_in_any_area=self.get_next_value(data, "Is the loft boarded in any area?"),
condition_of_existing_roof_lining=self.get_next_value(data, "Condition of existing roof lining"),
is_there_an_existing_heating_system_or_plumbing_located_in_the_loft=self.get_next_value(data, "the loft?"),
is_there_any_open_flue_heating_applicanes_within_the_room=self.get_next_value(data, "Is there any open flue heating appliances within the room?"),
)
else:
return LoftSpace(
is_the_main_loft_space_accessible=self.get_next_value(data, "Is the main loft space accessible?"),
is_there_more_than_one_loft_space=True if self.get_next_value(data, "extension)?").lower() == "yes" else False,
)
def get_room_in_roof(self):
data = self.get_data_between("Room in Roof", "4. Heating System")
room_in_roof_state = True if self.get_next_value(data, "Is there a room in roof?").lower() == "yes" else False
room_info = None
if room_in_roof_state:
room_info = self.get_room_info(data)
return RoomInRoof(
is_there_a_room_in_roof=room_in_roof_state,
room_info=room_info
)
def get_section_4(self):
general_condition_of_heating_system = self.get_general_condition_of_heating_system()
main_heating_one = self.get_main_heating_one()
main_heating_two = self.get_main_heating_two()
# secondary_heating = self.get_secondary_heating()
heating_by_room = self.get_heating_by_room()
renewables = self.get_renewables()
return HeatingSystem(
general_condition=general_condition_of_heating_system,
main_heating_one=main_heating_one,
main_heating_two=main_heating_two,
# secondary_heating=secondary_heating,
heating_by_room=heating_by_room,
renewables=renewables
)
def get_main_heating_one(self):
data = self.get_data_between("Main Heating 1", "Main Heating 2")
return MainHeatingOne(
as_defined_by=self.get_next_value(data, "As defined by"),
fuel=self.get_next_value(data, "Fuel:"),
type=self.get_next_value_greedy(data, "Type", 2)
)
def get_main_heating_two(self):
data = self.get_data_between("Main Heating 2", "Secondary Heating")
return MainHeatingTwo(
is_there_a_main_heating_two=True if self.get_next_value(data, "Is there a Main Heating 2?").lower() == "yes" else False,
)
# def get_secondary_heating(self):
# data = self.get_data_between("Secondary Heating", "Heating by room")
# return SecondaryHeating(
# is_there_a_secondary_heating=True if self.get_next_value(data, "Is there a Secondary Heating?").lower() == "yes" else False,
# fuel=self.get_next_value(data, "Fuel:"),
# electric_heating_type=self.get_next_value_greedy(data, "Type", 2),
# gas_heating_type=self.get_x_value(data, "Type", 1),
# )
def get_heating_by_room(self):
data = self.get_data_between("Heating by room", "Renewables")
list_of_main_heating_system_by_one = self.get_data_between("Rooms heated by Main System 1:", "Rooms heated by Main System 2:")[1:]
list_of_main_heating_system_by_two = self.get_data_between("Rooms heated by Main System 2:", "Rooms heated by Secondary Heating:")[1:]
list_of_rooms_heated_by_secondary_heating = self.get_data_between("Rooms heated by Secondary Heating:", "Are there any partially heated rooms?")[1:]
are_there_any_partially_heated_rooms = True if self.get_next_value(data, "Are there any partially heated rooms?").lower() == "yes" else False
are_there_any_unheated_rooms = True if self.get_next_value(data, "Are there any unheated rooms?").lower() == "yes" else False
list_of_partially_heated_rooms = self.get_data_between("Are there any partially heated rooms?", "Are there any unheated rooms?")[2:]
list_of_unheated_rooms = self.get_data_between("Are there any unheated rooms?", "Renewables")[2:]
def remove_special_character(lst):
return_lst = []
for i, item in enumerate(lst):
if '\xa0' in item or 'Rooms' in item or len(item) == 1 or item.lower() == "None".lower():
pass
else:
return_lst.append(item)
return return_lst
return HeatingByRoom(
rooms_heated_by_main_system_one=remove_special_character(list_of_main_heating_system_by_one),
rooms_heated_by_main_system_two=remove_special_character(list_of_main_heating_system_by_two),
rooms_heated_by_secondary_heating=remove_special_character(list_of_rooms_heated_by_secondary_heating),
are_there_any_partially_heated_rooms=are_there_any_partially_heated_rooms,
are_there_any_unheated_rooms=are_there_any_unheated_rooms,
list_of_partially_heated_rooms=remove_special_character(list_of_partially_heated_rooms),
unheated_rooms=remove_special_character(list_of_unheated_rooms),
)
def get_renewables(self):
data = self.get_data_between("Renewables", "5. Occupancy Assessment")
return Renewables(
is_there_any_renewable_energy_system_in_place=True if self.get_next_value(data, "Is there any renewable energy system in place?").lower() == "yes" else False,
suitable_roof_orientation_for_solar_pv_water=self.get_next_value(data, "Suitable roof orientation for Solar PV/water:"),
is_there_a_water_tank=True if self.get_next_value(data, "Is there a Water Tank?").lower() == "yes" else False,
# type=self.get_next_value(data, "Type"),
# size=self.get_next_value(data, "Size"),
# tank_location=self.get_next_value(data, "Tank Location"),
# is_the_tank_insulated=True if self.get_next_value(data, "Is the tank Insulated") else False,
# type_of_insulation=self.get_next_value(data, "Type of Insulation"),
# thickness_of_insulation_in_mm=int(self.get_next_value(data, "Thickness of Insulation (mm)")),
)
def get_general_condition_of_heating_system(self):
data = self.get_data_between("4. Heating System", "Main Heating 1")
return GeneralConditionHeatingSystem(
is_the_heating_system_in_working_order=True if self.get_next_value(data, "Is the Heating System in working order?") else False,
does_the_occupant_have_a_smart_meter=True if self.get_next_value(data, "Does the occupant have a Smart Meter?") else False,
are_there_any_smart_monitoring_devices=True if self.get_next_value(data, "Are there any smart monitoring devices (Switchee etc)?") else False,
are_the_electricity_meters_accessible=True if self.get_next_value(data, "Is the Electricity Meter accessible?") else False,
are_the_gas_meters_accessible=True if self.get_next_value(data, "Is the Gas Meter accessible?") else False,
# dual_or_single_electric_meter=self.get_next_value(data, "Dual or single electric meter?"),
)
def get_section_5(self):
occupants = self.get_occupants()
# energy_use = self.get_energy_use()
# heating = self.get_heating()
# shower_and_bath = self.get_shower_and_bath()
# appliances = self.get_appliances()
# fridge_and_freezers = self.get_fridge_and_freezers()
# cooker = self.get_cooker()
# tumble_dryer = self.get_tumble_dryer()
return occupants
return OccupantAssessment(
occupant=occupants,
energy_use=energy_use,
heating=heating,
shower_and_bath=shower_and_bath,
appliances=appliances,
fridge_and_freezers=fridge_and_freezers,
cooker=cooker,
tumble_dryer=tumble_dryer
)
def get_occupants(self):
data = self.get_data_between("Occupants", "Energy use")
second_data = self.get_data_between("Tumble dryer", "Media summary")
no_of_child_occupants = self.get_next_value(data, "No. of Child Occupants (Under 18)")
if no_of_child_occupants == "\xa0":
no_of_child_occupants = 0
else:
no_of_child_occupants = int(no_of_child_occupants)
print(self.get_next_value(data, "No. of Child Occupants (Under 18)"))
return Occupant(
# name=self.get_next_value(second_data, "Name of the occupant:"),
have_evidence_of_12_months_of_fuel_bill_data= True if self.get_next_value(second_data, "Have you evidenced 12 months of fuel bill data?").lower() == "yes" else False,
total_number_of_occupants=int(self.get_next_value(data, "Total number of occupants:")),
no_of_adult_occupants=int(self.get_next_value(data, "No. of Adult Occupants (18+)")),
no_of_child_occupants=no_of_child_occupants,
no_of_occupant_of_a_pensionable_age=int(self.get_next_value(data, "No. of occupant of a pensionable age")),
are_there_any_vulnerable_people=True if self.get_next_value(data, "Are there any vulnerable people?").lower() == "yes" else False,
is_there_anyone_with_a_disability=True if self.get_next_value(data, "Is there anyone with a disability?").lower() == "yes" else False,
status_of_occupant=self.get_next_value(data, "Status of the occupant:"),
landlord_has_written_confirmation_that_the_tenent_agrees_to_the_assessment_been_supplied=True if self.get_next_value(data, "the assessment been supplied").lower() == "yes" else False,
)
def get_energy_use(self):
data = self.get_data_between("Energy use", "Heating")
return EnergyUse(
property_tenure=self.get_next_value(data, "Property tenure:"),
who_is_the_electricity_payer=self.get_next_value(data, "Who is the electricity bill payer?")
)
def get_heating(self):
data = self.get_data_between("Heating", "Shower & bath")
return HeatingFromConditionReport(
room_stat_in_temperature_in_celsius=self.get_next_value(data, "Room Stat Temperature (in °C)", avoid=["Room Stat Location", '\xa0']),
room_stat_location=self.get_next_value(data, "Room Stat Location", avoid = ["Is the heating pattern known?", '\xa0']),
is_the_heating_pattern_known=self.get_next_value(data, "Is the heating pattern known?", avoid=["Shower & bath", '\xa0']),
)
def get_shower_and_bath(self):
data = self.get_data_between("Shower & bath", "Appliances")
return ShowerAndBath(
shower_type=self.get_next_value(data, "Shower Type:"),
do_you_know_the_no_of_showers_per_day_per_week=True if self.get_next_value(data, "Do you know the number of showers per day or per week?").lower() == "yes" else False,
please_input_no_of_showers_and_specify_a_day_or_a_week=self.get_next_value(data, '"per week"'),
do_you_know_the_number_of_baths_per_day_or_per_week=self.get_next_value(data, "Do you know the number of baths per day or per week?"),
)
def get_appliances(self):
print("Skipped appliances due to not having this example yet")
def get_fridge_and_freezers(self):
data = self.get_data_between("Fridge & freezers", "Cooker")
return FridgeAndFreezers(
no_of_stand_alone_seperate_fridges=int(self.get_next_value(data,"No. of Standalone (separate) Fridges:")),
no_of_stand_alone_seperate_freezers=int(self.get_next_value(data, "No. of Standalone (separate) Freezers:")),
no_of_stand_alone_or_integrated_fridge_freezers=int(self.get_next_value(data, "No. of Standalone or Integrated Fridge Freezers:"))
)
def get_cooker(self):
data = self.get_data_between("Cooker", "Tumble dryer")
return Cooker(
cooker_type=self.get_next_value(data, "Cooker Type:"),
normal_large_range=self.get_next_value(data, "Normal - Large - Range"),
range_fuel=self.get_next_value(data, "Range Fuel:")
)
def get_tumble_dryer(self):
data = self.get_data_between("Tumble dryer", "Have you evidenced 12 months of fuel bill data?")
return TumbleDryer(
percentage_of_annual_use=int(self.get_next_value(data, "Percentage of annual use:")),
space_for_outdoor_drying=True if self.get_next_value(data,"Space for outdoor drying?").lower() == "yes" else False,
)
class QuidosSiteNotesExtractor(SiteNotesExtractor):
def __init__(self, data_list):
super().__init__(data_list)
self.type = ReportType.QUIDOS_PRESITE_NOTE
self.company_information = None
self.survey_information = None
self.property_description = None
self.setup()
def setup(self):
"""
A function to read QUIDOS SITE REPORT and get all data
"""
self.transform_summary_information()
self.transform_sections()
# Heaters require thought so will complete later
# self.get_section_14()
# self.get_section_14_1()
# self.get_section_14_2()
def transform_summary_information(self):
# Summary Information
avoid = [
"Reference Number",
"EPC Language",
"UPRN",
"Postcode",
"Region",
"Address",
"Town",
"County",
"Property Tenure",
"Transaction Type",
"Inspection Date",
'Assessors accreditation number',
'Assessors name',
'Company name/trading name',
'Address',
'POST CODE',
'Phone number',
'Fax number',
'E-mail address',
'Related party disclosure',
'Current SAP rating',
'Potential SAP rating',
'Current EI rating',
'Current annual emissions',
'Current annual energy costs',
'Emission figures including 9.92 emission factor of 0.925',
]
get_value = lambda key: None if self.raw_data[self.raw_data.index(key) + 1] in avoid else self.raw_data[self.raw_data.index(key) + 1]
index = self.get_x_occurance(self.raw_data, "Current annual emissions")
if index:
including_9_92_emission_factor = self.raw_data[index + 1]
else:
including_9_92_emission_factor = None
self.survey_information = PreSiteNotesSummaryInfo(
reference_number = get_value('Reference Number'),
epc_language = get_value('EPC Language'),
uprn = get_value('UPRN').lstrip('0'),
postcode = get_value('Postcode'),
region = get_value('Region'),
address = ','.join(get_value('Address').split(',')[:-1]).strip(),
town = get_value('Town'),
county = get_value('County'),
property_tenure = get_value('Property Tenure'),
transaction_type = get_value('Transaction Type'),
inspection_date = datetime.strptime(get_value('Inspection Date'), '%d %B %Y'),
current_sap = get_value('Current SAP rating'),
potential_sap = get_value('Potential SAP rating'),
current_ei = get_value('Current EI rating'),
potential_ei = get_value('Potential EI rating'),
current_annual_emissions = get_value('Current annual emissions'),
current_annual_energy_costs = get_value('Current annual energy costs'),
current_annual_emission_including_0925_multiplayer=including_9_92_emission_factor,
)
self.company_information = CompanyInfo(
address=self.raw_data[self.get_x_occurance(self.raw_data,'Address', 1) + 1],
trading_name = get_value('Company name/trading name'),
post_code = get_value('POST CODE'),
fax_number = get_value('Fax number'),
related_party_disclosure = get_value("Related party disclosure")
)
self.assessor_information = AssessorInfo(
accreditation_number = get_value("Assessors accreditation number"),
name = get_value("Assessors name"),
phone_number = get_value("Phone number"),
email_address = get_value("E-mail address"),
)
index = self.get_x_occurance(self.raw_data, "Address")
if index:
assessor_address = self.raw_data[index + 1]
else:
assessor_address = None
self.assessor_information = AssessorInfo(
accreditation_number = get_value("Assessors accreditation number"),
name = get_value("Assessors name"),
phone_number = get_value("Phone number"),
email_address = get_value("E-mail address"),
address = assessor_address,
)
def transform_sections(self):
# Section 1
data = self.get_data_between("1.0 Property Type","2.0 Number Of")
avoid = [
"1.0 Property Type",
"Built Form",
"Detachment/Position",
"2.0 Number Of"
]
get_value = lambda key: None if self.raw_data[self.raw_data.index(key) + 1] in avoid else self.raw_data[self.raw_data.index(key) + 1]
# Section 2
data = self.get_data_between("2.0 Number Of","3.0 Date Built")
avoid = [
'2.0 Number Of',
'Main Property',
'Extension 1',
'Extension 2',
'Extension 3',
'Extension 4',
'Number of Habitable Rooms',
'Number of Heated Habitable Rooms',
'Heated Basement',
'Conservatory Type',
'Terrain Type',
'Percentage of Draught Proofed(%)',
"3.0 Date Built",
]
# Section 3
age_bands = self.get_age_band()
# Section 4
no_of_main_property = int(get_value("Main Property"))
no_of_extension_1 = int(get_value('Extension 1') or 0)
no_of_extension_2 = int(get_value('Extension 2') or 0)
no_of_extension_3 = int(get_value('Extension 3') or 0)
no_of_extension_4 = int(get_value('Extension 4') or 0)
dimensions = self.get_dimensions(
no_of_main_property,
no_of_extension_1,
no_of_extension_2,
no_of_extension_3,
no_of_extension_4,
)
# Section 5
conservatory = self.is_there_a_conservatory()
# Section 7
walls = self.get_walls()
# Section 8
roofs = self.get_roof()
# Section 9
floors = self.get_floors()
# Section 10
door = self.get_door()
windows = self.get_windows()
# Section 12
ventilationAndCooling = self.get_ventilation_and_cooling()
# Section 13
lighting = self.get_lighting()
# Section 14.0
main_heating = self.get_main_heating()
# Section 14.1
secondary_heating = self.get_secondary_heating()
# Section 14.2
secondary_heating_type = self.get_secondary_heating_type()
# Section 15.0 Water Heating
waterHeating = self.get_water_heating()
# Section 15.1 Hot Water Cylinder
hotWaterCylinder = self.get_hot_water_cylinder()
# Section 16 Solar Water Heating
solarWaterHeating = self.get_solar_water_heating()
# Section 17.0
# ignored as it has nothing in the copy i'm using. Future todo clarity
# Section 18.0
showerAndBaths = self.get_shower_and_baths()
# Section 19.0
fghrs = self.get_fghrs()
# Section 20.0
photovoltaicPanel = self.get_photovoltaic_panel()
# Section 21.0
windTurbine = self.get_wind_turbine()
# Section 22.0
otherDetails = self.get_other_details()
self.property_description = PropertyDescription(
built_form = get_value("Built Form"),
detachment_or_position = get_value("Detachment/Position"),
no_of_main_property = no_of_main_property,
no_of_extension_1 = no_of_extension_1,
no_of_extension_2 = no_of_extension_2,
no_of_extension_3 = no_of_extension_3,
no_of_extension_4 = no_of_extension_4,
no_of_habitable_rooms = int(get_value('Number of Habitable Rooms')),
no_of_heated_rooms = int(get_value('Number of Heated Habitable Rooms')),
heated_basement = False if get_value('Heated Basement') == "NO" else True,
conservatory_type = get_value('Conservatory Type'),
percentage_of_draught_proofed= int(get_value('Percentage of Draught Proofed(%)')),
main_property=PropertyDetail(
age_band=age_bands[0],
dimensions=dimensions["main"] if "main" in dimensions else [],
wall=walls[0],
roof=roofs[0],
floor=floors[0],
windows=windows.get("main_property", []),
)if no_of_main_property > 0 else None,
ex1_property=PropertyDetail(
age_band= age_bands[1],
dimensions= dimensions["ex1"] if "ex1" in dimensions else [],
wall=walls[1],
roof=roofs[1],
floor=floors[1],
windows=windows.get("extension_1", []),
)if no_of_extension_1 > 0 else None,
ex2_property=PropertyDetail(
age_band= age_bands[2],
dimensions= dimensions["ex2"] if "ex2" in dimensions else [],
wall=walls[2],
roof=roofs[2],
floor=floors[2],
windows=windows.get("extension_2", []),
)if no_of_extension_2 > 0 else None,
ex3_property=PropertyDetail(
age_band= age_bands[3],
dimensions=dimensions["ex3"] if "ex3" in dimensions else [],
wall=walls[3],
roof=roofs[3],
floor=floors[3],
windows=windows.get("extension_3", []),
)if no_of_extension_4 > 0 else None,
conservatory=conservatory,
door=door,
ventilationAndCooling=ventilationAndCooling,
lighting=lighting,
waterHeating=waterHeating,
hotWaterCylinder=hotWaterCylinder,
solarWaterHeating=solarWaterHeating,
showerAndBaths=showerAndBaths,
flueGasHeatRecoverySystem=fghrs,
photovoltaicPanel=photovoltaicPanel,
windTurbine=windTurbine,
otherDetails=otherDetails,
mainHeating=main_heating,
secondaryHeatingType=secondary_heating_type,
mainHeating2=secondary_heating,
)
def get_age_band(self):
data = self.raw_data[self.raw_data.index('3.0 Date Built'):self.raw_data.index('4.0 Dimensions')]
avoid = [
'3.0 Date Built',
'Age Band',
'Main Property',
'Extension 1',
'Extension 2',
'Extension 3',
'Extension 4',
'4.0 Dimensions',
]
property_age = []
get_value = lambda x: None if data[data.index(x) + 1] in avoid else data[data.index(x) + 1]
age = (get_value("Main Property"))
if age:
property_age.append(age)
else:
property_age.append(None)
for i in range(1,4):
if f"Extension {i}" in data:
property_age.append(get_value(f"Extension {i}"))
else:
property_age.append(None)
return property_age
def get_dimensions(self, main, ext1=0, ext2=0, ext3=0, ext4=0):
data = self.get_data_between('4.0 Dimensions','5.0 Conservatory')
avoid = [
'4.0 Dimensions',
'5.0 Conservatory',
'Dimension Type internal',
'Part',
'Floor Area (m2)',
'Room Height (m)',
'Loss Perimeter (m)',
'Party Wall Length (m)',
'Main Property',
'Extension 1 Property',
'Extension 2 Property',
'Extension 3 Property',
'Extension 4 Property',
]
def create_dimensions_array(key, no_of_property):
index = data.index(key) + 1
allNumbers = []
for propertyNo in range(no_of_property):
numbers = []
for i in range(4):
numbers.append(data[i + propertyNo*4 + index])
details = Dimension(
floor_area_m2=float(numbers[0]),
room_height_m=float(numbers[1]),
loss_perimeter_m=float(numbers[2]),
party_wall_length_m=float(numbers[3]),
)
allNumbers.append(details)
return allNumbers
return_dict = {}
return_dict.update({"main": create_dimensions_array("Main Property", main)})
ext = [ext1, ext2, ext3, ext4]
for i in range(1,5):
if f"Extension {i} Property" in data:
return_dict.update({f"ex{i}" : create_dimensions_array(f"Extension {i} Property", ext[i-1])})
return return_dict
def is_there_a_conservatory(self):
data = self.raw_data[self.raw_data.index('5.0 Conservatory'):self.raw_data.index('7.0 Walls')]
avoid = [
'Is there a conservatory?',
'7.0 Walls'
]
get_value = lambda key: None if self.raw_data[self.raw_data.index(key) + 1] in avoid else self.raw_data[self.raw_data.index(key) + 1]
return True if get_value("Is there a conservatory?").upper() == "YES" else False
def get_section_6(self):
pass
def get_walls(self):
data = self.get_data_between('7.0 Walls','8.0 Roofs')
sub_titles = [
"Construction",
"Insulation",
"Insulation Thickness(mm)",
"Wall Thickness Measured?",
"Wall Thickness Measured",
"Wall Thickness(mm)",
"U-value Known?",
"U-value Known",
"U-value (W/m²K)",
"Dry-lining?",
"Alternative Wall Present?",
"Alternative Wall Present",
]
main_titles = [
"Main Property",
"Extension 1",
"Extension 2",
"Extension 3",
"Extension 4",
]
lst = self.two_column_with_extension_processor(data, sub_titles, main_titles)
walls = []
for ext in lst:
walls.append(Walls(
construction=ext["construction"] if ext["construction"] is not None else "",
insulation=str(ext["insulation"]),
insulation_thickness_mm=str(ext["insulation_thickness(mm)"]),
wall_thickness_measured=True if ext["wall_thickness_measured"].upper() == "YES" else False,
wall_thickness_mm=int(ext.get("wall_thickness(mm)",-1)),
u_value_known=True if ext["u_value_known"].upper() == "YES" else False,
u_value_w_m2_k=float(ext.get("u_value_(w/m²k)", -1.0)),
dry_lining=True if ext.get("dry_lining", "NO").upper() == "YES" else False,
alternative_wall_present=True if ext.get("alternative_wall_present", "NO").upper() == "YES" else False,
))
return walls
def get_roof(self):
data = self.raw_data[self.raw_data.index('8.0 Roofs'): self.raw_data.index('9.0 Floors')]
sub_titles = [
"Construction",
"Insulation Type",
"Insulation Thickness",
"U-value Known",
]
titles = [
"Main Property",
"Extension 1",
"Extension 2",
"Extension 3",
"Extension 4",
]
lst = self.two_column_with_extension_processor(data, sub_titles, titles)
roofs = []
for roof in lst:
roofs.append(Roofs(
construction=roof["construction"] if roof["construction"] is not None else "",
insulation_type=str(roof["insulation_type"]),
insulation_thickness=str(roof["insulation_thickness"]),
u_value_known=True if roof["u_value_known"].upper() == "YES" else False,
))
return roofs
def two_column_with_extension_processor(self, data, sub_titles, main_titles):
def get_value(key):
try:
index = proc_data.index(key)
value = proc_data[index + 1]
return None if value in sub_titles else value
except (ValueError, IndexError):
return None
title = None
proc_data = data
return_dict = {}
group_data = []
for items in data:
if items in main_titles:
title = items.lower().replace(" ", "_").replace("-", "_")
index = main_titles.index(items)
if main_titles[index] in data:
if return_dict:
group_data.append(return_dict)
return_dict = {}
proc_data = data[data.index(main_titles[index]):]
continue
else:
if return_dict:
group_data.append(return_dict)
break
if title is None:
continue
if items in sub_titles:
return_dict.update({f"{items.lower().replace("?", "").replace(' ', '_').replace('-','_')}": get_value(items)})
if return_dict:
group_data.append(return_dict)
return group_data
def get_floors(self):
data = self.raw_data[self.raw_data.index('9.0 Floors'): self.raw_data.index('10.0 Doors')]
sub_titles = [
"Floor Type",
"Ground Floor Construction",
"Ground Floor Insulation Type",
"Floor Insulation Thickness (mm)",
"U-value Known",
]
titles = [
"Main Property",
"Extension 1",
"Extension 2",
"Extension 3",
"Extension 4",
]
lst = self.two_column_with_extension_processor(data, sub_titles, titles)
floors = []
for floor in lst:
floors.append(Floors(
floor_type=floor.get("floor_type", ""),
ground_floor_construction=floor.get("get_floor_construction", ""),
ground_floor_insulation_type=floor.get("ground_floor_insulation_type", ""),
floor_insulation_thickness_mm=floor.get("floor_insulation_thickness_(mm)", -1) if floor.get("floor_insulation_thickness_(mm)", -1) is not None else -1,
u_value_known=True if floor.get("u_value_known").upper() == "YES" else False,
))
return floors
def get_door(self):
data = self.raw_data[self.raw_data.index("10.0 Doors"): self.raw_data.index("11.0 Windows")]
avoid = [
"10.0 Doors",
"11.0 Windows",
]
sub_titles = [
"Number of Doors",
"Number of Insulated Doors",
"U-value (W/m²K)",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return Door(
no_of_doors=int(dict_.get("no_of_doors", -1)),
no_of_insulated_doors=int(dict_.get("number_of_insulated_doors", -1)),
u_value_w_m2_k=dict_.get("u_value_(w/m²k)", "") if dict_.get("u_value_(w/m²k), '')") is not None else "",
)
def get_windows(self):
data = self.get_data_between("11.0 Windows", "12.0 Ventilation & Cooling")
headers = data[:8]
data_entries = data[8:]
num_attributes = 5
subtitles=[
"Main Property",
"Extension 1",
"Extension 2",
"Extension 3",
"Extension 4",
]
orientation = [
"north",
"east",
"west",
"south",
"n",
"w",
"s",
"e",
"nw",
"ne",
"sw",
"se",
"south west",
"south east",
"north west",
"north east",
]
def find_compose_index(lst, compose):
for i, item in enumerate(lst):
if item.lower() in compose:
return i
return None
title = None
until = 0
dict_to_return = {}
for i, items in enumerate(data_entries):
if data_entries[i] in subtitles:
title = data_entries[i].lower().replace(" ", "_").replace("-", "_")
dict_to_return.update({f"{title}":[]})
if title and until == i:
entry = data_entries[i:]
index = find_compose_index(entry,orientation)
new_entry = entry[index-3:index+3]
window = Windows(
glazing_type= new_entry[0],
area_m2=float(new_entry[1]),
roof_window=True if new_entry[2].upper() == "YES" else False,
orientation=new_entry[3].upper(),
u_value_w_m2_k=int(new_entry[4]),
g_value=int(new_entry[5]),
)
dict_to_return[f"{title}"].append(window)
until = index + 3 + i
return dict_to_return
def get_ventilation_and_cooling(self):
data = self.raw_data[self.raw_data.index('12.0 Ventilation & Cooling'): self.raw_data.index('13.0 Lighting')]
avoid = [
'12.0 Ventilation & Cooling',
'13.0 Lighting'
]
sub_titles = [
"Number of Open Fireplaces",
"Ventilation Type",
"Space Cooling System Present",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return VentilationAndCooling(
no_of_open_fireplaces=int(dict_.get("number_of_open_fireplaces", -1)),
ventilation_type=str(dict_.get("ventilation_type", "")),
space_cooling_system_present=True if dict_.get("space_cooling_system_present").upper() == "YES" else False
)
def get_lighting(self):
data = self.raw_data[self.raw_data.index('13.0 Lighting'): self.raw_data.index('14.0 Main Heating1')]
avoid = [
"13.0 Lighting",
"14.0 Main Heating1"
]
sub_titles = [
"Total number of light fittings",
"Total number of L.E.L. fittings",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid = avoid)
return Lighting(
total_no_of_light_fittings=int(dict_["total_number_of_light_fittings"]),
total_no_of_lel_fittings=int(dict_["total_number_of_l.e.l._fittings"]),
)
def get_main_heating(self):
data = self.raw_data[self.raw_data.index('14.0 Main Heating1'): self.raw_data.index('14.1 Main Heating2')]
main_titles = [
"Main Heating Type",
"Product Database"
"Main Heating System Controls",
]
sub_titles = [
"Heating Source",
"Efficiency Source",
"Heating Fuel",
"Brand Name",
"Model Name",
"Model Qualifier",
"Control Type",
"Flue Type",
"Fan Assisted Flue",
"Heat Emitter Type",
"Electricity Meter Type",
"Mains Gas Available",
]
lst_ = self.two_column_with_extension_processor(data, sub_titles, main_titles)
dict_ = lst_[0]
return Heating(
type="main",
heating_source=dict_.get("heating_source", ""),
efficiency_source=dict_.get("efficiency_source", ""),
heating_fuel=dict_.get("heating_fuel", ""),
brand_name=dict_.get("brand_name", ""),
model_name=dict_.get("model_name", ""),
model_qualifer=dict_.get("model_qualifier", ""),
controls=HeatingSystemControls(
control_type=dict_.get("control_type", "") if dict_.get("control_type", "") is not None else "",
flue_type=dict_.get("flue_type","") if dict_.get("flue_type", "") is not None else "",
fan_assisted_flue=True if dict_.get("fan_assisted_flue", "NO").upper() == "YES" else False,
heat_emitter_type=dict_.get("heat_emitter_type", "") if dict_.get("heat_emitter_type") is not None else "",
electricity_meter_type=dict_.get("electricity_meter_type", ""),
mains_gas_available=True if dict_.get("mains_gas_available", "NO").upper() == "YES" else False,
)
)
# def get_secondary_heating(self):
# data = self.raw_data[self.raw_data.index("14.1 Main Heating2"):self.raw_data.index("14.2 Secondary Heating Type")]
# main_titles = [
# "Second Main Heating Type",
# "Main Heating System Controls",
# ]
# sub_titles = [
# "Percentage of Heated Floor Area Served (%)",
# "Heating Source",
# "Efficiency Source",
# "Heating Fuel",
# "SAP 2009 Table 4a/4b",
# "Heating Type",
# "Heating Description",
# "Control Type",
# "Flue Type",
# "Fan Assisted Flue",
# "Heat Emitter Type",
# ]
# list = self.two_column_with_extension_processor(data, sub_titles, main_titles)
# dict_ = list[0]
# return Heating(
# type="secondary",
# percentage_of_heated_floor_area_served=dict_.get("percentage_of_heated_floor_area_served_(%)", ""),
# heating_source=dict_.get("heating_source", "") if dict_["heating_source"] is not None else "",
# efficiency_source=dict_.get("efficiency_source", "") if dict_["efficiency_source"] is not None else "",
# heating_fuel=dict_.get("heating_fuel", "") if dict_.get("heating_fuel") is not None else "",
# brand_name=dict_.get("brand_name", ""),
# model_name=dict_.get("model_name", ""),
# model_qualifer=dict_.get("model_qualifier", ""),
# controls=HeatingSystemControls(
# control_type=dict_.get("control_type", "") if dict_.get("control_type", "") is not None else "",
# flue_type=dict_.get("flue_type","") if dict_.get("flue_type", "") is not None else "",
# fan_assisted_flue=True if dict_.get("fan_assisted_flue", "NO").upper() == "YES" else False,
# heat_emitter_type=dict_.get("heat_emitter_type", "") if dict_.get("heat_emitter_type") is not None else "",
# electricity_meter_type=dict_.get("electricity_meter_type", "") if dict_.get("electricity_meter_type", "") is not None else "",
# mains_gas_available=True if dict_.get("mains_gas_available", "NO").upper() == "YES" else False,
# )
# )
# def get_secondary_heating_type(self):
# data = self.raw_data[self.raw_data.index("14.2 Secondary Heating Type"):self.raw_data.index("15.0 Water Heating")]
# avoid = [
# "14.2 Secondary Heating Type",
# "15.0 Water Heating",
# ]
# sub_titles = [
# "Heating Type",
# "Fuel Type",
# ]
# dict_ = self.two_columns_processor(data, sub_titles, avoid)
# return HeatingType(
# heating_type=dict_.get("heating_type", ""),
# fuel_type=dict_.get("fuel_type", ""),
# )
def get_water_heating(self):
data = self.raw_data[self.raw_data.index("15.0 Water Heating"):self.raw_data.index("15.1 Hot Water Cylinder")]
sub_titles = [
"Heating Type",
"Fuel Type",
]
avoid = [
"15.0 Water Heating",
"15.1 Hot Water Cylinder",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return WaterHeating(
heating_type=dict_.get("heating_type", ""),
fuel_type=dict_.get("fuel_type", ""),
)
def get_hot_water_cylinder(self):
data = self.raw_data[self.raw_data.index("15.1 Hot Water Cylinder"):self.raw_data.index("16.0 Solar Water Heating")]
sub_tites = [
"Volume",
"Insulation Type",
"Insulation Thickness",
"Thermostat",
]
avoid = [
"16.0 Solar Water Heating",
"15.1 Hot Water Cylinder"
]
dict_ = self.two_columns_processor(data, sub_tites, avoid)
return HotWaterCylinder(
volume=dict_.get("volume", ""),
insulation_type=dict_.get("insulation_type", ""),
insulation_thickness=dict_.get("insulation_thickness", ""),
thermostat=True if dict_.get("thermostat", "NO").upper() == "YES" else False,
)
def get_solar_water_heating(self):
data = self.raw_data[self.raw_data.index("16.0 Solar Water Heating"):self.raw_data.index("17.0 Waste Water Heat Recovery System")]
avoid = [
"16.0 Solar Water Heating",
"17.0 Waste Water Heat Recovery System",
]
sub_titles = [
"Solar Water Heating Details Known?"
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return SolarWaterHeating(
solar_water_heating_details_known=True if dict_.get("solar_water_heating_details_known", "NO").upper() == "YES" else False
)
def get_section_17(self):
"""
Furture todo
"""
raise NotImplemented("Please contact Jun-te Kim to implement this")
def get_shower_and_baths(self):
data = self.get_data_between("18.0 Showers And Baths", "19.0 Flue Gas Heat Recovery System")
sub_titles = [
"Number of Rooms with Bath and/or Shower",
]
avoid = [
"18.0 Showers And Baths",
"19.0 Flue Gas Heat Recovery System",
]
dict_1 = self.two_columns_processor(data, sub_titles, avoid)
avoid = [
"18.0 Showers And Baths",
"19.0 Flue Gas Heat Recovery System",
]
sub_titles = [
"Number of Rooms with Mixer Shower and no", # Number of Rooms with Mixer Shower and no Bath
"Number of Rooms with Mixer Shower and", # Number of Rooms with Mixer Shower and Bath
]
dict_2 = self.two_columns_processor(data, sub_titles, avoid, 2)
return ShowerAndBaths(
no_of_rooms_with_baths_and_or_shower=int(dict_1.get("number_of_rooms_with_bath_and/or_shower", -1)),
no_of_rooms_with_mixer_shower_and_no_baths=int(dict_2.get("number_of_rooms_with_mixer_shower_and_no", -1)),
no_of_rooms_with_mixer_shower_and_baths=int(dict_2.get("number_of_rooms_with_mixer_shower_and", -1)),
)
def get_fghrs(self):
data = self.get_data_between("19.0 Flue Gas Heat Recovery System","20.0 Photovoltaic Panel")
sub_titles = [
"FGHRS Present",
]
avoid = [
"19.0 Flue Gas Heat Recovery System",
"20.0 Photovoltaic Panel",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return FlueGasHeatRecoverySystem(
fghrs_present=True if dict_.get("fghrs_present", "NO").upper() == "YES" else False
)
def get_photovoltaic_panel(self):
data = self.get_data_between("20.0 Photovoltaic Panel","21.0 Wind Turbine")
sub_titles = [
"Percentage of External Roof Area with PVs"
]
avoid = [
"20.0 Photovoltaic Panel",
"21.0 Wind Turbine",
]
dict_1 = self.two_columns_processor(data, sub_titles, avoid)
sub_titles = [
"PVs are connected to dwelling electricity" # PVs are connected to dwelling electricity meter
]
dict_2 = self.two_columns_processor(data, sub_titles, avoid, 2)
return PhotovoltaicPanel(
pvs_are_connected_to_dwelling_electricity_meter=True if dict_2.get("pvs_are_connected_to_dwelling_electricity", "NO").upper() == "YES" else False,
percentage_of_external_roof_area_with_pvs=dict_1.get("percentage_of_external_roof_area_with_pvs")
)
def get_wind_turbine(self):
data = self.get_data_between("21.0 Wind Turbine","22.0 Other Details")
sub_titles = [
"Wind Turbine",
]
avoid = [
"21.0 Wind Turbine",
"22.0 Other Details",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return WindTurbine(
wind_turbine=True if dict_.get("wind_turbine", "NO").upper()=="YES" else False
)
def get_other_details(self):
data = self.get_data_between("22.0 Other Details","Recommendations (Carbon Saving Figures Are For Guidance Only)")
sub_titles = [
"Electricity Meter Type",
"Mains Gas Available",
]
avoid = [
"22.0 Other Details",
"Recommendations (Carbon Saving Figures Are For Guidance Only)",
]
dict_ = self.two_columns_processor(data, sub_titles, avoid)
return OtherDetails(
electricity_meter_type=dict_.get("electricity_meter_type", ""),
main_gas_avalible=True if dict_.get("main_gas_available", "NO").upper() == "YES" else False,
)
class EnergyPerformanceReportWithData(SiteNotesExtractor):
def __init__(self, data_list):
super().__init__(data_list)
self.type = ReportType.ENERGY_PERFORMANCE_REPORT_WITH_DATA
self.master_obj = self.setup()
def setup(self):
pass
class EnergyPerformanceReportSummaryInformation(SiteNotesExtractor):
def __init__(self, data_list):
self.raw_data = data_list
self.type = ReportType.ENERGY_PERFORMANCE_REPORT_SUMMARY_INFORMATION
self.setup()
def setup(self):
pass
class SmartEpcSiteNote(SiteNotesExtractor):
def __init__(self, data_list):
super().__init__(data_list)
self.type = ReportType.SMART_EPC_SITE_NOTE
self.master_obj = self.setup()
def setup(self) -> SmartEpcSiteNoteModel:
return SmartEpcSiteNoteModel(
header=self.get_header(),
general=self.get_general(),
building_construction=self.get_building_construction(),
roof_space=self.get_roof_space(),
windows=self.get_windows(),
main_heating=self.get_main_heating(),
secondary_heating=self.get_secondary_heating(),
water_heating=self.get_water_heating(),
ventilation=self.get_ventilation(),
renewables=self.get_renewables(),
room_count=self.get_room_count(),
misc=self.get_misc(),
customer_response=self.get_customer_response(),
addendum=self.get_addendum(),
)
def _safe_get(self, key):
try:
return self.get_next_value(self.raw_data, key)
except (ValueError, IndexError):
return None
def get_header(self) -> SmartEpcHeader:
# Address is multi-line; greedily concat lines until next known header key
address_parts = []
try:
addr_idx = self.raw_data.index("Property Address:") + 1
end_keys = {"RdSAP Assessment", "General", "Page 1", "Page 2"}
i = addr_idx
while i < len(self.raw_data) and self.raw_data[i] not in end_keys:
val = self.raw_data[i].strip()
if val:
address_parts.append(val)
i += 1
except (ValueError, IndexError):
pass
address = ", ".join(address_parts) if address_parts else None
return SmartEpcHeader(
inspection_surveyor=self._safe_get("Inspection Surveyor:"),
email_address=self._safe_get("E-Mail Address:"),
report_reference=self._safe_get("Report Reference:"),
created_on=self._safe_get("Created On:"),
date_of_inspection=self._safe_get("Date of Inspection:"),
property_address=address,
)
def get_general(self) -> SmartEpcGeneral:
return SmartEpcGeneral(
epc_checked=self._safe_get("Confirm you have checked for the existence of an"),
epc_exists=self._safe_get("Does an EPC exist at the point of carrying out this"),
inspection_date=self._safe_get("Inspection Date:"),
transaction_type=self._safe_get("Transaction Type:"),
tenure=self._safe_get("Tenure:"),
property_type=self._safe_get("Type of Property:"),
detachment_type=self._safe_get("Detachment Type:"),
number_of_storeys=self._safe_get("Number of storeys:"),
terrain_type=self._safe_get("Terrain Type:"),
number_of_extensions=self._safe_get("Number of Extensions:"),
electricity_smart_meter=self._safe_get("Is an electricity smart meter present?"),
electric_meter_type=self._safe_get("Electric meter type:"),
dwelling_export_capable=self._safe_get("Is the dwelling export-capable?"),
mains_gas_available=self._safe_get("Is mains gas available?"),
gas_smart_meter=self._safe_get("Is there a gas smart meter?"),
gas_meter_accessible=self._safe_get("Is the gas meter accessible?"),
)
def get_building_construction(self) -> SmartEpcBuildingConstruction:
measurements = self._get_floor_measurements()
return SmartEpcBuildingConstruction(
age_range=self._safe_get("Age Range:"),
age_indicators=self._safe_get("Record indicators of property age:"),
walls_construction_type=self._safe_get("Walls - Construction Type:"),
cavity_construction_indicators=self._safe_get("Record external indicators of Cavity Construction:"),
walls_insulation_type=self._safe_get("Walls - Insulation Type:"),
filled_cavity_indicators=self._safe_get("Record indicators of filled cavity:"),
thermal_conductivity=self._safe_get("Thermal conductivity of wall insulation:"),
wall_u_value_known=self._safe_get("Wall U-Value known?"),
wall_thickness=self._safe_get("Wall thickness:"),
party_wall_construction_type=self._safe_get("Party wall construction type:"),
floor_type=self._safe_get("Floor type:"),
floor_construction=self._safe_get("Floor Construction:"),
floor_insulation_type=self._safe_get("Floor Insulation Type:"),
floor_u_value_known=self._safe_get("Floor U-Value known?"),
measurements=measurements,
)
def _get_floor_measurements(self):
measurements = []
try:
bm_idx = self.raw_data.index("Building Measurements")
# Skip the header row: Area (m2), Height (m), Heat Loss Perimeter (m), PWL (m)
# Then read rows: floor_name, area, height, heat_loss, pwl
# Rows start after "PWL (m)" token
try:
header_end = self.raw_data.index("PWL (m)", bm_idx) + 1
except ValueError:
return measurements
i = header_end
# Floor rows look like: "Floor 0", "41.33", "2.46", "18.63", "7.29"
# or section headers like "Main Building"
# Stop when we hit a known section header
stop_tokens = {"Roof Space", "Windows", "Heating & Hot Water", "Page 6", "Page 7"}
while i + 4 < len(self.raw_data):
name = self.raw_data[i].strip()
if not name or name in stop_tokens or not (name.startswith("Floor") or name.startswith("Main Building")):
if name.startswith("Floor"):
pass
else:
break
if name.startswith("Floor"):
measurements.append(SmartEpcFloorMeasurement(
floor_name=name,
area_m2=self.raw_data[i + 1].strip() or None,
height_m=self.raw_data[i + 2].strip() or None,
heat_loss_perimeter_m=self.raw_data[i + 3].strip() or None,
pwl_m=self.raw_data[i + 4].strip() or None,
))
i += 5
else:
i += 1
except (ValueError, IndexError):
pass
return measurements
def get_roof_space(self) -> SmartEpcRoofSpace:
return SmartEpcRoofSpace(
construction_type=self._safe_get("Roofs - Construction Type:"),
insulation_at=self._safe_get("Roofs - Insulation At:"),
u_value=self._safe_get("Roof U-Value:"),
insulation_thickness=self._safe_get("Roofs - Insulation Thickness:"),
cavity_wall_indicators_in_roof=self._safe_get("Record indicators of Cavity Wall Construction in roof"),
rooms_in_roof=self._safe_get("Are there rooms in the roof?"),
)
def get_windows(self):
windows = []
window_num = 1
while True:
label = f"Window {window_num}"
if label not in self.raw_data:
break
try:
start = self.raw_data.index(label)
# Find next "Window N" or a known section header to bound the block
next_label = f"Window {window_num + 1}"
stop_tokens = {"Heating & Hot Water", "Main Heating Systems", "Secondary Heating System"}
if next_label in self.raw_data:
end = self.raw_data.index(next_label)
else:
end = len(self.raw_data)
for tok in stop_tokens:
try:
end = min(end, self.raw_data.index(tok))
except ValueError:
pass
block = self.raw_data[start:end]
def blk_get(key):
try:
return block[block.index(key) + 1].strip() or None
except (ValueError, IndexError):
return None
windows.append(SmartEpcWindow(
window_number=window_num,
location=blk_get("Window location:"),
wall_type=blk_get("Window wall type:"),
glazing_type=blk_get("Glazing Type:"),
window_type=blk_get("Window type:"),
frame_type=blk_get("Window frame type:"),
glazing_gap=blk_get("What size is the glazing gap?"),
draught_proofed=blk_get("Is the window draught proofed?"),
permanent_shutters=blk_get("Are there permanent shutters present?"),
height_m=blk_get("Window height:"),
width_m=blk_get("Window width:"),
orientation=blk_get("Orientation:"),
))
except (ValueError, IndexError):
break
window_num += 1
return windows
def get_main_heating(self) -> SmartEpcMainHeating:
return SmartEpcMainHeating(
selection_method=self._safe_get("How would you like to select the Heating System?"),
system_type=self._safe_get("System type:"),
product_id=self._safe_get("Product Id"),
manufacturer=self._safe_get("Manufacturer"),
model=self._safe_get("Model"),
orig_manuf=self._safe_get("Orig Manuf"),
fuel=self._safe_get("Fuel"),
seasonal_efficiency=self._safe_get("S. Efficiency"),
heating_type=self._safe_get("Type"),
condensing=self._safe_get("Condensing"),
year=self._safe_get("Year"),
mount=self._safe_get("Mount"),
open_flue=self._safe_get("Open Flue"),
fan_assist=self._safe_get("Fan Assist"),
status=self._safe_get("Status"),
pump_age=self._safe_get("Central heating pump age:"),
controls=self._safe_get("Controls:"),
fghrs=self._safe_get("Does the boiler have a Flue Gas Heat Recover"),
weather_compensator=self._safe_get("Is there a weather compensator?"),
emitter=self._safe_get("Emitter:"),
emitter_temperature=self._safe_get("Emitter Temperature:"),
)
def get_secondary_heating(self) -> SmartEpcSecondaryHeating:
return SmartEpcSecondaryHeating(
secondary_fuel=self._safe_get("Secondary Fuel"),
secondary_system=self._safe_get("Secondary System:"),
)
def get_water_heating(self) -> SmartEpcWaterHeating:
return SmartEpcWaterHeating(
water_heating_type=self._safe_get("Water Heating Type:"),
water_heating_system=self._safe_get("Water Heating System:"),
cylinder_size=self._safe_get("Cylinder Size:"),
)
def get_ventilation(self) -> SmartEpcVentilation:
return SmartEpcVentilation(
ventilation_type=self._safe_get("Ventilation type:"),
fixed_air_conditioning=self._safe_get("Has fixed air conditioning?"),
in_pcdf_database=self._safe_get("Is the ventilation in the PCDF database?"),
open_flues=self._safe_get("Number of open flues:"),
closed_flues=self._safe_get("Number of closed flues:"),
boiler_flues=self._safe_get("Number of boiler flues:"),
other_flues=self._safe_get("Number of other flues:"),
extract_fans=self._safe_get("Number of extract fans:"),
passive_vents=self._safe_get("Number of passive vents:"),
flueless_gas_fires=self._safe_get("Number of flueless gas fires:"),
pressure_test=self._safe_get("Pressure test:"),
draught_lobby=self._safe_get("Is there a draught lobby?"),
)
def get_renewables(self) -> SmartEpcRenewables:
return SmartEpcRenewables(
wind_turbines=self._safe_get("Has wind turbines?"),
solar_hot_water=self._safe_get("Has solar hot water?"),
pv_array=self._safe_get("Has photovoltaic array?"),
pv_batteries=self._safe_get("Number of PV batteries:"),
hydro=self._safe_get("Is the dwelling connected to Hydro?"),
)
def get_room_count(self) -> SmartEpcRoomCount:
return SmartEpcRoomCount(
habitable_rooms=self._safe_get("Number of habitable rooms?"),
unheated_rooms=self._safe_get("Are any of these rooms unheated?"),
external_doors=self._safe_get("Number of external doors?"),
insulated_external_doors=self._safe_get("Number of insulated external doors?"),
draughtproofed_external_doors=self._safe_get("Number of draughtproofed external doors?"),
open_chimneys=self._safe_get("Number of open chimneys?"),
blocked_chimneys=self._safe_get("Number of blocked chimneys?"),
fixed_incandescent_bulbs=self._safe_get("Number of fixed incandescent bulbs:"),
led_cfl_known=self._safe_get("Is the exact number of LED and CFL bulbs known?"),
led_bulbs=self._safe_get("Number of fixed LED bulbs:"),
cfl_bulbs=self._safe_get("Number of fixed CFL bulbs:"),
)
def get_misc(self) -> SmartEpcMisc:
shower_types = []
for i, token in enumerate(self.raw_data):
if token == "Shower outlet type:" and i + 1 < len(self.raw_data):
val = self.raw_data[i + 1].strip()
if val:
shower_types.append(val)
return SmartEpcMisc(
waste_water_heat_recovery=self._safe_get("Are there any waste water heat recovery systems?"),
number_of_baths=self._safe_get("Number of baths:"),
special_features=self._safe_get("How many special features are there at the"),
shower_outlet_types=shower_types,
conservatory=self._safe_get("Is there conservatory?"),
)
def get_customer_response(self) -> SmartEpcCustomerResponse:
return SmartEpcCustomerResponse(
customer_present=self._safe_get("Customer present?"),
willing_to_answer_survey=self._safe_get("Customer willing to answer satisfaction survey?"),
)
def get_addendum(self) -> SmartEpcAddendum:
return SmartEpcAddendum(
addendum=self._safe_get("Addendum"),
related_party_disclosure=self._safe_get("Related party disclosure"),
hard_to_treat_access_issues=self._safe_get("Hard to treat cavity walls: Property has access"),
hard_to_treat_high_exposure=self._safe_get("Hard to treat cavity walls: Property has high"),
hard_to_treat_narrow_cavities=self._safe_get("Hard to treat cavity walls: Property has narrow"),
)