Spaces:
Sleeping
Sleeping
| 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 | |
| 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 | |
| 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} | |
| 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(), | |
| } | |
| 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, | |
| ) | |