diff --git a/containers/backend/Dockerfile b/containers/backend/Dockerfile index 242746ca..6e9e9ce7 100644 --- a/containers/backend/Dockerfile +++ b/containers/backend/Dockerfile @@ -3,10 +3,13 @@ LABEL maintainer="frederick.thomas@ouce.ox.ac.uk" WORKDIR /code +# Install project with dependencies - these can be cached as docker layers if unchanged COPY pyproject.toml . - RUN python -m pip install . + +# Copy and install latest package code COPY ./backend /code/backend +RUN python -m pip install --upgrade --compile . CMD ["uvicorn", "backend.app.main:app", "--host", "0.0.0.0", "--port", "8888"] diff --git a/containers/backend/backend/app/routers/tiles.py b/containers/backend/backend/app/routers/tiles.py index 53a08cf1..4ff2692e 100644 --- a/containers/backend/backend/app/routers/tiles.py +++ b/containers/backend/backend/app/routers/tiles.py @@ -16,6 +16,7 @@ from sqlalchemy.exc import NoResultFound from sqlalchemy.orm import Session from geoalchemy2 import functions +from terracotta.exceptions import DatasetNotFoundError from app import schemas @@ -93,19 +94,29 @@ def _domain_exists(db: Session, domain: str) -> bool: return res == 1 -def _tile_db_from_domain(db: Session, domain: str) -> str: +def _tile_db_from_domain(domain: str) -> str: """ Query the name of the mysql database within-which the tiles reside using the first value of the path keys (which links to hazard-type in the UI) See: frontend/src/config/hazards/domains.ts """ - source_db = db.execute( - # Should only return one entry - select(models.RasterTileSource.source_db).where( - models.RasterTileSource.domain == domain - ) - ).scalar_one() - return source_db + # TODO try conventional or configured mappin again - hot fix for db pool exhaustion + domain_to_db = { + "buildings": "terracotta_buildings", + "coastal": "terracotta_aqueduct", + "cyclone_iris": "terracotta_iris", + "cyclone": "terracotta_cyclone", + "dem": "terracotta_dem", + "drought": "terracotta_drought", + "earthquake": "terracotta_gem_earthquake", + "extreme_heat": "terracotta_extreme_heat", + "fluvial": "terracotta_aqueduct", + "land_cover": "terracotta_land_cover", + "nature": "terracotta_exposure_nature", + "population": "terracotta_jrc_pop", + "traveltime_to_healthcare": "terracotta_traveltime_to_healthcare", + } + return domain_to_db[domain] def _source_options(source_db: str, domain: str = None) -> List[dict]: @@ -307,7 +318,6 @@ async def get_tile( colormap: Union[str, None] = None, stretch_range: Union[str, None] = None, explicit_color_map: Union[str, None] = None, - db: Session = Depends(get_db), ): """ Serves XYZ Raster Tiles with the given colormap / stretch range or explicit color map for categorical data. @@ -346,9 +356,9 @@ async def get_tile( parsed_keys = _parse_keys(keys) domain = _domain_from_keys(parsed_keys) try: - source_db = _tile_db_from_domain(db, domain) + source_db = _tile_db_from_domain(domain) logger.debug("source DB for tile path: %s", source_db) - except NoResultFound: + except KeyError: raise HTTPException( status_code=404, detail=f"No source database for the given domain {domain} could be found", @@ -393,6 +403,12 @@ async def get_tile( status_code=400, detail=f"source database {source_db} does not exist in tiles metastore", ) + except DatasetNotFoundError as err: + handle_exception(logger, err) + raise HTTPException( + status_code=400, + detail=f"layer with keys {parsed_keys} not found in {source_db} in tiles metastore", + ) except Exception as err: handle_exception(logger, err) raise HTTPException(status_code=500) diff --git a/containers/backend/backend/app/schemas.py b/containers/backend/backend/app/schemas.py index 9b20a5f6..fcf30b91 100644 --- a/containers/backend/backend/app/schemas.py +++ b/containers/backend/backend/app/schemas.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import Generic, List, Optional, TypeVar +from typing import Generic, List, Optional, TypeVar, Union from pydantic import BaseModel, ConfigDict, conint, validator @@ -7,7 +7,7 @@ class FeatureBase(BaseModel): id: int string_id: str layer: str - sublayer: str | None + sublayer: Optional[str] = None properties: dict @@ -33,7 +33,7 @@ class DataParameters(BaseModel): class ExpectedDamagesDimensions(DataDimensions): hazard: str rcp: str - epoch: str + epoch: Union[str, int] protection_standard: int @@ -54,7 +54,7 @@ class ExpectedDamage(ExpectedDamagesDimensions, ExpectedDamagesVariables): class ReturnPeriodDamagesDimensions(DataDimensions): hazard: str rcp: str - epoch: str + epoch: Union[str, int] rp: int diff --git a/docker-compose-dev.yaml b/docker-compose-dev.yaml index 12ac664d..b9a6fc88 100644 --- a/docker-compose-dev.yaml +++ b/docker-compose-dev.yaml @@ -46,7 +46,7 @@ services: backend: build: ./containers/backend - image: ghcr.io/nismod/gri-backend:1.3 + image: ghcr.io/nismod/gri-backend:1.4.2 volumes: - ./etl/raster/cog/:/data/ - ./containers/backend/backend/:/code/backend/ diff --git a/docker-compose-prod-build.yaml b/docker-compose-prod-build.yaml index 3100120a..885a6450 100644 --- a/docker-compose-prod-build.yaml +++ b/docker-compose-prod-build.yaml @@ -1,6 +1,6 @@ services: backend: - image: ghcr.io/nismod/gri-backend:1.3 + image: ghcr.io/nismod/gri-backend:1.4.2 build: ./containers/backend vector-tileserver: diff --git a/docker-compose-prod-deploy.yaml b/docker-compose-prod-deploy.yaml index fab79aab..91bbb3c5 100644 --- a/docker-compose-prod-deploy.yaml +++ b/docker-compose-prod-deploy.yaml @@ -45,7 +45,7 @@ services: mem_limit: "250M" backend: - image: ghcr.io/nismod/gri-backend:1.3 + image: ghcr.io/nismod/gri-backend:1.4.2 restart: always volumes: - ./tileserver/raster/data:/data