From 8e5388b1ea9bb36dd5d3e2e157bce70b545cf8ce Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 18 Aug 2025 18:02:56 +0100 Subject: [PATCH] adding materials to recommender --- backend/apis/GoogleSolarApi.py | 38 ++++++++++++++++----- backend/engine/engine.py | 5 +-- recommendations/FireplaceRecommendations.py | 2 +- recommendations/HeatingRecommender.py | 10 +++--- recommendations/Recommendations.py | 6 ++-- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/backend/apis/GoogleSolarApi.py b/backend/apis/GoogleSolarApi.py index cda32faa..51e9c893 100644 --- a/backend/apis/GoogleSolarApi.py +++ b/backend/apis/GoogleSolarApi.py @@ -60,7 +60,7 @@ class GoogleSolarApi: # Error Messages ENTITY_NOT_FOUND_ERROR = 'Requested entity was not found.' - def __init__(self, api_key, max_retries=5): + def __init__(self, api_key, solar_materials: list, max_retries=5): """ Initialize the GoogleSolarApi class with the provided API key and maximum retries. @@ -87,6 +87,7 @@ class GoogleSolarApi: # Indicates if we think we have both units attached to a semi-detached property self.double_property = False + self.solar_materials = solar_materials def get_building_insights(self, longitude, latitude, required_quality="MEDIUM", max_retries=None): """ @@ -208,13 +209,13 @@ class GoogleSolarApi: self.optimise_solar_configuration( energy_consumption=energy_consumption, is_building=is_building, - property_instance=property_instance + property_instance=property_instance, ) # Finally, if we have a double property, we half the data we stored area if self.double_property: self.roof_area = self.roof_area / 2 - self.floor_area = self.floor_area / 2 + self.floor_area = float(self.floor_area) / 2 def save_to_db(self, session, uprns_to_location, scenario_type): if self.insights_data is None: @@ -279,7 +280,9 @@ class GoogleSolarApi: installation_life_span)) / (1 - efficiency_depreciation_factor)) - def optimise_solar_configuration(self, energy_consumption, is_building=False, property_instance=None): + def optimise_solar_configuration( + self, energy_consumption, is_building=False, property_instance=None + ): """ Optimise the solar panel configuration for the building. :return: @@ -321,9 +324,25 @@ class GoogleSolarApi: if roi_summary["n_panels"].sum() < min_panels: continue + total_panels = roi_summary["n_panels"].sum() + # find a product which is suitable for the ROI calc, without a battery. 400 Watts for the baseline + solar_product = next( + (m for m in self.solar_materials if m["type"] == "solar_pv" and + abs(m["size"] - (400 * total_panels) / 1000) < 0.1 and not m["includes_battery"]), + None + ) + + if solar_product is None: + logger.info("No suitable solar product found for the configuration with %d panels.", total_panels) + continue + total_cost = Costs.solar_pv( - n_panels=roi_summary["n_panels"].sum(), - has_battery=False, + solar_product=solar_product, + # We don't actually need scaffolding for the ROI calc + scaffolding_options=[ + {"total_cost": 1000, "size": property_instance.number_of_floors}, + {"total_cost": 1000, "size": 3} + ], # Assume the most amount of scaffolding n_floors=3 if property_instance is None else property_instance.number_of_floors )["total"] @@ -805,7 +824,8 @@ class GoogleSolarApi: @classmethod def unit_solar_analysis( - cls, unit_solar_config: List, input_properties: List[Property], session, body, google_solar_api_key: str + cls, unit_solar_config: List, input_properties: List[Property], session, body, google_solar_api_key: str, + solar_materials: list ): if not unit_solar_config: @@ -844,7 +864,7 @@ class GoogleSolarApi: ) continue - solar_api_client = cls(api_key=google_solar_api_key) + solar_api_client = cls(api_key=google_solar_api_key, solar_materials=solar_materials) solar_api_client.get( longitude=unit["longitude"], latitude=unit["latitude"], @@ -852,7 +872,7 @@ class GoogleSolarApi: is_building=False, session=session, uprn=unit["uprn"], - property_instance=property_instance + property_instance=property_instance, ) # Store the data in the database diff --git a/backend/engine/engine.py b/backend/engine/engine.py index 7bc083d8..14e5d85a 100644 --- a/backend/engine/engine.py +++ b/backend/engine/engine.py @@ -689,7 +689,7 @@ async def model_engine(body: PlanTriggerRequest): building_solar_config=building_solar_config, input_properties=input_properties, session=session, - google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY + google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY, ) input_properties = GoogleSolarApi.unit_solar_analysis( @@ -697,7 +697,8 @@ async def model_engine(body: PlanTriggerRequest): input_properties=input_properties, session=session, body=body, - google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY + solar_materials=[m for m in materials if m["type"] == "solar_pv"], + google_solar_api_key=get_settings().GOOGLE_SOLAR_API_KEY, ) logger.info("Identifying property recommendations") diff --git a/recommendations/FireplaceRecommendations.py b/recommendations/FireplaceRecommendations.py index 60e54073..60445821 100644 --- a/recommendations/FireplaceRecommendations.py +++ b/recommendations/FireplaceRecommendations.py @@ -11,7 +11,7 @@ class FireplaceRecommendations(Definitions): def __init__( self, property_instance: Property, - materials: list = None, + materials: list, ): self.property = property_instance diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 60163090..e7e008d5 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -1,11 +1,11 @@ import re import backend.app.assumptions as assumptions -from recommendations.Costs import Costs, BOILER_UPGRADE_SCHEME_ASHP_VALUE from recommendations.recommendation_utils import ( check_simulation_difference, override_costs, combine_recommendation_configs ) from backend.Property import Property from backend.app.plan.schemas import MEASURE_MAP +from recommendations.Costs import Costs from etl.epc_clean.epc_attributes.MainheatAttributes import MainHeatAttributes from etl.epc_clean.epc_attributes.HotWaterAttributes import HotWaterAttributes from etl.epc_clean.epc_attributes.MainFuelAttributes import MainFuelAttributes @@ -85,7 +85,7 @@ class HeatingRecommender: } } - def __init__(self, property_instance: Property, materials: list = None): + def __init__(self, property_instance: Property, materials: list): self.property = property_instance self.costs = Costs(self.property) @@ -573,12 +573,12 @@ class HeatingRecommender: if has_cavity_or_loft_recommendations: description = description + ( f" You must ensure that the property has an insulated cavity and " - f"270mm+ loft insulation to qualify for the grant, to claim £" - f"{BOILER_UPGRADE_SCHEME_ASHP_VALUE} of funding from the boiler upgrade scheme grant. " + f"270mm+ loft insulation to qualify for the grant, to claim £7,500" + f" of funding from the boiler upgrade scheme grant. " ) else: description = description + ( - f" £{BOILER_UPGRADE_SCHEME_ASHP_VALUE} of funding can be claimed from the boiler upgrade scheme" + f" £7,500 of funding can be claimed from the boiler upgrade scheme" ) simulation_config = { diff --git a/recommendations/Recommendations.py b/recommendations/Recommendations.py index 462d43aa..614e4a4a 100644 --- a/recommendations/Recommendations.py +++ b/recommendations/Recommendations.py @@ -65,11 +65,11 @@ class Recommendations: property_instance=property_instance, materials=materials ) self.draught_proofing_recommender = DraughtProofingRecommendations(property_instance=property_instance) - self.fireplace_recommender = FireplaceRecommendations(property_instance=property_instance) + self.fireplace_recommender = FireplaceRecommendations(property_instance=property_instance, materials=materials) self.lighting_recommender = LightingRecommendations(property_instance=property_instance, materials=materials) self.windows_recommender = WindowsRecommendations(property_instance=property_instance, materials=materials) - self.solar_recommender = SolarPvRecommendations(property_instance=property_instance) - self.heating_recommender = HeatingRecommender(property_instance=property_instance) + self.solar_recommender = SolarPvRecommendations(property_instance=property_instance, materials=materials) + self.heating_recommender = HeatingRecommender(property_instance=property_instance, materials=materials) self.hotwater_recommender = HotwaterRecommendations(property_instance=property_instance) self.secondary_heating_recommender = SecondaryHeating(property_instance=property_instance)