Restructuration de l'application

This commit is contained in:
2026-08-22 13:39:20 +02:00
parent dc25b5fefa
commit b9e9d00e7d
44 changed files with 561 additions and 8675 deletions
+30
View File
@@ -0,0 +1,30 @@
from __future__ import annotations
import argparse
from pathlib import Path
from copienator_gui.app import CopienatorApp
def personal_steps_enabled() -> bool:
try:
from copienator.configuration import SHOW_PERSONAL_STEPS
except (ImportError, AttributeError):
return False
return bool(SHOW_PERSONAL_STEPS)
def main(argv=None) -> int:
parser = argparse.ArgumentParser(description="Interface graphique du workflow Copienator")
parser.add_argument("evaluation", nargs="?", type=Path, help="Dossier d’évaluation à ouvrir")
args = parser.parse_args(argv)
repository = Path.cwd().resolve()
evaluation = args.evaluation.resolve() if args.evaluation else None
app = CopienatorApp(repository, personal_steps_enabled(), evaluation)
app.mainloop()
return 0
if __name__ == "__main__":
raise SystemExit(main())
+2 -2
View File
@@ -8,7 +8,7 @@ from tkinter import filedialog, messagebox, ttk
from typing import Any
from copienator import ExitCode
from platform_utils import WindowsLabelError, open_path, validate_windows_labels
from copienator.platform import WindowsLabelError, open_path, validate_windows_labels
from .diagnostics import collect_diagnostics
from .runner import ProcessRunner
@@ -71,7 +71,7 @@ def detected_annotation_directories(evaluation: Path) -> tuple[str, ...]:
def plotting_shortcut_lines() -> list[str]:
try:
from config import PLOTTING_KB
from copienator.configuration import PLOTTING_KB
except (ImportError, AttributeError):
from default_config import PLOTTING_KB
+2 -2
View File
@@ -7,7 +7,7 @@ import sys
from dataclasses import dataclass
from pathlib import Path
from platform_utils import windows_filename_problems
from copienator.platform import windows_filename_problems
@dataclass(frozen=True)
@@ -111,7 +111,7 @@ def collect_diagnostics(
)
)
try:
from config import CURRENT_SCORE_ODS_PATH, FINAL_SCORE_ODS_PATH
from copienator.configuration import CURRENT_SCORE_ODS_PATH, FINAL_SCORE_ODS_PATH
for label, configured_path in (
("ODS courant", CURRENT_SCORE_ODS_PATH),
+33 -33
View File
@@ -90,8 +90,8 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Analyser l’énoncé",
"Détecte les questions, leurs groupes, le corrigé et les indications de barème.",
(
python("gemini", "Analyse avec Gemini", "gemini_for_enonce.py"),
python("personal", "Alternative enonce_info.py", "enonce_info.py"),
python("gemini", "Analyse avec Gemini", "statement"),
python("personal", "Alternative personnelle", "statement-personal"),
),
arguments=(
arg_target("Dossier de l’évaluation"),
@@ -124,7 +124,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
CommandVariant(
"rotate",
"Rotation",
"copies_tools.py",
"copies",
"python",
("rotate",),
fixed_args_before_positionals=True,
@@ -142,7 +142,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
CommandVariant(
"rename",
"Renommage",
"copies_tools.py",
"copies",
"python",
("rename",),
fixed_args_before_positionals=True,
@@ -155,7 +155,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Prétraitement des copies",
"Séparer et réordonner les pages",
"Ouvre loutil interactif de découpage A3 vers A4. La cible peut être un dossier ou un PDF.",
(python("default", "Séparation des pages", "page_splitter.py"),),
(python("default", "Séparation des pages", "page-split"),),
arguments=(arg_target(),),
artifacts=("Copies", "Copies Originales"),
),
@@ -164,7 +164,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Prétraitement des copies",
"Découper la marge des labels",
"Produit les images de la partie gauche des copies. Une copie précise peut être ciblée.",
(python("default", "Découpe", "cutleft.py"),),
(python("default", "Découpe", "crop-labels"),),
arguments=(
arg_target(),
ArgumentSpec("fullpage", "Toujours utiliser la page entière", "bool", "--fullpage"),
@@ -177,7 +177,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Labels et regroupement",
"Détecter les labels avec Gemini",
"Identifie les labels dans les images produites par la découpe gauche.",
(python("default", "Détection des labels", "gemini_for_labels.py"),),
(python("default", "Détection des labels", "labels"),),
arguments=(
arg_target(),
ArgumentSpec("overwrite", "Régénérer les résultats", "bool", "--overwrite"),
@@ -190,7 +190,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Labels et regroupement",
"Vérifier visuellement les labels",
"Ouvre la fenêtre de vérification. La cible peut être toute l’évaluation ou une copie.",
(python("default", "Vérification", "plotting.py"),),
(python("default", "Vérification", "review-labels"),),
arguments=(arg_target(),),
requires=("labels", "Cutleft"),
artifacts=("Copies/Copie*.json",),
@@ -200,7 +200,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Labels et regroupement",
"Découper les réponses par question",
"Découpe les copies à partir des coordonnées de labels vérifiées.",
(python("default", "Découpage", "splitting_int.py"),),
(python("default", "Découpage", "split-answers"),),
arguments=(arg_target(),),
requires=("Copies",),
artifacts=("Copies/Copie*/*",),
@@ -211,7 +211,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Labels et regroupement",
"Regrouper les réponses",
"Regroupe les réponses portant le même label pour préparer les requêtes.",
(python("default", "Regroupement", "grouping.py"),),
(python("default", "Regroupement", "group-answers"),),
arguments=(arg_target("Dossier de l’évaluation"),),
requires=("Copies",),
artifacts=("Par label",),
@@ -222,7 +222,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Labels et regroupement",
"Vérifier les groupes produits",
"Vérifie que chaque réponse PDF apparaît dans les métadonnées des groupes.",
(python("default", "Vérification des groupes", "verify_groups.py"),),
(python("default", "Vérification des groupes", "verify-groups"),),
arguments=(arg_target("Dossier de l’évaluation"),),
optional=True,
requires=("Copies", "Par label"),
@@ -233,17 +233,17 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Lancer ou intégrer la correction",
"Choisir une correction immédiate, batch, hybride, une recorrection, ou lintégration dun batch.",
(
python("live", "Correction immédiate", "correction.py"),
python("batch", "Préparer toutes les requêtes batch", "correction.py", fixed_args=("--batch",)),
python("hybrid", "Batch à partir dun label", "correction.py"),
python("refaire", "Recorrection depuis refaire.json", "correction.py", fixed_args=("--refaire",)),
python("live", "Correction immédiate", "correct"),
python("batch", "Préparer toutes les requêtes batch", "correct", fixed_args=("--batch",)),
python("hybrid", "Batch à partir dun label", "correct"),
python("refaire", "Recorrection depuis refaire.json", "correct", fixed_args=("--refaire",)),
python(
"integrate",
"Intégrer les résultats batch",
"correction.py",
"correct",
fixed_args=("--deal-with-batched",),
),
python("reset", "Réinitialiser les corrections", "correction.py", fixed_args=("--reset",), dangerous=True),
python("reset", "Réinitialiser les corrections", "correct", fixed_args=("--reset",), dangerous=True),
),
arguments=(
arg_target("Évaluation ou image Group_X.jpg"),
@@ -259,7 +259,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Correction",
"Envoyer les batchs",
"Envoie à Gemini les fichiers JSONL produits par le mode batch.",
(python("default", "Envoi", "submit_batches.py"),),
(python("default", "Envoi", "batch-submit"),),
arguments=(arg_target("Dossier de l’évaluation"),),
optional=True,
artifacts=("batch_jobs.json",),
@@ -270,7 +270,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Correction",
"Consulter l’état des batchs",
"Affiche les jobs Gemini en cours. Lidentifiant de téléchargement est facultatif.",
(python("default", "État des batchs", "batch_status.py"),),
(python("default", "État des batchs", "batch-status"),),
arguments=(ArgumentSpec("download", "Télécharger le job", "text", "--download"),),
optional=True,
skip_for_live_correction=True,
@@ -280,7 +280,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Correction",
"Récupérer les résultats batch",
"Télécharge et rassemble les réponses des jobs terminés.",
(python("default", "Récupération", "fetch_batched_results.py"),),
(python("default", "Récupération", "batch-fetch"),),
arguments=(arg_target("Dossier de l’évaluation"),),
optional=True,
skip_for_live_correction=True,
@@ -290,7 +290,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Correction",
"Nettoyer la correction",
"Corrige certains problèmes dencodage et prépare le texte pour LaTeX.",
(python("default", "Post-correction", "post-correction.py"),),
(python("default", "Post-correction", "post-correction"),),
arguments=(arg_target("Dossier de l’évaluation"),),
requires=("correction.json",),
),
@@ -299,7 +299,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Correction",
"Résoudre les conflits manuels",
"Étape conditionnelle, uniquement si manual_resolutions.txt contient des conflits à traiter.",
(python("default", "Résolution", "resolve_manual.py"),),
(python("default", "Résolution", "resolve-manual"),),
arguments=(arg_target("Dossier de l’évaluation"),),
optional=True,
requires=("manual_resolutions.txt", "correction.json"),
@@ -311,9 +311,9 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Générer les copies annotées",
"Les trois modes sont exclusifs pour un parcours donné.",
(
python("simple", "Annotations simples (Anot)", "annotating.py"),
python("checks", "Annotations avec cases (Bnot)", "annotating_with_checks.py"),
python("grouped", "Annotations groupées (BGnot)", "annotating_by_label.py"),
python("simple", "Annotations simples (Anot)", "annotate-simple"),
python("checks", "Annotations avec cases (Bnot)", "annotate-checks"),
python("grouped", "Annotations groupées (BGnot)", "annotate-grouped"),
),
arguments=(
arg_target("Dossier de l’évaluation"),
@@ -328,7 +328,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Génération des annotations",
"Exporter",
"Exporte les annotations vers le dossier EXPORT_DIR défini dans config.py.",
(python("default", "Export", "export.py"),),
(python("default", "Export", "export"),),
arguments=(
arg_target("Dossier de l’évaluation"),
ArgumentSpec(
@@ -355,7 +355,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Correction manuscrite",
"Importer les annotations manuscrites",
"Copie les PDF présents dans IMPORT_DIR vers l’évaluation.",
(python("default", "Import", "import.py"),),
(python("default", "Import", "import"),),
arguments=(
arg_target("Dossier de l’évaluation"),
ArgumentSpec(
@@ -375,8 +375,8 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Lire les annotations manuscrites",
"Le mode doit correspondre au mode choisi lors de la génération des annotations.",
(
python("standard", "Lecture Bnot", "reading_annotations.py"),
python("grouped", "Lecture BGnot", "reading_grouped_annotations.py"),
python("standard", "Lecture Bnot", "read-annotations"),
python("grouped", "Lecture BGnot", "read-grouped"),
),
arguments=(
arg_target("Dossier de l’évaluation"),
@@ -389,7 +389,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Finalisation",
"Attribuer les noms et préparer A Rendre",
"Crée le dossier A Rendre à partir du dossier dannotations choisi.",
(python("default", "Attribution des noms", "giving_names.py"),),
(python("default", "Attribution des noms", "giving-names"),),
arguments=(
arg_target("Dossier de l’évaluation"),
ArgumentSpec(
@@ -424,7 +424,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Étapes personnelles",
"Mettre à jour le fichier ODS",
"Transfère les scores, avec la possibilité de n’écrire que leur somme.",
(python("default", "Mise à jour ODS", "update_ods.py"),),
(python("default", "Mise à jour ODS", "update-ods"),),
arguments=(
arg_target("Dossier de l’évaluation"),
ArgumentSpec("sum", "Écrire seulement la somme", "bool", "--sum"),
@@ -452,7 +452,7 @@ def build_workflow(show_personal_steps: bool) -> list[StepDefinition]:
"Étapes personnelles",
"Ajouter le score final",
"Génère les fichiers de diffusion avec le score final.",
(python("default", "Score final", "add_final_score.py"),),
(python("default", "Score final", "add-final-score"),),
arguments=(arg_target("Dossier de l’évaluation"),),
personal=True,
),
@@ -494,7 +494,7 @@ def build_command(
program_path = repository / variant.program
if variant.kind == "python":
command = [sys.executable, "-u", str(program_path)]
command = [sys.executable, "-u", "-m", "copienator", variant.program]
elif variant.kind == "shell":
command = [str(program_path)]
else: