paused for the moment

This commit is contained in:
Khalim Conn-Kowlessar 2025-01-24 11:31:41 +00:00
parent 3ccc5eae89
commit fe193305e6
3 changed files with 120 additions and 29 deletions

View file

@ -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()

View file

@ -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

35
etl/funding/app.py Normal file
View file

@ -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"
)