diff --git a/gui_v6/tabs/tab_config.py b/gui_v6/tabs/tab_config.py index 2e09340..7221226 100644 --- a/gui_v6/tabs/tab_config.py +++ b/gui_v6/tabs/tab_config.py @@ -20,6 +20,31 @@ from gui_v6 import ui_kit from gui_v6.config_state import ConfigState, default_profile_key, list_profile_keys from manual_masking import ensure_mask_templates_dir, list_mask_templates, mask_template_label +NER_DISABLE_WARNING = ( + "Vous allez désactiver le moteur d'intelligence artificielle " + "(CamemBERT-bio).\n\n" + "Sans lui, la détection des NOMS de personnes repose uniquement sur des " + "règles (expressions régulières) : des noms peuvent rester EN CLAIR dans " + "les documents.\n\n" + "Pour un usage médical, garder ce moteur activé est fortement recommandé.\n\n" + "Confirmer la désactivation ?" +) + + +def confirm_ner_disable(asker) -> bool: + """Décision de désactivation du NER. + + ``asker`` est une fonction ``() -> bool`` (ex. ``messagebox.askyesno``), + injectée pour rester testable sans display. Retourne True si l'utilisateur + CONFIRME la désactivation (regex-only). Toute erreur de l'asker est traitée + comme un refus (sens sûr : le NER reste actif). + """ + try: + return bool(asker()) + except Exception: + return False + + _SUBTABS = [ ("reg", "⚙️ Réglages"), ("pro", "👤 Profils"), @@ -891,7 +916,19 @@ class ConfigTab(ctk.CTkFrame): setattr(self._state, field_name, bool(toggle.get())) def _on_ner(self) -> None: - self._state.use_local_ner = self._tog_ner.get() + new_value = self._tog_ner.get() + if not new_value: + confirmed = confirm_ner_disable( + lambda: messagebox.askyesno( + "Moteur de détection", NER_DISABLE_WARNING, icon="warning" + ) + ) + if not confirmed: + # Refus : rétablir l'affichage du switch et garder le NER actif. + self._tog_ner.var.set(True) + self._state.use_local_ner = True + return + self._state.use_local_ner = new_value def _on_eds(self) -> None: self._state.enable_eds = self._tog_eds.get() diff --git a/tests/unit/test_gui_v6_ner_confirm.py b/tests/unit/test_gui_v6_ner_confirm.py new file mode 100644 index 0000000..6576e02 --- /dev/null +++ b/tests/unit/test_gui_v6_ner_confirm.py @@ -0,0 +1,31 @@ +"""Confirmation avant de désactiver le NER (regex-only) — outil médical. + +Pur : la décision est isolée dans ``confirm_ner_disable(asker)`` ; ``asker`` est +injecté (pas de messagebox réel, pas de display). +""" +from __future__ import annotations + +from gui_v6.tabs.tab_config import NER_DISABLE_WARNING, confirm_ner_disable + + +def test_confirm_true_when_user_accepts(): + assert confirm_ner_disable(lambda: True) is True + + +def test_confirm_false_when_user_declines(): + assert confirm_ner_disable(lambda: False) is False + + +def test_confirm_false_when_asker_raises(): + def boom(): + raise RuntimeError("Tk indisponible") + # Sens sûr : une erreur de dialogue ne désactive jamais le NER. + assert confirm_ner_disable(boom) is False + + +def test_warning_text_is_explicit_for_medical_use(): + txt = NER_DISABLE_WARNING.lower() + # L'avertissement DOIT nommer la dégradation : règles/regex + risque noms. + assert "règles" in txt or "regex" in txt + assert "nom" in txt + assert "recommand" in txt # « fortement recommandé »