Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add FWI API route #84

Merged
merged 27 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6685746
fix: Bump setup-python action to v4
jsakv Jul 12, 2024
def918f
chore: Update dependencies
jsakv Jul 12, 2024
06deec1
fix: Update import statements
jsakv Jul 12, 2024
c0604be
fix: Update lock file
jsakv Jul 12, 2024
ffbff86
chore: Remove build directory
jsakv Jul 16, 2024
16ec207
feat: Add pyrorisks conda-lock for rasterio
jsakv Jul 16, 2024
40aa2aa
chore: Add conda to dockerfile for rasterio
jsakv Jul 16, 2024
99e07cb
chore: Update docker-compose config
jsakv Jul 16, 2024
ba06faa
feat: Update app configuration
jsakv Jul 12, 2024
722bf03
style: Fix type hints
jsakv Jul 16, 2024
8d06a2c
refactor: Update API settings
jsakv Jul 16, 2024
5f5db5b
wip: Add score and query params
jsakv Jul 16, 2024
f37a31e
fix: Update import statements
jsakv Jul 18, 2024
c0d2c0a
chore: Update dependencies and configuration
jsakv Jul 18, 2024
4a5bd79
chore: Update FWI script workflow
jsakv Jul 18, 2024
2aa5e23
style: Update image
jsakv Jul 18, 2024
156a951
chore: Update doc-deployment action
jsakv Jul 23, 2024
1d5d5ae
chore: Update docker compose config
jsakv Jul 23, 2024
93890c5
feat: Add get_fwi helper
jsakv Jul 23, 2024
467df26
feat: Add app config
jsakv Jul 23, 2024
2ed70c7
feat: Update api schemas
jsakv Jul 23, 2024
6b7c5d2
feat: Add FWI route to api
jsakv Jul 23, 2024
056fee4
feat: Exclude risk route
jsakv Jul 23, 2024
992f6d2
fix: Fix type hints
jsakv Jul 23, 2024
f573897
style: Fix formatting
jsakv Jul 23, 2024
d6a6ea4
chore: Update dockerfile
jsakv Jul 23, 2024
5c6d985
chore: Add docker image push workflow
jsakv Jul 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/doc-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
with:
persist-credentials: false
- name: Set up Python 3.10.5
uses: actions/setup-python@v1
uses: actions/setup-python@v4
with:
python-version: 3.10.5
python-version: 3.10

- name: Install dependencies
run: poetry install
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: push
on:
push:
branches: [ main ]

env:
IMAGE_NAME: pyro-risks
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_LOGIN }}

jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build docker
run: docker compose build -t $DOCKERHUB_USER/$IMAGE_NAME:latest
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_LOGIN }}
password: ${{ secrets.DOCKERHUB_PW }}
- name: Push to hub
run: docker push $DOCKERHUB_USER/$IMAGE_NAME:latest
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push to container registry
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
docker tag $DOCKERHUB_USER/$IMAGE_NAME:latest $IMAGE_ID:latest
docker push $IMAGE_ID:latest
10 changes: 8 additions & 2 deletions .github/workflows/scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ jobs:
with:
python-version: 3.10.5

- uses: s-weigand/setup-conda@v1
with:
activate-conda: false
- run: conda install conda-lock
- run: conda-lock install -n pyro-risks pyrorisks.conda-lock.yml
- run: conda activate pyro-risks

- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: 1.8.1
virtualenvs-create: true
virtualenvs-in-project: true
virtualenvs-create: false

- name: Install dependencies
run: poetry install
Expand Down
37 changes: 30 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
FROM python:3.10-buster

WORKDIR /app

RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && \
/bin/bash /tmp/miniconda.sh -b -p /opt/conda && \
rm /tmp/miniconda.sh

ENV PATH=/opt/conda/bin:$PATH

RUN conda install -c conda-forge conda-lock

# Install and activate pyrorisks environment

COPY pyrorisks.conda-lock.yml pyrorisks.conda-lock.yml

RUN conda-lock install --name pyrorisks pyrorisks.conda-lock.yml && conda clean -a

ENV CONDA_DEFAULT_ENV=pyrorisks
ENV PATH /opt/conda/envs/${CONDA_DEFAULT_ENV}/bin:$PATH
RUN echo "conda activate ${CONDA_DEFAULT_ENV}" >> ~/.bashrc

# Install poetry
RUN pip install poetry==1.8.1

# Set environment variables for poetry
ENV POETRY_NO_INTERACTION=1 \
POETRY_VIRTUALENVS_IN_PROJECT=1 \
POETRY_VIRTUALENVS_CREATE=1 \
POETRY_VIRTUALENVS_CREATE=0 \
POETRY_CACHE_DIR=/tmp/poetry_cache \
VIRTUAL_ENV=/app/.venv \
PATH="/app/.venv/bin:$PATH"

WORKDIR /app
VIRTUAL_ENV=/opt/conda/envs/${CONDA_DEFAULT_ENV} \
PATH="/opt/conda/envs/${CONDA_DEFAULT_ENV}/bin:$PATH" \
PYTHONPATH="/opt/conda/envs/${CONDA_DEFAULT_ENV}/lib/python3.10/site-packages:${PYTHONPATH}"

COPY pyrorisks ./pyrorisks
COPY app ./app
COPY build ./build
COPY pyrorisks ./pyrorisks
COPY pyproject.toml poetry.lock README.md ./

