mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added labour days to cavity walls costs
This commit is contained in:
parent
1da9433ee2
commit
7ebfb3b99c
9 changed files with 51 additions and 16 deletions
|
|
@ -3,15 +3,16 @@ from backend.app.db.models.recommendations import Plan, PlanRecommendations, Rec
|
|||
from backend.app.db.models.portfolio import Portfolio
|
||||
|
||||
|
||||
def aggregate_portfolio_recommendations(session, portfolio_id: int):
|
||||
def aggregate_portfolio_recommendations(session, portfolio_id: int, total_valuation_increase: float):
|
||||
# Aggregate multiple fields
|
||||
aggregates = (
|
||||
session.query(
|
||||
func.sum(Recommendation.estimated_cost).label("cost"),
|
||||
func.sum(Recommendation.total_work_hours).label("total_work_hours"),
|
||||
func.sum(Recommendation.heat_demand).label("total_heat_demand"),
|
||||
func.sum(Recommendation.energy_savings).label("total_energy_savings"),
|
||||
func.sum(Recommendation.energy_cost_savings).label("energy_cost_savings")
|
||||
func.sum(Recommendation.heat_demand).label("energy_savings"),
|
||||
func.sum(Recommendation.co2_equivalent_savings).label("co2_equivalent_savings"),
|
||||
func.sum(Recommendation.energy_cost_savings).label("energy_cost_savings"),
|
||||
func.sum(Recommendation.labour_days).label("labour_days"),
|
||||
)
|
||||
.join(PlanRecommendations, PlanRecommendations.recommendation_id == Recommendation.id)
|
||||
.join(Plan, Plan.id == PlanRecommendations.plan_id)
|
||||
|
|
@ -22,9 +23,10 @@ def aggregate_portfolio_recommendations(session, portfolio_id: int):
|
|||
aggregates_dict = {
|
||||
"cost": aggregates.cost or 0,
|
||||
"total_work_hours": aggregates.total_work_hours or 0,
|
||||
"total_heat_demand": aggregates.total_heat_demand or 0,
|
||||
"total_energy_savings": aggregates.total_energy_savings or 0,
|
||||
"energy_savings": aggregates.energy_savings or 0,
|
||||
"co2_equivalent_savings": aggregates.co2_equivalent_savings or 0,
|
||||
"energy_cost_savings": aggregates.energy_cost_savings or 0,
|
||||
"labour_days": aggregates.labour_days or 0,
|
||||
}
|
||||
|
||||
# Get the portfolio and update the fields
|
||||
|
|
@ -33,6 +35,9 @@ def aggregate_portfolio_recommendations(session, portfolio_id: int):
|
|||
for key, value in aggregates_dict.items():
|
||||
setattr(portfolio, key, value)
|
||||
|
||||
# Insert total valuation increase
|
||||
portfolio.property_valuation_increase = total_valuation_increase
|
||||
|
||||
# Merge the updated portfolio back into the session
|
||||
session.merge(portfolio)
|
||||
session.flush()
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ def upload_recommendations(session: Session, recommendations_to_upload, property
|
|||
"heat_demand": rec["heat_demand"],
|
||||
"co2_equivalent_savings": rec["co2_equivalent_savings"],
|
||||
"total_work_hours": rec["labour_hours"],
|
||||
"energy_cost_savings": rec["energy_cost_savings"]
|
||||
"energy_cost_savings": rec["energy_cost_savings"],
|
||||
"labour_days": rec["labour_days"]
|
||||
}
|
||||
for rec in recommendations_to_upload
|
||||
]
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class Portfolio(Base):
|
|||
property_valuation_increase = Column(Float) # Unit is always £ so we don't need to store the unit for the moment
|
||||
rental_yield_increase = Column(Float) # Unit is always £ so we don't need to store the unit for the moment
|
||||
total_work_hours = Column(Float)
|
||||
labour_days = Column(Float)
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
||||
updated_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class Recommendation(Base):
|
|||
property_valuation_increase = Column(Float)
|
||||
rental_yield_increase = Column(Float)
|
||||
total_work_hours = Column(Float)
|
||||
labour_days = Column(Float)
|
||||
|
||||
|
||||
class RecommendationMaterials(Base):
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from backend.app.db.models.portfolio import rating_lookup
|
|||
from backend.app.dependencies import validate_token
|
||||
from backend.app.plan.schemas import PlanTriggerRequest
|
||||
from backend.app.plan.utils import create_recommendation_scoring_data, get_cleaned
|
||||
from backend.app.utils import epc_to_sap_lower_bound, read_csv_from_s3, read_parquet_from_s3
|
||||
from backend.app.utils import epc_to_sap_lower_bound, read_csv_from_s3, read_parquet_from_s3, sap_to_epc
|
||||
|
||||
from backend.ml_models.api import ModelApi
|
||||
from backend.Property import Property
|
||||
|
|
@ -33,6 +33,7 @@ from recommendations.optimiser.optimiser_functions import prepare_input_measures
|
|||
from recommendations.Recommendations import Recommendations
|
||||
from utils.logger import setup_logger
|
||||
from utils.s3 import read_dataframe_from_s3_parquet
|
||||
from backend.ml_models.Valuation import PropertyValuation
|
||||
|
||||
logger = setup_logger()
|
||||
|
||||
|
|
@ -250,6 +251,7 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
# 3) the recommendations
|
||||
|
||||
logger.info("Uploading recommendations to the database")
|
||||
property_valuation_increases = []
|
||||
session.commit()
|
||||
for i in range(0, len(input_properties), BATCH_SIZE):
|
||||
try:
|
||||
|
|
@ -289,6 +291,16 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
session, plan_id=new_plan_id, recommendation_ids=uploaded_recommendation_ids
|
||||
)
|
||||
|
||||
# Get defaults
|
||||
default_recommendations = [r for r in recommendations_to_upload if r["default"]]
|
||||
total_sap_points = sum([r["sap_points"] for r in default_recommendations])
|
||||
new_sap_points = float(p.data["current-energy-efficiency"]) + total_sap_points
|
||||
new_epc = sap_to_epc(new_sap_points)
|
||||
|
||||
property_valuation_increases.append(
|
||||
PropertyValuation.estimate(property_instance=p, target_epc=new_epc)
|
||||
)
|
||||
|
||||
# Commit the session after each batch
|
||||
session.commit()
|
||||
|
||||
|
|
@ -304,7 +316,12 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
# way to do this, but it's the simplest and will be a process that we can re-use since when we change a
|
||||
# recommendation from being default to not default, we'll need to re-run this process to re-calculate the
|
||||
# the portfolion level impact
|
||||
aggregate_portfolio_recommendations(session, portfolio_id=body.portfolio_id)
|
||||
|
||||
total_valuation_increase = sum(property_valuation_increases)
|
||||
|
||||
aggregate_portfolio_recommendations(
|
||||
session, portfolio_id=body.portfolio_id, total_valuation_increase=total_valuation_increase
|
||||
)
|
||||
|
||||
# Commit final changes
|
||||
session.commit()
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ class PropertyValuation:
|
|||
{
|
||||
"starting_epc": "D",
|
||||
"ending_epc": "C",
|
||||
"increase_percentage": 0.057,
|
||||
"increase_percentage": 0.03625,
|
||||
},
|
||||
{
|
||||
"starting_epc": "D",
|
||||
"ending_epc": "B",
|
||||
"increase_percentage": 0.057,
|
||||
"increase_percentage": 0.05725,
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ class PropertyValuation:
|
|||
|
||||
valuation_increases = [
|
||||
v for v in cls.VALUE_INCREASE_MAPPING if
|
||||
v["starting_epc"] == property_instance.epc_band and v["ending_epc"] == target_epc
|
||||
v["starting_epc"] == property_instance.data["current-energy-rating"] and v["ending_epc"] == target_epc
|
||||
]
|
||||
|
||||
if len(valuation_increases) != 1:
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ class Costs:
|
|||
|
||||
labour_hours = material["labour_hours_per_unit"] * wall_area
|
||||
|
||||
# Assume a team of 2
|
||||
labour_days = (labour_hours / 8) / 2
|
||||
|
||||
return {
|
||||
"total": total_cost,
|
||||
"subtotal": subtotal_before_vat,
|
||||
|
|
@ -124,7 +127,8 @@ class Costs:
|
|||
"material": base_material_cost,
|
||||
"profit": profit_cost,
|
||||
"labour_hours": labour_hours,
|
||||
"labour_cost": labour_cost
|
||||
"labour_cost": labour_cost,
|
||||
"labour_days": labour_days
|
||||
}
|
||||
|
||||
def loft_insulation(self, floor_area, material):
|
||||
|
|
@ -153,6 +157,9 @@ class Costs:
|
|||
|
||||
labour_hours = material["labour_hours_per_unit"] * floor_area
|
||||
|
||||
# Assume a team of 1 person
|
||||
labour_days = labour_hours / 8
|
||||
|
||||
return {
|
||||
"total": total_cost,
|
||||
"subtotal": subtotal_before_vat,
|
||||
|
|
@ -162,7 +169,8 @@ class Costs:
|
|||
"material": base_material_cost,
|
||||
"profit": profit_cost,
|
||||
"labour_hours": labour_hours,
|
||||
"labour_cost": labour_cost
|
||||
"labour_cost": labour_cost,
|
||||
"labour_days": labour_days
|
||||
}
|
||||
|
||||
def internal_wall_insulation(self, wall_area, material, non_insulation_materials):
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class FireplaceRecommendations(Definitions):
|
|||
"sap_points": None,
|
||||
"total": estimated_cost,
|
||||
# Take a very basic estimate of 6 hours, multipled by the number of open fireplaces to seal
|
||||
"labour_hours": 6 * number_open_fireplaces
|
||||
"labour_hours": 6 * number_open_fireplaces,
|
||||
"labour_days": 6 * number_open_fireplaces / 8, # Assume 8 hour day
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ class VentilationRecommendations(Definitions):
|
|||
"sap_points": None,
|
||||
"total": estimated_cost,
|
||||
# We use a very simple and rough estimate of 4 hours per unit
|
||||
"labour_hours": 4 * n_units
|
||||
"labour_hours": 4 * n_units,
|
||||
"labour_days": 4 * n_units / 8.0 # Assume 8 hour day
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue