mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +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
|
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
|
# Aggregate multiple fields
|
||||||
aggregates = (
|
aggregates = (
|
||||||
session.query(
|
session.query(
|
||||||
func.sum(Recommendation.estimated_cost).label("cost"),
|
func.sum(Recommendation.estimated_cost).label("cost"),
|
||||||
func.sum(Recommendation.total_work_hours).label("total_work_hours"),
|
func.sum(Recommendation.total_work_hours).label("total_work_hours"),
|
||||||
func.sum(Recommendation.heat_demand).label("total_heat_demand"),
|
func.sum(Recommendation.heat_demand).label("energy_savings"),
|
||||||
func.sum(Recommendation.energy_savings).label("total_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.energy_cost_savings).label("energy_cost_savings"),
|
||||||
|
func.sum(Recommendation.labour_days).label("labour_days"),
|
||||||
)
|
)
|
||||||
.join(PlanRecommendations, PlanRecommendations.recommendation_id == Recommendation.id)
|
.join(PlanRecommendations, PlanRecommendations.recommendation_id == Recommendation.id)
|
||||||
.join(Plan, Plan.id == PlanRecommendations.plan_id)
|
.join(Plan, Plan.id == PlanRecommendations.plan_id)
|
||||||
|
|
@ -22,9 +23,10 @@ def aggregate_portfolio_recommendations(session, portfolio_id: int):
|
||||||
aggregates_dict = {
|
aggregates_dict = {
|
||||||
"cost": aggregates.cost or 0,
|
"cost": aggregates.cost or 0,
|
||||||
"total_work_hours": aggregates.total_work_hours or 0,
|
"total_work_hours": aggregates.total_work_hours or 0,
|
||||||
"total_heat_demand": aggregates.total_heat_demand or 0,
|
"energy_savings": aggregates.energy_savings or 0,
|
||||||
"total_energy_savings": aggregates.total_energy_savings or 0,
|
"co2_equivalent_savings": aggregates.co2_equivalent_savings or 0,
|
||||||
"energy_cost_savings": aggregates.energy_cost_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
|
# 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():
|
for key, value in aggregates_dict.items():
|
||||||
setattr(portfolio, key, value)
|
setattr(portfolio, key, value)
|
||||||
|
|
||||||
|
# Insert total valuation increase
|
||||||
|
portfolio.property_valuation_increase = total_valuation_increase
|
||||||
|
|
||||||
# Merge the updated portfolio back into the session
|
# Merge the updated portfolio back into the session
|
||||||
session.merge(portfolio)
|
session.merge(portfolio)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,8 @@ def upload_recommendations(session: Session, recommendations_to_upload, property
|
||||||
"heat_demand": rec["heat_demand"],
|
"heat_demand": rec["heat_demand"],
|
||||||
"co2_equivalent_savings": rec["co2_equivalent_savings"],
|
"co2_equivalent_savings": rec["co2_equivalent_savings"],
|
||||||
"total_work_hours": rec["labour_hours"],
|
"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
|
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
|
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
|
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)
|
total_work_hours = Column(Float)
|
||||||
|
labour_days = Column(Float)
|
||||||
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now(pytz.utc))
|
||||||
updated_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)
|
property_valuation_increase = Column(Float)
|
||||||
rental_yield_increase = Column(Float)
|
rental_yield_increase = Column(Float)
|
||||||
total_work_hours = Column(Float)
|
total_work_hours = Column(Float)
|
||||||
|
labour_days = Column(Float)
|
||||||
|
|
||||||
|
|
||||||
class RecommendationMaterials(Base):
|
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.dependencies import validate_token
|
||||||
from backend.app.plan.schemas import PlanTriggerRequest
|
from backend.app.plan.schemas import PlanTriggerRequest
|
||||||
from backend.app.plan.utils import create_recommendation_scoring_data, get_cleaned
|
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.ml_models.api import ModelApi
|
||||||
from backend.Property import Property
|
from backend.Property import Property
|
||||||
|
|
@ -33,6 +33,7 @@ from recommendations.optimiser.optimiser_functions import prepare_input_measures
|
||||||
from recommendations.Recommendations import Recommendations
|
from recommendations.Recommendations import Recommendations
|
||||||
from utils.logger import setup_logger
|
from utils.logger import setup_logger
|
||||||
from utils.s3 import read_dataframe_from_s3_parquet
|
from utils.s3 import read_dataframe_from_s3_parquet
|
||||||
|
from backend.ml_models.Valuation import PropertyValuation
|
||||||
|
|
||||||
logger = setup_logger()
|
logger = setup_logger()
|
||||||
|
|
||||||
|
|
@ -250,6 +251,7 @@ async def trigger_plan(body: PlanTriggerRequest):
|
||||||
# 3) the recommendations
|
# 3) the recommendations
|
||||||
|
|
||||||
logger.info("Uploading recommendations to the database")
|
logger.info("Uploading recommendations to the database")
|
||||||
|
property_valuation_increases = []
|
||||||
session.commit()
|
session.commit()
|
||||||
for i in range(0, len(input_properties), BATCH_SIZE):
|
for i in range(0, len(input_properties), BATCH_SIZE):
|
||||||
try:
|
try:
|
||||||
|
|
@ -289,6 +291,16 @@ async def trigger_plan(body: PlanTriggerRequest):
|
||||||
session, plan_id=new_plan_id, recommendation_ids=uploaded_recommendation_ids
|
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
|
# Commit the session after each batch
|
||||||
session.commit()
|
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
|
# 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
|
# recommendation from being default to not default, we'll need to re-run this process to re-calculate the
|
||||||
# the portfolion level impact
|
# 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
|
# Commit final changes
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ class PropertyValuation:
|
||||||
{
|
{
|
||||||
"starting_epc": "D",
|
"starting_epc": "D",
|
||||||
"ending_epc": "C",
|
"ending_epc": "C",
|
||||||
"increase_percentage": 0.057,
|
"increase_percentage": 0.03625,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"starting_epc": "D",
|
"starting_epc": "D",
|
||||||
"ending_epc": "B",
|
"ending_epc": "B",
|
||||||
"increase_percentage": 0.057,
|
"increase_percentage": 0.05725,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ class PropertyValuation:
|
||||||
|
|
||||||
valuation_increases = [
|
valuation_increases = [
|
||||||
v for v in cls.VALUE_INCREASE_MAPPING if
|
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:
|
if len(valuation_increases) != 1:
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,9 @@ class Costs:
|
||||||
|
|
||||||
labour_hours = material["labour_hours_per_unit"] * wall_area
|
labour_hours = material["labour_hours_per_unit"] * wall_area
|
||||||
|
|
||||||
|
# Assume a team of 2
|
||||||
|
labour_days = (labour_hours / 8) / 2
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"total": total_cost,
|
"total": total_cost,
|
||||||
"subtotal": subtotal_before_vat,
|
"subtotal": subtotal_before_vat,
|
||||||
|
|
@ -124,7 +127,8 @@ class Costs:
|
||||||
"material": base_material_cost,
|
"material": base_material_cost,
|
||||||
"profit": profit_cost,
|
"profit": profit_cost,
|
||||||
"labour_hours": labour_hours,
|
"labour_hours": labour_hours,
|
||||||
"labour_cost": labour_cost
|
"labour_cost": labour_cost,
|
||||||
|
"labour_days": labour_days
|
||||||
}
|
}
|
||||||
|
|
||||||
def loft_insulation(self, floor_area, material):
|
def loft_insulation(self, floor_area, material):
|
||||||
|
|
@ -153,6 +157,9 @@ class Costs:
|
||||||
|
|
||||||
labour_hours = material["labour_hours_per_unit"] * floor_area
|
labour_hours = material["labour_hours_per_unit"] * floor_area
|
||||||
|
|
||||||
|
# Assume a team of 1 person
|
||||||
|
labour_days = labour_hours / 8
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"total": total_cost,
|
"total": total_cost,
|
||||||
"subtotal": subtotal_before_vat,
|
"subtotal": subtotal_before_vat,
|
||||||
|
|
@ -162,7 +169,8 @@ class Costs:
|
||||||
"material": base_material_cost,
|
"material": base_material_cost,
|
||||||
"profit": profit_cost,
|
"profit": profit_cost,
|
||||||
"labour_hours": labour_hours,
|
"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):
|
def internal_wall_insulation(self, wall_area, material, non_insulation_materials):
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ class FireplaceRecommendations(Definitions):
|
||||||
"sap_points": None,
|
"sap_points": None,
|
||||||
"total": estimated_cost,
|
"total": estimated_cost,
|
||||||
# Take a very basic estimate of 6 hours, multipled by the number of open fireplaces to seal
|
# 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,
|
"sap_points": None,
|
||||||
"total": estimated_cost,
|
"total": estimated_cost,
|
||||||
# We use a very simple and rough estimate of 4 hours per unit
|
# 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