Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
83 lines
2.7 KiB
Python
83 lines
2.7 KiB
Python
from datetime import datetime
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query
|
|
from psycopg import AsyncConnection
|
|
|
|
from app.infra.db.timescaledb.repositories.analysis import AnalysisResultsRepository
|
|
|
|
from .dependencies import get_timescale_connection
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post("/timeseries/analysis/runs/{run_id}/results", status_code=201)
|
|
async def store_analysis_results(
|
|
run_id: UUID = Path(..., description="分析运行 ID"),
|
|
payload: dict = Body(...),
|
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
|
):
|
|
try:
|
|
node_rows = payload.get("node_results", [])
|
|
link_rows = payload.get("link_results", [])
|
|
await AnalysisResultsRepository.store_results(
|
|
conn, run_id, node_rows, link_rows
|
|
)
|
|
return {
|
|
"run_id": run_id,
|
|
"node_count": len(node_rows),
|
|
"link_count": len(link_rows),
|
|
}
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/timeseries/analysis/runs/{run_id}/nodes/{node_id}")
|
|
async def get_analysis_node_series(
|
|
run_id: UUID,
|
|
node_id: str,
|
|
start_time: datetime = Query(...),
|
|
end_time: datetime = Query(...),
|
|
field: str = Query(...),
|
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
|
):
|
|
try:
|
|
return await AnalysisResultsRepository.get_node_series(
|
|
conn, run_id, node_id, start_time, end_time, field
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/timeseries/analysis/runs/{run_id}/links/{link_id}")
|
|
async def get_analysis_link_series(
|
|
run_id: UUID,
|
|
link_id: str,
|
|
start_time: datetime = Query(...),
|
|
end_time: datetime = Query(...),
|
|
field: str = Query(...),
|
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
|
):
|
|
try:
|
|
return await AnalysisResultsRepository.get_link_series(
|
|
conn, run_id, link_id, start_time, end_time, field
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/timeseries/analysis/runs/{run_id}/values")
|
|
async def get_analysis_values_at_time(
|
|
run_id: UUID,
|
|
result_time: datetime = Query(...),
|
|
element_type: str = Query(..., pattern="^(node|link)$"),
|
|
field: str = Query(...),
|
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
|
):
|
|
try:
|
|
return await AnalysisResultsRepository.get_values_at_time(
|
|
conn, run_id, element_type, result_time, field
|
|
)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|