# Install pyrorisks package in pyrorisks conda environment
RUN poetry install

CMD ["bash"]
28 changes: 28 additions & 0 deletions app/api/routes/fwi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (C) 2021-2022, Pyronear.

# This program is licensed under the Apache License version 2.
# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.

from typing import Dict, Any
from fastapi import APIRouter, Depends
from fastapi import HTTPException, status
from app.api.schemas import ScoreQueryParams, Score
from pyrorisks.platform_fwi.get_fwi_effis_score import get_fwi as _get_fwi


router = APIRouter()


@router.get(
path="/",
response_model=Score,
summary="Provide European Forest Fire Information System (EFFIS) Fire Weather Index (FWI) categories.",
)
async def get_fwi(query: ScoreQueryParams = Depends()) -> Dict[str, Any]:
results = _get_fwi(longitude=query.longitude, latitude=query.latitude, crs=query.crs)
if results is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Fire Weather Index (FWI) for longitude {query.longitude} and latitude {query.latitude} was not found.",
)
return results
21 changes: 20 additions & 1 deletion app/api/schemas.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2021-2022, Pyronear.
# Copyright (C) 2020-2024, Pyronear.

# This program is licensed under the Apache License version 2.
# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.
Expand All @@ -11,3 +11,22 @@ class RegionRisk(BaseModel):
geocode: str = Field(..., examples=["01"])
score: float = Field(..., gt=0, lt=1, examples=[0.5])
explainability: Optional[str] = Field(None, examples=["weather"])


class ScoreQueryParams(BaseModel):
longitude: float = Field(..., gt=-90.0, lt=90.0)
latitude: float = Field(..., gt=-180.0, lt=180.0)
crs: str = Field(
default="EPSG:4326",
examples=["EPSG:4326"],
description="Coordinate Reference System (CRS), Default to World Geodetic System CRS (EPSG:4326 / WGS84).",
)


class Score(BaseModel):
longitude: float = Field(..., gt=-90.0, lt=90.0, examples=[2.638828])
latitude: float = Field(..., gt=-180.0, lt=180.0, examples=[48.391842])
crs: str = Field(..., examples=["EPSG:4326"], description="Coordinate Reference System (CRS).")
score: str = Field(..., examples=["fwi"], description="Score name.")
value: float = Field(..., examples=[2, 1], description="Score value.")
date: str = Field(..., examples=["2024-01-01"], description="Date in %Y-%m-%d format")
21 changes: 0 additions & 21 deletions app/config.py

This file was deleted.

Empty file added app/core/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions app/core/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (C) 2021-2024, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0> for full license details.

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import field_validator
from typing import Optional

__all__ = ["settings"]


class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file="./../../.env")

PROJECT_NAME: str = "Pyrorisks"
PROJECT_DESCRIPTION: str = "API for wildfire risk estimation"
LOGO_URL: str = "https://pyronear.org/img/logo_letters.png"
VERSION: str = "0.1.0"
DEBUG: bool = False

@field_validator("DEBUG", mode="before")
@classmethod
def transform_debug(cls, value: str) -> bool:
return value != "False"

S3_BUCKET_NAME: Optional[str] = None
S3_ACCESS_KEY: Optional[str] = None
S3_SECRET_KEY: Optional[str] = None
S3_REGION: Optional[str] = None
S3_ENDPOINT_URL: Optional[str] = None


settings = Settings() # type: ignore[call-arg]
22 changes: 11 additions & 11 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
from fastapi import FastAPI, Request
from fastapi.openapi.utils import get_openapi

from app import config as cfg
from app.api.routes import risk
from app.core.config import settings
from app.api.routes import fwi


app = FastAPI(
title=cfg.PROJECT_NAME,
description=cfg.PROJECT_DESCRIPTION,
debug=cfg.DEBUG,
version=cfg.VERSION,
title=settings.PROJECT_NAME,
description=settings.PROJECT_DESCRIPTION,
debug=settings.DEBUG,
version=settings.VERSION,
)

# Routing
app.include_router(risk.router, prefix="/risk", tags=["risk"])
app.include_router(fwi.router, prefix="/fwi", tags=["fwi"])


# Middleware
Expand All @@ -37,12 +37,12 @@ def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=cfg.PROJECT_NAME,
version=cfg.VERSION,
description=cfg.PROJECT_DESCRIPTION,
title=settings.PROJECT_NAME,
version=settings.VERSION,
description=settings.PROJECT_DESCRIPTION,
routes=app.routes,
)
openapi_schema["info"]["x-logo"] = {"url": cfg.LOGO_URL}
openapi_schema["info"]["x-logo"] = {"url": settings.LOGO_URL}
app.openapi_schema = openapi_schema
return app.openapi_schema

Expand Down
Binary file not shown.
11 changes: 7 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.7'

services:
web:
build: .
Expand All @@ -9,5 +7,10 @@ services:
ports:
- ${PORT}:8000
environment:
- CDS_UID=${CDS_UID}
- CDS_API_KEY=${CDS_API_KEY}
- DEBUG=${DEBUG}
- S3_BUCKET_NAME=${S3_BUCKET_NAME}
- S3_ACCESS_KEY=${S3_ACCESS_KEY}
- S3_SECRET_KEY=${S3_SECRET_KEY}
- S3_REGION=${S3_REGION}
- S3_ENDPOINT_URL=${S3_ENDPOINT_URL}
platform: "linux/amd64"
Loading
Loading