mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
fixed bug in create_base_difference_epc_record
This commit is contained in:
parent
12d1223f17
commit
5e3d522378
3 changed files with 106 additions and 167 deletions
|
|
@ -3,6 +3,7 @@ import ast
|
|||
from itertools import groupby
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from typing import Set
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from etl.epc.Dataset import TrainingDataset
|
||||
|
|
@ -55,12 +56,11 @@ class Property:
|
|||
walls = None
|
||||
windows = None
|
||||
lighting = None
|
||||
energy_source = None
|
||||
|
||||
spatial = None
|
||||
base_difference_record = None
|
||||
|
||||
DATA_ANOMALY_MATCHES = DATA_ANOMALY_MATCHES
|
||||
DATA_ANOMALY_MATCHES: Set = DATA_ANOMALY_MATCHES
|
||||
|
||||
# Surplus information, that can be provided as optional inputs, by a customer
|
||||
n_bathrooms = None
|
||||
|
|
@ -101,8 +101,7 @@ class Property:
|
|||
self.address = address
|
||||
self.postcode = postcode
|
||||
|
||||
self.old_data = self.epc_record.get("old_data")
|
||||
self.property_dimensions = None
|
||||
self.old_data = self.epc_record.old_data
|
||||
# This is a list of measures that have already been installed in the property, typically found as a result
|
||||
# of the non-invasive surveys. We reflect that this has been installed in the recommendations, but remove the
|
||||
# cost and instead, provide a message that the measure has already been installed
|
||||
|
|
@ -121,17 +120,17 @@ class Property:
|
|||
|
||||
self.valuation = property_valuation
|
||||
|
||||
self.uprn = uprn if uprn is not None else epc_record.get("uprn")
|
||||
self.uprn_source = self.epc_record.get("uprn-source")
|
||||
self.uprn = uprn if uprn is not None else epc_record.uprn
|
||||
self.uprn_source = self.epc_record.uprn_source
|
||||
|
||||
self.full_sap_epc = self.epc_record.get("full_sap_epc")
|
||||
self.full_sap_epc = self.epc_record.full_sap_epc
|
||||
self.in_conservation_area, self.is_listed, self.is_heritage = None, None, None
|
||||
self.restricted_measures = False
|
||||
self.year_built = epc_record.get("year_built")
|
||||
self.number_of_rooms = epc_record.get("number_habitable_rooms")
|
||||
self.age_band = epc_record.get("age_band")
|
||||
self.construction_age_band = epc_record.get("construction_age_band")
|
||||
self.number_of_floors = epc_record.get("number_of_floors")
|
||||
self.year_built = self.epc_record.year_built
|
||||
self.number_of_rooms = epc_record.number_habitable_rooms
|
||||
self.age_band = epc_record.age_band
|
||||
self.construction_age_band = epc_record.construction_age_band
|
||||
self.number_of_floors = epc_record.number_of_floors
|
||||
self.perimeter = None
|
||||
self.wall_type = None
|
||||
self.floor_type = None
|
||||
|
|
@ -141,61 +140,27 @@ class Property:
|
|||
|
||||
# when storing the energy, we'll also
|
||||
self.energy = {
|
||||
"primary_energy_consumption": epc_record.get("energy_consumption_current"),
|
||||
"epc_co2_emissions": epc_record.get("co2_emissions_current"),
|
||||
"primary_energy_consumption": epc_record.energy_consumption_current,
|
||||
"epc_co2_emissions": epc_record.co2_emissions_current,
|
||||
# These will be added in once we estimate the amount of emissions from appliances - using the carbon
|
||||
# intensity of electricity
|
||||
"appliances_co2_emissions": None,
|
||||
"co2_emissions": None
|
||||
}
|
||||
self.ventilation = {
|
||||
"ventilation": epc_record.get("mechanical_ventilation"),
|
||||
}
|
||||
self.solar_pv = {
|
||||
"solar_pv": epc_record.get("photo_supply"),
|
||||
}
|
||||
self.solar_hot_water = {
|
||||
"solar_hot_water": self.epc_record.get("solar_water_heating_flag"),
|
||||
"solar_hot_water_boolean": self.epc_record.get("solar_water_heating_flag_bool"),
|
||||
}
|
||||
self.wind_turbine = {
|
||||
"wind_turbine": self.epc_record.get("wind_turbine_count"),
|
||||
}
|
||||
self.number_of_open_fireplaces = {
|
||||
"number_of_open_fireplaces": self.epc_record.get(
|
||||
"number_open_fireplaces"
|
||||
),
|
||||
}
|
||||
self.number_of_extensions = {
|
||||
"number_of_extensions": self.epc_record.get("extension_count"),
|
||||
}
|
||||
self.number_of_storeys = {
|
||||
"number_of_storeys": self.epc_record.get("flat_storey_count"),
|
||||
}
|
||||
self.heat_loss_corridor = {
|
||||
"heat_loss_corridor": self.epc_record.get("heat_loss_corridor"),
|
||||
"length": self.epc_record.get("unheated_corridor_length"),
|
||||
"heat_loss_corridor_boolean": self.epc_record.get("heat_loss_corridor_bool"),
|
||||
}
|
||||
self.mains_gas = self.epc_record.get("mains_gas_flag")
|
||||
self.floor_height = self.epc_record.get("floor_height")
|
||||
self.mains_gas = self.epc_record.mains_gas_flag
|
||||
self.floor_height = self.epc_record.floor_height
|
||||
self.insulation_wall_area = None
|
||||
self.floor_area = self.epc_record.get("total_floor_area")
|
||||
self.floor_area = self.epc_record.total_floor_area
|
||||
self.roof_area = None
|
||||
self.insulation_floor_area = None
|
||||
self.number_lighting_outlets = self.epc_record.get("fixed_lighting_outlets_count")
|
||||
self.number_lighting_outlets = self.epc_record.fixed_lighting_outlets_count
|
||||
self.floor_level = None
|
||||
self.number_of_windows = None
|
||||
self.windows_area = None
|
||||
self.solar_pv_percentage = None
|
||||
|
||||
self.current_energy_consumption = None
|
||||
self.current_energy_consumption_heating_hotwater = None
|
||||
self.current_energy_bill = None
|
||||
self.expected_energy_bill = None
|
||||
|
||||
self.heating_energy_source = None
|
||||
self.hot_water_energy_source = None
|
||||
|
||||
self.recommendations_scoring_data = []
|
||||
self.simulation_epcs = {}
|
||||
|
|
@ -225,6 +190,12 @@ class Property:
|
|||
# Ventilation
|
||||
self.has_ventilation = self.identify_ventilation()
|
||||
|
||||
@staticmethod
|
||||
def _safe_int(value: str | int | float | None) -> int | None:
|
||||
if value in [None, ""]:
|
||||
return None
|
||||
return int(round(float(value) + 1e-5))
|
||||
|
||||
@classmethod
|
||||
def extract_kwargs(cls, kwargs):
|
||||
"""
|
||||
|
|
@ -237,24 +208,24 @@ class Property:
|
|||
# Note - none of this data is contained in an energy asssessment, but we should consider how this is done
|
||||
# as we collect more data from the energy assessment
|
||||
|
||||
n_bathrooms = kwargs.get("n_bathrooms", None)
|
||||
n_bathrooms = kwargs.get("n_bathrooms")
|
||||
# We add on a small value to ensure that the number of bathrooms is rounded up, in case the value is 0.5
|
||||
n_bathrooms = int(round(float(n_bathrooms) + 1e-5)) if n_bathrooms not in [None, ""] else None
|
||||
n_bathrooms = cls._safe_int(n_bathrooms) if n_bathrooms not in [None, ""] else None
|
||||
|
||||
n_bedrooms = kwargs.get("n_bedrooms", None)
|
||||
n_bedrooms = int(round(float(n_bedrooms) + 1e-5)) if n_bedrooms not in [None, ""] else None
|
||||
n_bedrooms = kwargs.get("n_bedrooms")
|
||||
n_bedrooms = cls._safe_int(n_bedrooms) if n_bedrooms not in [None, ""] else None
|
||||
|
||||
number_of_floors = kwargs.get("number_of_floors", None)
|
||||
number_of_floors = int(round(float(number_of_floors) + 1e-5)) if number_of_floors not in [None, ""] else None
|
||||
number_of_floors = kwargs.get("number_of_floors")
|
||||
number_of_floors = cls._safe_int(number_of_floors) if number_of_floors not in [None, ""] else None
|
||||
|
||||
insulation_floor_area = kwargs.get("insulation_floor_area", None)
|
||||
insulation_floor_area = kwargs.get("insulation_floor_area")
|
||||
insulation_floor_area = float(insulation_floor_area) if insulation_floor_area not in [None, ""] else None
|
||||
|
||||
insulation_wall_area = kwargs.get("insulation_wall_area", None)
|
||||
insulation_wall_area = kwargs.get("insulation_wall_area")
|
||||
insulation_wall_area = float(insulation_wall_area) if insulation_wall_area not in [None, ""] else None
|
||||
|
||||
# We allow for the asset owner to provide us with total floor area, in the event of it being incorrect
|
||||
floor_area = kwargs.get("floor_area", None)
|
||||
floor_area = kwargs.get("floor_area")
|
||||
floor_area = float(floor_area) if floor_area not in [None, ""] else None
|
||||
|
||||
return {
|
||||
|
|
@ -283,14 +254,11 @@ class Property:
|
|||
It will be the same starting and ending EPC, as we don't have the expected EPC yet
|
||||
"""
|
||||
|
||||
fixed_data_col_names = MANDATORY_FIXED_FEATURES + LATEST_FIELD
|
||||
fixed_data_col_names = [
|
||||
x.lower().replace("_", "-") for x in fixed_data_col_names
|
||||
]
|
||||
fixed_data_col_names = [x.lower() for x in MANDATORY_FIXED_FEATURES + LATEST_FIELD]
|
||||
|
||||
fixed_data = {
|
||||
k.replace("-", "_"): v
|
||||
for k, v in self.data.items()
|
||||
for k, v in vars(self.epc_record).items()
|
||||
if k in fixed_data_col_names
|
||||
}
|
||||
|
||||
|
|
@ -311,7 +279,7 @@ class Property:
|
|||
|
||||
# If we have variables that have been given to us by the landlord that we know are correct, whereas the EPC
|
||||
# may not be, we use them
|
||||
if self.owner_floor_area is not None:
|
||||
if self.owner_floor_area:
|
||||
self.base_difference_record.df["total_floor_area_ending"] = self.floor_area
|
||||
self.base_difference_record.df["estimated_perimeter_ending"] = self.perimeter
|
||||
|
||||
|
|
@ -410,7 +378,7 @@ class Property:
|
|||
|
||||
self.recommendations_scoring_data.append(scoring_dict)
|
||||
|
||||
simulation_epc = self.epc_record.__dict__.copy()
|
||||
simulation_epc = vars(self.epc_record).copy()
|
||||
# Insert static values
|
||||
simulation_epc["lodgement_date"] = simulation_lodgment_date
|
||||
simulation_epc = {k.replace("_", "-"): v for k, v in simulation_epc.items()}
|
||||
|
|
@ -487,7 +455,7 @@ class Property:
|
|||
# CO₂ emissions per square metre floor area per year in kg/m². Since CO₂ emissions are in tonnes
|
||||
# per year, we multiply by 1000 to get kg/m²
|
||||
"co2-emiss-curr-per-floor-area": round(
|
||||
1000 * (rec_impact["carbon"] / self.epc_record.get("total_floor_area"))
|
||||
1000 * (rec_impact["carbon"] / self.epc_record.total_floor_area)
|
||||
),
|
||||
"co2-emissions-current": rec_impact["carbon"],
|
||||
"current-energy-rating": sap_to_epc(rec_impact["sap"]),
|
||||
|
|
@ -600,15 +568,16 @@ class Property:
|
|||
for description, attribute in cleaned.items():
|
||||
|
||||
cleaner_cls = all_cleaner_map[description]
|
||||
description_underscore = description.replace("-", "_")
|
||||
|
||||
if self.epc_record.get(description) in self.DATA_ANOMALY_MATCHES:
|
||||
if getattr(self.epc_record, description_underscore) in self.DATA_ANOMALY_MATCHES:
|
||||
if description == "lighting-description":
|
||||
cleaner_cls = cleaner_cls("", averages=None)
|
||||
else:
|
||||
cleaner_cls = cleaner_cls("")
|
||||
fill_dict = {
|
||||
"original_description": self.epc_record.get(description),
|
||||
"clean_description": self.epc_record.get(description),
|
||||
"original_description": getattr(self.epc_record, description_underscore),
|
||||
"clean_description": getattr(self.epc_record, description_underscore),
|
||||
**cleaner_cls.process()
|
||||
}
|
||||
setattr(self, self.ATTRIBUTE_MAP[description], fill_dict)
|
||||
|
|
@ -617,7 +586,7 @@ class Property:
|
|||
attributes = [
|
||||
x
|
||||
for x in cleaned[description]
|
||||
if x["original_description"] == self.epc_record.get(description)
|
||||
if x["original_description"] == getattr(self.epc_record, description_underscore)
|
||||
]
|
||||
|
||||
if len(attributes) > 1:
|
||||
|
|
@ -628,11 +597,11 @@ class Property:
|
|||
if len(attributes) == 0:
|
||||
# We attempt to perform the clean on the fly
|
||||
if description == "lighting-description":
|
||||
cleaner_cls = cleaner_cls(self.epc_record.get(description), averages=None)
|
||||
cleaner_cls = cleaner_cls(getattr(self.epc_record, description_underscore), averages=None)
|
||||
else:
|
||||
cleaner_cls = cleaner_cls(self.epc_record.get(description))
|
||||
cleaner_cls = cleaner_cls(getattr(self.epc_record, description_underscore))
|
||||
processed = {
|
||||
"original_description": self.epc_record.get(description),
|
||||
"original_description": getattr(self.epc_record, description_underscore),
|
||||
"clean_description": cleaner_cls.description.replace(
|
||||
"(assumed)", ""
|
||||
)
|
||||
|
|
@ -671,12 +640,12 @@ class Property:
|
|||
|
||||
# Today's costs
|
||||
todays_lighting_cost = kwh_client.convert_cost_to_today(
|
||||
original_cost=float(self.data["lighting-cost-current"]),
|
||||
lodgement_date=pd.Timestamp(self.epc_record.get("lodgement_date")).tz_localize(None)
|
||||
original_cost=float(self.epc_record.lighting_cost_current),
|
||||
lodgement_date=pd.Timestamp(self.epc_record.lodgement_date).tz_localize(None)
|
||||
)
|
||||
|
||||
# If we have the kwh figures, we don't need to predict them
|
||||
condition_data = self.energy_assessment_condition_data.copy()
|
||||
condition_data = self.energy_assessment_condition_data
|
||||
|
||||
heating_kwh_predictions = kwh_predictions["heating_kwh_predictions"]
|
||||
hotwater_kwh_predictions = kwh_predictions["hotwater_kwh_predictions"]
|
||||
|
|
@ -715,19 +684,13 @@ class Property:
|
|||
}
|
||||
|
||||
# Sum up the adjusted kwh figures
|
||||
self.current_energy_consumption = sum(list(unadjusted_kwh_estimates.values()))
|
||||
self.current_energy_consumption = sum(unadjusted_kwh_estimates.values())
|
||||
self.current_energy_consumption_heating_hotwater = (
|
||||
unadjusted_kwh_estimates["heating"] + unadjusted_kwh_estimates["hot_water"]
|
||||
)
|
||||
|
||||
self.energy_cost_estimates = {
|
||||
"unadjusted": unadjusted_heating_costs,
|
||||
# Don't think we need the EPC
|
||||
# "epc": {
|
||||
# "heating": float(self.data["heating-cost-current"]),
|
||||
# "hot_water": float(self.data["hot-water-cost-current"]),
|
||||
# "lighting": float(self.data["lighting-cost-current"]),
|
||||
# }
|
||||
}
|
||||
|
||||
self.energy_consumption_estimates = {
|
||||
|
|
@ -787,7 +750,7 @@ class Property:
|
|||
:return:
|
||||
"""
|
||||
|
||||
current_sap_rating = float(self.data["current-energy-efficiency"])
|
||||
current_sap_rating = float(self.epc_record.current_energy_efficiency)
|
||||
if needs_rebaselining:
|
||||
current_sap_rating += rebaselining_sap
|
||||
|
||||
|
|
@ -795,24 +758,24 @@ class Property:
|
|||
|
||||
property_data = {
|
||||
"creation_status": "READY",
|
||||
"uprn": int(self.data["uprn"]),
|
||||
"uprn": int(self.epc_record.uprn),
|
||||
"building_reference_number": (
|
||||
int(self.data["building-reference-number"]) if
|
||||
self.data["building-reference-number"] is not None else None
|
||||
int(self.epc_record.building_reference_number) if
|
||||
self.epc_record.building_reference_number is not None else None
|
||||
),
|
||||
"has_pre_condition_report": True,
|
||||
"has_recommendations": True,
|
||||
"property_type": self.data["property-type"],
|
||||
"built_form": self.data["built-form"],
|
||||
"local_authority": self.data["local-authority-label"],
|
||||
"constituency": self.data["constituency-label"],
|
||||
"property_type": self.epc_record.property_type,
|
||||
"built_form": self.epc_record.built_form,
|
||||
"local_authority": self.epc_record.local_authority_label,
|
||||
"constituency": self.epc_record.constituency_label,
|
||||
"number_of_rooms": self.number_of_rooms,
|
||||
"year_built": self.year_built,
|
||||
"tenure": self.data["tenure"],
|
||||
"tenure": self.epc_record.tenure,
|
||||
"current_epc_rating": current_epc_rating,
|
||||
"current_sap_points": current_sap_rating,
|
||||
"current_valuation": current_valuation,
|
||||
"original_sap_points": self.data["current-energy-efficiency"],
|
||||
"original_sap_points": self.epc_record.current_energy_efficiency,
|
||||
"is_sap_points_adjusted_for_installed_measures": needs_rebaselining,
|
||||
"installed_measures_sap_point_adjustment": rebaselining_sap,
|
||||
}
|
||||
|
|
@ -841,7 +804,7 @@ class Property:
|
|||
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_overwritten = self.epc_record.sap_05_overwritten
|
||||
|
||||
sap_05_score, sap_05_epc_rating = None, None
|
||||
if sap_05_overwritten:
|
||||
|
|
@ -854,7 +817,7 @@ class Property:
|
|||
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"]
|
||||
lodgement_date = self.epc_record.lodgement_date
|
||||
# We check if the lodgement date is more than 10 years old
|
||||
is_expired = self.epc_is_expired
|
||||
|
||||
|
|
@ -876,42 +839,42 @@ class Property:
|
|||
"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"]),
|
||||
"full_address": self.epc_record.address,
|
||||
"total_floor_area": float(self.epc_record.total_floor_area),
|
||||
"walls": self.walls["clean_description"],
|
||||
"walls_rating": self._prepare_rating_field(self.data["walls-energy-eff"]),
|
||||
"walls_rating": self._prepare_rating_field(self.epc_record.walls_energy_eff),
|
||||
"roof": self.roof["clean_description"],
|
||||
"roof_rating": self._prepare_rating_field(self.data["roof-energy-eff"]),
|
||||
"roof_rating": self._prepare_rating_field(self.epc_record.roof_energy_eff),
|
||||
"floor": self.floor["clean_description"],
|
||||
"floor_rating": self._prepare_rating_field(self.data["floor-energy-eff"]),
|
||||
"floor_rating": self._prepare_rating_field(self.epc_record.floor_energy_eff),
|
||||
"windows": self.windows["clean_description"],
|
||||
"windows_rating": self._prepare_rating_field(self.data["windows-energy-eff"]),
|
||||
"windows_rating": self._prepare_rating_field(self.epc_record.windows_energy_eff),
|
||||
"heating": self.main_heating["clean_description"],
|
||||
"heating_rating": self._prepare_rating_field(self.data["mainheat-energy-eff"]),
|
||||
"heating_rating": self._prepare_rating_field(self.epc_record.mainheat_energy_eff),
|
||||
"heating_controls": self.main_heating_controls["clean_description"],
|
||||
"heating_controls_rating": self._prepare_rating_field(self.data["mainheatc-energy-eff"]),
|
||||
"heating_controls_rating": self._prepare_rating_field(self.epc_record.mainheatc_energy_eff),
|
||||
"hot_water": self.hotwater["clean_description"],
|
||||
"hot_water_rating": self._prepare_rating_field(self.data["hot-water-energy-eff"]),
|
||||
"hot_water_rating": self._prepare_rating_field(self.epc_record.hot_water_energy_eff),
|
||||
"lighting": self.lighting["clean_description"],
|
||||
"lighting_rating": self._prepare_rating_field(self.data["lighting-energy-eff"]),
|
||||
"lighting_rating": self._prepare_rating_field(self.epc_record.lighting_energy_eff),
|
||||
"mainfuel": self.main_fuel["clean_description"],
|
||||
"ventilation": self.ventilation["ventilation"],
|
||||
"solar_pv": self.solar_pv["solar_pv"],
|
||||
"solar_hot_water": self.solar_hot_water["solar_hot_water_boolean"],
|
||||
"wind_turbine": self.wind_turbine["wind_turbine"],
|
||||
"ventilation": self.epc_record.mechanical_ventilation,
|
||||
"solar_pv": self.epc_record.photo_supply,
|
||||
"solar_hot_water": self.epc_record.solar_water_heating_flag_bool,
|
||||
"wind_turbine": self.epc_record.wind_turbine_count,
|
||||
"floor_height": self.floor_height,
|
||||
"heat_loss_corridor": self.heat_loss_corridor["heat_loss_corridor_boolean"],
|
||||
"unheated_corridor_length": self.heat_loss_corridor["length"],
|
||||
"number_of_open_fireplaces": self.number_of_open_fireplaces["number_of_open_fireplaces"],
|
||||
"number_of_extensions": self.number_of_extensions["number_of_extensions"],
|
||||
"number_of_storeys": self.number_of_storeys["number_of_storeys"],
|
||||
"heat_loss_corridor": self.epc_record.heat_loss_corridor_bool,
|
||||
"unheated_corridor_length": self.epc_record.unheated_corridor_length,
|
||||
"number_of_open_fireplaces": self.epc_record.number_open_fireplaces,
|
||||
"number_of_extensions": self.epc_record.extension_count,
|
||||
"number_of_storeys": self.epc_record.flat_storey_count,
|
||||
"mains_gas": self.mains_gas,
|
||||
"energy_tariff": self.data["energy-tariff"],
|
||||
"energy_tariff": self.epc_record.energy_tariff,
|
||||
"primary_energy_consumption": primary_energy_consumption,
|
||||
"co2_emissions": co2_emissions,
|
||||
"current_energy_demand": current_kwh_demand, # This is kwh - naming is confusing
|
||||
"current_energy_demand_heating_hotwater": current_kwh_heating_hotwater, # This is kwh
|
||||
"estimated": self.data.get("estimated", False),
|
||||
"estimated": self.epc_record.estimated,
|
||||
# We indicate if we've overwritten a SAP 05 EPC
|
||||
"sap_05_overwritten": sap_05_overwritten,
|
||||
"sap_05_score": sap_05_score,
|
||||
|
|
@ -974,7 +937,7 @@ class Property:
|
|||
"""
|
||||
|
||||
result = property_dimensions[
|
||||
(property_dimensions["PROPERTY_TYPE"] == self.data["property-type"])
|
||||
(property_dimensions["PROPERTY_TYPE"] == self.epc_record.property_type)
|
||||
]
|
||||
|
||||
if (
|
||||
|
|
@ -986,10 +949,10 @@ class Property:
|
|||
]
|
||||
|
||||
if (
|
||||
self.data["built-form"] not in self.DATA_ANOMALY_MATCHES
|
||||
and self.data["built-form"] in result["BUILT_FORM"]
|
||||
self.epc_record.built_form not in self.DATA_ANOMALY_MATCHES
|
||||
and self.epc_record.built_form in result["BUILT_FORM"]
|
||||
):
|
||||
result = result[(result["BUILT_FORM"] == self.data["built-form"])]
|
||||
result = result[(result["BUILT_FORM"] == self.epc_record.built_form)]
|
||||
|
||||
return result[
|
||||
["NUMBER_HABITABLE_ROOMS", "TOTAL_FLOOR_AREA", "FLOOR_HEIGHT"]
|
||||
|
|
@ -1032,7 +995,7 @@ class Property:
|
|||
num_floors=self.number_of_floors,
|
||||
floor_height=self.floor_height,
|
||||
perimeter=self.perimeter,
|
||||
built_form=self.data["built-form"],
|
||||
built_form=self.epc_record.built_form,
|
||||
)
|
||||
|
||||
if self.insulation_floor_area is None:
|
||||
|
|
@ -1051,15 +1014,15 @@ class Property:
|
|||
|
||||
def set_floor_level(self):
|
||||
self.floor_level = (
|
||||
FLOOR_LEVEL_MAP[self.data["floor-level"]]
|
||||
if self.data["floor-level"] not in self.DATA_ANOMALY_MATCHES
|
||||
and self.data["floor-level"] is not None
|
||||
FLOOR_LEVEL_MAP[self.epc_record.floor_level]
|
||||
if self.epc_record.floor_level not in self.DATA_ANOMALY_MATCHES
|
||||
and self.epc_record.floor_level is not None
|
||||
else None
|
||||
)
|
||||
|
||||
if self.floor_level is None:
|
||||
|
||||
if self.data["property-type"] != "Flat":
|
||||
if self.epc_record.property_type != "Flat":
|
||||
return
|
||||
|
||||
if self.floor["another_property_below"]:
|
||||
|
|
@ -1119,21 +1082,6 @@ class Property:
|
|||
)
|
||||
self.floor_type = "suspended"
|
||||
|
||||
@staticmethod
|
||||
def _extract_component(
|
||||
component_data, component_rename_cols, component_drop_cols, rename_prefix=None
|
||||
):
|
||||
for k in component_rename_cols:
|
||||
component_data[f"{rename_prefix}_{k}"] = component_data.get(k)
|
||||
|
||||
component_data = {
|
||||
k: v
|
||||
for k, v in component_data.items()
|
||||
if k not in component_drop_cols + component_rename_cols
|
||||
}
|
||||
|
||||
return component_data
|
||||
|
||||
def set_windows_count(self):
|
||||
"""
|
||||
Using the estimate_windows function, this method will set the number of windows in the property
|
||||
|
|
@ -1145,8 +1093,8 @@ class Property:
|
|||
self.number_of_windows = int(condition_data["number_of_windows"]) \
|
||||
if condition_data.get("number_of_windows") is not None \
|
||||
else estimate_windows(
|
||||
property_type=self.data["property-type"],
|
||||
built_form=self.data["built-form"],
|
||||
property_type=self.epc_record.property_type,
|
||||
built_form=self.epc_record.built_form,
|
||||
construction_age_band=self.construction_age_band,
|
||||
floor_area=self.floor_area,
|
||||
number_habitable_rooms=self.number_of_rooms,
|
||||
|
|
@ -1166,14 +1114,14 @@ class Property:
|
|||
|
||||
# If we have a house over a floor area threshold, we recommend an ASHP
|
||||
if (
|
||||
self.data["property-type"] in ["House", "Bungalow"] and
|
||||
self.epc_record.property_type in ["House", "Bungalow"] and
|
||||
self.floor_area > assumptions.ASHP_FLOOR_AREA_THRESHOLD
|
||||
):
|
||||
return True
|
||||
|
||||
suitable_property_type = (
|
||||
self.data["property-type"] in ["House", "Bungalow"] and
|
||||
self.data["built-form"] not in ["Enclosed Mid-Terrace", "Enclosed End-Terrace"]
|
||||
self.epc_record.property_type in ["House", "Bungalow"] and
|
||||
self.epc_record.built_form not in ["Enclosed Mid-Terrace", "Enclosed End-Terrace"]
|
||||
)
|
||||
|
||||
has_air_source_heat_pump = self.main_heating["has_air_source_heat_pump"]
|
||||
|
|
@ -1195,12 +1143,12 @@ class Property:
|
|||
# may be installed such that they are not visible from the street
|
||||
return False
|
||||
|
||||
if (self.data["property-type"] in ["House", "Bungalow"]) and (
|
||||
if (self.epc_record.property_type in ["House", "Bungalow"]) and (
|
||||
not pd.isnull(self.roof["thermal_transmittance"])
|
||||
):
|
||||
return True
|
||||
|
||||
is_valid_property_type = self.data["property-type"] in ["House", "Bungalow", "Maisonette"]
|
||||
is_valid_property_type = self.epc_record.property_type in ["House", "Bungalow", "Maisonette"]
|
||||
is_valid_roof_type = (
|
||||
self.roof["is_flat"] or self.roof["is_pitched"] or self.roof["is_roof_room"]
|
||||
)
|
||||
|
|
@ -1213,7 +1161,7 @@ class Property:
|
|||
"already has solar pv", "roof too small", "no roof"
|
||||
]
|
||||
else:
|
||||
has_no_existing_solar_pv = self.data["photo-supply"] in [
|
||||
has_no_existing_solar_pv = self.epc_record.photo_supply in [
|
||||
None, 0, self.DATA_ANOMALY_MATCHES
|
||||
]
|
||||
|
||||
|
|
@ -1285,12 +1233,10 @@ class Property:
|
|||
|
||||
def identify_ventilation(self):
|
||||
|
||||
ventilation_descriptions = [
|
||||
return self.epc_record.mechanical_ventilation in {
|
||||
'mechanical, extract only',
|
||||
'mechanical, supply and extract'
|
||||
]
|
||||
|
||||
return self.epc_record.get("mechanical-ventilation") in ventilation_descriptions
|
||||
}
|
||||
|
||||
@property
|
||||
def epc_is_expired(self) -> bool:
|
||||
|
|
@ -1299,7 +1245,7 @@ class Property:
|
|||
valid for 10 years.
|
||||
:return: boolean indicating whether the EPC is expired
|
||||
"""
|
||||
lodgement_date = self.epc_record.get("lodgement-date")
|
||||
lodgement_date = self.epc_record.lodgement_date
|
||||
return (datetime.now() - pd.to_datetime(lodgement_date)) > timedelta(days=3650)
|
||||
|
||||
@property
|
||||
|
|
@ -1308,4 +1254,4 @@ class Property:
|
|||
This property indicates that the EPC is estimated, based on the presence of the "estimated" flag in the data
|
||||
:return: boolean indicating whether the EPC is estimated
|
||||
"""
|
||||
return self.epc_record.get("estimated")
|
||||
return self.epc_record.estimated
|
||||
|
|
|
|||
|
|
@ -813,19 +813,11 @@ async def model_engine(body: PlanTriggerRequest):
|
|||
# 1) EPC expired
|
||||
# 2) Missing EPC
|
||||
# 3) Materially different information from landlord vs EPC
|
||||
# make the landlord remapping dictionary
|
||||
|
||||
needs_rebaselining = p.epc_is_expired | p.epc_is_estimated | (len(differences) > 0)
|
||||
|
||||
p.epc_record.update(differences)
|
||||
# make the landlord remapping dictionar
|
||||
needs_rebaselining = p.epc_is_expired | p.epc_is_estimated | (len(p.epc_record.landlord_differences) > 0)
|
||||
|
||||
# Need to adjust p.data and p.epc_record.df?
|
||||
if needs_rebaselining:
|
||||
if len(differences):
|
||||
# Insert into prepared_epc
|
||||
for k, v in differences.items():
|
||||
p.epc_record.prepared_epc[k] = v
|
||||
|
||||
p.create_base_difference_epc_record(cleaned_lookup=cleaned)
|
||||
scoring_data = p.base_difference_record.df.copy()
|
||||
rebaselining_scoring_data.append(scoring_data)
|
||||
|
|
|
|||
|
|
@ -308,6 +308,7 @@ class EPCRecord:
|
|||
# ------------------------------------------------------------------
|
||||
# Indicates if the EPC record has been predicted. By default, false
|
||||
estimated: Optional[bool] = False
|
||||
sap_05_overwritten: Optional[bool] = False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# MODEL FLAGS
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue