feat(gui): localiser les documents livrés + bouton ouvrir le dossier (P1-5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 19:10:14 +02:00
parent 416b347d7f
commit 1d65d42430
3 changed files with 193 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ import customtkinter as ctk
from gui_v6 import theme as theme_mod
from gui_v6 import ui_kit
from gui_v6.fsutil import open_in_file_manager
from gui_v6.processing_runner import ProcessingRunner, default_output_dir
_STEPS = ["📖 Extraction", "🧠 Détection", "🔒 Masquage", "📄 PDF final"]
@@ -33,6 +34,29 @@ _HELP_USAGE = (
)
def failure_hint(summary, output_dir) -> str | None:
"""Message localisant les documents livrés, ou None si run nominal.
Honnête : les documents en échec / quarantaine ne sont PAS anonymisés et
ne sont donc pas écrits. Si AUCUN document n'a abouti, on ne prétend pas
qu'un dossier de sortie contient des documents anonymisés.
"""
if summary is None or output_dir is None:
return None
if summary.failed == 0 and not getattr(summary, "stopped", False):
return None
if summary.succeeded == 0:
return (
"Aucun document n'a été anonymisé. Les documents en échec ou en "
"quarantaine ne sont PAS anonymisés et n'ont pas été écrits."
)
return (
f"Documents anonymisés écrits dans : {output_dir}\n"
"Les documents en échec ou en quarantaine ne sont PAS anonymisés et "
"n'ont pas été écrits."
)
class UsageTab(ctk.CTkFrame):
def __init__(
self,
@@ -59,6 +83,7 @@ class UsageTab(ctk.CTkFrame):
self._input_path: Path | None = None
self._output_dir: Path | None = None
self._last_output_dir: Path | None = None
self._stop_event: threading.Event | None = None
self._is_running = False
self._events: "queue.Queue[tuple]" = queue.Queue()
@@ -158,6 +183,7 @@ class UsageTab(ctk.CTkFrame):
self._stats_row = ctk.CTkFrame(self._rsec, fg_color="transparent")
self._stats_row.pack(fill="x", padx=16, pady=(0, 14))
self._result_built = False
self._hint_row = None
# -- thème ------------------------------------------------------------
@@ -211,6 +237,7 @@ class UsageTab(ctk.CTkFrame):
return
self._is_running = True
run_runner, run_output_dir = self._build_run_runner()
self._last_output_dir = run_output_dir or default_output_dir(self._input_path)
self._stop_event = threading.Event()
self._run_btn.configure(state="disabled")
self._psec.pack(fill="x", padx=14, pady=7)
@@ -281,6 +308,7 @@ class UsageTab(ctk.CTkFrame):
self._progress.set(1.0)
self._set_status(f"Terminé : {summary.succeeded} OK, {summary.failed} échec(s) sur {summary.total}.")
self._show_results(summary)
self._show_failure_hint(summary)
self._send_usage_telemetry(summary)
def _send_usage_telemetry(self, summary) -> None:
@@ -311,6 +339,28 @@ class UsageTab(ctk.CTkFrame):
ui_kit.StatCard(self._stats_row, p, value, label, value_color=color).pack(side="left", expand=True, fill="x", padx=4)
self._rsec.pack(fill="x", padx=14, pady=(7, 14))
def _show_failure_hint(self, summary) -> None:
# Nettoyage inconditionnel : éviter l'empilement et les hints périmés.
if getattr(self, "_hint_row", None) is not None:
self._hint_row.destroy()
self._hint_row = None
hint = failure_hint(summary, getattr(self, "_last_output_dir", None))
if hint is None:
return
p = self._p
self._hint_row = ctk.CTkFrame(self._rsec, fg_color="transparent")
self._hint_row.pack(fill="x", padx=16, pady=(0, 12))
ctk.CTkLabel(
self._hint_row, text=hint, text_color=p["text_dim"], font=ui_kit.font(11),
anchor="w", justify="left",
).pack(side="left", fill="x", expand=True)
# Bouton « ouvrir » uniquement s'il y a réellement des documents livrés.
if summary is not None and summary.succeeded > 0:
ui_kit.secondary_button(
self._hint_row, p, "📂 Ouvrir le dossier",
command=lambda: open_in_file_manager(self._last_output_dir),
).pack(side="right")
# -- helpers widgets --------------------------------------------------
def _set_status(self, text: str) -> None: