[Correction] L'export anonymisé perd une partie du diagnostic (période, frise, saturation) #4

Open
opened 2026-09-24 15:26:11 +00:00 by srsk7896 · 0 comments
Owner

Origine : AUDIT_001, constat n° 2 (Élevée). À traiter dans le même chantier que ISSUE_005, qui concerne les graphiques du PDF anonymisé.

Problème

Pour l'export destiné au client (profil « client », FEAT-09), l'outil anonymise d'abord les données : il réduit les horodatages à la date seule et le modèle à « PBX Plug ». Ensuite seulement, il recalcule toute l'analyse. Sans heure, il ne peut plus calculer la période couverte, la frise chronologique, l'occupation des canaux ni les fenêtres de saturation. Sans modèle précis, la capacité du boîtier devient inconnue.

_projet/design.md prévoit pourtant que, dans l'export anonymisé, « le contenu de diagnostic est conservé ».

Comportement observé

Reproduit par un test jetable sur internal/cdr/testdata/v5_sample.txt et v6_sample.txt :

Interne Anonymisé
Période (HasPeriod) oui non
Frise (Series.Timeline.HasData) oui non
Pic d'occupation (PeakConcurrency) 1 0

Sur un boîtier réellement saturé, l'alerte de saturation (internal/analysis/analyze.go:326) disparaît de l'export client. Le verdict global peut alors passer de « hors seuil » à « conforme ».

Comportement attendu

Le diagnostic de l'export anonymisé est identique au diagnostic interne : verdict, alertes, KPI, séries. Seules les données identifiantes affichées sont masquées.

Étapes de reproduction

  1. Analyser une sortie CLI contenant des appels horodatés.
  2. Exporter en profil « interne », puis en profil « client », au format « page web ».
  3. Comparer la période, la frise et les fenêtres de saturation.

Hypothèse de cause

  • internal/web/export_handler.go:45-49 : anaData = export.Anonymize(parsed) puis analysis.Evaluate(anaData, now), soit une analyse après anonymisation.
  • internal/export/export.go:111 (reduceModel) et :124-125 (dateOnly sur SetupTime / ConnectionTime).
  • internal/analysis/analyze.go:146 (parseCallTime échoue sur une date seule, donc Start est vide) et internal/analysis/traffic.go:57-58 (les appels sans Start sont ignorés).

Proposition / solution

  1. Dans handleExporter, calculer result := analysis.Evaluate(parsed, now) une seule fois, sur les données en clair.
  2. En profil client, n'appliquer export.Anonymize qu'aux données affichées (View.Analysis), et ajouter une fonction qui anonymise aussi le résultat. Elle doit masquer les libellés horaires des séries : Series.Timeline.Buckets (format "02/01 15:04", internal/analysis/series.go:146) et Series.Timeline.Concurrency[].T (format "02/01 15:04:05", series.go:246). Par exemple, les remplacer par des repères relatifs (« T0 », « T0 + 10 min ») ou par la date seule, selon le profil client-2026.08.
  3. Vérifier qu'aucun autre champ de Result ne transporte un identifiant : numéros dans les exemples d'alerte, registrar, etc.
  4. Si le mode de masquage des séries change, incrémenter AnonProfileVersion (internal/export/export.go:103).

Critères d'acceptation

  • Test dans internal/export : pour v5_sample.txt et v6_sample.txt, Verdict (hors libellés), Alerts et SaturationWindows sont identiques en profil interne et en profil client.
  • Test : dans le résultat anonymisé, aucun libellé de série ne contient d'heure au format hh:mm.
  • Test : l'export client ne contient toujours ni numéro complet, ni IP, ni call-id, ni numéro de série (régression FEAT-09).
  • La page web, le PDF, le CSV et la synthèse courriel en profil client affichent la même période et les mêmes alertes que le profil interne, avec les masquages prévus.

Priorité proposée

Haute : le client peut recevoir un rapport plus favorable que la réalité, ce qui fausse la « situation de la panne par rapport à la limite de responsabilité », cœur de _projet/vision.md.

Périmètre estimé

  • Zone concernée : backend (internal/web/export_handler.go, internal/export, internal/analysis/series.go)
  • Impact attendu : localisé

Questions ouvertes

  • Pour la frise d'un export client, quel masquage retenir : repères relatifs (« T0 + n min ») ou date seule ? La réponse fixe le contenu du profil client-2026.08, ou de sa version suivante.

Labels proposés

  • type:bug
  • priority:high
  • area:backend
