diff --git a/gui_v6/tabs/tab_config.py b/gui_v6/tabs/tab_config.py index 15b4c12..59b8f62 100644 --- a/gui_v6/tabs/tab_config.py +++ b/gui_v6/tabs/tab_config.py @@ -22,7 +22,6 @@ from manual_masking import ensure_mask_templates_dir, list_mask_templates, mask_ _SUBTABS = [ ("reg", "⚙️ Réglages"), - ("pro", "👤 Profils"), ("msk", "🎭 Masquage"), ("shr", "🔄 Partage"), ("rul", "🛡️ Règles"), @@ -57,10 +56,6 @@ _MASK_COLORS = [ ("Bleu marine", "#1e3a5f"), ] -_PRESERVE_TERMS = ["FUROSEMIDE", "rééducation fonctionnelle", "classification internationale"] -_MASK_TERMS = ["CHUXX"] -_STOPWORDS = ["hospitalisation", "contrôle", "prescription"] - MANUAL_MASK_NONE_LABEL = "Aucun masque manuel" # Textes d'aide « ? » (français simple, pour utilisateurs non informaticiens). @@ -101,7 +96,7 @@ _HELP_PROFIL = ( "(ex. : interne standard, diffusion prudente, recherche…).\n\n" "Il définit les moteurs utilisés, les données détectées, les termes à conserver " "ou à masquer, et si un masque manuel est requis.\n\n" - "Choisissez un profil ici, et consultez son détail dans l'onglet « Profils »." + "Choisissez un profil ici ; ses termes sont consultables via « Ouvrir le tableau des termes »." ) _HELP_MOTEURS = ( "Les moteurs détectent les données personnelles.\n\n" @@ -116,7 +111,7 @@ _HELP_LISTES = ( "• À conserver : termes à ne jamais masquer (vocabulaire métier).\n" "• À masquer : termes à toujours masquer (sigles, en-têtes…).\n" "• À ignorer : mots à ne pas considérer.\n\n" - "Pour une liste longue, ouvrez le tableau des termes (onglet « Profils ») : " + "Pour une liste longue, ouvrez le tableau des termes : " "il reste lisible et permet la recherche." ) @@ -226,7 +221,6 @@ class ConfigTab(ctk.CTkFrame): builders = { "reg": self._build_reglages, - "pro": self._build_profils, "msk": self._build_masquage, "shr": self._build_partage, "rul": self._build_regles, @@ -294,9 +288,6 @@ class ConfigTab(ctk.CTkFrame): self._profile_menu.set(current) self._profile_menu.pack(side="left", pady=10) ui_kit.help_button(top, p, _HELP_PROFIL, title="Profil d'anonymisation").pack(side="left", padx=(6, 0), pady=10) - ui_kit.secondary_button(top, p, "👤 Voir le profil", command=lambda: self._show_sub("pro")).pack( - side="left", padx=(10, 4), pady=10 - ) sortie = ui_kit.secondary_button(top, p, "📁 Dossier de sortie…", command=self._pick_output) sortie.pack(side="left", padx=(6, 6), pady=10) @@ -383,24 +374,32 @@ class ConfigTab(ctk.CTkFrame): terms_help, text="Termes propres à votre établissement", text_color=p["text_muted"], font=ui_kit.font(11), anchor="w" ).pack(side="left") ui_kit.help_button(terms_help, p, _HELP_LISTES, title="Listes locales").pack(side="right") - self._compact_tag_editor(terms, "Termes à conserver", "Ex : FUROSEMIDE", _PRESERVE_TERMS, "keep") - self._compact_tag_editor(terms, "Termes à masquer", "Ex : CHUXX", _MASK_TERMS, "mask") - self._compact_tag_editor(terms, "Mots à ignorer", "Ex : prescription", _STOPWORDS, "stop") - ctk.CTkButton( + ctk.CTkLabel( terms, - text="📋 Ouvrir le tableau des termes", - command=lambda: self._show_sub("pro"), - fg_color=p["btn_sec_bg"], - hover_color=p["card_border"], - text_color=p["text"], - border_color=p["btn_sec_border"], - border_width=1, - corner_radius=8, - height=30, + text="Les termes du profil actif sont consultables dans un tableau dédié.", + text_color=p["text_dim"], font=ui_kit.font(12), - ).pack(fill="x", padx=12, pady=(6, 12)) + justify="left", + wraplength=240, + anchor="w", + ).pack(fill="x", padx=12, pady=(2, 6)) + counts = self._active_profile_summary().list_counts + chips = ctk.CTkFrame(terms, fg_color="transparent") + chips.pack(fill="x", padx=12, pady=(0, 8)) + for label, count in counts.items(): + ctk.CTkLabel( + chips, + text=f"{label} : {count}", + text_color=p["text"], + fg_color=p["divider"], + corner_radius=8, + font=ui_kit.font(11, "bold"), + ).pack(side="left", padx=(0, 6), ipadx=7, ipady=2) + ui_kit.primary_button( + terms, p, "📋 Ouvrir le tableau des termes", command=self._open_terms_table + ).pack(fill="x", padx=12, pady=(2, 12)) - # -- Profils ---------------------------------------------------------- + # -- Profil actif / tableau des termes -------------------------------- def _active_profile_dict(self) -> dict: try: @@ -427,66 +426,6 @@ class ConfigTab(ctk.CTkFrame): rows = profile_term_rows(self._active_profile_dict()) TermsTableWindow(self.winfo_toplevel(), self._p, rows, profile_label=summary.label) - def _rebuild_profils(self) -> None: - panel = self._panels.get("pro") - if panel is None: - return - for child in panel.winfo_children(): - child.destroy() - self._build_profils(panel) - - def _build_profils(self, parent) -> None: - p = self._p - self._section_intro( - parent, - "Un profil regroupe tous les réglages d'anonymisation. Voici le profil actif.", - _HELP_PROFIL, - "Création / modification d'un profil d'anonymisation", - ) - summary = self._active_profile_summary() - - card = ui_kit.Card(parent, p, title=f"👤 {summary.label}") - card.pack(fill="x", pady=(0, 8)) - if summary.description: - self._note(card, summary.description) - grid = ctk.CTkFrame(card, fg_color="transparent") - grid.pack(fill="x", padx=12, pady=(0, 10)) - infos = [ - ("Masque manuel requis", "Oui" if summary.require_manual_mask else "Non"), - ("Template de masque", summary.mask_template or "—"), - ("Moteur VLM (images)", "désactivé" if summary.disable_vlm else "selon réglages"), - ] - for idx, (key, val) in enumerate(infos): - ctk.CTkLabel(grid, text=key, text_color=p["text_muted"], font=ui_kit.font(11), anchor="w").grid( - row=idx, column=0, sticky="w", pady=1 - ) - ctk.CTkLabel(grid, text=val, text_color=p["text"], font=ui_kit.font(11, "bold"), anchor="w").grid( - row=idx, column=1, sticky="w", padx=(12, 0), pady=1 - ) - grid.grid_columnconfigure(1, weight=1) - - lists_card = ui_kit.Card(parent, p, title="✅ Listes locales du profil") - lists_card.pack(fill="x", pady=(0, 8)) - chips = ctk.CTkFrame(lists_card, fg_color="transparent") - chips.pack(fill="x", padx=12, pady=(0, 8)) - for label, count in summary.list_counts.items(): - ctk.CTkLabel( - chips, - text=f"{label} : {count}", - text_color=p["text"], - fg_color=p["divider"], - corner_radius=8, - font=ui_kit.font(11, "bold"), - ).pack(side="left", padx=(0, 8), ipadx=8, ipady=3) - ui_kit.primary_button(lists_card, p, "📋 Ouvrir le tableau des termes", command=self._open_terms_table).pack( - anchor="w", padx=12, pady=(0, 12) - ) - - create = ui_kit.Card(parent, p, title="🧩 Créer / modifier un profil") - create.pack(fill="x") - self._note(create, "La création et la modification de profils seront disponibles prochainement.") - self._mockup_button(create, "+ Nouveau profil").pack(anchor="w", padx=12, pady=(0, 12)) - # -- Masquage --------------------------------------------------------- def _build_masquage(self, parent) -> None: @@ -691,7 +630,6 @@ class ConfigTab(ctk.CTkFrame): def _on_profile(self, value: str) -> None: self._state.profile = value - self._rebuild_profils() def _on_ner(self) -> None: self._state.use_local_ner = self._tog_ner.get() @@ -883,38 +821,6 @@ class ConfigTab(ctk.CTkFrame): row.get = lambda: bool(var.get()) # type: ignore[attr-defined] return row - def _compact_tag_editor(self, parent, title: str, placeholder: str, terms: list[str], kind: str) -> None: - p = self._p - color = {"keep": p["success"], "mask": p["primary"], "stop": p["warning"]}.get(kind, p["primary"]) - ctk.CTkLabel(parent, text=title, text_color=p["text"], font=ui_kit.font(12, "bold"), anchor="w").pack( - fill="x", padx=12, pady=(0, 2) - ) - row = ctk.CTkFrame(parent, fg_color="transparent") - row.pack(fill="x", padx=12, pady=(0, 5)) - ctk.CTkEntry( - row, - placeholder_text=placeholder, - fg_color=p["btn_sec_bg"], - border_color=p["btn_sec_border"], - text_color=p["text"], - height=28, - ).pack(side="left", fill="x", expand=True, padx=(0, 6)) - ui_kit.secondary_button(row, p, "+").pack(side="right") - cloud = ctk.CTkFrame(parent, fg_color="transparent") - cloud.pack(fill="x", padx=12, pady=(0, 8)) - for term in terms[:2]: - display = f"{term[:18]}{'…' if len(term) > 18 else ''} ×" - ctk.CTkLabel( - cloud, - text=display, - width=150, - anchor="w", - text_color=color, - fg_color=p["btn_sec_bg"], - corner_radius=99, - font=ui_kit.font(10), - ).pack(anchor="w", fill="x", pady=2, ipadx=5, ipady=2) - def _slider_row(self, parent, label: str, variable: ctk.IntVar, command) -> None: p = self._p row = ctk.CTkFrame(parent, fg_color="transparent") diff --git a/tests/unit/test_gui_v6_profiles.py b/tests/unit/test_gui_v6_profiles.py index 44a9a89..df6b2fd 100644 --- a/tests/unit/test_gui_v6_profiles.py +++ b/tests/unit/test_gui_v6_profiles.py @@ -118,13 +118,14 @@ def test_attach_tooltip_does_not_break_widget(ctk_root): assert lbl.winfo_exists() -def test_subtabs_include_profils(): +def test_subtabs_no_profils_subtab(): + """Retour Dom : le sous-onglet Profils (doublon non câblé) est retiré.""" from gui_v6.tabs.tab_config import _SUBTABS keys = [k for k, _ in _SUBTABS] labels = [lbl for _, lbl in _SUBTABS] - assert "pro" in keys - assert any("Profils" in lbl for lbl in labels) + assert "pro" not in keys + assert not any("Profils" in lbl for lbl in labels) def _all_texts(widget): @@ -150,11 +151,16 @@ def test_reglages_labels_renamed_and_profile_readable(ctk_root, tmp_path, monkey assert "Profil métier" not in texts assert "Dossier de sortie" in texts # addendum : « Sortie… » clarifié + # retour Dom : accès direct au tableau depuis Réglages, plus de pastilles inline + assert "Ouvrir le tableau des termes" in texts + assert "Voir le profil" not in texts + assert "FUROSEMIDE" not in texts # plus de pastilles de termes exemple inline + # profil lisible : résumé avec les 3 listes summary = tab._active_profile_summary() assert set(summary.list_counts.keys()) == {"À conserver", "À masquer", "À ignorer"} - # tableau des termes ouvrable sans erreur + # tableau des termes ouvrable DIRECTEMENT depuis Réglages (sans onglet Profils) tab._open_terms_table() tab.update_idletasks() tab.destroy()