[EF-09] Migration recommandation : cycle de vie (statut, resolue_a, action) #164
Labels
No labels
Compat/Breaking
EC01
EC02
EC03
EC04
EC05
EC06
Kind/BDD
Kind/Back
Kind/Bug
Kind/CICD
Kind/Cloud
Kind/Contenu
Kind/Data
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Front
Kind/IA
Kind/Infra
Kind/Monitoring
Kind/Security
Kind/Testing
Portée/Post-jury
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
ops/alerte
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
g2/enervision#164
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Exigence couverte
EF-09
Épreuve servie
EC05 · Data, ETL et BI
Charge estimée
0,5 j.h
Ce qu'on veut obtenir
Persister le cycle de vie d'une recommandation (active → résolue) dans la zone or, décidé sur #153 entre Olivier et moi le 2026-09-07 : ajout de colonnes plutôt que réinterprétation du rejeu idempotent actuel de
recommandation(0014).cycle.rapprocher(#154, #39) est déjà écrit pour ce modèle et attend de savoir où écrire.Critères d'acceptation
0017_zone_or_recommandation_cycle.sqlajoute àrecommandationles colonnesstatut(active/resolue, défautactive),resolue_a(timestamptz, nullable) etaction(text not null), sans retoucher 0014.(site_id, horodatage, regle_id, regle_version)est retirée et remplacée par un index unique partiel(site_id, regle_id) where statut = 'active'.0017figure dans la liste figéeNOUVEAUXdetests/unit/db/test_migrations_zone_or.py.action text not nullface à des lignes déjà présentes en préprod est vérifié et tranché (default ''puisdrop default, ou nullable).Comment on le vérifie
Tests tests/unit/db/test_migrations_zone_or.py
Commande pytest tests/unit/db/test_migrations_zone_or.py -v puis python db/migrate.py et \d recommandation dans psql
Preuve sortie pytest + sortie \d recommandation, collées à la fermeture
Hors périmètre
Brancher
cycle.rapprocherà cette persistance (lecture/écriture depuis le servicerecommendations) — ticket séparé une fois le schéma posé. Pas de changement API ni dashboard.Fait, dans la demande de fusion #180 — branche
olivier/164-recommandation-cycle.Un écart à signaler : le fichier est la 0019, pas la 0017
Le critère 1 nomme
0017_zone_or_recommandation_cycle.sql. Entre l'écriture du ticket et maintenant, 0017 a été prise par le #36 (référence de comparaison des prévisions) et 0018 par le #168 (libellé et état des alertes) — les deux fusionnées dansdevelop. Le fichier s'appelle donc0019_zone_or_recommandation_cycle.sql, contenu et intention inchangés.Rien n'est perdu au passage : la 0018 avait posé le rendez-vous en toutes lettres — «
resolue_avient avec le cycle de vie des recommandations au #164 ».Les cinq critères
statut(active/resolue, défautactive),resolue_a(timestamptznullable) etaction(text not null). La 0014 n'est pas retouchée.unique (site_id, horodatage, regle_id, regle_version)retirée, remplacée par l'index unique partiel(site_id, regle_id) where statut = 'active'.ev-postgres(supprimées à la fin, ni la préprod ni la prod touchées) : base neuve 0001 → 0019 d'affilée, base arrêtée à 0018 puis 0019 seule, et la 0019 une seconde fois sur les deux. Le retour arrière écrit en fin de fichier a été joué : la table revient aux huit colonnes de la 0014 et retrouve son ancienne contrainte sous le nom exact écrit dans ledrop constraint.0019_zone_or_recommandation_cycle.sqlfigure dansNOUVEAUXdetests/unit/db/test_migrations_zone_or.py.default ''puisdrop default, et non le nullable. Motif ci-dessous.Le critère 5, et ce qui a été mesuré
La table est vide partout aujourd'hui : rien n'y écrit. Le chargement de la zone or ne remplit que
mesure,qualite_jouretalerte, et/api/v1/sites/{id}/recommandationssert des fixtures. Unadd column ... not nullsans défaut serait donc passé.Il n'a pas été retenu, parce qu'il ne marche que tant que c'est vrai : un
not nullsans défaut échoue dès la première ligne présente, et les migrations s'appliquent au démarrage du serveur — à un moment que personne ne choisit. Le jour où le job du #39 écrit sur la préprod avant que la 0019 n'y passe, le serveur ne redémarre plus.Le retrait du défaut compte autant que le défaut : sans lui,
not nullne garderait plus rien, la contrainte étant toujours satisfaite par une chaîne vide que personne n'a voulue. Le nullable a été écarté pour la raison qui vaut déjà côté moteur, oùregle.pyrefuse d'émettre : « une recommandation qui ne dit pas quoi faire n'est pas une recommandation ».Vérifié sur une base portant déjà une ligne écrite avant la migration :
Une contrainte de plus que le ticket ne demandait
check ((statut = 'resolue') = (resolue_a is not null)). L'état et son instant ne peuvent pas se contredire — c'est l'invariant pour lequelresolue_aexiste. Signalé parce que les critères ne l'énuméraient pas ; à retirer si le groupe préfère.Les gardes, poussées et non supposées
recommandation_activeactionnot nullstatutinconnurecommandation_statutresolue_a, ou active avec unresolue_arecommandation_resolue_aPreuve —
\d recommandationPreuve — pytest
pytest tests/unitsur l'ensemble du dépôt : 932 passés, 1 sauté.ruff checketruff format --checkpropres.Ce qui reste, comme le ticket le pose en hors périmètre
Brancher
cycle.rapprochersur cette persistance — lire les actives, écrire les ouvertures et les résolutions depuis le servicerecommendations— appartient au ticket séparé annoncé ici. Le schéma est posé, il attend son job.