"""Start processing (background) and poll status/progress.""" from __future__ import annotations from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request from sqlalchemy.orm import Session as OrmSession from ...db import models from ...services.audit import record as audit from ...services.jobs import run_processing from ..deps import db_dep, ensure_editable, get_session_or_404, session_dict router = APIRouter(prefix="/api/sessions", tags=["processing"]) @router.post("/{session_id}/process") def start_processing(session_id: int, background: BackgroundTasks, request: Request, db: OrmSession = Depends(db_dep)) -> dict: s = get_session_or_404(session_id, db) ensure_editable(s) if s.month_end_date is None: raise HTTPException(400, "Set the month-end date before processing.") valid_files = db.query(models.SessionFile).filter( models.SessionFile.session_id == session_id).all() if not valid_files: raise HTTPException(400, "Upload at least one file before processing.") if any(f.status == "invalid" for f in valid_files): raise HTTPException(400, "One or more files failed validation; fix them before processing.") if s.status == "processing": raise HTTPException(409, "Already processing.") s.status = "processing" s.progress_stage = "Queued" s.progress_pct = 0.0 s.error = "" audit(db, request, "process_run", session=s, detail=f"{len(valid_files)} file(s)") db.commit() background.add_task(run_processing, session_id) return {"started": True, "session_id": session_id} @router.get("/{session_id}/status") def status(session_id: int, db: OrmSession = Depends(db_dep)) -> dict: s = get_session_or_404(session_id, db) return { "status": s.status, "stage": s.progress_stage, "pct": s.progress_pct, "rows_done": s.progress_rows_done, "rows_total": s.progress_rows_total, "eta_seconds": s.eta_seconds, "error": s.error, "session": session_dict(s), }