Miscs
This commit is contained in:
+96
-26
@@ -8,7 +8,8 @@ from pathlib import Path
|
||||
from tkinter import filedialog, messagebox, ttk
|
||||
from typing import Any
|
||||
|
||||
from copienator import EvaluationWorkspace, ExitCode, atomic_write_json
|
||||
from copienator import CliError, EvaluationWorkspace, ExitCode, atomic_write_json
|
||||
from copienator.copy_errors import copy_errors
|
||||
from copienator.platform import (
|
||||
WindowsLabelError,
|
||||
add_platform_executable_paths,
|
||||
@@ -205,25 +206,29 @@ class CopienatorApp(tk.Tk):
|
||||
|
||||
top = ttk.Frame(self, padding=(10, 8))
|
||||
top.grid(row=0, column=0, sticky="ew")
|
||||
top.columnconfigure(2, weight=1)
|
||||
top.columnconfigure(5, weight=1)
|
||||
ttk.Button(top, text="Charger", command=self._load_evaluation).grid(
|
||||
row=0, column=0, padx=(0, 8)
|
||||
)
|
||||
ttk.Label(top, text="Évaluation").grid(row=0, column=1, sticky="w", padx=(0, 8))
|
||||
path_entry = ttk.Entry(top, textvariable=self.evaluation_var)
|
||||
path_entry.grid(row=0, column=2, sticky="ew")
|
||||
path_entry.bind("<Return>", lambda _event: self._load_evaluation())
|
||||
ttk.Button(top, text="Parcourir…", command=self._browse_evaluation).grid(
|
||||
row=0, column=3, padx=6
|
||||
self.evaluation_entry = ttk.Entry(top, textvariable=self.evaluation_var, width=42)
|
||||
self.evaluation_entry.grid(row=0, column=2, sticky="w")
|
||||
self.evaluation_entry.bind("<Return>", lambda _event: self._load_evaluation())
|
||||
self.open_evaluation_button = ttk.Button(
|
||||
top, text="Ouvrir le dossier", command=self._open_evaluation_folder
|
||||
)
|
||||
ttk.Button(top, text="Diagnostic…", command=self._show_diagnostics).grid(row=0, column=4, padx=(6, 0))
|
||||
self.open_evaluation_button.grid(row=0, column=3, padx=(6, 0))
|
||||
ttk.Button(top, text="Parcourir…", command=self._browse_evaluation).grid(
|
||||
row=0, column=4, padx=6
|
||||
)
|
||||
ttk.Button(top, text="Diagnostic…", command=self._show_diagnostics).grid(row=0, column=6, padx=(6, 0))
|
||||
|
||||
ttk.Label(top, text="Clé Gemini").grid(row=1, column=0, sticky="w", padx=(0, 8), pady=(7, 0))
|
||||
ttk.Entry(top, textvariable=self.api_key_var, show="•", width=32).grid(
|
||||
row=1, column=1, sticky="w", pady=(7, 0)
|
||||
)
|
||||
environment = ttk.Frame(top)
|
||||
environment.grid(row=1, column=2, columnspan=3, sticky="e", pady=(7, 0))
|
||||
environment.grid(row=1, column=2, columnspan=5, sticky="e", pady=(7, 0))
|
||||
ttk.Checkbutton(
|
||||
environment,
|
||||
text="Utiliser le proxy HTTPS",
|
||||
@@ -403,6 +408,9 @@ class CopienatorApp(tk.Tk):
|
||||
self.evaluation_var.set(selected)
|
||||
self._load_evaluation()
|
||||
|
||||
def _open_evaluation_folder(self) -> None:
|
||||
self._open_desktop_path(self.evaluation, "dossier d’évaluation")
|
||||
|
||||
def _toggle_proxy(self) -> None:
|
||||
self.proxy_entry.configure(state="normal" if self.use_proxy_var.get() else "disabled")
|
||||
|
||||
@@ -581,10 +589,12 @@ class CopienatorApp(tk.Tk):
|
||||
return
|
||||
self._rendering = True
|
||||
redo = step.section == REFAIRE_SECTION
|
||||
self.console.configure(height=8 if redo else 14)
|
||||
self.rowconfigure(1, weight=4 if redo else 3)
|
||||
self.rowconfigure(2, weight=1 if redo else 2)
|
||||
self.form_canvas.configure(height=(340 if step.id == "refaire_selection" else 260) if redo else 200)
|
||||
expanded_form = redo or step.id in {"page_splitter", "cutleft", "labels"}
|
||||
self.console.configure(height=8 if expanded_form else 14)
|
||||
self.rowconfigure(1, weight=4 if expanded_form else 3)
|
||||
self.rowconfigure(2, weight=1 if expanded_form else 2)
|
||||
form_height = (340 if step.id == "refaire_selection" else 260) if redo else (300 if expanded_form else 200)
|
||||
self.form_canvas.configure(height=form_height)
|
||||
self.form_canvas.yview_moveto(0)
|
||||
for child in self.form.winfo_children():
|
||||
child.destroy()
|
||||
@@ -667,6 +677,15 @@ class CopienatorApp(tk.Tk):
|
||||
row=row, column=0, sticky="w", pady=(10, 4), padx=(0, 8)
|
||||
)
|
||||
ttk.Entry(self.form, textvariable=self.extra_var).grid(row=row, column=1, sticky="ew", pady=(10, 4))
|
||||
row += 1
|
||||
|
||||
if self.show_personal_steps and step.id == "statement":
|
||||
actions = ttk.LabelFrame(self.form, text="Après génération — facultatif", padding=7)
|
||||
actions.grid(row=row, column=0, columnspan=2, sticky="ew", pady=(10, 8))
|
||||
for ident, title in (("statement_groups", "Regrouper avec Gemini…"),
|
||||
("statement_persp", "Remplacer Persp avec Gemini…")):
|
||||
ttk.Button(actions, text=title, command=lambda target=ident: self._select_statement_action(target)).pack(
|
||||
side="left", padx=(0, 6))
|
||||
|
||||
self.run_button.configure(text="Enregistrer la sélection" if step.id == "refaire_selection" else ("Marquer terminée" if step.is_manual else "Exécuter"))
|
||||
self.skip_button.configure(state="normal" if step.optional else "disabled")
|
||||
@@ -675,13 +694,11 @@ class CopienatorApp(tk.Tk):
|
||||
self._update_controls()
|
||||
|
||||
def _render_context_controls(self, step: StepDefinition, row: int) -> int:
|
||||
if self.show_personal_steps and step.id in {"statement", "statement_groups", "statement_persp", "review_persp"}:
|
||||
actions = ttk.LabelFrame(self.form, text="Après génération — facultatif", padding=7)
|
||||
actions.grid(row=row, column=0, columnspan=2, sticky="ew", pady=(0, 8))
|
||||
for ident, title in (("statement_groups", "Regrouper avec Gemini…"),
|
||||
("statement_persp", "Remplacer Persp avec Gemini…")):
|
||||
ttk.Button(actions, text=title, command=lambda target=ident: self._select_statement_action(target)).pack(
|
||||
side="left", padx=(0, 6))
|
||||
if step.id == "rename":
|
||||
self.validate_rename_button = ttk.Button(
|
||||
self.form, text="Valider sans renommer", command=self._validate_rename_step
|
||||
)
|
||||
self.validate_rename_button.grid(row=row, column=0, columnspan=2, sticky="w", pady=(0, 8))
|
||||
row += 1
|
||||
if step.id == "inputs":
|
||||
ttk.Button(self.form, text="Recharger — vérifier à nouveau", command=self._reload_inputs).grid(
|
||||
@@ -714,7 +731,7 @@ class CopienatorApp(tk.Tk):
|
||||
return row + 1
|
||||
if step.id in {"review_persp", "correction"}:
|
||||
row = self._correction_folder_buttons(row)
|
||||
if step.section == "Prétraitement des copies":
|
||||
if step.id in {"page_splitter", "cutleft", "labels"}:
|
||||
evaluation = self.evaluation
|
||||
paths = copy_pdf_paths(evaluation) if evaluation else []
|
||||
self.copy_paths = {path.name: path for path in paths}
|
||||
@@ -745,14 +762,36 @@ class CopienatorApp(tk.Tk):
|
||||
command=self._open_selected_copy,
|
||||
state="normal" if names else "disabled",
|
||||
).grid(row=1, column=1, padx=(6, 0), pady=(7, 0))
|
||||
if step.id == "page_splitter":
|
||||
if step.id in {"page_splitter", "cutleft", "labels"}:
|
||||
actions = ttk.Frame(copies)
|
||||
actions.grid(row=2, column=0, columnspan=2, sticky="w", pady=(7, 0))
|
||||
ttk.Button(actions, text="Refaire la copie sélectionnée", command=self._redo_selected_pages,
|
||||
label = "Refaire la copie sélectionnée" if step.id == "page_splitter" else "Cibler la copie sélectionnée"
|
||||
ttk.Button(actions, text=label, command=self._target_selected_copy,
|
||||
state="normal" if names else "disabled").pack(side="left")
|
||||
ttk.Button(actions, text="Cibler tout le dossier", command=self._target_all_pages).pack(side="left", padx=6)
|
||||
ttk.Label(copies, text="La reprise utilise l’original conservé. Vérifiez la commande puis cliquez sur Exécuter.",
|
||||
help_text = (
|
||||
"La reprise utilise l’original conservé. "
|
||||
if step.id == "page_splitter"
|
||||
else "Seule la copie ciblée sera analysée par Gemini. "
|
||||
if step.id == "labels"
|
||||
else "Seule la copie ciblée sera traitée. "
|
||||
)
|
||||
ttk.Label(copies, text=help_text + "Vérifiez la commande puis cliquez sur Exécuter.",
|
||||
wraplength=570).grid(row=3, column=0, columnspan=2, sticky="w", pady=(5, 0))
|
||||
if step.id in {"page_splitter", "cutleft"}:
|
||||
try:
|
||||
marked = copy_errors(EvaluationWorkspace(evaluation)) if evaluation else {}
|
||||
marked_summary = ", ".join(marked) or "Aucune copie signalée."
|
||||
except (CliError, OSError, ValueError, RuntimeError) as exc:
|
||||
marked = {}
|
||||
marked_summary = f"Lecture des copies signalées impossible : {exc}"
|
||||
self.marked_copies_button = ttk.Button(
|
||||
copies, text=f"Traiter les copies signalées ({len(marked)})", command=self._run_marked_copies,
|
||||
state="normal" if marked and not self.active_step_id and not self.runner.running else "disabled",
|
||||
)
|
||||
self.marked_copies_button.grid(row=4, column=0, columnspan=2, sticky="w", pady=(7, 0))
|
||||
ttk.Label(copies, text=marked_summary, wraplength=570).grid(
|
||||
row=5, column=0, columnspan=2, sticky="w")
|
||||
row += 1
|
||||
|
||||
if step.id == "plotting":
|
||||
@@ -852,16 +891,28 @@ class CopienatorApp(tk.Tk):
|
||||
def _open_selected_copy(self) -> None:
|
||||
self._open_desktop_path(self.copy_paths.get(self.copy_var.get()), "copie")
|
||||
|
||||
def _redo_selected_pages(self) -> None:
|
||||
def _target_selected_copy(self) -> None:
|
||||
path = self.copy_paths.get(self.copy_var.get())
|
||||
if path and "target" in self.arg_vars:
|
||||
if "marked" in self.arg_vars:
|
||||
self.arg_vars["marked"].set(False)
|
||||
self.arg_vars["target"].set(str(path))
|
||||
self.info_var.set(f"Reprise de {path.name} prête — cliquez sur Exécuter.")
|
||||
self.info_var.set(f"Copie ciblée : {path.name} — cliquez sur Exécuter.")
|
||||
|
||||
def _target_all_pages(self) -> None:
|
||||
if "target" in self.arg_vars:
|
||||
if "marked" in self.arg_vars:
|
||||
self.arg_vars["marked"].set(False)
|
||||
self.arg_vars["target"].set(self._evaluation_arg())
|
||||
|
||||
def _run_marked_copies(self) -> None:
|
||||
if (not self.current_step or self.current_step.id not in {"page_splitter", "cutleft"}
|
||||
or self.active_step_id or self.runner.running or not self.state_store.evaluation):
|
||||
return
|
||||
self.arg_vars["target"].set(self._evaluation_arg())
|
||||
self.arg_vars["marked"].set(True)
|
||||
self._run_current_step()
|
||||
|
||||
def _open_desktop_path(self, path: Path | None, label: str) -> None:
|
||||
if path is None or not path.exists():
|
||||
messagebox.showerror("Élément introuvable", f"Le {label} n’existe pas encore.")
|
||||
@@ -1186,6 +1237,19 @@ class CopienatorApp(tk.Tk):
|
||||
f"{self.current_step.title} : {STATUS_LABELS.get(status, status).lower()}{detail}."
|
||||
)
|
||||
|
||||
def _validate_rename_step(self) -> None:
|
||||
if (not self.current_step or self.current_step.id != "rename"
|
||||
or not self.state_store.evaluation or self.active_step_id or self.runner.running):
|
||||
return
|
||||
self._save_current_form()
|
||||
self.state_store.update_step("rename", status="success")
|
||||
self.state_store.add_history({
|
||||
"step": "rename", "status": "success", "manual": True,
|
||||
"reason": "Noms des PDF validés sans renommer",
|
||||
})
|
||||
self._populate_tree()
|
||||
self.info_var.set("Renommage validé — aucun fichier modifié.")
|
||||
|
||||
def _skip_step(self) -> None:
|
||||
if self.current_step and self.current_step.optional:
|
||||
step_id = self.current_step.id
|
||||
@@ -1314,6 +1378,12 @@ class CopienatorApp(tk.Tk):
|
||||
|
||||
def _update_controls(self) -> None:
|
||||
running = bool(self.active_step_id) or self.runner.running
|
||||
if running and self.current_step and self.current_step.id in {"page_splitter", "cutleft"}:
|
||||
self.marked_copies_button.configure(state="disabled")
|
||||
if self.current_step and self.current_step.id == "rename":
|
||||
self.validate_rename_button.configure(
|
||||
state="disabled" if running or not self.state_store.evaluation else "normal"
|
||||
)
|
||||
self.run_button.configure(state="disabled" if running or not self.current_step else "normal")
|
||||
self.skip_button.configure(state="normal" if not running and self.current_step and self.current_step.optional else "disabled")
|
||||
self.interrupt_button.configure(state="normal" if running else "disabled")
|
||||
|
||||
@@ -173,7 +173,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
|
||||
"Séparer et réordonner les pages",
|
||||
"Ouvre l’outil interactif de découpage A3 vers A4. La cible peut être un dossier ou un PDF.",
|
||||
(python("default", "Séparation des pages", "page-split"),),
|
||||
arguments=(arg_target(),),
|
||||
arguments=(arg_target(), ArgumentSpec("marked", "Copies signalées uniquement", "bool", "--marked")),
|
||||
artifacts=("Copies", "Copies Originales"),
|
||||
),
|
||||
StepDefinition(
|
||||
@@ -185,6 +185,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
|
||||
arguments=(
|
||||
arg_target(),
|
||||
ArgumentSpec("fullpage", "Toujours utiliser la page entière", "bool", "--fullpage"),
|
||||
ArgumentSpec("marked", "Copies signalées uniquement", "bool", "--marked"),
|
||||
),
|
||||
requires=("Copies",),
|
||||
artifacts=("Cutleft",),
|
||||
|
||||
Reference in New Issue
Block a user