From df9a402d27093e81f2e4c300640df3468aa8dbf4 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 22 Aug 2026 22:11:50 +0000 Subject: [PATCH] =?UTF-8?q?docs(planning):=20pr=C3=A9ciser=20relais=20Pron?= =?UTF-8?q?ote=20et=20cr=C3=A9neaux=20prot=C3=A9g=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ui-audit/PLANNING_ENGINE_PROPOSAL.md | 159 ++++++++++++++++------ 1 file changed, 117 insertions(+), 42 deletions(-) diff --git a/docs/ui-audit/PLANNING_ENGINE_PROPOSAL.md b/docs/ui-audit/PLANNING_ENGINE_PROPOSAL.md index 22df484..9ef5330 100644 --- a/docs/ui-audit/PLANNING_ENGINE_PROPOSAL.md +++ b/docs/ui-audit/PLANNING_ENGINE_PROPOSAL.md @@ -48,42 +48,90 @@ résolue avant d'automatiser la disponibilité. ## C. Principe de vérité interne La GMAO possède des créneaux internes d'occupation. Chaque créneau conserve -sa source : `manual`, `pronote`, `import` ou une future source. Les champs -conceptuels sont `source`, `external_id`, `last_synced_at`, -`source_updated_at`, période de validité et statut actif. Le métier parle de -créneau d'occupation interne ; il ne dépend jamais d'une réponse en direct de -Pronote. +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é. -L'application doit démarrer et rester pleinement utilisable avec -`PRONOTE_ENABLED=false`, sans token et sans synchronisation. +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 les `RoomSchedule` de source `manual` à partir +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. Les règles de -validation refusent début ≥ fin et signalent les chevauchements. Une saisie -manuelle est toujours conservée, même lorsqu'une source externe est absente. +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 -Pronote peut fournir des créneaux après configuration, mais ne possède jamais -la source interne de vérité. La synchronisation ne doit lire/écrire que les -créneaux qu'elle a créés (`source=pronote`, `external_id` connu). +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 -- Créneau manuel : jamais modifié ni supprimé automatiquement par Pronote. -- Créneau Pronote identique : mise à jour de ses champs et de - `last_synced_at`. -- Créneau supprimé côté Pronote : désactivation du seul créneau Pronote - correspondant ; aucune suppression manuelle. -- Collision manuel/Pronote : le manuel gagne, le conflit est signalé à - l'utilisateur avec les deux provenances. -- Pronote indisponible : conserver les dernières données importées, marquer - leur fraîcheur et afficher un avertissement ; ne jamais vider le planning. -- Donnée Pronote obsolète : elle peut être désactivée selon une politique de - rétention explicite, jamais effacée silencieusement. +### 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 @@ -188,9 +236,10 @@ par deux affectations liées à la même activité. Réutiliser au maximum les tables présentes : -1. **Étendre `RoomSchedule`** : `source`, `external_id`, horodatage de - synchronisation, validité, type/libellé et statut actif. Index futur sur - `(room_id, week_start, day_of_week, start_time, end_time, source)`. +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. @@ -209,11 +258,14 @@ que `PlanningItem` ne peut pas être consolidé. ## Q. Migrations futures -Les extensions ci-dessus nécessiteront des migrations Alembic non destructives, -avec valeurs par défaut (`source=manual`, statut actif) et backfill explicite -des `RoomSchedule` existants. Les données Pronote historiques ne doivent pas -être confondues avec le manuel : si leur origine est inconnue, les marquer -`import` et demander validation. +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 @@ -250,8 +302,15 @@ compatibles. L'utilisateur choisit et confirme. - salle libre/occupée et chevauchement ; - planning manuel sans Pronote ; -- Pronote désactivé, indisponible, puis synchronisé ; -- conflit manuel/Pronote et suppression distante ; +- 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 ; @@ -265,12 +324,18 @@ compatibles. L'utilisateur choisit et confirme. ## U. Scénarios métier de validation -1. **Début d'année manuel** : saisir les créneaux de plusieurs salles, - planifier une maintenance et vérifier l'absence de Pronote. -2. **Mise à jour Pronote** : importer un nouveau cours, modifier un cours - importé et vérifier qu'un créneau manuel reste intact. +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 et afficher sa date de fraîcheur. + 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 @@ -308,8 +373,9 @@ d'autres sources et statistiques de charge. Avant développement, valider : 1. `RoomSchedule` comme base des occupations internes et ses champs de - provenance ; -2. la règle « Pronote ne modifie jamais le manuel » ; + 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 ; @@ -317,3 +383,12 @@ Avant développement, valider : 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.