diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..a931f718 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,34 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3/.devcontainer/base.Dockerfile + + +# [Choice] Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon): ubuntu-22.04, ubuntu-20.04, ubuntu-18.04 +ARG VARIANT=ubuntu-24.04 +FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT} + +# Postgres & our packages. Currently not customizable via VERSION param. +# RUN apt-get update \ +# && apt-get -y install --no-install-recommends curl ca-certificates gnupg +RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - +RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' +RUN apt-get update \ + && apt-get -y install --no-install-recommends postgresql-plpython3-17 postgresql-17-postgis-3 libpq-dev + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -yq --no-install-recommends \ + libhdf5-dev \ + libnetcdf-dev \ + libyaml-dev \ + libgdal-dev \ + python3 \ + python3-dev \ + python3-pip \ + cython3 && \ + rm -rf /var/lib/apt/lists/* + +ENV CPLUS_INCLUDE_PATH=/usr/include/gdal +ENV C_INCLUDE_PATH=/usr/include/gdal + +RUN pip install numpy --break-system-packages + +RUN pip install gdal==3.8.4 h5py netCDF4 psycopg2 PyYAML pillow --break-system-packages \ No newline at end of file diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 00000000..43440e04 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,24 @@ +{ + "features": { + "ghcr.io/devcontainers-extra/features/poetry:2": { + "version": "2.0.18", + "resolved": "ghcr.io/devcontainers-extra/features/poetry@sha256:7b5f2c66d25d8b3cc5d601af6b81b720c77dd315f24d5e6f84b342cccc3c9228", + "integrity": "sha256:7b5f2c66d25d8b3cc5d601af6b81b720c77dd315f24d5e6f84b342cccc3c9228" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.17.0", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c", + "integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.5", + "resolved": "ghcr.io/devcontainers/features/git@sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251", + "integrity": "sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "1.8.0", + "resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511", + "integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..a29dda01 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,67 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3 +{ + "name": "Python3 & Poetry & Postgres", + "build": { + "dockerfile": "Dockerfile", + "args": { + } + }, + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "remote.autoForwardPorts": false, + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", + "python.linting.enabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" + }, + // VSCODE ONLY: Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance" + ] + } + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + //"forwardPorts": [48423], + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "bash ./.devcontainer/post-install.sh", + "postStartCommand": "bash ./.devcontainer/post-start.sh", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": "latest", + "ghcr.io/devcontainers/features/git:1": "latest", + // add python to container + "ghcr.io/devcontainers/features/python:1": { + "version": "3.14" + }, + // add poetry to container + "ghcr.io/devcontainers-extra/features/poetry:2": { + "version": "2.4" + } + }, + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "remoteEnv": { + // "PATH": "${containerEnv:PATH}:${containerEnv:HOME}/.local/bin" + }, + "runArgs": [ + // allow container to be treated with no network isolation + "--network=host", + // give a nicer name to the container + "--name", + "pcex_backend_devcontainer_${localEnv:USER}" + ], + "mounts": [ + "source=/storage/data/projects/comp_support/daccs/test-data/,target=/storage/data/projects/comp_support/daccs/test-data/,type=bind,consistency=cached" + ] +} \ No newline at end of file diff --git a/.devcontainer/post-install.sh b/.devcontainer/post-install.sh new file mode 100644 index 00000000..4fa1dd38 --- /dev/null +++ b/.devcontainer/post-install.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -ex + +## +## Create some aliases +## +echo 'alias ll="ls -alF"' >> $HOME/.bashrc +echo 'alias la="ls -A"' >> $HOME/.bashrc +echo 'alias l="ls -CF"' >> $HOME/.bashrc + +# Convenience workspace directory for later use +WORKSPACE_DIR=$(pwd) + +# Change some Poetry settings to better deal with working in a container +poetry config cache-dir ${WORKSPACE_DIR}/.cache +poetry config virtualenvs.in-project true + +# Now install all dependencies +poetry install --all-extras + +echo "Done!" \ No newline at end of file diff --git a/.devcontainer/post-start.sh b/.devcontainer/post-start.sh new file mode 100644 index 00000000..4fa1dd38 --- /dev/null +++ b/.devcontainer/post-start.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -ex + +## +## Create some aliases +## +echo 'alias ll="ls -alF"' >> $HOME/.bashrc +echo 'alias la="ls -A"' >> $HOME/.bashrc +echo 'alias l="ls -CF"' >> $HOME/.bashrc + +# Convenience workspace directory for later use +WORKSPACE_DIR=$(pwd) + +# Change some Poetry settings to better deal with working in a container +poetry config cache-dir ${WORKSPACE_DIR}/.cache +poetry config virtualenvs.in-project true + +# Now install all dependencies +poetry install --all-extras + +echo "Done!" \ No newline at end of file diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index d26fc1e7..68cebf53 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -14,7 +14,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: "3.12" # Python ≥3.11 required for use_pyproject + python-version: "3.14" # Python ≥3.11 required for use_pyproject - uses: psf/black@35ea67920b7f6ac8e09be1c47278752b1e827f76 # stable with: use_pyproject: true diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 3eadb537..bfd2f5c1 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - python-version: '3.12' + python-version: '3.14' - name: Install Poetry run: | pipx install "poetry==2.4.0" diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 9fd1a377..5bdff5ac 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -8,6 +8,17 @@ jobs: test: runs-on: ubuntu-24.04 container: pcic/geospatial-python:3.8.4 + timeout-minutes: 15 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.python-version }} + cancel-in-progress: true + strategy: + matrix: + python-version: + - "3.11" + - "3.12" + - "3.13" + - "3.14" env: HOME: /root diff --git a/ce/__init__.py b/ce/__init__.py index 2edbf15c..7a452294 100644 --- a/ce/__init__.py +++ b/ce/__init__.py @@ -1,7 +1,9 @@ import os +from typing import Any from flask import Flask from flask_cors import CORS +from sqlalchemy.pool import NullPool from ce.api.multimeta_cache import ( DEFAULT_PRELOAD_MULTIMETA_PARAMS, @@ -11,6 +13,31 @@ from ce.views import add_routes +def _build_engine_options(): + """Build SQLAlchemy engine options from environment variables. + + Set SQLALCHEMY_POOL_CLASS=null to use NullPool (recommended when deploying + behind pgbouncer). Optional pool tuning via SQLALCHEMY_POOL_SIZE, + SQLALCHEMY_MAX_OVERFLOW, SQLALCHEMY_POOL_TIMEOUT, SQLALCHEMY_POOL_RECYCLE. + """ + if os.getenv("SQLALCHEMY_POOL_CLASS", "").lower() == "null": + return {"poolclass": NullPool} + + options: dict[str, Any] = {"pool_pre_ping": True} + + for env_var, key, cast in [ + ("SQLALCHEMY_POOL_SIZE", "pool_size", int), + ("SQLALCHEMY_MAX_OVERFLOW", "max_overflow", int), + ("SQLALCHEMY_POOL_TIMEOUT", "pool_timeout", float), + ("SQLALCHEMY_POOL_RECYCLE", "pool_recycle", float), + ]: + val = os.getenv(env_var) + if val is not None: + options[key] = cast(val) + + return options + + def get_app(config=None): app = Flask(__name__) CORS(app) @@ -19,7 +46,7 @@ def get_app(config=None): "SQLALCHEMY_DATABASE_URI": os.getenv( "MDDB_DSN", "postgresql://httpd_meta@db.pcic.uvic.ca/pcic_meta" ), - "SQLALCHEMY_ENGINE_OPTIONS": {"pool_pre_ping": True}, + "SQLALCHEMY_ENGINE_OPTIONS": _build_engine_options(), "SQLALCHEMY_TRACK_MODIFICATIONS": False, "MULTIMETA_CACHE_DIR": os.getenv("MULTIMETA_CACHE_DIR", "/tmp/multimeta-cache"), "MULTIMETA_CACHE_ENABLED": True, diff --git a/ce/api/geo.py b/ce/api/geo.py index 937c5987..6e232fa8 100644 --- a/ce/api/geo.py +++ b/ce/api/geo.py @@ -249,8 +249,9 @@ def polygon_to_masked_array_helper(raster): return array * scale_factor + add_offset - if "dodsC" in resource: - with rasterio_thredds_helper(resource, variable) as raster: + if "http" in resource: + fs_resource = resource.replace("/dodsC/", "/fileServer/", 1) + with rasterio_thredds_helper(fs_resource, variable) as raster: return polygon_to_masked_array_helper(raster) # Local file path case @@ -263,10 +264,10 @@ def polygon_to_masked_array_helper(raster): @contextmanager def rasterio_thredds_helper(resource, variable): """ - Opens a remote NetCDF resource for Rasterio, handling OpenDAP (dodsC) or fileServer as needed. - Assumes Rasterio has GDAL with OpenDAP support. + Opens a remote NetCDF resource for Rasterio via GDAL's vsicurl. + Expects a fileServer URL; GDAL's NetCDF driver requires direct HTTP + file access and cannot use OPeNDAP. """ - # Construct proper NetCDF subdataset path - dataset_url = f'NETCDF:"{resource}":{variable}' + dataset_url = f'NETCDF:"/vsicurl/{resource}":{variable}' with rasterio.open(dataset_url, mode="r", driver="NetCDF") as raster: yield raster diff --git a/ce/api/util.py b/ce/api/util.py index 58b88a95..9b46610c 100644 --- a/ce/api/util.py +++ b/ce/api/util.py @@ -68,12 +68,9 @@ def open_nc(resource): f"The metadata database is out of sync with the filesystem. I was told to open file with name {resource}, but it does not exist." ) - try: - nc = Dataset(resource, "r") + with Dataset(resource, "r") as nc: nc.set_always_mask(False) yield nc - finally: - nc.close() def get_array(nc, resource, time, area, variable): diff --git a/ce/tests/api/test_geospatial.py b/ce/tests/api/test_geospatial.py index c70c83a3..f088700f 100644 --- a/ce/tests/api/test_geospatial.py +++ b/ce/tests/api/test_geospatial.py @@ -1,6 +1,6 @@ import pytest from shapely.errors import ShapelyError -from shapely.geometry import Polygon +from shapely.geometry import MultiPolygon, Polygon from ce.api.geospatial import ( geojson_feature, outline_cell_rect, @@ -36,31 +36,43 @@ def test_geojson_feature(thing, kwargs, expected): # Test cases are on a unit grid. For simplicity, cell centres are at # coordinates integer + 0.5, therefore boundaries at integer coordinates. # Simplest case: one cell - (((0.5, 0.5),), 1, 1, ((0, 0), (1, 0), (1, 1), (0, 1))), - # Various 2x2 grid cases + (((0.5, 0.5),), 1, 1, Polygon(((0, 0), (1, 0), (1, 1), (0, 1)))), + # Various 2x2 grid cases. + # Diagonally-touching cells only share a corner, so Shapely 2.x returns + # a MultiPolygon rather than the self-touching Polygon of Shapely 1.x. ( ((0.5, 0.5), (1.5, 1.5)), 1, 1, - ((0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (1, 1), (0, 1)), + MultiPolygon( + [ + Polygon(((1, 0), (0, 0), (0, 1), (1, 1))), + Polygon(((1, 2), (2, 2), (2, 1), (1, 1))), + ] + ), ), ( ((1.5, 0.5), (0.5, 1.5)), 1, 1, - ((1, 0), (2, 0), (2, 1), (1, 1), (1, 2), (0, 2), (0, 1), (1, 1)), + MultiPolygon( + [ + Polygon(((2, 1), (2, 0), (1, 0), (1, 1))), + Polygon(((0, 1), (0, 2), (1, 2), (1, 1))), + ] + ), ), ( ((0.5, 0.5), (0.5, 1.5), (1.5, 1.5)), 1, 1, - ((0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)), + Polygon(((0, 0), (1, 0), (1, 1), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1))), ), ( ((0.5, 0.5), (0.5, 1.5), (1.5, 0.5), (1.5, 1.5)), 1, 1, - ((0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1)), + Polygon(((0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (1, 2), (0, 2), (0, 1))), ), # Case on a 3x4 grid # @@ -68,6 +80,8 @@ def test_geojson_feature(thing, kwargs, expected): # XX # X # X + # The bottom-left cell touches the rest of the shape only at a corner, + # so the result is a MultiPolygon in Shapely 2.x. ( ( (0.5, 0.5), @@ -80,30 +94,34 @@ def test_geojson_feature(thing, kwargs, expected): ), 1, 1, - ( - (0, 0), - (1, 0), - (1, 1), - (2, 1), - (2, 2), - (3, 2), - (3, 3), - (3, 4), - (2, 4), - (1, 4), - (0, 4), - (0, 3), - (1, 3), - (1, 2), - (1, 1), - (0, 1), + MultiPolygon( + [ + Polygon(((0, 0), (0, 1), (1, 1), (1, 0))), + Polygon( + ( + (1, 2), + (1, 3), + (0, 3), + (0, 4), + (1, 4), + (2, 4), + (3, 4), + (3, 3), + (3, 2), + (2, 2), + (2, 1), + (1, 1), + ) + ), + ] ), ), ), ) def test_outline(centres, height, width, expected): ol = outline_cell_rect(centres, height, width) - assert ol.equals(Polygon(expected)) + assert isinstance(ol, (Polygon, MultiPolygon)) + assert ol.equals(expected) @pytest.mark.parametrize( @@ -119,7 +137,7 @@ def test_outline(centres, height, width, expected): ), ) def test_WKT_point_to_lonlat(text, result): - if type(result) == tuple: + if isinstance(result, tuple): assert WKT_point_to_lonlat(text) == result else: with pytest.raises(result): diff --git a/ce/tests/conftest.py b/ce/tests/conftest.py index c83ec623..b13b0df7 100644 --- a/ce/tests/conftest.py +++ b/ce/tests/conftest.py @@ -30,6 +30,13 @@ from ce import get_app +# Filesystem root where /testdata/ database paths are stored locally. +# Override with TESTDATA_PATH env var if files live elsewhere. +_default_testdata = "/storage/data/projects/comp_support/daccs/test-data" +if not os.path.exists(_default_testdata): + _default_testdata = os.path.join(os.path.dirname(__file__), "data") +_TESTDATA_LOCAL = os.getenv("TESTDATA_PATH", _default_testdata) + def resource_filename(package, path): return str(files(import_module(package)) / path) @@ -239,6 +246,8 @@ def make_data_file( "ce", "tests/data/{}".format(filename), ) + elif filename.startswith("/testdata/"): + filename = os.path.join(_TESTDATA_LOCAL, filename[len("/testdata/") :]) return DataFile( filename=filename, unique_id=unique_id, @@ -277,7 +286,7 @@ def make_data_file( ) df_5_monthly_online = make_data_file( unique_id="tasmax_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test", - filename="/storage/data/projects/comp_support/daccs/test-data/tasmax_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", + filename="/testdata/tasmax_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", run=run3, ) df_5_seasonal = make_data_file( @@ -294,7 +303,7 @@ def make_data_file( ) df_6_monthly_online = make_data_file( unique_id="tasmin_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test", - filename="/storage/data/projects/comp_support/daccs/test-data/tasmin_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", + filename="/testdata/tasmin_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", run=run3, ) df_6_seasonal = make_data_file( @@ -1193,7 +1202,18 @@ def polygon( @pytest.fixture def mock_thredds_url_root(monkeypatch): - monkeypatch.setenv( - "THREDDS_URL_ROOT", - "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/dodsC/datasets/", - ) + _thredds_base = "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/dodsC/datasets/testdata" + + def _apply_thredds_root(filename): + rel = ( + filename[len(_TESTDATA_LOCAL) :].lstrip("/") + if filename.startswith(_TESTDATA_LOCAL) + else filename.lstrip("/") + ) + return f"{_thredds_base}/{rel}" + + import importlib + + stats_module = importlib.import_module("ce.api.stats") + monkeypatch.setenv("THREDDS_URL_ROOT", f"{_thredds_base}/") + monkeypatch.setattr(stats_module, "apply_thredds_root", _apply_thredds_root) diff --git a/ce/tests/test_geo.py b/ce/tests/test_geo.py index 76ead316..0872c765 100644 --- a/ce/tests/test_geo.py +++ b/ce/tests/test_geo.py @@ -136,12 +136,12 @@ def test_make_mask_grid_key(ncobject, polygon): [ ( "ce/tests/data/tasmin_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230.nc", - "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/dodsC/datasets/storage/data/projects/comp_support/daccs/test-data/tasmin_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", + "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/fileServer/datasets/testdata/tasmin_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", "tasmin", ), ( "ce/tests/data/tasmax_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230.nc", - "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/dodsC/datasets/storage/data/projects/comp_support/daccs/test-data/tasmax_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", + "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/fileServer/datasets/testdata/tasmax_mClim_BNU-ESM_historical_r1i1p1_19650101-19701230_test.nc", "tasmax", ), ], diff --git a/ce/tests/test_util.py b/ce/tests/test_util.py index 57a0c004..d64552a8 100644 --- a/ce/tests/test_util.py +++ b/ce/tests/test_util.py @@ -98,7 +98,7 @@ def test_mean_datetime(input_, output): [ ( "/storage/data/projects/comp_support/daccs/test-data/fdd_seasonal_CanESM2_rcp85_r1i1p1_1951-2100.nc", - "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/dodsC/datasets/storage/data/projects/comp_support/daccs/test-data/fdd_seasonal_CanESM2_rcp85_r1i1p1_1951-2100.nc", + "https://marble-dev01.pcic.uvic.ca/twitcher/ows/proxy/thredds/dodsC/datasets/testdata/fdd_seasonal_CanESM2_rcp85_r1i1p1_1951-2100.nc", ) ], ) diff --git a/ce/views.py b/ce/views.py index b626f9b7..7e3a7a31 100644 --- a/ce/views.py +++ b/ce/views.py @@ -1,11 +1,34 @@ +from flask import jsonify, request from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import text import ce.api +### +# TODO: For consideration, the /api url prefix could probably be dropped entirely and controlled by the +# deployment behaviour. +### + def add_routes(app): db = SQLAlchemy(app) + @app.route("/api/readyz") + def readyz(): + status = {"status": "ok"} + http_status = 200 + if request.args.get("verbose", "").lower() in ("1", "true", "yes"): + try: + db.session.execute(text("SELECT 1")) + status["db"] = "ok" + except Exception as e: + status["status"] = "error" + status["db"] = str(e) + http_status = 503 + response = jsonify(status) + response.cache_control.no_store = True + return response, http_status + @app.route("/api/", methods=["GET", "POST"]) def api_request(*args, **kwargs): return ce.api.call(db.session, *args, **kwargs) @@ -30,6 +53,7 @@ def health_item_request(*args, **kwargs): @app.after_request def add_header(response): - response.cache_control.public = True - response.cache_control.max_age = 86400 + if request.endpoint != "readyz": + response.cache_control.public = True + response.cache_control.max_age = 86400 return response diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod index a301872d..f4ddf4c3 100644 --- a/docker/Dockerfile.prod +++ b/docker/Dockerfile.prod @@ -7,6 +7,7 @@ RUN apt-get update && \ libgeos-dev \ libffi-dev \ pipx \ + curl \ && rm -rf /var/lib/apt/lists/* ADD . /app @@ -18,6 +19,9 @@ RUN poetry install EXPOSE 8000 +HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ + CMD curl -f http://localhost:8000/api/readyz || exit 1 + # gunicorn.conf is set up so that one can tune gunicorn settings when # running the container by setting environment an variable # GUNICORN_[setting], where setting is any of the parameters in this diff --git a/poetry.lock b/poetry.lock index 4599457c..c5854fff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.0 and should not be changed by hand. [[package]] name = "affine" @@ -1394,53 +1394,73 @@ files = [ [[package]] name = "rasterio" -version = "1.3.9" +version = "1.4.4" description = "Fast and direct raster I/O for use with Numpy and SciPy" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "rasterio-1.3.9-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:04247da9f4002587ac2bec967c3a72f63fc0e6654101c06850bae3d8131b700d"}, - {file = "rasterio-1.3.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c9edce37b70f4cd4be5d3f5d314877e3130aeebb612120405cd28f83fe200865"}, - {file = "rasterio-1.3.9-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:fd6a850a37840ba590ddcf7ff90ba007b1e231b04434d8b4ac5ce0f746ada91a"}, - {file = "rasterio-1.3.9-cp310-cp310-win_amd64.whl", hash = "sha256:0c83156a44f8fda11876ff9f2ff1b602d7e7434447f7d621353f2929cefb1bf1"}, - {file = "rasterio-1.3.9-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:0172dbd80bd9adc105ec2c9bd207dbd5519ea06b438a4d965c6290ae8ed6ff9f"}, - {file = "rasterio-1.3.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ea5b42597d85868ee88c750cc33f2ae729e1b5e3fe28f99071f39e1417bf1c0"}, - {file = "rasterio-1.3.9-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:be9b343bd08245df22115775dc9513c912afb4134d832662fa165d70cb805c34"}, - {file = "rasterio-1.3.9-cp311-cp311-win_amd64.whl", hash = "sha256:06d53e2e0885f039f960beb7c861400b92ea3e0e5abc2c67483fb56b1e5cbc13"}, - {file = "rasterio-1.3.9-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:a34bb9eef67b7896e2dfb39e10ba6372f9894226fb790bd7a46f5748f205b7d8"}, - {file = "rasterio-1.3.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:67b144b9678f9ad4cf5f2c3f455cbc6a7166c0523179249cee8f2e2c57d76c5b"}, - {file = "rasterio-1.3.9-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:99b72fccb702a921f43e56a4507b4cafe2a9196b478b993b98e82ec6851916d7"}, - {file = "rasterio-1.3.9-cp312-cp312-win_amd64.whl", hash = "sha256:6777fad3c31eb3e5da0ccaa28a032ad07c20d003bcd14f8bc13e16ca2f62348c"}, - {file = "rasterio-1.3.9-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:55bb1a2701dd67c1952b261a2ffbabd947a435d4457f13c25092a32ab7a4b36e"}, - {file = "rasterio-1.3.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:726d8e8884359c34f672312171310052d5483af550ef00fb4f2562cc022a6f5a"}, - {file = "rasterio-1.3.9-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:f65879415df188fdc9388ccf2ee01e0659abae370d12518a17b60151e7d04efe"}, - {file = "rasterio-1.3.9-cp38-cp38-win_amd64.whl", hash = "sha256:89771b70ee722c4cc808e2a6139b367bef1a736ecd497b311b3515d78a5d16bc"}, - {file = "rasterio-1.3.9-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:14df8413c030b04e54d478d6ecec4e5958b46585c3cb970bf0dc19b4831146c8"}, - {file = "rasterio-1.3.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:911e54e0bb97c456a045f6d8e24b00aeb055a235d2aa7c2c1f9128f4c6c7a52d"}, - {file = "rasterio-1.3.9-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:01e428ee5ba8444f5cb4fff56225acb1ab9bc8b77209b6e4198e04565d8a8509"}, - {file = "rasterio-1.3.9-cp39-cp39-win_amd64.whl", hash = "sha256:26d9aea05b035927647bb32cc04fad0a68346a2f5186224dc1c2555c33515183"}, - {file = "rasterio-1.3.9.tar.gz", hash = "sha256:fc6d0d290492fa1a5068711cfebb21cc936968891b7ed9da0690c8a7388885c5"}, + {file = "rasterio-1.4.4-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:35401e84d4d0b239bd62b33d4ee68d7bb13b47c3b41078f4aad7ad7964e61c73"}, + {file = "rasterio-1.4.4-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:1f17fc9608b6b6666894a04e0118d3329e831a6347bc3650584d247a9d476fdd"}, + {file = "rasterio-1.4.4-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:1f0edb8cb30ff8f5be341583f69c115b7c36ad52bbbe7582345d32af115bc6b3"}, + {file = "rasterio-1.4.4-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:5197da0e3dd09907bdb343717a49e8fb5229ffdbff0e583b874959ec41fa9558"}, + {file = "rasterio-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:15109134c7b4770e6aeb8d45dc52c2603824805ba734323268a44f5a81756a7a"}, + {file = "rasterio-1.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:b8eea428b5f0c78a963f6003a19b60777df83a0aba8c28231d65431e32ac160e"}, + {file = "rasterio-1.4.4-cp311-cp311-macosx_15_0_x86_64.whl", hash = "sha256:1cc0ea5aa0d22f5f349aa221674481de689b7b3a99607ce6bb58a29e5be54d17"}, + {file = "rasterio-1.4.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:7eb25b23666b29dadfc49a59206cead62c99190584b61771bba0e95f7da06801"}, + {file = "rasterio-1.4.4-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e24b7b8c2df801dde2a1dffb44c58902bd76b5cab740dc11de4ff9963992a71a"}, + {file = "rasterio-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:0718630f607be2f5742d8e4b34b434746fd788a192d77eefc9bb924399fea802"}, + {file = "rasterio-1.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:0308ff4762ae9eb40a991f12d758626b59af4376b13675480391dd7295d17bbf"}, + {file = "rasterio-1.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3c4f0cbd188f893011f2a0a6dc2852b3892799b3a0d79eddf92f2b115ec7ed7"}, + {file = "rasterio-1.4.4-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:6fce26090b9f509eab337228420145947c491a13628965410f25bc3e6e05cf75"}, + {file = "rasterio-1.4.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c1c722da390dc264aeccdc0dc200ca37923875d910ca4cd5bec0fec351bb818e"}, + {file = "rasterio-1.4.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98b6dfb8282b2a54b9d75c3dc8d2520a69bbc66916c7d43de8e0bbf6e0240ca1"}, + {file = "rasterio-1.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:9513f4c7a6d93b45098f8dff2421fa9516604e3bfbf35aa144484a88d36a321f"}, + {file = "rasterio-1.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:60b49a482e0f12f12ce9d2cc3090add02f89f3d422e85f2cffaa9207adb83c04"}, + {file = "rasterio-1.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:df26c96aa81ffbd0b33189680859211eadf9950123c21579f84de73bb0f91d81"}, + {file = "rasterio-1.4.4-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:b3af0ecc922a80f3755516629f7948e37bade9077b5f5c12a3869a5e7f01619b"}, + {file = "rasterio-1.4.4-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7ce3b0f9a22e95a27790087908753973644d7c3877d495ec9bd6e04a25233ca4"}, + {file = "rasterio-1.4.4-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c072450caa96428b1218b030500bb908fd6f09bc013a88969ff81a124b6a112a"}, + {file = "rasterio-1.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ee92ef10c0ba89f45f9c2b40fca9f971f357385f04ee9b716fb09cbd9ce20c"}, + {file = "rasterio-1.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:65c10afe64b5e488185aaff0b659e08eda22c89285b54a3e433b80e6c6621770"}, + {file = "rasterio-1.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:18c2c1130e789dc2771d0aa5ec4b56d5b8a0097c648ccb94882d5ff3ab55c928"}, + {file = "rasterio-1.4.4-cp313-cp313t-macosx_15_0_x86_64.whl", hash = "sha256:2d1654b7ffa6f3dde42c5fd27159ae45148c11e352de26f12fe7313a3236aeed"}, + {file = "rasterio-1.4.4-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c4022cbddb659856e120603b12233cec8913ae760fff220657ce888c3c6b9f9d"}, + {file = "rasterio-1.4.4-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:96b88880551a07b7a3b50439483cefbd9af91a09e19ff2b736815994e5671314"}, + {file = "rasterio-1.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:def75d486d0ab8f306f918a913c425ed57159495518c54efe8e18d5164d37d90"}, + {file = "rasterio-1.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:770b7e86f6c565e6f9cf30f6fa4479a5a2bab4e10ff44fe7acfd518ca4a71d1b"}, + {file = "rasterio-1.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:019693f14a83ae9225cb57c16e466901d0e6284962dcf13a9f4bb1175b979011"}, + {file = "rasterio-1.4.4-cp314-cp314-macosx_15_0_x86_64.whl", hash = "sha256:87d7c3e97e3b40c9041d1602e2dcb4fc2d88abe6c645fccb4939dec297a91cf8"}, + {file = "rasterio-1.4.4-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:a2401e4c43a31c7382154d4042b60a63b9bca5886802983c5c9362cdc5b09548"}, + {file = "rasterio-1.4.4-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6c4287d8934d953f7870b8e2a1df1096fbf47eba39ad0f777a31ea500f4e5010"}, + {file = "rasterio-1.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:c3ba1871549221140661227dd4fa1f9a472ded4a6d2f2c2e367b0648bb15b99d"}, + {file = "rasterio-1.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:7c9d7dc824cb8d222808be153643cd4e65ea3e1f66019ada1ccd630221edfe30"}, + {file = "rasterio-1.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:98e17bded830a59992d9f8f8d9f227ce1c4be0694930afcc4360358f5cb1a5db"}, + {file = "rasterio-1.4.4-cp314-cp314t-macosx_15_0_x86_64.whl", hash = "sha256:56134ca203f952855e60774b06672033cf65057eb9810fcc5c1a75f1921053a3"}, + {file = "rasterio-1.4.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:52edde65515b33fe4314c8a44a9ee2fc00b550deed6d56e1a8d085d42bbca3e6"}, + {file = "rasterio-1.4.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d61d3f2c171c64050bd75e54a5d964ff7f165b3f5d2b92c9ee09b9716aa1b8bf"}, + {file = "rasterio-1.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:40137fe512c0d6e96c0167a0ae4e56d82c488f244163c45494b7392e51c844de"}, + {file = "rasterio-1.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:29ec3a794454b5bb255c9c0374cc380030a8a1e295c81eee7feb036802d2a9e3"}, + {file = "rasterio-1.4.4.tar.gz", hash = "sha256:c95424e2c7f009b8f7df1095d645c52895cd332c0c2e1b4c2e073ea28b930320"}, ] [package.dependencies] affine = "*" attrs = "*" certifi = "*" -click = ">=4.0" +click = ">=4.0,<8.2.dev0 || >=8.3.dev0" click-plugins = "*" cligj = ">=0.5" -numpy = "*" -setuptools = "*" -snuggs = ">=1.4.1" +numpy = ">=1.24" +pyparsing = "*" [package.extras] -all = ["boto3 (>=1.2.4)", "ghp-import", "hypothesis", "ipython (>=2.0)", "matplotlib", "numpydoc", "packaging", "pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "shapely ; python_version < \"3.12\"", "sphinx", "sphinx-rtd-theme"] -docs = ["ghp-import", "numpydoc", "sphinx", "sphinx-rtd-theme"] +all = ["boto3 (>=1.2.4)", "fsspec", "ghp-import", "hypothesis", "ipython (>=2.0)", "matplotlib", "numpydoc", "packaging", "pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "shapely", "sphinx", "sphinx-click", "sphinx-rtd-theme"] +docs = ["ghp-import", "numpydoc", "sphinx", "sphinx-click", "sphinx-rtd-theme"] ipython = ["ipython (>=2.0)"] plot = ["matplotlib"] s3 = ["boto3 (>=1.2.4)"] -test = ["boto3 (>=1.2.4)", "hypothesis", "packaging", "pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "shapely ; python_version < \"3.12\""] +test = ["boto3 (>=1.2.4)", "fsspec", "hypothesis", "packaging", "pytest (>=2.8.2)", "pytest-cov (>=2.2.0)", "shapely"] [[package]] name = "requests" @@ -1504,61 +1524,77 @@ type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.deve [[package]] name = "shapely" -version = "2.0.2" +version = "2.1.2" description = "Manipulation and analysis of geometric objects" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6ca8cffbe84ddde8f52b297b53f8e0687bd31141abb2c373fd8a9f032df415d6"}, - {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:baa14fc27771e180c06b499a0a7ba697c7988c7b2b6cba9a929a19a4d2762de3"}, - {file = "shapely-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36480e32c434d168cdf2f5e9862c84aaf4d714a43a8465ae3ce8ff327f0affb7"}, - {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef753200cbffd4f652efb2c528c5474e5a14341a473994d90ad0606522a46a2"}, - {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9a41ff4323fc9d6257759c26eb1cf3a61ebc7e611e024e6091f42977303fd3a"}, - {file = "shapely-2.0.2-cp310-cp310-win32.whl", hash = "sha256:72b5997272ae8c25f0fd5b3b967b3237e87fab7978b8d6cd5fa748770f0c5d68"}, - {file = "shapely-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:34eac2337cbd67650248761b140d2535855d21b969d76d76123317882d3a0c1a"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f"}, - {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89"}, - {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2"}, - {file = "shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd"}, - {file = "shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8"}, - {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b"}, - {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4"}, - {file = "shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611"}, - {file = "shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033"}, - {file = "shapely-2.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ed1e99702125e7baccf401830a3b94d810d5c70b329b765fe93451fe14cf565b"}, - {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7d897e6bdc6bc64f7f65155dbbb30e49acaabbd0d9266b9b4041f87d6e52b3a"}, - {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0521d76d1e8af01e712db71da9096b484f081e539d4f4a8c97342e7971d5e1b4"}, - {file = "shapely-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:5324be299d4c533ecfcfd43424dfd12f9428fd6f12cda38a4316da001d6ef0ea"}, - {file = "shapely-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:78128357a0cee573257a0c2c388d4b7bf13cb7dbe5b3fe5d26d45ebbe2a39e25"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87dc2be34ac3a3a4a319b963c507ac06682978a5e6c93d71917618b14f13066e"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42997ac806e4583dad51c80a32d38570fd9a3d4778f5e2c98f9090aa7db0fe91"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ccfd5fa10a37e67dbafc601c1ddbcbbfef70d34c3f6b0efc866ddbdb55893a6c"}, - {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7c95d3379ae3abb74058938a9fcbc478c6b2e28d20dace38f8b5c587dde90aa"}, - {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a21353d28209fb0d8cc083e08ca53c52666e0d8a1f9bbe23b6063967d89ed24"}, - {file = "shapely-2.0.2-cp38-cp38-win32.whl", hash = "sha256:03e63a99dfe6bd3beb8d5f41ec2086585bb969991d603f9aeac335ad396a06d4"}, - {file = "shapely-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:c6fd29fbd9cd76350bd5cc14c49de394a31770aed02d74203e23b928f3d2f1aa"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f217d28ecb48e593beae20a0082a95bd9898d82d14b8fcb497edf6bff9a44d7"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:394e5085b49334fd5b94fa89c086edfb39c3ecab7f669e8b2a4298b9d523b3a5"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd3ad17b64466a033848c26cb5b509625c87d07dcf39a1541461cacdb8f7e91c"}, - {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d41a116fcad58048d7143ddb01285e1a8780df6dc1f56c3b1e1b7f12ed296651"}, - {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dea9a0651333cf96ef5bb2035044e3ad6a54f87d90e50fe4c2636debf1b77abc"}, - {file = "shapely-2.0.2-cp39-cp39-win32.whl", hash = "sha256:b8eb0a92f7b8c74f9d8fdd1b40d395113f59bd8132ca1348ebcc1f5aece94b96"}, - {file = "shapely-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:794affd80ca0f2c536fc948a3afa90bd8fb61ebe37fe873483ae818e7f21def4"}, - {file = "shapely-2.0.2.tar.gz", hash = "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7"}, + {file = "shapely-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ae48c236c0324b4e139bea88a306a04ca630f49be66741b340729d380d8f52f"}, + {file = "shapely-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eba6710407f1daa8e7602c347dfc94adc02205ec27ed956346190d66579eb9ea"}, + {file = "shapely-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef4a456cc8b7b3d50ccec29642aa4aeda959e9da2fe9540a92754770d5f0cf1f"}, + {file = "shapely-2.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e38a190442aacc67ff9f75ce60aec04893041f16f97d242209106d502486a142"}, + {file = "shapely-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:40d784101f5d06a1fd30b55fc11ea58a61be23f930d934d86f19a180909908a4"}, + {file = "shapely-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f6f6cd5819c50d9bcf921882784586aab34a4bd53e7553e175dece6db513a6f0"}, + {file = "shapely-2.1.2-cp310-cp310-win32.whl", hash = "sha256:fe9627c39c59e553c90f5bc3128252cb85dc3b3be8189710666d2f8bc3a5503e"}, + {file = "shapely-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:1d0bfb4b8f661b3b4ec3565fa36c340bfb1cda82087199711f86a88647d26b2f"}, + {file = "shapely-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91121757b0a36c9aac3427a651a7e6567110a4a67c97edf04f8d55d4765f6618"}, + {file = "shapely-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a9c722ba774cf50b5d4541242b4cce05aafd44a015290c82ba8a16931ff63d"}, + {file = "shapely-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cc4f7397459b12c0b196c9efe1f9d7e92463cbba142632b4cc6d8bbbbd3e2b09"}, + {file = "shapely-2.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:136ab87b17e733e22f0961504d05e77e7be8c9b5a8184f685b4a91a84efe3c26"}, + {file = "shapely-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:16c5d0fc45d3aa0a69074979f4f1928ca2734fb2e0dde8af9611e134e46774e7"}, + {file = "shapely-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ddc759f72b5b2b0f54a7e7cde44acef680a55019eb52ac63a7af2cf17cb9cd2"}, + {file = "shapely-2.1.2-cp311-cp311-win32.whl", hash = "sha256:2fa78b49485391224755a856ed3b3bd91c8455f6121fee0db0e71cefb07d0ef6"}, + {file = "shapely-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:c64d5c97b2f47e3cd9b712eaced3b061f2b71234b3fc263e0fcf7d889c6559dc"}, + {file = "shapely-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe2533caae6a91a543dec62e8360fe86ffcdc42a7c55f9dfd0128a977a896b94"}, + {file = "shapely-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4d1333cc0bc94381d6d4308d2e4e008e0bd128bdcff5573199742ee3634359"}, + {file = "shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3"}, + {file = "shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b"}, + {file = "shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc"}, + {file = "shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d"}, + {file = "shapely-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9111274b88e4d7b54a95218e243282709b330ef52b7b86bc6aaf4f805306f454"}, + {file = "shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179"}, + {file = "shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8"}, + {file = "shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a"}, + {file = "shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e"}, + {file = "shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6"}, + {file = "shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af"}, + {file = "shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd"}, + {file = "shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350"}, + {file = "shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715"}, + {file = "shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40"}, + {file = "shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b"}, + {file = "shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801"}, + {file = "shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0"}, + {file = "shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c"}, + {file = "shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99"}, + {file = "shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf"}, + {file = "shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c"}, + {file = "shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223"}, + {file = "shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c"}, + {file = "shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df"}, + {file = "shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf"}, + {file = "shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4"}, + {file = "shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc"}, + {file = "shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566"}, + {file = "shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c"}, + {file = "shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a"}, + {file = "shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076"}, + {file = "shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1"}, + {file = "shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0"}, + {file = "shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26"}, + {file = "shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0"}, + {file = "shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735"}, + {file = "shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9"}, + {file = "shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9"}, ] [package.dependencies] -numpy = ">=1.14" +numpy = ">=1.21" [package.extras] docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] -test = ["pytest", "pytest-cov"] +test = ["pytest", "pytest-cov", "scipy-doctest"] [[package]] name = "six" @@ -1597,25 +1633,6 @@ files = [ {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, ] -[[package]] -name = "snuggs" -version = "1.4.7" -description = "Snuggs are s-expressions for Numpy" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "snuggs-1.4.7-py3-none-any.whl", hash = "sha256:988dde5d4db88e9d71c99457404773dabcc7a1c45971bfbe81900999942d9f07"}, - {file = "snuggs-1.4.7.tar.gz", hash = "sha256:501cf113fe3892e14e2fee76da5cd0606b7e149c411c271898e6259ebde2617b"}, -] - -[package.dependencies] -numpy = "*" -pyparsing = ">=2.1.6" - -[package.extras] -test = ["hypothesis", "pytest"] - [[package]] name = "sphinx" version = "8.2.3" @@ -1991,4 +2008,4 @@ docs = ["m2r2", "poethepoet", "sphinx", "sphinx-pyproject"] [metadata] lock-version = "2.1" python-versions = ">=3.11,<4" -content-hash = "b566aafb846094c272c7b83e79ce6d6cf9eb730ec7f56c4f3050078658b961ef" +content-hash = "e2202a240c953b266e753db4a5ae2d833b0e15cd35d2effe46e90d8b68bae988" diff --git a/pyproject.toml b/pyproject.toml index ad89e3c8..6f943f7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,13 +31,12 @@ keywords = [ ] dependencies = [ "Flask>=3.1.1,<4", - "Flask-SQLAlchemy>=3.0.5,<4", - "Flask-Cors", + "Flask-Cors>=4.0.0,<5", "shapely>2.0,<3", "numpy<2", "GDAL>=3.3.0,<3.4", - "rasterio~=1.3", + "rasterio>=1.4,<2", "SQLAlchemy>=2.0.40,<3.0.0", "contexttimer==0.3.3", "Pint<1", @@ -84,3 +83,6 @@ target-version = ["py311", "py312"] [tool.poe.tasks] document = "sphinx-build doc/source doc/build/html" pdf = "make latexpdf -C doc" + +[solver] +min-release-age = 7 \ No newline at end of file