From a6daeab88928f87cf2da87e482e2eedeea620b61 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 4 Mar 2025 11:27:43 +0000 Subject: [PATCH] working on funding --- .idea/Model.iml | 2 +- .idea/misc.xml | 2 +- backend/Funding.py | 157 +++++++++++++++++------- etl/customers/remote_assessments/app.py | 40 ++---- 4 files changed, 129 insertions(+), 72 deletions(-) diff --git a/.idea/Model.iml b/.idea/Model.iml index 762580d9..df6c4faa 100644 --- a/.idea/Model.iml +++ b/.idea/Model.iml @@ -7,7 +7,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index c916a158..50cad4ca 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,7 +3,7 @@ - + diff --git a/backend/Funding.py b/backend/Funding.py index 2839c7ff..f5f85b9f 100644 --- a/backend/Funding.py +++ b/backend/Funding.py @@ -98,11 +98,14 @@ class Funding: self, scheme: str, eligible: bool, + types: List[str], measure_types: List[str], + project_score: float, estimated_funding: float, notify_tenant_benefits_requirements: bool, notify_council_tax_band_requirements: bool, notify_tenant_low_income_requirements: bool, + innovation_required: bool, ): """" """ @@ -113,11 +116,14 @@ class Funding: return { "scheme": scheme, "eligible": eligible, + "type": types, "measure_types": measure_types, + "project_score": project_score, "estimated_funding": estimated_funding, "requires_benefits": notify_tenant_benefits_requirements, "requires_council_tax_band": notify_council_tax_band_requirements, - "requires_low_income": notify_tenant_low_income_requirements + "requires_low_income": notify_tenant_low_income_requirements, + "innovation_required": innovation_required, } @staticmethod @@ -140,7 +146,7 @@ class Funding: """ pass - def find_best_gbis_measure(self, measures): + def find_gbis_measures(self, measures): """ The best measure is one that: 1) Creates some SAP movement, therefore enables eligiblity @@ -247,21 +253,26 @@ class Funding: ) and (self.council_tax_band in [None, "A", "B", "C", "D"]) ): - # We find the best measure for GBIS - recommended_measure = self.find_best_gbis_measure( + # This function pulls out the various measures that can provide funding under GBIS + recommended_measures = self.find_gbis_measures( measures=[m for m in valid_measures if m not in ["cavity_wall_insulation", "loft_insulation"]] ) # 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, - notify_council_tax_band_requirements=self.council_tax_band is None, - notify_tenant_low_income_requirements=False, - ) + return [ + self.output( + scheme="gbis", + eligible=True, + types=[m["type"]], # This is single measure so we only have one type + measure_types=[m["measure_type"]], + project_score=m["project_score"], + estimated_funding=m["estimated_funding"], + notify_tenant_benefits_requirements=False, + notify_council_tax_band_requirements=self.council_tax_band is None, + notify_tenant_low_income_requirements=False, + innovation_required=False + ) for m in recommended_measures + ] # Low income/flex if ( @@ -271,28 +282,83 @@ class Funding: # Find the best measure, and can also include CWI/LI but requires the tenant to be # low inome or on benefits # 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, - notify_council_tax_band_requirements=False, - notify_tenant_low_income_requirements=True, - ) + recommended_measures = self.find_gbis_measures(measures=valid_measures) + return [ + self.output( + scheme="gbis", + eligible=True, + types=[m["type"]], # This is single measure so we only have one type + measure_types=[m["measure_type"]], + project_score=m["project_score"], + estimated_funding=m["estimated_funding"], + notify_tenant_benefits_requirements=True, + notify_council_tax_band_requirements=False, + notify_tenant_low_income_requirements=True, + innovation_required=False + ) for m in recommended_measures + ] # Otherwise, no funding availability - return self.output( - scheme="gbis", - 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 [] + + def gbis_social(self): + """ + Because this is social housing, we have two typical means for eligibility + 1) EPC D, where an innovation measure is required + 2) EPC G-E, where an innovation measure isn't required + :return: + """ + valid_measures = [ + "internal_wall_insulation", + "external_wall_insulation", + "flat_roof_insulation", + "suspended_floor_insulation", + "room_roof_insulation", + # Not available for every eligiblity type + "cavity_wall_insulation", + "loft_insulation", + "heating_control" + ] + + recommended_measures = self.find_gbis_measures( + measures=valid_measures ) + # All measures are available + if self.starting_sap == "D": + return [ + self.output( + scheme="gbis", + eligible=True, + types=[m["type"]], # This is single measure so we only have one type + measure_types=[m["measure_type"]], + project_score=m["project_score"], + estimated_funding=m["estimated_funding"], + notify_tenant_benefits_requirements=False, + notify_council_tax_band_requirements=False, + notify_tenant_low_income_requirements=False, + innovation_required=True + ) for m in recommended_measures + ] + + if self.starting_sap in ["G", "F", "E"]: + return [ + self.output( + scheme="gbis", + eligible=True, + types=[m["type"]], # This is single measure so we only have one type + measure_types=[m["measure_type"]], + project_score=m["project_score"], + estimated_funding=m["estimated_funding"], + notify_tenant_benefits_requirements=False, + notify_council_tax_band_requirements=False, + notify_tenant_low_income_requirements=False, + innovation_required=False + ) for m in recommended_measures + ] + + return [] + def gbis(self): """ Check if a property is eligible for GBIS @@ -303,24 +369,33 @@ class Funding: self.gbis_eligibiltiy = self.gbis_prs() return + if self.tenure == "Social": + self.gbis_eligibiltiy = self.gbis_social() + 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 - ) + self.whlg_eligibility = [] return if not self.whlg_eligible_postcodes.empty: - print("Eligible implement me!") + raise Exception("Implement me") + # self.whlg_eligibility = [ + # self.output( + # scheme, + # eligible, + # types, + # measure_types, + # project_score: float, + # estimated_funding: float, + # notify_tenant_benefits_requirements: bool, + # notify_council_tax_band_requirements: bool, + # notify_tenant_low_income_requirements: bool, + # innovation_required: bool, + # ) + # ] def eco4(self): if self.tenure == "Private": diff --git a/etl/customers/remote_assessments/app.py b/etl/customers/remote_assessments/app.py index fc3b7ec6..a4d60d85 100644 --- a/etl/customers/remote_assessments/app.py +++ b/etl/customers/remote_assessments/app.py @@ -4,7 +4,7 @@ from dotenv import load_dotenv from utils.s3 import save_csv_to_s3 from etl.find_my_epc.AssetListEpcData import AssetListEpcData -PORTFOLIO_ID = 134 +PORTFOLIO_ID = 138 USER_ID = 8 load_dotenv(dotenv_path="backend/.env") @@ -19,25 +19,15 @@ def app(): asset_list = [ { - "address": "Flat 2, 42 Malden Road, London NW5 3HG", - "postcode": "NW5 3HG", - "uprn": 5117165, + "address": "42 Rippolson Road", + "postcode": "SE18 1NS", + "uprn": 100020999275, }, { - "address": "15 Bournville Lane", - "postcode": "B30 2JY", - "uprn": 100070301128 + "address": "66 Riverdale Road", + "postcode": "DA8 1PX", + "uprn": 100020235516 }, - { - "address": "34 Bournville Lane", - "postcode": "B30 2LN", - "uprn": 100070301140 - }, - { - "address": "36 Bournville Lane", - "postcode": "B30 2LN", - "uprn": 100070301142 - } ] asset_list = pd.DataFrame(asset_list) @@ -67,20 +57,12 @@ def app(): valuation_data = [ { - "uprn": 5117165, - "valuation": 467_000 + "valuation": 469_000, + "uprn": 100020999275, }, { - "uprn": 100070301128, - "valuation": 335_000 - }, - { - "uprn": 100070301140, - "valuation": 276_000 - }, - { - "uprn": 100070301142, - "valuation": 276_000 + "valuation": 382_000, + "uprn": 100020235516 }, ] # Store valuation data to s3