Merge pull request #602 from Hestia-Homes/eco-eligiblity-bug

added additional fields to db and fixed flat roof recommendations bug
This commit is contained in:
KhalimCK 2025-12-09 02:46:44 +08:00 committed by GitHub
commit 81ce9fd4de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 74 additions and 29 deletions

View file

@ -833,9 +833,29 @@ class Property:
if self.current_energy_bill is None:
raise ValueError("Current energy bill has not been set")
# IF we have a SAP05 overwrite, we pull out the relevant information
sap_05_overwritten = self.data.get("sap_05_overwritten", False)
sap_05_score, sap_05_epc_rating = None, None
if sap_05_overwritten:
if not self.old_data:
# Trying to fetch SAP05 EPC but no data
raise ValueError("Trying to fetch SAP05 EPC but no old data available")
# We get the last rating from the old data
newest_old_epc = max(self.old_data, key=lambda d: pd.to_datetime(d["lodgement-date"]))
# Get the rating and score
sap_05_score = int(newest_old_epc["current-energy-efficiency"])
sap_05_epc_rating = newest_old_epc["current-energy-rating"]
lodgement_date = self.data["lodgement-date"]
# We check if the lodgement date is more than 10 years old
is_expired = (datetime.now() - pd.to_datetime(lodgement_date)) > timedelta(days=3650)
property_details_epc = {
"property_id": self.id,
"portfolio_id": portfolio_id,
"lodgement_date": datetime.fromisoformat(lodgement_date),
"is_expired": is_expired,
"full_address": self.data["address"],
"total_floor_area": float(self.data["total-floor-area"]),
"walls": self.walls["clean_description"],
@ -891,7 +911,9 @@ class Property:
"current_energy_demand_heating_hotwater": self.current_energy_consumption_heating_hotwater,
"estimated": self.data.get("estimated", False),
# We indicate if we've overwritten a SAP 05 EPC
"sap_05_overwritten": self.data.get("sap_05_overwritten", False),
"sap_05_overwritten": sap_05_overwritten,
"sap_05_score": sap_05_score,
"sap_05_epc_rating": sap_05_epc_rating,
**self.current_energy_bill
}

View file

@ -44,6 +44,10 @@ def prepare_plan_data(
valuation_increase = valuations["average_increase"]
valuation_post_retrofit = valuations["average_increased_value"]
# plan costing data
cost_of_works = sum([r["total"] for r in default_recommendations])
contingency_cost = sum([r["contingency"] for r in default_recommendations])
return {
"portfolio_id": body.portfolio_id,
"property_id": p.id,
@ -69,6 +73,8 @@ def prepare_plan_data(
"energy_consumption_savings": float(energy_consumption_savings),
"valuation_post_retrofit": valuation_post_retrofit,
"valuation_increase": valuation_increase,
"cost_of_works": cost_of_works,
"contingency_cost": contingency_cost,
"plan_type": eco_packages.get(p.id, (None, None, None))[2]
}

View file

@ -137,6 +137,8 @@ class PropertyDetailsEpcModel(Base):
property_id = Column(Integer, ForeignKey('property.id'), nullable=False)
portfolio_id = Column(Integer, ForeignKey('portfolio.id'), nullable=False)
full_address = Column(Text)
lodgement_date = Column(DateTime)
is_expired = Column(Boolean)
total_floor_area = Column(Float)
walls = Column(Text)
walls_rating = Column(Integer, CheckConstraint('walls_rating>=1 AND walls_rating<=5'))
@ -176,6 +178,8 @@ class PropertyDetailsEpcModel(Base):
current_energy_demand_heating_hotwater = Column(Float)
estimated = Column(Boolean, default=False)
sap_05_overwritten = Column(Boolean, default=False)
sap_05_score = Column(Integer)
sap_05_epc_rating = Column(Enum(Epc))
# Include estimates for energy bills, across the different types of energy
heating_cost_current = Column(Float)
hot_water_cost_current = Column(Float)

View file

@ -89,6 +89,9 @@ class Plan(Base):
energy_consumption_savings = Column(Float)
valuation_post_retrofit = Column(Float)
valuation_increase = Column(Float)
# Financial metrics, excluding funding
cost_of_works = Column(Float)
contingency_cost = Column(Float)
class PlanRecommendations(Base):

View file

@ -239,42 +239,52 @@ def get_wall_u_value(
return float(mapped_value)
def _try_convert_to_int(value):
try:
return int(value)
except (TypeError, ValueError):
return None
def extract_thickness(thickness, is_roof_room, is_at_rafters, is_loft, is_flat):
thickness_map = {
"below average": "50",
"average": "100",
"above average": "150",
"none": "0",
}
# Normalise none value early
if thickness is None:
thickness = "none"
if is_roof_room or is_at_rafters:
# TODO: We get None instead of a string none, this should be fixed
if thickness is None:
thickness = "none"
int_thickness = _try_convert_to_int(thickness)
if int_thickness is not None:
return int_thickness
# We re-map the thickness
thickness_map = {
"below average": "50",
"average": "100",
"above average": "150",
"none": "0",
}
thickness = thickness_map[thickness]
thickness = thickness_map.get(thickness)
if thickness is None:
return None
return int(thickness)
if is_flat:
try:
thickness = int(thickness)
return thickness
except (TypeError, ValueError):
# If thickness is not a valid number (could be a string or None), return None
return None
return _try_convert_to_int(thickness)
if thickness in ["below average", "average", "above average", "none", None] or (
not is_loft and not is_roof_room and not is_at_rafters
# Thicknes will never be none
if thickness in thickness_map or (
not (is_loft or is_roof_room or is_at_rafters)
):
return None
elif thickness.endswith("+"):
thickness = int(thickness[:-1])
return thickness
else:
try:
thickness = int(thickness)
return thickness
except ValueError:
# If thickness is not a valid number (could be a string or None), return None
return None
if isinstance(thickness, str) and str(thickness).endswith("+"):
return _try_convert_to_int(thickness[:-1])
# final attempt
return _try_convert_to_int(thickness)
def get_u_value_from_s9(