76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
|
|
import logging
|
||
|
|
from typing import Optional
|
||
|
|
|
||
|
|
from fastapi import APIRouter, HTTPException, Query
|
||
|
|
from fastapi.responses import FileResponse
|
||
|
|
|
||
|
|
from app.schemas.common import TaskDetail, TaskListResponse, TaskStatus
|
||
|
|
from app.services import task_tracker
|
||
|
|
from app.services.file_manager import get_result_path
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
router = APIRouter(prefix='/api/v1/tasks', tags=['tasks'])
|
||
|
|
|
||
|
|
|
||
|
|
@router.get('', response_model=TaskListResponse)
|
||
|
|
async def list_tasks(
|
||
|
|
status: Optional[TaskStatus] = Query(None),
|
||
|
|
limit: int = Query(20, ge=1, le=100),
|
||
|
|
offset: int = Query(0, ge=0),
|
||
|
|
):
|
||
|
|
tasks, total = task_tracker.list_tasks(status=status, limit=limit, offset=offset)
|
||
|
|
return TaskListResponse(
|
||
|
|
tasks=[_to_detail(t) for t in tasks],
|
||
|
|
total=total,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get('/{task_id}', response_model=TaskDetail)
|
||
|
|
async def get_task(task_id: str):
|
||
|
|
task = task_tracker.get_task(task_id)
|
||
|
|
if not task:
|
||
|
|
raise HTTPException(status_code=404, detail='Task not found')
|
||
|
|
return _to_detail(task)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get('/{task_id}/result')
|
||
|
|
async def get_task_result(task_id: str):
|
||
|
|
task = task_tracker.get_task(task_id)
|
||
|
|
if not task:
|
||
|
|
raise HTTPException(status_code=404, detail='Task not found')
|
||
|
|
if task['status'] != TaskStatus.completed:
|
||
|
|
raise HTTPException(status_code=409, detail=f'Task status is {task["status"].value}')
|
||
|
|
|
||
|
|
path = task.get('local_path') or get_result_path(task_id)
|
||
|
|
if not path:
|
||
|
|
# Fall back to redirect if we have a remote URL
|
||
|
|
if task.get('result_url'):
|
||
|
|
from fastapi.responses import RedirectResponse
|
||
|
|
return RedirectResponse(url=task['result_url'])
|
||
|
|
raise HTTPException(status_code=404, detail='Result file not found')
|
||
|
|
|
||
|
|
return FileResponse(path)
|
||
|
|
|
||
|
|
|
||
|
|
@router.delete('/{task_id}')
|
||
|
|
async def delete_task(task_id: str):
|
||
|
|
if not task_tracker.delete_task(task_id):
|
||
|
|
raise HTTPException(status_code=404, detail='Task not found')
|
||
|
|
return {'status': 'deleted', 'task_id': task_id}
|
||
|
|
|
||
|
|
|
||
|
|
def _to_detail(task: dict) -> TaskDetail:
|
||
|
|
result_url = None
|
||
|
|
if task['status'] == TaskStatus.completed:
|
||
|
|
result_url = f'/api/v1/tasks/{task["task_id"]}/result'
|
||
|
|
return TaskDetail(
|
||
|
|
task_id=task['task_id'],
|
||
|
|
status=task['status'],
|
||
|
|
created_at=task['created_at'],
|
||
|
|
updated_at=task['updated_at'],
|
||
|
|
progress=task.get('progress'),
|
||
|
|
result_url=result_url,
|
||
|
|
error=task.get('error'),
|
||
|
|
)
|