import os import pytest import pickle import numpy as np from unittest.mock import Mock, MagicMock from recommendations.WallRecommendations import WallRecommendations from backend.Property import Property from recommendations.recommendation_utils import is_diminishing_returns from recommendations.tests.test_data.materials import materials from etl.epc.Record import EPCRecord # import inspect # file_path = inspect.getfile(lambda: None) # with open( # os.path.abspath(os.path.dirname(file_path)) + "/recommendations/tests/test_data/input_properties.pkl", "rb" # ) as f: # input_properties = pickle.load(f) class TestWallRecommendations: @pytest.fixture def input_properties(self): with open( os.path.abspath(os.path.dirname(__file__)) + "/test_data/input_properties.pkl", "rb" ) as f: return pickle.load(f) @pytest.fixture def mock_wall_rec_instance(self): # Creating a mock instance of WallRecommendations with the necessary attributes property_mock = Mock() property_mock.full_sap_epc = {"lodgement-date": "2000-01-01"} # or any date you want property_mock.data = {"construction-age-band": "1950", "county": "Derbyshire"} # or any other data that fits your tests mock_wall_rec_instance = WallRecommendations( property_mock, materials=materials ) return mock_wall_rec_instance def test_init(self, input_properties): input_properties[0].insulation_wall_area = 100 obj = WallRecommendations( property_instance=input_properties[0], materials=materials ) assert obj assert obj.property def test_uvalue_0_16(self, input_properties): """ This tests the wall description Average thermal transmittance 0.16 W/m-¦K The important data for this recommendation is: - u value of 0.16 - property built in 2014 Since epc built after 1990 are typically built with insulation and this property already has really good insulation, we do NOT recommend any measures for this property """ input_properties[0].year_built = 2014 input_properties[0].in_conservation_area = None input_properties[0].restricted_measures = False input_properties[0].insulation_wall_area = 100 recommender = WallRecommendations( property_instance=input_properties[0], materials=materials ) assert recommender.property.walls["original_description"] == "Average thermal transmittance 0.16 W/m-¦K" recommender.recommend() # This should be empty assert recommender.recommendations == [] def test_solid_brick_no_insulation(self, input_properties): """ This tests a property with a wall description of Solid brick, as built, no insulation (assumed) The property was built in 1930, right on the threshold for when cavity walls were introduced However, we're told this property is solid brik so we assume no cavity. We're also told that it has no insulation, so we will recommend internal/external wall insulation This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation """ input_properties[1].year_built = 1930 input_properties[1].insulation_wall_area = 100 input_properties[1].walls["clean_description"] = "Solid brick, as built, no insulation" input_properties[1].walls["is_sandstone_or_limestone"] = False input_properties[1].age_band = "A" input_properties[1].restricted_measures = False input_properties[1].already_installed = [] input_properties[1].walls["is_park_home"] = False input_properties[1].construction_age_band = "England and Wales: 1930-1949" input_properties[1].non_invasive_recommendations = [] recommender = WallRecommendations( property_instance=input_properties[1], materials=materials ) assert recommender.property.walls["original_description"] == "Solid brick, as built, no insulation (assumed)" assert not recommender.ewi_valid() assert recommender.property.in_conservation_area == "not_in_conservation_area" assert recommender.property.data["property-type"] == "Flat" recommender.recommend(phase=0) # This should result in some recommendations, all of which should be internal insulation assert recommender.recommendations rec_types = {part["type"] for rec in recommender.recommendations for part in rec["parts"]} assert rec_types == {"internal_wall_insulation"} # Check the recommendations provide a u value below the minimum assert all( rec["new_u_value"] < WallRecommendations.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE for rec in recommender.recommendations ) def test_solid_brick_insulation(self, input_properties): """ This tests a property with a wall description of Solid brick, as built, insulation (assumed) The property was built in 1991, after cavity walls were introduced However, we're told this property is solid brik so we assume no cavity. We're also told that it has some insulation already Since the walls are already insulated, we don't recommend further measures This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation """ input_properties[6].year_built = 1991 input_properties[6].restricted_measures = False input_properties[6].insulation_wall_area = 100 recommender = WallRecommendations( property_instance=input_properties[6], materials=materials ) assert recommender.property.walls["original_description"] == "Solid brick, as built, insulated (assumed)" assert not recommender.ewi_valid() assert recommender.property.in_conservation_area == "not_in_conservation_area" assert recommender.property.data["property-type"] == "Flat" assert recommender.estimated_u_value is None recommender.recommend() assert not recommender.recommendations assert not recommender.estimated_u_value def test_is_diminishing_returns_no_recommendations(self): # We have no recommendations but the new u value is below the diminishing returns threshold # however since there are no recommendaions yet, we should include it and say # it is NOT diminishing new_u_value = 0.24 assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE assert not is_diminishing_returns([], new_u_value, None, WallRecommendations.DIMINISHING_RETURNS_U_VALUE) def test_is_diminishing_returns_diminishing(self): # We have a recommendation already and the new u value falls below the diminishing returns # threshold and is also lower than the lowest selected u value, so we should say this IS # diminishing returns new_u_value = 0.2 lowest_selected_u_value = 0.23 assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE assert is_diminishing_returns([Mock()], new_u_value, lowest_selected_u_value, WallRecommendations.DIMINISHING_RETURNS_U_VALUE) def test_is_diminishing_returns_is_diminishing(self): # We have a recommendation already and the new u value falls below the diminishing returns # threshold and it's lower than the lowest selected u value, so we should say this IS DIMINISHIN new_u_value = 0.24 lowest_selected_u_value = 0.26 assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE assert is_diminishing_returns([Mock()], new_u_value, lowest_selected_u_value, WallRecommendations.DIMINISHING_RETURNS_U_VALUE) def test_is_diminishing_returns_not_diminishing(self): # We have a recommendation already and the new u value falls below the diminishing returns # threshold however it's higher than the lowest selected u value, so we should say this is NOT # diminishing returns new_u_value = 0.24 lowest_selected_u_value = 0.22 assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE assert not is_diminishing_returns([Mock()], new_u_value, lowest_selected_u_value, WallRecommendations.DIMINISHING_RETURNS_U_VALUE) def test_is_diminishing_returns_error_in_recommendations(self, mock_wall_rec_instance): # Testing case where there's an error in recommendations mock_wall_rec_instance.recommendations = [] with pytest.raises(ValueError): is_diminishing_returns([], 0.2, 0.24, 0.25) class TestWallRecommendationsBase: @pytest.fixture def property_mock(self): property_mock = MagicMock(spec=Property) property_mock.full_sap_epc = {"lodgement-date": "1999-12-31"} property_mock.in_conservation_area = "not_in_conservation_area" property_mock.restricted_measures = False property_mock.insulation_wall_area = 100 property_mock.data = {"county": "Derbyshire"} property_mock.walls = { "is_cob": False, "is_sandstone_or_limestone": False, "is_cavity_wall": False } return property_mock @pytest.fixture def wall_recommendations_instance(self, property_mock): wall_recommendations_instance = WallRecommendations( property_mock, materials=materials ) return wall_recommendations_instance def test_ewi_valid_in_conservation_area(self, wall_recommendations_instance): wall_recommendations_instance.property.in_conservation_area = "in_conversation_area" wall_recommendations_instance.property.restricted_measures = True assert wall_recommendations_instance.ewi_valid() is False def test_ewi_valid_is_flat(self, wall_recommendations_instance): wall_recommendations_instance.property.data = {"property-type": "flat"} assert wall_recommendations_instance.ewi_valid() is False def test_ewi_valid_not_in_conservation_area_and_not_flat(self, wall_recommendations_instance): wall_recommendations_instance.property.in_conservation_area = "not_in_conversation_area" wall_recommendations_instance.property.restricted_measures = False wall_recommendations_instance.property.data = {"property-type": "house"} assert wall_recommendations_instance.ewi_valid() is True class TestCavityWallRecommensations: def test_fill_empty_cavity(self): epc_record = EPCRecord() epc_record.prepared_epc = {"county": "Derbyshire", "walls-energy-eff": "Very Poor"} input_property = Property(id=1, postcode="F4k3", address="123 fake street", epc_record=epc_record) input_property.walls = { 'original_description': 'Cavity wall, as built, no insulation (assumed)', 'clean_description': 'Cavity wall, as built, no insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': True, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, 'is_as_built': True, 'is_cob': False, 'is_assumed': True, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } input_property.age_band = "C" input_property.insulation_wall_area = 50 input_property.construction_age_band = "England and Wales: 1930-1949" recommender = WallRecommendations( property_instance=input_property, materials=materials ) assert not recommender.recommendations recommender.recommend() assert recommender.recommendations assert recommender.estimated_u_value == 1.5 assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.35) assert np.isclose(recommender.recommendations[0]["total"], 710.5) def test_fill_partial_filled_cavity(self): epc_record = EPCRecord() epc_record.prepared_epc = {"county": "County Durham", "walls-energy-eff": "Poor"} input_property = Property(id=1, postcode="F4k3", address="123 fake street", epc_record=epc_record) input_property.walls = { 'original_description': 'Cavity wall, as built, partial insulation (assumed)', 'clean_description': 'Cavity wall, as built, partial insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': True, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, 'is_as_built': True, 'is_cob': False, 'is_assumed': True, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'insulation_thickness': 'below average', 'external_insulation': False, 'internal_insulation': False } input_property.age_band = "C" input_property.insulation_wall_area = 50 input_property.construction_age_band = "England and Wales: 1930-1949" recommender = WallRecommendations( property_instance=input_property, materials=materials ) assert not recommender.recommendations recommender.recommend() assert recommender.recommendations assert recommender.estimated_u_value == 1.3 assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.41) assert np.isclose(recommender.recommendations[0]["total"], 710.5) def test_system_built_wall(self): epc_record = EPCRecord() epc_record.prepared_epc = { "property-type": "House", "county": "Derbyshire", "built-form": "Detached", "walls-energy-eff": "Very Poor" } input_property2 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record) input_property2.walls = { 'original_description': 'System built, as built, no insulation (assumed)', 'clean_description': 'System built, as built, no insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': True, 'is_timber_frame': False, 'is_granite_or_whinstone': False, 'is_as_built': True, 'is_cob': False, 'is_assumed': True, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } input_property2.age_band = "F" input_property2.insulation_wall_area = 120 input_property2.restricted_measures = False input_property2.construction_age_band = "England and Wales: 1976-1982" assert input_property2.walls["is_system_built"] recommender2 = WallRecommendations( property_instance=input_property2, materials=materials ) assert not recommender2.recommendations recommender2.recommend() assert recommender2.recommendations assert len(recommender2.recommendations) == 2 assert recommender2.estimated_u_value == 1 assert np.isclose(recommender2.recommendations[0]["new_u_value"], 0.21) assert np.isclose(recommender2.recommendations[0]["total"], 35802.0) assert recommender2.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" assert recommender2.recommendations[0]["parts"][0]["depth"] == 150 assert np.isclose(recommender2.recommendations[1]["new_u_value"], 0.26) assert np.isclose(recommender2.recommendations[1]["total"], 29376) assert recommender2.recommendations[1]["parts"][0]["type"] == "internal_wall_insulation" assert recommender2.recommendations[1]["parts"][0]["depth"] == 95 def test_timber_frame_wall(self): epc_record = EPCRecord() epc_record.prepared_epc = { "property-type": "House", "county": "Derbyshire", "built-form": "Semi-Detached", "walls-energy-eff": "Very Poor" } input_property3 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record) input_property3.walls = { 'original_description': 'Timber frame, as built, no insulation (assumed)', 'clean_description': 'Timber frame, as built, no insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': False, 'is_timber_frame': True, 'is_granite_or_whinstone': False, 'is_as_built': True, 'is_cob': False, 'is_assumed': True, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } input_property3.age_band = "B" input_property3.insulation_wall_area = 99 input_property3.restricted_measures = False input_property3.construction_age_band = "England and Wales: 1950-1966" assert input_property3.walls["is_timber_frame"] recommender3 = WallRecommendations( property_instance=input_property3, materials=materials ) assert not recommender3.recommendations recommender3.recommend() assert recommender3.recommendations assert len(recommender3.recommendations) == 2 assert recommender3.estimated_u_value == 1.9 assert np.isclose(recommender3.recommendations[0]["new_u_value"], 0.23) assert np.isclose(recommender3.recommendations[0]["total"], 29536.65) assert recommender3.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" assert recommender3.recommendations[0]["parts"][0]["depth"] == 150.0 assert np.isclose(recommender3.recommendations[1]["new_u_value"], 0.29) assert np.isclose(recommender3.recommendations[1]["total"], 24235.2) assert recommender3.recommendations[1]["parts"][0]["type"] == "internal_wall_insulation" assert recommender3.recommendations[1]["parts"][0]["depth"] == 95.0 def test_granite_or_whinstone_wall(self): epc_record = EPCRecord() epc_record.prepared_epc = { "property-type": "Bungalow", "county": "Derbyshire", "built-form": "Detached", "walls-energy-eff": "Very Poor" } input_property4 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record) input_property4.walls = { 'original_description': 'Granite or whinstone, as built, no insulation (assumed)', 'clean_description': 'Granite or whinstone, as built, no insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': True, 'is_as_built': True, 'is_cob': False, 'is_assumed': True, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } input_property4.age_band = "A" input_property4.insulation_wall_area = 223 input_property4.restricted_measures = False input_property4.construction_age_band = "England and Wales: before 1900" assert input_property4.walls["is_granite_or_whinstone"] recommender4 = WallRecommendations( property_instance=input_property4, materials=materials ) assert not recommender4.recommendations recommender4.recommend() assert recommender4.recommendations assert len(recommender4.recommendations) == 2 assert recommender4.estimated_u_value == 2.3 assert np.isclose(recommender4.recommendations[0]["new_u_value"], 0.23) assert np.isclose(recommender4.recommendations[0]["total"], 66532.05) assert recommender4.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" assert recommender4.recommendations[0]["parts"][0]["depth"] == 150 assert np.isclose(recommender4.recommendations[1]["new_u_value"], 0.3) assert np.isclose(recommender4.recommendations[1]["total"], 54590.4) assert recommender4.recommendations[1]["parts"][0]["type"] == "internal_wall_insulation" assert recommender4.recommendations[1]["parts"][0]["depth"] == 95 def test_cob_wall(self): epc_record = EPCRecord() epc_record.prepared_epc = { "property-type": "Bungalow", "county": "Derbyshire", "built-form": "Detached", "walls-energy-eff": "Very Poor" } input_property5 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record) input_property5.walls = { 'original_description': 'Cob, as built', 'clean_description': 'Cob, as built', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, 'is_as_built': False, 'is_cob': True, 'is_assumed': False, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } input_property5.age_band = "E" input_property5.insulation_wall_area = 77 input_property5.restricted_measures = False input_property5.construction_age_band = "England and Wales: 1967-1975" assert input_property5.walls["is_cob"] recommender5 = WallRecommendations( property_instance=input_property5, materials=materials ) assert not recommender5.recommendations recommender5.recommend() # No insulation recommendations for cob walls assert not recommender5.recommendations def test_sandstone_or_limestone_wall(self): epc_record = EPCRecord() epc_record.prepared_epc = { "property-type": "House", "county": "Derbyshire", "built-form": "Mid-Terrace", "walls-energy-eff": "Very Poor" } input_property6 = Property(id=1, postcode="F4k3 6", address="623 fake street", epc_record=epc_record) input_property6.walls = { 'original_description': 'Sandstone or limestone, as built, no insulation (assumed)', 'clean_description': 'Sandstone or limestone, as built, no insulation', 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, 'is_as_built': False, 'is_cob': False, 'is_assumed': False, 'is_sandstone_or_limestone': True, 'is_park_home': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } input_property6.age_band = "F" input_property6.insulation_wall_area = 350 input_property6.restricted_measures = False input_property6.construction_age_band = "England and Wales: 1976-1982" assert input_property6.walls["is_sandstone_or_limestone"] recommender6 = WallRecommendations( property_instance=input_property6, materials=materials ) assert not recommender6.recommendations recommender6.recommend() # For sandstone walls, we only recommend internal wall insulation assert recommender6.recommendations assert len(recommender6.recommendations) == 1 assert recommender6.estimated_u_value == 1 assert np.isclose(recommender6.recommendations[0]["new_u_value"], 0.26) assert np.isclose(recommender6.recommendations[0]["total"], 85680.0) assert recommender6.recommendations[0]["parts"][0]["type"] == "internal_wall_insulation" assert recommender6.recommendations[0]["parts"][0]["depth"] == 95