diff --git a/src/gisaf/api/download.py b/src/gisaf/api/download.py new file mode 100644 index 0000000..5e9a6c5 --- /dev/null +++ b/src/gisaf/api/download.py @@ -0,0 +1,77 @@ +import logging +import io +from typing import Annotated + +from fastapi import (Depends, APIRouter, HTTPException, status, Response) +from fastapi.responses import FileResponse, StreamingResponse +from sqlmodel import select +from sqlalchemy.orm import selectinload, joinedload + +from gisaf.database import pandas_query, fastapi_db_session as db_session +from gisaf.models.geo_models_base import GeoModel, PlottableModel +from gisaf.security import ( + Token, authenticate_user, get_current_active_user, create_access_token, + ) +from gisaf.models.authentication import (User, UserRead, Role, RoleRead) +from gisaf.registry import registry, NotInRegistry + +logger = logging.getLogger(__name__) + +api = APIRouter( + tags=["download"], + # dependencies=[Depends(get_token_header)], + responses={404: {"description": "Not found"}}, +) + +@api.get('/csv/{store}/{model_id}/{value}/{resample}', + responses={ + 200: { + "content": { + "text/csv": {} + } + } + }) +async def download_csv( + store: str, model_id: int, value: str, resample: str, + db_session: db_session, + user: Annotated[UserRead, Depends(get_current_active_user)] + ) -> StreamingResponse: + try: + store_record = registry.stores.loc[store] + model: type[GeoModel] = store_record.model + values_model = registry.values_for_model[model][0] + except KeyError: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + ## Check if there's a custom getter + custom_getter = getattr(values_model, f'get_{value}', None) + if custom_getter: + df = await custom_getter(model_id) + else: + df = await values_model.get_as_dataframe(model_id=model_id, with_only_columns=[value]) + if len(df) == 0: + raise HTTPException(status.HTTP_204_NO_CONTENT) + if resample and resample != '0': + value_defs = [v for v in values_model.values if v['name'] == value] + if len(value_defs) > 0 and 'agg' in value_defs[0]: + resampling_method = value_defs[0]['agg'] + else: + resampling_method = 'mean' + df = df.resample(resample).agg(resampling_method) + if len(df) > 0: + df.reset_index(inplace=True) + query = select(model).where(model.id == model_id).options( + *(joinedload(jt) for jt in model.selectinload())) + response = await db_session.exec(query) + item = response.one() + filename = f'"{item.caption}-{value}-{resample}.csv"' + filename = f'{item.__class__.__name__}-{value}-{resample}.csv' + stream = io.StringIO() + df.reset_index().to_csv(stream, + index=False, + date_format='%d/%m/%Y %H:%M', + float_format=values_model.float_format) + response = Response(stream.getvalue(), + headers={ + 'Content-Disposition': f"attachment; filename={filename}" + }) + return response diff --git a/src/gisaf/application.py b/src/gisaf/application.py index ed79bff..26567b5 100644 --- a/src/gisaf/application.py +++ b/src/gisaf/application.py @@ -14,6 +14,7 @@ from gisaf.api.geoapi import api as geoapi from gisaf.api.admin import api as admin_api from gisaf.api.dashboard import api as dashboard_api from gisaf.api.map import api as map_api +from gisaf.api.download import api as download_api from gisaf.plugins import manager as plugin_manger logging.basicConfig(level=conf.gisaf.debugLevel) @@ -45,4 +46,5 @@ app.include_router(api, prefix="/api") app.include_router(geoapi, prefix="/api/gj") app.include_router(admin_api, prefix="/api/admin") app.include_router(dashboard_api, prefix="/api/dashboard") -app.include_router(map_api, prefix='/api/map') \ No newline at end of file +app.include_router(map_api, prefix='/api/map') +app.include_router(download_api, prefix='/api/download') \ No newline at end of file