feat: add temporal Mol explorer
This commit is contained in:
@@ -1 +1 @@
|
||||
__all__ = ["analysis", "areas", "datasets", "health", "projects", "exports", "jobs", "external", "qa"]
|
||||
__all__ = ["analysis", "areas", "datasets", "health", "projects", "exports", "jobs", "external", "qa", "temporal"]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
from uuid import UUID as _UUID
|
||||
@@ -30,7 +31,7 @@ from app.schemas import (
|
||||
VectorSelectionResponse,
|
||||
)
|
||||
from app.schemas.job import JobCreate
|
||||
from app.schemas.dataset import DatasetCreateResponse
|
||||
from app.schemas.dataset import DatasetCreateResponse, DatasetTemporalUpdate
|
||||
from app.schemas.operations import VectorOperationResult
|
||||
from app.services.job_service import JobService
|
||||
from app.services.raster_operations_service import RasterOperationsService
|
||||
@@ -87,6 +88,12 @@ async def upload_dataset(
|
||||
reference_layer_name: str | None = Form(None),
|
||||
source_metadata_json: str | None = Form(None),
|
||||
provenance_metadata_json: str | None = Form(None),
|
||||
temporal_series_key: str | None = Form(None),
|
||||
observed_at: datetime | None = Form(None),
|
||||
valid_from: datetime | None = Form(None),
|
||||
valid_to: datetime | None = Form(None),
|
||||
temporal_granularity: str | None = Form(None),
|
||||
source_version: str | None = Form(None),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
if area_id is not None:
|
||||
@@ -108,6 +115,12 @@ async def upload_dataset(
|
||||
source_metadata=_parse_metadata_json(source_metadata_json, "source_metadata_json"),
|
||||
provenance_metadata=_parse_metadata_json(provenance_metadata_json, "provenance_metadata_json"),
|
||||
area_id=area_id,
|
||||
temporal_series_key=temporal_series_key,
|
||||
observed_at=observed_at,
|
||||
valid_from=valid_from,
|
||||
valid_to=valid_to,
|
||||
temporal_granularity=temporal_granularity,
|
||||
source_version=source_version,
|
||||
)
|
||||
return envelope(created.model_dump())
|
||||
|
||||
@@ -135,6 +148,33 @@ def get_dataset(
|
||||
return envelope(DatasetCreateResponse.model_validate(dataset).model_dump())
|
||||
|
||||
|
||||
@router.patch("/datasets/{dataset_id}/temporal", response_model=dict)
|
||||
def update_dataset_temporal_metadata(
|
||||
project_id: UUID,
|
||||
dataset_id: UUID,
|
||||
payload: DatasetTemporalUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
dataset = DatasetService.get_dataset(db, dataset_id)
|
||||
if dataset.project_id != project_id:
|
||||
raise HTTPException(status_code=404, detail="Dataset not found")
|
||||
updated = DatasetService.update_temporal_metadata(db, dataset_id, payload)
|
||||
return envelope(updated.model_dump())
|
||||
|
||||
|
||||
@router.get("/datasets/{dataset_id}/versions", response_model=dict)
|
||||
def list_dataset_versions(
|
||||
project_id: UUID,
|
||||
dataset_id: UUID,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
dataset = DatasetService.get_dataset(db, dataset_id)
|
||||
if dataset.project_id != project_id:
|
||||
raise HTTPException(status_code=404, detail="Dataset not found")
|
||||
versions = DatasetService.list_versions(db, dataset_id)
|
||||
return envelope({"items": [item.model_dump() for item in versions], "total": len(versions)})
|
||||
|
||||
|
||||
@router.post("/datasets/{dataset_id}/metadata/refresh", response_model=dict)
|
||||
def refresh_dataset_metadata(
|
||||
project_id: UUID,
|
||||
@@ -203,6 +243,13 @@ def select_vector_features(
|
||||
bbox=payload.bbox.model_dump(),
|
||||
limit=payload.limit,
|
||||
)
|
||||
if isinstance(dataset.source_metadata, dict) and dataset.source_metadata.get("selection_aggregation"):
|
||||
result["summary"] = VectorFeatureService.summarize_features_by_bbox(
|
||||
db,
|
||||
dataset=dataset,
|
||||
bbox=payload.bbox.model_dump(),
|
||||
total_feature_count=result.get("total_feature_count"),
|
||||
)
|
||||
return envelope(VectorSelectionResponse(**result).model_dump(exclude_none=True))
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.session import get_db
|
||||
from app.schemas.temporal import TemporalComparisonRequest
|
||||
from app.services.temporal_analysis_service import TemporalAnalysisService
|
||||
from app.utils.response import envelope
|
||||
|
||||
|
||||
router = APIRouter(prefix="/projects/{project_id}/temporal", tags=["temporal"])
|
||||
|
||||
|
||||
@router.get("/series", response_model=dict)
|
||||
def list_temporal_series(project_id: UUID, db: Session = Depends(get_db)):
|
||||
series = TemporalAnalysisService.list_series(db, project_id)
|
||||
return envelope({"items": [item.model_dump() for item in series], "total": len(series)})
|
||||
|
||||
|
||||
@router.post("/compare", response_model=dict)
|
||||
def compare_temporal_snapshots(
|
||||
project_id: UUID,
|
||||
payload: TemporalComparisonRequest,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return envelope(TemporalAnalysisService.compare(db, project_id=project_id, payload=payload).model_dump())
|
||||
Reference in New Issue
Block a user