opton to skip visual check in cutleft

This commit is contained in:
sebastien committed 2026-09-29 15:46:47 +02:00
1 parent 5b7d4b611e
commit 2f8422f6d5
5 files changed
+112 -3

No files matched your search

+37 -1
View File
@@ -384,6 +384,7 @@ def run(
target: Path, target: Path,
*, *,
fullpage: bool = False, fullpage: bool = False,
no_visual_check: bool = False,
marked: bool = False, marked: bool = False,
) -> ExitCode: ) -> ExitCode:
files = marked_copy_paths(workspace) if marked else _selected_files(workspace, target) files = marked_copy_paths(workspace) if marked else _selected_files(workspace, target)
@@ -392,6 +393,30 @@ def run(
return ExitCode.SUCCESS return ExitCode.SUCCESS
workspace.cutleft_dir.mkdir(parents=True, exist_ok=True) workspace.cutleft_dir.mkdir(parents=True, exist_ok=True)
_get_pdf_pages_cached.cache_clear() _get_pdf_pages_cached.cache_clear()
if fullpage and no_visual_check:
had_errors = False
for pdf_path in files:
result = process_single_pdf(pdf_path, max_per_file=1)
if result is None:
mark_copy_error(
workspace,
pdf_path,
"Échec de la conversion en pages entières",
)
had_errors = True
continue
try:
save_results(result, pdf_path, workspace.cutleft_dir)
clear_copy_error(workspace, pdf_path)
except Exception as exc: # noqa: BLE001 - continue with remaining copies
mark_copy_error(
workspace,
pdf_path,
f"Échec de l’enregistrement : {exc}",
)
print(f"[Copie signalée] {pdf_path.name}: {exc}")
had_errors = True
return ExitCode.PARTIAL if had_errors else ExitCode.SUCCESS
reviewer = ImageReviewer( reviewer = ImageReviewer(
files, files,
workspace.cutleft_dir, workspace.cutleft_dir,
@@ -410,6 +435,11 @@ def build_parser() -> argparse.ArgumentParser:
action="store_true", action="store_true",
help="Use each complete page instead of cropping the label margin", help="Use each complete page instead of cropping the label margin",
) )
parser.add_argument(
"--no-visual-check",
action="store_true",
help="Save full pages without opening the visual reviewer (requires --fullpage)",
)
return parser return parser
@@ -418,7 +448,13 @@ def main(argv: Sequence[str] | None = None) -> int:
def handle(args: argparse.Namespace) -> ExitCode: def handle(args: argparse.Namespace) -> ExitCode:
workspace, target = workspace_from_target(args) workspace, target = workspace_from_target(args)
return run(workspace, target, fullpage=args.fullpage, marked=args.marked) return run(
workspace,
target,
fullpage=args.fullpage,
no_visual_check=args.no_visual_check,
marked=args.marked,
)
return execute(parser, argv, handle) return execute(parser, argv, handle)
+7
View File
@@ -285,6 +285,13 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"--fullpage", "--fullpage",
help="Désactive la découpe habituelle de la marge gauche et transmet chaque page entière à la détection des labels.", help="Désactive la découpe habituelle de la marge gauche et transmet chaque page entière à la détection des labels.",
), ),
ArgumentSpec(
"no_visual_check",
"et ne pas vérifier visuellement",
"bool",
"--no-visual-check",
help="Avec « Toujours utiliser la page entière », enregistre directement les images sans ouvrir la fenêtre de vérification.",
),
ArgumentSpec( ArgumentSpec(
"marked", "marked",
"Copies signalées uniquement", "Copies signalées uniquement",
+18
View File
@@ -51,6 +51,24 @@ class GuiConvenienceTests(unittest.TestCase):
start.assert_not_called() start.assert_not_called()
self.assertEqual(self.app.state_store.step(step.id)["status"], "success") self.assertEqual(self.app.state_store.step(step.id)["status"], "success")
def test_cutleft_visual_check_option_is_below_and_off_by_default(self):
step = self.app.step_by_id["cutleft"]
self.app.current_step = step
self.app._render_step()
labels = {
child.cget("text"): child
for child in self.app.form.winfo_children()
if isinstance(child, ttk.Label)
}
fullpage = labels["Toujours utiliser la page entière ⓘ"]
no_visual_check = labels["et ne pas vérifier visuellement ⓘ"]
self.assertGreater(
int(no_visual_check.grid_info()["row"]),
int(fullpage.grid_info()["row"]),
)
self.assertFalse(self.app.arg_vars["no_visual_check"].get())
@staticmethod @staticmethod
def _label_texts(widget): def _label_texts(widget):
return [ return [
+19 -1
View File
@@ -566,7 +566,11 @@ class StandardCliTests(unittest.TestCase):
"cutleft": ( "cutleft": (
"cutleft", "cutleft",
"default", "default",
{"target": evaluation, "fullpage": True}, {
"target": evaluation,
"fullpage": True,
"no_visual_check": True,
},
), ),
"splitting_int": ( "splitting_int": (
"splitting", "splitting",
@@ -757,6 +761,20 @@ class StandardCliTests(unittest.TestCase):
self.assertEqual(output_dir, evaluation / "Cutleft") self.assertEqual(output_dir, evaluation / "Cutleft")
self.assertEqual(reviewer.call_args.kwargs["default_max_per_file"], 1) self.assertEqual(reviewer.call_args.kwargs["default_max_per_file"], 1)
result = (Mock(), [Mock()], {})
with patch.object(module, "ImageReviewer") as reviewer, patch.object(
module, "process_single_pdf", return_value=result
) as process, patch.object(module, "save_results") as save:
self.assertEqual(
module.main(
[str(copy_pdf), "--fullpage", "--no-visual-check"]
),
0,
)
reviewer.assert_not_called()
process.assert_called_once_with(copy_pdf, max_per_file=1)
save.assert_called_once_with(result, copy_pdf, evaluation / "Cutleft")
def test_cutleft_atomic_save_removes_only_obsolete_copy_outputs(self) -> None: def test_cutleft_atomic_save_removes_only_obsolete_copy_outputs(self) -> None:
module = self.modules["cutleft"] module = self.modules["cutleft"]
with tempfile.TemporaryDirectory() as directory: with tempfile.TemporaryDirectory() as directory:
+31 -1
View File
@@ -4,7 +4,7 @@ import threading
import unittest import unittest
from pathlib import Path from pathlib import Path
from queue import Queue from queue import Queue
from unittest.mock import Mock, patch from unittest.mock import Mock, call, patch
from PIL import Image from PIL import Image
@@ -157,6 +157,36 @@ class MarkedCopiesTests(unittest.TestCase):
self.assertEqual(cutleft.main([str(self.workspace.root), "--marked"]), expected) self.assertEqual(cutleft.main([str(self.workspace.root), "--marked"]), expected)
self.assertEqual(reviewer.call_args.args[0], [self.files[1]]) self.assertEqual(reviewer.call_args.args[0], [self.files[1]])
def test_fullpage_processes_every_copy_without_opening_the_reviewer(self):
for path in self.files:
mark_copy_error(self.workspace, path, "Review needed")
image = Image.new("RGB", (8, 8), "white")
result = (image, [image], {"columns_per_file": [1]})
with patch.object(
cutleft, "process_single_pdf", side_effect=[result, None]
) as process, patch.object(cutleft, "save_results") as save, patch.object(
cutleft, "ImageReviewer"
) as reviewer:
status = cutleft.run(
self.workspace,
self.workspace.root,
fullpage=True,
no_visual_check=True,
)
self.assertEqual(status, ExitCode.PARTIAL)
reviewer.assert_not_called()
self.assertEqual(
process.call_args_list,
[
call(self.files[0], max_per_file=1),
call(self.files[1], max_per_file=1),
],
)
save.assert_called_once_with(result, self.files[0], self.workspace.cutleft_dir)
self.assertEqual(set(copy_errors(self.workspace)), {"Copie02.pdf"})
@unittest.skipUnless(os.environ.get("DISPLAY"), "Tk requires a display") @unittest.skipUnless(os.environ.get("DISPLAY"), "Tk requires a display")
class MarkedCopiesGuiTests(unittest.TestCase): class MarkedCopiesGuiTests(unittest.TestCase):