394 lines
19 KiB
Markdown
394 lines
19 KiB
Markdown
# Moteur de planification interne — proposition d'architecture
|
||
|
||
**Statut : étude et spécification uniquement.** Aucun modèle, route,
|
||
migration, synchronisation ou changement applicatif n'est créé par ce
|
||
document.
|
||
|
||
## A. Architecture actuelle
|
||
|
||
Le dépôt possède déjà plusieurs briques réutilisables :
|
||
|
||
- `Room` et `RoomSchedule` (`app_new/core/models/college.py`) pour les salles
|
||
et un emploi du temps hebdomadaire (`week_start`, jour, début/fin, matière,
|
||
enseignant, classe) ;
|
||
- `WorkSchedule`, `WorkScheduleTemplate`, `TechnicianAvailability`,
|
||
`PersonalLeave`, `Training`, `CollegeClosure`, `ClosureSchedule` et
|
||
`ClosureWorkDay` (`app_new/core/models/planning.py`) pour horaires, pauses,
|
||
absences, formations, fermetures et permanences ;
|
||
- `PreventiveTask` (durée, lot, équipement/catégorie, fenêtre autorisée),
|
||
`LotTask` (durée, périodicité, sécurité) et `ScheduledTask` (date, début,
|
||
fin, durée, salle, équipement, entreprise, contrat, technicien, statut) ;
|
||
- `Intervention` avec date planifiée, priorité, salle/équipement,
|
||
technicien et entreprise ;
|
||
- `AdminTask`, actuellement récurrente avec heure et durée ;
|
||
- `PlanningDay`/`PlanningItem`, qui représentent déjà prévention, curatif,
|
||
administratif et formation avec durée, salle, priorité et statut.
|
||
|
||
`planning_service.py` sait produire une journée et calculer des heures de
|
||
travail, mais `schedule_task` ne place pas réellement de créneau et la
|
||
disponibilité de salle n'est pas croisée avec toutes les tâches internes.
|
||
L'implémentation interroge en outre un symbole `PronoteSchedule` alors que le
|
||
modèle local de salle observé est `RoomSchedule` : cette dette doit être
|
||
résolue avant d'automatiser la disponibilité.
|
||
|
||
## B. Limites actuelles
|
||
|
||
1. `RoomSchedule` ne conserve pas de provenance, identifiant externe,
|
||
validité ou récurrence explicite.
|
||
2. Le service de planification peut retomber sur 08:00–17:00 et certaines
|
||
requêtes d'horaires/indisponibilités ne filtrent pas toujours par agent.
|
||
3. Les conflits salle, tâches, pauses, fermetures et interventions ne sont
|
||
pas évalués dans un même calendrier.
|
||
4. `AdminTask` ne porte ni statut d'exécution, priorité, agent ou fenêtre.
|
||
5. `PlanningItem` et `ScheduledTask` se recouvrent partiellement, ce qui peut
|
||
produire des doublons d'affichage.
|
||
6. L'import Pronote est optionnel dans les routes mais ne doit jamais être une
|
||
dépendance du calcul interne.
|
||
|
||
## C. Principe de vérité interne
|
||
|
||
La GMAO possède des créneaux internes d'occupation. Chaque créneau conserve
|
||
une provenance (`manual`, `pronote`, `import`) et, conceptuellement, un
|
||
booléen `protected_from_sync`, un `external_id`, les horodatages de
|
||
synchronisation et une période de validité.
|
||
|
||
La distinction métier est la suivante :
|
||
|
||
- **manuel initial** : `source=manual`, protection désactivée ; il permet de
|
||
démarrer avant Pronote et peut être supplanté par une donnée Pronote fiable ;
|
||
- **manuel protégé** : `source=manual`, protection activée ; il représente une
|
||
décision locale explicite et ne peut être écrasé automatiquement ;
|
||
- **Pronote** : `source=pronote`, géré uniquement par la synchronisation qui
|
||
l'a créé ;
|
||
- **import** : donnée issue d'une autre importation connue ou historique.
|
||
|
||
L'occupation active présentée au moteur de maintenance est une vue résolue :
|
||
elle ne contient pas deux fois un créneau manuel initial et son équivalent
|
||
Pronote. Pronote est une source de synchronisation, jamais le moteur de
|
||
planning de la GMAO. L'application doit démarrer et rester pleinement
|
||
utilisable avec `PRONOTE_ENABLED=false`, sans token et sans synchronisation.
|
||
|
||
## D. Saisie manuelle en début d'année
|
||
|
||
Une fonction de gestion crée des `RoomSchedule` de source `manual` à partir
|
||
des salles, périodes, jours, heures et types d'occupation : cours, réunion,
|
||
examen, réservation, travaux, intervention, fermeture ou autre. Par défaut,
|
||
une saisie de cours destinée à l'initialisation est **non protégée**. Pour une
|
||
réunion exceptionnelle, un examen local, des travaux, une réservation,
|
||
intervention ou fermeture, l'utilisateur peut cocher une formulation métier
|
||
telle que « Ne pas modifier automatiquement ce créneau ».
|
||
|
||
La validation refuse début ≥ fin et signale les chevauchements. L'interface
|
||
affiche « Saisi manuellement » et, si nécessaire, « Protégé des mises à jour
|
||
automatiques » ; elle ne montre jamais le nom technique
|
||
`protected_from_sync`.
|
||
|
||
## E. Pronote facultatif
|
||
|
||
Lorsque Pronote devient disponible et fiable, il peut prendre progressivement
|
||
le relais sur les créneaux manuels **non protégés** correspondant à son emploi
|
||
du temps. Il ne modifie ni ne supprime les créneaux manuels protégés. Il ne
|
||
possède jamais la source interne de vérité : le service de synchronisation
|
||
résout d'abord les provenances et les conflits, puis le moteur consomme la vue
|
||
interne active.
|
||
|
||
## F. Synchronisation et conflits
|
||
|
||
### Rapprochement déterministe
|
||
|
||
Un créneau Pronote peut être rapproché automatiquement d'un manuel initial
|
||
uniquement si les critères stables concordent : même salle, même période de
|
||
validité, même jour/date et horaires identiques ou suffisamment proches,
|
||
complétés si disponibles par classe/groupe, matière et enseignant. La V1 ne
|
||
déduit pas une correspondance à partir d'un seul critère. Si la correspondance
|
||
est ambiguë, elle conserve les deux lignes, signale un conflit et demande une
|
||
validation humaine.
|
||
|
||
### Règles
|
||
|
||
- Manuel initial non protégé + Pronote correspondant : le créneau Pronote
|
||
devient actif et le manuel initial est marqué supplanté/archivé ; il n'y a
|
||
pas de doublon dans la vue d'occupation.
|
||
- Manuel initial non protégé + horaire Pronote modifié : le Pronote remplace
|
||
l'ancien intervalle, qui n'est plus considéré comme une occupation active.
|
||
- Manuel protégé + Pronote chevauchant : le manuel reste actif, Pronote reste
|
||
visible comme conflit ; rien n'est écrasé automatiquement.
|
||
- Créneau Pronote identique ou modifié : seule la ligne `source=pronote` avec
|
||
`external_id` correspondant est mise à jour et son `last_synced_at` avancé.
|
||
- Suppression côté Pronote : désactivation de la ligne Pronote propriétaire.
|
||
Un manuel initial supplanté reste archivé comme historique et ne redevient
|
||
actif automatiquement que si une règle de réactivation explicite est
|
||
validée ; par défaut, l'interface propose à l'utilisateur de le restaurer.
|
||
Aucun manuel protégé n'est supprimé.
|
||
- Pronote indisponible : conserver tous les créneaux actifs connus, afficher
|
||
la date de dernière synchronisation et un avertissement de fraîcheur ; ne
|
||
jamais vider le planning.
|
||
|
||
Chaque transition laisse une trace minimale : créé manuellement, protégé ou
|
||
déprotégé, repris par Pronote, modifié par synchronisation, conflit détecté ou
|
||
désactivé car absent de Pronote. Cette trace doit rester compréhensible sans
|
||
imposer un moteur d'audit complexe en V1.
|
||
|
||
Le moteur de maintenance demande uniquement « quels sont les créneaux
|
||
d'occupation internes actifs de cette salle ? ». Il ne connaît ni la priorité
|
||
Pronote ni les règles de rapprochement.
|
||
|
||
## G. Disponibilité des salles
|
||
|
||
Le calcul croise les occupations internes, fermetures de salle/collège,
|
||
interventions, tâches planifiées, rendez-vous d'entreprises et permanence.
|
||
Les catégories d'occupation sont celles de la saisie manuelle. Une tâche de
|
||
45 minutes peut occuper 12:00–12:45 entre deux cours si le technicien est
|
||
également disponible ; les créneaux hors horaires, pause ou fermeture sont
|
||
exclus.
|
||
|
||
Le futur service doit utiliser `RoomSchedule` pour les cours importés ou
|
||
manuels et `ScheduledTask`/`PlanningItem`/`Intervention` pour les activités
|
||
internes, sans appeler Pronote en direct.
|
||
|
||
## H. Préventif et lots
|
||
|
||
`PreventiveTask` et `LotTask` fournissent la périodicité, la durée, la portée,
|
||
les restrictions d'accès et le caractère critique. `ScheduledTask` est la
|
||
source d'exécution d'un travail généré : lot, tâche, équipement/local, date
|
||
cible, durée, début/fin, priorité, statut, entreprise/contrat et technicien.
|
||
|
||
Les créneaux proposés doivent afficher **quoi, où, quand, durée** et refuser
|
||
une date dépassant une échéance de conformité. Une intervention convertie ne
|
||
doit pas être affichée deux fois.
|
||
|
||
## I. Horaires de travail
|
||
|
||
Le moteur utilise le `WorkSchedule` de l'agent, les templates, pauses,
|
||
`TechnicianAvailability`, congés, formations, fermetures et
|
||
`ClosureWorkDay`. Il ne planifie jamais sur la pause ou hors intervalle. La
|
||
V1 doit exiger un agent explicite pour une proposition automatique et ne pas
|
||
inventer 08:00–17:00 lorsque la configuration manque ; l'absence de données
|
||
doit être expliquée.
|
||
|
||
## J. Tâches administratives
|
||
|
||
Les tâches administratives sont des activités planifiables qui consomment du
|
||
temps : commande, devis, compte rendu, registre, dossier ou appels. La V1
|
||
doit compléter le modèle actuel par une durée, priorité, statut, fenêtre/date,
|
||
heure planifiée et responsable, ou projeter ces données dans
|
||
`PlanningItem`. Une récurrence ne doit pas masquer l'instance réellement
|
||
faite ou reportée.
|
||
|
||
## K. Entreprises extérieures
|
||
|
||
Un rendez-vous doit représenter au minimum entreprise, date/heure fixe,
|
||
intervention/contrat lié, durée d'accueil, accompagnement, éventuels passages
|
||
et temps administratif final. Pour la V1, réutiliser `ScheduledTask`/`PlanningItem`
|
||
avec `company_id` et un type d'étape simple (accueil, contrôle, signature)
|
||
plutôt que créer un système de calendrier externe. Une entité
|
||
`ExternalCompanyAppointment` séparée ne sera justifiée que si plusieurs
|
||
étapes doivent être suivies indépendamment.
|
||
|
||
## L. Urgences et contraintes
|
||
|
||
Les niveaux compréhensibles sont :
|
||
|
||
- **Fixe** : rendez-vous entreprise ou examen à heure imposée ;
|
||
- **Urgent** : fuite, sécurité ou panne critique ;
|
||
- **Échéance** : conformité ou tâche à terminer avant une date ;
|
||
- **Déplaçable** : prévention réalisable dans la fenêtre ;
|
||
- **Administratif** : travail planifiable avant une échéance.
|
||
|
||
Une urgence peut interrompre ou repousser, mais le système conserve chaque
|
||
tâche, son ancien créneau, sa raison et sa nouvelle proposition.
|
||
|
||
## M. Optimisation géographique
|
||
|
||
La première version calcule un coût ordinal simple : même salle (0), même
|
||
zone (1), même bâtiment (2), autre bâtiment (3). Ce coût ne passe jamais avant
|
||
sécurité, rendez-vous fixe, échéance, disponibilité, horaires et compétences.
|
||
Il sert uniquement à départager des créneaux équivalents, sans GPS ni tournée
|
||
complexe.
|
||
|
||
Ordre d'arbitrage : urgence/sécurité, rendez-vous fixe, échéance,
|
||
disponibilité salle, horaires, compétence/affectation, regroupement
|
||
géographique, réduction des trous.
|
||
|
||
## N. Replanification et contrôle humain
|
||
|
||
Lorsqu'une urgence arrive à 10:00, l'interface propose les tâches décalables,
|
||
les conserve avec leur historique et expose les créneaux alternatifs. Elle
|
||
classe chaque élément en conservé, décalé, non planifiable ou nouvelle
|
||
proposition. Une validation humaine est obligatoire ; aucun déplacement
|
||
silencieux.
|
||
|
||
Un déplacement manuel revalide conflit salle, horaires, pauses, autre tâche,
|
||
rendez-vous fixe et fermeture. Le message doit être métier : « Salle 101
|
||
occupée jusqu'à 11:00 », avec créneaux disponibles.
|
||
|
||
## O. Multi-techniciens futur
|
||
|
||
La colonne actuelle `assigned_to_id` convient à la V1 mono-agent, mais ne doit
|
||
pas devenir une contrainte conceptuelle. Une évolution pourra ajouter une
|
||
association `PlannedTaskAssignee` (tâche, utilisateur, rôle), avec
|
||
`preferred_technician`, `required_skill` et `required_people_count`. Une
|
||
personne préférée n'est pas une personne assignée ; une compétence requise
|
||
est une contrainte, pas une préférence. Deux personnes seront représentées
|
||
par deux affectations liées à la même activité.
|
||
|
||
## P. Modèle de données recommandé
|
||
|
||
Réutiliser au maximum les tables présentes :
|
||
|
||
1. **Étendre `RoomSchedule`** : `source`, `protected_from_sync`,
|
||
`external_id`, horodatage de synchronisation, validité, type/libellé,
|
||
statut actif et, si nécessaire, référence au créneau supplanté. Index
|
||
futur sur `(room_id, week_start, day_of_week, start_time, end_time, source)`.
|
||
2. **Conserver `ScheduledTask`** comme échéance/exécution préventive ; ajouter
|
||
ultérieurement contrainte de priorité et fenêtre si les champs existants ne
|
||
suffisent pas.
|
||
3. **Utiliser `PlanningItem` comme projection de placement** (date, début,
|
||
fin, durée, salle, statut, priorité) et imposer une règle anti-doublon avec
|
||
les références `scheduled_task_id`/`intervention_id`/`admin_task_id`.
|
||
4. **Étendre `AdminTask` ou sa projection** pour responsable, statut,
|
||
priorité et fenêtre, sans perdre la récurrence.
|
||
5. **Rendez-vous entreprise** : d'abord `ScheduledTask`/`PlanningItem` avec
|
||
`company_id`; créer `ExternalCompanyAppointment` seulement si les étapes
|
||
deviennent des objets suivis.
|
||
|
||
Chaque futur index doit couvrir les requêtes par date/statut, salle/intervalle
|
||
et agent. Aucune table de planning parallèle ne doit être créée sans preuve
|
||
que `PlanningItem` ne peut pas être consolidé.
|
||
|
||
## Q. Migrations futures
|
||
|
||
Une migration non destructive ajoutera à `RoomSchedule` la provenance, la
|
||
protection, les identifiants/horodatages de synchronisation, la validité et le
|
||
statut. Les lignes existantes seront initialisées avec `source=manual` et
|
||
`protected_from_sync=false`, puis un écran de validation permettra de protéger
|
||
les exceptions locales. Les données Pronote historiques d'origine inconnue
|
||
seront marquées `import` et soumises à validation, jamais fusionnées à
|
||
l'aveugle. Le statut supplanté/archivé doit conserver le lien historique sans
|
||
réactiver automatiquement un faux créneau.
|
||
|
||
## R. Algorithme V1 déterministe
|
||
|
||
1. Construire les intervalles de travail de l'agent, en retirant pauses,
|
||
congés, formations, fermetures et permanences incompatibles.
|
||
2. Construire les intervalles occupés de la salle et du planning interne.
|
||
3. Générer les créneaux possibles par durée et fenêtre.
|
||
4. Éliminer conflits, hors horaires, échéances et rendez-vous fixes.
|
||
5. Classer par contrainte puis coût géographique, puis heure la plus proche.
|
||
6. Retourner une proposition explicable, sans commit automatique.
|
||
|
||
Une heuristique simple est préférable à un solveur d'optimisation complet :
|
||
elle est déterministe, testable, lisible et réversible.
|
||
|
||
## S. Interface proposée
|
||
|
||
### Vue journalière
|
||
|
||
Afficher pour chaque bloc : heure, bâtiment/zone/salle, titre, catégorie
|
||
(préventif, curatif, urgent, administratif, entreprise), durée, statut et
|
||
contrainte. Un panneau « Propositions » explique les créneaux refusés.
|
||
|
||
### Gestion des salles
|
||
|
||
Calendrier par salle/semaine, saisie manuelle claire, provenance visible et
|
||
indication « données Pronote anciennes » sans bloquer le planning interne.
|
||
|
||
### Conflits
|
||
|
||
Afficher la cause, la ressource concernée et deux ou trois créneaux
|
||
compatibles. L'utilisateur choisit et confirme.
|
||
|
||
## T. Tests à prévoir
|
||
|
||
- salle libre/occupée et chevauchement ;
|
||
- planning manuel sans Pronote ;
|
||
- Pronote prend le relais sur un manuel initial non protégé ;
|
||
- Pronote modifie l'heure d'un manuel initial ;
|
||
- manuel protégé jamais écrasé ;
|
||
- conflit Pronote/réservation protégée ;
|
||
- Pronote désactivé ou indisponible ;
|
||
- retour de Pronote après plusieurs jours et fraîcheur affichée ;
|
||
- suppression côté Pronote et manuel initial supplanté ;
|
||
- absence de doublon manuel + Pronote ;
|
||
- rapprochement ambigu nécessitant validation humaine ;
|
||
- préventif de 45 minutes entre deux cours ;
|
||
- durée/échéance d'une tâche liée à un lot ;
|
||
- pause, horaires hors travail, fermeture et permanence ;
|
||
- tâche administrative récurrente, réalisée et reportée ;
|
||
- entreprise avec accueil, accompagnement et signature ;
|
||
- urgence décalant deux tâches sans perte d'historique ;
|
||
- regroupement même salle/zone/bâtiment ;
|
||
- déplacement manuel vers rendez-vous fixe ;
|
||
- données sans agent/horaires explicites ;
|
||
- futur multi-techniciens compatible (test de structure, pas d'optimiseur).
|
||
|
||
## U. Scénarios métier de validation
|
||
|
||
1. **Rentrée progressive** : le gestionnaire saisit le planning manuellement,
|
||
planifie immédiatement la maintenance, puis Pronote fonctionne quelques
|
||
jours plus tard. Les cours compatibles reprennent les manuels initiaux,
|
||
sans doublon ; une réservation protégée reste intacte.
|
||
2. **Mise à jour Pronote** : importer un cours correspondant, modifier son
|
||
heure puis vérifier que seul le manuel initial non protégé est supplanté et
|
||
que l'historique reste lisible.
|
||
3. **Panne Pronote** : simuler une semaine sans connexion, conserver le
|
||
dernier planning Pronote connu et tous les manuels, avec date de fraîcheur.
|
||
4. **Retour de Pronote** : après plusieurs jours, synchroniser une nouvelle
|
||
version, traiter une suppression distante et demander validation pour un
|
||
rapprochement ambigu.
|
||
4. **Préventif interstitiel** : proposer 45 minutes entre deux cours en
|
||
respectant la pause et le temps de travail.
|
||
5. **Tournée locale** : cinq préventifs de la même zone sont regroupés avant
|
||
un autre bâtiment.
|
||
6. **Entreprise fixe** : rendez-vous chaudière à 09:00, accueil, contrôle,
|
||
signature et temps administratif visibles.
|
||
7. **Urgence** : fuite à 10:00, conserver le rendez-vous fixe, décaler deux
|
||
tâches déplaçables et faire valider la proposition.
|
||
8. **Journée mixte** : maintenance, commande, entreprise et intervention
|
||
curative avec horaires et statuts cohérents.
|
||
9. **Fermeture/permanence** : proposer une tâche pendant une journée rouverte
|
||
de vacances, refuser les dates fermées.
|
||
10. **Salle indisponible** : fermeture d'une salle, replanification vers une
|
||
autre salle compatible sans modifier silencieusement l'objet métier.
|
||
|
||
## V. MVP obligatoire
|
||
|
||
- planning interne des salles, saisie manuelle et provenance ;
|
||
- Pronote optionnel, import limité à ses propres créneaux ;
|
||
- durée, début/fin, statut et priorité des tâches ;
|
||
- respect horaires, pauses, fermetures et absences ;
|
||
- prévention/lot, interventions, tâches administratives ;
|
||
- rendez-vous entreprise simple ;
|
||
- urgences et propositions de replanification ;
|
||
- regroupement salle/zone/bâtiment ;
|
||
- déplacement manuel avec avertissements ;
|
||
- vue journalière lisible et déterministe.
|
||
|
||
## W. Évolutions futures et décisions à valider
|
||
|
||
**Plus tard :** multi-techniciens avancé, compétences, absences détaillées,
|
||
deux personnes par tâche, optimisation complexe, calcul de trajets, import
|
||
d'autres sources et statistiques de charge.
|
||
|
||
Avant développement, valider :
|
||
|
||
1. `RoomSchedule` comme base des occupations internes et ses champs de
|
||
provenance/protection ;
|
||
2. la règle « Pronote peut supplanter le manuel initial non protégé, mais ne
|
||
modifie jamais le manuel protégé » ;
|
||
3. l'usage de `PlanningItem` comme projection anti-doublon ;
|
||
4. le périmètre des tâches administratives et des étapes entreprise ;
|
||
5. le niveau de priorité/contrainte exposé aux utilisateurs ;
|
||
6. le comportement d'une donnée Pronote obsolète ;
|
||
7. la politique lorsque les horaires d'un agent sont absents ;
|
||
8. la future association multi-techniciens ;
|
||
9. les index et volumes acceptables pour la vue semaine/année.
|
||
|
||
### Décision recommandée
|
||
|
||
Retenir l'**option A** : `source = manual | pronote | import` complétée par
|
||
`protected_from_sync` et un statut historique (actif, supplanté, désactivé).
|
||
Elle évite de multiplier les valeurs de provenance tout en distinguant
|
||
clairement le manuel initial du manuel protégé. La vue interne résolue est la
|
||
seule entrée du moteur de maintenance ; Pronote ne devient jamais une
|
||
dépendance fonctionnelle.
|