diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..f5c7b106 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +model_data/local_data/* diff --git a/backend/docker/Dockerfile b/backend/docker/Dockerfile index 16aa9b4b..7d35a53a 100644 --- a/backend/docker/Dockerfile +++ b/backend/docker/Dockerfile @@ -5,19 +5,22 @@ FROM python:3.10.12-slim-buster ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 -# Set work directory -WORKDIR /app +# Set work directory to the root of your project +WORKDIR /Model # Install system dependencies RUN apt-get update && apt-get install -y netcat-openbsd # Install python dependencies -COPY ./requirements/base.txt ./requirements/base.txt +COPY ./backend/requirements/base.txt ./backend/requirements/base.txt +COPY ./model_data/requirements/requirements.txt ./model_data/requirements/requirements.txt RUN pip install --upgrade pip -RUN pip install -r requirements/base.txt +RUN pip install -r backend/requirements/base.txt +RUN pip install -r model_data/requirements/requirements.txt # Copy project -COPY . . +COPY ./backend ./backend +COPY ./model_data ./model_data # command to run on container start -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +CMD ["uvicorn", "backend.app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 33748cea..5a1693c4 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -1,6 +1,7 @@ anyio==3.7.1 cffi==1.15.1 click==8.1.3 +cryptography==37.0.4 ecdsa==0.18.0 exceptiongroup==1.1.2 fastapi==0.99.1 @@ -15,4 +16,13 @@ PyJWT==2.7.0 python-dotenv==1.0.0 python-jose==3.3.0 PyYAML==6.0 -cryptography==37.0.4 \ No newline at end of file +rsa==4.9 +six==1.16.0 +sniffio==1.3.0 +starlette==0.27.0 +typing_extensions==4.7.1 +uvicorn==0.22.0 +uvloop==0.17.0 +watchfiles==0.19.0 +websockets==11.0.3 +boto3 \ No newline at end of file diff --git a/model_data/ConservationAreaClient.py b/model_data/ConservationAreaClient.py index a7f1063a..7df8c69f 100644 --- a/model_data/ConservationAreaClient.py +++ b/model_data/ConservationAreaClient.py @@ -1,4 +1,4 @@ -import pandas as pd +from enum import Enum import geopandas as gpd from shapely.geometry import Point from model_data.utils import setup_logger @@ -39,6 +39,31 @@ 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): + + if not coordinates: + raise ValueError("Coordinates have not been set, run get_coordinates() first") + + is_in_conservation_area = self.is_in_conservation_area_historic_england( + x_bng=coordinates["x_coordinate"], + y_bng=coordinates["y_coordinate"] + ) + + if is_in_conservation_area != "unknown": + return is_in_conservation_area + + if is_in_conservation_area == "unknown": + # We double check the secondary data source + backup = self.is_in_conservation_area_historic_gov( + longitude=coordinates["longitude"], + latitude=coordinates["latitude"] + ) + + if backup: + return ConservationAreaClient.IN_CONSERVATION_AREA + else: + return ConservationAreaClient.UNKNOWN + def is_in_conservation_area_historic_england(self, x_bng: float, y_bng: float) -> str: """ Check if a property is in a conservation area @@ -103,3 +128,9 @@ class ConservationAreaClient: distance_meters = distances.min() return distance_meters + + +class InConservationArea(Enum): + IN_CONSERVATION_AREA = ConservationAreaClient.IN_CONSERVATION_AREA + NOT_IN_CONSERVATION_AREA = ConservationAreaClient.NOT_IN_CONSERVATION_AREA + UNKNOWN = ConservationAreaClient.UNKNOWN diff --git a/model_data/Property.py b/model_data/Property.py index 3627a1d1..98b7a0b0 100644 --- a/model_data/Property.py +++ b/model_data/Property.py @@ -5,7 +5,6 @@ from model_data.config import EPC_AUTH_TOKEN from model_data.OpenUprnClient import OpenUprnClient from model_data.EpcClean import EpcClean from model_data.BaseUtility import BaseUtility -from model_data.ConservationAreaClient import ConservationAreaClient class Property(BaseUtility): @@ -117,28 +116,12 @@ class Property(BaseUtility): raise ValueError("Either No attributes or multiple found for %s" % description) setattr(self, self.ATTRIBUTE_MAP[description], attributes[0]) - def set_is_in_conservation_area(self, conservation_area_client: ConservationAreaClient): - - if not self.coordinates: - raise ValueError("Coordinates have not been set, run get_coordinates() first") - - is_in_conservation_area = conservation_area_client.is_in_conservation_area_historic_england( - x_bng=self.coordinates["x_coordinate"], - y_bng=self.coordinates["y_coordinate"] - ) - - self.in_conservation_area = is_in_conservation_area - if is_in_conservation_area == "unknown": - # We double check the secondary data source - backup = conservation_area_client.is_in_conservation_area_historic_gov( - longitude=self.coordinates["longitude"], - latitude=self.coordinates["latitude"] - ) - - if backup: - self.in_conservation_area = ConservationAreaClient.IN_CONSERVATION_AREA - else: - self.in_conservation_area = ConservationAreaClient.UNKNOWN + def set_is_in_conservation_area(self, in_conservation_area): + """ + Sets whether the property is in a conservation area given the output of the ConservationAreaClient + :param in_conservation_area: string value, indicating whether the property is in a conservation area + """ + self.in_conservation_area = in_conservation_area def set_year_built(self): """ diff --git a/model_data/app.py b/model_data/app.py index 8e340e9e..7b4f057e 100644 --- a/model_data/app.py +++ b/model_data/app.py @@ -67,7 +67,8 @@ def handler(): # Check if the property is in a conversation area for p in input_properties: - p.set_is_in_conservation_area(conservation_area_client) + in_conservation_area = conservation_area_client.is_in_conservation_area(p.coordinates) + p.set_is_in_conservation_area(in_conservation_area) local_authorities = {p.data['local-authority'] for p in input_properties} # TODO: Do this at a constituency level diff --git a/model_data/requirements/dev.txt b/model_data/requirements/dev.txt index 1bfe0872..c48188c6 100644 --- a/model_data/requirements/dev.txt +++ b/model_data/requirements/dev.txt @@ -3,3 +3,4 @@ pytest mock pytest-cov pytest-mock +pip-check-reqs diff --git a/model_data/requirements/requirements.txt b/model_data/requirements/requirements.txt index e12edb31..42e3e369 100644 --- a/model_data/requirements/requirements.txt +++ b/model_data/requirements/requirements.txt @@ -10,7 +10,6 @@ python-Levenshtein dbfread pyproj pint -geopandas mip seaborn statsmodels diff --git a/model_data/requirements/static.txt b/model_data/requirements/static.txt new file mode 100644 index 00000000..b9b8e7c5 --- /dev/null +++ b/model_data/requirements/static.txt @@ -0,0 +1 @@ +geopandas \ No newline at end of file