gui: Ajout indicateurs qualité (fuites, performances)
This commit is contained in:
@@ -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
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user