Files
Copies/copienator/return_answers.py
T

73 lines
3.5 KiB
Python

from __future__ import annotations
from pathlib import Path
from PIL import Image
from copienator import atomic_write_json, configuration, read_json, utils
from copienator.filesystem import staged_directory
from copienator.platform import safe_filename
def publish_answer_returns(root: Path, source: Path, destination: Path) -> None:
"""Publish individual reviewed answers and per-question information."""
scores = read_json(source / "score.json")
if not isinstance(scores, dict):
raise ValueError(f"Expected a score object in {source}")
info_path = source / "info.json"
if not info_path.is_file():
raise ValueError(f"Missing {info_path}; recompile annotations before giving-names")
info = read_json(info_path)
if not isinstance(info, dict) or set(info) != set(scores):
raise ValueError(f"Invalid question information in {info_path}; recompile annotations")
for label, entry in info.items():
if (
not isinstance(entry, dict)
or set(entry) != {"present", "not_empty", "touched", "score"}
or any(type(entry[key]) is not bool for key in ("present", "not_empty", "touched"))
or (entry["not_empty"] and not entry["present"])
or (entry["touched"] and not entry["not_empty"])
):
raise ValueError(f"Invalid question information in {info_path}; recompile annotations")
# Match score.json, including manual score edits awaiting recompilation.
entry["score"] = scores[label]
answers_dir = destination / "answers"
if answers_dir.is_symlink():
raise ValueError(f"Expected a real answer directory: {answers_dir}")
if configuration.RETURN_ANSWERS_ENABLED:
labels = [label for label, entry in info.items() if entry["present"] and entry["not_empty"]]
# Import the rendering backend only when individual images are requested.
from copienator.commands.annotating import make_base_image
from copienator.commands.reading_annotations import concatenate
all_labels = utils.read_all_labels(root)
with staged_directory(answers_dir) as staging:
for index, label in enumerate(sorted(labels, key=utils.natural_key), 1):
paths = []
if configuration.RETURN_ANSWERS_CONTEXT:
paths.extend(utils.pdf_images_of_contexts(root, label, all_labels))
if configuration.RETURN_ANSWERS_QUESTION:
paths.append(utils.pdf_image_of_enonce(root, label))
if configuration.RETURN_ANSWERS_SOLUTION:
paths.append(utils.pdf_image_of_solution(root, label))
images = []
for path in paths:
if path:
supplement, _, _ = make_base_image(path)
if supplement is None:
raise ValueError(f"Could not render {path}")
images.append(supplement)
with Image.open(source / f"{label}.jpg") as answer:
images.append(answer.convert("RGB"))
image = concatenate(images)
# Numbering avoids collisions between sanitized label filenames.
image.save(staging / f"{index:03d} - {safe_filename(label)}.jpg")
elif answers_dir.exists():
# Replace the managed directory with an empty one to remove stale exports.
with staged_directory(answers_dir):
pass
atomic_write_json(destination / "info.json", info)
(destination / "touched.json").unlink(missing_ok=True)