From dc25b5fefa6b27348fe7711792c27a4a34d1985b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Miquel?= Date: Sat, 22 Aug 2026 13:17:54 +0200 Subject: [PATCH] Splitting of Readme.org --- Architecture.org | 124 ++++++++++++++ Readme.org | 413 +---------------------------------------------- Script.org | 354 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 485 insertions(+), 406 deletions(-) create mode 100644 Architecture.org create mode 100644 Script.org diff --git a/Architecture.org b/Architecture.org new file mode 100644 index 0000000..72d9178 --- /dev/null +++ b/Architecture.org @@ -0,0 +1,124 @@ +#+title: Architecture et conventions de développement +#+author: Sébastien Miquel +#+date: 22-08-2026 +#+OPTIONS: + +Ce document décrit les invariants techniques du projet. Il s'adresse +aux développeurs et aux agents logiciels qui modifient le dépôt. + +- [[file:Readme.org][Guide de démarrage]] +- [[file:Script.org][Référence des étapes et des scripts]] + +* API commune pour les scripts + +Le paquet =copienator= centralise les chemins d'une évaluation et les +écritures JSON sûres. Un script ne devrait donc plus reconstruire les +chemins partagés à la main : + +#+BEGIN_SRC python +from copienator import EvaluationWorkspace, atomic_write_json + +workspace = EvaluationWorkspace("Interro") +atomic_write_json(workspace.correction_file, corrections) +#+END_SRC + +=EvaluationWorkspace.discover(path)= retrouve également la racine d'une +évaluation à partir d'un fichier ou d'un sous-dossier. Sa construction +ne crée aucun fichier. La création explicite de +=.copienator/logs/= et =.copienator/runs/= se fait avec +=workspace.ensure_control_directories()=. Le chemin +=.copienator/state.sqlite3= est réservé à une future couche d'état +transactionnelle. + +=atomic_write_json= écrit d'abord dans un fichier temporaire situé dans +le même dossier, synchronise son contenu, puis remplace la destination. +Une interruption ne laisse donc pas un JSON partiellement écrit. Pour +une modification concurrente de type lire-modifier-écrire, utiliser +=atomic_update_json= : cet utilitaire protège l'opération complète avec +un verrou inter-processus Linux/Windows. + +* Convention des scripts standardisés + +Les scripts standardisés exposent =build_parser()=, =run(...)= et +=main(argv=None)=. Leur import ne lance aucun traitement. Ils acceptent +le dossier d'évaluation comme premier argument positionnel, utilisent +=EvaluationWorkspace= pour les chemins partagés et peuvent afficher la +trace complète d'une erreur avec =--verbose=. + +Les codes de sortie communs sont : + +| Code | Signification | +|------+---------------| +| 0 | réussite | +| 1 | erreur de traitement | +| 2 | arguments invalides | +| 3 | évaluation ou prérequis invalides | +| 4 | traitement partiel, avec avertissements | +| 130 | interruption par l'utilisateur | + +Le GUI distingue notamment un traitement partiel d'un échec. Les +scripts migrés vers cette convention sont actuellement : + +- =copies_tools.py=, =grouping.py= et =verify_groups.py= ; +- =post-correction.py= et =resolve_manual.py= ; +- =page_splitter.py=, =cutleft.py=, =plotting.py= et + =splitting_int.py= ; +- =gemini_for_labels.py= ; +- =gemini_for_enonce.py= et =enonce_info.py= ; +- =correction.py=, =submit_batches.py=, =batch_status.py= et + =fetch_batched_results.py= ; +- =annotating.py=, =annotating_with_checks.py= et + =annotating_by_label.py= ; +- =reading_annotations.py= et =reading_grouped_annotations.py= ; +- =export.py=, =import.py= et =giving_names.py=. + +* Organisation du code + +- Le paquet =copienator= contient les abstractions partagées par les + scripts, notamment =EvaluationWorkspace= et les écritures atomiques. +- Le paquet =copienator_gui= contient la définition du workflow, l'état + persistant, le lanceur de processus et l'application Tk. +- Les scripts situés à la racine restent des points d'entrée autonomes. + Leur import ne doit pas déclencher de traitement. + +* État et exécution du GUI + +L'état d'une évaluation est conservé dans =.copienator-gui.json=. Les +valeurs des arguments, le dernier mode exécuté, les statuts et un +historique borné y sont enregistrés atomiquement. Les sorties des +processus sont conservées dans =.copienator/logs/=. Le GUI lance les +scripts dans des processus séparés afin de pouvoir afficher leur sortie, +leur transmettre une saisie et les interrompre. + +Les retours en arrière ne suppriment pas les artefacts. Ils marquent les +étapes suivantes comme étant à revalider. Les automatismes de première +visite sont également persistés et ne doivent pas être rejoués lors d'un +retour en arrière. + +* Invariants à préserver + +- Utiliser =EvaluationWorkspace= pour les chemins partagés d'une + évaluation au lieu de reconstruire ces chemins dans chaque script. +- Publier les JSON et les ensembles de fichiers importants de façon + atomique. Une interruption ou un résultat partiel doit conserver la + dernière sortie complète lorsqu'elle existe. +- Conserver la convention =build_parser()=, =run(...)= et + =main(argv=None)= pour les scripts standardisés. +- Ne lancer aucun traitement lors de l'import d'un module. +- Retourner les codes de sortie communs afin que le GUI distingue une + réussite, un résultat partiel, un échec et une interruption. +- Préserver les modifications manuelles et les fichiers déjà présents, + sauf lorsqu'une option explicite comme =--overwrite= ou =--reset= + autorise leur remplacement. +- Les labels deviennent des noms de fichiers. Ils doivent donc respecter + les contraintes de la plateforme, notamment l'interdiction de =:= sous + Windows. + +* Règle de classement de la documentation + +- Une information nécessaire pour installer ou démarrer l'application + appartient à [[file:Readme.org][Readme.org]]. +- Une commande, un argument ou une étape du workflow appartient à + [[file:Script.org][Script.org]]. +- Une convention interne, un invariant ou une décision d'architecture + appartient au présent document. diff --git a/Readme.org b/Readme.org index 99ec4c8..7c45549 100644 --- a/Readme.org +++ b/Readme.org @@ -1,10 +1,10 @@ -#+title: Script +#+title: Copienator #+author: Sébastien Miquel #+date: 14-03-2026 # Time-stamp: <22-08-26 12:22> #+OPTIONS: -* Méta +* Présentation ** Quézaco Ce dépôt contient un certain nombre de script Python que j'utilise @@ -108,408 +108,9 @@ Les chemins des étapes personnelles peuvent être adaptés avec =CURRENT_SCORE_ODS_PATH=, =FINAL_SCORE_ODS_PATH=, =FINAL_SCORE_OUTPUT_DIR= et =FINAL_SCORE_FONT_PATH= dans =config.py=. -*** API commune pour les scripts +* Documentation complémentaire -Le paquet =copienator= centralise les chemins d'une évaluation et les -écritures JSON sûres. Un script ne devrait donc plus reconstruire les -chemins partagés à la main : - -#+BEGIN_SRC python -from copienator import EvaluationWorkspace, atomic_write_json - -workspace = EvaluationWorkspace("Interro") -atomic_write_json(workspace.correction_file, corrections) -#+END_SRC - -=EvaluationWorkspace.discover(path)= retrouve également la racine d'une -évaluation à partir d'un fichier ou d'un sous-dossier. Sa construction -ne crée aucun fichier. La création explicite de -=.copienator/logs/= et =.copienator/runs/= se fait avec -=workspace.ensure_control_directories()=. Le chemin -=.copienator/state.sqlite3= est réservé à une future couche d'état -transactionnelle. - -=atomic_write_json= écrit d'abord dans un fichier temporaire situé dans -le même dossier, synchronise son contenu, puis remplace la destination. -Une interruption ne laisse donc pas un JSON partiellement écrit. Pour -une modification concurrente de type lire-modifier-écrire, utiliser -=atomic_update_json= : cet utilitaire protège l'opération complète avec -un verrou inter-processus Linux/Windows. - -*** Convention des scripts standardisés - -Les scripts standardisés exposent =build_parser()=, =run(...)= et -=main(argv=None)=. Leur import ne lance aucun traitement. Ils acceptent -le dossier d'évaluation comme premier argument positionnel, utilisent -=EvaluationWorkspace= pour les chemins partagés et peuvent afficher la -trace complète d'une erreur avec =--verbose=. - -Les codes de sortie communs sont : - -| Code | Signification | -|------+---------------| -| 0 | réussite | -| 1 | erreur de traitement | -| 2 | arguments invalides | -| 3 | évaluation ou prérequis invalides | -| 4 | traitement partiel, avec avertissements | -| 130 | interruption par l'utilisateur | - -Le GUI distingue notamment un traitement partiel d'un échec. Les -scripts migrés vers cette convention sont actuellement : - -- =copies_tools.py=, =grouping.py= et =verify_groups.py= ; -- =post-correction.py= et =resolve_manual.py= ; -- =page_splitter.py=, =cutleft.py=, =plotting.py= et - =splitting_int.py= ; -- =gemini_for_labels.py= ; -- =gemini_for_enonce.py= et =enonce_info.py= ; -- =correction.py=, =submit_batches.py=, =batch_status.py= et - =fetch_batched_results.py= ; -- =annotating.py=, =annotating_with_checks.py= et - =annotating_by_label.py= ; -- =reading_annotations.py= et =reading_grouped_annotations.py= ; -- =export.py=, =import.py= et =giving_names.py=. - -* Étapes et Script - -Utiliser `python gui.py` ou `python gui.py Interro` pour lancer un GUI -qui suit automatiquement les étapes décrites ci-dessous. - -** Prétraitement de l'énoncé - -Dans le dossier de l'évaluation, mettre les fichiers suivants de l'évaluation : - -`enonce.pdf`, `enonce.tex`, `correction.tex`. - - - `python gemini_for_enonce.py Interro` or - `python gemini_for_enonce.py Interro --restart` - - À partir des trois fichiers précédents, se charge de détecter les - labels des questions et leur contenu. - - Les questions vont également être regroupées. Par la suite, quand - des requêtes de corrections seront effectuées sur une question, - seulement les énoncés des questions du groupe seront envoyés (et le - corrigé de la question). Il faut donc que chaque groupe contienne - si possible le contexte nécessaire pour comprendre la question. - - Une fenêtre s'ouvre pour permettre d'éditer le résultat. Ne pas - hésiter à faire des groupes plus gros que les groupes par défaut. - - Après relecture le script génère : - - + un fichier `labels` avec les labels des questions - + Un dossier `Text` avec le contenu textuel des questions, - regroupées. - + Un dossier `Sol` avec le contenu textuel du corrigé, question par - question. - + Un dossier `Text2`, qui compile un fichier `.tex` pour chaque - question (utilisé pour compiler un rendu pdf du corrigé pour - chaque question) - + Un dossier `Sol2`, qui compile un fichier `.tex` pour chaque - correction de chaque question. - + Un dossier `Persp` avec des instruction de barème pour chaque - question. - - Éventuellement : vérifier et modifier les barèmes dans `Persp`. - - - Alternative personnelle : `python enonce_info.py Interro` - - Ces deux commandes suivent la convention des scripts standardisés. - Leur import ne lance aucun traitement et les erreurs partielles sont - distinguées des échecs. Les réponses d'extraction mises en cache par - =gemini_for_enonce.py= et le fichier =labels= sont publiés - atomiquement. - -** Prétraitement des copies - -Mettre les copies scannées au format pdf dans =Interro=. - - 1. =python copies_tools.py rotate Interro= (facultatif) - - Retourne tous les pdf de 180°, si la photocopie a été faite à - l'envers. - 2. =python copies_tools.py rename Interro= - change le nom des copies en =Copie{id}.pdf= - 3. =python page_splitter.py Interro= - - Découpe des copies A3 en pages A4, en retirant les pages vides. - - Pour chaque page double il est possible - + de garder les deux pages - + de ne garder qu'une des deux pages. - + de jeter les deux pages - + de déplacer la délimitation à droite/gauche - - Fix issues with =python page_splitter.py Interro14/Copies/Copie01.pdf= - - Le PDF transformé est construit dans un dossier temporaire. La - copie produite et la sauvegarde dans =Copies Originales= sont - ensuite installées avec rollback : une erreur conserve les deux - versions précédentes. Une relance ciblée lit directement la - sauvegarde originale sans la déplacer au préalable. - 4. =python cutleft.py Interro= - - Découpe la partie gauche des copies, là où il devrait y avoir les - labels des exercices/questions. - - =python cutleft.py Interro --fullpage= to use the full page always. - - Rerun on a single file with =python cutleft.py Interro/Copies/Copie01.pdf= - - Les images et le fichier =_schema.json= d'une copie sont remplacés - ensemble. Fermer l'outil juste après la dernière validation ne peut - donc plus interrompre un thread de sauvegarde en arrière-plan. - -** Labelisation et regroupement - -Optional : Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~ - - 1. =python gemini_for_labels.py Interro=, avec éventuellement =--overwrite= - - Fait des requêtes à Gemini pour identifier les labels des - questions dans images générées à partir des parties gauches des copies. - - Une copie PDF ou une image précise de =Cutleft= peut également être - ciblée. Plusieurs cibles de la même évaluation sont acceptées. Les - parties d'une copie restent traitées séquentiellement afin de - conserver les labels précédents comme contexte, tandis que les - copies différentes sont traitées en parallèle. Chaque réponse JSON - validée est écrite atomiquement. Une cible sans image correspondante - produit le code de sortie 4. - 2. =python plotting.py Interro= - - Permet de vérifier visuellement les labels trouvés. - + Sous linux, on peut faire =e= pour ouvrir le fichier .json et - l'éditer a la main. - + Quand un label est manquant, il est possible de cliquer sur - l'image, ce qui copie les coordonnées dans le presse papier - (sous linux…), puis on peut l'ajouter à la main. - + Utilisation de `_`, `|…` et `…|` : - + `|…` n'est pas arrêté verticalement par son type opposé. - + `…|` est stoppé horizontalement par le `|…` le plus proche. - Pour modifier une seule copie : - =python plotting.py Interro/Copies/Copie01.pdf= - - Les coordonnées agrégées sont écrites atomiquement dans le JSON de - la copie. Fermer la fenêtre avant la fin d'une copie ne remplace pas - son JSON par un résultat incomplet. - - It also generates les =Copie01.json=, à partir des =Copie01_01.json= - En cas de soucis, (par exemple les pages ne sont pas dans le bon ordre) - - Réordonner les pages du fichier pdf - - Rerun =python cutleft.py Interro/Copie{id}= - - Rerun =python gemini_for_labels.py Interro/Copie{id}= - 3. =python splitting_int.py Interro= - - Découpe les copies suivant les exercices - Peut-être appelé avec une seule copie. - - Les réponses d'une copie sont préparées dans un dossier temporaire, - puis remplacent ensemble le dossier précédent. En cas d'erreur, - l'ancienne version est conservée. Les réponses devenues obsolètes - restent archivées dans le sous-dossier =Missing=. - 4. =python grouping.py Interro= - - Regroupe les mêmes questions de différentes copies en groupes de - tailles raisonnables. - 5. Facultatif : =python verify_groups.py Interro= - - Vérifie que chaque réponse PDF apparaît bien dans les métadonnées - des groupes. La commande renvoie un code non nul si une réponse est - absente ou si la vérification est incomplète. - -** Correction et annotation - -Optional : Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~ - - 1. Il faut créer des persp, pour indication de comment corriger, et - relancer =enonce_info.py= - 2. =python correction.py Interro --limit 240= OU - =python correction.py Interro/Par\ label/Ex\ 2/Group_1.jpg= OU - =python correction.py Interro --overwrite= - - Fais les requêtes de correction à Gemini. - - =correction.py= peut être relancé sans supprimer son état. Les - fichiers =correction.json= et =correction_progress.json= sont mis à - jour atomiquement. Avec =--overwrite=, leur version précédente reste - en place jusqu'à la première écriture réussie de la nouvelle - exécution. =--reset= est la seule option qui supprime explicitement - cet état et restaure les fichiers =*_old.pdf=. - - L'argument =limit= limite le nombre de requêtes à Gemini Pro - (chères), pour une version low cost, passer =--limit 0=, toutes - les requêtes seront sur Gemini Flash. - - Pour diminuer le coût, il est possible de batch les requêtes, qui - seront alors traitées sous au plus 24h. - + =python correction.py Interro --batch= - + OU =python correction.py Interro --batch-from 'Ex 4'= - + =python submit_batches.py Interro= - + =python batch_status.py= - + =python fetch_batched_results.py Interro= - + =python correction.py Interro --deal-with-batched= - - Les quatre commandes de ce flux suivent la convention des scripts - standardisés. Les fichiers de requêtes et le résultat JSONL combiné - sont publiés atomiquement : une interruption ne laisse pas de fichier - final partiellement écrit. =submit_batches.py= conserve aussi les - identifiants distants dans =batch_jobs.json= ; la récupération les - utilise en priorité et garde la recherche par nom pour les anciens - batchs. =batch_status.py --download JOB --output - resultat.jsonl= permet aussi de télécharger atomiquement le résultat - d'un job particulier. - 3. =python post-correction.py Interro= - - - Essaye de corriger des erreurs d'encodage/d'accents dans - =correction.json=. - - aussi échappe les `_` en dehors du mode math, pour LaTeX. - 4. Résolution manuel de conflits, s'il y en a. - - Edit `manual_resolutions.txt`. Use : - + `->` or `x>` : Here set a pipe `|` before or after the new_label name - + `-x` : replace the goal - + `ss` : do nothing - + `sx` : stay, and remove goal. - + `xx` : move to goal. - + `xs` : remove old, keep goal. - - Then call `python resolve_manual.py Interro` - 5. Call `python correction.py Interro --refaire`. - - -** Génération des copies annotées - - 1. =python annotating.py Interro= (facultatif) - - Ajoute les annotations Gemini aux copies, enregistrées dans le dossier =Anot=. - On peut passer l'argument =--overwrite=. -OU - 2. =python annotating_with_checks.py Interro= - - Ajoute les annotations Gemini, et des checkboxes à cocher. - Enregistrées dans le dossier =Bnot=, - =--overwrite= - - Une seule copie peut être ciblée avec, par exemple, - =python annotating_with_checks.py Interro/Copies/Copie01.pdf=. - Le mode =--refaire= exige un fichier =refaire.json= et écrit dans - =BRnot=. -OU - 2. =python annotating_by_label.py Interro= dans =BGnot= - - Ajoute les annotations Gemini, et des checkboxes, et regroupe les - réponses par question. - Enregistrées dans =BGnot= - - _Needs_ : label_groups file (made automatically by this function), - qui dit quelles questions regrouper. - - 3. =python export.py Interro BGnot= (gestion perso) - Cela déplace les groupes dans le dossier configuré par =EXPORT_DIR= - (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 -tablette graphique. - -** Lecture de la correction manuscrite - -_Before_ : vider le dossier configuré par =IMPORT_DIR= (par défaut -=Import=), puis y copier ou synchroniser les fichiers depuis la tablette. - - 1. =python import.py Interro BGnot= - - Une fois les corrections manuelles appliquées aux fichiers - =Concat.pdf=, il faut enregistrer le fichier annoté au même endroit, - 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= - - Lit les =Concat_annotated= dans =Bnot=, regénère les copies avec - les modifications. Les fichiers générés (=score.json=, - =Concat.jpg=, etc.) sont préparés séparément puis installés - ensemble. Les entrées du dossier =Bnot= restent en place et une - erreur de génération conserve les anciennes sorties. -OU - 2. =python reading_grouped_annotations.py Interro= - - Idem, mais pour =BGnot=. Les tâches parallèles remontent leurs - erreurs au processus principal au lieu de les ignorer. - - 3. =python giving_names.py Interro BGnot= - - Crée un dossier =A Rendre= avec des liens symboliques vers - + La copie à rendre - + un fichier =score.json= qui contient les notes par question - - Si un nom est =Unknown= : renommer à la main le dossier et le fichier dedans. - 4. Éventuellement, faire des modifications manuelles aux =score.json=. - - Puis - - `python reading_annotations.py --update-score Interro` - - `python reading_grouped_annotations.py --update-score Interro` - pour mettre à jour les scores dans les images. - 4. (gestion perso) - + =gestion_classe ne= pour créer l'interro puis - + =gestion_classe we= (set barème here) - + =python update_ods.py Interro= - ou =python update_ods.py Interro --sum= (en l'absence de barème) - + =gestion_classe re= - + =gestion_classe wsent= - + =python add_final_score.py Interro21= - (this makes files in =Server/copies=) - 5. (gestion perso) - + Deploy =miqmacs-copies-assets=, and - + update the copies from =miqmacs.fr/admin=. - 6. (gestion perso) Impression d'une copie. Via Evince » print to pdf. - -* Autres -** Recorrection d'une seule copie (peu testé) - -!! Attention, refaire ne marchera pas si tu fais une annotation non -groupée into refaire !! - - 1. Redécoupage - + =python plotting.py InterroTest/Copie01.pdf= - + =python splitting_int.py InterroTest/Copie20.pdf= - 2. Créer =refaire.json=, avec un contenu comme - : [["Copie02", []], - : ["Copie01", ["Ex 1 : 1)"]]] - 3. Appeler =correction= avec --refaire. Il doit créer des groupes - individuels, faire des requêtes, et remplacer les corrections - précédentes (à sauver ailleurs). - - Ou non, si tu veux le faire à la main. - 4. ?? Si je fais refaire, avant d'avoir créer les annotating with - checks, que se passe-t-il ??? - 5. Appeler =annotating_with_checks.py --refaire --overwrite= - 6. =python export.py --refaire Interro24= - 6. =python import.py --refaire Interro24= - 7. =python reading_grouped_annotations.py --refaire Interro24= - - Avec =--refaire=, =refaire.json= et le dossier =BRnot= sont des - prérequis obligatoires ; leur absence produit le code de sortie 3. - -** Exemple de replotting, refaire d'une copie - - 1. replot it. - 2. `python splitting_int.py DS09VA/Copies/Copie25.pdf` - this will get rid of old/new. - !! Attention, et si ça dégage un new : bad bad bad. - 3. Make `refaire.json`, avec la copie, et les labels à refaire. - 4. `python correction.py DS09VA --refaire` - 5. `python annotating_with_checks.py DS09VA --refaire` - 6. `python import.py Interro24 --refaire` +- [[file:Script.org][Référence des étapes et des scripts]] : commandes, arguments, + prérequis, fichiers produits et parcours alternatifs. +- [[file:Architecture.org][Architecture et conventions de développement]] : API commune, + état partagé, écritures sûres et règles de maintenance. diff --git a/Script.org b/Script.org new file mode 100644 index 0000000..4c5d45c --- /dev/null +++ b/Script.org @@ -0,0 +1,354 @@ +#+title: Référence des étapes et des scripts +#+author: Sébastien Miquel +#+date: 22-08-2026 +#+OPTIONS: + +Ce document décrit le workflow détaillé, les commandes disponibles et +les parcours alternatifs. + +- [[file:Readme.org][Guide de démarrage]] +- [[file:Architecture.org][Architecture et conventions de développement]] + +* Étapes et Script + +Utiliser `python gui.py` ou `python gui.py Interro` pour lancer un GUI +qui suit automatiquement les étapes décrites ci-dessous. + +** Prétraitement de l'énoncé + +Dans le dossier de l'évaluation, mettre les fichiers suivants de l'évaluation : + +`enonce.pdf`, `enonce.tex`, `correction.tex`. + + - `python gemini_for_enonce.py Interro` or + `python gemini_for_enonce.py Interro --restart` + + À partir des trois fichiers précédents, se charge de détecter les + labels des questions et leur contenu. + + Les questions vont également être regroupées. Par la suite, quand + des requêtes de corrections seront effectuées sur une question, + seulement les énoncés des questions du groupe seront envoyés (et le + corrigé de la question). Il faut donc que chaque groupe contienne + si possible le contexte nécessaire pour comprendre la question. + + Une fenêtre s'ouvre pour permettre d'éditer le résultat. Ne pas + hésiter à faire des groupes plus gros que les groupes par défaut. + + Après relecture le script génère : + + + un fichier `labels` avec les labels des questions + + Un dossier `Text` avec le contenu textuel des questions, + regroupées. + + Un dossier `Sol` avec le contenu textuel du corrigé, question par + question. + + Un dossier `Text2`, qui compile un fichier `.tex` pour chaque + question (utilisé pour compiler un rendu pdf du corrigé pour + chaque question) + + Un dossier `Sol2`, qui compile un fichier `.tex` pour chaque + correction de chaque question. + + Un dossier `Persp` avec des instruction de barème pour chaque + question. + + Éventuellement : vérifier et modifier les barèmes dans `Persp`. + + - Alternative personnelle : `python enonce_info.py Interro` + + Ces deux commandes suivent la convention des scripts standardisés. + Leur import ne lance aucun traitement et les erreurs partielles sont + distinguées des échecs. Les réponses d'extraction mises en cache par + =gemini_for_enonce.py= et le fichier =labels= sont publiés + atomiquement. + +** Prétraitement des copies + +Mettre les copies scannées au format pdf dans =Interro=. + + 1. =python copies_tools.py rotate Interro= (facultatif) + + Retourne tous les pdf de 180°, si la photocopie a été faite à + l'envers. + 2. =python copies_tools.py rename Interro= + change le nom des copies en =Copie{id}.pdf= + 3. =python page_splitter.py Interro= + + Découpe des copies A3 en pages A4, en retirant les pages vides. + + Pour chaque page double il est possible + + de garder les deux pages + + de ne garder qu'une des deux pages. + + de jeter les deux pages + + de déplacer la délimitation à droite/gauche + + Fix issues with =python page_splitter.py Interro14/Copies/Copie01.pdf= + + Le PDF transformé est construit dans un dossier temporaire. La + copie produite et la sauvegarde dans =Copies Originales= sont + ensuite installées avec rollback : une erreur conserve les deux + versions précédentes. Une relance ciblée lit directement la + sauvegarde originale sans la déplacer au préalable. + 4. =python cutleft.py Interro= + + Découpe la partie gauche des copies, là où il devrait y avoir les + labels des exercices/questions. + + =python cutleft.py Interro --fullpage= to use the full page always. + + Rerun on a single file with =python cutleft.py Interro/Copies/Copie01.pdf= + + Les images et le fichier =_schema.json= d'une copie sont remplacés + ensemble. Fermer l'outil juste après la dernière validation ne peut + donc plus interrompre un thread de sauvegarde en arrière-plan. + +** Labelisation et regroupement + +Optional : Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~ + + 1. =python gemini_for_labels.py Interro=, avec éventuellement =--overwrite= + + Fait des requêtes à Gemini pour identifier les labels des + questions dans images générées à partir des parties gauches des copies. + + Une copie PDF ou une image précise de =Cutleft= peut également être + ciblée. Plusieurs cibles de la même évaluation sont acceptées. Les + parties d'une copie restent traitées séquentiellement afin de + conserver les labels précédents comme contexte, tandis que les + copies différentes sont traitées en parallèle. Chaque réponse JSON + validée est écrite atomiquement. Une cible sans image correspondante + produit le code de sortie 4. + 2. =python plotting.py Interro= + + Permet de vérifier visuellement les labels trouvés. + + Sous linux, on peut faire =e= pour ouvrir le fichier .json et + l'éditer a la main. + + Quand un label est manquant, il est possible de cliquer sur + l'image, ce qui copie les coordonnées dans le presse papier + (sous linux…), puis on peut l'ajouter à la main. + + Utilisation de `_`, `|…` et `…|` : + + `|…` n'est pas arrêté verticalement par son type opposé. + + `…|` est stoppé horizontalement par le `|…` le plus proche. + Pour modifier une seule copie : + =python plotting.py Interro/Copies/Copie01.pdf= + + Les coordonnées agrégées sont écrites atomiquement dans le JSON de + la copie. Fermer la fenêtre avant la fin d'une copie ne remplace pas + son JSON par un résultat incomplet. + + It also generates les =Copie01.json=, à partir des =Copie01_01.json= + En cas de soucis, (par exemple les pages ne sont pas dans le bon ordre) + - Réordonner les pages du fichier pdf + - Rerun =python cutleft.py Interro/Copie{id}= + - Rerun =python gemini_for_labels.py Interro/Copie{id}= + 3. =python splitting_int.py Interro= + + Découpe les copies suivant les exercices + Peut-être appelé avec une seule copie. + + Les réponses d'une copie sont préparées dans un dossier temporaire, + puis remplacent ensemble le dossier précédent. En cas d'erreur, + l'ancienne version est conservée. Les réponses devenues obsolètes + restent archivées dans le sous-dossier =Missing=. + 4. =python grouping.py Interro= + + Regroupe les mêmes questions de différentes copies en groupes de + tailles raisonnables. + 5. Facultatif : =python verify_groups.py Interro= + + Vérifie que chaque réponse PDF apparaît bien dans les métadonnées + des groupes. La commande renvoie un code non nul si une réponse est + absente ou si la vérification est incomplète. + +** Correction et annotation + +Optional : Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~ + + 1. Il faut créer des persp, pour indication de comment corriger, et + relancer =enonce_info.py= + 2. =python correction.py Interro --limit 240= OU + =python correction.py Interro/Par\ label/Ex\ 2/Group_1.jpg= OU + =python correction.py Interro --overwrite= + + Fais les requêtes de correction à Gemini. + + =correction.py= peut être relancé sans supprimer son état. Les + fichiers =correction.json= et =correction_progress.json= sont mis à + jour atomiquement. Avec =--overwrite=, leur version précédente reste + en place jusqu'à la première écriture réussie de la nouvelle + exécution. =--reset= est la seule option qui supprime explicitement + cet état et restaure les fichiers =*_old.pdf=. + + L'argument =limit= limite le nombre de requêtes à Gemini Pro + (chères), pour une version low cost, passer =--limit 0=, toutes + les requêtes seront sur Gemini Flash. + + Pour diminuer le coût, il est possible de batch les requêtes, qui + seront alors traitées sous au plus 24h. + + =python correction.py Interro --batch= + + OU =python correction.py Interro --batch-from 'Ex 4'= + + =python submit_batches.py Interro= + + =python batch_status.py= + + =python fetch_batched_results.py Interro= + + =python correction.py Interro --deal-with-batched= + + Les quatre commandes de ce flux suivent la convention des scripts + standardisés. Les fichiers de requêtes et le résultat JSONL combiné + sont publiés atomiquement : une interruption ne laisse pas de fichier + final partiellement écrit. =submit_batches.py= conserve aussi les + identifiants distants dans =batch_jobs.json= ; la récupération les + utilise en priorité et garde la recherche par nom pour les anciens + batchs. =batch_status.py --download JOB --output + resultat.jsonl= permet aussi de télécharger atomiquement le résultat + d'un job particulier. + 3. =python post-correction.py Interro= + + - Essaye de corriger des erreurs d'encodage/d'accents dans + =correction.json=. + - aussi échappe les `_` en dehors du mode math, pour LaTeX. + 4. Résolution manuel de conflits, s'il y en a. + + Edit `manual_resolutions.txt`. Use : + + `->` or `x>` : Here set a pipe `|` before or after the new_label name + + `-x` : replace the goal + + `ss` : do nothing + + `sx` : stay, and remove goal. + + `xx` : move to goal. + + `xs` : remove old, keep goal. + + Then call `python resolve_manual.py Interro` + 5. Call `python correction.py Interro --refaire`. + + +** Génération des copies annotées + + 1. =python annotating.py Interro= (facultatif) + + Ajoute les annotations Gemini aux copies, enregistrées dans le dossier =Anot=. + On peut passer l'argument =--overwrite=. +OU + 2. =python annotating_with_checks.py Interro= + + Ajoute les annotations Gemini, et des checkboxes à cocher. + Enregistrées dans le dossier =Bnot=, + =--overwrite= + + Une seule copie peut être ciblée avec, par exemple, + =python annotating_with_checks.py Interro/Copies/Copie01.pdf=. + Le mode =--refaire= exige un fichier =refaire.json= et écrit dans + =BRnot=. +OU + 2. =python annotating_by_label.py Interro= dans =BGnot= + + Ajoute les annotations Gemini, et des checkboxes, et regroupe les + réponses par question. + Enregistrées dans =BGnot= + + _Needs_ : label_groups file (made automatically by this function), + qui dit quelles questions regrouper. + + 3. =python export.py Interro BGnot= (gestion perso) + Cela déplace les groupes dans le dossier configuré par =EXPORT_DIR= + (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 +tablette graphique. + +** Lecture de la correction manuscrite + +_Before_ : vider le dossier configuré par =IMPORT_DIR= (par défaut +=Import=), puis y copier ou synchroniser les fichiers depuis la tablette. + + 1. =python import.py Interro BGnot= + + Une fois les corrections manuelles appliquées aux fichiers + =Concat.pdf=, il faut enregistrer le fichier annoté au même endroit, + 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= + + Lit les =Concat_annotated= dans =Bnot=, regénère les copies avec + les modifications. Les fichiers générés (=score.json=, + =Concat.jpg=, etc.) sont préparés séparément puis installés + ensemble. Les entrées du dossier =Bnot= restent en place et une + erreur de génération conserve les anciennes sorties. +OU + 2. =python reading_grouped_annotations.py Interro= + + Idem, mais pour =BGnot=. Les tâches parallèles remontent leurs + erreurs au processus principal au lieu de les ignorer. + + 3. =python giving_names.py Interro BGnot= + + Crée un dossier =A Rendre= avec des liens symboliques vers + + La copie à rendre + + un fichier =score.json= qui contient les notes par question + + Si un nom est =Unknown= : renommer à la main le dossier et le fichier dedans. + 4. Éventuellement, faire des modifications manuelles aux =score.json=. + + Puis + - `python reading_annotations.py --update-score Interro` + - `python reading_grouped_annotations.py --update-score Interro` + pour mettre à jour les scores dans les images. + 4. (gestion perso) + + =gestion_classe ne= pour créer l'interro puis + + =gestion_classe we= (set barème here) + + =python update_ods.py Interro= + ou =python update_ods.py Interro --sum= (en l'absence de barème) + + =gestion_classe re= + + =gestion_classe wsent= + + =python add_final_score.py Interro21= + (this makes files in =Server/copies=) + 5. (gestion perso) + + Deploy =miqmacs-copies-assets=, and + + update the copies from =miqmacs.fr/admin=. + 6. (gestion perso) Impression d'une copie. Via Evince » print to pdf. + +* Autres +** Recorrection d'une seule copie (peu testé) + +!! Attention, refaire ne marchera pas si tu fais une annotation non +groupée into refaire !! + + 1. Redécoupage + + =python plotting.py InterroTest/Copie01.pdf= + + =python splitting_int.py InterroTest/Copie20.pdf= + 2. Créer =refaire.json=, avec un contenu comme + : [["Copie02", []], + : ["Copie01", ["Ex 1 : 1)"]]] + 3. Appeler =correction= avec --refaire. Il doit créer des groupes + individuels, faire des requêtes, et remplacer les corrections + précédentes (à sauver ailleurs). + + Ou non, si tu veux le faire à la main. + 4. ?? Si je fais refaire, avant d'avoir créer les annotating with + checks, que se passe-t-il ??? + 5. Appeler =annotating_with_checks.py --refaire --overwrite= + 6. =python export.py --refaire Interro24= + 6. =python import.py --refaire Interro24= + 7. =python reading_grouped_annotations.py --refaire Interro24= + + Avec =--refaire=, =refaire.json= et le dossier =BRnot= sont des + prérequis obligatoires ; leur absence produit le code de sortie 3. + +** Exemple de replotting, refaire d'une copie + + 1. replot it. + 2. `python splitting_int.py DS09VA/Copies/Copie25.pdf` + this will get rid of old/new. + !! Attention, et si ça dégage un new : bad bad bad. + 3. Make `refaire.json`, avec la copie, et les labels à refaire. + 4. `python correction.py DS09VA --refaire` + 5. `python annotating_with_checks.py DS09VA --refaire` + 6. `python import.py Interro24 --refaire` +