[EF-04] Le job silver refuse toute journée en cours : la grille compte 1440 minutes quelle que soit l'heure #135

Closed
opened 2026-09-04 07:50:46 +00:00 by lenaic · 2 comments
Owner

Exigence couverte

EF-04, EF-06

Épreuve servie

EC05 · Data, ETL et BI

Charge estimée

0,5 j.h

Ce qu'on veut obtenir

Le job de la zone argent refuse toute journée en cours, et il le fera tous les matins tant que la règle ne change pas.

Le contexte qui l'a fait apparaître. Le collecteur ne tourne que depuis le 3 septembre 16 h 15. Aucune journée complète n'existait avant ce matin, et la zone argent est restée vide alors que le code du #34 est fusionné et déployé. On a d'abord cru à une panne de la transformation. Ce n'en est pas une.

Le mécanisme. grille_du_jour() rend range(MINUTES_PAR_JOUR), soit 1440 minutes en dur. construire() passe minutes_attendues=len(grille) à disponibilite_du_jour(), qui calcule taux_de_trous = nulles / minutes_attendues. Le garde-fou du §11 refuse au delà de 30 %.

À 7 h du matin, 1000 minutes de la journée ne sont pas encore arrivées et comptent comme des trous. Le job annonce 70,1 % de trous alors que la couverture réelle de la collecte est de 100 % : 439 objets bronze par site pour 438 minutes écoulées depuis minuit UTC, 1024 passes du collecteur, toutes à 7 sites sur 7, zéro erreur.

Et l'en-tête du lanceur annonce l'inverse : « tourne une fois par heure, sur la journée courante », avec 0 * * * * en exemple. Les deux règles sont incompatibles. Le job ne peut pas réussir avant environ 17 h UTC, quel que soit l'état des données.

Le code lui-même est bon, et c'est le point important. Passe manuelle du 4 septembre à 7 h 36, seuil desserré, écriture détournée vers un seau silver-essai pour ne rien polluer :

SITE001  mesurées=391  imputées=66  disponibilité=31,7%
...
passe silver-2026-09-04 terminée : 10 080 lignes de mesure sur 7 sites

Trois tables Parquet écrites, mesure, journal_capteur, disponibilite_jour. L'imputation reconstruit environ 55 minutes par site. Le schéma porte, pour chaque grandeur, la valeur brute, la valeur retenue, la méthode, le drapeau et la raison d'invalidité, plus bronze_key, run_id, silver_run_id, processed_at et pipeline_version : on remonte de n'importe quelle ligne argent au fichier bronze qui l'a produite.

Seul le cadrage de la grille est en cause.

Critères d'acceptation

  • Sur la journée en cours, la grille s'arrête à la dernière minute écoulée en UTC, et le garde-fou compare à ce dénominateur.
  • Sur une journée passée, la grille reste à 1440 minutes : le rejeu d'un jour ancien ne change pas ses indicateurs.
  • disponibilite_jour dit sur quelle base le taux est calculé, pour qu'un taux de 5 % à 8 h du matin ne se lise pas comme un taux de 5 % sur une journée pleine.
  • Un cas d'essai fige une passe en milieu de journée et vérifie qu'elle écrit au lieu de refuser.
  • Un cas d'essai vérifie qu'une vraie journée trouée, au delà du seuil, est toujours refusée : le garde-fou ne doit pas être désarmé au passage.
  • Le §11 de la documentation est repris pour dire ce que le seuil compare.

Comment on le vérifie

Commande silver-daily.sh --jour <aujourd'hui>, lancé avant midi
Attendu la passe écrit, et le taux publié correspond aux minutes écoulées
Commande silver-daily.sh --jour
Attendu 1440 minutes attendues, indicateurs inchangés par rapport à la veille

Hors périmètre

La planification du job, qui est un sujet d'infrastructure traité à part : le lanceur n'est aujourd'hui appelé par aucune crontab, et deux obstacles empêchent d'en poser une.

Une alternative existe et mérite d'être pesée avant de coder : ne faire tourner le job que sur des journées terminées, à 00 h 30 pour la veille. Elle ne demande aucune modification du code, mais elle retarde d'un jour la disponibilité des données pour l'entraînement. Le choix décide de l'horaire retenu côté infra, les deux tickets sont donc à trancher ensemble.

### Exigence couverte EF-04, EF-06 ### Épreuve servie EC05 · Data, ETL et BI ### Charge estimée 0,5 j.h ### Ce qu'on veut obtenir Le job de la zone argent refuse toute journée en cours, et il le fera tous les matins tant que la règle ne change pas. **Le contexte qui l'a fait apparaître.** Le collecteur ne tourne que depuis le 3 septembre 16 h 15. Aucune journée complète n'existait avant ce matin, et la zone argent est restée vide alors que le code du #34 est fusionné et déployé. On a d'abord cru à une panne de la transformation. Ce n'en est pas une. **Le mécanisme.** `grille_du_jour()` rend `range(MINUTES_PAR_JOUR)`, soit 1440 minutes en dur. `construire()` passe `minutes_attendues=len(grille)` à `disponibilite_du_jour()`, qui calcule `taux_de_trous = nulles / minutes_attendues`. Le garde-fou du §11 refuse au delà de 30 %. À 7 h du matin, 1000 minutes de la journée ne sont pas encore arrivées et comptent comme des trous. Le job annonce **70,1 %** de trous alors que la couverture réelle de la collecte est de **100 %** : 439 objets bronze par site pour 438 minutes écoulées depuis minuit UTC, 1024 passes du collecteur, toutes à 7 sites sur 7, zéro erreur. **Et l'en-tête du lanceur annonce l'inverse** : « tourne une fois par heure, sur la journée courante », avec `0 * * * *` en exemple. Les deux règles sont incompatibles. Le job ne peut pas réussir avant environ 17 h UTC, quel que soit l'état des données. **Le code lui-même est bon**, et c'est le point important. Passe manuelle du 4 septembre à 7 h 36, seuil desserré, écriture détournée vers un seau `silver-essai` pour ne rien polluer : ``` SITE001 mesurées=391 imputées=66 disponibilité=31,7% ... passe silver-2026-09-04 terminée : 10 080 lignes de mesure sur 7 sites ``` Trois tables Parquet écrites, `mesure`, `journal_capteur`, `disponibilite_jour`. L'imputation reconstruit environ 55 minutes par site. Le schéma porte, pour chaque grandeur, la valeur brute, la valeur retenue, la méthode, le drapeau et la raison d'invalidité, plus `bronze_key`, `run_id`, `silver_run_id`, `processed_at` et `pipeline_version` : on remonte de n'importe quelle ligne argent au fichier bronze qui l'a produite. Seul le cadrage de la grille est en cause. ### Critères d'acceptation - [x] Sur la journée en cours, la grille s'arrête à la dernière minute écoulée en UTC, et le garde-fou compare à ce dénominateur. - [x] Sur une journée passée, la grille reste à 1440 minutes : le rejeu d'un jour ancien ne change pas ses indicateurs. - [x] `disponibilite_jour` dit sur quelle base le taux est calculé, pour qu'un taux de 5 % à 8 h du matin ne se lise pas comme un taux de 5 % sur une journée pleine. - [x] Un cas d'essai fige une passe en milieu de journée et vérifie qu'elle écrit au lieu de refuser. - [x] Un cas d'essai vérifie qu'une vraie journée trouée, au delà du seuil, est toujours refusée : le garde-fou ne doit pas être désarmé au passage. - [x] Le §11 de la documentation est repris pour dire ce que le seuil compare. ### Comment on le vérifie Commande silver-daily.sh --jour <aujourd'hui>, lancé avant midi Attendu la passe écrit, et le taux publié correspond aux minutes écoulées Commande silver-daily.sh --jour <un jour complet et sain> Attendu 1440 minutes attendues, indicateurs inchangés par rapport à la veille ### Hors périmètre La planification du job, qui est un sujet d'infrastructure traité à part : le lanceur n'est aujourd'hui appelé par aucune crontab, et deux obstacles empêchent d'en poser une. **Une alternative existe et mérite d'être pesée avant de coder** : ne faire tourner le job que sur des journées terminées, à 00 h 30 pour la veille. Elle ne demande aucune modification du code, mais elle retarde d'un jour la disponibilité des données pour l'entraînement. Le choix décide de l'horaire retenu côté infra, les deux tickets sont donc à trancher ensemble.
lenaic self-assigned this 2026-09-04 07:52:53 +00:00
Author
Owner

