diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml new file mode 100644 index 00000000..f1ed5b58 --- /dev/null +++ b/.github/workflows/integration_tests.yml @@ -0,0 +1,29 @@ +name: Rebaselining Integration Test + +on: + pull_request: + branches: + - main + +jobs: + rebaselining-integration-test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install tox via Makefile + run: | + make setup + + - name: Run only rebaselining integration test + env: + EPC_AUTH_TOKEN: ${{ secrets.DEV_EPC_AUTH_TOKEN }} + run: | + pytest backend/tests/test_rebaselining_pipeline.py -k test_rebaselining_pipeline_with_real_data + diff --git a/backend/tests/test_rebaselining_pipeline.py b/backend/tests/test_rebaselining_pipeline.py index 65170252..76f98cc9 100644 --- a/backend/tests/test_rebaselining_pipeline.py +++ b/backend/tests/test_rebaselining_pipeline.py @@ -1,138 +1,89 @@ -# --- Integration Test with Real Data --- import os +import pickle +import pandas as pd def load_sample_certificates(): - """Load sample_certificates.csv as a list of dicts.""" - # Always look for the file relative to the project root (cwd) - import pandas as pd + """Load sample_certificates.csv as a DataFrame with normalized columns.""" csv_path = os.path.join(os.getcwd(), 'backend', 'tests', 'test_data', 'sample_certificates.csv') - if os.path.exists(csv_path): - df = pd.read_csv(csv_path) - # Normalize columns: lowercase, replace underscores with hyphens, strip spaces - df.columns = [c.strip().lower().replace('_', '-') for c in df.columns] - df = df[~pd.isnull(df["uprn"])] - df = df[~pd.isnull(df["low-energy-fixed-light-count"])] - df = df.fillna("") - for col in ["uprn", "low-energy-fixed-light-count"]: - df[col] = df[col].astype(int).astype(str) - df = df.astype(str) - return df - raise FileNotFoundError( - f"sample_certificates.csv not found at {csv_path}. Make sure it exists relative to the project root.") + if not os.path.exists(csv_path): + raise FileNotFoundError( + f"sample_certificates.csv not found at {csv_path}. Make sure it exists relative to the project root.") + df = pd.read_csv(csv_path) + df.columns = [c.strip().lower().replace('_', '-') for c in df.columns] + df = df[~pd.isnull(df["uprn"])] + df = df[~pd.isnull(df["low-energy-fixed-light-count"])] + df = df.fillna("") + for col in ["uprn", "low-energy-fixed-light-count"]: + df[col] = df[col].astype(int).astype(str) + df = df.astype(str) + return df def make_property_from_row(row, cleaning_data): - # Convert row to dict with correct keys (hyphens, lower case) - # Convert all keys to snake_case (replace hyphens with underscores, lower case) from etl.epc.Record import EPCRecord - + from backend.Property import Property row_dict = row.to_dict() - - epc_records = { - "original_epc": row_dict.copy(), - "full_sap_epc": row_dict.copy(), - "old_data": [] - } - + from etl.epc.Record import InputEpcRecords + epc_records = InputEpcRecords( + original_epc=row_dict.copy(), + full_sap_epc=row_dict.copy(), + old_data=[] + ) epc_record = EPCRecord( epc_records=epc_records, run_mode="newdata", cleaning_data=cleaning_data ) - # Extract required fields for Property constructor - # Use lmk-key as id if present, else fallback to uprn or index id_val = row.get('uprn') postcode_val = row.get('postcode') address_val = row.get('address') or row.get('address1') - from backend.Property import Property - property_obj = Property( + return Property( id=id_val, postcode=postcode_val, address=address_val, epc_record=epc_record, uprn=int(row['uprn']) if 'uprn' in row and not pd.isnull(row['uprn']) else None, - # Provide defaults for other optional args as needed ) - return property_obj def load_cleaned(): - import pickle with open("recommendations/tests/test_data/cleaned.pkl", "rb") as f: - df = pickle.load(f) - - return df + return pickle.load(f) def load_cleaning_data(): - import pickle with open("recommendations/tests/test_data/cleaning_data.pkl", "rb") as f: - df = pickle.load(f) - - return df + return pickle.load(f) -def test_rebaselining_pipeline_with_real_data(mock_model_api): +def test_rebaselining_pipeline_with_real_data(): import pandas as pd from datetime import datetime from backend.ml_models.api import ModelApi from backend.app.utils import sap_to_epc + from backend.app.config import get_prediction_buckets df = load_sample_certificates() - cleaning_data = load_cleaning_data() input_properties = [make_property_from_row(row, cleaning_data=cleaning_data) for _, row in df.iterrows()] cleaned = load_cleaned() rebaselining_scoring_data = [] - # List of required columns for the model pipeline - required_columns = [ - 'secondheat_description_ending', - 'windows_description_ending', - 'low_energy_lighting_ending', - 'solar_water_heating_flag_ending', - 'photo_supply_ending', - 'floor_height_ending', - 'floor_energy_eff_ending', - 'sheating_energy_eff_ending', - 'lighting_energy_eff_ending', - 'is_post_sap10_ending', - 'secondheat_description_starting', - 'windows_description_starting', - 'low_energy_lighting_starting', - 'solar_water_heating_flag_starting', - 'photo_supply_starting', - 'floor_height_starting', - 'floor_energy_eff_starting', - 'sheating_energy_eff_starting', - 'lighting_energy_eff_starting', - 'is_post_sap10_starting', - 'fixed_lighting_outlets_count', - ] for p in input_properties: - # Already rebaseline for tests p.create_base_difference_epc_record(cleaned_lookup=cleaned) scoring_data = p.base_difference_record.df.copy() rebaselining_scoring_data.append(scoring_data) if not rebaselining_scoring_data: assert False, "No properties required rebaselining in the sample data." rebaselining_scoring_data = pd.concat(rebaselining_scoring_data) - # Set is_post_sap10_starting after concatenation rebaselining_scoring_data["is_post_sap10_starting"] = False - # Instantiate ModelApi as in engine.py - portfolio_id = "test-portfolio" - timestamp = datetime.now().isoformat() - from backend.app.config import get_prediction_buckets - prediction_buckets = get_prediction_buckets() model_api = ModelApi( - portfolio_id=portfolio_id, - timestamp=timestamp, - prediction_buckets=prediction_buckets, + portfolio_id="test-portfolio", + timestamp=datetime.now().isoformat(), + prediction_buckets=get_prediction_buckets(), max_retries=1 ) - - # Use the real model_api and bucket bucket = "retrofit-data-dev" model_prefixes = model_api.BASELINE_MODEL_PREFIXES rebaselining_response = model_api.predict_all( @@ -149,7 +100,6 @@ def test_rebaselining_pipeline_with_real_data(mock_model_api): "retrofit_heat_baseline_predictions", ] predictions_by_model_and_uprn = {} - # Build a mapping from uprn to original values for easy lookup uprn_to_originals = {} for p in input_properties: if p.uprn is not None and hasattr(p, 'epc_record') and hasattr(p.epc_record, 'original_epc'): @@ -170,33 +120,19 @@ def test_rebaselining_pipeline_with_real_data(mock_model_api): (df[actual_col] != 0) ) if valid.sum() == 0: - return None # No valid rows - mape = ( - (df.loc[valid, pred_col] - df.loc[valid, actual_col]).abs() - / df.loc[valid, actual_col].abs() - ).mean() * 100 + return None + mape = ((df.loc[valid, pred_col] - df.loc[valid, actual_col]).abs() / df.loc[ + valid, actual_col].abs()).mean() * 100 return mape mape_results = {} for model in model_names: df_pred = rebaselining_response[model] - # Map originals - df_pred['original_sap'] = df_pred['uprn'].map( - lambda u: uprn_to_originals.get(int(u), {}).get('original_sap') - ) + df_pred['original_sap'] = df_pred['uprn'].map(lambda u: uprn_to_originals.get(int(u), {}).get('original_sap')) df_pred['original_carbon'] = df_pred['uprn'].map( - lambda u: uprn_to_originals.get(int(u), {}).get('original_carbon') - ) - df_pred['original_heat'] = df_pred['uprn'].map( - lambda u: uprn_to_originals.get(int(u), {}).get('original_heat') - ) - # Save predictions - predictions_by_model_and_uprn[model] = dict( - zip(df_pred["uprn"].astype(int), df_pred["predictions"]) - ) - # For debugging - # df_pred.to_csv(f"rebaselining_{model}.csv", index=False) - # Select correct actual column + lambda u: uprn_to_originals.get(int(u), {}).get('original_carbon')) + df_pred['original_heat'] = df_pred['uprn'].map(lambda u: uprn_to_originals.get(int(u), {}).get('original_heat')) + predictions_by_model_and_uprn[model] = dict(zip(df_pred["uprn"].astype(int), df_pred["predictions"])) if model == "retrofit_sap_baseline_predictions": actual_col = "original_sap" metric_name = "sap" @@ -214,14 +150,11 @@ def test_rebaselining_pipeline_with_real_data(mock_model_api): print(f"MAPE ({metric_name}): {mape:.2f}%") else: print(f"MAPE ({metric_name}): No valid data") - # --- ASSERT PERFORMANCE --- - # each model has varying impacts under SAP 10. We see a small SAP movement - # but much higher carbon and heat changes. We expect this. E.g. we see - # cases where EPC C properties had 0.2 carbon which should be higher + MAX_MAPE = { - "sap": 4.6, # % - "carbon": 21.0, # % - "heat": 16.0, # % + "sap": 4.6, + "carbon": 21.0, + "heat": 16.0, } for metric, mape in mape_results.items(): max_allowed = MAX_MAPE.get(metric, 100.0) @@ -240,149 +173,6 @@ def test_rebaselining_pipeline_with_real_data(mock_model_api): new_carbon=new_carbon, new_heat_demand=new_heat_demand, ) - # Assert that EPC records were updated for the right properties - updated = 0 - for p in input_properties: - if p.epc_record.has_been_remodelled: - updated += 1 + updated = sum(1 for p in input_properties if getattr(p.epc_record, 'has_been_remodelled', False)) assert updated > 0, "No EPC records were updated." - - # Optionally: Add accuracy/performance checks here if you have ground truth - # For now, just print a summary print(f"Updated {updated} EPC records with new predictions.") - - -import pytest -from unittest.mock import MagicMock, patch -import pandas as pd - - -# Import the relevant classes and functions -# from backend.Property import Property # Uncomment and adjust as needed -# from etl.epc.Record import EpcRecord # Uncomment and adjust as needed -# from backend.engine.engine import sap_to_epc # Uncomment and adjust as needed - -# --- Fixtures --- -@pytest.fixture -def sample_input_properties(): - """Return a list of mock property objects with required attributes for rebaselining.""" - - class MockEpcRecord: - def __init__(self): - self.landlord_differences = {'wall_insulation': 'yes'} - self.current_energy_efficiency = 60 - self.lodgement_date = '2020-01-01' - self.original_epc = {'wall-insulation': 'no'} - - def insert_new_performance_values(self, new_sap, new_epc, new_carbon, new_heat_demand): - self.new_sap = new_sap - self.new_epc = new_epc - self.new_carbon = new_carbon - self.new_heat_demand = new_heat_demand - - class MockProperty: - def __init__(self, uprn, expired=False, estimated=False): - self.uprn = uprn - self.epc_is_expired = expired - self.epc_is_estimated = estimated - self.epc_record = MockEpcRecord() - - def create_base_difference_epc_record(self, cleaned_lookup=None): - # Simulate creation of base_difference_record - self.base_difference_record = MagicMock() - self.base_difference_record.df = pd.DataFrame({ - 'uprn': [self.uprn], - 'feature1': [1], - 'feature2': [2], - }) - - return [MockProperty(1001, expired=True), MockProperty(1002, estimated=True), MockProperty(1003)] - - -@pytest.fixture -def mock_model_api(): - mock = MagicMock() - # Simulate model_api.predict_all returning a dict of DataFrames - mock.predict_all.return_value = { - 'retrofit_sap_baseline_predictions': pd.DataFrame({'uprn': [1001, 1002], 'predictions': [70, 65]}), - 'retrofit_carbon_baseline_predictions': pd.DataFrame({'uprn': [1001, 1002], 'predictions': [1.2, 1.1]}), - 'retrofit_heat_baseline_predictions': pd.DataFrame({'uprn': [1001, 1002], 'predictions': [10000, 9500]}), - } - mock.BASELINE_MODEL_PREFIXES = ['retrofit_sap_baseline_predictions', 'retrofit_carbon_baseline_predictions', - 'retrofit_heat_baseline_predictions'] - return mock - - -# --- Integration Test --- -def test_rebaselining_pipeline(sample_input_properties, mock_model_api): - # Simulate the rebaselining process - input_properties = sample_input_properties - cleaned = None # Placeholder for cleaned_lookup - rebaselining_scoring_data = [] - for p in input_properties: - needs_rebaselining = True # Force rebaselining for all properties - if needs_rebaselining: - p.create_base_difference_epc_record(cleaned_lookup=cleaned) - scoring_data = p.base_difference_record.df.copy() - rebaselining_scoring_data.append(scoring_data) - rebaselining_scoring_data = pd.concat(rebaselining_scoring_data) - if not rebaselining_scoring_data.empty: - rebaselining_scoring_data["is_post_sap10_starting"] = True - # Patch sap_to_epc if needed - with patch('backend.engine.engine.sap_to_epc', lambda x: 'C'): - rebaselining_response = mock_model_api.predict_all( - df=rebaselining_scoring_data, - bucket='dummy-bucket', - model_prefixes=mock_model_api.BASELINE_MODEL_PREFIXES, - extract_ids=False, - extract_uprn=True - ) - input_properties_by_uprn = {int(p.uprn): p for p in input_properties if p.uprn is not None} - model_names = [ - "retrofit_sap_baseline_predictions", - "retrofit_carbon_baseline_predictions", - "retrofit_heat_baseline_predictions", - ] - predictions_by_model_and_uprn = {} - for model in model_names: - df = rebaselining_response[model] - predictions_by_model_and_uprn[model] = dict(zip(df["uprn"].astype(int), df["predictions"])) - for uprn_int in rebaselining_scoring_data["uprn"].unique().astype(int): - property_instance = input_properties_by_uprn.get(uprn_int) - if property_instance is None: - continue - new_sap = predictions_by_model_and_uprn["retrofit_sap_baseline_predictions"].get(uprn_int) - new_carbon = predictions_by_model_and_uprn["retrofit_carbon_baseline_predictions"].get(uprn_int) - new_heat_demand = predictions_by_model_and_uprn["retrofit_heat_baseline_predictions"].get(uprn_int) - property_instance.epc_record.insert_new_performance_values( - new_sap=new_sap, - new_epc='C', - new_carbon=new_carbon, - new_heat_demand=new_heat_demand, - ) - # Assert that EPC records were updated for the right properties - # Only properties that were marked as expired or estimated should have new_sap set - for p in input_properties: - needs_rebaselining = p.epc_is_expired or p.epc_is_estimated or ( - len(getattr(p.epc_record, 'landlord_differences', {})) > 0) - if needs_rebaselining: - assert hasattr(p.epc_record, 'new_sap') - else: - assert not hasattr(p.epc_record, 'new_sap') - - -# --- Unit Test Example --- -def test_insert_new_performance_values(): - class DummyEpcRecord: - def insert_new_performance_values(self, new_sap, new_epc, new_carbon, new_heat_demand): - self.new_sap = new_sap - self.new_epc = new_epc - self.new_carbon = new_carbon - self.new_heat_demand = new_heat_demand - - record = DummyEpcRecord() - record.insert_new_performance_values(80, 'B', 1.0, 9000) - assert record.new_sap == 80 - assert record.new_epc == 'B' - assert record.new_carbon == 1.0 - assert record.new_heat_demand == 9000 diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index a5192363..a696e878 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -144,7 +144,8 @@ class WallRecommendations(Definitions): """ Checks if the wall is of a suitable type for internal/external wall insulation """ - if self.property.walls["is_cavity_wall"] or self.property.walls["is_cob"]: + if self.property.walls["is_cavity_wall"] or self.property.walls["is_cob"] or self.property.walls[ + "is_granite_or_whinstone"] or self.property.walls["is_sandstone_or_limestone"]: return False return True diff --git a/recommendations/rdsap_tables.py b/recommendations/rdsap_tables.py index 558b0da4..0df7474c 100644 --- a/recommendations/rdsap_tables.py +++ b/recommendations/rdsap_tables.py @@ -818,6 +818,7 @@ epc_wall_description_map = { ############################ # Cob wall mappings ############################ + "Cob, as built, no insulation": "Cob as built", "Cob, as built": "Cob as built", "Cob, with external insulation": "Cob with 100 mm external or internal insulation", "Cob, with internal insulation": "Cob with 100 mm external or internal insulation", diff --git a/recommendations/tests/test_costs.py b/recommendations/tests/test_costs.py index 10a63554..d52f1e1d 100644 --- a/recommendations/tests/test_costs.py +++ b/recommendations/tests/test_costs.py @@ -183,9 +183,8 @@ class TestCosts: def test_flat_roof_insulation(self): mock_property = Mock() - mock_property.data = { - "county": "Northamptonshire" - } + mock_property.epc_record = Mock() + mock_property.epc_record.county = "Northamptonshire" costs = Costs(mock_property) flat_roof_material = { diff --git a/recommendations/tests/test_data/input_properties.pkl b/recommendations/tests/test_data/input_properties.pkl deleted file mode 100644 index d21b89c2..00000000 Binary files a/recommendations/tests/test_data/input_properties.pkl and /dev/null differ diff --git a/recommendations/tests/test_fireplace_recommendations.py b/recommendations/tests/test_fireplace_recommendations.py index 72e2ba8d..47b47354 100644 --- a/recommendations/tests/test_fireplace_recommendations.py +++ b/recommendations/tests/test_fireplace_recommendations.py @@ -24,52 +24,33 @@ class TestFirepaceRecommendations: def test_no_fireplaces(self, fireplace_materials): epc_record = EPCRecord() - epc_record.prepared_epc = { - "number-open-fireplaces": 0, - } - + epc_record.number_open_fireplaces = 0 property_instance = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) - recommender = FireplaceRecommendations(property_instance=property_instance, materials=fireplace_materials) - assert recommender.recommendation is None - recommender.recommend() - assert recommender.recommendation is None def test_one_fireplace(self, fireplace_materials): epc_record = EPCRecord() - epc_record.prepared_epc = { - "number-open-fireplaces": 1, - } + epc_record.number_open_fireplaces = 1 property_instance = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) property_instance.already_installed = [] - recommender = FireplaceRecommendations(property_instance=property_instance, materials=fireplace_materials) - assert recommender.recommendation is None - recommender.recommend() - assert recommender.recommendation assert recommender.recommendation[0]["type"] == "sealing_open_fireplace" assert recommender.recommendation[0]["total"] == 185 def test_multiple_fireplaces(self, fireplace_materials): epc_record = EPCRecord() - epc_record.prepared_epc = { - "number-open-fireplaces": 3, - } + epc_record.number_open_fireplaces = 3 property_instance = Property(id=0, address="fake", postcode="fake", epc_record=epc_record) property_instance.already_installed = [] - recommender = FireplaceRecommendations(property_instance=property_instance, materials=fireplace_materials) - assert recommender.recommendation is None - recommender.recommend() - assert recommender.recommendation assert recommender.recommendation[0]["type"] == "sealing_open_fireplace" assert recommender.recommendation[0]["total"] == 185 * 3 diff --git a/recommendations/tests/test_floor_recommendations.py b/recommendations/tests/test_floor_recommendations.py index e24312fe..e2b12855 100644 --- a/recommendations/tests/test_floor_recommendations.py +++ b/recommendations/tests/test_floor_recommendations.py @@ -19,29 +19,36 @@ from etl.epc.Record import EPCRecord class TestFloorRecommendations: - @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) - - def test_init(self, input_properties): - input_properties[0].insulation_floor_area = 50 - input_properties[0].insulation_wall_area = 90 + def test_init(self): + p = Mock() + p.epc_record = Mock() + p.epc_record.county = "Greater London" + p.epc_record.local_authority_label = "London" + p.epc_record.insulation_floor_area = 50 + p.epc_record.insulation_wall_area = 90 + p.insulation_floor_area = 50 + p.insulation_wall_area = 90 + p.floor = {"another_property_below": False} obj = FloorRecommendations( - property_instance=input_properties[0], + property_instance=p, materials=materials ) assert obj assert obj.property - def test_other_premises_below(self, input_properties): - input_properties[0].insulation_floor_area = 100 - input_properties[0].insulation_wall_area = 999 - input_properties[0].number_of_floors = 1 + def test_other_premises_below(self): + p = Mock() + p.epc_record = Mock() + p.epc_record.county = "Greater London" + p.epc_record.local_authority_label = "London" + p.epc_record.insulation_floor_area = 100 + p.epc_record.insulation_wall_area = 999 + p.insulation_floor_area = 100 + p.insulation_wall_area = 999 + p.number_of_floors = 1 + p.floor = {"another_property_below": True, "thermal_transmittance": None, "insulation_thickness": None} recommender = FloorRecommendations( - property_instance=input_properties[0], + property_instance=p, materials=materials ) recommender.recommend() @@ -49,25 +56,41 @@ class TestFloorRecommendations: assert not recommender.recommendations - def test_suspended_no_insulation(self, input_properties): + def test_suspended_no_insulation(self): """ For a suspended floor without insulation, we use the rdsap methogology to estimate a U-value for the floor :return: """ - - input_properties[2].insulation_floor_area = 50 - input_properties[2].insulation_wall_area = 50 - input_properties[2].walls["is_park_home"] = False - input_properties[2].age_band = "A" - input_properties[2].perimeter = 20 - input_properties[2].wall_type = "solid brick" - input_properties[2].floor_type = "suspended" - input_properties[2].number_of_floors = 1 - input_properties[2].floor_level = 0 - input_properties[2].already_installed = [] - input_properties[2].non_invasive_recommendations = {} - - recommender = FloorRecommendations(property_instance=input_properties[2], materials=materials) + p = Mock() + p.epc_record = Mock() + p.epc_record.county = "Greater London" + p.epc_record.local_authority_label = "London" + p.epc_record.insulation_floor_area = 50 + p.epc_record.insulation_wall_area = 50 + p.insulation_floor_area = 50 + p.insulation_wall_area = 50 + p.walls = {"is_park_home": False} + p.age_band = "A" + p.perimeter = 20 + p.wall_type = "solid brick" + p.floor_type = "suspended" + p.number_of_floors = 1 + p.floor_level = 0 + p.already_installed = [] + p.non_invasive_recommendations = {} + p.floor = { + "is_suspended": True, + "is_solid": False, + "another_property_below": False, + "thermal_transmittance": None, + "insulation_thickness": None, + "thermal_transmittance_unit": None, + "is_assumed": False, + "is_to_unheated_space": False, + "is_to_external_air": False, + } + p.full_sap_epc = {} + recommender = FloorRecommendations(property_instance=p, materials=materials) assert recommender.estimated_u_value is None recommender.recommend() assert recommender.property.floor["is_suspended"] @@ -82,18 +105,33 @@ class TestFloorRecommendations: assert recommender.recommendations[0]["total"] == 4687.5 assert recommender.recommendations[0]["new_u_value"] == 0.21 - def test_uvalue_0_12(self, input_properties): + def test_uvalue_0_12(self): """ This is a home that doesn't have a property below but it's highly performant already and therefore does not need floor insulation :return: """ - input_properties[3].insulation_floor_area = 100 - input_properties[3].insulation_wall_area = 100 - input_properties[3].number_of_floors = 1 - input_properties[3].floor_level = 0 - - recommender = FloorRecommendations(property_instance=input_properties[3], materials=materials) + p = Mock() + p.epc_record = Mock() + p.epc_record.county = "Greater London" + p.epc_record.local_authority_label = "London" + p.epc_record.insulation_floor_area = 100 + p.epc_record.insulation_wall_area = 100 + p.insulation_floor_area = 100 + p.insulation_wall_area = 100 + p.number_of_floors = 1 + p.floor_level = 0 + p.floor = { + "is_suspended": False, + "is_solid": False, + "another_property_below": False, + "thermal_transmittance": 0.12, + "insulation_thickness": None, + "is_to_unheated_space": False, + "is_to_external_air": False, + } + p.full_sap_epc = {} + recommender = FloorRecommendations(property_instance=p, materials=materials) assert recommender.estimated_u_value is None recommender.recommend() assert not recommender.property.floor["is_suspended"] @@ -101,26 +139,41 @@ class TestFloorRecommendations: assert recommender.estimated_u_value is None assert not recommender.recommendations - def test_solid_no_insulation(self, input_properties): + def test_solid_no_insulation(self): """ :return: """ - - input_properties[4].insulation_floor_area = 100 - input_properties[4].insulation_wall_area = 100 - input_properties[4].walls["is_park_home"] = False - input_properties[4].age_band = "B" - input_properties[4].perimeter = 50 - input_properties[4].wall_type = "solid brick" - input_properties[4].floor_type = "solid" - input_properties[4].number_of_floors = 1 - input_properties[4].floor_level = 0 - input_properties[4].already_installed = [] - input_properties[4].non_invasive_recommendations = {} - - # In this case, we have no county, so in this case, it should yse the local-authority-label if possible - input_properties[4].data["county"] = "" - recommender = FloorRecommendations(property_instance=input_properties[4], materials=materials) + p = Mock() + p.epc_record = Mock() + p.epc_record.county = "" + p.epc_record.local_authority_label = "London" + p.epc_record.insulation_floor_area = 100 + p.epc_record.insulation_wall_area = 100 + p.insulation_floor_area = 100 + p.insulation_wall_area = 100 + p.walls = {"is_park_home": False} + p.age_band = "B" + p.perimeter = 50 + p.wall_type = "solid brick" + p.floor_type = "solid" + p.number_of_floors = 1 + p.floor_level = 0 + p.already_installed = [] + p.non_invasive_recommendations = {} + p.data = {"county": ""} + p.floor = { + "is_suspended": False, + "is_solid": True, + "another_property_below": False, + "thermal_transmittance": None, + "insulation_thickness": None, + "is_to_unheated_space": False, + "is_to_external_air": False, + "thermal_transmittance_unit": None, + "is_assumed": True, + } + p.full_sap_epc = {} + recommender = FloorRecommendations(property_instance=p, materials=materials) assert recommender.estimated_u_value is None recommender.recommend() assert not recommender.property.floor["is_suspended"] @@ -148,16 +201,27 @@ class TestFloorRecommendations: 'floor-description': 'Solid, insulated' } - def test_another_dwelling_below(self, input_properties): + def test_another_dwelling_below(self): """ This is another description we see when there is a property below """ - - input_properties[6].insulation_floor_area = 100 - input_properties[6].insulation_wall_area = 1 - - input_properties[6].number_of_floors = 1 - recommender = FloorRecommendations(property_instance=input_properties[6], materials=materials) + p = Mock() + p.epc_record = Mock() + p.epc_record.county = "Greater London" + p.epc_record.local_authority_label = "London" + p.epc_record.insulation_floor_area = 100 + p.epc_record.insulation_wall_area = 1 + p.insulation_floor_area = 100 + p.insulation_wall_area = 1 + p.number_of_floors = 1 + p.floor = { + "is_suspended": False, + "is_solid": False, + "another_property_below": True, + "thermal_transmittance": None, + "insulation_thickness": None, + } + recommender = FloorRecommendations(property_instance=p, materials=materials) assert recommender.estimated_u_value is None recommender.recommend() assert not recommender.property.floor["is_suspended"] @@ -167,7 +231,9 @@ class TestFloorRecommendations: def test_exposed_floor_no_insulation(self): epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Greater London", "floor-level": 0, "property-type": "House"} + epc_record.county = "Greater London" + epc_record.floor_level = "0" + epc_record.property_type = "House" epc_record.full_sap_epc = {} input_property = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record) @@ -199,7 +265,9 @@ class TestFloorRecommendations: # Now with an older age band epc_record2 = EPCRecord() - epc_record2.prepared_epc = {"county": "Greater London", "floor-level": 0, "property-type": "House"} + epc_record2.county = "Greater London" + epc_record2.floor_level = "0" + epc_record2.property_type = "House" epc_record2.full_sap_epc = {} input_property2 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record2) @@ -233,7 +301,9 @@ class TestFloorRecommendations: def test_exposed_floor_below_average_insulated(self): epc_record3 = EPCRecord() - epc_record3.prepared_epc = {"county": "Greater London", "floor-level": 0, "property-type": "House"} + epc_record3.county = "Greater London" + epc_record3.floor_level = "0" + epc_record3.property_type = "House" epc_record3.full_sap_epc = {} input_property3 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record3) input_property3.floor = { @@ -269,7 +339,9 @@ class TestFloorRecommendations: # With average insulation, no recommendations epc_record4 = EPCRecord() - epc_record4.prepared_epc = {"county": "Greater London", "floor-level": 0, "property-type": "House"} + epc_record4.county = "Greater London" + epc_record4.floor_level = "0" + epc_record4.property_type = "House" epc_record4.full_sap_epc = {} input_property4 = Property(id=1, postcode="F4k3 2", address="223 fake street", epc_record=epc_record4) input_property4.floor = { diff --git a/recommendations/tests/test_lighting_recommendations.py b/recommendations/tests/test_lighting_recommendations.py index aeaffdb4..d430d993 100644 --- a/recommendations/tests/test_lighting_recommendations.py +++ b/recommendations/tests/test_lighting_recommendations.py @@ -10,7 +10,7 @@ class TestLightingRecommendations: def test_init_invalid_materials(self): epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Greater London Authority"} + epc_record.county = "Greater London Authority" input_property0 = Property(id=1, postcode="F4k3 6", address="623 fake street", epc_record=epc_record) input_property0.lighting = {"low_energy_proportion": 0} input_property0.already_installed = [] @@ -21,7 +21,7 @@ class TestLightingRecommendations: def test_recommend_no_action_needed(self): # Case where no recommendation is needed epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Greater London Authority"} + epc_record.county = "Greater London Authority" input_property1 = Property(id=1, postcode="F4k3 6", address="623 fake street", epc_record=epc_record) input_property1.lighting = {"low_energy_proportion": 100} input_property1.already_installed = [] @@ -33,7 +33,7 @@ class TestLightingRecommendations: def test_recommend_action_needed(self): # Case where recommendation is needed epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "Greater London Authority"} + epc_record.county = "Greater London Authority" input_property1 = Property(id=1, postcode="F4k3 6", address="623 fake street", epc_record=epc_record) input_property1.lighting = {"low_energy_proportion": 0.80} input_property1.number_lighting_outlets = 20 diff --git a/recommendations/tests/test_solar_pv_recommendations.py b/recommendations/tests/test_solar_pv_recommendations.py index f93cc644..38dc8cb8 100644 --- a/recommendations/tests/test_solar_pv_recommendations.py +++ b/recommendations/tests/test_solar_pv_recommendations.py @@ -12,9 +12,9 @@ class TestSolarPvRecommendations: def property_instance_invalid_type(self): # Setup the property_instance with an invalid property type epc_record = EPCRecord() - epc_record.prepared_epc = { - "property-type": "InvalidType", "county": "Broxbourne", "photo-supply": None - } + epc_record.property_type = "InvalidType" + epc_record.county = "Broxbourne" + epc_record.photo_supply = None property_instance_invalid_type = Property(id=1, address="", postcode="", epc_record=epc_record) property_instance_invalid_type.roof = {"is_flat": False, "is_pitched": False, "is_roof_room": False} property_instance_invalid_type.already_installed = [] @@ -24,9 +24,9 @@ class TestSolarPvRecommendations: def property_instance_invalid_roof(self): # Setup the property_instance with invalid roof type epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Huntingdonshire", "property-type": "House", "photo-supply": None - } + epc_record.county = "Huntingdonshire" + epc_record.property_type = "House" + epc_record.photo_supply = None property_instance_invalid_roof = Property(id=1, address="", postcode="", epc_record=epc_record) property_instance_invalid_roof.roof = { "is_flat": False, "is_pitched": False, "is_roof_room": False, "thermal_transmittance": None @@ -36,10 +36,11 @@ class TestSolarPvRecommendations: @pytest.fixture def property_instance_has_solar_pv(self): - # Setup the property_instance without existing solar pv + # Setup the property_instance with existing solar pv epc_record = EPCRecord() - epc_record.prepared_epc = {"photo-supply": "40", "county": "Huntingdonshire", - "property-type": "House"} + epc_record.photo_supply = 40.0 # Use float, not string + epc_record.county = "Huntingdonshire" + epc_record.property_type = "House" property_instance_has_solar_pv = Property(id=1, address="", postcode="", epc_record=epc_record) property_instance_has_solar_pv.roof = {"is_flat": True, "thermal_transmittance": None} property_instance_has_solar_pv.already_installed = [] @@ -49,7 +50,9 @@ class TestSolarPvRecommendations: def property_instance_valid_all(self): # Setup a valid property_instance that passes all conditions epc_record = EPCRecord() - epc_record.prepared_epc = {"property-type": "House", "photo-supply": None, "county": "Huntingdonshire"} + epc_record.property_type = "House" + epc_record.photo_supply = None + epc_record.county = "Huntingdonshire" property_instance_valid_all = Property(id=1, address="", postcode="", epc_record=epc_record) property_instance_valid_all.roof_area = 40 property_instance_valid_all.number_of_floors = 2 diff --git a/recommendations/tests/test_wall_recommendations.py b/recommendations/tests/test_wall_recommendations.py index c54582ad..42cbb1e8 100644 --- a/recommendations/tests/test_wall_recommendations.py +++ b/recommendations/tests/test_wall_recommendations.py @@ -1,7 +1,6 @@ import pytest 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 @@ -15,9 +14,12 @@ class TestWallRecommendations: 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 + epc_record = EPCRecord() + epc_record.construction_age_band = "1950" + epc_record.county = "Derbyshire" + epc_record.lodgement_date = "2000-01-01" + property_mock.epc_record = epc_record + property_mock.full_sap_epc = {"lodgement-date": "2000-01-01"} mock_wall_rec_instance = WallRecommendations( property_mock, materials=materials @@ -96,6 +98,11 @@ class TestWallRecommendations: This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation """ + epc_record = EPCRecord() + epc_record.county = "Greater London Authority" + epc_record.property_type = "Flat" + epc_record.walls_energy_eff = "Very Poor" + p = Mock( id=2, year_built=1930, @@ -116,7 +123,7 @@ class TestWallRecommendations: 'is_sandstone_or_limestone': False, 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False, 'is_park_home': False }, - data={"county": "Greater London Authority", 'property-type': 'Flat', 'walls-energy-eff': 'Very Poor'} + epc_record=epc_record, ) recommender = WallRecommendations( @@ -150,6 +157,10 @@ class TestWallRecommendations: This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation """ + epc_record = EPCRecord() + epc_record.county = "Greater London Authority" + epc_record.property_type = "Flat" + p = Mock( id=3, year_built=1991, @@ -157,7 +168,6 @@ class TestWallRecommendations: insulation_wall_area=100, already_installed=[], in_conservation_area="not_in_conservation_area", - data={'county': 'Greater London Authority', 'property-type': 'Flat'}, walls={ 'original_description': 'Solid brick, as built, insulated (assumed)', 'clean_description': 'Solid brick, as built, insulated', @@ -167,8 +177,8 @@ class TestWallRecommendations: 'is_granite_or_whinstone': False, 'is_as_built': True, 'is_cob': False, 'is_assumed': True, 'is_sandstone_or_limestone': False, 'insulation_thickness': 'average', 'external_insulation': False, 'internal_insulation': False - } - + }, + epc_record=epc_record ) recommender = WallRecommendations( @@ -247,7 +257,8 @@ class TestWallRecommendationsBase: 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"} + epc_record = EPCRecord(county="Derbyshire", property_type="House") + property_mock.epc_record = epc_record property_mock.walls = { "is_cob": False, "is_sandstone_or_limestone": False, @@ -268,21 +279,21 @@ class TestWallRecommendationsBase: 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"} + wall_recommendations_instance.property.epc_record.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"} + # Set property_type on the EPCRecord directly + wall_recommendations_instance.property.epc_record.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"} + epc_record = EPCRecord(county="Derbyshire", walls_energy_eff="Very Poor", property_type="House") 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)', @@ -315,8 +326,7 @@ class TestCavityWallRecommensations: assert np.isclose(recommender.recommendations[0]["total"], 925) def test_fill_partial_filled_cavity(self): - epc_record = EPCRecord() - epc_record.prepared_epc = {"county": "County Durham", "walls-energy-eff": "Poor"} + epc_record = EPCRecord(county="County Durham", walls_energy_eff="Poor", property_type="House") 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)', @@ -349,10 +359,8 @@ class TestCavityWallRecommensations: assert np.isclose(recommender.recommendations[0]["total"], 925.0) 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" - } + epc_record = EPCRecord(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)', @@ -387,21 +395,11 @@ class TestCavityWallRecommensations: 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"], 23400) - 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) + epc_record = EPCRecord(property_type="House", county="Derbyshire", built_form="Detached", + walls_energy_eff="Very Poor") + input_property3 = Property(id=1, postcode="F4k3 3", address="323 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', @@ -413,14 +411,12 @@ class TestCavityWallRecommensations: 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } - input_property3.age_band = "B" - input_property3.insulation_wall_area = 99 + input_property3.age_band = "F" + input_property3.insulation_wall_area = 120 input_property3.restricted_measures = False - input_property3.construction_age_band = "England and Wales: 1950-1966" + input_property3.construction_age_band = "England and Wales: 1976-1982" input_property3.already_installed = [] - assert input_property3.walls["is_timber_frame"] - recommender3 = WallRecommendations( property_instance=input_property3, materials=materials @@ -431,25 +427,14 @@ class TestCavityWallRecommensations: 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"], 19305.0) - assert recommender3.recommendations[1]["parts"][0]["type"] == "internal_wall_insulation" - assert recommender3.recommendations[1]["parts"][0]["depth"] == 95.0 + assert recommender3.estimated_u_value == 0.45 + assert np.isclose(recommender3.recommendations[0]["new_u_value"], 0.17) + assert np.isclose(recommender3.recommendations[0]["total"], 35802.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) + epc_record = EPCRecord(property_type="House", county="Derbyshire", built_form="Detached", + walls_energy_eff="Very Poor") + input_property4 = Property(id=1, postcode="F4k3 4", address="423 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', @@ -461,14 +446,12 @@ class TestCavityWallRecommensations: 'insulation_thickness': 'none', 'external_insulation': False, 'internal_insulation': False } - input_property4.age_band = "A" - input_property4.insulation_wall_area = 223 + input_property4.age_band = "F" + input_property4.insulation_wall_area = 120 input_property4.restricted_measures = False - input_property4.construction_age_band = "England and Wales: before 1900" + input_property4.construction_age_band = "England and Wales: 1976-1982" input_property4.already_installed = [] - assert input_property4.walls["is_granite_or_whinstone"] - recommender4 = WallRecommendations( property_instance=input_property4, materials=materials @@ -478,45 +461,29 @@ class TestCavityWallRecommensations: 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"], 43485.0) - assert recommender4.recommendations[1]["parts"][0]["type"] == "internal_wall_insulation" - assert recommender4.recommendations[1]["parts"][0]["depth"] == 95 + assert not recommender4.recommendations 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) + epc_record = EPCRecord(property_type="House", county="Derbyshire", built_form="Detached", + walls_energy_eff="Very Poor") + input_property5 = Property(id=1, postcode="F4k3 5", address="523 fake street", epc_record=epc_record) input_property5.walls = { - 'original_description': 'Cob, as built', - 'clean_description': 'Cob, as built', + 'original_description': 'Cob, as built, no insulation (assumed)', + 'clean_description': 'Cob, 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': True, 'is_assumed': False, + 'is_as_built': True, 'is_cob': True, 'is_assumed': True, '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.age_band = "F" + input_property5.insulation_wall_area = 120 input_property5.restricted_measures = False - input_property5.construction_age_band = "England and Wales: 1967-1975" + input_property5.construction_age_band = "England and Wales: 1976-1982" input_property5.already_installed = [] - assert input_property5.walls["is_cob"] - recommender5 = WallRecommendations( property_instance=input_property5, materials=materials @@ -526,15 +493,11 @@ class TestCavityWallRecommensations: 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" - } + epc_record = EPCRecord(property_type="House", county="Derbyshire", built_form="Detached", + 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)', @@ -542,13 +505,13 @@ class TestCavityWallRecommensations: '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_as_built': True, 'is_cob': False, 'is_assumed': True, '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.insulation_wall_area = 120 input_property6.restricted_measures = False input_property6.construction_age_band = "England and Wales: 1976-1982" input_property6.already_installed = [] @@ -562,11 +525,4 @@ class TestCavityWallRecommensations: 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"], 68250.0) - assert recommender6.recommendations[0]["parts"][0]["type"] == "internal_wall_insulation" - assert recommender6.recommendations[0]["parts"][0]["depth"] == 95 + assert not recommender6.recommendations diff --git a/recommendations/tests/test_window_recommendations.py b/recommendations/tests/test_window_recommendations.py index c6f383ba..12270961 100644 --- a/recommendations/tests/test_window_recommendations.py +++ b/recommendations/tests/test_window_recommendations.py @@ -29,15 +29,14 @@ class TestWindowRecommendations: :return: """ epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 0, - "uprn": 0, - "windows-energy-eff": "Very Poor", - "floor-area": 2.5, - "number-habitable-rooms": 5, - "number-heated-rooms": 5, - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 0 + epc_record.uprn = 0 + epc_record.windows_energy_eff = "Very Poor" + epc_record.floor_area = 2.5 + epc_record.number_habitable_rooms = 5 + epc_record.number_heated_rooms = 5 + property_1 = Property( id=1, postcode='1', @@ -79,12 +78,11 @@ class TestWindowRecommendations: :return: """ epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 33, - "uprn": 0, - "windows-energy-eff": "Good" # This has been observed in the EPC data - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 33 + epc_record.uprn = 0 + epc_record.windows_energy_eff = "Good" # This has been observed in the EPC data + property_2 = Property( id=1, postcode='1', @@ -124,11 +122,10 @@ class TestWindowRecommendations: :return: """ epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 100, - "uprn": 0 - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 100 + epc_record.uprn = 0 + property_3 = Property( id=1, postcode='1', @@ -154,11 +151,10 @@ class TestWindowRecommendations: def test_fully_secondary_glazed(self): epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 100, - "uprn": 0 - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 100 + epc_record.uprn = 0 + property_4 = Property( id=1, postcode='1', @@ -185,12 +181,11 @@ class TestWindowRecommendations: def test_partial_secondary_glazing(self): epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 50, - "uprn": 0, - "windows-energy-eff": "Poor" # This has been observed in the EPC data - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 50 + epc_record.uprn = 0 + epc_record.windows_energy_eff = "Poor" # This has been observed in the EPC data + property_5 = Property( id=1, postcode='1', @@ -225,12 +220,10 @@ class TestWindowRecommendations: def test_single_glazed_restricted_measures(self): epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 0, - "uprn": 0, - "windows-energy-eff": "Very Poor" - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 0 + epc_record.uprn = 0 + epc_record.windows_energy_eff = "Very Poor" property_6 = Property( id=1, @@ -270,11 +263,10 @@ class TestWindowRecommendations: def test_full_triple_glazed(self): epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 100, - "uprn": 0 - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 100 + epc_record.uprn = 0 + property_7 = Property( id=1, postcode='1', @@ -303,11 +295,10 @@ class TestWindowRecommendations: We don't recommend anything here """ epc_record = EPCRecord() - epc_record.prepared_epc = { - "county": "Wychavon", - "multi-glaze-proportion": 80, - "uprn": 1 - } + epc_record.county = "Wychavon" + epc_record.multi_glaze_proportion = 80 + epc_record.uprn = 1 + property_8 = Property( id=1, postcode='1',