refactor(gui): intégrer les Règles dans Administration > Profils

Retour Dom : « les règles du profil doivent être dans le menu profil, pas à
part ! ». Même logique que le Masquage — les règles qui influencent
l'anonymisation appartiennent au profil ; un sous-onglet séparé crée la
même confusion.

- Retrait du sous-onglet « Administration > Règles » (_SUBTABS, builder,
  méthode _build_regles supprimée). Sous-onglets restants : Réglages /
  Profils / Partage.
- Section « Profils > Règles du profil » enrichie : wording clair (règles
  d'anonymisation portées par le profil), aperçu illustratif de la table
  des règles (réutilise _rule_row + _HELP_REGLES), édition fine annoncée
  « à venir ».
- Abandon du « Testeur de règle » (écran outil global) pour ne pas
  réintroduire un second réglage métier.

Cible UX : Réglages / Profils (Général・Masquage・Mots・Moteurs・Règles du
profil) / Partage. Test obsolète test_rules_subtab_has_no_unexplained_2
remplacé par test_no_separate_rules_subtab.

262 tests unit OK (0 régression), self-test OK, nav 3 sous-onglets + section
Règles dans Profils + thème OK. Préserve d8bc0cd + GO Qwen. Aucun build/push
sans GO Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-16 12:00:10 +02:00
parent d8bc0cd8c8
commit 764cf00581
3 changed files with 47 additions and 44 deletions

View File

@@ -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: