diff --git a/.coveragerc b/.coveragerc
index 0ed78884..a220497a 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -9,4 +9,5 @@ omit =
model_data/plotting/*
recommendations/rdsap_tables.py
model_data/simulation_system/*
- model_data/cleaner_app.py
\ No newline at end of file
+ model_data/cleaner_app.py
+ backend/app/*
\ No newline at end of file
diff --git a/.idea/Model.iml b/.idea/Model.iml
index 05b9012b..b03b31b1 100644
--- a/.idea/Model.iml
+++ b/.idea/Model.iml
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3b05c6ac..ca0e1cd9 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py
index bc593031..d120a552 100644
--- a/backend/app/plan/router.py
+++ b/backend/app/plan/router.py
@@ -99,12 +99,6 @@ walls_decile_data = {
'Decile 9', 'Decile 10'], 'decile_boundaries': [6., 49., 51., 55., 64., 71., 76., 83., 96.,
120., 2279.]}
-lighting_averages = [
- {'lighting-description': 'good lighting efficiency', 'low-energy-lighting': 99.26666666666667},
- {'lighting-description': 'excellent lighting efficiency', 'low-energy-lighting': 100.0},
- {'lighting-description': 'below average lighting efficiency', 'low-energy-lighting': 0.0}
-]
-
def filter_materials(materials):
materials_by_type = defaultdict(list)
diff --git a/backend/tests/test_property.py b/backend/tests/test_property.py
index 1305a7db..679b25b0 100644
--- a/backend/tests/test_property.py
+++ b/backend/tests/test_property.py
@@ -2,7 +2,7 @@ import pytest
import pandas as pd
from unittest.mock import Mock
from epc_api.client import EpcClient
-from model_data.Property import Property
+from backend.Property import Property
from open_uprn.OpenUprnClient import OpenUprnClient
from model_data.EpcClean import EpcClean
@@ -19,6 +19,18 @@ mock_epc_response = {
"hotwater-description": "Hot Water Description",
"transaction-type": "rental",
"lighting-description": "Good Lighting Efficiency",
+ "energy-consumption-current": "50",
+ "co2-emissions-current": "123",
+ "mechanical-ventilation": "natural",
+ 'photo-supply': 0,
+ "solar-water-heating-flag": "N",
+ "wind-turbine-count": 0,
+ "extension-count": 0,
+ "heat-loss-corridor": "no corridor",
+ "unheated-corridor-length": 0,
+ "mains-gas-flag": "Y",
+ "floor-height": 2.5,
+ "total-floor-area": 100
},
{
"inspection-date": "2023-05-01",
@@ -30,6 +42,18 @@ mock_epc_response = {
"hotwater-description": "Hot Water Description",
"transaction-type": "rental",
"lighting-description": "Good Lighting Efficiency",
+ "energy-consumption-current": "50",
+ "co2-emissions-current": "123",
+ "mechanical-ventilation": "natural",
+ 'photo-supply': 0,
+ "solar-water-heating-flag": "N",
+ "wind-turbine-count": 0,
+ "extension-count": 0,
+ "heat-loss-corridor": "no corridor",
+ "unheated-corridor-length": 0,
+ "mains-gas-flag": "Y",
+ "floor-height": 2.5,
+ "total-floor-area": 100
}
]
}
@@ -42,6 +66,18 @@ mock_epc_response_dupe = {
'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description',
"transaction-type": "rental",
"lighting-description": "Good Lighting Efficiency",
+ "energy-consumption-current": "50",
+ "co2-emissions-current": "123",
+ "mechanical-ventilation": "natural",
+ 'photo-supply': 0,
+ "solar-water-heating-flag": "N",
+ "wind-turbine-count": 0,
+ "extension-count": 0,
+ "heat-loss-corridor": "no corridor",
+ "unheated-corridor-length": 0,
+ "mains-gas-flag": "Y",
+ "floor-height": 2.5,
+ "total-floor-area": 100
},
{
'inspection-date': '2023-05-01', 'some-other-key': 'some-other-value',
@@ -50,6 +86,18 @@ mock_epc_response_dupe = {
'hotwater-description': 'Hot Water Description',
"transaction-type": "rental",
"lighting-description": "Good Lighting Efficiency",
+ "energy-consumption-current": "50",
+ "co2-emissions-current": "123",
+ "mechanical-ventilation": "natural",
+ 'photo-supply': 0,
+ "solar-water-heating-flag": "N",
+ "wind-turbine-count": 0,
+ "extension-count": 0,
+ "heat-loss-corridor": "no corridor",
+ "unheated-corridor-length": 0,
+ "mains-gas-flag": "Y",
+ "floor-height": 2.5,
+ "total-floor-area": 100
},
{
'inspection-date': '2023-06-01', 'some-other-key': 'duplicate-date',
@@ -58,6 +106,18 @@ mock_epc_response_dupe = {
'mainheat-description': 'Main Heating Description', 'hotwater-description': 'Hot Water Description',
"transaction-type": "rental",
"lighting-description": "Good Lighting Efficiency",
+ "energy-consumption-current": "50",
+ "co2-emissions-current": "123",
+ "mechanical-ventilation": "natural",
+ 'photo-supply': 0,
+ "solar-water-heating-flag": "N",
+ "wind-turbine-count": 0,
+ "extension-count": 0,
+ "heat-loss-corridor": "no corridor",
+ "unheated-corridor-length": 0,
+ "mains-gas-flag": "Y",
+ "floor-height": 2.5,
+ "total-floor-area": 100
}
]
}
@@ -66,11 +126,11 @@ mock_epc_response_dupe = {
class TestProperty:
@pytest.fixture(autouse=True)
def property_instance(self, mock_epc_client, mock_open_uprn_client, mock_cleaner):
- return Property("AB12CD", "Test Address", epc_client=mock_epc_client)
+ return Property(1, "AB12CD", "Test Address", epc_client=mock_epc_client)
@pytest.fixture(autouse=True)
def property_instance_dupe_data(self, mock_epc_client_dupe_data):
- return Property("AB12CD", "Test Address", epc_client=mock_epc_client_dupe_data)
+ return Property(2, "AB12CD", "Test Address", epc_client=mock_epc_client_dupe_data)
@pytest.fixture
def mock_epc_client(self):
@@ -99,15 +159,26 @@ class TestProperty:
@pytest.fixture
def mock_cleaner(self):
- mock_cleaner = Mock(spec=EpcClean(data=[
- {"roof-description": "Roof Description"},
- {"walls-description": "Walls Description"},
- {"windows-description": "Windows Description"},
- {"mainheat-description": "Main Heating Description"},
- {"hotwater-description": "Hot Water Description"},
- {"lighting-description": "Good Lighting Efficiency"},
- {"low-energy-lighting": 0}
- ]))
+ lighting_averages = [
+ {'lighting-description': 'good lighting efficiency', 'low-energy-lighting': 99.26666666666667},
+ {'lighting-description': 'excellent lighting efficiency', 'low-energy-lighting': 100.0},
+ {'lighting-description': 'below average lighting efficiency', 'low-energy-lighting': 0.0}
+ ]
+
+ cleaner_spec = EpcClean(
+ data=[
+ {"roof-description": "Roof Description"},
+ {"walls-description": "Walls Description"},
+ {"windows-description": "Windows Description"},
+ {"mainheat-description": "Main Heating Description"},
+ {"hotwater-description": "Hot Water Description"},
+ {"lighting-description": "Good Lighting Efficiency"},
+ {"low-energy-lighting": 0}
+ ],
+ lighting_averages=lighting_averages
+ )
+
+ mock_cleaner = Mock(spec=cleaner_spec)
mock_cleaner.cleaned = {
"roof-description": [{"original_description": "Roof Description"}],
"walls-description": [{"original_description": "Walls Description"}],
@@ -119,14 +190,14 @@ class TestProperty:
return mock_cleaner
def test_init(self, mock_epc_client):
- inst1 = Property("AB12CD", "Test Address", epc_client=mock_epc_client)
+ inst1 = Property(0, "AB12CD", "Test Address", epc_client=mock_epc_client)
# Should be mocked auth token
assert inst1.epc_client.auth_token == "mocked_auth_token"
- inst2 = Property("AB12CD", "Test Address")
+ inst2 = Property(3, "AB12CD", "Test Address")
assert inst2.epc_client.auth_token
- inst3 = Property("AB12CD", "Test Address", data={"some": "data"})
+ inst3 = Property(4, "AB12CD", "Test Address", data={"some": "data"})
assert inst3.data == {"some": "data"}
data = inst3.search_address_epc()
@@ -143,29 +214,9 @@ class TestProperty:
with pytest.raises(Exception, match="More than one result found for this address - investigate me"):
property_instance_dupe_data.search_address_epc()
- def test_get_coordinates(self, property_instance, mock_open_uprn_client):
- # Set up the mock OpenUprnClient
- property_instance.data = {"uprn": 12345}
- property_instance.get_coordinates(mock_open_uprn_client)
-
- # Verify that the coordinates are set correctly
- assert property_instance.coordinates == {
- "uprn": 12345,
- "longitude": 1.2345,
- "latitude": 2.3456
- }
-
- def test_get_coordinates_without_open_uprn_data(self, property_instance, mock_open_uprn_client):
- # Modify the mock OpenUprnClient to not have read any data
- mock_open_uprn_client.data = None
-
- # Verify that ValueError is raised when OpenUprnClient data is None
- with pytest.raises(ValueError, match="OpenUprnClient has not read data"):
- property_instance.get_coordinates(mock_open_uprn_client)
-
def test_get_components(self, property_instance, mock_cleaner, mock_epc_client):
property_instance.search_address_epc()
- property_instance.get_components(mock_cleaner)
+ property_instance.get_components(mock_cleaner.cleaned)
# Verify that the components are set correctly
assert property_instance.roof == {"original_description": "Roof Description"}
@@ -180,7 +231,7 @@ class TestProperty:
# Verify that ValueError is raised when EpcClean doesn't contain cleaned data
with pytest.raises(ValueError, match="Cleaner does not contain cleaned data"):
- property_instance.get_components(mock_cleaner)
+ property_instance.get_components(mock_cleaner.cleaned)
def test_get_components_no_data(self, property_instance, mock_cleaner):
# Modify the mock cleaner to have no attributes for a specific description
@@ -190,7 +241,7 @@ class TestProperty:
# Verify that ValueError is raised when no attributes are found
with pytest.raises(ValueError, match="Property does not contain data"):
- property_instance.get_components(mock_cleaner)
+ property_instance.get_components(mock_cleaner.cleaned)
def test_get_components_no_attributes(self, property_instance, mock_cleaner):
# Modify the mock cleaner to have no attributes for a specific description
@@ -201,12 +252,12 @@ class TestProperty:
# Verify that ValueError is raised when no attributes are found
with pytest.raises(ValueError, match="Either No attributes or multiple found for roof-description"):
- property_instance.get_components(mock_cleaner)
+ property_instance.get_components(mock_cleaner.cleaned)
def test_get_components_multiple_attributes(self, property_instance, mock_cleaner):
# This shouldn't happen - it would mean a cleaning error
property_instance.search_address_epc()
- mock_cleaner.cleaned = {
+ cleaned = {
"roof-description": [
{"original_description": "Roof Description"},
{"original_description": "Roof Description"}
@@ -215,4 +266,4 @@ class TestProperty:
# Verify that ValueError is raised when multiple attributes are found
with pytest.raises(ValueError, match="Either No attributes or multiple found for roof-description"):
- property_instance.get_components(mock_cleaner)
+ property_instance.get_components(cleaned)
diff --git a/model_data/epc_attributes/LightingAttributes.py b/model_data/epc_attributes/LightingAttributes.py
index 6315db7f..94285170 100644
--- a/model_data/epc_attributes/LightingAttributes.py
+++ b/model_data/epc_attributes/LightingAttributes.py
@@ -13,14 +13,28 @@ class LightingAttributes:
def __init__(self, description, averages):
self.description: str = clean_description(description.lower())
- translation = self.WELSH_TEXT.get(self.description)
- if translation:
- self.nodata = False
- self.description = translation
+ self.welsh_translation_search()
self.description = correct_spelling(self.description)
self.averages = averages
+ def welsh_translation_search(self):
+ """
+ For welsh text describing the percentage of low energy lighting, we match the regular
+ expression and perform the translation
+ """
+ lel_match = re.search(r"goleuadau ynni-isel mewn (\d+)%? ogçör mannau gosod", self.description)
+
+ if lel_match:
+ # Perform the actual translation
+ percentage = lel_match.group(1)
+ self.description = f"low energy lighting in {percentage}% of fixed outlets"
+ else:
+ translation = self.WELSH_TEXT.get(self.description)
+ if translation:
+ self.nodata = False
+ self.description = translation
+
def process(self):
description = self.description
diff --git a/pytest.ini b/pytest.ini
index b2fa207a..401ecc0f 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,4 +1,4 @@
[pytest]
pythonpath = .
addopts = --cov-report term-missing --cov=model_data --cov=recommendations
-testpaths = model_data/tests recommendations/tests
+testpaths = model_data/tests recommendations/tests backend/tests