Standardisation 7

This commit is contained in:
2026-08-20 15:06:14 +02:00
parent aa40e58dd1
commit 18d1e5e2bb
3 changed files with 399 additions and 157 deletions
+128 -1
View File
@@ -11,7 +11,7 @@ import unittest
from concurrent.futures import ThreadPoolExecutor
from contextlib import redirect_stderr
from pathlib import Path
from unittest.mock import patch
from unittest.mock import Mock, patch
from PIL import Image
from pypdf import PdfReader, PdfWriter
@@ -310,6 +310,9 @@ class StandardCliTests(unittest.TestCase):
"page_splitter.py", "page_splitter"
),
"plotting": load_script_module("plotting.py", "plotting"),
"gemini_for_labels": load_script_module(
"gemini_for_labels.py", "gemini_for_labels"
),
"copies_tools": load_script_module(
"copies_tools.py", "copienator_copies_tools_test"
),
@@ -351,6 +354,7 @@ class StandardCliTests(unittest.TestCase):
"splitting_int": [missing],
"page_splitter": [missing],
"plotting": [missing],
"gemini_for_labels": [missing],
}
for name, arguments in invocations.items():
with self.subTest(script=name), redirect_stderr(io.StringIO()):
@@ -463,6 +467,11 @@ class StandardCliTests(unittest.TestCase):
"default",
{"target": evaluation},
),
"gemini_for_labels": (
"labels",
"default",
{"target": evaluation, "overwrite": True},
),
}
for module_name, (step_id, variant_id, values) in cases.items():
step = steps[step_id]
@@ -773,6 +782,124 @@ class StandardCliTests(unittest.TestCase):
self.assertIsNone(json_path)
self.assertEqual(metadata, {"worker_error": "worker failed"})
def test_label_detection_resolves_copy_and_cutleft_targets(self) -> None:
module = self.modules["gemini_for_labels"]
with tempfile.TemporaryDirectory() as directory:
evaluation = Path(directory) / "Exam"
copy_pdf = evaluation / "Copies" / "Copie01.pdf"
image_one = evaluation / "Cutleft" / "Copie01_01.jpg"
image_two = evaluation / "Cutleft" / "Copie01_02.jpg"
copy_pdf.parent.mkdir(parents=True)
image_one.parent.mkdir()
copy_pdf.write_bytes(b"pdf")
image_one.write_bytes(b"image")
image_two.write_bytes(b"image")
workspace = EvaluationWorkspace(evaluation)
images, warnings = module.selected_images(workspace, [copy_pdf])
self.assertEqual(images, [image_one, image_two])
self.assertEqual(warnings, [])
images, warnings = module.selected_images(workspace, [image_two])
self.assertEqual(images, [image_two])
self.assertEqual(warnings, [])
def test_label_detection_preserves_context_and_writes_atomically(self) -> None:
module = self.modules["gemini_for_labels"]
with tempfile.TemporaryDirectory() as directory:
evaluation = Path(directory) / "Exam"
copies = evaluation / "Copies"
cutleft = evaluation / "Cutleft"
copies.mkdir(parents=True)
cutleft.mkdir()
first = cutleft / "Copie01_01.jpg"
second = cutleft / "Copie01_02.jpg"
first.write_bytes(b"first image")
second.write_bytes(b"second image")
atomic_write_json(
copies / "Copie01_01.json",
{
"name": "Student",
"list": [{"box_2d": [1, 2, 3, 4], "label": "Ex 1"}],
},
)
response = Mock(
text=(
'{"name":"Continued","list":'
'[{"box_2d":[10,20,30,40],"label":"Ex 2"}]}'
)
)
client = Mock()
client.models.generate_content.return_value = response
generated = module.process_copy_group(
EvaluationWorkspace(evaluation),
"Copie01",
[first, second],
client=client,
labels_text="Ex 1\nEx 2\n",
names_text="Student\n",
valid_labels={"Ex 1", "Ex 2"},
valid_names={"Student", "Continued", "Unknown"},
overwrite=False,
sleep=lambda _seconds: None,
target_interval=0,
)
self.assertEqual(generated, 1)
self.assertEqual(client.models.generate_content.call_count, 1)
self.assertEqual(
read_json(copies / "Copie01_02.json"),
{
"name": "Continued",
"list": [{"box_2d": [10, 20, 30, 40], "label": "Ex 2"}],
},
)
def test_label_detection_retries_unknown_labels(self) -> None:
module = self.modules["gemini_for_labels"]
with tempfile.TemporaryDirectory() as directory:
evaluation = Path(directory) / "Exam"
(evaluation / "Copies").mkdir(parents=True)
image = evaluation / "Cutleft" / "Copie01_01.jpg"
image.parent.mkdir()
image.write_bytes(b"image")
client = Mock()
client.models.generate_content.side_effect = [
Mock(
text=(
'{"name":"Student","list":'
'[{"box_2d":[1,2,3,4],"label":"Wrong"}]}'
)
),
Mock(
text=(
'{"name":"Student","list":'
'[{"box_2d":[1,2,3,4],"label":"Ex 1"}]}'
)
),
]
sleeps = []
module.process_copy_group(
EvaluationWorkspace(evaluation),
"Copie01",
[image],
client=client,
labels_text="Ex 1\n",
names_text="Student\n",
valid_labels={"Ex 1"},
valid_names={"Student", "Unknown", "Continued"},
overwrite=True,
sleep=sleeps.append,
target_interval=0,
)
self.assertEqual(client.models.generate_content.call_count, 2)
self.assertIn(10, sleeps)
self.assertEqual(
read_json(evaluation / "Copies" / "Copie01_01.json")["list"][0][
"label"
],
"Ex 1",
)
def test_post_correction_main_cleans_json_atomically(self) -> None:
module = self.modules["post_correction"]
with tempfile.TemporaryDirectory() as directory: