From 2a002c1fafb95425fad318eea289d7a0e5def2f2 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 20 Jul 2023 18:02:17 +0100 Subject: [PATCH] refactoring recommendations --- .idea/Model.iml | 2 +- .idea/misc.xml | 2 +- backend/app/plan/router.py | 44 ++++++++++++++-- model_data/app.py | 28 +++++----- recommendations/FloorRecommendations.py | 6 ++- recommendations/WallRecommendations.py | 70 ++++++++++++------------- 6 files changed, 92 insertions(+), 60 deletions(-) diff --git a/.idea/Model.iml b/.idea/Model.iml index ac61a988..81384fa9 100644 --- a/.idea/Model.iml +++ b/.idea/Model.iml @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 242c02bb..3b05c6ac 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index dbc01935..4a68e7a7 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -102,18 +102,52 @@ async def trigger_plan(body: PlanTriggerRequest): logger.info("Getting components and properties recommendations") - for p in input_properties: + recommendations = [] + for property_id, p in enumerate(input_properties): # For each property, classiy floor area decide total_floor_area_group_decile = classify_decile_newvalues( decile_boundaries=floors_decile_data["decile_boundaries"], decile_labels=floors_decile_data["decile_labels"], new_values=[float(p.data["total-floor-area"])], )[0] - print("hey") - print(total_floor_area_group_decile) - for p in input_properties: + # Property recommendations p.get_components(cleaned) - # floor_recommendations = FloorRecommendations(property_instance=p, uvalue_estimates=uvalue_estimates_floors) + + # Floor recommendations + floor_recommender = FloorRecommendations(property_instance=p, uvalue_estimates=uvalue_estimates_floors) + floor_recommender.recommend() + floor_recommendations = floor_recommender.recommendations + # insert property id + for rec in floor_recommendations: + rec["property_id"] = property_id + + # Wall recommendations + # We would make this u-value query directly to the database + total_floor_area_group_decile = classify_decile_newvalues( + decile_boundaries=walls_decile_data["decile_boundaries"], + decile_labels=walls_decile_data["decile_labels"], + new_values=[float(p.data["total-floor-area"])], + )[0] + + # This + walls_u_value_estimate = [ + x for x in uvalue_estimates_walls + if (x['local-authority'] == p.data["local-authority"]) & + (x['property-type'] == p.data["property-type"]) & + (x['built-form'] == p.data["built-form"]) & + (x['walls-energy-eff'] == p.data["walls-energy-eff"]) & + (x['walls-env-eff'] == p.data["walls-env-eff"]) & + (x['total-floor-area_group'] == total_floor_area_group_decile) + ] + + wall_recomendations = WallRecommendations(property_instance=p, uvalue_estimates=walls_u_value_estimate) + wall_recomendations.recommend() + wall_recomendations = wall_recomendations.recommendations + # insert property id + for rec in wall_recomendations: + rec["property_id"] = property_id + + recommendations.extend(floor_recommendations) return {"message": "Plan complete"} diff --git a/model_data/app.py b/model_data/app.py index fd97ddb8..74fa5c15 100644 --- a/model_data/app.py +++ b/model_data/app.py @@ -341,20 +341,20 @@ def app(): ) # Production of sample data for land registry - address_meta = [ - { - "postcode": x["postcode"].upper(), - "address1": x["address1"].upper(), - "address2": x["address2"].upper(), - "address3": x["address3"].upper(), - "address": x["address"], - "uprn": x["uprn"] - } for x in data - ] - - import pickle - with open("sample_addresses.pkl", "wb") as f: - pickle.dump(address_meta, f) + # address_meta = [ + # { + # "postcode": x["postcode"].upper(), + # "address1": x["address1"].upper(), + # "address2": x["address2"].upper(), + # "address3": x["address3"].upper(), + # "address": x["address"], + # "uprn": x["uprn"] + # } for x in data + # ] + # + # import pickle + # with open("sample_addresses.pkl", "wb") as f: + # pickle.dump(address_meta, f) # Incorporate input data into cleaning cleaner = EpcClean(data) diff --git a/recommendations/FloorRecommendations.py b/recommendations/FloorRecommendations.py index acbc0ddf..6641bd28 100644 --- a/recommendations/FloorRecommendations.py +++ b/recommendations/FloorRecommendations.py @@ -88,7 +88,9 @@ class FloorRecommendations(BaseUtility): "Ground": 0, # We don't know what floor level, we just make sure it's not 0 "mid floor": 1, - "4th": 4 + "4th": 4, + # We set + "00": 0, } def __init__(self, property_instance: Property, uvalue_estimates): @@ -253,7 +255,7 @@ class FloorRecommendations(BaseUtility): self.recommend_floor_insulation(u_value=u_value, parts=solid_floor_insulation_parts) def _get_floors_uvalue_estimate(self, total_floor_area_group_decile): - + blah """ Wrapper function which contains the methodology to extract a property's walls u-value estimate when we don't have a true value and if we can't base our assumption off of the material diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index be618433..572bdf92 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -1,5 +1,6 @@ import itertools import math +from statistics import mean from model_data.Property import Property from model_data.BaseUtility import BaseUtility @@ -321,10 +322,12 @@ class WallRecommendations(BaseUtility): if new_u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value) - recommendations.append({ - "parts": [get_recommended_part(part, depth)], - "new_u_value": new_u_value, - }) + recommendations.append( + { + "parts": [get_recommended_part(part, depth)], + "new_u_value": new_u_value, + } + ) return recommendations @@ -405,46 +408,39 @@ class WallRecommendations(BaseUtility): :return: """ - total_floor_area_group_decile = self.uvalue_estimates.classify_decile_newvalues( - decile_boundaries=self.uvalue_estimates.walls_decile_data["decile_boundaries"], - decile_labels=self.uvalue_estimates.walls_decile_data["decile_labels"], - new_values=[float(self.property.data["total-floor-area"])], - )[0] - - u_value_estimate = self.uvalue_estimates.walls[ - (self.uvalue_estimates.walls["local-authority"] == self.property.data["local-authority"]) & - (self.uvalue_estimates.walls["property-type"] == self.property.data["property-type"]) & - (self.uvalue_estimates.walls["built-form"] == self.property.data["built-form"]) & - (self.uvalue_estimates.walls["walls-energy-eff"] == self.property.data["walls-energy-eff"]) & - (self.uvalue_estimates.walls["walls-env-eff"] == self.property.data["walls-env-eff"]) & - (self.uvalue_estimates.walls["total-floor-area_group"] == total_floor_area_group_decile) - ] - - if u_value_estimate.empty: + if not self.uvalue_estimates: raise ValueError("No U-value estimate found for the given property") # Because of how spuriously populated the data is for number-habitable-rooms and number-heated-rooms, # we will try and filter on these to see if we get a result - habitable_rooms_filter = ( - self.uvalue_estimates.walls["number-habitable-rooms"] == self.property.data["number-habitable-rooms"] + habitable_rooms_filer = [ + x for x in self.uvalue_estimates if + x["number-habitable-rooms"] == self.property.data["number-habitable-rooms"] + ] + + if not habitable_rooms_filer: + # Take a mean of all the u-value estimates + return mean( + [x["median_thermal_transmittance"] for x in self.uvalue_estimates if x["median_thermal_transmittance"]] + ) + + # Try perform a filter on heated rooms + heated_rooms_filter = [ + x for x in habitable_rooms_filer if + x["number-heated-rooms"] == self.property.data["number-heated-rooms"] + ] + + if not heated_rooms_filter: + # Take a mean of all the u-value estimates + return mean( + [x["median_thermal_transmittance"] for x in habitable_rooms_filer if x["median_thermal_transmittance"]] + ) + + return mean( + [x["median_thermal_transmittance"] for x in heated_rooms_filter if x["median_thermal_transmittance"]] ) - if any(habitable_rooms_filter): - u_value_estimate = u_value_estimate[habitable_rooms_filter] - - heated_rooms_filter = ( - self.uvalue_estimates.walls["number-heated-rooms"] == self.property.data["number-heated-rooms"] - ) - - if any(heated_rooms_filter): - u_value_estimate = u_value_estimate[heated_rooms_filter] - - # It's possible for us to have multiple rows if we didn't do a habitable/heated rooms filter so we - # average - - return u_value_estimate["median_thermal_transmittance"].mean() - @staticmethod def rvalue_per_mm(total_r_value: float, thickness_mm: float) -> float: """Return R-value per mm.