diff --git a/backend/Property.py b/backend/Property.py index 79c79f8a..b80ef217 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -49,6 +49,7 @@ class Property(Definitions): self.address1 = address1 self.data = data self.old_data = None + self.property_dimensions = None self.uprn = None self.full_sap_epc = None @@ -490,6 +491,23 @@ class Property(Definitions): # Pull out spatial features self.set_spatial(spatial) + def _filter_property_dimensions(self, property_dimensions): + """ + Will filter the property dimensions dataframe to only include the relevant rows for the property + :param property_dimensions: + :return: filtered property dimensions dataframe + """ + + result = property_dimensions[(property_dimensions["PROPERTY_TYPE"] == self.data["property-type"])] + + if self.age_band: + result = result[(result["CONSTRUCTION_AGE_BAND"] == self.age_band)] + + if self.data["built-form"] not in self.DATA_ANOMALY_MATCHES: + result = result[(result["BUILT_FORM"] == self.data["built-form"])] + + return result[["NUMBER_HABITABLE_ROOMS", "TOTAL_FLOOR_AREA"]].mean() + def set_basic_property_dimensions(self): """ This method sets the number of floors of the property, using a simple approach based on an estimate for @@ -505,23 +523,33 @@ class Property(Definitions): self.floor_area = float(self.data["total-floor-area"]) - number_of_rooms = float(self.data["number-habitable-rooms"]) + if not self.data["number-habitable-rooms"] or ( + self.data["floor-height"] == "" or self.data["floor-height"] in self.DATA_ANOMALY_MATCHES + ): + property_dimensions = read_dataframe_from_s3_parquet( + bucket_name=DATA_BUCKET, file_key=f"property_dimensions/{self.data['local-authority']}.parquet" + ) + self.property_dimensions = self._filter_property_dimensions(property_dimensions) + + if not self.data["number-habitable-rooms"]: + self.number_of_rooms = float(self.property_dimensions["NUMBER_HABITABLE_ROOMS"].round()) + else: + self.number_of_rooms = float(self.data["number-habitable-rooms"]) if self.data["property-type"] == "House": - self.number_of_floors = estimate_floors(self.floor_area, number_of_rooms) + self.number_of_floors = estimate_floors(self.floor_area, self.number_of_rooms) elif self.data["property-type"] == "Flat": self.number_of_floors = 1 else: raise NotImplementedError("Implement me") if self.data["floor-height"] == "" or self.data["floor-height"] in self.DATA_ANOMALY_MATCHES: - self.floor_height = 2.3 - print("This is where we should fill with cleaned data") + self.floor_height = float(self.property_dimensions["FLOOR_HEIGHT"].round()) else: self.floor_height = float(self.data["floor-height"]) self.perimeter = estimate_perimeter( - self.floor_area / self.number_of_floors, number_of_rooms / self.number_of_floors + self.floor_area / self.number_of_floors, self.number_of_rooms / self.number_of_floors ) self.insulation_wall_area = estimate_wall_area( diff --git a/backend/app/db/models/materials.py b/backend/app/db/models/materials.py index 00430b1c..09d7369d 100644 --- a/backend/app/db/models/materials.py +++ b/backend/app/db/models/materials.py @@ -12,6 +12,7 @@ class MaterialType(enum.Enum): solid_floor_insulation = "solid_floor_insulation" external_wall_insulation = "external_wall_insulation" internal_wall_insulation = "internal_wall_insulation" + cavity_wall_insulation = "cavity_wall_insulation" class DepthUnit(enum.Enum): diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 1aded11d..e1c0830f 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -97,18 +97,6 @@ async def trigger_plan(body: PlanTriggerRequest): if not input_properties: return Response(status_code=204) - local_property_data = [] - for p in input_properties: - local_property_data.append( - { - "id": p.id, - "uprn": p.uprn, - "data": p.data, - "full_sap_epc": p.full_sap_epc, - "old_data": p.old_data, - } - ) - logger.info("Getting EPC, and spatial data") for p in input_properties: p.search_address_epc() diff --git a/etl/property_dimensions/app.py b/etl/property_dimensions/app.py index 9e797308..876d67e2 100644 --- a/etl/property_dimensions/app.py +++ b/etl/property_dimensions/app.py @@ -33,10 +33,12 @@ def app(): data = data[~pd.isnull(data["CONSTRUCTION_AGE_BAND"])] data = data[~data["CONSTRUCTION_AGE_BAND"].isin(Definitions.DATA_ANOMALY_MATCHES)] data = data[~pd.isnull(data["TOTAL_FLOOR_AREA"])] + data = data[~pd.isnull(data["NUMBER_HABITABLE_ROOMS"])] + data = data[~pd.isnull(data["FLOOR_HEIGHT"])] df = ( data.groupby(GROUPBY) - .agg({"NUMBER_HABITABLE_ROOMS": "median", "TOTAL_FLOOR_AREA": "mean"}) + .agg({"NUMBER_HABITABLE_ROOMS": "median", "TOTAL_FLOOR_AREA": "mean", "FLOOR_HEIGHT": "mean"}) .reset_index() ) diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index 8464c02b..d2178554 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -137,8 +137,12 @@ class WallRecommendations(Definitions): Width of a cavity: There are various sources online that suggest the width of a cavity wall is around 50mm. The retrofit course - indicates that most cavities are 50-75mm. Many sources online indicate that 50mm is the standard figure - therefore we'll use 50mm as the base assumption + indicates that most cavities are 50-75mm. Many sources online indicate that 50mm is the standard MINIMUM figure + therefore we'll use 75mm as the base assumption + + This document: + https://www.buildingcentre.co.uk/media/_file/pdf/22220_pdf27.pdf + Indicates that they could be 50-85mm wide :param u_value: u_value of the starting wall :param insulation_thickness: describes the insulation level of the wall. If "below average", we have a partially @@ -146,9 +150,9 @@ class WallRecommendations(Definitions): """ cavity_wall_fills = [m for m in self.materials if m["type"] == "cavity_wall_insulation"] - cavity_width = 50 + cavity_width = 75 if insulation_thickness == "below average": - cavity_width = 50 * (1 - PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION) + cavity_width = cavity_width * (1 - PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION) # Test the different fill options lowest_selected_u_value = None