mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added property tests
This commit is contained in:
parent
3f2992bcb0
commit
35a9679220
5 changed files with 172 additions and 22 deletions
|
|
@ -54,3 +54,21 @@ class BoreholeClient:
|
|||
distance_m = math.sqrt((x2_bng - x1_bng) ** 2 + (y2_bng - y1_bng) ** 2)
|
||||
distance_km = distance_m / 1000 # convert meters to kilometers
|
||||
return distance_m, distance_km
|
||||
|
||||
# EXAMPLE
|
||||
# There are ~1.4 million entries in this dataset and so we firstly want to reduce the number of
|
||||
# entries in here if possible before we produce any form of comparison between our properties, to infer
|
||||
# the distance from the property to the nearest borehole
|
||||
|
||||
# Let's take a sample
|
||||
# borehold_compare_to = borehole_client.data[0]
|
||||
# property = input_properties[0]
|
||||
#
|
||||
# # for each property, find the nearest borehole
|
||||
# # This is just an example, looking at the distance from a property to a borehole
|
||||
# dist_m, dist_km = borehole_client.distance_between_bng_coords(
|
||||
# x1_bng=property.coordinates["x_coordinate"],
|
||||
# y1_bng=property.coordinates["y_coordinate"],
|
||||
# x2_bng=borehold_compare_to["X"],
|
||||
# y2_bng=borehold_compare_to["Y"],
|
||||
# )
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class EpcClean:
|
|||
"hotwater-description",
|
||||
"main-fuel",
|
||||
"mainheat-description",
|
||||
"main-heating-controls",
|
||||
"mainheatcont-description",
|
||||
"roof-description",
|
||||
"walls-description",
|
||||
"windows-description",
|
||||
|
|
@ -50,7 +50,7 @@ class EpcClean:
|
|||
self.clean_wrapper(field="hotwater-description", cleaning_cls=HotWaterAttributes)
|
||||
self.clean_wrapper(field="main-fuel", cleaning_cls=MainFuelAttributes)
|
||||
self.clean_wrapper(field="mainheat-description", cleaning_cls=MainHeatAttributes)
|
||||
self.clean_wrapper(field="main-heating-controls", cleaning_cls=MainheatControlAttributes)
|
||||
self.clean_wrapper(field="mainheatcont-description", cleaning_cls=MainheatControlAttributes)
|
||||
self.clean_wrapper(field="roof-description", cleaning_cls=RoofAttributes)
|
||||
self.clean_wrapper(field="walls-description", cleaning_cls=WallAttributes)
|
||||
self.clean_wrapper(field="windows-description", cleaning_cls=WindowAttributes)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,30 @@
|
|||
from epc_api.client import EpcClient
|
||||
from model_data.config import EPC_AUTH_TOKEN
|
||||
from model_data.OpenUprnClient import OpenUprnClient
|
||||
from model_data.EpcClean import EpcClean
|
||||
|
||||
|
||||
class Property:
|
||||
ATTRIBUTE_MAP = {
|
||||
"floor-description": "floor",
|
||||
"hotwater-description": "hotwater",
|
||||
"main-fuel": "main_fuel",
|
||||
"mainheat-description": "main_heating",
|
||||
"mainheatcont-description": "main_heating_controls",
|
||||
"roof-description": "roof",
|
||||
"walls-description": "walls",
|
||||
"windows-description": "windows",
|
||||
}
|
||||
|
||||
floor = None
|
||||
hotwater = None
|
||||
main_fuel = None
|
||||
main_heating = None
|
||||
main_heating_controls = None
|
||||
roof = None
|
||||
walls = None
|
||||
windows = None
|
||||
|
||||
coordinates = None
|
||||
|
||||
def __init__(self, postcode, address1, epc_client=None, data=None):
|
||||
|
|
@ -56,3 +77,23 @@ class Property:
|
|||
)
|
||||
|
||||
self.coordinates = {key.lower(): value for key, value in self.coordinates.items()}
|
||||
|
||||
def get_components(self, cleaner: EpcClean):
|
||||
"""
|
||||
Given the cleaning that has been performed, we'll use this to identify the property
|
||||
components, from roof to walls to windows, heating and hot water
|
||||
:param cleaner:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not cleaner.cleaned:
|
||||
raise ValueError("Cleaner does not contain cleaned data")
|
||||
|
||||
for description, attribute in cleaner.cleaned.items():
|
||||
|
||||
attributes = [
|
||||
x for x in cleaner.cleaned[description] if x["original_description"] == self.data[description]
|
||||
]
|
||||
if len(attributes) != 1:
|
||||
raise ValueError("Either No attributes or multiple found for %s" % description)
|
||||
setattr(self, self.ATTRIBUTE_MAP[description], attributes[0])
|
||||
|
|
|
|||
|
|
@ -106,19 +106,7 @@ def handler():
|
|||
)
|
||||
borehole_client.read()
|
||||
|
||||
# There are ~1.4 million entries in this dataset and so we firstly want to reduce the number of
|
||||
# entries in here if possible before we produce any form of comparison between our properties, to infer
|
||||
# the distance from the property to the nearest borehole
|
||||
|
||||
# Let's take a sample
|
||||
borehold_compare_to = borehole_client.data[0]
|
||||
property = input_properties[0]
|
||||
|
||||
# for each property, find the nearest borehole
|
||||
# This is just an example, looking at the distance from a property to a borehole
|
||||
dist_m, dist_km = borehole_client.distance_between_bng_coords(
|
||||
x1_bng=property.coordinates["x_coordinate"],
|
||||
y1_bng=property.coordinates["y_coordinate"],
|
||||
x2_bng=borehold_compare_to["X"],
|
||||
y2_bng=borehold_compare_to["Y"],
|
||||
)
|
||||
# Now, for our input properties, we need to identify the components of the building, based
|
||||
# on the cleaning we've done
|
||||
for p in input_properties:
|
||||
p.get_components(cleaner)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import pytest
|
||||
import pandas as pd
|
||||
from unittest.mock import Mock
|
||||
from epc_api.client import EpcClient
|
||||
from model_data.Property import Property
|
||||
from model_data.OpenUprnClient import OpenUprnClient
|
||||
from model_data.EpcClean import EpcClean
|
||||
|
||||
# Define some test data
|
||||
mock_epc_response = {
|
||||
|
|
@ -9,21 +12,54 @@ mock_epc_response = {
|
|||
{
|
||||
"inspection-date": "2023-06-01",
|
||||
"some-other-key": "some-value",
|
||||
# add other keys as necessary
|
||||
"roof-description": "Roof Description",
|
||||
"walls-description": "Walls Description",
|
||||
"windows-description": "Windows Description",
|
||||
"mainheat-description": "Main Heating Description",
|
||||
"hotwater-description": "Hot Water Description"
|
||||
},
|
||||
{
|
||||
"inspection-date": "2023-05-01",
|
||||
"some-other-key": "some-other-value",
|
||||
# add other keys as necessary
|
||||
"roof-description": "Roof Description",
|
||||
"walls-description": "Walls Description",
|
||||
"windows-description": "Windows Description",
|
||||
"mainheat-description": "Main Heating Description",
|
||||
"hotwater-description": "Hot Water Description"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# Create a mock EPC client
|
||||
mock_client = Mock(spec=EpcClient())
|
||||
mock_client.domestic.search.return_value = mock_epc_response
|
||||
mock_client.domestic.search.return_value = mock_epc_response.copy()
|
||||
mock_client.auth_token = "mocked_auth_token"
|
||||
|
||||
# Create a mock OpenUprnClient instance
|
||||
mock_open_uprn_client = Mock(spec=OpenUprnClient(path=None, uprns=[12345]))
|
||||
mock_open_uprn_client.data = pd.DataFrame(
|
||||
[
|
||||
{"UPRN": 12345, "longitude": 1.2345, "latitude": 2.3456},
|
||||
{"UPRN": 12346, "longitude": 3.4567, "latitude": 4.5678}
|
||||
]
|
||||
)
|
||||
|
||||
# Create a mock EpcClean instance
|
||||
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"}
|
||||
]))
|
||||
mock_cleaner.cleaned = {
|
||||
"roof-description": [{"original_description": "Roof Description"}],
|
||||
"walls-description": [{"original_description": "Walls Description"}],
|
||||
"windows-description": [{"original_description": "Windows Description"}],
|
||||
"mainheat-description": [{"original_description": "Main Heating Description"}],
|
||||
"hotwater-description": [{"original_description": "Hot Water Description"}]
|
||||
}
|
||||
|
||||
|
||||
class TestProperty:
|
||||
@pytest.fixture
|
||||
|
|
@ -53,10 +89,77 @@ class TestProperty:
|
|||
|
||||
def test_search_address_epc_multiple_results(self, property_instance):
|
||||
# Modify the mock response to return two results with the same date
|
||||
mock_epc_response["rows"].append({
|
||||
mock_client.domestic.search.return_value["rows"].append({
|
||||
"inspection-date": "2023-06-01",
|
||||
"some-other-key": "duplicate-date"
|
||||
})
|
||||
|
||||
with pytest.raises(Exception, match="More than one result found for this address - investigate me"):
|
||||
property_instance.search_address_epc()
|
||||
|
||||
# Reset the change
|
||||
mock_client.domestic.search.return_value["rows"].pop(-1)
|
||||
assert len(mock_client.domestic.search.return_value["rows"]) == 1
|
||||
|
||||
def test_get_coordinates(self, property_instance):
|
||||
# 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):
|
||||
# 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):
|
||||
property_instance.search_address_epc()
|
||||
property_instance.get_components(mock_cleaner)
|
||||
|
||||
# Verify that the components are set correctly
|
||||
assert property_instance.roof == {"original_description": "Roof Description"}
|
||||
assert property_instance.walls == {"original_description": "Walls Description"}
|
||||
assert property_instance.windows == {"original_description": "Windows Description"}
|
||||
assert property_instance.main_heating == {"original_description": "Main Heating Description"}
|
||||
assert property_instance.hotwater == {"original_description": "Hot Water Description"}
|
||||
|
||||
def test_get_components_without_cleaned_data(self, property_instance):
|
||||
# Modify the mock EpcClean to not have cleaned data
|
||||
mock_cleaner.cleaned = {}
|
||||
|
||||
# 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)
|
||||
|
||||
def test_get_components_no_attributes(self, property_instance):
|
||||
# Modify the mock cleaner to have no attributes for a specific description
|
||||
mock_cleaner.cleaned = {
|
||||
"roof-description": []
|
||||
}
|
||||
|
||||
# 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)
|
||||
|
||||
def test_get_components_multiple_attributes(self, property_instance):
|
||||
# This shouldn't happen - it would mean a cleaning error
|
||||
property_instance.search_address_epc()
|
||||
mock_cleaner.cleaned = {
|
||||
"roof-description": [
|
||||
{"original_description": "Roof Description"},
|
||||
{"original_description": "Roof Description"}
|
||||
]
|
||||
}
|
||||
|
||||
# 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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue