feat(gui): recâbler import/export de configuration par email (P1-3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,12 +170,15 @@ _HELP_PROFIL_MOTS = (
|
||||
"Pour de longues listes, utilisez le tableau des termes afin de rechercher et vérifier plus facilement."
|
||||
)
|
||||
_HELP_EXPORT_CONFIG = (
|
||||
"Exporte uniquement les réglages de l'application : profils, listes locales, règles et style de masque.\n\n"
|
||||
"Exporte vos deux listes locales — termes à toujours conserver et termes à toujours "
|
||||
"masquer — dans un fichier .json à envoyer à un autre poste ou à l'administrateur.\n\n"
|
||||
"Les documents patients, résultats d'anonymisation et audits ne sont pas exportés."
|
||||
)
|
||||
_HELP_IMPORT_CONFIG = (
|
||||
"Importe des réglages reçus d'un administrateur ou d'un autre poste.\n\n"
|
||||
"L'import ne lit pas de documents patients. Vérifiez toujours le profil actif après import."
|
||||
"Importe les deux listes locales (termes à conserver, termes à masquer) reçues d'un "
|
||||
"autre poste ou de l'administrateur. La fusion ajoute ces termes à votre configuration "
|
||||
"sans rien écraser.\n\n"
|
||||
"L'import ne lit pas de documents patients. Redémarrez l'application pour appliquer."
|
||||
)
|
||||
|
||||
CONFIG_MOCKUP_SECTIONS = {
|
||||
@@ -223,10 +226,11 @@ def _app_base_dir() -> Path:
|
||||
|
||||
|
||||
class ConfigTab(ctk.CTkFrame):
|
||||
def __init__(self, master, state: ConfigState | None = None, palette: dict | None = None, **kwargs):
|
||||
def __init__(self, master, state: ConfigState | None = None, palette: dict | None = None, config_path: Path | None = None, **kwargs):
|
||||
self._p = palette or theme_mod.get_palette(theme_mod.DEFAULT_THEME)
|
||||
super().__init__(master, fg_color=self._p["bg"], **kwargs)
|
||||
self._state = state if state is not None else ConfigState()
|
||||
self._config_path = config_path
|
||||
self._sub = "reg"
|
||||
self._sub_buttons: dict[str, ctk.CTkButton] = {}
|
||||
self._panels: dict[str, ctk.CTkFrame] = {}
|
||||
@@ -868,16 +872,20 @@ class ConfigTab(ctk.CTkFrame):
|
||||
help_text=_HELP_EXPORT_CONFIG, help_title="Exporter la configuration",
|
||||
)
|
||||
export.pack(fill="both", expand=True)
|
||||
self._note(export, "Listes locales, règles admin, style de masquage et template actif.")
|
||||
self._mockup_button(export, "⬇ Exporter (.json)").pack(anchor="w", padx=12, pady=(0, 12))
|
||||
self._note(export, "Vos listes locales : termes à toujours conserver et termes à toujours masquer.")
|
||||
ui_kit.secondary_button(
|
||||
export, p, "⬇ Exporter (.json)", command=self._on_export_config
|
||||
).pack(anchor="w", padx=12, pady=(0, 12))
|
||||
|
||||
import_card = ui_kit.Card(
|
||||
cols[1], p, title="📥 Importer une configuration",
|
||||
help_text=_HELP_IMPORT_CONFIG, help_title="Importer une configuration",
|
||||
)
|
||||
import_card.pack(fill="both", expand=True)
|
||||
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))
|
||||
self._note(import_card, "Fusionne les listes reçues (à conserver / à masquer) avec vos listes locales.")
|
||||
ui_kit.secondary_button(
|
||||
import_card, p, "⬆ Importer (.json)", command=self._on_import_config
|
||||
).pack(anchor="w", padx=12, pady=(0, 12))
|
||||
|
||||
# -- helpers aide / maquette -----------------------------------------
|
||||
|
||||
@@ -942,6 +950,47 @@ class ConfigTab(ctk.CTkFrame):
|
||||
def _on_manual_mask_template(self, label: str) -> None:
|
||||
self._state.manual_mask_template = self._manual_mask_templates.get(label)
|
||||
|
||||
def _on_export_config(self) -> None:
|
||||
from gui_v6.config_share import build_export_payload
|
||||
import json
|
||||
|
||||
lists = getattr(self, "_pro_term_lists", {})
|
||||
wl = lists["whitelist"].terms() if "whitelist" in lists else []
|
||||
bl = lists["blacklist"].terms() if "blacklist" in lists else []
|
||||
path = filedialog.asksaveasfilename(
|
||||
title="Exporter la configuration", defaultextension=".json",
|
||||
filetypes=[("Configuration JSON", "*.json")],
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
try:
|
||||
payload = build_export_payload(wl, bl)
|
||||
Path(path).write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
messagebox.showinfo("Export", f"Configuration exportée :\n{path}")
|
||||
except Exception as exc: # pragma: no cover - chemin UI
|
||||
messagebox.showerror("Export", f"Échec de l'export : {exc}")
|
||||
|
||||
def _on_import_config(self) -> None:
|
||||
from gui_v6.config_share import import_config_file
|
||||
|
||||
if self._config_path is None:
|
||||
messagebox.showerror("Import", "Aucune configuration cible résolue.")
|
||||
return
|
||||
path = filedialog.askopenfilename(
|
||||
title="Importer une configuration",
|
||||
filetypes=[("Configuration JSON", "*.json")],
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
try:
|
||||
changed = import_config_file(path, self._config_path)
|
||||
if changed:
|
||||
messagebox.showinfo("Import", "Configuration fusionnée. Redémarrez pour appliquer.")
|
||||
else:
|
||||
messagebox.showinfo("Import", "Rien de nouveau à fusionner.")
|
||||
except Exception as exc: # pragma: no cover - chemin UI
|
||||
messagebox.showerror("Import", f"Échec de l'import : {exc}")
|
||||
|
||||
def _pick_output(self) -> None:
|
||||
path = filedialog.askdirectory(title="Dossier de sortie")
|
||||
if path:
|
||||
|
||||
Reference in New Issue
Block a user