Solveur de suggestions (OR-Tools CP-SAT) — ne place jamais personne
Nouveau module `backend/app/solver/` (mêmes conventions que
`validation/` : models purs, engine pur testé, loader seul lien avec la
base). Pour chaque créneau encore sous son effectif idéal, calcule une
liste ordonnée de bénévoles candidats — c'est la coordination qui
affecte, dans l'écran habituel ; le solveur fait juste remonter ces
candidats en tête de liste (voir le câblage frontend à suivre).
- Seule contrainte dure : aucun chevauchement de créneaux pour un même
bénévole (règle SHIFT_OVERLAP), à la fois contre l'existant et entre
deux suggestions du même calcul. L'effectif idéal est un plafond dur.
- Tout le reste est un terme de l'objectif, pas un blocage : priorité
forte aux créneaux sous l'effectif minimal, bonus vœu de poste, malus
disponibilité manquante (réutilise `validation.engine.uncovered_minutes`),
cumul de 8 h et repos (contre les affectations déjà décidées),
répartition de la charge.
- `POST /editions/{id}/solver/suggestions` (COORDINATION/référent) :
recalcule à la demande, ne persiste jamais d'affectation.
Dépendance `ortools`. 18 nouveaux tests (moteur + API), 172 tests au
vert, `ruff` et `alembic check` verts. Testé sur le jeu de démo (120
bénévoles, 100 créneaux) : ~0,3 s.
CLAUDE.md : nouvelle section 4 (solveur), renumérotation 5-10,
compétences retirées de « hors périmètre », journal étape 15.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
36d108b47d
commit
803db5e9a9
@@ -235,7 +235,48 @@ Sémantique figée :
|
||||
|
||||
---
|
||||
|
||||
## 4. Périmètre
|
||||
## 4. Solveur de suggestions (OR-Tools CP-SAT)
|
||||
|
||||
Module isolé `backend/app/solver/` (mêmes conventions que `validation/` :
|
||||
`models.py` types purs, `engine.py` fonction pure `solve()`, `loader.py` seul
|
||||
point de contact avec la base). **Ne place jamais personne** : il calcule,
|
||||
pour chaque créneau encore sous son effectif idéal, une liste ordonnée de
|
||||
bénévoles candidats. La coordination affecte elle-même dans l'écran habituel
|
||||
(grille des besoins) ; l'appli fait simplement remonter ces candidats en tête
|
||||
de la colonne des choix quand on ouvre le détail d'un poste. Aucune
|
||||
affectation n'est jamais persistée par cet endpoint.
|
||||
|
||||
- `POST /editions/{id}/solver/suggestions` (COORDINATION ou référent) :
|
||||
recalcule à la demande (rien n'est mis en cache côté serveur) et renvoie
|
||||
`{ suggestions: [{ shift_id, volunteer_ids }], shifts_considered,
|
||||
volunteers_considered }`, `volunteer_ids` du meilleur au moins bon.
|
||||
- **Seule contrainte dure** : aucun chevauchement de créneaux pour un même
|
||||
bénévole (règle `SHIFT_OVERLAP`) — à la fois contre les affectations déjà
|
||||
décidées et entre deux créneaux nouvellement suggérés. L'effectif idéal est
|
||||
un plafond dur (le solveur ne sur-staffe jamais).
|
||||
- Tout le reste est un terme de l'objectif (maximisation), pas une
|
||||
contrainte — cohérent avec le moteur de validation, qui reste seul juge au
|
||||
moment où la coordination confirme une affectation :
|
||||
- un créneau encore sous son effectif **minimal** prime largement sur tout
|
||||
le reste (le solveur doit presque toujours proposer quelqu'un pour un
|
||||
trou urgent, quitte à ce que ce soit un pis-aller signalé ensuite par un
|
||||
avertissement) ;
|
||||
- bonus si le poste fait partie des vœux du bénévole
|
||||
(`VolunteerPositionWish`) ;
|
||||
- malus proportionnel aux minutes hors des fenêtres de disponibilité
|
||||
déclarées (réutilise `validation.engine.uncovered_minutes`), au dépassement
|
||||
du cumul de 8 h ce jour-là, et si le repos avec une affectation déjà
|
||||
décidée serait `< 8 h 00` ;
|
||||
- léger malus pour les bénévoles déjà bien chargés (répartition de la
|
||||
charge).
|
||||
- Simplification assumée : cumul de 8 h et repos ne sont vérifiés que
|
||||
contre les affectations **déjà décidées**, pas entre deux suggestions du
|
||||
même calcul — la validation finale à la confirmation reste seule juge de
|
||||
ce cas limite.
|
||||
|
||||
---
|
||||
|
||||
## 5. Périmètre
|
||||
|
||||
### Premier incrément (en cours)
|
||||
|
||||
@@ -252,15 +293,16 @@ Sémantique figée :
|
||||
`[{ position, shifts: [{ shift, min_staff, ideal_staff, assigned }] }]`.
|
||||
6. Jeu de données de démonstration : 120 bénévoles, 15 postes, 4 jours,
|
||||
quelques trous d'effectif, 2–3 avertissements actifs.
|
||||
7. Solveur de suggestions OR-Tools CP-SAT (section 4) — en cours.
|
||||
|
||||
### Hors périmètre (le modèle doit juste pouvoir les accueillir sans refonte)
|
||||
|
||||
Solveur OR-Tools · SMS · constructeur de formulaire · pointage sur site ·
|
||||
SMS · constructeur de formulaire · pointage sur site ·
|
||||
fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
|
||||
|
||||
---
|
||||
|
||||
## 5. Arborescence
|
||||
## 6. Arborescence
|
||||
|
||||
```
|
||||
.
|
||||
@@ -286,12 +328,13 @@ fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
|
||||
│ │ │ # health, auth, accounts, editions,
|
||||
│ │ │ # day_parts, imports, artists, volunteers,
|
||||
│ │ │ # availability, positions, shifts,
|
||||
│ │ │ # assignments, matrix, notifications
|
||||
│ │ │ # assignments, solver, matrix, notifications
|
||||
│ │ ├── auth/ # mot de passe + lien magique + JWT + rôles
|
||||
│ │ ├── email/ # SMTP (sender) + rendu des emails (templates)
|
||||
│ │ ├── importing/ # import CSV/Excel (parser, text, service)
|
||||
│ │ ├── models/ # tables SQLModel
|
||||
│ │ ├── validation/ # moteur pur (models, engine) + loader (base)
|
||||
│ │ ├── solver/ # suggestions OR-Tools (models, engine, loader)
|
||||
│ │ ├── bootstrap.py # instance vierge : édition + 1er compte
|
||||
│ │ ├── seed.py # jeu de démonstration (python -m app.seed)
|
||||
│ │ ├── i18n/
|
||||
@@ -306,7 +349,7 @@ fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
|
||||
|
||||
---
|
||||
|
||||
## 6. Commandes
|
||||
## 7. Commandes
|
||||
|
||||
Aucune dépendance locale requise hors Docker.
|
||||
|
||||
@@ -326,7 +369,7 @@ Aucune dépendance locale requise hors Docker.
|
||||
|
||||
---
|
||||
|
||||
## 7. Conventions
|
||||
## 8. Conventions
|
||||
|
||||
- **Code, base, noms de fichiers en anglais.** Le français vit dans les
|
||||
fichiers de traduction et les commits.
|
||||
@@ -347,7 +390,7 @@ Aucune dépendance locale requise hors Docker.
|
||||
|
||||
---
|
||||
|
||||
## 8. Déploiement
|
||||
## 9. Déploiement
|
||||
|
||||
- Dépôt GitHub. VPS avec Docker.
|
||||
- `.github/workflows/deploy.yml` : sur chaque push `main` → build de l'image
|
||||
@@ -361,7 +404,7 @@ Aucune dépendance locale requise hors Docker.
|
||||
|
||||
---
|
||||
|
||||
## 9. Journal des étapes
|
||||
## 10. Journal des étapes
|
||||
|
||||
| Étape | Description | État |
|
||||
|-------|-------------------------------------------------------|----------|
|
||||
@@ -379,7 +422,8 @@ Aucune dépendance locale requise hors Docker.
|
||||
| 12 | Créneaux types (`DayPart` CRUD) + génération auto des créneaux 0/0 à la création d'un poste ; case « à définir » / « sureffectif » sur la grille | à valider |
|
||||
| 13 | Bénévole : retrait taille de t-shirt / régime ; entité `Artist` (CRUD, par jour) + vœux des bénévoles (artistes, postes) | à valider |
|
||||
| 14 | Retrait des compétences (`Skill` et règle `MISSING_REQUIRED_SKILL`) ; disponibilités = fenêtres horaires libres par jour (`GET`/`PUT` availability), prises en compte par `OUTSIDE_AVAILABILITY` | à valider |
|
||||
| 15 | Solveur de suggestions OR-Tools CP-SAT (section 4) : `POST /editions/{id}/solver/suggestions`, candidats remontés en tête de liste dans la grille des besoins — aucune affectation automatique | à valider |
|
||||
|
||||
**Reste hors périmètre** (modèle prêt à les accueillir) : solveur OR-Tools,
|
||||
SMS, constructeur de formulaire, pointage sur site, IA. Frontend :
|
||||
squelette + 6 écrans + connexion en place (voir `frontend/`).
|
||||
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
|
||||
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +
|
||||
connexion en place (voir `frontend/`).
|
||||
|
||||
Reference in New Issue
Block a user