Solveur OR-Tools (services/roster-solver) : couverture, compétences, équité, coût chargé, cadence/efficacité, capacité-par-job ; contraintes dures/souples façon Timefold. Hub (lib/roster.js) : génération via solveur, publication par réécriture de semaine (anti-doublons), demande (effectif ou nb de jobs), cadence/coût/ compétences par tech, pause, congés (Tech Availability + approbation), booking (slots roster-aware / fit 3-dispos / confirm) + portail public /book. Réessai sur serialization failures frappe_pg ; appels ERP séquentiels. Ops : page Planification (grille compacte « J8 », multi-shift, drag-select + undo/redo, modèles de semaine, éditeur cadence&coût, congés, SMS opt-in), page Rendez-vous (répartiteur), jobColor tech en pause → tickets rouges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
44 lines
1.1 KiB
Python
44 lines
1.1 KiB
Python
"""
|
|
Roster AI — API HTTP (FastAPI) autour du solveur OR-Tools CP-SAT.
|
|
|
|
Endpoints :
|
|
GET /health → liveness
|
|
POST /solve → résout un horaire (payload = voir README/sample_request.json)
|
|
|
|
Pensé pour tourner à côté du hub (targo-hub l'appelle), pas exposé publiquement.
|
|
"""
|
|
from fastapi import FastAPI
|
|
from fastapi.responses import JSONResponse
|
|
from pydantic import BaseModel
|
|
from typing import Any
|
|
import os
|
|
|
|
from solver import solve_roster
|
|
|
|
app = FastAPI(title="Roster AI Solver", version="0.1.0")
|
|
|
|
API_TOKEN = os.environ.get("ROSTER_SOLVER_TOKEN", "")
|
|
|
|
|
|
class SolveRequest(BaseModel):
|
|
horizon: dict
|
|
shift_templates: list
|
|
technicians: list
|
|
coverage: list = []
|
|
weights: dict | None = None
|
|
max_seconds: float | None = None
|
|
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return {"ok": True, "service": "roster-solver"}
|
|
|
|
|
|
@app.post("/solve")
|
|
def solve(req: SolveRequest):
|
|
try:
|
|
result = solve_roster(req.model_dump())
|
|
return result
|
|
except Exception as e: # noqa: BLE001 — renvoyer l'erreur proprement au hub
|
|
return JSONResponse(status_code=400, content={"status": "ERROR", "message": str(e)})
|