Updating logic for extracting heat loss perimeter and party walls from xml data

This commit is contained in:
Khalim Conn-Kowlessar 2024-07-26 15:39:47 +01:00
parent bdd6171626
commit 2c931b4383
5 changed files with 74 additions and 19 deletions

View file

@ -76,6 +76,7 @@ class Property:
already_installed=None,
non_invasive_recommendations=None,
measures=None,
energy_assessment=None,
**kwargs
):
@ -178,6 +179,11 @@ class Property:
self.recommendations_scoring_data = []
self.simulation_epcs = {}
# This additional condition data should change how we pass kwargs to this. We should no longer need to pass
# kwargs to this class, but instead, we should pass the energy assessment condition data
self.energy_assessment_condition_data = energy_assessment["condition"]
# TODO: We keep this but only temporarily until we add bathrooms, bedrooms, building id to the condition data
self.parse_kwargs(kwargs)
@classmethod
@ -188,6 +194,10 @@ class Property:
:param kwargs:
:return:
"""
# Note - none of this data is contained in an energy asssessment, but we should consider how this is done
# as we collect more data from the energy assessment
n_bathrooms = kwargs.get("n_bathrooms", None)
if n_bathrooms not in [None, ""]:
# We add on a small value to ensure that the number of bathrooms is rounded up, in case the value is 0.5
@ -1034,9 +1044,14 @@ class Property:
# TODO: These functions should work on an EPCRecord object, so that the format is more standardised.
# They could also be added as attributes to the EPC Record
self.perimeter = estimate_perimeter(
self.floor_area / self.number_of_floors,
self.number_of_rooms / self.number_of_floors,
# Many of these pieces of information are now contained in the condition data
condition_data = self.energy_assessment_condition_data.copy()
self.perimeter = float(self.energy_assessment_condition_data["perimeter"]) \
if condition_data["perimeter"] is not None \
else estimate_perimeter(
floor_area=self.floor_area / self.number_of_floors,
num_rooms=self.number_of_rooms / self.number_of_floors
)
self.insulation_wall_area = estimate_external_wall_area(

View file

@ -150,13 +150,13 @@ class EnergyAssessment(Base):
epc = {key.replace("_", "-"): getattr(self, key) for key in self.EPC_KEYS}
# Get everything else
additional = {
condition = {
column.name: getattr(self, column.name)
for column in self.__table__.columns if column.name not in self.EPC_KEYS
}
return {"epc": epc, "additional": additional}
return {"epc": epc, "condition": condition}
@staticmethod
def empty_response():
return {"epc": {}, "additional": {}}
return {"epc": {}, "condition": {}}

View file

@ -360,6 +360,27 @@ async def trigger_plan(body: PlanTriggerRequest):
# Otherwise, we use the newest EPC
epc_records = create_epc_records(epc_searcher, energy_assessment)
patch = next((
x for x in patches if (x["address"] == config["address"]) and (x["postcode"] == config["postcode"])
), {})
epc_records = patch_epc(patch, epc_records)
prepared_epc = EPCRecord(
epc_records=epc_records,
run_mode="newdata",
cleaning_data=cleaning_data
)
property_already_installed = next((
x for x in already_installed if
(x["address"] == config["address"]) and (x["postcode"] == config["postcode"])
), {})
property_non_invasive_recommendations = next((
x for x in non_invasive_recommendations if
(x["address"] == config["address"]) and (x["postcode"] == config["postcode"])
), {})
input_properties.append(
Property(
id=property_id,
@ -368,7 +389,8 @@ async def trigger_plan(body: PlanTriggerRequest):
epc_record=prepared_epc,
already_installed=property_already_installed,
non_invasive_recommendations=property_non_invasive_recommendations,
**Property.extract_kwargs(config)
energy_assessment=energy_assessment,
**Property.extract_kwargs(config), # TODO: Depraecate this
)
)

View file

@ -645,19 +645,25 @@ class XmlParser:
self.number_of_floors = len(
[f for f in self.floor_dimensions if f["building_part_identifier"] == "Main Dwelling"]
)
self.heat_loss_perimeter = max(
[
float(f["heat_loss_perimeter"]) for f in self.floor_dimensions
if f["building_part_identifier"] == "Main Dwelling" and not f["room_roof"]
]
)
self.party_wall_length = max(
[
float(f["party_wall_length"]) for f in self.floor_dimensions
if f["building_part_identifier"] == "Main Dwelling" and not f["room_roof"]
]
)
# We extract the maximum heat loss perimeter, per building part
max_heat_loss_perimeters = {d['building_part_identifier']: max(
(float(x['heat_loss_perimeter']) for x in self.floor_dimensions if
x['building_part_identifier'] == d['building_part_identifier'] and x['heat_loss_perimeter']),
default=float('-inf')
) for d in self.floor_dimensions}
self.heat_loss_perimeter = sum(max_heat_loss_perimeters.values())
max_party_walls = {
d['building_part_identifier']: max(
(float(x['party_wall_length']) for x in self.floor_dimensions if
x['building_part_identifier'] == d['building_part_identifier'] and x['party_wall_length']),
default=float('-inf')
) for d in self.floor_dimensions
}
self.party_wall_length = sum(max_party_walls.values())
self.perimeter = self.heat_loss_perimeter + self.party_wall_length

View file

@ -48,6 +48,9 @@ def main():
# TODO: IF we have many uploads, we can do them in a batch so we don't try and upload huge amounts of data to
# the database at onece
# TODO: We now have detailed information about primary and secondary walls, so we should use this information
# in our recommendations when we have it
# For each property, we download the xmls and extract the data
database_data = []
for uprn, xmls in assessments_map.items():
@ -117,3 +120,12 @@ def main():
# https://www.ncm-pcdb.org.uk/sap/download
# However retrieving this data is not a priority, so we can leave this for now as parsing the database
# is a non-trivial task
# TODO: The condition report contains additional data such as the number of bedrooms and the number of bathrooms
# We can extract this data and store it in the database as well. We can then update our kwargs methodology
# that is passed to the property class, where instead we store this additional data in our database (it could
# be stored in the energy assessment table, or in a separate table) and then when we're passed additional data
# we can query the database for this data and use it to update the property object, instead of storing it
# in the asset list and pulling it out of the asset list
# 1) Bathrooms
# 2) Bedrooms