personal workflow improvements
This commit is contained in:
1 parent
96c00fa941
commit
42a5b9778e
3 files changed
+77
-6
No files matched your search
+50
-3
@@ -25,6 +25,7 @@ from .diagnostics import collect_diagnostics
|
|||||||
from .batch_monitor import BatchMonitor
|
from .batch_monitor import BatchMonitor
|
||||||
from .giving_names import GivingNamesPanel, find_name_issues
|
from .giving_names import GivingNamesPanel, find_name_issues
|
||||||
from .notifications import notify_desktop
|
from .notifications import notify_desktop
|
||||||
|
from .syncthing import SyncthingPanel
|
||||||
from .manual_resolution import ManualResolutionPanel
|
from .manual_resolution import ManualResolutionPanel
|
||||||
from .refaire import SECTION as REFAIRE_SECTION
|
from .refaire import SECTION as REFAIRE_SECTION
|
||||||
from .refaire import (
|
from .refaire import (
|
||||||
@@ -426,6 +427,7 @@ class CopienatorApp(tk.Tk):
|
|||||||
self.form.columnconfigure(1, weight=1)
|
self.form.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
command_box = ttk.LabelFrame(self.detail, text="Commande", padding=6)
|
command_box = ttk.LabelFrame(self.detail, text="Commande", padding=6)
|
||||||
|
self.command_box = command_box
|
||||||
command_box.grid(row=4, column=0, sticky="ew", pady=(8, 5))
|
command_box.grid(row=4, column=0, sticky="ew", pady=(8, 5))
|
||||||
command_box.columnconfigure(0, weight=1)
|
command_box.columnconfigure(0, weight=1)
|
||||||
ttk.Label(command_box, textvariable=self.command_var, wraplength=690, justify="left").grid(
|
ttk.Label(command_box, textvariable=self.command_var, wraplength=690, justify="left").grid(
|
||||||
@@ -782,6 +784,10 @@ class CopienatorApp(tk.Tk):
|
|||||||
if not step:
|
if not step:
|
||||||
return
|
return
|
||||||
self._rendering = True
|
self._rendering = True
|
||||||
|
if step.id == "personal_read":
|
||||||
|
self.command_box.grid_remove()
|
||||||
|
else:
|
||||||
|
self.command_box.grid()
|
||||||
redo = step.section == REFAIRE_SECTION
|
redo = step.section == REFAIRE_SECTION
|
||||||
expanded_form = redo or step.id in {
|
expanded_form = redo or step.id in {
|
||||||
"page_splitter",
|
"page_splitter",
|
||||||
@@ -907,12 +913,32 @@ class CopienatorApp(tk.Tk):
|
|||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
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.run_button.configure(text="Enregistrer la sélection" if step.id == "refaire_selection" else ("Marquer terminée" if step.is_manual else "Exécuter"))
|
||||||
|
if step.id == "personal_create":
|
||||||
|
self.run_button.configure(text="Marquer Réussie")
|
||||||
|
if step.id == "personal_read":
|
||||||
|
self.run_button.configure(text="Marquer réussie")
|
||||||
self.skip_button.configure(state="normal" if step.optional else "disabled")
|
self.skip_button.configure(state="normal" if step.optional else "disabled")
|
||||||
self._rendering = False
|
self._rendering = False
|
||||||
self._update_command_preview()
|
self._update_command_preview()
|
||||||
self._update_controls()
|
self._update_controls()
|
||||||
|
|
||||||
def _render_context_controls(self, step: StepDefinition, row: int) -> int:
|
def _render_context_controls(self, step: StepDefinition, row: int) -> int:
|
||||||
|
if step.id == "personal_read":
|
||||||
|
controls = ttk.Frame(self.form)
|
||||||
|
controls.grid(row=row, column=0, columnspan=2, sticky="w", pady=8)
|
||||||
|
self.review_buttons = []
|
||||||
|
for command in ("re", "wsn", "we"):
|
||||||
|
button = ttk.Button(
|
||||||
|
controls, text=f"gestion_classe {command}",
|
||||||
|
command=lambda command=command: self._run_current_step(review_command=command),
|
||||||
|
)
|
||||||
|
button.pack(side="left", padx=(0, 6))
|
||||||
|
self.review_buttons.append(button)
|
||||||
|
return row + 1
|
||||||
|
if step.id == "export":
|
||||||
|
self.syncthing_panel = SyncthingPanel(self.form)
|
||||||
|
self.syncthing_panel.grid(row=row, column=0, columnspan=2, sticky="ew", pady=8)
|
||||||
|
return row + 1
|
||||||
if step.id == "batch_status":
|
if step.id == "batch_status":
|
||||||
controls = ttk.Frame(self.form)
|
controls = ttk.Frame(self.form)
|
||||||
controls.grid(row=row, column=0, columnspan=2, sticky="w", pady=8)
|
controls.grid(row=row, column=0, columnspan=2, sticky="w", pady=8)
|
||||||
@@ -1407,9 +1433,13 @@ class CopienatorApp(tk.Tk):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _run_current_step(self) -> None:
|
def _run_current_step(self, *, syncthing_ready=False, review_command=None) -> None:
|
||||||
step = self.current_step
|
step = self.current_step
|
||||||
evaluation = self.evaluation
|
evaluation = self.evaluation
|
||||||
|
if review_command is not None and (
|
||||||
|
not step or step.id != "personal_read" or review_command not in {"re", "wsn", "we"}
|
||||||
|
):
|
||||||
|
return
|
||||||
if not step or not evaluation or not self.state_store.evaluation:
|
if not step or not evaluation or not self.state_store.evaluation:
|
||||||
messagebox.showerror("Évaluation absente", "Chargez d’abord un dossier d’évaluation.")
|
messagebox.showerror("Évaluation absente", "Chargez d’abord un dossier d’évaluation.")
|
||||||
return
|
return
|
||||||
@@ -1427,7 +1457,8 @@ class CopienatorApp(tk.Tk):
|
|||||||
except (OSError, ValueError, TypeError) as exc:
|
except (OSError, ValueError, TypeError) as exc:
|
||||||
messagebox.showerror("Sélection à vérifier", str(exc))
|
messagebox.showerror("Sélection à vérifier", str(exc))
|
||||||
return
|
return
|
||||||
if step.is_manual:
|
if (step.is_manual or step.id == "personal_create"
|
||||||
|
or (step.id == "personal_read" and review_command is None)):
|
||||||
self._mark_step("success")
|
self._mark_step("success")
|
||||||
self._move_selection_from(step.id, 1)
|
self._move_selection_from(step.id, 1)
|
||||||
return
|
return
|
||||||
@@ -1456,6 +1487,8 @@ class CopienatorApp(tk.Tk):
|
|||||||
return
|
return
|
||||||
variant = self._current_variant()
|
variant = self._current_variant()
|
||||||
command = self._make_command()
|
command = self._make_command()
|
||||||
|
if step.id == "personal_read":
|
||||||
|
command = ["gestion_classe", review_command]
|
||||||
if variant.dangerous and not messagebox.askyesno(
|
if variant.dangerous and not messagebox.askyesno(
|
||||||
"Nettoyage irréversible" if step.id == "clean" else "Confirmation requise",
|
"Nettoyage irréversible" if step.id == "clean" else "Confirmation requise",
|
||||||
variant.danger_warning
|
variant.danger_warning
|
||||||
@@ -1464,6 +1497,13 @@ class CopienatorApp(tk.Tk):
|
|||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if step.id == "export" and not syncthing_ready:
|
||||||
|
def continue_export():
|
||||||
|
if self.current_step is step and self.evaluation == evaluation:
|
||||||
|
self._run_current_step(syncthing_ready=True)
|
||||||
|
self.syncthing_panel.ensure_started(continue_export)
|
||||||
|
return
|
||||||
|
|
||||||
self._save_current_form()
|
self._save_current_form()
|
||||||
run_values = self._values()
|
run_values = self._values()
|
||||||
ordered_ids = self._progression_ids(step.id)
|
ordered_ids = self._progression_ids(step.id)
|
||||||
@@ -1701,7 +1741,7 @@ class CopienatorApp(tk.Tk):
|
|||||||
return
|
return
|
||||||
stored_status = (
|
stored_status = (
|
||||||
"detected"
|
"detected"
|
||||||
if step_id == "giving_names" and status == "success"
|
if step_id in {"giving_names", "personal_read"} and status == "success"
|
||||||
else status
|
else status
|
||||||
)
|
)
|
||||||
self.state_store.update_step(
|
self.state_store.update_step(
|
||||||
@@ -1732,6 +1772,10 @@ class CopienatorApp(tk.Tk):
|
|||||||
self.active_step_id = None
|
self.active_step_id = None
|
||||||
self._populate_tree()
|
self._populate_tree()
|
||||||
self._update_controls()
|
self._update_controls()
|
||||||
|
if step_id == "personal_read":
|
||||||
|
if status == "success":
|
||||||
|
self.info_var.set("Commande terminée. Poursuivez la relecture, puis cliquez sur « Marquer réussie ».")
|
||||||
|
return
|
||||||
if step_id == "giving_names" and status == "success":
|
if step_id == "giving_names" and status == "success":
|
||||||
if self.giving_names_panel:
|
if self.giving_names_panel:
|
||||||
self.giving_names_panel.reload()
|
self.giving_names_panel.reload()
|
||||||
@@ -1820,6 +1864,9 @@ class CopienatorApp(tk.Tk):
|
|||||||
|
|
||||||
def _update_controls(self) -> None:
|
def _update_controls(self) -> None:
|
||||||
running = bool(self.active_step_id) or self.runner.running
|
running = bool(self.active_step_id) or self.runner.running
|
||||||
|
if self.current_step and self.current_step.id == "personal_read":
|
||||||
|
for button in self.review_buttons:
|
||||||
|
button.configure(state="disabled" if running or not self.state_store.evaluation else "normal")
|
||||||
if self.current_step and self.current_step.id == "batch_status":
|
if self.current_step and self.current_step.id == "batch_status":
|
||||||
self.watch_batches_button.configure(state="disabled" if running or self.batch_monitor.active or not self.state_store.evaluation else "normal")
|
self.watch_batches_button.configure(state="disabled" if running or self.batch_monitor.active or not self.state_store.evaluation else "normal")
|
||||||
self.stop_watch_batches_button.configure(state="normal" if self.batch_monitor.active else "disabled")
|
self.stop_watch_batches_button.configure(state="normal" if self.batch_monitor.active else "disabled")
|
||||||
|
|||||||
@@ -633,7 +633,8 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
|
|||||||
"personal_create",
|
"personal_create",
|
||||||
"Étapes personnelles",
|
"Étapes personnelles",
|
||||||
"Créer l’interro dans gestion_classe",
|
"Créer l’interro dans gestion_classe",
|
||||||
"Exécute gestion_classe ne.",
|
"Copiez la commande gestion_classe ne et exécutez-la dans un terminal, "
|
||||||
|
"puis cliquez sur « Marquer Réussie ».",
|
||||||
(CommandVariant("default", "gestion_classe ne", "gestion_classe", "external", ("ne",)),),
|
(CommandVariant("default", "gestion_classe ne", "gestion_classe", "external", ("ne",)),),
|
||||||
personal=True,
|
personal=True,
|
||||||
),
|
),
|
||||||
@@ -667,14 +668,14 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
|
|||||||
"personal_read",
|
"personal_read",
|
||||||
"Étapes personnelles",
|
"Étapes personnelles",
|
||||||
"Relire avec gestion_classe",
|
"Relire avec gestion_classe",
|
||||||
"Exécute gestion_classe re.",
|
"Utilisez les boutons gestion_classe re, wsn et we, puis cliquez sur « Marquer réussie ».",
|
||||||
(CommandVariant("default", "gestion_classe re", "gestion_classe", "external", ("re",)),),
|
(CommandVariant("default", "gestion_classe re", "gestion_classe", "external", ("re",)),),
|
||||||
personal=True,
|
personal=True,
|
||||||
),
|
),
|
||||||
StepDefinition(
|
StepDefinition(
|
||||||
"personal_sent",
|
"personal_sent",
|
||||||
"Étapes personnelles",
|
"Étapes personnelles",
|
||||||
"Marquer comme envoyé",
|
"Écrire les notes finales",
|
||||||
"Exécute gestion_classe wsent.",
|
"Exécute gestion_classe wsent.",
|
||||||
(CommandVariant("default", "gestion_classe wsent", "gestion_classe", "external", ("wsent",)),),
|
(CommandVariant("default", "gestion_classe wsent", "gestion_classe", "external", ("wsent",)),),
|
||||||
personal=True,
|
personal=True,
|
||||||
|
|||||||
@@ -27,6 +27,29 @@ class GuiConvenienceTests(unittest.TestCase):
|
|||||||
self.app.destroy()
|
self.app.destroy()
|
||||||
self.temp.cleanup()
|
self.temp.cleanup()
|
||||||
|
|
||||||
|
def test_personal_review_commands_require_explicit_completion(self):
|
||||||
|
from copienator_gui.workflow import build_workflow
|
||||||
|
step = next(step for step in build_workflow(True) if step.id == "personal_read")
|
||||||
|
self.app.step_by_id[step.id] = step
|
||||||
|
self.app.current_step = step
|
||||||
|
self.app._render_step()
|
||||||
|
self.assertEqual([button.cget("text") for button in self.app.review_buttons],
|
||||||
|
["gestion_classe re", "gestion_classe wsn", "gestion_classe we"])
|
||||||
|
self.assertEqual(self.app.command_box.winfo_manager(), "")
|
||||||
|
self.assertEqual(self.app.run_button.cget("text"), "Marquer réussie")
|
||||||
|
with patch.object(self.app.runner, "start") as start, \
|
||||||
|
patch.object(self.app, "_move_selection_from") as advance:
|
||||||
|
for button, command in zip(self.app.review_buttons, ("re", "wsn", "we")):
|
||||||
|
button.invoke()
|
||||||
|
self.assertEqual(start.call_args.args[0], ["gestion_classe", command])
|
||||||
|
self.app._finish_process(0, False)
|
||||||
|
self.assertNotEqual(self.app.state_store.step(step.id)["status"], "success")
|
||||||
|
advance.assert_not_called()
|
||||||
|
start.reset_mock()
|
||||||
|
self.app.run_button.invoke()
|
||||||
|
start.assert_not_called()
|
||||||
|
self.assertEqual(self.app.state_store.step(step.id)["status"], "success")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _label_texts(widget):
|
def _label_texts(widget):
|
||||||
return [
|
return [
|
||||||
|
|||||||
Reference in new issue
Block a user