> Origine : AUDIT_001, constat n° 2 (Élevée). À traiter dans le même chantier que ISSUE_005, qui concerne les graphiques du PDF anonymisé. ## Problème Pour l'export destiné au client (profil « client », FEAT-09), l'outil anonymise d'abord les données : il réduit les horodatages à la date seule et le modèle à « PBX Plug ». Ensuite seulement, il **recalcule toute l'analyse**. Sans heure, il ne peut plus calculer la période couverte, la frise chronologique, l'occupation des canaux ni les fenêtres de saturation. Sans modèle précis, la capacité du boîtier devient inconnue. `_projet/design.md` prévoit pourtant que, dans l'export anonymisé, « le contenu de diagnostic est conservé ». ## Comportement observé Reproduit par un test jetable sur `internal/cdr/testdata/v5_sample.txt` et `v6_sample.txt` : | | Interne | Anonymisé | |---|---|---| | Période (`HasPeriod`) | oui | **non** | | Frise (`Series.Timeline.HasData`) | oui | **non** | | Pic d'occupation (`PeakConcurrency`) | 1 | **0** | Sur un boîtier réellement saturé, l'alerte de saturation (`internal/analysis/analyze.go:326`) disparaît de l'export client. Le verdict global peut alors passer de « hors seuil » à « conforme ». ## Comportement attendu Le diagnostic de l'export anonymisé est **identique** au diagnostic interne : verdict, alertes, KPI, séries. Seules les données identifiantes **affichées** sont masquées. ## Étapes de reproduction 1. Analyser une sortie CLI contenant des appels horodatés. 2. Exporter en profil « interne », puis en profil « client », au format « page web ». 3. Comparer la période, la frise et les fenêtres de saturation. ## Hypothèse de cause - `internal/web/export_handler.go:45-49` : `anaData = export.Anonymize(parsed)` puis `analysis.Evaluate(anaData, now)`, soit une analyse **après** anonymisation. - `internal/export/export.go:111` (`reduceModel`) et `:124-125` (`dateOnly` sur `SetupTime` / `ConnectionTime`). - `internal/analysis/analyze.go:146` (`parseCallTime` échoue sur une date seule, donc `Start` est vide) et `internal/analysis/traffic.go:57-58` (les appels sans `Start` sont ignorés). ## Proposition / solution 1. Dans `handleExporter`, calculer `result := analysis.Evaluate(parsed, now)` **une seule fois, sur les données en clair**. 2. En profil client, n'appliquer `export.Anonymize` qu'aux **données affichées** (`View.Analysis`), et ajouter une fonction qui anonymise aussi le résultat. Elle doit masquer les libellés horaires des séries : `Series.Timeline.Buckets` (format `"02/01 15:04"`, `internal/analysis/series.go:146`) et `Series.Timeline.Concurrency[].T` (format `"02/01 15:04:05"`, `series.go:246`). Par exemple, les remplacer par des repères relatifs (« T0 », « T0 + 10 min ») ou par la date seule, selon le profil `client-2026.08`. 3. Vérifier qu'aucun autre champ de `Result` ne transporte un identifiant : numéros dans les exemples d'alerte, registrar, etc. 4. Si le mode de masquage des séries change, incrémenter `AnonProfileVersion` (`internal/export/export.go:103`). ## Critères d'acceptation - [ ] Test dans `internal/export` : pour `v5_sample.txt` et `v6_sample.txt`, `Verdict` (hors libellés), `Alerts` et `SaturationWindows` sont identiques en profil interne et en profil client. - [ ] Test : dans le résultat anonymisé, aucun libellé de série ne contient d'heure au format `hh:mm`. - [ ] Test : l'export client ne contient toujours ni numéro complet, ni IP, ni call-id, ni numéro de série (régression FEAT-09). - [ ] La page web, le PDF, le CSV et la synthèse courriel en profil client affichent la même période et les mêmes alertes que le profil interne, avec les masquages prévus. ## Priorité proposée **Haute** : le client peut recevoir un rapport plus favorable que la réalité, ce qui fausse la « situation de la panne par rapport à la limite de responsabilité », cœur de `_projet/vision.md`. ## Périmètre estimé - Zone concernée : backend (`internal/web/export_handler.go`, `internal/export`, `internal/analysis/series.go`) - Impact attendu : localisé ## Questions ouvertes - Pour la frise d'un export client, quel masquage retenir : repères relatifs (« T0 + n min ») ou date seule ? La réponse fixe le contenu du profil `client-2026.08`, ou de sa version suivante. ## Labels proposés - `type:bug` - `priority:high` - `area:backend`
Sign in to join this conversation.
No labels
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.

Dependencies

No dependencies set

Reference
OneXpertise/PlugAnalytics#4
No description provided.