Allow signaling issues in label verification

This commit is contained in:
sebastien committed 2026-10-11 21:05:27 +02:00
1 parent 3754c0c11a
commit 9925362352
21 files changed
+1347 -36

No files matched your search

+49 -5
View File
@@ -21,6 +21,7 @@ from copienator import (
target_parser,
workspace_from_target,
)
from copienator.copy_errors import clear_copy_error, mark_copy_error, marked_copy_paths
from copienator.platform import open_path
from copienator.utils import natural_key, read_all_labels
@@ -307,6 +308,7 @@ class ImageViewer:
self.forward_stack = []
self.current_pil_image = None
self.failed = False
self.skipped_copies = set()
from config import PLOTTING_KB
@@ -314,6 +316,7 @@ class ImageViewer:
self.root.bind(PLOTTING_KB["OK"], self.on_enter)
self.root.bind(PLOTTING_KB["previous"], self.on_previous)
self.root.bind("P", self.on_previous)
self.root.bind("!", self.on_skip)
self.root.bind(PLOTTING_KB["edit"], self.on_edit)
self.root.bind(PLOTTING_KB["open pdf"], self.on_open_pdf)
self.root.bind(PLOTTING_KB["open original pdf"], self.on_open_ori_pdf)
@@ -345,6 +348,10 @@ class ImageViewer:
self.root.quit()
return
if metadata["copie"] in self.skipped_copies:
self.root.after(0, self.poll_queue)
return
# Check if we switched to a new "Copie" group
if self.active_copie_name != metadata["copie"]:
self.save_current_batch() # Write previous group to disk
@@ -361,8 +368,21 @@ class ImageViewer:
"""Writes the accumulated data to the main JSON file."""
if self.active_copie_name and self.accumulated_results:
main_json_path = self.base_dir / "Copies" / f"{self.active_copie_name}.json"
accepted = [item[2] for item in self.history
if item[3] == self.active_copie_name]
parts = {item["part"] for item in accepted}
expected = (set(range(1, len(accepted[-1]["schema"]["columns_per_file"]) + 1))
if accepted else set())
if not expected or parts != expected:
mark_copy_error(self.workspace, main_json_path.with_suffix(".pdf"),
"Vérification des labels incomplète", source="plotting")
self.failed = True
self.accumulated_results = None
return
print(f"Writing aggregated result to {main_json_path}")
atomic_write_json(main_json_path, self.accumulated_results)
clear_copy_error(self.workspace, main_json_path.with_suffix(".pdf"),
source="plotting")
self.accumulated_results = None
def close(self):
@@ -467,6 +487,21 @@ class ImageViewer:
self.is_viewing = False
self.label.config(image="", text="Loading next...")
def on_skip(self, _event=None):
if not self.is_viewing:
return
copie = self.current_meta["copie"]
mark_copy_error(self.workspace, self.workspace.copies_dir / f"{copie}.pdf",
"Problème repéré pendant la vérification des labels", source="plotting")
self.failed = True
self.skipped_copies.add(copie)
# Discard accepted parts of this copy without replacing its previous result.
self.accumulated_results = None
self.history = [item for item in self.history if item[3] != copie]
self.forward_stack = [item for item in self.forward_stack if item[2]["copie"] != copie]
self.is_viewing = False
self.label.config(image="", text="Copie signalée. Chargement de la suivante…")
def on_open_pdf(self, event):
if self.is_viewing and self.current_json_path:
a = self.current_json_path.stem.split('_')[0] + ".pdf"
@@ -530,17 +565,24 @@ def _selected_images(workspace: EvaluationWorkspace, target: Path) -> list[Path]
return sorted(workspace.cutleft_dir.glob("*.jpg"), key=natural_key)
def run(workspace: EvaluationWorkspace, target: Path) -> ExitCode:
def run(workspace: EvaluationWorkspace, target: Path, *, marked: bool = False) -> ExitCode:
workspace.require_files("labels")
all_labels = read_all_labels(workspace.root)
files_to_process = _selected_images(workspace, target)
if marked:
copies = marked_copy_paths(workspace)
if not copies:
print("No flagged copies to verify.")
return ExitCode.SUCCESS
files_to_process = [image for pdf in copies for image in _selected_images(workspace, pdf)]
else:
files_to_process = _selected_images(workspace, target)
if not files_to_process:
print(f"No Cutleft images found for {target}")
return ExitCode.PARTIAL
print(
"o to open pdf, O original pdf, e to edit part, p to go back, "
"i to open the statement, click for coordinates"
"i to open the statement, ! to flag and skip the copy, click for coordinates"
)
input_queue = queue.Queue(maxsize=5)
worker = threading.Thread(
@@ -556,7 +598,9 @@ def run(workspace: EvaluationWorkspace, target: Path) -> ExitCode:
def build_parser() -> argparse.ArgumentParser:
return target_parser("Interactively verify detected label coordinates")
parser = target_parser("Interactively verify detected label coordinates")
parser.add_argument("--marked", action="store_true", help="Verify only copies with remaining flags")
return parser
def main(argv: Sequence[str] | None = None) -> int:
@@ -564,7 +608,7 @@ def main(argv: Sequence[str] | None = None) -> int:
def handle(args: argparse.Namespace) -> ExitCode:
workspace, target = workspace_from_target(args)
return run(workspace, target)
return run(workspace, target, marked=args.marked)
return execute(parser, argv, handle)