Miscs improvements (Interro02)
This commit is contained in:
@@ -9,6 +9,7 @@ from typing import Any
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from copienator import configuration
|
||||
from copienator import (
|
||||
EvaluationWorkspace,
|
||||
ExitCode,
|
||||
@@ -29,9 +30,11 @@ from copienator.commands.reading_annotations import (
|
||||
has_significant_notes,
|
||||
)
|
||||
from copienator.filesystem import staged_files
|
||||
from copienator.return_answers import save_return_answer_options
|
||||
|
||||
LabelNotes = dict[str, dict[str, Any]]
|
||||
ScanResult = tuple[dict[str, list[dict[str, Any]]], dict[str, LabelNotes]]
|
||||
SCAN_WORKERS = 2
|
||||
|
||||
|
||||
def get_extra_pdfs_as_images(
|
||||
@@ -191,9 +194,11 @@ def apply_actions_and_regenerate_grouped(
|
||||
output_dir = workspace.root / annotation_dir / f"Copie{student_id}"
|
||||
labels_data = data.get(student_id, {})
|
||||
apply_checkbox_actions(labels_data, actions, logs.append)
|
||||
score_path = output_dir / "score.json"
|
||||
preserve_score_file = update_score and score_path.is_file()
|
||||
if update_score:
|
||||
apply_score_overrides(
|
||||
labels_data, output_dir / "score.json", logs.append
|
||||
labels_data, score_path, logs.append
|
||||
)
|
||||
|
||||
selected_labels = selected_labels if selected_labels is not None else set()
|
||||
@@ -350,7 +355,8 @@ def apply_actions_and_regenerate_grouped(
|
||||
atomic_write_json(staging / "refaire_simple_layout.json", simple_layout)
|
||||
for label, image in dirty_images.items():
|
||||
image.save(staging / f"{label}.jpg")
|
||||
atomic_write_json(staging / "score.json", scores)
|
||||
if not preserve_score_file:
|
||||
atomic_write_json(staging / "score.json", scores)
|
||||
atomic_write_json(staging / "info.json", build_answer_info(
|
||||
scores, labels_data, answer_labels, touched
|
||||
))
|
||||
@@ -364,6 +370,8 @@ def apply_actions_and_regenerate_grouped(
|
||||
[image for group in filtered_groups for image in group]
|
||||
)
|
||||
filtered_image.save(staging / "Concat_F.jpg")
|
||||
if preserve_score_file:
|
||||
logs.append(f" Preserved existing score.json in {output_dir}")
|
||||
logs.append(f" Saved regenerated files in {output_dir}")
|
||||
status = ExitCode.PARTIAL if incomplete else ExitCode.SUCCESS
|
||||
return status, "\n".join(logs)
|
||||
@@ -461,6 +469,9 @@ def run(
|
||||
refaire: bool = False,
|
||||
update_score: bool = False,
|
||||
annotation_dir: str = "BGnot",
|
||||
return_answers_context: bool | None = None,
|
||||
return_answers_question: bool | None = None,
|
||||
return_answers_solution: bool | None = None,
|
||||
) -> ExitCode:
|
||||
workspace.require_files("labels", "correction.json")
|
||||
workspace.require_directories("Copies", "Par label", annotation_dir)
|
||||
@@ -470,6 +481,25 @@ def run(
|
||||
workspace.require_files("refaire.json")
|
||||
workspace.require_directories("BRnot")
|
||||
refaire_list, refaire_by_student = _read_refaire(workspace)
|
||||
if configuration.RETURN_ANSWERS_ENABLED:
|
||||
save_return_answer_options(
|
||||
workspace.root,
|
||||
context=(
|
||||
configuration.RETURN_ANSWERS_CONTEXT
|
||||
if return_answers_context is None
|
||||
else return_answers_context
|
||||
),
|
||||
question=(
|
||||
configuration.RETURN_ANSWERS_QUESTION
|
||||
if return_answers_question is None
|
||||
else return_answers_question
|
||||
),
|
||||
solution=(
|
||||
configuration.RETURN_ANSWERS_SOLUTION
|
||||
if return_answers_solution is None
|
||||
else return_answers_solution
|
||||
),
|
||||
)
|
||||
|
||||
all_labels = utils.read_all_labels(workspace.root)
|
||||
loaded = load_annotation_data(workspace)
|
||||
@@ -494,7 +524,10 @@ def run(
|
||||
and path.is_dir()
|
||||
and not path.name.startswith("Copie")
|
||||
]
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor:
|
||||
# Each worker decodes a full-height returned group and its reference image.
|
||||
# Keep this stage deliberately narrow; answer regeneration below has its own
|
||||
# parallel executor and a much smaller per-task memory footprint.
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=SCAN_WORKERS) as executor:
|
||||
futures = [
|
||||
executor.submit(_scan_annotation_directory, path, only_ids)
|
||||
for path in group_dirs
|
||||
@@ -599,7 +632,28 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument(
|
||||
"--update-score",
|
||||
action="store_true",
|
||||
help="Override generated scores with values from existing score.json files",
|
||||
help=(
|
||||
"Regenerate images with current statement/solution PDFs while "
|
||||
"preserving and applying existing score.json values"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--return-answers-context",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=configuration.RETURN_ANSWERS_CONTEXT,
|
||||
help="Include applicable context pages in individual answer exports",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--return-answers-question",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=configuration.RETURN_ANSWERS_QUESTION,
|
||||
help="Include the current question PDF in individual answer exports",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--return-answers-solution",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=configuration.RETURN_ANSWERS_SOLUTION,
|
||||
help="Include the current solution PDF in individual answer exports",
|
||||
)
|
||||
return parser
|
||||
|
||||
@@ -615,6 +669,9 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
refaire=args.refaire,
|
||||
update_score=args.update_score,
|
||||
annotation_dir=args.annotation_dir,
|
||||
return_answers_context=args.return_answers_context,
|
||||
return_answers_question=args.return_answers_question,
|
||||
return_answers_solution=args.return_answers_solution,
|
||||
)
|
||||
|
||||
return execute(parser, argv, handle)
|
||||
|
||||
Reference in New Issue
Block a user