Cache for reading annotation

This commit is contained in:
sebastien committed 2026-10-24 10:22:41 +02:00
1 parent 55d5cf16c9
commit ff7496b113
12 files changed
+466 -13

No files matched your search

@@ -27,6 +27,7 @@ from copienator.annotation_actions import apply_checkbox_actions, apply_score_ov
from copienator.annotation_data import AnnotationData, RefaireList, load_annotation_data
from copienator.annotation_documents import copy_documents, tablet_documents
from copienator.annotation_layout import legacy_layout
from copienator.annotation_read_cache import AnnotationReadCache
from copienator.annotation_modifications import (
BASELINE,
baseline,
@@ -36,6 +37,7 @@ from copienator.annotation_modifications import (
)
from copienator.answer_info import build_answer_info
from copienator.commands import annotating
from copienator.commands.giving_names import update_named_return_answers
from copienator.commands.reading_annotations import (
concatenate,
detect_checks_and_notes,
@@ -594,6 +596,18 @@ def run(
and path.is_dir()
and not path.name.startswith("Copie")
]
# Redo reads deliberately remain full reads: their selected labels and
# preserved baseline images have different dependencies.
cache = (AnnotationReadCache(workspace, annotation_dir, loaded.data, group_dirs)
if update_score and not refaire else None)
pending_ids = set(loaded.data)
if cache is not None:
pending_ids = {copy_id for copy_id in loaded.data if not cache.unchanged(copy_id)}
for copy_id in sorted(set(loaded.data) - pending_ids, key=utils.natural_key):
print(f"Skipping unchanged Copie{copy_id}")
if not pending_ids:
return ExitCode.PARTIAL if loaded.warnings else ExitCode.SUCCESS
only_ids = pending_ids
# 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.
@@ -657,6 +671,7 @@ def run(
if refaire
else sorted(loaded.data, key=utils.natural_key)
)
regenerated: set[str] = set()
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
futures = {
executor.submit(
@@ -676,13 +691,19 @@ def run(
),
): student_id
for student_id in student_ids
if student_id in loaded.data and student_id not in skipped_students
if student_id in pending_ids and student_id not in skipped_students
}
for future in concurrent.futures.as_completed(futures):
result, output = future.result()
print(output)
if result != ExitCode.SUCCESS:
status = ExitCode.PARTIAL
else:
regenerated.add(futures[future])
if update_named_return_answers(workspace, annotation_dir, copy_ids=regenerated) != ExitCode.SUCCESS:
status = ExitCode.PARTIAL
elif cache is not None:
cache.remember(regenerated)
return status