Bnot : split large copies into pieces.

This commit is contained in:
sebastien committed 2026-10-23 08:32:17 +02:00
1 parent 834ffe32f2
commit 1959c5d3bb
10 files changed
+333 -52

No files matched your search

+64 -22
View File
@@ -26,6 +26,7 @@ from copienator import (
workspace_from_target,
)
from copienator.annotation_data import load_annotation_data
from copienator.annotation_documents import MAX_HEIGHT_PX, copy_documents
from copienator.commands import annotating
from copienator.commands.import_annotations import clear_import_directory
from copienator.filesystem import staged_directory
@@ -143,7 +144,11 @@ class CheckboxRenderer:
def _output_complete(output_dir: Path) -> bool:
return all((output_dir / name).is_file() for name in EXPECTED_OUTPUTS)
return all(
(document / name).is_file()
for document in copy_documents(output_dir)
for name in EXPECTED_OUTPUTS
)
def _render_student(
@@ -197,7 +202,6 @@ def _render_student(
"label": label,
"header_height": header_height,
"layout": final_image.info.get('annotation_layout', annotating.layout_for(None)),
"img_h": final_image.height,
}
)
@@ -205,6 +209,46 @@ def _render_student(
print(f"Warning: no annotations could be rendered for Copie{student_id}")
return "partial"
batches = []
start = 0
height = 0
for index, image in enumerate(label_images):
if image.height > MAX_HEIGHT_PX:
raise CliError(
f"Copie{student_id} ({bnote_entries[index]['label']}) is "
f"{image.height} px high; a single answer cannot be split and "
f"the maximum document height is {MAX_HEIGHT_PX} px."
)
if height + image.height > MAX_HEIGHT_PX:
batches.append((start, index))
start = index
height = 0
height += image.height
batches.append((start, len(label_images)))
with staged_directory(output_dir) as staging:
parts = []
for index, (start, end) in enumerate(batches, start=1):
destination = staging
if len(batches) > 1:
name = f"Part{index}"
parts.append(name)
destination /= name
_save_document(destination, label_images[start:end],
checkbox_groups[start:end], bnote_entries[start:end])
if parts:
atomic_write_json(staging / "bnote.json", {
"parts": parts, "images": bnote_entries,
})
return "partial" if problems else "success"
def _save_document(
output_dir: Path,
label_images: list[Image.Image],
checkbox_groups: list[list[dict[str, Any]]],
bnote_entries: list[dict[str, Any]],
) -> None:
max_width = max(image.width for image in label_images)
total_height = sum(image.height for image in label_images)
concatenated = Image.new("RGB", (max_width, total_height), "white")
@@ -216,7 +260,6 @@ def _render_student(
concatenated.paste(image, (0, current_y))
bnote_entries[index]["hmin"] = current_y
bnote_entries[index]["hmax"] = current_y + image.height
del bnote_entries[index]["img_h"]
for item in checkboxes:
box = item.get("final_box") or item.get("rel_box")
item["global_box"] = [
@@ -228,25 +271,24 @@ def _render_student(
checkbox_map.append(item)
current_y += image.height
with staged_directory(output_dir) as staging:
atomic_write_json(
staging / "bnote.json",
{"width": max_width, "height": total_height, "images": bnote_entries},
)
atomic_write_json(staging / "checkboxes.json", checkbox_map)
reference = staging / "Reference.jpg"
concatenated.save(reference, quality=90)
pdf_path = staging / "Concat.pdf"
pdf_canvas = canvas.Canvas(str(pdf_path), pagesize=(max_width, total_height))
pdf_canvas.drawImage(
str(reference),
0,
0,
width=max_width,
height=total_height,
)
pdf_canvas.save()
return "partial" if problems else "success"
output_dir.mkdir(parents=True, exist_ok=True)
atomic_write_json(
output_dir / "bnote.json",
{"width": max_width, "height": total_height, "images": bnote_entries},
)
atomic_write_json(output_dir / "checkboxes.json", checkbox_map)
reference = output_dir / "Reference.jpg"
concatenated.save(reference, quality=90)
pdf_path = output_dir / "Concat.pdf"
pdf_canvas = canvas.Canvas(str(pdf_path), pagesize=(max_width, total_height))
pdf_canvas.drawImage(
str(reference),
0,
0,
width=max_width,
height=total_height,
)
pdf_canvas.save()
def _copy_id_from_target(workspace: EvaluationWorkspace, target: Path) -> str | None: