import asyncio from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel, Field from app.auth import require_user from app.database import get_db from app.tasks import ( create_task, get_task_status, get_active_task, cancel_task, ) from app.miner import run_mining_task_background router = APIRouter(tags=["Background Tasks"]) class MineBooksRequest(BaseModel): book_id: Optional[int] = None level: str = "A1" count: int = Field(15, ge=1, le=50) @router.post("/api/tasks/mine-books") async def api_tasks_mine_books( req: MineBooksRequest, user: dict = Depends(require_user), ): """ Starts an asynchronous background task to mine CEFR Cloze Tap questions from user's uploaded books (EPUB, TXT, PDF). """ level = req.level.upper().strip() if level not in ["A1", "A2", "B1", "B2", "ALL"]: raise HTTPException(status_code=400, detail="Invalid CEFR level") count = max(1, min(req.count, 50)) with get_db() as conn: active = get_active_task(conn, user["id"], task_type="mine_books") if active: return { "status": "already_running", "message": "Сборка уже выполняется в фоне", "task": active, } task_id = create_task( conn, user_id=user["id"], task_type="mine_books", params={ "book_id": req.book_id, "level": level, "count": count, }, total=count, ) # Launch background asyncio task asyncio.create_task( run_mining_task_background( task_id=task_id, user_id=user["id"], book_id=req.book_id, target_level=level, target_count=count, ) ) return { "status": "ok", "task_id": task_id, "message": f"Запущена фоновая сборка тестов ({level})", } @router.get("/api/tasks/active") async def api_tasks_active( task_type: Optional[str] = Query(None), user: dict = Depends(require_user), ): """ Returns current user's active background task (if any). """ with get_db() as conn: task = get_active_task(conn, user["id"], task_type=task_type) return {"task": task} @router.get("/api/tasks/{task_id}") async def api_tasks_status( task_id: int, user: dict = Depends(require_user), ): """ Returns status and progress of a background task. """ with get_db() as conn: task = get_task_status(conn, task_id, user_id=user["id"]) if not task: raise HTTPException(status_code=404, detail="Task not found") return {"task": task} @router.post("/api/tasks/{task_id}/cancel") async def api_tasks_cancel( task_id: int, user: dict = Depends(require_user), ): """ Cancels a running background task. """ with get_db() as conn: cancelled = cancel_task(conn, task_id, user_id=user["id"]) return {"status": "ok" if cancelled else "not_found", "cancelled": cancelled}