- Une ligne de log par requête (route modèle, statut, durée, code d'erreur, compte), identifiant X-Request-ID repris dans chaque log et affiché à l'utilisateur sous une erreur, exceptions non gérées tracées (500 JSON) - Journal d'accès d'uvicorn coupé : il exposait le jeton du formulaire public - Logs métier : paiement SumUp, connexion, import, plannings, solveur - Nom du bénévole dans la description du checkout SumUp - Code et identifiant de transaction SumUp enregistrés au paiement (migration 0023), affichés sur la fiche et dans l'export Excel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
"""Suggestions du solveur (OR-Tools CP-SAT). Ne persiste rien : la
|
|
coordination affecte elle-même, dans l'écran habituel, en tenant compte des
|
|
bénévoles remontés en tête de liste."""
|
|
|
|
import logging
|
|
import time
|
|
import uuid
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from sqlmodel import Session
|
|
|
|
from app.api.deps import get_edition
|
|
from app.api.schemas import SolverRunResult, SolverSuggestion
|
|
from app.auth.deps import require_staff
|
|
from app.db import get_session
|
|
from app.models import Account, Edition
|
|
from app.solver import build_solver_input, solve
|
|
|
|
logger = logging.getLogger("app.solver")
|
|
|
|
router = APIRouter(prefix="/editions/{edition_id}/solver", tags=["solver"])
|
|
|
|
|
|
@router.post("/suggestions", response_model=SolverRunResult)
|
|
def run_solver(
|
|
edition: Edition = Depends(get_edition),
|
|
_guard: Account = Depends(require_staff),
|
|
session: Session = Depends(get_session),
|
|
) -> SolverRunResult:
|
|
started = time.monotonic()
|
|
data = build_solver_input(session=session, edition=edition)
|
|
loaded = time.monotonic()
|
|
solution = solve(data)
|
|
logger.info(
|
|
"Solveur (édition %s) : %d créneaux, %d bénévoles, %d suggestions — "
|
|
"chargement %d ms, résolution %d ms",
|
|
edition.id,
|
|
len(data.shifts),
|
|
len(data.volunteers),
|
|
len(solution.suggestions),
|
|
(loaded - started) * 1000,
|
|
(time.monotonic() - loaded) * 1000,
|
|
)
|
|
return SolverRunResult(
|
|
suggestions=[
|
|
SolverSuggestion(
|
|
shift_id=uuid.UUID(s.shift_id),
|
|
volunteer_ids=[uuid.UUID(v) for v in s.volunteer_ids],
|
|
)
|
|
for s in solution.suggestions
|
|
],
|
|
shifts_considered=len(data.shifts),
|
|
volunteers_considered=len(data.volunteers),
|
|
)
|