118 lines
3.1 KiB
Python
118 lines
3.1 KiB
Python
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}
|