diff --git a/.gitignore b/.gitignore index 33827bd6..30ba031d 100644 --- a/.gitignore +++ b/.gitignore @@ -250,4 +250,5 @@ backend/node_modules node_modules/ backend/.idea open_uprn/.idea/ +conservation_areas/.idea/ diff --git a/.idea/Model.iml b/.idea/Model.iml index ac61a988..9812b63d 100644 --- a/.idea/Model.iml +++ b/.idea/Model.iml @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 242c02bb..b4d05b45 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/model_data/ConservationAreaClient.py b/conservation_areas/ConservationAreaClient.py similarity index 93% rename from model_data/ConservationAreaClient.py rename to conservation_areas/ConservationAreaClient.py index 7df8c69f..357cfe13 100644 --- a/model_data/ConservationAreaClient.py +++ b/conservation_areas/ConservationAreaClient.py @@ -1,7 +1,8 @@ from enum import Enum import geopandas as gpd from shapely.geometry import Point -from model_data.utils import setup_logger +from utils.logger import setup_logger +from datatypes.datatypes import OpenUprnCoordinateData logger = setup_logger() @@ -39,7 +40,13 @@ class ConservationAreaClient: self.gov_data = gpd.read_file(self.gov_path) self.gov_data = self.gov_data.drop(columns=["dataset"]) - def is_in_conservation_area(self, coordinates: dict): + def is_in_conservation_area(self, coordinates: OpenUprnCoordinateData): + + """ + Check if a property is in a conservation area + :param coordinates: dictionary, which should have the OpenUprnCoordinateData format + :return: + """ if not coordinates: raise ValueError("Coordinates have not been set, run get_coordinates() first") diff --git a/conservation_areas/app.py b/conservation_areas/app.py new file mode 100644 index 00000000..544993a0 --- /dev/null +++ b/conservation_areas/app.py @@ -0,0 +1,53 @@ +""" +This application reads in the open uprn data from a static location and loads it into +our database for querying from other services +""" + +import os +from conservation_areas.ConservationAreaClient import ConservationAreaClient + + +def app(): + conservation_area_client = ConservationAreaClient( + historic_england_path=os.path.abspath( + os.path.dirname(__file__) + ) + "/model_data/local_data/Historic_Eng_Conservation_Areas/Conservation_Areas.shp", + gov_path=os.path.abspath( + os.path.dirname(__file__) + ) + "/model_data/local_data/gov-conservation-area.geojson" + ) + conservation_area_client.read() + + # We need to iterate through the open uprn data and check if the coordinates are in a conservation area + open_uprn_data = [ + {'UPRN': 6032920, 'X_COORDINATE': 535110.0, 'Y_COORDINATE': 181819.0, 'LATITUDE': 51.5191407, + 'LONGITUDE': -0.0540506}, + {'UPRN': 6038625, 'X_COORDINATE': 535374.0, 'Y_COORDINATE': 182784.0, 'LATITUDE': 51.5277492, + 'LONGITUDE': -0.0498772}, + {'UPRN': 34153991, 'X_COORDINATE': 523238.74, 'Y_COORDINATE': 178003.02, 'LATITUDE': 51.4875579, + 'LONGITUDE': -0.226392}, + {'UPRN': 10008299676, 'X_COORDINATE': 533285.0, 'Y_COORDINATE': 184711.0, 'LATITUDE': 51.5455629, + 'LONGITUDE': -0.0792445}, + {'UPRN': 10008299677, 'X_COORDINATE': 533285.0, 'Y_COORDINATE': 184711.0, 'LATITUDE': 51.5455629, + 'LONGITUDE': -0.0792445}, + {'UPRN': 100021039066, 'X_COORDINATE': 535506.0, 'Y_COORDINATE': 185624.0, 'LATITUDE': 51.5532385, + 'LONGITUDE': -0.0468833}, + {'UPRN': 100021226060, 'X_COORDINATE': 529247.0, 'Y_COORDINATE': 187959.0, 'LATITUDE': 51.5756908, + 'LONGITUDE': -0.1362513}, + {'UPRN': 200003489276, 'X_COORDINATE': 533210.0, 'Y_COORDINATE': 179442.0, 'LATITUDE': 51.4982309, + 'LONGITUDE': -0.0823165} + ] + + result = [conservation_area_client.is_in_conservation_area(coordinates) for coordinates in open_uprn_data] + + conservation_area_client + + uprns = [ + int(x) for x in + ['34153991', '6038625', '100021039066', '100021226060', '10008299676', '10008299677', '6032920', '200003489276'] + ] + + open_uprn_client.data[open_uprn_client.data["UPRN"].isin( + uprns + )].to_dict("records") + # TODO: Add a method to write to the database diff --git a/conservation_areas/requirements.txt b/conservation_areas/requirements.txt new file mode 100644 index 00000000..b089769a --- /dev/null +++ b/conservation_areas/requirements.txt @@ -0,0 +1,16 @@ +attrs==23.1.0 +certifi==2023.5.7 +click==8.1.6 +click-plugins==1.1.1 +cligj==0.7.2 +fiona==1.9.4.post1 +geopandas==0.13.2 +numpy==1.25.1 +packaging==23.1 +pandas==2.0.3 +pyproj==3.6.0 +python-dateutil==2.8.2 +pytz==2023.3 +shapely==2.0.1 +six==1.16.0 +tzdata==2023.3 diff --git a/datatypes/__init__.py b/datatypes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/datatypes/datatypes.py b/datatypes/datatypes.py new file mode 100644 index 00000000..3f48dee1 --- /dev/null +++ b/datatypes/datatypes.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass + + +@dataclass +class OpenUprnCoordinateData: + UPRN: int + X_COORDINATE: float + Y_COORDINATE: float + LATITUDE: float + LONGITUDE: float diff --git a/model_data/BoreholeClient.py b/model_data/BoreholeClient.py index 4212d94b..831440df 100644 --- a/model_data/BoreholeClient.py +++ b/model_data/BoreholeClient.py @@ -1,7 +1,7 @@ import math from tqdm import tqdm from dbfread import DBF -from model_data.utils import setup_logger +from utils.logger import setup_logger logger = setup_logger() diff --git a/model_data/LandRegistryClient.py b/model_data/LandRegistryClient.py index 7a025f4a..ab7fb7fc 100644 --- a/model_data/LandRegistryClient.py +++ b/model_data/LandRegistryClient.py @@ -2,7 +2,7 @@ from typing import List, Dict import pandas as pd from tqdm import tqdm import string -from model_data.utils import setup_logger +from utils.logger import setup_logger from fuzzywuzzy import fuzz import numpy as np diff --git a/model_data/analysis/SapModel.py b/model_data/analysis/SapModel.py index 378886ef..e39296ce 100644 --- a/model_data/analysis/SapModel.py +++ b/model_data/analysis/SapModel.py @@ -13,7 +13,7 @@ from model_data.EpcClean import EpcClean from statsmodels.stats.outliers_influence import variance_inflation_factor from tqdm import tqdm -from model_data.utils import setup_logger +from utils.logger import setup_logger logger = setup_logger() diff --git a/model_data/app.py b/model_data/app.py index 52b4444a..076f3095 100644 --- a/model_data/app.py +++ b/model_data/app.py @@ -2,7 +2,7 @@ from tqdm import tqdm import os from model_data.BoreholeClient import BoreholeClient from model_data.LandRegistryClient import LandRegistryClient -from model_data.ConservationAreaClient import ConservationAreaClient +from conservation_areas.ConservationAreaClient import ConservationAreaClient from model_data.temp_inputs import input_data from model_data.Property import Property diff --git a/model_data/tests/test_utils.py b/model_data/tests/test_utils.py index 1bd8c2f0..26a9bd7d 100644 --- a/model_data/tests/test_utils.py +++ b/model_data/tests/test_utils.py @@ -1,7 +1,8 @@ import logging from io import StringIO from unittest.mock import patch -from model_data.utils import setup_logger, is_percentage_or_number, correct_spelling +from model_data.utils import is_percentage_or_number, correct_spelling +from utils.logger import setup_logger class TestLogger: diff --git a/model_data/utils.py b/model_data/utils.py index 28f8da41..744914a4 100644 --- a/model_data/utils.py +++ b/model_data/utils.py @@ -1,4 +1,3 @@ -import logging import re from textblob import TextBlob @@ -6,40 +5,6 @@ from textblob import TextBlob PERCENTAGE_PATTERN = re.compile(r'^\d+%?$') -def setup_logger(log_file=None, level=logging.INFO, overwrite_handler=False): - # Create a logger and set the logging level - logger = logging.getLogger() - logger.setLevel(level) - - # if logger already has handlers, just return it - if logger.hasHandlers() and not overwrite_handler: - return logger - - # Define the log message format - log_format = "%(asctime)s [%(levelname)s] %(message)s" - date_format = "%Y-%m-%d %H:%M:%S" - formatter = logging.Formatter(log_format, datefmt=date_format) - - # Create a file handler and set the file path and format - if log_file: - file_handler = logging.FileHandler(log_file) - file_handler.setLevel(level) - file_handler.setFormatter(formatter) - logger.addHandler(file_handler) - - # Create a console handler and set the format - console_handler = logging.StreamHandler() - console_handler.setLevel(level) - - # Set the formatter for the handlers - console_handler.setFormatter(formatter) - - # Add the handlers to the logger - logger.addHandler(console_handler) - - return logger - - def is_percentage_or_number(s): # re.match returns None if the string does not match the pattern return PERCENTAGE_PATTERN.match(s) is not None diff --git a/open_uprn/OpenUprnClient.py b/open_uprn/OpenUprnClient.py index 053ba91b..502ed25a 100644 --- a/open_uprn/OpenUprnClient.py +++ b/open_uprn/OpenUprnClient.py @@ -1,5 +1,5 @@ import pandas as pd -from model_data.utils import setup_logger +from utils.logger import setup_logger logger = setup_logger() diff --git a/open_uprn/app.py b/open_uprn/app.py index a96b8105..6ed62c44 100644 --- a/open_uprn/app.py +++ b/open_uprn/app.py @@ -15,12 +15,4 @@ def app(): ) open_uprn_client.read() - uprns = [ - int(x) for x in - ['34153991', '6038625', '100021039066', '100021226060', '10008299676', '10008299677', '6032920', '200003489276'] - ] - - open_uprn_client.data[open_uprn_client.data["UPRN"].isin( - uprns - )].to_dict("records") # TODO: Add a method to write to the database diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/utils/logger.py b/utils/logger.py new file mode 100644 index 00000000..d643f36a --- /dev/null +++ b/utils/logger.py @@ -0,0 +1,35 @@ +import logging + + +def setup_logger(log_file=None, level=logging.INFO, overwrite_handler=False): + # Create a logger and set the logging level + logger = logging.getLogger() + logger.setLevel(level) + + # if logger already has handlers, just return it + if logger.hasHandlers() and not overwrite_handler: + return logger + + # Define the log message format + log_format = "%(asctime)s [%(levelname)s] %(message)s" + date_format = "%Y-%m-%d %H:%M:%S" + formatter = logging.Formatter(log_format, datefmt=date_format) + + # Create a file handler and set the file path and format + if log_file: + file_handler = logging.FileHandler(log_file) + file_handler.setLevel(level) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + # Create a console handler and set the format + console_handler = logging.StreamHandler() + console_handler.setLevel(level) + + # Set the formatter for the handlers + console_handler.setFormatter(formatter) + + # Add the handlers to the logger + logger.addHandler(console_handler) + + return logger