mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
ammended RIR conditions
This commit is contained in:
parent
2b6c86e557
commit
42dc635aa3
7 changed files with 166 additions and 10 deletions
2
.idea/Model.iml
generated
2
.idea/Model.iml
generated
|
|
@ -7,7 +7,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Route-March" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Fastapi-backend" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyNamespacePackagesService">
|
||||
|
|
|
|||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
|
@ -3,7 +3,7 @@
|
|||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10 (backend)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Route-March" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Fastapi-backend" project-jdk-type="Python SDK" />
|
||||
<component name="PyCharmProfessionalAdvertiser">
|
||||
<option name="shown" value="true" />
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -759,7 +759,11 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
new_epc = sap_to_epc(new_sap_points)
|
||||
new_epc_bands[p.id] = new_epc
|
||||
|
||||
valuations = PropertyValuation.estimate(property_instance=p, target_epc=new_epc)
|
||||
total_cost = sum([r["total"] for r in default_recommendations])
|
||||
|
||||
valuations = PropertyValuation.estimate(
|
||||
property_instance=p, target_epc=new_epc, total_cost=total_cost
|
||||
)
|
||||
property_value_increase_ranges[p.id] = valuations
|
||||
|
||||
if p.is_new:
|
||||
|
|
|
|||
|
|
@ -203,7 +203,14 @@ class PropertyValuation:
|
|||
return msm_increase, lloyds_increase
|
||||
|
||||
@classmethod
|
||||
def estimate(cls, property_instance, target_epc):
|
||||
def estimate(cls, property_instance, target_epc, total_cost=None):
|
||||
"""
|
||||
This function estimates the value of a property based on the current EPC rating and the target EPC rating
|
||||
:param property_instance: An instance of the Property class
|
||||
:param target_epc: The target EPC rating
|
||||
:param total_cost: The total cost of the retrofit
|
||||
:return:
|
||||
"""
|
||||
current_value = (
|
||||
property_instance.valuation if property_instance.valuation else
|
||||
cls.UPRN_VALUE_LOOKUP.get(property_instance.uprn)
|
||||
|
|
@ -242,6 +249,19 @@ class PropertyValuation:
|
|||
|
||||
avg_increase = np.mean(all_increases)
|
||||
|
||||
if total_cost is not None:
|
||||
# We CAP the retrofit ROI at 2
|
||||
avg_increase_value = current_value * avg_increase
|
||||
if avg_increase_value / total_cost > 2:
|
||||
# We re-scale the % so that the average value increase is no more than 2 times the total cost
|
||||
double_cost = 2 * total_cost
|
||||
new_avg_increase = double_cost / current_value
|
||||
scalar = new_avg_increase / avg_increase
|
||||
# We scale the min and max increases by the same scalar
|
||||
min_increase *= scalar
|
||||
max_increase *= scalar
|
||||
avg_increase = new_avg_increase
|
||||
|
||||
return {
|
||||
"current_value": current_value,
|
||||
"lower_bound_increased_value": float(current_value * (1 + min_increase)),
|
||||
|
|
|
|||
123
etl/customers/warwick/remote_assessments.py
Normal file
123
etl/customers/warwick/remote_assessments.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import pandas as pd
|
||||
from utils.s3 import save_csv_to_s3
|
||||
|
||||
PORTFOLIO_ID = 115
|
||||
USER_ID = 8
|
||||
|
||||
|
||||
def app():
|
||||
"""
|
||||
Used to set up the remote assessments for Warwick
|
||||
"""
|
||||
|
||||
asset_list = [
|
||||
{
|
||||
"uprn": 10033604792,
|
||||
"address": "Flat 2, 3 Green Street",
|
||||
"postcode": "W1K 6RN"
|
||||
},
|
||||
{
|
||||
"uprn": 10033604794,
|
||||
"address": "Flat 4, 3 Green Street",
|
||||
"postcode": "W1K 6RN"
|
||||
},
|
||||
{
|
||||
"uprn": 10033615515,
|
||||
"address": "Apartment 4, 52 Green Street",
|
||||
"postcode": "W1K 6RS"
|
||||
}
|
||||
]
|
||||
asset_list = pd.DataFrame(asset_list)
|
||||
|
||||
# Store the asset list in s3
|
||||
filename = f"{USER_ID}/{PORTFOLIO_ID}/asset_list.csv"
|
||||
save_csv_to_s3(
|
||||
dataframe=asset_list,
|
||||
bucket_name="retrofit-plan-inputs-dev",
|
||||
file_name=filename
|
||||
)
|
||||
|
||||
non_invasive_recommendations = [
|
||||
{
|
||||
"uprn": 10033604792,
|
||||
"recommendations": [
|
||||
{
|
||||
"type": "internal_wall_insulation",
|
||||
"sap_points": 16,
|
||||
"survey": True
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uprn": 10033604794,
|
||||
"recommendations": [
|
||||
{
|
||||
"type": "internal_wall_insulation",
|
||||
"sap_points": 14,
|
||||
"survey": True
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uprn": 10033615515,
|
||||
"recommendations": [
|
||||
{
|
||||
"type": "room_roof_insulation",
|
||||
"sap_points": 12,
|
||||
"survey": True
|
||||
},
|
||||
{
|
||||
"type": "internal_wall_insulation",
|
||||
"sap_points": 2,
|
||||
"survey": True
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
# Store non-invasive recommendations in S3
|
||||
non_invasive_recommendations_filename = f"{USER_ID}/{PORTFOLIO_ID}/non_invasive_recommendations.csv"
|
||||
save_csv_to_s3(
|
||||
dataframe=pd.DataFrame(non_invasive_recommendations),
|
||||
bucket_name="retrofit-plan-inputs-dev",
|
||||
file_name=non_invasive_recommendations_filename
|
||||
)
|
||||
|
||||
valuation_data = [
|
||||
{
|
||||
"uprn": 10033604792,
|
||||
"value": 3_692_000
|
||||
},
|
||||
{
|
||||
"uprn": 10033604794,
|
||||
"value": 3_789_000
|
||||
},
|
||||
{
|
||||
"uprn": 10033615515,
|
||||
"value": 3_499_000
|
||||
}
|
||||
]
|
||||
|
||||
# Store valuation data to s3
|
||||
valuation_filename = f"{USER_ID}/{PORTFOLIO_ID}/valuation.csv"
|
||||
save_csv_to_s3(
|
||||
dataframe=pd.DataFrame(valuation_data),
|
||||
bucket_name="retrofit-plan-inputs-dev",
|
||||
file_name=valuation_filename
|
||||
)
|
||||
|
||||
body = {
|
||||
"portfolio_id": str(PORTFOLIO_ID),
|
||||
"housing_type": "Private",
|
||||
"goal": "Increasing EPC",
|
||||
"goal_value": "C",
|
||||
"trigger_file_path": filename,
|
||||
"already_installed_file_path": "",
|
||||
"patches_file_path": "",
|
||||
"non_invasive_recommendations_file_path": non_invasive_recommendations_filename,
|
||||
"valuation_file_path": valuation_filename,
|
||||
"scenario_name": "Full package remote assessment",
|
||||
"multi_plan": True,
|
||||
"budget": None,
|
||||
}
|
||||
print(body)
|
||||
|
|
@ -519,6 +519,7 @@ class Recommendations:
|
|||
# heating_cost_starting and heating_cost_ending are just the values in the EPC. However, with
|
||||
# heating_cost_ending, we expect that the EPC will predict a heating cost based on what would happen
|
||||
# if we implemented the recommendation today, so our starting value is the EPC
|
||||
|
||||
previous_phase_values = {
|
||||
"sap": float(property_instance.data["current-energy-efficiency"]),
|
||||
"carbon": float(property_instance.data["co2-emissions-current"]),
|
||||
|
|
@ -541,8 +542,13 @@ class Recommendations:
|
|||
previous_phase_values = previous_phase_values_multiple[0]
|
||||
|
||||
# We extract the values for the current phase
|
||||
if rec.get("survey", False):
|
||||
current_phase_sap = rec["sap_points"] + previous_phase_values["sap"]
|
||||
else:
|
||||
current_phase_sap = phase_energy_efficiency_metrics["sap_change"]
|
||||
|
||||
current_phase_values = {
|
||||
"sap": phase_energy_efficiency_metrics["sap_change"],
|
||||
"sap": current_phase_sap,
|
||||
"carbon": phase_energy_efficiency_metrics["carbon_change"],
|
||||
"heat_demand": phase_energy_efficiency_metrics["heat_demand"],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,11 @@ class RoofRecommendations:
|
|||
self.property.roof["insulation_thickness"] in ["average", "above_average"]
|
||||
)
|
||||
|
||||
return full_insulated_room_roof or room_roof_insulated_at_rafters
|
||||
has_non_invasive_recommendation = any(
|
||||
x["type"] == "room_roof_insulation" for x in self.property.non_invasive_recommendations
|
||||
)
|
||||
|
||||
return (full_insulated_room_roof or room_roof_insulated_at_rafters) and not has_non_invasive_recommendation
|
||||
|
||||
def recommend(self, phase, measures=None, default_u_values=False):
|
||||
|
||||
|
|
@ -181,7 +185,8 @@ class RoofRecommendations:
|
|||
|
||||
# We firstly handle non-intrusive recommendations, which may override the normal roof insulation recommendations
|
||||
if ("loft_insulation" in [x["type"] for x in non_invasive_recommendations]) or (
|
||||
self.property.roof["is_pitched"] and "loft_insulation" in measures
|
||||
self.property.roof["is_pitched"] and "loft_insulation" in measures and
|
||||
not self.property.roof["is_at_rafters"]
|
||||
):
|
||||
self.recommend_roof_insulation(
|
||||
u_value=u_value,
|
||||
|
|
@ -512,8 +517,6 @@ class RoofRecommendations:
|
|||
rir_non_invasive_recommendation.get("cost")
|
||||
)
|
||||
|
||||
sap_points = rir_non_invasive_recommendation.get("sap_points", None)
|
||||
|
||||
# Could also be Roof room(s), ceiling insulated
|
||||
new_descriptin = "Roof room(s), insulated"
|
||||
roof_ending_config = RoofAttributes(new_descriptin).process()
|
||||
|
|
@ -562,7 +565,7 @@ class RoofRecommendations:
|
|||
"description": "Insulate room in roof at rafters and re-decorate",
|
||||
"starting_u_value": u_value,
|
||||
"new_u_value": new_u_value,
|
||||
"sap_points": sap_points,
|
||||
"sap_points": rir_non_invasive_recommendation.get("sap_points", None),
|
||||
"simulation_config": simulation_config,
|
||||
"description_simulation": {
|
||||
"roof-description": new_descriptin,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue