mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added unit tests for recommendation utils and still restructuing repo
This commit is contained in:
parent
39a76daecc
commit
caedd79523
8 changed files with 142 additions and 50 deletions
1
.idea/Model.iml
generated
1
.idea/Model.iml
generated
|
|
@ -5,6 +5,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/backend" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/model_data" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (hestia-data)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
|
|
|||
|
|
@ -130,18 +130,21 @@ async def trigger_plan(body: PlanTriggerRequest):
|
|||
new_values=[float(p.data["total-floor-area"])],
|
||||
)[0]
|
||||
|
||||
# This
|
||||
# This is placeholder, until the full dataset is loaded into the database and we just make a read to the
|
||||
# database
|
||||
walls_u_value_estimate = [
|
||||
x for x in uvalue_estimates_walls
|
||||
if (x['local-authority'] == p.data["local-authority"]) &
|
||||
(x['property-type'] == p.data["property-type"]) &
|
||||
(x['built-form'] == p.data["built-form"]) &
|
||||
(x['walls-energy-eff'] == p.data["walls-energy-eff"]) &
|
||||
(x['walls-env-eff'] == p.data["walls-env-eff"]) &
|
||||
(x['total-floor-area_group'] == total_floor_area_group_decile)
|
||||
(x['walls-env-eff'] == p.data["walls-env-eff"])
|
||||
]
|
||||
|
||||
wall_recomendations = WallRecommendations(property_instance=p, uvalue_estimates=walls_u_value_estimate)
|
||||
wall_recomendations = WallRecommendations(
|
||||
property_instance=p, uvalue_estimates=walls_u_value_estimate,
|
||||
total_floor_area_group_decile=total_floor_area_group_decile
|
||||
)
|
||||
wall_recomendations.recommend()
|
||||
wall_recomendations = wall_recomendations.recommendations
|
||||
# insert property id
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
[pytest]
|
||||
addopts = --cov-report term-missing --cov=model_data
|
||||
testpaths = model_data/tests
|
||||
3
pytest.ini
Normal file
3
pytest.ini
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[pytest]
|
||||
addopts = --cov-report term-missing --cov=model_data --cov=recommendations
|
||||
testpaths = model_data/tests recommendations/tests
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import itertools
|
||||
import math
|
||||
from statistics import mean
|
||||
|
||||
from model_data.Property import Property
|
||||
from model_data.BaseUtility import BaseUtility
|
||||
|
|
@ -217,9 +216,10 @@ class WallRecommendations(BaseUtility):
|
|||
"solid_brick": 2,
|
||||
}
|
||||
|
||||
def __init__(self, property_instance: Property, uvalue_estimates):
|
||||
def __init__(self, property_instance: Property, uvalue_estimates, total_floor_area_group_decile):
|
||||
self.property = property_instance
|
||||
self.uvalue_estimates = uvalue_estimates
|
||||
self.total_floor_area_group_decile = total_floor_area_group_decile
|
||||
# For audit purposes, when estimating u values we'll store it
|
||||
self.estimated_u_value = None
|
||||
|
||||
|
|
@ -400,47 +400,6 @@ class WallRecommendations(BaseUtility):
|
|||
rec for rec in self.recommendations if rec["new_u_value"] >= self.DIMINISHING_RETURNS_U_VALUE
|
||||
]
|
||||
|
||||
def _get_walls_uvalue_estimate(self):
|
||||
|
||||
"""
|
||||
Wrapper function which contains the methodology to extract a property's walls u-value estimate
|
||||
when we don't have a true value and if we can't base our assumption off of the material
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not self.uvalue_estimates:
|
||||
raise ValueError("No U-value estimate found for the given property")
|
||||
|
||||
# Because of how spuriously populated the data is for number-habitable-rooms and number-heated-rooms,
|
||||
# we will try and filter on these to see if we get a result
|
||||
|
||||
habitable_rooms_filer = [
|
||||
x for x in self.uvalue_estimates if
|
||||
x["number-habitable-rooms"] == self.property.data["number-habitable-rooms"]
|
||||
]
|
||||
|
||||
if not habitable_rooms_filer:
|
||||
# Take a mean of all the u-value estimates
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in self.uvalue_estimates if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
||||
# Try perform a filter on heated rooms
|
||||
heated_rooms_filter = [
|
||||
x for x in habitable_rooms_filer if
|
||||
x["number-heated-rooms"] == self.property.data["number-heated-rooms"]
|
||||
]
|
||||
|
||||
if not heated_rooms_filter:
|
||||
# Take a mean of all the u-value estimates
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in habitable_rooms_filer if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in heated_rooms_filter if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def rvalue_per_mm(total_r_value: float, thickness_mm: float) -> float:
|
||||
"""Return R-value per mm.
|
||||
|
|
|
|||
0
recommendations/__init__.py
Normal file
0
recommendations/__init__.py
Normal file
|
|
@ -1,4 +1,6 @@
|
|||
from copy import deepcopy
|
||||
from model_data.Property import Property
|
||||
from statistics import mean
|
||||
|
||||
|
||||
def r_value_per_mm_to_u_value(depth_mm: int, r_value_per_mm: float):
|
||||
|
|
@ -110,3 +112,56 @@ def get_recommended_part(part, selected_depth):
|
|||
recommended_part["depths"] = [selected_depth]
|
||||
|
||||
return recommended_part
|
||||
|
||||
|
||||
def get_uvalue_estimate(uvalue_estimates, property: Property):
|
||||
"""
|
||||
Wrapper function which contains the methodology to extract a property's walls u-value estimate
|
||||
when we don't have a true value and if we can't base our assumption off of the material
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not uvalue_estimates:
|
||||
raise ValueError("No U-value estimate found for the given property - investigate")
|
||||
|
||||
# We try and filter on total_floor_area_group_decile
|
||||
floor_area_filter = [
|
||||
x for x in uvalue_estimates if
|
||||
x["total-floor-area_group"] == property.data["total_floor_area_group_decile"]
|
||||
]
|
||||
|
||||
if not floor_area_filter:
|
||||
# Take a mean of all the u-value estimates
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in uvalue_estimates if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
||||
# Because of how spuriously populated the data is for number-habitable-rooms and number-heated-rooms,
|
||||
# we will try and filter on these to see if we get a result
|
||||
|
||||
habitable_rooms_filer = [
|
||||
x for x in floor_area_filter if
|
||||
x["number-habitable-rooms"] == property.data["number-habitable-rooms"]
|
||||
]
|
||||
|
||||
if not habitable_rooms_filer:
|
||||
# Take a mean of all the u-value estimates
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in floor_area_filter if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
||||
# Try perform a filter on heated rooms
|
||||
heated_rooms_filter = [
|
||||
x for x in habitable_rooms_filer if
|
||||
x["number-heated-rooms"] == property.data["number-heated-rooms"]
|
||||
]
|
||||
|
||||
if not heated_rooms_filter:
|
||||
# Take a mean of all the u-value estimates
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in habitable_rooms_filer if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
||||
return mean(
|
||||
[x["median_thermal_transmittance"] for x in heated_rooms_filter if x["median_thermal_transmittance"]]
|
||||
)
|
||||
|
|
|
|||
74
recommendations/tests/test_recommendation_utils.py
Normal file
74
recommendations/tests/test_recommendation_utils.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
from recommendations import recommendation_utils
|
||||
|
||||
|
||||
class TestRecommendationUtils:
|
||||
@pytest.fixture
|
||||
def property_mock(self):
|
||||
PropertyMock = MagicMock()
|
||||
PropertyMock.data = {
|
||||
'total_floor_area_group_decile': 'Decile 1',
|
||||
'number-habitable-rooms': 3,
|
||||
'number-heated-rooms': 2
|
||||
}
|
||||
return PropertyMock
|
||||
|
||||
def test_r_value_per_mm_to_u_value(self):
|
||||
assert recommendation_utils.r_value_per_mm_to_u_value(1, 2) == 0.5
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
recommendation_utils.r_value_per_mm_to_u_value(0, 2)
|
||||
|
||||
def test_calculate_u_value_uplift(self):
|
||||
assert recommendation_utils.calculate_u_value_uplift(1, 2) == (0.33333333333333337, 0.6666666666666666)
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
recommendation_utils.calculate_u_value_uplift(0, 2)
|
||||
with pytest.raises(ZeroDivisionError):
|
||||
recommendation_utils.calculate_u_value_uplift(1, 0)
|
||||
|
||||
def test_is_diminishing_returns(self):
|
||||
assert not recommendation_utils.is_diminishing_returns([1, 2, 3], 1, 1, 1)
|
||||
assert recommendation_utils.is_diminishing_returns([1, 2, 3], 0.5, 1, 1)
|
||||
assert not recommendation_utils.is_diminishing_returns([], 1, None, 1)
|
||||
|
||||
def test_update_lowest_selected_u_value(self):
|
||||
assert recommendation_utils.update_lowest_selected_u_value(1, 2) == 1
|
||||
assert recommendation_utils.update_lowest_selected_u_value(None, 2) == 2
|
||||
assert recommendation_utils.update_lowest_selected_u_value(1, 0.5) == 0.5
|
||||
|
||||
def test_get_recommended_part(self):
|
||||
part = {'depths': [1, 2, 3]}
|
||||
assert recommendation_utils.get_recommended_part(part, 1) == {'depths': [1]}
|
||||
|
||||
def test_get_uvalue_estimate(self, property_mock):
|
||||
uvalue_estimates = [
|
||||
{
|
||||
'total-floor-area_group': 'Decile 1',
|
||||
'number-habitable-rooms': 3,
|
||||
'number-heated-rooms': 2,
|
||||
'median_thermal_transmittance': 1
|
||||
},
|
||||
{
|
||||
'total-floor-area_group': 'Decile 1',
|
||||
'number-habitable-rooms': 3,
|
||||
'number-heated-rooms': 2,
|
||||
'median_thermal_transmittance': 2
|
||||
}
|
||||
]
|
||||
|
||||
assert recommendation_utils.get_uvalue_estimate(uvalue_estimates, property_mock) == 1.5
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
recommendation_utils.get_uvalue_estimate([], property_mock)
|
||||
|
||||
# Test with missing 'median_thermal_transmittance' key
|
||||
uvalue_estimates_missing_key = [
|
||||
{
|
||||
'total-floor-area_group': 'Decile 1',
|
||||
'number-habitable-rooms': 3,
|
||||
'number-heated-rooms': 2
|
||||
}
|
||||
]
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
recommendation_utils.get_uvalue_estimate(uvalue_estimates_missing_key, property_mock)
|
||||
Loading…
Add table
Reference in a new issue