gui: Ajout indicateurs qualité (fuites, performances)

This commit is contained in:
2026-03-02 21:34:18 +01:00
parent 78adb3ba70
commit 3b1f6cdfbe
11 changed files with 2518 additions and 1845 deletions

View File

@@ -155,6 +155,7 @@ class UiMessage:
ko: int = 0
masked: int = 0
outdir: str = ""
total_time: float = 0.0 # Temps total de traitement en secondes
# ---------------------------------------------------------------------------
@@ -527,6 +528,29 @@ class App:
self._stat_masked = self._make_stat_card(stats_row, "0", "données masquées", CLR_PRIMARY, CLR_PRIMARY_LIGHT, 1)
self._stat_errors = self._make_stat_card(stats_row, "0", "erreurs", CLR_TEXT_SECONDARY, "#f3f4f6", 2)
# Indicateurs de qualité et sécurité
quality_row = tk.Frame(self._results_frame, bg=CLR_BG)
quality_row.pack(fill=tk.X, pady=(0, 12))
# Badge de fuites
self._leak_badge = tk.Label(
quality_row,
text="🔒 Vérification en cours...",
font=self._f_body_bold,
bg=CLR_BLUE_LIGHT, fg=CLR_PRIMARY,
padx=12, pady=6,
)
self._leak_badge.pack(side=tk.LEFT, padx=(0, 8))
# Temps de traitement
self._perf_label = tk.Label(
quality_row,
text="⏱️ Calcul en cours...",
font=self._f_small,
bg=CLR_BG, fg=CLR_TEXT_SECONDARY,
)
self._perf_label.pack(side=tk.LEFT)
self.btn_open_out = tk.Button(
self._results_frame, text="Ouvrir le dossier de résultats",
font=self._f_button, bg=CLR_GREEN, fg="white",
@@ -692,6 +716,9 @@ class App:
threading.Thread(target=self._worker, args=(folder, pdfs), daemon=True).start()
def _worker(self, folder: Path, pdfs: List[Path]):
import time
start_time = time.time()
try:
outdir = folder / "anonymise"
outdir.mkdir(exist_ok=True)
@@ -755,10 +782,11 @@ class App:
self.queue.put(UiMessage(kind=MsgType.LOG, text=f"\u2717 {pdf.name} \u2192 ERREUR: {e}"))
ko += 1
total_time = time.time() - start_time
total_masked = sum(global_counts.values())
self.queue.put(UiMessage(
kind=MsgType.DONE, ok=ok, ko=ko, masked=total_masked,
outdir=str(outdir),
outdir=str(outdir), total_time=total_time,
))
if ok:
self.queue.put(UiMessage(
@@ -767,7 +795,8 @@ class App:
))
except Exception as e:
self.queue.put(UiMessage(kind=MsgType.LOG, text=f"Erreur fatale : {e}"))
self.queue.put(UiMessage(kind=MsgType.DONE, ok=0, ko=len(pdfs), masked=0, outdir=""))
total_time = time.time() - start_time
self.queue.put(UiMessage(kind=MsgType.DONE, ok=0, ko=len(pdfs), masked=0, outdir="", total_time=total_time))
# ---------------------------------------------------------------
# Pompe de messages
@@ -840,6 +869,14 @@ class App:
if msg.outdir:
self._last_outdir = Path(msg.outdir)
# Vérifier les fuites
leak_count = self._check_leaks(Path(msg.outdir))
self._update_leak_indicator(leak_count)
# Calculer les performances
perf_string = self._calculate_performance(msg.ok, msg.total_time)
self._perf_label.configure(text=perf_string)
self._show_results(msg.ok, msg.ko, msg.masked)
@@ -917,6 +954,75 @@ class App:
pass
return d
# ---------------------------------------------------------------
# Vérification des fuites
# ---------------------------------------------------------------
def _check_leaks(self, output_dir: Path) -> int:
"""Vérifie les fuites dans les textes anonymisés."""
leak_count = 0
try:
# Patterns de fuites critiques
import re
patterns = {
"date_naissance": re.compile(r"(?:n[ée]+\s+le|DDN)\s*:?\s*\d{1,2}[/.\-]\d{1,2}[/.\-]\d{2,4}", re.IGNORECASE),
"chcb": re.compile(r"\bCHCB\b", re.IGNORECASE),
}
for txt_file in output_dir.glob("*.pseudonymise.txt"):
try:
with open(txt_file, 'r', encoding='utf-8') as f:
content = f.read()
for pattern in patterns.values():
matches = pattern.findall(content)
leak_count += len(matches)
except Exception:
pass
except Exception:
pass
return leak_count
# ---------------------------------------------------------------
# Calcul des performances
# ---------------------------------------------------------------
def _calculate_performance(self, total_files: int, total_time: float) -> str:
"""Calcule et formate les performances de traitement."""
if total_files == 0 or total_time == 0:
return "⏱️ Temps de traitement non disponible"
avg_time = total_time / total_files
# Formater le temps total
if total_time < 60:
time_str = f"{total_time:.0f}s"
elif total_time < 3600:
minutes = int(total_time // 60)
seconds = int(total_time % 60)
time_str = f"{minutes}m {seconds}s"
else:
hours = int(total_time // 3600)
minutes = int((total_time % 3600) // 60)
time_str = f"{hours}h {minutes}m"
return f"⏱️ Traité en {time_str} ({avg_time:.1f}s/document)"
# ---------------------------------------------------------------
# Mise à jour de l'indicateur de fuites
# ---------------------------------------------------------------
def _update_leak_indicator(self, leak_count: int):
"""Met à jour l'indicateur de fuites."""
if leak_count == 0:
self._leak_badge.configure(
text="🔒 0 fuite détectée",
bg=CLR_GREEN_LIGHT, fg=CLR_GREEN
)
else:
self._leak_badge.configure(
text=f"⚠️ {leak_count} fuite{'s' if leak_count > 1 else ''} potentielle{'s' if leak_count > 1 else ''}",
bg=CLR_RED_LIGHT, fg=CLR_RED
)
# ---------------------------------------------------------------
# Chargement automatique NER au démarrage
# ---------------------------------------------------------------