Traité par la #142, poussée et en attente de relecture.

La correction est faite, quinze cas ajoutés dont onze virent au rouge sur le
défaut réintroduit. 178 passed, ruff et mypy propres.

Un trou trouvé en me relisant, et corrigé dans un second commit : la passe de
23 h écrit jusqu'à 22 h 59 et celle de minuit porte déjà sur le jour suivant,
donc la dernière heure de chaque journée n'aurait jamais été écrite. À
minuit la passe ferme désormais la veille.

La chaîne est rouge sur cette demande, mais pour le #147 : une panne du point
d'audit npm, sans rapport avec ce ticket.

Traité par la **#142**, poussée et en attente de relecture. La correction est faite, quinze cas ajoutés dont onze virent au rouge sur le défaut réintroduit. `178 passed`, ruff et mypy propres. Un trou trouvé en me relisant, et corrigé dans un second commit : la passe de 23 h écrit jusqu'à 22 h 59 et celle de minuit porte déjà sur le jour suivant, donc **la dernière heure de chaque journée n'aurait jamais été écrite**. À minuit la passe ferme désormais la veille. La chaîne est rouge sur cette demande, mais pour le **#147** : une panne du point d'audit npm, sans rapport avec ce ticket.
lenaic added this to the EnerVision project 2026-09-04 08:57:34 +00:00
Author
Owner

Fusionné par la #142 et déployé sur le serveur. Les six critères sont
prouvés en production
, pas seulement en test.

La preuve qui compte : la même journée, avant et après

avant   SITE001  disponibilité 31,7 %   trous 983
après   SITE001  disponibilité 99,8 %   trous   1

Ce sont les mêmes données bronze. Seul le dénominateur a changé. Les 983
« trous » d'avant étaient des minutes qui n'étaient pas encore arrivées.

Passe du 4 septembre à 09 h 15 UTC, sur le lanceur déployé, écriture détournée
vers un seau d'essai pour ne pas préempter la preuve de la passe planifiée :

site=SITE001 mesurées=469 imputées=85 trous=1 disponibilité=99.8%
site=SITE002 mesurées=484 imputées=71 trous=0 disponibilité=100.0%
site=SITE003 mesurées=494 imputées=61 trous=0 disponibilité=100.0%
site=SITE004 mesurées=487 imputées=65 trous=3 disponibilité=99.5%
site=SITE005 mesurées=486 imputées=69 trous=0 disponibilité=100.0%
site=SITE006 mesurées=485 imputées=66 trous=4 disponibilité=99.3%
site=SITE007 mesurées=480 imputées=75 trous=0 disponibilité=100.0%
passe silver-2026-09-04-20260904T091518Z terminée : 3885 lignes sur 7 sites

3885 lignes, soit 7 sites × 555 minutes, à 09 h 15 UTC. Le compte est juste.

Critères 1 et 3, lus dans la table écrite

SITE001  attendues=555  mesurées=469  imputées=85  nulles=1
         dispo=0.9982   journée_complète=False
table mesure : 3885 lignes, de 2026-09-04 00:00:00+00 à 2026-09-04 09:14:00+00

minutes_attendues vaut 555 et non 1440 : le garde-fou compare bien au
dénominateur des minutes écoulées.

journee_complete vaut False : la ligne dit d'elle-même que ses taux portent
sur une journée en cours. C'est ce qui empêchera un entraînement de la prendre
pour une journée finie.

Et la table s'arrête à 09 h 14 alors que la passe tournait à 09 h 15 : la
minute en cours est exclue, comme prévu. Sans ça, chaque passe ouvrait un trou
d'une minute sur elle-même.

