Support evaluation-local configuration and names overrides
This commit is contained in:
1 parent
0bb6f7d97c
commit
d4404e4c27
36 files changed
+409
-184
No files matched your search
@@ -44,7 +44,7 @@ class AddFinalScoreTests(unittest.TestCase):
|
||||
merged.write_bytes(b'merged')
|
||||
output = root / 'output'
|
||||
output.mkdir()
|
||||
with patch.object(add_final_score, 'HISTOGRAM_PATH', histogram):
|
||||
with patch.object(add_final_score.configuration, 'FINAL_SCORE_HISTOGRAM_PATH', histogram):
|
||||
add_final_score.copy_histogram(output)
|
||||
self.assertFalse((output / merged.name).exists())
|
||||
add_final_score.copy_histogram(output, copy_merged_histogramme=True)
|
||||
@@ -54,7 +54,7 @@ class AddFinalScoreTests(unittest.TestCase):
|
||||
def test_cli_forwards_merged_histogram_option(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
with patch.object(add_final_score, 'OUTPUT_DIR', root / 'output'), patch.object(
|
||||
with patch.object(add_final_score.configuration, 'FINAL_SCORE_OUTPUT_DIR', root / 'output'), patch.object(
|
||||
add_final_score, 'process_images'
|
||||
) as process:
|
||||
self.assertEqual(add_final_score.main([str(root), '--copy-merged-histogramme']), 0)
|
||||
@@ -83,7 +83,7 @@ class AddFinalScoreTests(unittest.TestCase):
|
||||
histogram = root / "histogramme.pdf"
|
||||
histogram.write_bytes(b"histogram")
|
||||
with patch.object(add_final_score.pd, "read_excel", return_value=scores), \
|
||||
patch.object(add_final_score, "HISTOGRAM_PATH", histogram), \
|
||||
patch.object(add_final_score.configuration, "FINAL_SCORE_HISTOGRAM_PATH", histogram), \
|
||||
contextlib.redirect_stdout(io.StringIO()):
|
||||
add_final_score.process_images(root, output)
|
||||
|
||||
@@ -137,7 +137,7 @@ class AddFinalScoreTests(unittest.TestCase):
|
||||
with patch.object(
|
||||
add_final_score.pd, "read_excel", return_value=scores
|
||||
), patch.object(
|
||||
add_final_score, "HISTOGRAM_PATH", root / "missing.pdf"
|
||||
add_final_score.configuration, "FINAL_SCORE_HISTOGRAM_PATH", root / "missing.pdf"
|
||||
), contextlib.redirect_stdout(messages):
|
||||
add_final_score.process_images(root, output)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class AnnotationHeightConfigTests(unittest.TestCase):
|
||||
[sys.executable, "-c",
|
||||
"from copienator.commands import annotating_by_label as grouped; "
|
||||
"from copienator.commands import annotating_with_checks as checks; "
|
||||
f"print(grouped.{attribute}, checks.{attribute})"],
|
||||
f"print(grouped.annotation_limits()[{0 if attribute == 'MAX_HEIGHT_PX' else 1}], checks.annotation_limits()[{0 if attribute == 'MAX_HEIGHT_PX' else 1}])"],
|
||||
cwd=Path(__file__).resolve().parents[1],
|
||||
env={**os.environ, "COPIENATOR_CONFIG": str(config)},
|
||||
capture_output=True, text=True, check=False,
|
||||
|
||||
@@ -24,7 +24,7 @@ class AnnotationImportCleanupTests(unittest.TestCase):
|
||||
(self.workspace.root / "correction.json").write_text("{}")
|
||||
self.import_dir = self.root / "Annotées"
|
||||
self.import_dir.mkdir()
|
||||
self.patch = patch.object(import_annotations, "IMPORT_DIR", self.import_dir)
|
||||
self.patch = patch.object(import_annotations.configuration, "IMPORT_DIR", self.import_dir)
|
||||
self.patch.start()
|
||||
self.addCleanup(self.patch.stop)
|
||||
|
||||
@@ -45,12 +45,12 @@ class AnnotationImportCleanupTests(unittest.TestCase):
|
||||
def test_cleanup_handles_unconfigured_or_missing_folder(self):
|
||||
for configured in (None, "", self.root / "absent"):
|
||||
with self.subTest(configured=configured), \
|
||||
patch.object(import_annotations, "IMPORT_DIR", configured):
|
||||
patch.object(import_annotations.configuration, "IMPORT_DIR", configured):
|
||||
import_annotations.clear_import_directory(self.workspace)
|
||||
self.assertFalse((self.root / "absent").exists())
|
||||
|
||||
def test_cleanup_rejects_an_ancestor_of_evaluation(self):
|
||||
with patch.object(import_annotations, "IMPORT_DIR", self.root), \
|
||||
with patch.object(import_annotations.configuration, "IMPORT_DIR", self.root), \
|
||||
self.assertRaises(CliError):
|
||||
import_annotations.clear_import_directory(self.workspace)
|
||||
self.assertTrue(self.workspace.root.is_dir())
|
||||
|
||||
@@ -6,9 +6,10 @@ from unittest.mock import patch
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from copienator import configuration
|
||||
from copienator import CliError, EvaluationWorkspace, ExitCode, read_json
|
||||
from copienator.annotation_data import AnnotationLoadResult
|
||||
from copienator.annotation_documents import MAX_HEIGHT_PX, copy_documents
|
||||
from copienator.annotation_documents import annotation_limits, copy_documents
|
||||
from copienator.commands import annotating_with_checks as checks
|
||||
from copienator.commands import export, import_annotations, reading_annotations as reader
|
||||
from copienator.commands import reading_grouped_annotations as grouped_reader
|
||||
@@ -46,7 +47,7 @@ class CheckedDocumentTests(unittest.TestCase):
|
||||
|
||||
def generate(self, overwrite=False):
|
||||
with (
|
||||
patch.object(checks, 'MAX_HEIGHT_PX', 100),
|
||||
patch.object(configuration, 'ANNOTATION_MAX_HEIGHT_PX', 100),
|
||||
patch.object(checks.annotating, 'make_base_image', return_value=(None, 0, 0)),
|
||||
patch.object(checks.annotating, 'compose_label_image', side_effect=self.compose),
|
||||
):
|
||||
@@ -54,7 +55,7 @@ class CheckedDocumentTests(unittest.TestCase):
|
||||
overwrite=overwrite, output_mode='checks')
|
||||
|
||||
def test_splits_on_labels_and_resets_geometry_at_each_part(self):
|
||||
self.assertEqual(MAX_HEIGHT_PX, 33000)
|
||||
self.assertEqual(annotation_limits()[0], 33000)
|
||||
self.assertEqual(self.generate(), 'success')
|
||||
parts = copy_documents(self.output)
|
||||
self.assertEqual([part.name for part in parts], ['Part1', 'Part2'])
|
||||
@@ -76,7 +77,7 @@ class CheckedDocumentTests(unittest.TestCase):
|
||||
self.assertFalse((self.output / 'Part1').exists())
|
||||
|
||||
def test_pixel_limit_splits_documents_at_exact_boundary(self):
|
||||
with patch.object(checks, 'MAX_PIXELS', 6000):
|
||||
with patch.object(configuration, 'ANNOTATION_MAX_PIXELS', 6000):
|
||||
self.assertEqual(self.generate(), 'success')
|
||||
parts = copy_documents(self.output)
|
||||
self.assertEqual([read_json(p / 'bnote.json')['height'] for p in parts], [60, 40, 60])
|
||||
@@ -88,13 +89,13 @@ class CheckedDocumentTests(unittest.TestCase):
|
||||
if label == 'Ex 2':
|
||||
image = image.resize((200, image.height))
|
||||
return image, header
|
||||
with patch.object(self, 'compose', side_effect=wider), patch.object(checks, 'MAX_PIXELS', 12000):
|
||||
with patch.object(self, 'compose', side_effect=wider), patch.object(configuration, 'ANNOTATION_MAX_PIXELS', 12000):
|
||||
self.assertEqual(self.generate(), 'success')
|
||||
parts = copy_documents(self.output)
|
||||
self.assertEqual([read_json(p / 'bnote.json')['width'] for p in parts], [100, 200, 100])
|
||||
sentinel = self.output / 'sentinel'
|
||||
sentinel.write_text('previous')
|
||||
with patch.object(checks, 'MAX_PIXELS', 5999):
|
||||
with patch.object(configuration, 'ANNOTATION_MAX_PIXELS', 5999):
|
||||
with self.assertRaisesRegex(CliError, 'Copie01.*Ex 1'):
|
||||
self.generate(overwrite=True)
|
||||
self.assertEqual(sentinel.read_text(), 'previous')
|
||||
@@ -121,7 +122,7 @@ class CheckedDocumentTests(unittest.TestCase):
|
||||
def returned_parts(self):
|
||||
self.generate()
|
||||
export_root = self.root / 'Export'
|
||||
with patch.object(export, 'EXPORT_DIR', export_root):
|
||||
with patch.object(export.configuration, 'EXPORT_DIR', export_root):
|
||||
self.assertEqual(export.run(self.workspace, annotation_dir='Bnot'), ExitCode.SUCCESS)
|
||||
self.assertEqual(sorted(p.name for p in (export_root / 'Exam').iterdir()),
|
||||
['Copie01 Part1.pdf', 'Copie01 Part2.pdf'])
|
||||
|
||||
@@ -99,7 +99,7 @@ class GeminiConfigTests(unittest.TestCase):
|
||||
]
|
||||
config = types.GenerateContentConfig(thinking_config=thinking_config("high"))
|
||||
with patch.object(correction, "client", client), patch.object(
|
||||
correction, "MODEL_ID_pro", "pro-model"
|
||||
configuration, "MODEL_PRO_ID", "pro-model"
|
||||
), patch.object(correction, "pro_quota_exhausted", False), patch.object(
|
||||
configuration, "THINKING_LEVEL_FLASH", "low"
|
||||
), patch.object(correction, "tprint"), patch.object(correction, "stop_requested") as stop:
|
||||
|
||||
@@ -88,7 +88,7 @@ class GroupedRedoTests(unittest.TestCase):
|
||||
[("01", "Ex 1"), ("02", "Ex 1")],
|
||||
)
|
||||
export_root = Path(self.temp.name) / "Export"
|
||||
with patch.object(export, "EXPORT_DIR", export_root):
|
||||
with patch.object(export.configuration, "EXPORT_DIR", export_root):
|
||||
self.assertEqual(export.run(self.workspace, refaire=True), ExitCode.SUCCESS)
|
||||
self.assertEqual(len(list((export_root / "Exam").glob("*.pdf"))), 1)
|
||||
imported_root = Path(self.temp.name) / "Import"
|
||||
@@ -99,7 +99,7 @@ class GroupedRedoTests(unittest.TestCase):
|
||||
draw.rectangle((17, 17, 43, 43), fill="black")
|
||||
draw.rectangle((70, 170, 90, 190), fill="black")
|
||||
annotated.save(imported_root / f"{directory.name}.pdf", "PDF", resolution=72)
|
||||
with patch.object(import_annotations, "IMPORT_DIR", imported_root):
|
||||
with patch.object(import_annotations.configuration, "IMPORT_DIR", imported_root):
|
||||
self.assertEqual(
|
||||
import_annotations.run(self.workspace, refaire=True), ExitCode.SUCCESS
|
||||
)
|
||||
|
||||
+15
-14
@@ -18,6 +18,7 @@ from unittest.mock import Mock, patch
|
||||
from PIL import Image
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
|
||||
from copienator import configuration
|
||||
from copienator import (
|
||||
CliError,
|
||||
EvaluationWorkspace,
|
||||
@@ -531,7 +532,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
evaluation = base / "Exam"
|
||||
evaluation.mkdir()
|
||||
export_dir = base / "Export"
|
||||
with patch.object(module, "EXPORT_DIR", export_dir), redirect_stderr(
|
||||
with patch.object(module.configuration, "EXPORT_DIR", export_dir), redirect_stderr(
|
||||
io.StringIO()
|
||||
):
|
||||
self.assertEqual(module.main([str(evaluation)]), 3)
|
||||
@@ -706,7 +707,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
source = evaluation / "BGnot" / "Ex 1"
|
||||
source.mkdir(parents=True)
|
||||
(source / "Concat.pdf").write_bytes(b"annotated")
|
||||
with patch.object(module, "EXPORT_DIR", base / "Export"):
|
||||
with patch.object(module.configuration, "EXPORT_DIR", base / "Export"):
|
||||
self.assertEqual(module.main([str(evaluation)]), 0)
|
||||
exported = base / "Export" / "Exam" / "Ex 1.pdf"
|
||||
self.assertEqual(exported.read_bytes(), b"annotated")
|
||||
@@ -721,7 +722,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
source.mkdir(parents=True)
|
||||
suffix = ".jpg" if annotation_dir == "Anot" else ".pdf"
|
||||
(source / f"Concat{suffix}").write_bytes(annotation_dir.encode())
|
||||
with patch.object(module, "EXPORT_DIR", base / "Export"):
|
||||
with patch.object(module.configuration, "EXPORT_DIR", base / "Export"):
|
||||
self.assertEqual(module.main([str(evaluation), annotation_dir]), 0)
|
||||
self.assertEqual(
|
||||
(base / "Export" / "Exam" / f"Copie01{suffix}").read_bytes(),
|
||||
@@ -738,7 +739,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
import_dir = base / "Import"
|
||||
import_dir.mkdir()
|
||||
(import_dir / "Ex 1.pdf").write_bytes(b"handwritten")
|
||||
with patch.object(module, "IMPORT_DIR", import_dir):
|
||||
with patch.object(module.configuration, "IMPORT_DIR", import_dir):
|
||||
self.assertEqual(module.main([str(evaluation)]), 0)
|
||||
self.assertEqual(
|
||||
(target / "Concat_annotated.pdf").read_bytes(), b"handwritten"
|
||||
@@ -756,7 +757,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
import_dir.mkdir()
|
||||
suffix = ".jpg" if annotation_dir == "Anot" else ".pdf"
|
||||
(import_dir / f"Copie01{suffix}").write_bytes(b"handwritten")
|
||||
with patch.object(module, "IMPORT_DIR", import_dir):
|
||||
with patch.object(module.configuration, "IMPORT_DIR", import_dir):
|
||||
self.assertEqual(module.main([str(evaluation), annotation_dir]), 0)
|
||||
self.assertEqual(
|
||||
(target / f"Concat_annotated{suffix}").read_bytes(), b"handwritten"
|
||||
@@ -2323,7 +2324,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
("02", "Ex 1", small, 0, []),
|
||||
("02", "Ex 2", small, 0, []),
|
||||
]
|
||||
with patch.object(module, "MAX_HEIGHT_PX", 100):
|
||||
with patch.object(configuration, "ANNOTATION_MAX_HEIGHT_PX", 100):
|
||||
batches = module.split_batches(rendered)
|
||||
self.assertEqual([len(batch) for batch in batches], [1, 2])
|
||||
self.assertTrue(
|
||||
@@ -2338,14 +2339,14 @@ class StandardCliTests(unittest.TestCase):
|
||||
("01", "Ex 2", image, 0, []),
|
||||
("02", "Ex 1", image, 0, []),
|
||||
]
|
||||
with patch.object(module, "MAX_HEIGHT_PX", 100):
|
||||
with patch.object(configuration, "ANNOTATION_MAX_HEIGHT_PX", 100):
|
||||
batches = module.split_batches(rendered)
|
||||
self.assertEqual(batches, [[rendered[0]], [rendered[1]], [rendered[2]]])
|
||||
|
||||
def test_grouped_oversized_answer_is_rejected(self) -> None:
|
||||
module = self.modules["annotating_by_label"]
|
||||
rendered = [("02", "Ex 1", Image.new("RGB", (10, 120)), 0, [])]
|
||||
with tempfile.TemporaryDirectory() as directory, patch.object(module, "MAX_HEIGHT_PX", 100):
|
||||
with tempfile.TemporaryDirectory() as directory, patch.object(configuration, "ANNOTATION_MAX_HEIGHT_PX", 100):
|
||||
with self.assertRaisesRegex(CliError, "Copie02.*Ex 1"):
|
||||
module.split_batches(rendered)
|
||||
with self.assertRaises(CliError):
|
||||
@@ -2359,7 +2360,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
("02", "Ex 1", Image.new("RGB", (30, 20)), 0, []),
|
||||
("03", "Ex 1", Image.new("RGB", (10, 40)), 0, []),
|
||||
]
|
||||
with patch.object(module, "MAX_HEIGHT_PX", 100), patch.object(module, "MAX_PIXELS", 2000):
|
||||
with patch.object(configuration, "ANNOTATION_MAX_HEIGHT_PX", 100), patch.object(configuration, "ANNOTATION_MAX_PIXELS", 2000):
|
||||
batches = module.split_batches(rendered)
|
||||
self.assertEqual(batches, [[rendered[0], rendered[2]], [rendered[1]]])
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
@@ -2372,9 +2373,9 @@ class StandardCliTests(unittest.TestCase):
|
||||
module = self.modules["annotating_by_label"]
|
||||
image = Image.new("RGB", (30, 40))
|
||||
rendered = [("01", label, image, 0, []) for label in ("Ex 1", "Ex 2")]
|
||||
with patch.object(module, "MAX_HEIGHT_PX", 100), patch.object(module, "MAX_PIXELS", 1200):
|
||||
with patch.object(configuration, "ANNOTATION_MAX_HEIGHT_PX", 100), patch.object(configuration, "ANNOTATION_MAX_PIXELS", 1200):
|
||||
self.assertEqual(module.split_batches(rendered), [[rendered[0]], [rendered[1]]])
|
||||
with patch.object(module, "MAX_PIXELS", 1199):
|
||||
with patch.object(configuration, "ANNOTATION_MAX_PIXELS", 1199):
|
||||
with self.assertRaisesRegex(CliError, "single answer cannot be split"):
|
||||
module.split_batches(rendered)
|
||||
|
||||
@@ -2387,7 +2388,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
("04", "Ex 1", Image.new("RGB", (10, 15)), 0, []),
|
||||
("04", "Ex 2", Image.new("RGB", (10, 15)), 0, []),
|
||||
]
|
||||
with patch.object(module, "MAX_HEIGHT_PX", 100):
|
||||
with patch.object(configuration, "ANNOTATION_MAX_HEIGHT_PX", 100):
|
||||
batches = module.split_batches(rendered)
|
||||
self.assertEqual([[item[0] for item in batch] for batch in batches],
|
||||
[["01", "04", "04"], ["02", "03"]])
|
||||
@@ -2397,7 +2398,7 @@ class StandardCliTests(unittest.TestCase):
|
||||
|
||||
def test_grouped_annotation_height_limit_is_33000_pixels(self) -> None:
|
||||
module = self.modules["annotating_by_label"]
|
||||
self.assertEqual(module.MAX_HEIGHT_PX, 33000)
|
||||
self.assertEqual(module.annotation_limits()[0], 33000)
|
||||
|
||||
|
||||
class WorkflowTests(unittest.TestCase):
|
||||
@@ -2511,7 +2512,7 @@ class WorkflowTests(unittest.TestCase):
|
||||
def test_crop_auto_start_follows_always_crop_configuration(self) -> None:
|
||||
for enabled in (False, True):
|
||||
with self.subTest(enabled=enabled), patch(
|
||||
"copienator_gui.workflow.ALWAYS_CROP", enabled
|
||||
"copienator_gui.workflow.configuration.ALWAYS_CROP", enabled
|
||||
):
|
||||
steps = {step.id: step for step in build_workflow(False)}
|
||||
self.assertEqual(
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
"""Evaluation files override shared settings without leaking across evaluations."""
|
||||
import contextlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from copienator import EvaluationWorkspace
|
||||
|
||||
|
||||
REPOSITORY = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class LocalConfigurationTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
self.root = Path(self.temporary.name)
|
||||
self.evaluation = self.root / "Exam"
|
||||
self.evaluation.mkdir()
|
||||
(self.evaluation / "labels").write_text("Ex 1\n")
|
||||
self.global_config = self.root / "config.py"
|
||||
self.global_config.write_text("MODEL_FLASH_ID = 'global-model'\nRETURN_PDF_ENABLED = False\n")
|
||||
|
||||
def run_python(self, source, *, explicit=True):
|
||||
env = dict(os.environ, PYTHONPATH=str(REPOSITORY))
|
||||
env.pop("COPIENATOR_CONFIG", None)
|
||||
if explicit:
|
||||
env["COPIENATOR_CONFIG"] = str(self.global_config)
|
||||
result = subprocess.run([sys.executable, "-c", source], cwd=self.root, env=env,
|
||||
capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
return result.stdout
|
||||
|
||||
def test_partial_local_config_inherits_global_and_defaults(self):
|
||||
(self.evaluation / "config.py").write_text("MODEL_FLASH_ID = 'local-model'\n")
|
||||
for explicit in (False, True):
|
||||
with self.subTest(explicit=explicit):
|
||||
self.run_python("""
|
||||
from copienator import configuration as c
|
||||
c.use_evaluation('Exam')
|
||||
assert c.MODEL_FLASH_ID == 'local-model'
|
||||
assert c.RETURN_PDF_ENABLED is False
|
||||
assert c.RETURN_JPEG_ENABLED is True
|
||||
assert c.CONFIG_PATH.name == 'config.py'
|
||||
assert c.LOCAL_CONFIG_PATH.parent.name == 'Exam'
|
||||
c.use_evaluation(None)
|
||||
assert c.MODEL_FLASH_ID == 'global-model'
|
||||
""", explicit=explicit)
|
||||
|
||||
def test_switching_evaluations_removes_previous_overrides(self):
|
||||
(self.evaluation / "config.py").write_text("RETURN_JPEG_ENABLED = False\n")
|
||||
(self.root / "Other").mkdir()
|
||||
self.run_python("""
|
||||
from copienator import configuration as c
|
||||
c.use_evaluation('Exam')
|
||||
assert c.RETURN_JPEG_ENABLED is False
|
||||
c.use_evaluation('Other')
|
||||
assert c.RETURN_JPEG_ENABLED is True
|
||||
assert c.RETURN_PDF_ENABLED is False
|
||||
assert c.LOCAL_CONFIG_PATH is None
|
||||
""")
|
||||
|
||||
def test_command_loads_config_for_directory_and_nested_target(self):
|
||||
(self.evaluation / "config.py").write_text("MODEL_FLASH_ID = 'local-model'\n")
|
||||
self.run_python("""
|
||||
from copienator import configuration as c
|
||||
from copienator.cli import execute, evaluation_parser, target_parser
|
||||
from copienator.commands import correction
|
||||
|
||||
def check(args):
|
||||
assert correction.settings.MODEL_FLASH_ID == 'local-model'
|
||||
return 0
|
||||
assert execute(evaluation_parser('test'), ['Exam'], check) == 0
|
||||
c.use_evaluation(None)
|
||||
assert execute(target_parser('test'), ['Exam/labels'], check) == 0
|
||||
""")
|
||||
|
||||
def test_annotation_limits_and_gui_defaults_follow_active_config(self):
|
||||
(self.evaluation / "config.py").write_text(
|
||||
"ANNOTATION_MAX_HEIGHT_PX = 12345\nALWAYS_CROP = True\n")
|
||||
self.run_python("""
|
||||
from copienator import configuration as c
|
||||
from copienator.commands import annotating_by_label, annotating_with_checks
|
||||
from copienator_gui.workflow import build_workflow
|
||||
c.use_evaluation('Exam')
|
||||
assert annotating_by_label.annotation_limits()[0] == 12345
|
||||
assert annotating_with_checks.annotation_limits()[0] == 12345
|
||||
steps = {step.id: step for step in build_workflow(False)}
|
||||
assert steps['crop_blank_margins'].auto_start_first_visit
|
||||
""")
|
||||
|
||||
@unittest.skipUnless(os.environ.get("DISPLAY"), "GUI display unavailable")
|
||||
def test_gui_switches_evaluation_settings(self):
|
||||
(self.evaluation / "config.py").write_text("ALWAYS_CROP = True\n")
|
||||
(self.root / "Other").mkdir()
|
||||
self.run_python("""
|
||||
from pathlib import Path
|
||||
from copienator import configuration as c
|
||||
from copienator_gui.app import CopienatorApp
|
||||
app = CopienatorApp(Path.cwd(), False, Path('Exam').resolve())
|
||||
try:
|
||||
assert app.step_by_id['crop_blank_margins'].auto_start_first_visit
|
||||
app.evaluation_var.set(str(Path('Other').resolve()))
|
||||
app._load_evaluation()
|
||||
assert not app.step_by_id['crop_blank_margins'].auto_start_first_visit
|
||||
assert c.LOCAL_CONFIG_PATH is None
|
||||
finally:
|
||||
app.destroy()
|
||||
""")
|
||||
|
||||
def test_explicit_command_option_overrides_local_config(self):
|
||||
(self.evaluation / "config.py").write_text("RETURN_ANSWERS_CONTEXT = True\n")
|
||||
self.run_python("""
|
||||
from unittest.mock import patch
|
||||
from copienator.commands import reading_annotations as command
|
||||
from copienator import configuration as c
|
||||
|
||||
def check(workspace, **options):
|
||||
assert c.RETURN_ANSWERS_CONTEXT is True
|
||||
assert options['return_answers_context'] is False
|
||||
return 0
|
||||
with patch.object(command, 'run', check):
|
||||
assert command.main(['Exam', '--no-return-answers-context']) == 0
|
||||
""")
|
||||
|
||||
def test_invalid_local_config_reports_error_before_handler(self):
|
||||
(self.evaluation / "config.py").write_text("ANNOTATION_MAX_HEIGHT_PX = 0\n")
|
||||
self.run_python("""
|
||||
from unittest.mock import Mock
|
||||
from copienator.cli import execute, evaluation_parser
|
||||
handler = Mock()
|
||||
assert execute(evaluation_parser('test'), ['Exam'], handler) == 1
|
||||
handler.assert_not_called()
|
||||
""")
|
||||
|
||||
def test_local_names_replace_global_including_an_empty_roster(self):
|
||||
(self.root / "names").write_text("Global\n")
|
||||
workspace = EvaluationWorkspace(self.evaluation, self.root)
|
||||
self.assertEqual(workspace.names_file(), self.root / "names")
|
||||
local = self.evaluation / "names"
|
||||
local.write_text("Local\n")
|
||||
self.assertEqual(workspace.names_file(), local)
|
||||
local.write_text("")
|
||||
self.assertEqual(workspace.names_file().read_text(), "")
|
||||
with contextlib.chdir(self.root):
|
||||
self.assertEqual(EvaluationWorkspace(self.evaluation).names_file(), local)
|
||||
local.unlink()
|
||||
self.assertEqual(EvaluationWorkspace(self.evaluation).names_file(), self.root / "names")
|
||||
@@ -116,7 +116,7 @@ class MarkedCopiesTests(unittest.TestCase):
|
||||
def test_personal_crop_binding_follows_personal_workflow_option(self):
|
||||
from copienator.configuration import SHOW_PERSONAL_STEPS
|
||||
|
||||
self.assertEqual(cutleft.personnal_binding, bool(SHOW_PERSONAL_STEPS))
|
||||
self.assertEqual(cutleft.personal_bindings_enabled(), bool(SHOW_PERSONAL_STEPS))
|
||||
|
||||
def test_process_single_pdf_enlarges_crop_and_caps_it_at_page_edge(self):
|
||||
page = Image.new("RGB", (900, 300), "white")
|
||||
|
||||
@@ -153,7 +153,7 @@ class NameGridTests(unittest.TestCase):
|
||||
self.assertEqual(len(requests), 4)
|
||||
self.assertEqual(requests[0].kwargs['model'], 'custom-name-model')
|
||||
self.assertEqual(requests[0].kwargs['config'].thinking_config.thinking_level.value, 'MEDIUM')
|
||||
self.assertEqual(requests[1].kwargs['model'], labels.MODEL_ID)
|
||||
self.assertEqual(requests[1].kwargs['model'], labels.config.MODEL_FOR_LABEL_ID)
|
||||
for request in requests[1:3]:
|
||||
text = request.kwargs['contents'][0].parts[1].text
|
||||
self.assertNotIn('Alice', text)
|
||||
|
||||
@@ -5,7 +5,8 @@ from unittest.mock import Mock
|
||||
|
||||
import pymupdf
|
||||
|
||||
from copienator.commands.page_splitter import PDFPreviewer, PAGE_SPLITTER_KB
|
||||
from copienator.commands.page_splitter import PDFPreviewer
|
||||
from copienator.configuration import PAGE_SPLITTER_KB
|
||||
|
||||
|
||||
class ReversePagesTests(unittest.TestCase):
|
||||
|
||||
@@ -120,7 +120,7 @@ class RefaireSessionTests(unittest.TestCase):
|
||||
output = self.workspace.annotation_dir("refaire") / "Ex 1 G1"
|
||||
output.mkdir(parents=True)
|
||||
(output / "Concat.pdf").write_bytes(expected_id.encode())
|
||||
with patch.object(export, "EXPORT_DIR", export_root):
|
||||
with patch.object(export.configuration, "EXPORT_DIR", export_root):
|
||||
self.assertEqual(
|
||||
export.run(self.workspace, refaire=True), ExitCode.SUCCESS
|
||||
)
|
||||
@@ -131,7 +131,7 @@ class RefaireSessionTests(unittest.TestCase):
|
||||
if expected_id == first:
|
||||
(import_root / exported.name).write_bytes(b"old return")
|
||||
current = self.workspace.annotation_dir("refaire") / "Ex 1 G1"
|
||||
with patch.object(import_annotations, "IMPORT_DIR", import_root):
|
||||
with patch.object(import_annotations.configuration, "IMPORT_DIR", import_root):
|
||||
self.assertEqual(
|
||||
import_annotations.run(self.workspace, refaire=True), ExitCode.PARTIAL
|
||||
)
|
||||
|
||||
@@ -325,7 +325,7 @@ class SelectiveGeminiTests(unittest.TestCase):
|
||||
for label in self.labels:
|
||||
(self.root / 'Persp' / label).write_text(label + '\nReviewed rubric')
|
||||
before = self.snapshot()
|
||||
with patch.object(gemini, 'api_key', ''), patch.object(gemini.genai, 'Client') as client:
|
||||
with patch.object(gemini.config, 'API_KEY', ''), patch.object(gemini.genai, 'Client') as client:
|
||||
self.assertEqual(gemini.refine_existing(self.workspace, 'persp'), ExitCode.SUCCESS)
|
||||
client.assert_not_called()
|
||||
self.assertEqual(self.snapshot(), before)
|
||||
|
||||
Reference in new issue
Block a user