| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- """Task management router for Web-Local system dispatch (P4-M2)."""
- from typing import Optional, List, Dict, Any
- from fastapi import APIRouter, HTTPException
- from pydantic import BaseModel, Field
- from ..services.task_manager import get_task_manager
- router = APIRouter(prefix="/api/tasks", tags=["Tasks"])
- class CreateTaskRequest(BaseModel):
- """Request to create a new simulation task."""
- plan_id: Optional[int] = Field(default=None, description="Associated plan ID")
- plan_data: Dict[str, Any] = Field(..., description="Full plan data")
- parameters: List[Dict[str, Any]] = Field(..., description="List of parameter sets to simulate")
- task_name: Optional[str] = Field(default=None, description="Optional task name")
- priority: int = Field(default=5, ge=1, le=10, description="Task priority (1-10)")
- class ProgressUpdateRequest(BaseModel):
- """Request to update task progress."""
- current_point: int = Field(..., ge=0, description="Current point index (0-based)")
- total_points: Optional[int] = Field(default=None, description="Total points")
- current_params: Optional[Dict[str, Any]] = Field(default=None)
- elapsed_time: Optional[float] = Field(default=None)
- class ResultsReportRequest(BaseModel):
- """Request to report final results."""
- results: List[Dict[str, Any]] = Field(..., description="List of simulation results")
- metrics: Optional[Dict[str, Any]] = Field(default=None)
- logs: Optional[str] = Field(default=None)
- duration: Optional[float] = Field(default=None)
- status: str = Field(default="completed", description="completed / failed")
- @router.post("")
- def create_task(request: CreateTaskRequest):
- """Create a new simulation task."""
- try:
- manager = get_task_manager()
- task = manager.create_task(
- plan_id=request.plan_id,
- plan_data=request.plan_data,
- parameters=request.parameters,
- task_name=request.task_name,
- priority=request.priority,
- )
- return task
- except Exception as e:
- raise HTTPException(status_code=500, detail=f"Task creation failed: {str(e)}")
- @router.get("")
- def list_tasks(
- status: Optional[str] = None,
- plan_id: Optional[int] = None,
- limit: int = 50,
- offset: int = 0,
- ):
- """List tasks with optional filters."""
- manager = get_task_manager()
- return manager.list_tasks(status=status, plan_id=plan_id, limit=limit, offset=offset)
- @router.get("/{task_id}")
- def get_task(task_id: str):
- """Get task details by ID."""
- manager = get_task_manager()
- task = manager.get_task(task_id)
- if not task:
- raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
- return task
- @router.post("/{task_id}/dispatch")
- def dispatch_task(task_id: str):
- """Mark task as dispatched (ready for local executor)."""
- try:
- manager = get_task_manager()
- return manager.dispatch_task(task_id)
- except ValueError as e:
- raise HTTPException(status_code=400, detail=str(e))
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
- @router.post("/{task_id}/progress")
- def update_progress(task_id: str, request: ProgressUpdateRequest):
- """Update task progress from local executor."""
- try:
- manager = get_task_manager()
- return manager.update_progress(
- task_id=task_id,
- current_point=request.current_point,
- total_points=request.total_points,
- current_params=request.current_params,
- elapsed_time=request.elapsed_time,
- )
- except ValueError as e:
- raise HTTPException(status_code=404, detail=str(e))
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
- @router.post("/{task_id}/results")
- def report_results(task_id: str, request: ResultsReportRequest):
- """Report final results from local executor."""
- try:
- manager = get_task_manager()
- return manager.report_results(
- task_id=task_id,
- results=request.results,
- metrics=request.metrics,
- logs=request.logs,
- duration=request.duration,
- status=request.status,
- )
- except ValueError as e:
- raise HTTPException(status_code=404, detail=str(e))
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
- @router.get("/{task_id}/results")
- def get_task_results(task_id: str):
- """Get task results file content."""
- manager = get_task_manager()
- results = manager.get_task_results(task_id)
- if results is None:
- raise HTTPException(status_code=404, detail=f"Results for task {task_id} not found")
- return results
- @router.post("/{task_id}/cancel")
- def cancel_task(task_id: str):
- """Cancel a pending or running task."""
- try:
- manager = get_task_manager()
- return manager.cancel_task(task_id)
- except ValueError as e:
- raise HTTPException(status_code=400, detail=str(e))
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
- @router.get("/{task_id}/download")
- def download_task_file(task_id: str):
- """Download task JSON file for local executor."""
- manager = get_task_manager()
- task = manager.get_task(task_id)
- if not task or not task.get("task_file"):
- raise HTTPException(status_code=404, detail=f"Task file for {task_id} not found")
- import os
- if not os.path.exists(task["task_file"]):
- raise HTTPException(status_code=404, detail="Task file not found on disk")
- from fastapi.responses import FileResponse
- return FileResponse(task["task_file"], filename=f"{task_id}_task.json", media_type="application/json")
|