Critères 2, 4, 5 et 6

Éprouvés par la chaîne, verte sur develop. Quinze cas ajoutés, dont onze
virent au rouge sur le défaut réintroduit
dans grille.py. Le §11 de
docs/data/etl-pipeline.md dit maintenant sur quelle base le seuil compare.

Le critère 5 mérite d'être souligné : borner la grille ne devait pas désarmer le
garde-fou. Un cas vérifie qu'une collecte muette depuis 1 h 40 du matin est
toujours refusée.

Ce que la relecture a rattrapé au passage

La passe de 23 h écrit jusqu'à 22 h 59, et celle de minuit porte déjà sur le jour
suivant : la dernière heure de chaque journée n'aurait jamais été écrite. À
minuit la passe ferme désormais la veille, seul moment où la journée d'avant est
complète.

Fusionné par la #142 et déployé sur le serveur. **Les six critères sont prouvés en production**, pas seulement en test. ### La preuve qui compte : la même journée, avant et après ``` avant SITE001 disponibilité 31,7 % trous 983 après SITE001 disponibilité 99,8 % trous 1 ``` Ce sont **les mêmes données bronze**. Seul le dénominateur a changé. Les 983 « trous » d'avant étaient des minutes qui n'étaient pas encore arrivées. Passe du 4 septembre à 09 h 15 UTC, sur le lanceur déployé, écriture détournée vers un seau d'essai pour ne pas préempter la preuve de la passe planifiée : ``` site=SITE001 mesurées=469 imputées=85 trous=1 disponibilité=99.8% site=SITE002 mesurées=484 imputées=71 trous=0 disponibilité=100.0% site=SITE003 mesurées=494 imputées=61 trous=0 disponibilité=100.0% site=SITE004 mesurées=487 imputées=65 trous=3 disponibilité=99.5% site=SITE005 mesurées=486 imputées=69 trous=0 disponibilité=100.0% site=SITE006 mesurées=485 imputées=66 trous=4 disponibilité=99.3% site=SITE007 mesurées=480 imputées=75 trous=0 disponibilité=100.0% passe silver-2026-09-04-20260904T091518Z terminée : 3885 lignes sur 7 sites ``` 3885 lignes, soit 7 sites × 555 minutes, à 09 h 15 UTC. Le compte est juste. ### Critères 1 et 3, lus dans la table écrite ``` SITE001 attendues=555 mesurées=469 imputées=85 nulles=1 dispo=0.9982 journée_complète=False table mesure : 3885 lignes, de 2026-09-04 00:00:00+00 à 2026-09-04 09:14:00+00 ``` `minutes_attendues` vaut **555 et non 1440** : le garde-fou compare bien au dénominateur des minutes écoulées. `journee_complete` vaut **False** : la ligne dit d'elle-même que ses taux portent sur une journée en cours. C'est ce qui empêchera un entraînement de la prendre pour une journée finie. Et la table **s'arrête à 09 h 14** alors que la passe tournait à 09 h 15 : la minute en cours est exclue, comme prévu. Sans ça, chaque passe ouvrait un trou d'une minute sur elle-même. ### Critères 2, 4, 5 et 6 Éprouvés par la chaîne, verte sur `develop`. Quinze cas ajoutés, dont **onze virent au rouge sur le défaut réintroduit** dans `grille.py`. Le §11 de `docs/data/etl-pipeline.md` dit maintenant sur quelle base le seuil compare. Le critère 5 mérite d'être souligné : borner la grille ne devait pas désarmer le garde-fou. Un cas vérifie qu'une collecte muette depuis 1 h 40 du matin est toujours refusée. ### Ce que la relecture a rattrapé au passage La passe de 23 h écrit jusqu'à 22 h 59, et celle de minuit porte déjà sur le jour suivant : **la dernière heure de chaque journée n'aurait jamais été écrite.** À minuit la passe ferme désormais la veille, seul moment où la journée d'avant est complète.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
g2/enervision#135
No description provided.