Files
anonymisation/gui_v6/diagnostics.py
2026-06-30 10:36:16 +02:00

80 lines
2.6 KiB
Python

"""Diagnostics structurés de la GUI V6 (E2/E3) — RGPD strict.
On n'émet QUE des métadonnées techniques liste-blanche : type d'exception
(nom de classe), catégorie d'erreur d'un ensemble fermé, statut, ordinal,
durée. JAMAIS de nom/chemin/texte de document, ni de message d'exception brut.
L'envoi est non bloquant : un échec réseau n'interrompt jamais le traitement.
Patron : gui_v6/usage_telemetry.py (télémétrie d'usage).
"""
from __future__ import annotations
import json
import uuid
from pathlib import Path
from typing import Any, Callable, Iterable, Optional
# Clés autorisées par item de diagnostic (filtre RGPD appliqué à la construction).
_ALLOWED_ITEM_KEYS = {"ordinal", "status", "error_type", "error_code", "duration_ms"}
REPORT_PATH = "/api/v1/diagnostics/report"
def new_run_id() -> str:
return uuid.uuid4().hex
def items_from_summary(summary: Any) -> list[dict]:
"""Extrait les items de diagnostic (RGPD-safe) d'un ``RunSummary``.
Ne lit que les attributs autorisés ; aucun nom/chemin/message n'est lu.
"""
items: list[dict] = []
for item in getattr(summary, "documents", None) or []:
items.append(
{
"ordinal": getattr(item, "ordinal", 0),
"status": getattr(item, "status", "success"),
"error_type": getattr(item, "error_type", None),
"error_code": getattr(item, "error_code", None),
"duration_ms": getattr(item, "duration_ms", None),
}
)
return items
def build_diagnostics_payload(
*,
run_id: str,
app_name: str,
app_version: Optional[str],
license_ref: Optional[str],
machine_id: Optional[str],
duration_ms: Optional[int],
items: Iterable[dict],
) -> dict:
"""Construit le payload diagnostic. Chaque item est filtré aux seules clés
autorisées → aucun nom/chemin/message ne peut fuir, même fourni par erreur."""
clean_items: list[dict] = []
succeeded = failed = 0
for raw in items:
it = {k: raw[k] for k in _ALLOWED_ITEM_KEYS if k in raw}
status = it.get("status")
if status == "success":
succeeded += 1
elif status == "failed":
failed += 1
clean_items.append(it)
return {
"run_id": run_id,
"license_ref": license_ref,
"machine_id": machine_id,
"app_name": app_name,
"app_version": app_version,
"duration_ms": duration_ms,
"document_count": len(clean_items),
"succeeded_count": succeeded,
"failed_count": failed,
"items": clean_items,
}