extension de export/import aux autres modes de génération
This commit is contained in:
+14
-5
@@ -1,7 +1,7 @@
|
|||||||
#+title: Script
|
#+title: Script
|
||||||
#+author: Sébastien Miquel
|
#+author: Sébastien Miquel
|
||||||
#+date: 14-03-2026
|
#+date: 14-03-2026
|
||||||
# Time-stamp: <22-08-26 09:58>
|
# Time-stamp: <22-08-26 12:05>
|
||||||
#+OPTIONS:
|
#+OPTIONS:
|
||||||
|
|
||||||
* Méta
|
* Méta
|
||||||
@@ -343,8 +343,6 @@ Optional : Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~
|
|||||||
(chères), pour une version low cost, passer =--limit 0=, toutes
|
(chères), pour une version low cost, passer =--limit 0=, toutes
|
||||||
les requêtes seront sur Gemini Flash.
|
les requêtes seront sur Gemini Flash.
|
||||||
|
|
||||||
Will it resume ? It seems so. Best to wait a bit.
|
|
||||||
|
|
||||||
Pour diminuer le coût, il est possible de batch les requêtes, qui
|
Pour diminuer le coût, il est possible de batch les requêtes, qui
|
||||||
seront alors traitées sous au plus 24h.
|
seront alors traitées sous au plus 24h.
|
||||||
+ =python correction.py Interro --batch=
|
+ =python correction.py Interro --batch=
|
||||||
@@ -414,10 +412,16 @@ Lors d'une régénération, les nouvelles sorties sont préparées dans un
|
|||||||
dossier temporaire voisin. Une erreur de rendu conserve donc la sortie
|
dossier temporaire voisin. Une erreur de rendu conserve donc la sortie
|
||||||
précédente ; pour =BGnot --overwrite=, le dossier complet n'est remplacé
|
précédente ; pour =BGnot --overwrite=, le dossier complet n'est remplacé
|
||||||
que si tous les groupes ont été produits.
|
que si tous les groupes ont été produits.
|
||||||
3. =python export.py Interro= (gestion perso)
|
3. =python export.py Interro BGnot= (gestion perso)
|
||||||
Cela déplace les groupes dans le dossier configuré par =EXPORT_DIR=
|
Cela déplace les groupes dans le dossier configuré par =EXPORT_DIR=
|
||||||
(par défaut =Export=).
|
(par défaut =Export=).
|
||||||
|
|
||||||
|
Le second argument peut être =BGnot=, =Bnot= ou =Anot= et reste
|
||||||
|
facultatif (=BGnot= par défaut). =Anot= exporte =Concat.jpg= ; les
|
||||||
|
deux autres modes exportent =Concat.pdf=. Dans le GUI, seuls les
|
||||||
|
dossiers présents sont proposés et le mode de la dernière génération
|
||||||
|
d'annotations est présélectionné.
|
||||||
|
|
||||||
Il faut ensuite annoter les fichiers dans `EXPORT_DIR` avec une
|
Il faut ensuite annoter les fichiers dans `EXPORT_DIR` avec une
|
||||||
tablette graphique.
|
tablette graphique.
|
||||||
|
|
||||||
@@ -426,12 +430,17 @@ tablette graphique.
|
|||||||
_Before_ : vider le dossier configuré par =IMPORT_DIR= (par défaut
|
_Before_ : vider le dossier configuré par =IMPORT_DIR= (par défaut
|
||||||
=Import=), puis y copier ou synchroniser les fichiers depuis la tablette.
|
=Import=), puis y copier ou synchroniser les fichiers depuis la tablette.
|
||||||
|
|
||||||
1. =python import.py Interro=
|
1. =python import.py Interro BGnot=
|
||||||
|
|
||||||
Une fois les corrections manuelles appliquées aux fichiers
|
Une fois les corrections manuelles appliquées aux fichiers
|
||||||
=Concat.pdf=, il faut enregistrer le fichier annoté au même endroit,
|
=Concat.pdf=, il faut enregistrer le fichier annoté au même endroit,
|
||||||
sous le nom =Concat_annotated.pdf=.
|
sous le nom =Concat_annotated.pdf=.
|
||||||
|
|
||||||
|
Comme pour l'export, le second argument accepte =BGnot=, =Bnot= ou
|
||||||
|
=Anot=. Le GUI présélectionne le dossier utilisé lors du dernier
|
||||||
|
export. Pour =Anot=, l'image est importée sous le nom
|
||||||
|
=Concat_annotated.jpg=.
|
||||||
|
|
||||||
2. =python reading_annotations.py Interro=
|
2. =python reading_annotations.py Interro=
|
||||||
|
|
||||||
Lit les =Concat_annotated= dans =Bnot=, regénère les copies avec
|
Lit les =Concat_annotated= dans =Bnot=, regénère les copies avec
|
||||||
|
|||||||
+43
-2
@@ -36,6 +36,12 @@ STATUS_LABELS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_HTTPS_PROXY = "http://10.0.0.1:3128"
|
DEFAULT_HTTPS_PROXY = "http://10.0.0.1:3128"
|
||||||
|
ANNOTATION_DIRECTORIES = ("BGnot", "Bnot", "Anot")
|
||||||
|
ANNOTATION_VARIANT_DIRECTORIES = {
|
||||||
|
"simple": "Anot",
|
||||||
|
"checks": "Bnot",
|
||||||
|
"grouped": "BGnot",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def copy_pdf_paths(evaluation: Path) -> list[Path]:
|
def copy_pdf_paths(evaluation: Path) -> list[Path]:
|
||||||
@@ -57,6 +63,12 @@ def copy_pdf_paths(evaluation: Path) -> list[Path]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def detected_annotation_directories(evaluation: Path) -> tuple[str, ...]:
|
||||||
|
return tuple(
|
||||||
|
name for name in ANNOTATION_DIRECTORIES if (evaluation / name).is_dir()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def plotting_shortcut_lines() -> list[str]:
|
def plotting_shortcut_lines() -> list[str]:
|
||||||
try:
|
try:
|
||||||
from config import PLOTTING_KB
|
from config import PLOTTING_KB
|
||||||
@@ -530,6 +542,9 @@ class CopienatorApp(tk.Tk):
|
|||||||
if spec.variants and variant.id not in spec.variants:
|
if spec.variants and variant.id not in spec.variants:
|
||||||
continue
|
continue
|
||||||
value = values.get(spec.name, value_for_default(spec.default, evaluation_arg))
|
value = values.get(spec.name, value_for_default(spec.default, evaluation_arg))
|
||||||
|
choices = spec.choices
|
||||||
|
if spec.name == "annotation_dir" and step.id in {"export", "import"}:
|
||||||
|
choices, value = self._annotation_directory_choices(step.id)
|
||||||
ttk.Label(self.form, text=spec.label).grid(row=row, column=0, sticky="nw", pady=4, padx=(0, 8))
|
ttk.Label(self.form, text=spec.label).grid(row=row, column=0, sticky="nw", pady=4, padx=(0, 8))
|
||||||
if spec.kind == "bool":
|
if spec.kind == "bool":
|
||||||
variable: tk.Variable = tk.BooleanVar(value=bool(value))
|
variable: tk.Variable = tk.BooleanVar(value=bool(value))
|
||||||
@@ -537,7 +552,7 @@ class CopienatorApp(tk.Tk):
|
|||||||
widget.grid(row=row, column=1, sticky="w", pady=4)
|
widget.grid(row=row, column=1, sticky="w", pady=4)
|
||||||
elif spec.kind == "choice":
|
elif spec.kind == "choice":
|
||||||
variable = tk.StringVar(value=str(value))
|
variable = tk.StringVar(value=str(value))
|
||||||
widget = ttk.Combobox(self.form, textvariable=variable, values=spec.choices, state="readonly")
|
widget = ttk.Combobox(self.form, textvariable=variable, values=choices, state="readonly")
|
||||||
widget.grid(row=row, column=1, sticky="ew", pady=4)
|
widget.grid(row=row, column=1, sticky="ew", pady=4)
|
||||||
else:
|
else:
|
||||||
variable = tk.StringVar(value=str(value))
|
variable = tk.StringVar(value=str(value))
|
||||||
@@ -626,6 +641,25 @@ class CopienatorApp(tk.Tk):
|
|||||||
row += 1
|
row += 1
|
||||||
return row
|
return row
|
||||||
|
|
||||||
|
def _annotation_directory_choices(self, step_id: str) -> tuple[tuple[str, ...], str]:
|
||||||
|
evaluation = self.evaluation
|
||||||
|
detected = detected_annotation_directories(evaluation) if evaluation else ()
|
||||||
|
choices = detected or ANNOTATION_DIRECTORIES
|
||||||
|
|
||||||
|
if step_id == "export":
|
||||||
|
annotation = self.state_store.step("annotation")
|
||||||
|
variant = annotation.get("last_run_variant", annotation.get("variant", "grouped"))
|
||||||
|
preferred = ANNOTATION_VARIANT_DIRECTORIES.get(str(variant), "BGnot")
|
||||||
|
else:
|
||||||
|
export = self.state_store.step("export")
|
||||||
|
last_values = export.get("last_run_values", export.get("values", {}))
|
||||||
|
preferred = (
|
||||||
|
str(last_values.get("annotation_dir", "BGnot"))
|
||||||
|
if isinstance(last_values, dict)
|
||||||
|
else "BGnot"
|
||||||
|
)
|
||||||
|
return choices, preferred if preferred in choices else choices[0]
|
||||||
|
|
||||||
def _open_persp(self) -> None:
|
def _open_persp(self) -> None:
|
||||||
evaluation = self.evaluation
|
evaluation = self.evaluation
|
||||||
self._open_desktop_path(evaluation / "Persp" if evaluation else None, "dossier Persp")
|
self._open_desktop_path(evaluation / "Persp" if evaluation else None, "dossier Persp")
|
||||||
@@ -775,9 +809,16 @@ class CopienatorApp(tk.Tk):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._save_current_form()
|
self._save_current_form()
|
||||||
|
run_values = self._values()
|
||||||
ordered_ids = [item.id for item in self.steps]
|
ordered_ids = [item.id for item in self.steps]
|
||||||
self.state_store.invalidate_after(ordered_ids, step.id)
|
self.state_store.invalidate_after(ordered_ids, step.id)
|
||||||
self.state_store.update_step(step.id, status="running", command=command_display(command))
|
self.state_store.update_step(
|
||||||
|
step.id,
|
||||||
|
status="running",
|
||||||
|
command=command_display(command),
|
||||||
|
last_run_variant=variant.id,
|
||||||
|
last_run_values=run_values,
|
||||||
|
)
|
||||||
self.active_step_id = step.id
|
self.active_step_id = step.id
|
||||||
workspace = self.state_store.workspace
|
workspace = self.state_store.workspace
|
||||||
assert workspace is not None
|
assert workspace is not None
|
||||||
|
|||||||
@@ -326,11 +326,19 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
|
|||||||
StepDefinition(
|
StepDefinition(
|
||||||
"export",
|
"export",
|
||||||
"Génération des annotations",
|
"Génération des annotations",
|
||||||
"Exporter vers la tablette",
|
"Exporter",
|
||||||
"Exporte les groupes vers le dossier EXPORT_DIR défini dans config.py.",
|
"Exporte les annotations vers le dossier EXPORT_DIR défini dans config.py.",
|
||||||
(python("default", "Export", "export.py"),),
|
(python("default", "Export", "export.py"),),
|
||||||
arguments=(
|
arguments=(
|
||||||
arg_target("Dossier de l’évaluation"),
|
arg_target("Dossier de l’évaluation"),
|
||||||
|
ArgumentSpec(
|
||||||
|
"annotation_dir",
|
||||||
|
"Dossier d’annotations",
|
||||||
|
"choice",
|
||||||
|
default="BGnot",
|
||||||
|
choices=("BGnot", "Bnot", "Anot"),
|
||||||
|
positional=True,
|
||||||
|
),
|
||||||
ArgumentSpec("refaire", "Mode refaire", "bool", "--refaire"),
|
ArgumentSpec("refaire", "Mode refaire", "bool", "--refaire"),
|
||||||
),
|
),
|
||||||
optional=True,
|
optional=True,
|
||||||
@@ -350,6 +358,14 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
|
|||||||
(python("default", "Import", "import.py"),),
|
(python("default", "Import", "import.py"),),
|
||||||
arguments=(
|
arguments=(
|
||||||
arg_target("Dossier de l’évaluation"),
|
arg_target("Dossier de l’évaluation"),
|
||||||
|
ArgumentSpec(
|
||||||
|
"annotation_dir",
|
||||||
|
"Dossier d’annotations",
|
||||||
|
"choice",
|
||||||
|
default="BGnot",
|
||||||
|
choices=("BGnot", "Bnot", "Anot"),
|
||||||
|
positional=True,
|
||||||
|
),
|
||||||
ArgumentSpec("refaire", "Mode refaire", "bool", "--refaire"),
|
ArgumentSpec("refaire", "Mode refaire", "bool", "--refaire"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ from copienator import (
|
|||||||
)
|
)
|
||||||
from platform_utils import replace_with_link_or_copy
|
from platform_utils import replace_with_link_or_copy
|
||||||
|
|
||||||
|
ANNOTATION_DIRECTORIES = ("BGnot", "Bnot", "Anot")
|
||||||
|
|
||||||
|
|
||||||
def export_directory(
|
def export_directory(
|
||||||
workspace: EvaluationWorkspace,
|
workspace: EvaluationWorkspace,
|
||||||
@@ -31,12 +33,22 @@ def export_directory(
|
|||||||
|
|
||||||
missing_outputs = 0
|
missing_outputs = 0
|
||||||
for subdir in subdirs:
|
for subdir in subdirs:
|
||||||
concat_file = subdir / "Concat.pdf"
|
concat_file = next(
|
||||||
if not concat_file.is_file():
|
(
|
||||||
print(f"Warning: file not found: {concat_file}", file=sys.stderr)
|
candidate
|
||||||
|
for candidate in (subdir / "Concat.pdf", subdir / "Concat.jpg")
|
||||||
|
if candidate.is_file()
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if concat_file is None:
|
||||||
|
print(
|
||||||
|
f"Warning: no Concat.pdf or Concat.jpg found in {subdir}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
missing_outputs += 1
|
missing_outputs += 1
|
||||||
continue
|
continue
|
||||||
destination = sync_dir / f"{subdir.name}.pdf"
|
destination = sync_dir / f"{subdir.name}{concat_file.suffix.lower()}"
|
||||||
method = replace_with_link_or_copy(concat_file, destination, prefer="hardlink")
|
method = replace_with_link_or_copy(concat_file, destination, prefer="hardlink")
|
||||||
print(f"Exported: {destination} ({method})")
|
print(f"Exported: {destination} ({method})")
|
||||||
return ExitCode.PARTIAL if missing_outputs else ExitCode.SUCCESS
|
return ExitCode.PARTIAL if missing_outputs else ExitCode.SUCCESS
|
||||||
@@ -44,12 +56,24 @@ def export_directory(
|
|||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
parser = evaluation_parser("Export annotated PDFs to the tablet directory.")
|
parser = evaluation_parser("Export annotated PDFs to the tablet directory.")
|
||||||
|
parser.add_argument(
|
||||||
|
"annotation_dir",
|
||||||
|
nargs="?",
|
||||||
|
choices=ANNOTATION_DIRECTORIES,
|
||||||
|
default="BGnot",
|
||||||
|
help="Annotation directory to export (default: BGnot)",
|
||||||
|
)
|
||||||
parser.add_argument("--refaire", action="store_true", help="Process only copies/labels defined in refaire.json")
|
parser.add_argument("--refaire", action="store_true", help="Process only copies/labels defined in refaire.json")
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def run(workspace: EvaluationWorkspace, *, refaire: bool = False) -> ExitCode:
|
def run(
|
||||||
return export_directory(workspace, "BRnot" if refaire else "BGnot")
|
workspace: EvaluationWorkspace,
|
||||||
|
*,
|
||||||
|
annotation_dir: str = "BGnot",
|
||||||
|
refaire: bool = False,
|
||||||
|
) -> ExitCode:
|
||||||
|
return export_directory(workspace, "BRnot" if refaire else annotation_dir)
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
@@ -57,7 +81,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|||||||
return execute(
|
return execute(
|
||||||
parser,
|
parser,
|
||||||
argv,
|
argv,
|
||||||
lambda args: run(workspace_from_args(args), refaire=args.refaire),
|
lambda args: run(
|
||||||
|
workspace_from_args(args),
|
||||||
|
annotation_dir=args.annotation_dir,
|
||||||
|
refaire=args.refaire,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,43 +13,69 @@ from copienator import (
|
|||||||
workspace_from_args,
|
workspace_from_args,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ANNOTATION_DIRECTORIES = ("BGnot", "Bnot", "Anot")
|
||||||
|
|
||||||
|
|
||||||
def sync_annotated(
|
def sync_annotated(
|
||||||
workspace: EvaluationWorkspace,
|
workspace: EvaluationWorkspace,
|
||||||
*,
|
*,
|
||||||
refaire: bool = False,
|
annotation_dir_name: str,
|
||||||
import_dir: Path,
|
import_dir: Path,
|
||||||
) -> ExitCode:
|
) -> ExitCode:
|
||||||
annotation_name = "BRnot" if refaire else "BGnot"
|
workspace.require_directories(annotation_dir_name)
|
||||||
workspace.require_directories(annotation_name)
|
annotation_dir = workspace.root / annotation_dir_name
|
||||||
annotation_dir = workspace.root / annotation_name
|
|
||||||
annotated_dir = Path(import_dir).expanduser()
|
annotated_dir = Path(import_dir).expanduser()
|
||||||
if not annotated_dir.is_dir():
|
if not annotated_dir.is_dir():
|
||||||
print(f"Error: directory does not exist: {annotated_dir}", file=sys.stderr)
|
print(f"Error: directory does not exist: {annotated_dir}", file=sys.stderr)
|
||||||
return ExitCode.INVALID_WORKSPACE
|
return ExitCode.INVALID_WORKSPACE
|
||||||
|
|
||||||
missing_targets = 0
|
missing_targets = 0
|
||||||
for pdf_file in annotated_dir.glob("*.pdf"):
|
annotated_files = sorted(
|
||||||
target_subdir = annotation_dir / pdf_file.stem
|
(
|
||||||
|
path
|
||||||
|
for path in annotated_dir.iterdir()
|
||||||
|
if path.is_file() and path.suffix.casefold() in {".pdf", ".jpg", ".jpeg"}
|
||||||
|
),
|
||||||
|
key=lambda path: path.name.casefold(),
|
||||||
|
)
|
||||||
|
for annotated_file in annotated_files:
|
||||||
|
target_subdir = annotation_dir / annotated_file.stem
|
||||||
|
|
||||||
if not target_subdir.is_dir():
|
if not target_subdir.is_dir():
|
||||||
print(f"Warning: directory not found: {target_subdir}", file=sys.stderr)
|
print(f"Warning: directory not found: {target_subdir}", file=sys.stderr)
|
||||||
missing_targets += 1
|
missing_targets += 1
|
||||||
else:
|
else:
|
||||||
dest_file = target_subdir / "Concat_annotated.pdf"
|
suffix = annotated_file.suffix.lower()
|
||||||
print(f"Copying {pdf_file} to {dest_file}")
|
dest_file = target_subdir / f"Concat_annotated{suffix}"
|
||||||
shutil.copy2(pdf_file, dest_file)
|
print(f"Copying {annotated_file} to {dest_file}")
|
||||||
|
shutil.copy2(annotated_file, dest_file)
|
||||||
return ExitCode.PARTIAL if missing_targets else ExitCode.SUCCESS
|
return ExitCode.PARTIAL if missing_targets else ExitCode.SUCCESS
|
||||||
|
|
||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
parser = evaluation_parser("Import handwritten annotations from the tablet directory.")
|
parser = evaluation_parser("Import handwritten annotations from the tablet directory.")
|
||||||
|
parser.add_argument(
|
||||||
|
"annotation_dir",
|
||||||
|
nargs="?",
|
||||||
|
choices=ANNOTATION_DIRECTORIES,
|
||||||
|
default="BGnot",
|
||||||
|
help="Annotation directory receiving imported PDFs (default: BGnot)",
|
||||||
|
)
|
||||||
parser.add_argument("--refaire", action="store_true", help="Process only copies/labels defined in refaire.json")
|
parser.add_argument("--refaire", action="store_true", help="Process only copies/labels defined in refaire.json")
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def run(workspace: EvaluationWorkspace, *, refaire: bool = False) -> ExitCode:
|
def run(
|
||||||
return sync_annotated(workspace, refaire=refaire, import_dir=Path(IMPORT_DIR))
|
workspace: EvaluationWorkspace,
|
||||||
|
*,
|
||||||
|
annotation_dir: str = "BGnot",
|
||||||
|
refaire: bool = False,
|
||||||
|
) -> ExitCode:
|
||||||
|
return sync_annotated(
|
||||||
|
workspace,
|
||||||
|
annotation_dir_name="BRnot" if refaire else annotation_dir,
|
||||||
|
import_dir=Path(IMPORT_DIR),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None) -> int:
|
def main(argv: Sequence[str] | None = None) -> int:
|
||||||
@@ -57,7 +83,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|||||||
return execute(
|
return execute(
|
||||||
parser,
|
parser,
|
||||||
argv,
|
argv,
|
||||||
lambda args: run(workspace_from_args(args), refaire=args.refaire),
|
lambda args: run(
|
||||||
|
workspace_from_args(args),
|
||||||
|
annotation_dir=args.annotation_dir,
|
||||||
|
refaire=args.refaire,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+90
-2
@@ -32,9 +32,11 @@ from copienator.annotation_actions import apply_checkbox_actions, apply_score_ov
|
|||||||
from copienator.annotation_data import AnnotationLoadResult, load_annotation_data
|
from copienator.annotation_data import AnnotationLoadResult, load_annotation_data
|
||||||
from copienator.filesystem import staged_directory, staged_files
|
from copienator.filesystem import staged_directory, staged_files
|
||||||
from copienator_gui.app import (
|
from copienator_gui.app import (
|
||||||
|
CopienatorApp,
|
||||||
DEFAULT_HTTPS_PROXY,
|
DEFAULT_HTTPS_PROXY,
|
||||||
build_runner_environment,
|
build_runner_environment,
|
||||||
copy_pdf_paths,
|
copy_pdf_paths,
|
||||||
|
detected_annotation_directories,
|
||||||
has_manual_conflicts,
|
has_manual_conflicts,
|
||||||
plotting_shortcut_lines,
|
plotting_shortcut_lines,
|
||||||
process_status,
|
process_status,
|
||||||
@@ -453,8 +455,16 @@ class StandardCliTests(unittest.TestCase):
|
|||||||
"personal",
|
"personal",
|
||||||
{"target": evaluation},
|
{"target": evaluation},
|
||||||
),
|
),
|
||||||
"export": ("export", "default", {"target": evaluation, "refaire": True}),
|
"export": (
|
||||||
"import": ("import", "default", {"target": evaluation, "refaire": True}),
|
"export",
|
||||||
|
"default",
|
||||||
|
{"target": evaluation, "annotation_dir": "Bnot", "refaire": True},
|
||||||
|
),
|
||||||
|
"import": (
|
||||||
|
"import",
|
||||||
|
"default",
|
||||||
|
{"target": evaluation, "annotation_dir": "Anot", "refaire": True},
|
||||||
|
),
|
||||||
"giving_names": (
|
"giving_names": (
|
||||||
"giving_names",
|
"giving_names",
|
||||||
"default",
|
"default",
|
||||||
@@ -596,6 +606,23 @@ class StandardCliTests(unittest.TestCase):
|
|||||||
exported = base / "Export" / "Exam" / "Ex 1.pdf"
|
exported = base / "Export" / "Exam" / "Ex 1.pdf"
|
||||||
self.assertEqual(exported.read_bytes(), b"annotated")
|
self.assertEqual(exported.read_bytes(), b"annotated")
|
||||||
|
|
||||||
|
def test_export_accepts_each_annotation_directory(self) -> None:
|
||||||
|
module = self.modules["export"]
|
||||||
|
for annotation_dir in ("Anot", "Bnot"):
|
||||||
|
with self.subTest(annotation_dir=annotation_dir), tempfile.TemporaryDirectory() as directory:
|
||||||
|
base = Path(directory)
|
||||||
|
evaluation = base / "Exam"
|
||||||
|
source = evaluation / annotation_dir / "Copie01"
|
||||||
|
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"):
|
||||||
|
self.assertEqual(module.main([str(evaluation), annotation_dir]), 0)
|
||||||
|
self.assertEqual(
|
||||||
|
(base / "Export" / "Exam" / f"Copie01{suffix}").read_bytes(),
|
||||||
|
annotation_dir.encode(),
|
||||||
|
)
|
||||||
|
|
||||||
def test_import_main_copies_handwritten_annotations(self) -> None:
|
def test_import_main_copies_handwritten_annotations(self) -> None:
|
||||||
module = self.modules["import"]
|
module = self.modules["import"]
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
@@ -612,6 +639,24 @@ class StandardCliTests(unittest.TestCase):
|
|||||||
(target / "Concat_annotated.pdf").read_bytes(), b"handwritten"
|
(target / "Concat_annotated.pdf").read_bytes(), b"handwritten"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_import_accepts_each_annotation_directory(self) -> None:
|
||||||
|
module = self.modules["import"]
|
||||||
|
for annotation_dir in ("Anot", "Bnot"):
|
||||||
|
with self.subTest(annotation_dir=annotation_dir), tempfile.TemporaryDirectory() as directory:
|
||||||
|
base = Path(directory)
|
||||||
|
evaluation = base / "Exam"
|
||||||
|
target = evaluation / annotation_dir / "Copie01"
|
||||||
|
target.mkdir(parents=True)
|
||||||
|
import_dir = base / "Import"
|
||||||
|
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):
|
||||||
|
self.assertEqual(module.main([str(evaluation), annotation_dir]), 0)
|
||||||
|
self.assertEqual(
|
||||||
|
(target / f"Concat_annotated{suffix}").read_bytes(), b"handwritten"
|
||||||
|
)
|
||||||
|
|
||||||
def test_giving_names_main_builds_return_directory(self) -> None:
|
def test_giving_names_main_builds_return_directory(self) -> None:
|
||||||
module = self.modules["giving_names"]
|
module = self.modules["giving_names"]
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
@@ -1406,6 +1451,15 @@ class WorkflowTests(unittest.TestCase):
|
|||||||
def test_review_persp_has_shorter_title(self) -> None:
|
def test_review_persp_has_shorter_title(self) -> None:
|
||||||
self.assertEqual(self.steps["review_persp"].title, "Relire les barèmes")
|
self.assertEqual(self.steps["review_persp"].title, "Relire les barèmes")
|
||||||
|
|
||||||
|
def test_export_has_shorter_title_and_annotation_argument(self) -> None:
|
||||||
|
self.assertEqual(self.steps["export"].title, "Exporter")
|
||||||
|
command = self.command(
|
||||||
|
"export",
|
||||||
|
"default",
|
||||||
|
{"target": self.evaluation, "annotation_dir": "Bnot"},
|
||||||
|
)
|
||||||
|
self.assertEqual(command[3:], [self.evaluation, "Bnot"])
|
||||||
|
|
||||||
def test_copy_listing_prefers_processed_copies(self) -> None:
|
def test_copy_listing_prefers_processed_copies(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
evaluation = Path(directory)
|
evaluation = Path(directory)
|
||||||
@@ -1421,6 +1475,40 @@ class WorkflowTests(unittest.TestCase):
|
|||||||
["Copie01.pdf", "Copie02.pdf"],
|
["Copie01.pdf", "Copie02.pdf"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_annotation_directory_detection_uses_known_directories(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
evaluation = Path(directory)
|
||||||
|
(evaluation / "Bnot").mkdir()
|
||||||
|
(evaluation / "Anot").mkdir()
|
||||||
|
(evaluation / "Other").mkdir()
|
||||||
|
self.assertEqual(
|
||||||
|
detected_annotation_directories(evaluation), ("Bnot", "Anot")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_export_and_import_defaults_follow_previous_runs(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
evaluation = Path(directory)
|
||||||
|
(evaluation / "Bnot").mkdir()
|
||||||
|
(evaluation / "Anot").mkdir()
|
||||||
|
store = StateStore()
|
||||||
|
store.load(evaluation)
|
||||||
|
store.update_step("annotation", last_run_variant="simple")
|
||||||
|
app = SimpleNamespace(evaluation=evaluation, state_store=store)
|
||||||
|
|
||||||
|
choices, default = CopienatorApp._annotation_directory_choices(
|
||||||
|
app, "export"
|
||||||
|
)
|
||||||
|
self.assertEqual(choices, ("Bnot", "Anot"))
|
||||||
|
self.assertEqual(default, "Anot")
|
||||||
|
|
||||||
|
store.update_step(
|
||||||
|
"export", last_run_values={"annotation_dir": "Bnot"}
|
||||||
|
)
|
||||||
|
_choices, default = CopienatorApp._annotation_directory_choices(
|
||||||
|
app, "import"
|
||||||
|
)
|
||||||
|
self.assertEqual(default, "Bnot")
|
||||||
|
|
||||||
def test_plotting_shortcuts_describe_open_actions(self) -> None:
|
def test_plotting_shortcuts_describe_open_actions(self) -> None:
|
||||||
rendered = "\n".join(plotting_shortcut_lines())
|
rendered = "\n".join(plotting_shortcut_lines())
|
||||||
self.assertIn("ouvrir l’énoncé", rendered)
|
self.assertIn("ouvrir l’énoncé", rendered)
|
||||||
|
|||||||
Reference in New Issue
Block a user