diff --git a/gui_v6/tabs/tab_config.py b/gui_v6/tabs/tab_config.py index b89a725..4c2088f 100644 --- a/gui_v6/tabs/tab_config.py +++ b/gui_v6/tabs/tab_config.py @@ -24,7 +24,6 @@ _SUBTABS = [ ("reg", "⚙️ Réglages"), ("pro", "👤 Profils"), ("shr", "🔄 Partage"), - ("rul", "🛡️ Règles"), ] _DETECTION_OPTIONS = [ @@ -210,7 +209,6 @@ class ConfigTab(ctk.CTkFrame): "reg": self._build_reglages, "pro": self._build_profils, "shr": self._build_partage, - "rul": self._build_regles, } for key, builder in builders.items(): panel = ctk.CTkFrame(self._body, fg_color="transparent") @@ -528,7 +526,25 @@ class ConfigTab(ctk.CTkFrame): rules = ui_kit.Card(parent, p, title="🛡️ Règles du profil") rules.pack(fill="x", pady=(8, 0)) - self._note(rules, "Les règles embarquées par profil seront éditables prochainement.") + rules_intro = ctk.CTkFrame(rules, fg_color="transparent") + rules_intro.pack(fill="x", padx=12, pady=(0, 2)) + ctk.CTkLabel( + rules_intro, + text="Règles d'anonymisation portées par ce profil (adaptées à votre établissement).", + text_color=p["text_dim"], font=ui_kit.font(12), anchor="w", justify="left", wraplength=520, + ).pack(side="left", padx=(0, 6)) + ui_kit.help_button(rules_intro, p, _HELP_REGLES, title="Les Règles du profil").pack(side="right") + headers = ctk.CTkFrame(rules, fg_color="transparent") + headers.pack(fill="x", padx=12, pady=(2, 4)) + for text, width in [("Label", 190), ("Type", 80), ("Cible → Résultat", 210), ("Statut", 70), ("", 70)]: + ctk.CTkLabel(headers, text=text.upper(), width=width, anchor="w", text_color=p["text_muted"], font=ui_kit.font(10, "bold")).pack(side="left") + for row in [ + ("Masquer le sigle CHUXX", "exact", "CHUXX → [MASK]", "Actif"), + ("Préserver “classification internationale”", "preserve", "conservé tel quel", "Actif"), + ("Identifier N° 1234567", "norm-id", "N° 1234567 → [NDA]", "Candidat"), + ]: + self._rule_row(rules, row) + self._note(rules, "Aperçu illustratif. L'édition fine des règles du profil arrivera dans une prochaine version.") self._mockup_button(rules, "+ Ajouter une règle").pack(anchor="w", padx=12, pady=(0, 12)) self._pro_refresh_and_load() @@ -716,43 +732,6 @@ class ConfigTab(ctk.CTkFrame): self._note(import_card, "Fusionne la configuration reçue avec vos réglages locaux.") self._mockup_button(import_card, "⬆ Importer (.json)").pack(anchor="w", padx=12, pady=(0, 12)) - def _build_regles(self, parent) -> None: - p = self._p - self._section_intro( - parent, - "Règles d'adaptation du moteur à votre établissement.", - _HELP_REGLES, - "Les Règles", - ) - card = ui_kit.Card(parent, p, title="🛡️ Règles actives") - card.pack(fill="x", pady=(0, 8)) - self._note(card, "Ces règles adaptent le moteur à votre établissement. Chaque règle est validée avant activation.") - headers = ctk.CTkFrame(card, fg_color="transparent") - headers.pack(fill="x", padx=12, pady=(0, 4)) - for text, width in [("Label", 190), ("Type", 80), ("Cible → Résultat", 210), ("Statut", 70), ("", 70)]: - ctk.CTkLabel(headers, text=text.upper(), width=width, anchor="w", text_color=p["text_muted"], font=ui_kit.font(10, "bold")).pack(side="left") - for row in [ - ("Masquer le sigle CHUXX", "exact", "CHUXX → [MASK]", "Actif"), - ("Préserver “classification internationale”", "preserve", "conservé tel quel", "Actif"), - ("Identifier N° 1234567", "norm-id", "N° 1234567 → [NDA]", "Candidat"), - ]: - self._rule_row(card, row) - actions = ctk.CTkFrame(card, fg_color="transparent") - actions.pack(fill="x", padx=12, pady=(8, 12)) - self._mockup_button(actions, "+ Nouvelle règle", primary=True).pack(side="left", padx=(0, 8)) - self._mockup_button(actions, "🔄 Recharger").pack(side="left") - - sim = ui_kit.Card(parent, p, title="🧪 Testeur de règle") - sim.pack(fill="x") - ctk.CTkLabel(sim, text="Texte de test", text_color=p["text_muted"], font=ui_kit.font(12)).pack(anchor="w", padx=12) - txt = ctk.CTkTextbox(sim, height=74, fg_color=p["divider"], text_color=p["text"], border_color=p["card_border"], border_width=1) - txt.pack(fill="x", padx=12, pady=(5, 8)) - txt.insert("1.0", "Compte rendu CHUXX, patient N° 1234567.") - btns = ctk.CTkFrame(sim, fg_color="transparent") - btns.pack(fill="x", padx=12, pady=(0, 12)) - self._mockup_button(btns, "▶ Tester", primary=True).pack(side="left", padx=(0, 8)) - self._mockup_button(btns, "✖ Fermer").pack(side="left") - # -- helpers aide / maquette ----------------------------------------- def _section_intro(self, parent, sentence: str, help_text: str, help_title: str) -> None: diff --git a/tests/unit/test_gui_v6_app_shell.py b/tests/unit/test_gui_v6_app_shell.py index 5fb922d..4e0d59d 100644 --- a/tests/unit/test_gui_v6_app_shell.py +++ b/tests/unit/test_gui_v6_app_shell.py @@ -53,13 +53,15 @@ def test_main_tab_renamed_to_administration(): assert not any("Configuration" in lbl for lbl in labels) -def test_rules_subtab_has_no_unexplained_2(): - """Retour Dom #3 : « Règles 2 » incompréhensible → simple « Règles ».""" +def test_no_separate_rules_subtab(): + """Retour Dom : les règles appartiennent au profil → plus de sous-onglet + « Règles » séparé (et donc plus de « Règles 2 » incompréhensible).""" from gui_v6.tabs.tab_config import _SUBTABS + keys = [key for key, _ in _SUBTABS] labels = [label for _, label in _SUBTABS] - assert any(lbl.strip() == "🛡️ Règles" for lbl in labels) - assert not any("Règles 2" in lbl or "Règles 2" in lbl for lbl in labels) + assert "rul" not in keys + assert not any("Règles" in lbl for lbl in labels) def test_help_button_opens_help_window(app): diff --git a/tests/unit/test_gui_v6_profiles.py b/tests/unit/test_gui_v6_profiles.py index 03a49b8..1da9c54 100644 --- a/tests/unit/test_gui_v6_profiles.py +++ b/tests/unit/test_gui_v6_profiles.py @@ -273,3 +273,25 @@ def test_masquage_moved_into_profils(ctk_root, tmp_path, monkeypatch): tab._on_mask_template_saved(saved) assert tab._pro_template_var.get().endswith("depuis_editeur.json") tab.destroy() + + +def test_regles_moved_into_profils(ctk_root, tmp_path, monkeypatch): + """Retour Dom : le sous-onglet Règles séparé est retiré ; les règles du + profil sont une section de Profils.""" + from gui_v6.tabs import tab_config + + keys = [k for k, _ in tab_config._SUBTABS] + assert "rul" not in keys # plus de sous-onglet Règles séparé + + monkeypatch.setattr(tab_config, "_app_base_dir", lambda: tmp_path) + tab = tab_config.ConfigTab(ctk_root) + tab._show_sub("pro") + tab.update_idletasks() + + # la section des règles du profil est dans le panneau Profils + texts = " | ".join(_all_texts(tab._panels["pro"])) + assert "Règles d'anonymisation portées par ce profil" in texts + assert "Masquer le sigle CHUXX" in texts # table de règles relocalisée dans Profils + # le builder du sous-onglet séparé n'existe plus + assert "rul" not in tab._panels + tab.destroy()