mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
completed funding tests
This commit is contained in:
parent
f355f12ed3
commit
97a6a27a15
13 changed files with 710 additions and 269 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$/open_uprn" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="epc_clean" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Fastapi-backend" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
|
@ -3,7 +3,7 @@
|
||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.10 (backend)" />
|
<option name="sdkName" value="Python 3.10 (backend)" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="epc_clean" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Fastapi-backend" project-jdk-type="Python SDK" />
|
||||||
<component name="PyCharmProfessionalAdvertiser">
|
<component name="PyCharmProfessionalAdvertiser">
|
||||||
<option name="shown" value="true" />
|
<option name="shown" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from backend.app.plan.schemas import HousingType, WALL_INSULATION_MEASURES, ROOF
|
||||||
|
|
||||||
|
|
||||||
class EligibilityCaveats(Enum):
|
class EligibilityCaveats(Enum):
|
||||||
|
EPC_RATING = "epc_rating" # EPC requirements not met
|
||||||
TENANT_ON_BENEFITS_OR_LOW_INCOME = "tenant_on_benefits_or_low_income"
|
TENANT_ON_BENEFITS_OR_LOW_INCOME = "tenant_on_benefits_or_low_income"
|
||||||
INNOVATION_REQUIRED = "innovation_required"
|
INNOVATION_REQUIRED = "innovation_required"
|
||||||
SOLAR_NEEDS_HEATING = "solar_needs_heating"
|
SOLAR_NEEDS_HEATING = "solar_needs_heating"
|
||||||
|
|
@ -26,19 +27,27 @@ class Funding:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
tenure: str, # 'Private' or 'Social'
|
tenure: str, # 'Private' or 'Social'
|
||||||
social_cavity_abs_rate: float,
|
eco4_social_cavity_abs_rate: float,
|
||||||
social_solid_abs_rate: float,
|
eco4_social_solid_abs_rate: float,
|
||||||
private_cavity_abs_rate: float,
|
eco4_private_cavity_abs_rate: float,
|
||||||
private_solid_abs_rate: float,
|
eco4_private_solid_abs_rate: float,
|
||||||
|
gbis_social_cavity_abs_rate: float,
|
||||||
|
gbis_social_solid_abs_rate: float,
|
||||||
|
gbis_private_cavity_abs_rate: float,
|
||||||
|
gbis_private_solid_abs_rate: float,
|
||||||
project_scores_matrix,
|
project_scores_matrix,
|
||||||
partial_project_scores_matrix,
|
partial_project_scores_matrix,
|
||||||
whlg_eligible_postcodes
|
whlg_eligible_postcodes
|
||||||
):
|
):
|
||||||
self.tenure = tenure
|
self.tenure = tenure
|
||||||
self.social_cavity_abs_rate = social_cavity_abs_rate
|
self.eco4_social_cavity_abs_rate = eco4_social_cavity_abs_rate
|
||||||
self.social_solid_abs_rate = social_solid_abs_rate
|
self.eco4_social_solid_abs_rate = eco4_social_solid_abs_rate
|
||||||
self.private_cavity_abs_rate = private_cavity_abs_rate
|
self.eco4_private_cavity_abs_rate = eco4_private_cavity_abs_rate
|
||||||
self.private_solid_abs_rate = private_solid_abs_rate
|
self.eco4_private_solid_abs_rate = eco4_private_solid_abs_rate
|
||||||
|
self.gbis_social_cavity_abs_rate = gbis_social_cavity_abs_rate
|
||||||
|
self.gbis_social_solid_abs_rate = gbis_social_solid_abs_rate
|
||||||
|
self.gbis_private_cavity_abs_rate = gbis_private_cavity_abs_rate
|
||||||
|
self.gbis_private_solid_abs_rate = gbis_private_solid_abs_rate
|
||||||
|
|
||||||
self.starting_sap_band = None
|
self.starting_sap_band = None
|
||||||
self.ending_sap_band = None
|
self.ending_sap_band = None
|
||||||
|
|
@ -55,6 +64,7 @@ class Funding:
|
||||||
|
|
||||||
# Funding calculation variables
|
# Funding calculation variables
|
||||||
self.full_project_abs = None
|
self.full_project_abs = None
|
||||||
|
self.gbis_funding = None
|
||||||
self.eco4_funding = None
|
self.eco4_funding = None
|
||||||
self.eco4_uplift = 0
|
self.eco4_uplift = 0
|
||||||
|
|
||||||
|
|
@ -141,7 +151,7 @@ class Funding:
|
||||||
|
|
||||||
if not meets_epc or not meets_upgrade_target:
|
if not meets_epc or not meets_upgrade_target:
|
||||||
self.eco4_eligible = False
|
self.eco4_eligible = False
|
||||||
self.eco4_eligibility_caveats = []
|
self.eco4_eligibility_caveats.append(EligibilityCaveats.EPC_RATING)
|
||||||
return
|
return
|
||||||
|
|
||||||
if has_solar and not solar_eligible:
|
if has_solar and not solar_eligible:
|
||||||
|
|
@ -587,6 +597,22 @@ class Funding:
|
||||||
raise ValueError("something went wrong, more than one pps for ashp")
|
raise ValueError("something went wrong, more than one pps for ashp")
|
||||||
return pps.squeeze()["Cost Savings"]
|
return pps.squeeze()["Cost Savings"]
|
||||||
|
|
||||||
|
if measure_type == "high_heat_retention_storage_heater":
|
||||||
|
pps_data = filtered_pps_matrix[
|
||||||
|
filtered_pps_matrix["Post_Main_Heating_Source"] == "High Heat Retention Storage Heaters"
|
||||||
|
]
|
||||||
|
# Not every heating upgrade, that ends at HHRSH, will have a PPS. E.g. a gas boiler to HHRSH upgrade
|
||||||
|
# doesn't have a PPS
|
||||||
|
if pre_heating_system in pps_data["Pre_Main_Heating_Source"].values:
|
||||||
|
pps = pps_data[
|
||||||
|
pps_data["Pre_Main_Heating_Source"] == pre_heating_system
|
||||||
|
]
|
||||||
|
if pps.shape[0] != 1:
|
||||||
|
raise ValueError("something went wrong, more than one pps for HHRSH")
|
||||||
|
return pps.squeeze()["Cost Savings"]
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
raise ValueError(f"Invalid measure type for partial project ABS calculation: {measure_type}")
|
raise ValueError(f"Invalid measure type for partial project ABS calculation: {measure_type}")
|
||||||
|
|
||||||
# -----------------------
|
# -----------------------
|
||||||
|
|
@ -752,6 +778,40 @@ class Funding:
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def calc_innovation_uplift(
|
||||||
|
self,
|
||||||
|
measure_types,
|
||||||
|
innovation_flags,
|
||||||
|
uplifts,
|
||||||
|
filtered_pps_matrix,
|
||||||
|
pre_heating_system,
|
||||||
|
mainheating,
|
||||||
|
main_fuel,
|
||||||
|
mainheat_energy_eff,
|
||||||
|
current_wall_uvalue,
|
||||||
|
is_partial,
|
||||||
|
existing_li_thickness,
|
||||||
|
):
|
||||||
|
"""Wrapper fundgion to calculate the innovation uplift for a project."""
|
||||||
|
project_uplifts = []
|
||||||
|
for i, measure in enumerate(measure_types):
|
||||||
|
if not innovation_flags[i]:
|
||||||
|
project_uplifts.append(0)
|
||||||
|
continue
|
||||||
|
pps = self.calculate_partial_project_abs(
|
||||||
|
measure_type=measure,
|
||||||
|
mainheating=mainheating,
|
||||||
|
main_fuel=main_fuel,
|
||||||
|
mainheat_energy_eff=mainheat_energy_eff,
|
||||||
|
current_wall_uvalue=current_wall_uvalue,
|
||||||
|
is_partial=is_partial,
|
||||||
|
existing_li_thickness=existing_li_thickness,
|
||||||
|
filtered_pps_matrix=filtered_pps_matrix,
|
||||||
|
pre_heating_system=pre_heating_system
|
||||||
|
)
|
||||||
|
project_uplifts.append(pps * uplifts[i])
|
||||||
|
return sum(project_uplifts)
|
||||||
|
|
||||||
def check_funding(
|
def check_funding(
|
||||||
self,
|
self,
|
||||||
measures: List[dict],
|
measures: List[dict],
|
||||||
|
|
@ -835,12 +895,43 @@ class Funding:
|
||||||
self.gbis_prs_eligibility(starting_sap, council_tax_band or "", measure_types)
|
self.gbis_prs_eligibility(starting_sap, council_tax_band or "", measure_types)
|
||||||
|
|
||||||
if self.eco4_eligible:
|
if self.eco4_eligible:
|
||||||
|
# Calculate the full project ABS for ECO4
|
||||||
self.full_project_abs = self.calculate_full_project_abs()
|
self.full_project_abs = self.calculate_full_project_abs()
|
||||||
|
|
||||||
|
self.eco4_uplift = self.calc_innovation_uplift(
|
||||||
|
measure_types=measure_types,
|
||||||
|
innovation_flags=innovation_flags,
|
||||||
|
uplifts=uplifts,
|
||||||
|
filtered_pps_matrix=filtered_pps_matrix,
|
||||||
|
pre_heating_system=pre_heating_system,
|
||||||
|
mainheating=mainheating,
|
||||||
|
main_fuel=main_fuel,
|
||||||
|
mainheat_energy_eff=mainheat_energy_eff,
|
||||||
|
current_wall_uvalue=current_wall_uvalue,
|
||||||
|
is_partial=is_partial,
|
||||||
|
existing_li_thickness=existing_li_thickness,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.full_project_abs += self.eco4_uplift
|
||||||
self.eco4_funding = self.full_project_abs * (
|
self.eco4_funding = self.full_project_abs * (
|
||||||
self.private_cavity_abs_rate if is_cavity else self.private_solid_abs_rate)
|
self.eco4_social_cavity_abs_rate if is_cavity else self.eco4_social_solid_abs_rate
|
||||||
|
)
|
||||||
|
|
||||||
if self.gbis_eligible:
|
if self.gbis_eligible:
|
||||||
raise NotImplementedError("FIX ME")
|
self.partial_project_abs = self.calculate_partial_project_abs(
|
||||||
|
measure_type=measure_types[0],
|
||||||
|
mainheating=mainheating,
|
||||||
|
main_fuel=main_fuel,
|
||||||
|
mainheat_energy_eff=mainheat_energy_eff,
|
||||||
|
current_wall_uvalue=current_wall_uvalue,
|
||||||
|
is_partial=is_partial,
|
||||||
|
existing_li_thickness=existing_li_thickness,
|
||||||
|
filtered_pps_matrix=filtered_pps_matrix,
|
||||||
|
pre_heating_system=pre_heating_system
|
||||||
|
)
|
||||||
|
self.gbis_funding = self.partial_project_abs * (
|
||||||
|
self.gbis_private_cavity_abs_rate if is_cavity else self.gbis_private_solid_abs_rate
|
||||||
|
)
|
||||||
|
|
||||||
elif self.tenure == "Social":
|
elif self.tenure == "Social":
|
||||||
# ECO4 Social
|
# ECO4 Social
|
||||||
|
|
@ -855,30 +946,23 @@ class Funding:
|
||||||
# Calculate the full project ABS for ECO4
|
# Calculate the full project ABS for ECO4
|
||||||
self.full_project_abs = self.calculate_full_project_abs()
|
self.full_project_abs = self.calculate_full_project_abs()
|
||||||
|
|
||||||
# We calculate uplift innovation, where required
|
self.eco4_uplift = self.calc_innovation_uplift(
|
||||||
project_uplifts = []
|
measure_types=measure_types,
|
||||||
for i, measure in enumerate(measure_types):
|
innovation_flags=innovation_flags,
|
||||||
if not innovation_flags[i]:
|
uplifts=uplifts,
|
||||||
# Capture 0 innovation uplift for debugging
|
filtered_pps_matrix=filtered_pps_matrix,
|
||||||
project_uplifts.append(0)
|
pre_heating_system=pre_heating_system,
|
||||||
continue
|
mainheating=mainheating,
|
||||||
|
main_fuel=main_fuel,
|
||||||
|
mainheat_energy_eff=mainheat_energy_eff,
|
||||||
|
current_wall_uvalue=current_wall_uvalue,
|
||||||
|
is_partial=is_partial,
|
||||||
|
existing_li_thickness=existing_li_thickness,
|
||||||
|
)
|
||||||
|
|
||||||
pps = self.calculate_partial_project_abs(
|
|
||||||
measure_type=measure,
|
|
||||||
mainheating=mainheating,
|
|
||||||
main_fuel=main_fuel,
|
|
||||||
mainheat_energy_eff=mainheat_energy_eff,
|
|
||||||
current_wall_uvalue=current_wall_uvalue,
|
|
||||||
is_partial=is_partial,
|
|
||||||
existing_li_thickness=existing_li_thickness,
|
|
||||||
filtered_pps_matrix=filtered_pps_matrix,
|
|
||||||
pre_heating_system=pre_heating_system
|
|
||||||
)
|
|
||||||
project_uplifts.append(pps * uplifts[i])
|
|
||||||
self.eco4_uplift = sum(project_uplifts)
|
|
||||||
self.full_project_abs += self.eco4_uplift
|
self.full_project_abs += self.eco4_uplift
|
||||||
self.eco4_funding = self.full_project_abs * (
|
self.eco4_funding = self.full_project_abs * (
|
||||||
self.social_cavity_abs_rate if is_cavity else self.social_solid_abs_rate
|
self.eco4_social_cavity_abs_rate if is_cavity else self.eco4_social_solid_abs_rate
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.gbis_eligible:
|
if self.gbis_eligible:
|
||||||
|
|
@ -894,6 +978,9 @@ class Funding:
|
||||||
filtered_pps_matrix=filtered_pps_matrix,
|
filtered_pps_matrix=filtered_pps_matrix,
|
||||||
pre_heating_system=pre_heating_system
|
pre_heating_system=pre_heating_system
|
||||||
)
|
)
|
||||||
|
self.gbis_funding = self.partial_project_abs * (
|
||||||
|
self.gbis_social_cavity_abs_rate if is_cavity else self.gbis_social_solid_abs_rate
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from backend.Funding import Funding, EligibilityCaveats
|
from backend.Funding import EligibilityCaveats
|
||||||
|
|
||||||
innovation_scenarios = [
|
innovation_scenarios = [
|
||||||
# 1) Innovation PV, non-eligible heating system in place, EPC D - not eligible
|
# 1) Innovation PV, non-eligible heating system in place, EPC D - not eligible
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, non-eligible heating system in place, EPC D",
|
"description": "Innovation PV, non-eligible heating system in place, EPC D",
|
||||||
"measures": [{"type": "solar_pv", "is_innovation": True}],
|
"measures": [{"type": "solar_pv", "is_innovation": True, "uplift": 0.45}],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Electric storage heaters",
|
"mainheat_description": "Electric storage heaters",
|
||||||
"heating_control_description": "Manual charge control",
|
"heating_control_description": "Manual charge control",
|
||||||
|
|
@ -16,7 +16,7 @@ innovation_scenarios = [
|
||||||
# 2) Innovation PV, eligible heating system in place, EPC D - eligible
|
# 2) Innovation PV, eligible heating system in place, EPC D - eligible
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, eligible heating system in place, EPC D",
|
"description": "Innovation PV, eligible heating system in place, EPC D",
|
||||||
"measures": [{"type": "solar_pv", "is_innovation": True}],
|
"measures": [{"type": "solar_pv", "is_innovation": True, "uplift": 0.45}],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
"heating_control_description": "Programmer, room thermostat and TRVs",
|
"heating_control_description": "Programmer, room thermostat and TRVs",
|
||||||
|
|
@ -29,8 +29,8 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV + HHRSH upgrade, EPC E",
|
"description": "Innovation PV + HHRSH upgrade, EPC E",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "high_heat_retention_storage_heater", "is_innovation": True}
|
{"type": "high_heat_retention_storage_heater", "is_innovation": True, "uplift": 0.1}
|
||||||
],
|
],
|
||||||
"starting_sap": 50,
|
"starting_sap": 50,
|
||||||
"mainheat_description": "Electric storage heaters",
|
"mainheat_description": "Electric storage heaters",
|
||||||
|
|
@ -44,8 +44,8 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV + HHRSH upgrade, EPC E",
|
"description": "Innovation PV + HHRSH upgrade, EPC E",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "high_heat_retention_storage_heater", "is_innovation": True}
|
{"type": "high_heat_retention_storage_heater", "is_innovation": True, "uplift": 0.1}
|
||||||
],
|
],
|
||||||
"starting_sap": 50,
|
"starting_sap": 50,
|
||||||
"mainheat_description": "Electric storage heaters",
|
"mainheat_description": "Electric storage heaters",
|
||||||
|
|
@ -58,7 +58,7 @@ innovation_scenarios = [
|
||||||
# 5) Innovation PV, needs wall insulation, no wall insulation measure - not eligible
|
# 5) Innovation PV, needs wall insulation, no wall insulation measure - not eligible
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, wall insulation recommended, but not installed",
|
"description": "Innovation PV, wall insulation recommended, but not installed",
|
||||||
"measures": [{"type": "solar_pv", "is_innovation": True}],
|
"measures": [{"type": "solar_pv", "is_innovation": True, "uplift": 0.45}],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
"heating_control_description": "Programmer, room thermostat and TRVs",
|
"heating_control_description": "Programmer, room thermostat and TRVs",
|
||||||
|
|
@ -71,8 +71,8 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, wall insulation recommended and installed",
|
"description": "Innovation PV, wall insulation recommended and installed",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "internal_wall_insulation", "is_innovation": False}
|
{"type": "internal_wall_insulation", "is_innovation": False, "uplift": 0.25}
|
||||||
],
|
],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
|
|
@ -85,7 +85,7 @@ innovation_scenarios = [
|
||||||
# 7) Innovation PV, needs roof insulation, no roof insulation measure - not eligible
|
# 7) Innovation PV, needs roof insulation, no roof insulation measure - not eligible
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, roof insulation recommended, not installed",
|
"description": "Innovation PV, roof insulation recommended, not installed",
|
||||||
"measures": [{"type": "solar_pv", "is_innovation": True}],
|
"measures": [{"type": "solar_pv", "is_innovation": True, "uplift": 0.45}],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
"heating_control_description": "Programmer, room thermostat and TRVs",
|
"heating_control_description": "Programmer, room thermostat and TRVs",
|
||||||
|
|
@ -98,8 +98,8 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, roof insulation recommended and installed",
|
"description": "Innovation PV, roof insulation recommended and installed",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "loft_insulation", "is_innovation": False}
|
{"type": "loft_insulation", "is_innovation": False, "uplift": 0}
|
||||||
],
|
],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
|
|
@ -112,7 +112,7 @@ innovation_scenarios = [
|
||||||
# 9) Innovation PV, needs both roof + wall insulation, no insulation - not eligible
|
# 9) Innovation PV, needs both roof + wall insulation, no insulation - not eligible
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, both insulations recommended, none installed",
|
"description": "Innovation PV, both insulations recommended, none installed",
|
||||||
"measures": [{"type": "solar_pv", "is_innovation": True}],
|
"measures": [{"type": "solar_pv", "is_innovation": True, "uplift": 0.45}],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
"heating_control_description": "Programmer, room thermostat and TRVs",
|
"heating_control_description": "Programmer, room thermostat and TRVs",
|
||||||
|
|
@ -125,8 +125,8 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, both insulations recommended, only wall done",
|
"description": "Innovation PV, both insulations recommended, only wall done",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "internal_wall_insulation", "is_innovation": False}
|
{"type": "internal_wall_insulation", "is_innovation": False, "uplift": 0.25}
|
||||||
],
|
],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
|
|
@ -140,8 +140,8 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, both insulations recommended, only roof done",
|
"description": "Innovation PV, both insulations recommended, only roof done",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "loft_insulation", "is_innovation": False}
|
{"type": "loft_insulation", "is_innovation": False, "uplift": 0}
|
||||||
],
|
],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
|
|
@ -155,9 +155,9 @@ innovation_scenarios = [
|
||||||
{
|
{
|
||||||
"description": "Innovation PV, both insulations recommended and installed",
|
"description": "Innovation PV, both insulations recommended and installed",
|
||||||
"measures": [
|
"measures": [
|
||||||
{"type": "solar_pv", "is_innovation": True},
|
{"type": "solar_pv", "is_innovation": True, "uplift": 0.45},
|
||||||
{"type": "internal_wall_insulation", "is_innovation": False},
|
{"type": "internal_wall_insulation", "is_innovation": False, "uplift": 0.25},
|
||||||
{"type": "loft_insulation", "is_innovation": False}
|
{"type": "loft_insulation", "is_innovation": False, "uplift": 0}
|
||||||
],
|
],
|
||||||
"starting_sap": 60,
|
"starting_sap": 60,
|
||||||
"mainheat_description": "Air source heat pump, radiators",
|
"mainheat_description": "Air source heat pump, radiators",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -40,28 +40,35 @@ def app():
|
||||||
cleaned_data = {}
|
cleaned_data = {}
|
||||||
epc_directories = [entry for entry in EPC_DIRECTORY.iterdir() if entry.is_dir()]
|
epc_directories = [entry for entry in EPC_DIRECTORY.iterdir() if entry.is_dir()]
|
||||||
|
|
||||||
|
errors = []
|
||||||
for directory in tqdm(epc_directories):
|
for directory in tqdm(epc_directories):
|
||||||
data = pd.read_csv(directory / "certificates.csv", low_memory=False)
|
try:
|
||||||
# Rename the columns to the same format as the api returns
|
data = pd.read_csv(directory / "certificates.csv", low_memory=False)
|
||||||
data.columns = [c.replace("_", "-").lower() for c in data.columns]
|
# Rename the columns to the same format as the api returns
|
||||||
# Take just date before the date threshold
|
data.columns = [c.replace("_", "-").lower() for c in data.columns]
|
||||||
data = data[data["lodgement-date"] >= "2011-01-01"]
|
# Take just date before the date threshold
|
||||||
|
data = data[data["lodgement-date"] >= "2011-01-01"]
|
||||||
|
|
||||||
# Convert to list of dictioaries as returned by the api
|
# Convert to list of dictioaries as returned by the api
|
||||||
data = data.to_dict("records")
|
data = data.to_dict("records")
|
||||||
|
|
||||||
# Incorporate input data into cleaning
|
# Incorporate input data into cleaning
|
||||||
cleaner = EpcClean(data)
|
cleaner = EpcClean(data)
|
||||||
|
|
||||||
cleaner.clean()
|
cleaner.clean()
|
||||||
# Extended cleaned_data
|
# Extended cleaned_data
|
||||||
for k, data in cleaner.cleaned.items():
|
for k, data in cleaner.cleaned.items():
|
||||||
if k not in cleaned_data:
|
if k not in cleaned_data:
|
||||||
cleaned_data[k] = data
|
cleaned_data[k] = data
|
||||||
else:
|
else:
|
||||||
existing_descriptions = [x["original_description"] for x in cleaned_data[k]]
|
existing_descriptions = [x["original_description"] for x in cleaned_data[k]]
|
||||||
new_data = [x for x in data if x["original_description"] not in existing_descriptions]
|
new_data = [x for x in data if x["original_description"] not in existing_descriptions]
|
||||||
cleaned_data[k].extend(new_data)
|
cleaned_data[k].extend(new_data)
|
||||||
|
except Exception as e:
|
||||||
|
errors.append(directory)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
raise ValueError("We have errors")
|
||||||
|
|
||||||
# Basic check to make sure all descriptions are unique
|
# Basic check to make sure all descriptions are unique
|
||||||
for _, cleaned in cleaned_data.items():
|
for _, cleaned in cleaned_data.items():
|
||||||
|
|
@ -75,7 +82,6 @@ def app():
|
||||||
# data being read in will be extremely small, meaning quicker load times. We'll begin by storing as a single
|
# data being read in will be extremely small, meaning quicker load times. We'll begin by storing as a single
|
||||||
# file and monitor usage patterns to see if it makes sense to split the data up
|
# file and monitor usage patterns to see if it makes sense to split the data up
|
||||||
|
|
||||||
# TODO: Copy the existing cleaned to an archive location, in case we wish to roll back easily
|
|
||||||
cleaned_historic = read_from_s3(
|
cleaned_historic = read_from_s3(
|
||||||
s3_file_name="cleaned_epc_data/cleaned.bson",
|
s3_file_name="cleaned_epc_data/cleaned.bson",
|
||||||
bucket_name=f"retrofit-data-{ENVIRONMENT}"
|
bucket_name=f"retrofit-data-{ENVIRONMENT}"
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ class FloorAttributes(Definitions):
|
||||||
"i ofod heb ei wresogi, dim inswleiddio (rhagdybiaeth)": "to unheated space, no insulation (assumed)",
|
"i ofod heb ei wresogi, dim inswleiddio (rhagdybiaeth)": "to unheated space, no insulation (assumed)",
|
||||||
"i ofod heb ei wresogi, heb ei inswleiddio (rhagdybiaeth)": "to unheated space, no insulation (assumed)",
|
"i ofod heb ei wresogi, heb ei inswleiddio (rhagdybiaeth)": "to unheated space, no insulation (assumed)",
|
||||||
"i ofod heb ei wresogi, dim inswleiddio": "to unheated space, no insulation",
|
"i ofod heb ei wresogi, dim inswleiddio": "to unheated space, no insulation",
|
||||||
|
"igçör awyr y tu allan, wedigçöi inswleiddio (rhagdybiaeth)": "to external air, insulated (assumed)",
|
||||||
|
"crog, inswleiddio cyfyngedig (rhagdybiaeth)": "suspended, limited insulation (assumed)"
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, description: str):
|
def __init__(self, description: str):
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ class HotWaterAttributes(Definitions):
|
||||||
"o r brif system, gydag ynni r haul, dim thermostat ar y silindr": "from main system, plus solar, no cylinder "
|
"o r brif system, gydag ynni r haul, dim thermostat ar y silindr": "from main system, plus solar, no cylinder "
|
||||||
"thermostat",
|
"thermostat",
|
||||||
"o r brif system, gydag ynni r haul": "from main system, plus solar",
|
"o r brif system, gydag ynni r haul": "from main system, plus solar",
|
||||||
|
"pwmp gwres": "heat pump"
|
||||||
}
|
}
|
||||||
|
|
||||||
NODATA_DESCRIPTIONS = [
|
NODATA_DESCRIPTIONS = [
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ class LightingAttributes(Definitions):
|
||||||
"goleuadau ynni-isel ym mhob un o'r mannau gosod": 'Low energy lighting in all fixed outlets',
|
"goleuadau ynni-isel ym mhob un o'r mannau gosod": 'Low energy lighting in all fixed outlets',
|
||||||
"effeithlonrwydd goleuo da": 'good lighting efficiency',
|
"effeithlonrwydd goleuo da": 'good lighting efficiency',
|
||||||
"effeithlonrwydd goleuo is na'r cyfartaledd": 'below average lighting efficiency',
|
"effeithlonrwydd goleuo is na'r cyfartaledd": 'below average lighting efficiency',
|
||||||
|
"effeithlonrwydd goleuo rhagorol": "excellent lighting efficiency"
|
||||||
}
|
}
|
||||||
|
|
||||||
OBSERVED_ERRORS = []
|
OBSERVED_ERRORS = []
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,9 @@ class MainHeatAttributes(Definitions):
|
||||||
"gas-fired heat pumps, electric": "air source heat pump, electric",
|
"gas-fired heat pumps, electric": "air source heat pump, electric",
|
||||||
"radiator heating, heat from boilers - gas": "boiler and radiators, mains gas",
|
"radiator heating, heat from boilers - gas": "boiler and radiators, mains gas",
|
||||||
"heat pump, warm air, mains gas": "air source heat pump, warm air, mains gas",
|
"heat pump, warm air, mains gas": "air source heat pump, warm air, mains gas",
|
||||||
"air sourceheat pump, radiators, electric": "air source heat pump, radiators, electric"
|
"air sourceheat pump, radiators, electric": "air source heat pump, radiators, electric",
|
||||||
|
"bwyler gyda rheiddiaduron a gwres dan y llawr, nwy prif gyflenwad": "Boiler and radiators, mains gas, "
|
||||||
|
"Boiler and underfloor heating, mains gas",
|
||||||
}
|
}
|
||||||
|
|
||||||
edge_case_result = {}
|
edge_case_result = {}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ class MainheatControlAttributes(Definitions):
|
||||||
TO_REMAP = {
|
TO_REMAP = {
|
||||||
"celect control": 'celect-type control',
|
"celect control": 'celect-type control',
|
||||||
"celect controls": 'celect-type control',
|
"celect controls": 'celect-type control',
|
||||||
|
"celect type controls": 'celect-type control',
|
||||||
"trv's, program & flow switch": 'trvs, programmer & flow switch',
|
"trv's, program & flow switch": 'trvs, programmer & flow switch',
|
||||||
'appliance thermostat': 'appliance thermostats',
|
'appliance thermostat': 'appliance thermostats',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -864,7 +864,7 @@ mainheat_cases = [
|
||||||
'has_wood_pellets': False, 'has_anthracite': False, 'has_dual_fuel_mineral_and_wood': True,
|
'has_wood_pellets': False, 'has_anthracite': False, 'has_dual_fuel_mineral_and_wood': True,
|
||||||
'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False, 'has_electricaire': False,
|
'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False, 'has_electricaire': False,
|
||||||
'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False, "has_electric_heat_pumps": False,
|
'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False, "has_electric_heat_pumps": False,
|
||||||
"has_micro-cogeneration": False},
|
"has_micro-cogeneration": False, 'has_mineral_and_wood': True},
|
||||||
{'original_description': 'Room heaters, electric', 'has_radiators': False, 'has_fan_coil_units': False,
|
{'original_description': 'Room heaters, electric', 'has_radiators': False, 'has_fan_coil_units': False,
|
||||||
'has_pipes_in_screed_above_insulation': False, 'has_pipes_in_insulated_timber_floor': False,
|
'has_pipes_in_screed_above_insulation': False, 'has_pipes_in_insulated_timber_floor': False,
|
||||||
'has_pipes_in_concrete_slab': False, 'has_boiler': False, 'has_air_source_heat_pump': False,
|
'has_pipes_in_concrete_slab': False, 'has_boiler': False, 'has_air_source_heat_pump': False,
|
||||||
|
|
@ -1455,8 +1455,7 @@ mainheat_cases = [
|
||||||
'has_coal': False, 'has_oil': False, 'has_wood_pellets': False, 'has_anthracite': False,
|
'has_coal': False, 'has_oil': False, 'has_wood_pellets': False, 'has_anthracite': False,
|
||||||
'has_dual_fuel_mineral_and_wood': True, 'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False,
|
'has_dual_fuel_mineral_and_wood': True, 'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False,
|
||||||
'has_electricaire': False, 'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False,
|
'has_electricaire': False, 'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False,
|
||||||
"has_electric_heat_pumps": False,
|
"has_electric_heat_pumps": False, "has_micro-cogeneration": False, "has_mineral_and_wood": True},
|
||||||
"has_micro-cogeneration": False},
|
|
||||||
{'original_description': 'Bwyler a rheiddiaduron, dau danwydd (mwynau a choed)', 'has_radiators': True,
|
{'original_description': 'Bwyler a rheiddiaduron, dau danwydd (mwynau a choed)', 'has_radiators': True,
|
||||||
'has_fan_coil_units': False,
|
'has_fan_coil_units': False,
|
||||||
'has_pipes_in_screed_above_insulation': False, 'has_pipes_in_insulated_timber_floor': False,
|
'has_pipes_in_screed_above_insulation': False, 'has_pipes_in_insulated_timber_floor': False,
|
||||||
|
|
@ -1468,8 +1467,8 @@ mainheat_cases = [
|
||||||
'has_coal': False, 'has_oil': False, 'has_wood_pellets': False, 'has_anthracite': False,
|
'has_coal': False, 'has_oil': False, 'has_wood_pellets': False, 'has_anthracite': False,
|
||||||
'has_dual_fuel_mineral_and_wood': True, 'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False,
|
'has_dual_fuel_mineral_and_wood': True, 'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False,
|
||||||
'has_electricaire': False, 'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False,
|
'has_electricaire': False, 'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False,
|
||||||
"has_electric_heat_pumps": False,
|
"has_electric_heat_pumps": False, "has_micro-cogeneration": False, "has_mineral_and_wood": True
|
||||||
"has_micro-cogeneration": False},
|
},
|
||||||
{'original_description': 'Pwmp gwres syGÇÖn tarddu yn y ddaear, dan y llawr, trydan', 'has_radiators': False,
|
{'original_description': 'Pwmp gwres syGÇÖn tarddu yn y ddaear, dan y llawr, trydan', 'has_radiators': False,
|
||||||
'has_fan_coil_units': False, 'has_pipes_in_screed_above_insulation': False,
|
'has_fan_coil_units': False, 'has_pipes_in_screed_above_insulation': False,
|
||||||
'has_pipes_in_insulated_timber_floor': False, 'has_pipes_in_concrete_slab': False, 'has_boiler': False,
|
'has_pipes_in_insulated_timber_floor': False, 'has_pipes_in_concrete_slab': False, 'has_boiler': False,
|
||||||
|
|
@ -1541,7 +1540,7 @@ mainheat_cases = [
|
||||||
'has_wood_pellets': False, 'has_anthracite': False, 'has_dual_fuel_mineral_and_wood': True,
|
'has_wood_pellets': False, 'has_anthracite': False, 'has_dual_fuel_mineral_and_wood': True,
|
||||||
'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False, 'has_electricaire': False,
|
'has_smokeless_fuel': False, 'has_lpg': False, 'has_assumed': False, 'has_electricaire': False,
|
||||||
'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False, "has_electric_heat_pumps": False,
|
'has_assumed_for_most_rooms': False, 'has_underfloor_heating': False, "has_electric_heat_pumps": False,
|
||||||
"has_micro-cogeneration": False},
|
"has_micro-cogeneration": False, "has_mineral_and_wood": True},
|
||||||
{'original_description': 'Room heaters, wood pellets', 'has_radiators': False, 'has_fan_coil_units': False,
|
{'original_description': 'Room heaters, wood pellets', 'has_radiators': False, 'has_fan_coil_units': False,
|
||||||
'has_pipes_in_screed_above_insulation': False, 'has_pipes_in_insulated_timber_floor': False,
|
'has_pipes_in_screed_above_insulation': False, 'has_pipes_in_insulated_timber_floor': False,
|
||||||
'has_pipes_in_concrete_slab': False, 'has_boiler': False, 'has_air_source_heat_pump': False,
|
'has_pipes_in_concrete_slab': False, 'has_boiler': False, 'has_air_source_heat_pump': False,
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,12 @@ class TestHeatingRecommendations:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# We patch an old version of cleaned which is missing some attributes for 'mainheat-description'
|
||||||
|
for x in cleaned['mainheat-description']:
|
||||||
|
x["has_hot-water-only"] = False
|
||||||
|
x["has_mineral_and_wood"] = False
|
||||||
|
x["has_dual_fuel_appliance"] = False
|
||||||
|
|
||||||
epc_records = {"original_epc": test_case["epc"].copy(), "full_sap_epc": {}, "old_data": []}
|
epc_records = {"original_epc": test_case["epc"].copy(), "full_sap_epc": {}, "old_data": []}
|
||||||
|
|
||||||
epc_record = EPCRecord(
|
epc_record = EPCRecord(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue