32k pixel as hard group limit, better manual resolution support
This commit is contained in:
1 parent
a9c0703f03
commit
5009831c7c
9 files changed
+322
-58
No files matched your search
+34
-8
@@ -10,7 +10,8 @@ from pathlib import Path
|
||||
from tkinter import filedialog, messagebox, ttk
|
||||
from typing import Any
|
||||
|
||||
from copienator import CliError, EvaluationWorkspace, ExitCode, atomic_write_json
|
||||
from copienator import CliError, EvaluationWorkspace, ExitCode, atomic_write_json, read_json
|
||||
from copienator.commands.resolve_manual import pending_manual_conflicts
|
||||
from copienator.copy_errors import copy_errors
|
||||
from copienator.filesystem import staged_files
|
||||
from copienator.platform import (
|
||||
@@ -244,7 +245,14 @@ def has_manual_conflicts(path: Path) -> bool:
|
||||
# If the file cannot be inspected, keep the step visible rather than
|
||||
# silently claiming that there is nothing to resolve.
|
||||
return True
|
||||
return any(line.strip() and not line.lstrip().startswith("###") for line in lines)
|
||||
if any(line.strip() and not line.lstrip().startswith("###") for line in lines):
|
||||
return True
|
||||
# An emptied manual file still needs execution to acknowledge deletions.
|
||||
try:
|
||||
results = read_json(path.parent / "correction.json", default={})
|
||||
return bool(pending_manual_conflicts(results))
|
||||
except (OSError, ValueError, TypeError, KeyError, AttributeError):
|
||||
return True
|
||||
|
||||
|
||||
class CopienatorApp(tk.Tk):
|
||||
@@ -1459,6 +1467,10 @@ class CopienatorApp(tk.Tk):
|
||||
self._save_current_form()
|
||||
run_values = self._values()
|
||||
ordered_ids = self._progression_ids(step.id)
|
||||
if step.id == "correction" and variant.id == "integrate":
|
||||
# Integration consumes the retrieved batches; it does not invalidate them.
|
||||
ordered_ids = [step_id for step_id in ordered_ids
|
||||
if not self.step_by_id[step_id].skip_for_live_correction]
|
||||
self.state_store.invalidate_after(ordered_ids, step.id)
|
||||
self.state_store.update_step(
|
||||
step.id,
|
||||
@@ -1728,11 +1740,14 @@ class CopienatorApp(tk.Tk):
|
||||
"sur « Marquer terminée »."
|
||||
)
|
||||
return
|
||||
if step_id in {"labels", "correction"} and self.state_store.evaluation:
|
||||
completion = (
|
||||
"détection des labels terminée"
|
||||
if step_id == "labels" else "étape de correction terminée"
|
||||
)
|
||||
notified_completions = {
|
||||
"labels": "détection des labels terminée",
|
||||
"correction": "étape de correction terminée",
|
||||
"annotation": "génération des copies annotées terminée",
|
||||
"read_annotations": "lecture des annotations manuscrites terminée",
|
||||
}
|
||||
if step_id in notified_completions and self.state_store.evaluation:
|
||||
completion = notified_completions[step_id]
|
||||
outcome = STATUS_LABELS[status].lower()
|
||||
self._notify_completion(
|
||||
f"Copienator — {completion} ({outcome})",
|
||||
@@ -1744,7 +1759,9 @@ class CopienatorApp(tk.Tk):
|
||||
self.batch_monitor.stop()
|
||||
self._batch_results_ready()
|
||||
return
|
||||
if step_id == "manual_resolution":
|
||||
if step_id == "fetch_batches":
|
||||
self.after_idle(self._select_batch_integration)
|
||||
elif step_id == "manual_resolution":
|
||||
self.after_idle(self._select_refaire_correction)
|
||||
else:
|
||||
self.after_idle(
|
||||
@@ -1842,6 +1859,15 @@ class CopienatorApp(tk.Tk):
|
||||
self.tree.selection_set(ids[target])
|
||||
self.tree.see(ids[target])
|
||||
|
||||
def _select_batch_integration(self) -> None:
|
||||
"""Prepare integration after batch results have been retrieved."""
|
||||
if self.runner.running or self.active_step_id or not self.state_store.evaluation:
|
||||
return
|
||||
self._save_current_form()
|
||||
self.state_store.update_step("correction", variant="integrate")
|
||||
self.tree.selection_set("correction")
|
||||
self.tree.see("correction")
|
||||
|
||||
def _select_refaire_correction(self) -> None:
|
||||
"""Return from manual conflict resolution to the main redo correction."""
|
||||
if self.runner.running or self.active_step_id or not self.state_store.evaluation:
|
||||
|
||||
@@ -29,7 +29,8 @@ c{43}2x : couper la source à 43 %, conserver la fin et remplacer la cible par l
|
||||
Cut permet de placer la coupe, avec accrochage entre les pages. Entrée ferme l’aperçu et affiche la commande à recopier ci-dessus : aucun fichier n’est modifié. Une séparation entre pages conserve les pages entières. La source complète est archivée en _old ; les deux labels modifiés sont générés en _new et ajoutés à refaire.json.
|
||||
|
||||
Pour les fusions : « Source x> Cible| » place la cible avant la source ; « Source x> |Cible » place la source avant la cible. Même règle avec -> et c{…}1>/c{…}2> (pour la partie transférée) ; sans |, la cible vient en premier.
|
||||
Vous pouvez changer l’opérateur, déplacer |, corriger les labels ou retirer une instruction. Les lignes vides et celles commençant par ### sont ignorées. Retirer/commenter une ligne ne résout pas son conflit.
|
||||
Si le PDF cible n’existe pas, il est considéré comme vide : la source (ou la partie transférée après une coupe) devient le nouveau PDF cible.
|
||||
Vous pouvez changer l’opérateur, déplacer |, corriger les labels ou retirer une instruction. Les lignes vides et celles commençant par ### sont ignorées. Retirer/commenter une ligne signifie conserver les PDF sans modification et considérer ce conflit comme résolu après Exécuter. Exécutez cette étape même si vous avez retiré toutes les instructions : ces conflits ne seront plus proposés lors de la reprise avec refaire.json.
|
||||
|
||||
Enregistrez dans l’éditeur, puis cliquez sur Recharger et enfin sur Exécuter. Seul le fichier enregistré est appliqué. L’archivage utilise le suffixe _old ; les PDF créés utilisent _new. Après succès, manual_resolutions.txt est supprimé et correction.json est mis à jour. Si des PDF sont créés, refaire.json est généré : relancez la correction avec --refaire."""
|
||||
|
||||
@@ -46,7 +47,10 @@ class ManualResolutionPanel(ttk.Frame):
|
||||
actions, text="Ouvrir dans un éditeur de texte", command=self.open_editor
|
||||
)
|
||||
self.editor_button.pack(side="left")
|
||||
ttk.Button(actions, text="Recharger", command=self.reload).pack(side="left", padx=6)
|
||||
self.reload_button = ttk.Button(
|
||||
actions, text="Recharger", command=self.reload
|
||||
)
|
||||
self.reload_button.pack(side="left", padx=6)
|
||||
self.cut_result = tk.StringVar()
|
||||
result = ttk.Frame(self)
|
||||
result.pack(fill="x", pady=4)
|
||||
@@ -116,10 +120,10 @@ class ManualResolutionPanel(ttk.Frame):
|
||||
else:
|
||||
if instructions:
|
||||
instruction = instructions[0]
|
||||
for title, label in (("PDF source", instruction.old_label),
|
||||
("PDF cible", instruction.new_label)):
|
||||
for title, label in (("Source", instruction.old_label),
|
||||
("Cible", instruction.new_label)):
|
||||
button = ttk.Button(
|
||||
self.text, text=title,
|
||||
self.text, text=f"{title} : {label}",
|
||||
command=lambda label=label, copy_id=instruction.copy_id, root=path.parent: self.open_pdf(root, copy_id, label),
|
||||
)
|
||||
self.pdf_buttons.append(button)
|
||||
|
||||
Reference in new issue
Block a user