From fe193305e672b49eeb3862b903a2552a8c21e334 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 24 Jan 2025 11:31:41 +0000 Subject: [PATCH] paused for the moment --- backend/Funding.py | 53 ++++++++++++++++++++++++++++++--- backend/app/plan/router.py | 61 ++++++++++++++++++++++---------------- etl/funding/app.py | 35 ++++++++++++++++++++++ 3 files changed, 120 insertions(+), 29 deletions(-) create mode 100644 etl/funding/app.py diff --git a/backend/Funding.py b/backend/Funding.py index 8a9b08ae..f0780c51 100644 --- a/backend/Funding.py +++ b/backend/Funding.py @@ -12,6 +12,8 @@ class Funding: and flag any tenant specific requirements that need to be considered to the funding to be attained """ + SCHEMES = ["eco4", "gbis", "whlg"] + ECO_SAP_SCORE_THREHOLDS = [ {'Band': 'High_A', 'From': 96.0, 'Up to': 100.0, 'Mid-point': 98.0}, {'Band': 'Low_A', 'From': 92.0, 'Up to': 96.0, 'Mid-point': 94.0}, @@ -34,10 +36,12 @@ class Funding: tenure: HousingType, starting_epc, starting_sap, + postcode, floor_area, council_tax_band, property_recommendations, project_scores_matrix, + whlg_eligible_postcodes, gbis_abs_rate: int, eco4_abs_rate: int, ): @@ -47,6 +51,10 @@ class Funding: :param starting_epc: The current EPC rating of the property :param starting_sap: The current SAP score for the property :param floor_area: The total floor area of the property + :param council_tax_band: The council tax band of the property + :param property_recommendations: The recommendations for the property + :param project_scores_matrix: The matrix of project scores for ECO4 + :param whlg_eligible_postcodes: The postcodes eligible for WHLG :param gbis_abs_rate: The assumed £/abs achieved by the installer for GBIS :param eco4_abs_rate: The assumed £/abs achieved by the installer for ECO4 """ @@ -58,6 +66,7 @@ class Funding: self.tenure = tenure self.starting_epc = starting_epc self.starting_sap = starting_sap + self.postcode = postcode self.starting_eco_band = self.sap_to_eco_band(self.starting_sap) self.floor_area_segment = self.classify_floor_area(floor_area) self.gbis_abs_rate = gbis_abs_rate @@ -75,6 +84,11 @@ class Funding: (project_scores_matrix["Starting Band"] == self.starting_eco_band) ] + # The postcode column is already lower case + self.whlg_eligible_postcodes = whlg_eligible_postcodes[ + whlg_eligible_postcodes["Postcode"] == self.postcode.lower() + ] + # Store the final outputs self.gbis_eligibiltiy = {} self.eco4_eligibility = {} @@ -82,6 +96,8 @@ class Funding: def output( self, + scheme: str, + eligible: bool, measure_types: List[str], estimated_funding: float, notify_tenant_benefits_requirements: bool, @@ -90,12 +106,18 @@ class Funding: ): """" """ + + if scheme not in self.SCHEMES: + raise ValueError("Scheme not recognised") + return { + "scheme": scheme, + "eligible": eligible, "measure_types": measure_types, "estimated_funding": estimated_funding, - "notify_tenant_benefits_requirements": notify_tenant_benefits_requirements, - "notify_council_tax_band_requirements": notify_council_tax_band_requirements, - "notify_tenant_low_income_requirements": notify_tenant_low_income_requirements + "requires_benefits": notify_tenant_benefits_requirements, + "requires_council_tax_band": notify_council_tax_band_requirements, + "requires_low_income": notify_tenant_low_income_requirements } @staticmethod @@ -234,6 +256,8 @@ class Funding: # If the council tax band is missing, we nofify the customer that this is a requirement that # should be checked return self.output( + scheme="gbis", + eligible=True, measure_types=[recommended_measure["measure_type"]], estimated_funding=recommended_measure["estimated_funding"], notify_tenant_benefits_requirements=False, @@ -251,6 +275,8 @@ class Funding: # We find the best measure for GBIS recommended_measure = self.find_best_gbis_measure(measures=valid_measures) return self.output( + scheme="gbis", + eligible=True, measure_types=[recommended_measure["measure_type"]], estimated_funding=recommended_measure["estimated_funding"], notify_tenant_benefits_requirements=True, @@ -260,6 +286,8 @@ class Funding: # Otherwise, no funding availability return self.output( + scheme="gbis", + eligible=False, measure_types=[], estimated_funding=0, notify_tenant_benefits_requirements=False, @@ -279,6 +307,23 @@ class Funding: raise NotImplementedError("Implement social/oo") + def whlg(self): + if self.tenure == "Social": + # We can't do anything for social housing + self.whlg_eligibility = self.output( + scheme="whlg", + eligible=False, + measure_types=[], + estimated_funding=0, + notify_tenant_benefits_requirements=False, + notify_council_tax_band_requirements=False, + notify_tenant_low_income_requirements=False + ) + return + + if not self.whlg_eligible_postcodes.empty: + print("Eligible implement me!") + def eco4(self): if self.tenure == "Private": self.eco4_eligibiltiy = self.eco4_prs() @@ -292,4 +337,4 @@ class Funding: self.gbis() # self.eco4() - # self.whlg() + self.whlg() diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 1b72e10e..04a2ef7f 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -388,15 +388,26 @@ def extract_property_request_data( return patch, property_already_installed, property_non_invasive_recommendations, property_valution -def get_eco_project_scores_matrix(): - data = read_csv_from_s3( +def get_funding_data(): + """ + This function retrieves the eco project scores matrix and the warm homes local grant funding data + :return: + """ + project_scores_matrix = read_csv_from_s3( bucket_name=get_settings().DATA_BUCKET, filepath="funding/ECO4 Full Project Scores Matrix.csv", ) - df = pd.DataFrame(data) - df.columns = ['Floor Area Segment', 'Starting Band', 'Finishing Band', 'Cost Savings'] - df["Cost Savings"] = df["Cost Savings"].astype(float) - return df + project_scores_matrix = pd.DataFrame(project_scores_matrix) + project_scores_matrix.columns = ['Floor Area Segment', 'Starting Band', 'Finishing Band', 'Cost Savings'] + project_scores_matrix["Cost Savings"] = project_scores_matrix["Cost Savings"].astype(float) + + whlg_eligible_postcodes = read_csv_from_s3( + bucket_name=get_settings().DATA_BUCKET, + filepath="funding/whlg eligible postcodes.csv", + ) + whlg_eligible_postcodes = pd.DataFrame(whlg_eligible_postcodes) + + return project_scores_matrix, whlg_eligible_postcodes router = APIRouter( @@ -544,7 +555,7 @@ async def trigger_plan(body: PlanTriggerRequest): logger.info("Reading in materials and cleaned datasets") materials = get_materials(session) cleaned = get_cleaned() - eco_project_scores_matrix = get_eco_project_scores_matrix() + eco_project_scores_matrix, whlg_eligible_postcodes = get_funding_data() kwh_client = KwhData(bucket=get_settings().DATA_BUCKET, read_consumption_data=True) @@ -688,9 +699,7 @@ async def trigger_plan(body: PlanTriggerRequest): # Insert the predictions into the recommendations and run the optimiser # TODO: If a recommendation has a negative impact on SAP, we should remove it - this seems to have become a - # possibility with heating system - # TODO: After optimising, if there are any cheap, quick win measures (e.g. insulate water tank with hot water - # cylinder jacket), we should add these to the recommendations as default + # possibility with heating system? for p in input_properties: if not recommendations.get(p.id): @@ -802,21 +811,23 @@ async def trigger_plan(body: PlanTriggerRequest): # Funding # ~~~~~~~~~~~~~~~~ - # for p in input_properties: - # funding_calulator = Funding( - # tenure=body.housing_type, - # starting_epc=p.data["current-energy-rating"], - # starting_sap=int(p.data["current-energy-efficiency"]), - # floor_area=p.floor_area, - # council_tax_band=None, # This is seemingly always None at the moment - # property_recommendations=recommendations[p.id], - # project_scores_matrix=eco_project_scores_matrix, - # gbis_abs_rate=20, - # eco4_abs_rate=20, - # ) - # funding_calulator.check_eligibiltiy() - # # Insert finding - # p.insert_funding(funding_calulator) + for p in input_properties: + funding_calulator = Funding( + tenure=body.housing_type, + starting_epc=p.data["current-energy-rating"], + starting_sap=int(p.data["current-energy-efficiency"]), + postcode=p.postcode, + floor_area=p.floor_area, + council_tax_band=None, # This is seemingly always None at the moment + property_recommendations=recommendations[p.id], + project_scores_matrix=eco_project_scores_matrix, + whlg_eligible_postcodes=whlg_eligible_postcodes, + gbis_abs_rate=20, + eco4_abs_rate=15, + ) + funding_calulator.check_eligibiltiy() + # Insert finding + p.insert_funding(funding_calulator) logger.info("Uploading recommendations to the database") # If we have any work to do, we create a new scenario diff --git a/etl/funding/app.py b/etl/funding/app.py new file mode 100644 index 00000000..fba48ca4 --- /dev/null +++ b/etl/funding/app.py @@ -0,0 +1,35 @@ +""" +This scipt prepares the data, required for us to perform funding calculations. The starting data should be stored +on the machine this is being run on, and this will prepare the information and upload if +""" +import pandas as pd +from utils.s3 import save_csv_to_s3 + +STAGE = "dev" +DATA_BUCKET = "retrofit-data-{stage}" +PROJECTS_SCORES_MATRIX_LOCATION = "/Users/khalimconn-kowlessar/Downloads/ECO4 Full Project Scores Matrix.csv" +WHLG_ELIGIBLE_POSTCODES = "/Users/khalimconn-kowlessar/Downloads/WHLG-eligible-postcodes.xlsx" + + +def app(): + # Read in the project scores matrix + project_scores_matrix = pd.read_csv(PROJECTS_SCORES_MATRIX_LOCATION) + + # Store in AWS S3 + save_csv_to_s3( + dataframe=project_scores_matrix, + bucket_name=DATA_BUCKET.format(stage=STAGE), + file_name="funding/ECO4 Full Project Scores Matrix.csv" + ) + + # Read in the Warm Homes Local Grant eligible postcodes data + whlg_eligible_postcodes = pd.read_excel(WHLG_ELIGIBLE_POSTCODES, sheet_name="Eligible postcodes", header=1) + # We tidy up the data before we store + whlg_eligible_postcodes = whlg_eligible_postcodes[["Postcode"]] + whlg_eligible_postcodes["Postcode"] = whlg_eligible_postcodes["Postcode"].str.lower() + + save_csv_to_s3( + dataframe=whlg_eligible_postcodes, + bucket_name=DATA_BUCKET.format(stage=STAGE), + file_name="funding/whlg eligible postcodes.csv" + )