from __future__ import annotations from uuid import UUID from fastapi import APIRouter, Depends, Query, Request from fastapi.responses import FileResponse from sqlalchemy.orm import Session from app.api.guest_scope import assert_guest_project_scope, guest_project_scope from app.core.errors import AppError from app.db.session import get_db from app.schemas import Envelope from app.schemas.export import ( ExportContentResponse, ExportCreateResponse, ExportListResponse, ExportRead, GeoJsonExportRequest, MapResultExportRequest, MetadataExportRequest, ReportExportRequest, ) from app.services.export_service import ExportService from app.services.dataset_service import DatasetService from app.services.detection_service import DetectionService from app.services.segmentation_service import SegmentationService from app.utils.response import envelope router = APIRouter(prefix="/exports", tags=["exports"]) @router.post("/geojson", response_model=Envelope[ExportCreateResponse]) def export_geojson( payload: GeoJsonExportRequest, request: Request, db: Session = Depends(get_db), ): if guest_project_scope(request) is not None: if payload.export_kind in {"dataset", "vector_selection"} and payload.dataset_id is not None: dataset = DatasetService.get_dataset(db, payload.dataset_id) assert_guest_project_scope(request, dataset.project_id) elif payload.export_kind == "detection_run" and payload.analysis_run_id is not None: run = DetectionService.get_run(db, payload.analysis_run_id) assert_guest_project_scope(request, run.project_id) elif payload.export_kind == "segmentation_run" and payload.analysis_run_id is not None: run = SegmentationService.get_run(db, payload.analysis_run_id) assert_guest_project_scope(request, run.project_id) if payload.export_kind == "vector_selection" and payload.dataset_id is not None and payload.bbox is not None: return envelope( ExportService.export_vector_selection_geojson( db, payload.dataset_id, payload.bbox.model_dump(), area_id=payload.area_id, limit=payload.limit, name=payload.name, ).model_dump(mode="json") ) if payload.export_kind == "detection_run" and payload.analysis_run_id is not None: return envelope( ExportService.export_detection_run_geojson( db, payload.analysis_run_id, payload.name, intended_use=payload.intended_use, ).model_dump(mode="json") ) if payload.export_kind == "segmentation_run" and payload.analysis_run_id is not None: return envelope( ExportService.export_segmentation_run_geojson(db, payload.analysis_run_id, payload.name).model_dump(mode="json") ) if payload.dataset_id is not None: return envelope(ExportService.export_dataset_geojson(db, payload.dataset_id, payload.name).model_dump(mode="json")) raise AppError( code="INVALID_EXPORT_REQUEST", message="GeoJSON export request does not match any supported export target", status_code=422, ) @router.post("/metadata", response_model=Envelope[ExportCreateResponse]) def export_project_metadata( payload: MetadataExportRequest, request: Request, db: Session = Depends(get_db), ): assert_guest_project_scope(request, payload.project_id) return envelope(ExportService.export_project_metadata(db, payload.project_id, payload.name).model_dump(mode="json")) @router.post("/report", response_model=Envelope[ExportCreateResponse]) def export_project_report( payload: ReportExportRequest, request: Request, db: Session = Depends(get_db), ): assert_guest_project_scope(request, payload.project_id) return envelope(ExportService.export_project_report(db, payload.project_id, payload.name).model_dump(mode="json")) @router.post("/map-result", response_model=Envelope[ExportCreateResponse]) def export_map_result( payload: MapResultExportRequest, request: Request, db: Session = Depends(get_db), ): assert_guest_project_scope(request, payload.project_id) return envelope(ExportService.export_map_result(db, payload).model_dump(mode="json")) @router.get( "/projects/{project_id}/exports", response_model=Envelope[ExportListResponse], ) def list_project_exports( project_id: UUID, request: Request, limit: int = Query(default=50, ge=1, le=100), offset: int = Query(default=0, ge=0), db: Session = Depends(get_db), ): assert_guest_project_scope(request, project_id) return envelope(ExportService.list_project_exports(db, project_id, limit=limit, offset=offset).model_dump(mode="json")) @router.get("/{export_id}", response_model=Envelope[ExportRead]) def get_export(export_id: UUID, request: Request, db: Session = Depends(get_db)): export = ExportService.get_export(db, export_id) assert_guest_project_scope(request, export.project_id) return envelope(export.model_dump(mode="json")) @router.get("/{export_id}/download") def download_export(export_id: UUID, request: Request, db: Session = Depends(get_db)): if guest_project_scope(request) is not None: export = ExportService.get_export(db, export_id) assert_guest_project_scope(request, export.project_id) path = ExportService.get_export_download_path(db, export_id) media_type = "text/html" if path.suffix.lower() in {".html", ".htm"} else "application/json" return FileResponse(path, filename=path.name, media_type=media_type) @router.get("/{export_id}/content", response_model=Envelope[ExportContentResponse]) def get_export_content(export_id: UUID, request: Request, db: Session = Depends(get_db)): if guest_project_scope(request) is not None: export = ExportService.get_export(db, export_id) assert_guest_project_scope(request, export.project_id) return envelope(ExportService.get_export_content(db, export_id).model_dump(mode="json"))