Added unit tests for recommendation utils and still restructuing repo

This commit is contained in:
Khalim Conn-Kowlessar 2023-07-21 11:49:58 +01:00
parent 39a76daecc
commit caedd79523
8 changed files with 142 additions and 50 deletions

1
.idea/Model.iml generated
View file

@ -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" />

View file

@ -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

View file

@ -1,3 +0,0 @@
[pytest]
addopts = --cov-report term-missing --cov=model_data
testpaths = model_data/tests

3
pytest.ini Normal file
View file

@ -0,0 +1,3 @@
[pytest]
addopts = --cov-report term-missing --cov=model_data --cov=recommendations
testpaths = model_data/tests recommendations/tests

View file

@ -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.

View file

View 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"]]
)

View 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)