miscs (Interro02) : horizontal cutting resolution

This commit is contained in:
2026-09-14 22:20:46 +02:00
parent 5080274e8f
commit 0a86403ca6
28 changed files with 1447 additions and 59 deletions
+48 -12
View File
@@ -9,6 +9,7 @@ from pathlib import Path
from typing import Any
from pypdf import PdfWriter
from copienator.pdf_cut import split_pdf
from copienator import (
CliError,
@@ -22,7 +23,8 @@ from copienator import (
)
OPERATORS = ("-x", "->", "x>", "ss", "sx", "xx", "xs")
OPERATOR_PATTERN = re.compile(r"\s+(-x|->|x>|ss|sx|xx|xs)\s+")
CUT_PATTERN = re.compile(r"c\{(\d+(?:\.\d+)?)\}([12])([x>])")
OPERATOR_PATTERN = re.compile(r"\s+(-x|->|x>|ss|sx|xx|xs|c\{\d+(?:\.\d+)?\}[12][x>])\s+")
COPY_PATTERN = re.compile(r"Copie(\d+)\s+(.+)")
@@ -34,6 +36,11 @@ class ManualInstruction:
new_label: str
pipe_first: bool
@property
def cut(self) -> tuple[float, int] | None:
match = CUT_PATTERN.fullmatch(self.operator)
return (float(match[1]), int(match[2])) if match else None
@property
def should_merge(self) -> bool:
return self.operator.endswith(">")
@@ -48,10 +55,14 @@ def build_parser() -> argparse.ArgumentParser:
def parse_instructions(path: Path) -> list[ManualInstruction]:
return parse_instruction_text(path.read_text(encoding="utf-8"))
def parse_instruction_text(text: str) -> list[ManualInstruction]:
instructions: list[ManualInstruction] = []
malformed: list[int] = []
for line_number, raw_line in enumerate(
path.read_text(encoding="utf-8").splitlines(), start=1
text.splitlines(), start=1
):
line = raw_line.strip()
if not line or line.startswith("###"):
@@ -64,7 +75,10 @@ def parse_instructions(path: Path) -> list[ManualInstruction]:
right = line[operator_match.end() :].strip()
copy_match = COPY_PATTERN.fullmatch(left)
new_label = right.strip("|").strip()
if copy_match is None or not new_label:
cut_match = CUT_PATTERN.fullmatch(operator_match.group(1))
if (copy_match is None or not new_label
or (cut_match and (not 0 < float(cut_match[1]) < 100
or copy_match.group(2).strip() == new_label))):
malformed.append(line_number)
continue
instructions.append(
@@ -97,15 +111,25 @@ def set_suffix_and_clean_error(
item["result"]["suffix"] = suffix
error = item["result"].get("error", "")
if new_label_target:
if f"wrg-lbl:{new_label_target}?delayed" in error:
item["result"]["error"] = (
f"wrg-lbl-moved-to:{new_label_target}"
)
if f"(delayed){new_label_target}" in error:
item["result"]["error"] = error.replace(
f"(delayed){new_label_target}",
f"(->){new_label_target}",
)
# This instruction acknowledges this source/target conflict,
# including decisions to keep/discard PDFs without merging.
# Leave other pending targets (and other copies) untouched.
result = item["result"]
if "delayed" in result:
pending = [entry for entry in result["delayed"]
if entry not in (["wrong-label", new_label_target],
["add-label", new_label_target])]
if pending:
result["delayed"] = pending
else:
result.pop("delayed")
if error in {f"wrg-lbl:{new_label_target}?",
f"wrg-lbl:{new_label_target}?delayed",
f"wrg-lbl:{new_label_target}?exists"}:
error = f"wrg-lbl-moved-to:{new_label_target}"
error = error.replace(f"(delayed){new_label_target}", f"(->){new_label_target}")
error = error.replace(f"(->){new_label_target}?", f"(->){new_label_target}")
result["error"] = error
def get_actual_pdf(copies_dir: Path, copy_id: str, label: str) -> Path:
@@ -154,6 +178,9 @@ def resolve_manual(workspace: EvaluationWorkspace) -> ExitCode:
raise CliError("correction.json must contain a JSON object")
results: dict[str, Any] = loaded
instructions = parse_instructions(workspace.manual_resolutions_file)
cut_sources = [(item.copy_id, item.old_label) for item in instructions if item.cut]
if len(set(cut_sources)) != len(cut_sources):
raise CliError("Une seule coupe par label source est autorisée dans une résolution.")
initial_paths: dict[tuple[str, str], Path] = {}
current_paths: dict[tuple[str, str], Path] = {}
@@ -174,6 +201,15 @@ def resolve_manual(workspace: EvaluationWorkspace) -> ExitCode:
key_new = (instruction.copy_id, instruction.new_label)
source = initial_paths[key_old]
destination = current_paths[key_new]
if instruction.cut:
percent, keep = instruction.cut
first = source.parent / f"temp_{len(temp_files)}.pdf"
second = source.parent / f"temp_{len(temp_files) + 1}.pdf"
temp_files.extend((first, second))
split_pdf(source, percent, first, second)
retained, source = (first, second) if keep == 1 else (second, first)
current_paths[key_old] = retained
files_to_old.add(initial_paths[key_old])
temp_output = (
workspace.copies_dir
/ f"Copie{instruction.copy_id}"