RamEx-Flow / backend /app /api /v1 /endpoints /analysis.py
Aye10032
feat: Support workflow payloads and preview downloads
3933dfe
Raw
History Blame Contribute Delete
3.91 kB
import json
from typing import Optional
from fastapi import APIRouter, Request, UploadFile, status
from fastapi.responses import FileResponse, JSONResponse
from app.core.errors import bad_request, not_found
from app.utils.r_core.outputs import empty_result
from app.schemas.analysis import AnalysisResultsResponse, AnalysisRunResponse, AnalysisTaskResponse
from app.services import analysis_service
router = APIRouter(prefix="/projects/{project_id}/analysis", tags=["analysis"])
def _parse_json_field(raw_value: object) -> Optional[dict]:
if not raw_value or not isinstance(raw_value, str):
return None
try:
parsed = json.loads(raw_value)
except json.JSONDecodeError:
bad_request("invalid_params", "参数格式无效")
return parsed if isinstance(parsed, dict) else None
async def _parse_analysis_params(request: Request) -> tuple[dict, Optional[dict], Optional[UploadFile]]:
content_type = request.headers.get("content-type", "")
if content_type.startswith("multipart/form-data"):
form = await request.form()
params = _parse_json_field(form.get("params")) or {}
workflow = _parse_json_field(form.get("workflow"))
upload = form.get("bands_csv")
if upload is not None and hasattr(upload, "filename") and hasattr(upload, "file"):
return params, workflow, upload
return params, workflow, None
try:
payload = await request.json()
except json.JSONDecodeError:
bad_request("invalid_params", "参数格式无效")
if not isinstance(payload, dict):
return {}, None, None
workflow = payload.get("workflow") if isinstance(payload.get("workflow"), dict) else None
return payload, workflow, None
@router.get("/status/", response_model=AnalysisTaskResponse)
def analysis_status(project_id: str) -> dict:
try:
task = analysis_service.latest_task(project_id)
except KeyError:
not_found("project_not_found", "项目不存在")
if not task:
return JSONResponse(status_code=status.HTTP_200_OK, content={"status": "not_started"})
return task
@router.post("/", response_model=AnalysisRunResponse)
async def run_analysis(project_id: str, request: Request):
params, workflow, bands_csv = await _parse_analysis_params(request)
try:
result = analysis_service.run_analysis(project_id, params, bands_csv=bands_csv, workflow=workflow)
except KeyError:
not_found("project_not_found", "项目不存在")
except RuntimeError as exc:
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"status": "error", "error": {"message": str(exc)}},
)
return {"status": "success", "message": "分析完成", "result": result}
@router.get("/results/", response_model=AnalysisResultsResponse)
def analysis_results(project_id: str) -> dict:
try:
task = analysis_service.latest_task(project_id)
except KeyError:
not_found("project_not_found", "项目不存在")
if task and task["status"] == "processing":
return {"status": "processing", "message": "分析任务正在进行中"}
result = analysis_service.latest_completed_result(project_id)
if result:
return {"status": "success", "result": result}
return {
"status": "success",
"message": "没有找到已完成的分析结果",
"result": empty_result(),
}
@router.get("/export/")
def export_results(project_id: str):
try:
zip_path = analysis_service.export_latest_results(project_id)
except KeyError:
not_found("project_not_found", "项目不存在")
except FileNotFoundError:
not_found("no_results", "没有找到可导出的分析结果")
return FileResponse(
path=zip_path,
media_type="application/zip",
filename=zip_path.name,
)