diff --git a/Readme.org b/Readme.org index 8d2acc8..50afb01 100644 --- a/Readme.org +++ b/Readme.org @@ -1,7 +1,7 @@ #+title: Script #+author: Sébastien Miquel #+date: 14-03-2026 -# Time-stamp: <09-07-26 15:54> +# Time-stamp: <07-08-26 18:06> #+OPTIONS: * Méta @@ -67,13 +67,21 @@ Puis ajouter =GEMINI_API_KEY= à l'environnement avec : export GEMINI_API_KEY=… #+END_SRC +ou éventuellement, la renseigner directement dans le fichier +`config.py`. + +** Configuration + +Copier `default_config.py` en `config.py`. Éventuellement le modifier. + ** Correction d'un paquet de copies 1. Créer un fichier =names= dans le dossier courant, avec les noms/prénoms des élèves, un par ligne - 2. Créer un dossier correspondant à l'évaluation, comme =Interro= + 2. Créer un dossier correspondant à l'évaluation (=Interro= dans la + suite) 3. Mettre l'énoncé, au format pdf, et l'énoncé et le corrigé au - format .tex dans le dossier. + format .tex dans le dossier. 4. Mettre les fichiers pdfs scannés dans =Interro=. 5. Dans le dossier =Interro= créer un fichier =labels= avec les labels des questions, comme =Ex 1 : 1)a)=, un par ligne. @@ -107,9 +115,10 @@ export GEMINI_API_KEY=… ** Prétraitement des copies - 1. =./rotate_all.sh Interro= - (facultatif) - Retourne toutes les pdf de 180° + 1. =./rotate_all.sh Interro= (facultatif) + + Retourne tous les pdf de 180°, si la photocopie a été faite à + l'envers. 2. =./rename_to_copie.sh Interro= change le nom des copies en =Copie{id}.pdf= 3. =python page_splitter.py Interro= diff --git a/correction.py b/correction.py index c630881..841d77c 100644 --- a/correction.py +++ b/correction.py @@ -78,9 +78,11 @@ if PROXY_URL: os.environ["http_proxy"] = PROXY_URL os.environ["https_proxy"] = PROXY_URL -MODEL_ID_pro = "gemini-3.1-pro-preview" -MODEL_ID_flash = "gemini-3-flash-preview" -api_key = os.environ["GEMINI_API_KEY"] +import config + +MODEL_ID_pro = config.MODEL_PRO_ID +MODEL_ID_flash = config.MODEL_FLASH_ID +api_key = config.API_KEY # --- Thread-safe Logging --- log_lock = threading.Lock() diff --git a/default_config.py b/default_config.py new file mode 100644 index 0000000..5cd72d2 --- /dev/null +++ b/default_config.py @@ -0,0 +1,41 @@ +import os + +API_KEY = os.environ.get("GEMINI_API_KEY") + +# Modèle pour des choses très légères +MODEL_LITE_ID = "gemini-3.5-flash-lite" + +# Modèle pour des choses normales +MODEL_FLASH_ID = "gemini-3.6-flash" + +# Modèle pour des choses dures +MODEL_PRO_ID = "gemini-3.6-flash" +# MODEL_PRO_ID = "gemini-3.1-pro-preview" + +PAGE_SPLITTER_KB = { + "move_left": "", + "move_right": "", + "confirm_next": "", + "rotate_page": "r", + "rotate_all_pages": "R", + "rotate_all_files": ",", + "keep_left": "q", + "keep_right": "d", + "keep_as_is": "f", + "next_page": "s", + "discard_page": "z", + "send_end": "a", # Send this page to the end + "restart_file": "T", + "arranger": "A", # Call `pdf arranger` software, if available + "prev_file": "P", +} + + +PLOTTING_KB = { + "OK": "", + "previous": "p", + "edit": "e", + "open pdf": "o", + "open original pdf": "O", + "open eval": "i", +} diff --git a/gemini_for_enonce.py b/gemini_for_enonce.py index 0554b6d..5aaf019 100644 --- a/gemini_for_enonce.py +++ b/gemini_for_enonce.py @@ -19,9 +19,10 @@ def get_lcp(s1: str, s2: str) -> str: return s1[:i] -# MODEL_ID = "gemini-3-flash-preview" -MODEL_ID = "gemini-3.1-flash-lite" -api_key = os.environ.get("GEMINI_API_KEY") +import config + +MODEL_ID = config.MODEL_LITE_ID +api_key = config.API_KEY # --- Modèles pour la Requête 1 --- class QuestionOnlyItem(BaseModel): diff --git a/gemini_for_labels.py b/gemini_for_labels.py index ca1adf8..a27761c 100644 --- a/gemini_for_labels.py +++ b/gemini_for_labels.py @@ -12,9 +12,10 @@ import argparse import re from collections import defaultdict from concurrent.futures import ThreadPoolExecutor +import config -MODEL_ID = "gemini-3-flash-preview" -api_key = os.environ["GEMINI_API_KEY"] +MODEL_ID = config.MODEL_FLASH_ID +api_key = config.API_KEY my_prompt = """I'm giving you an image of the left columns of a written exam. Students answer several exercises, which can have several questions. diff --git a/page_splitter.py b/page_splitter.py index 63c9084..39571de 100644 --- a/page_splitter.py +++ b/page_splitter.py @@ -10,6 +10,9 @@ import shutil import subprocess from pypdf import PdfReader, PdfWriter +from config import PAGE_SPLITTER_KB + + # --- Constants --- # Conversion factor: 1 cm to points (1 inch = 2.54 cm, 72 points = 1 inch) CM_TO_POINTS = (1 / 2.54) * 72 @@ -98,18 +101,21 @@ class PDFPreviewer: # Set a reasonable initial size for the window self.master.geometry("800x1000") + def fmt(action): + k = PAGE_SPLITTER_KB.get(action, "") + return k + + # Dynamic instructions text instructions = ( - "← / → : Move line 1cm left/right\n" - "'c': Rotate page 180°, 'C' : rotate all pages, ',' : rotate all files\n" - "t s r n m: keep left, next page, keep none, keep right, keep as is\n" - "z: send this page to the end, 'A':pdf arranger 'R':restart file, 'P':back to previous file\n" + f"{fmt('move_left')} / {fmt('move_right')} : Move line 1cm left/right\n" + f"'{fmt('rotate_page')}': Rotate page 180°, '{fmt('rotate_all_pages')}' : rotate all pages, '{fmt('rotate_all_files')}' : rotate all files\n" + f"{fmt('keep_left')} {fmt('next_page')} {fmt('discard_page')} {fmt('keep_right')} {fmt('keep_as_is')}: keep left, next page, keep none, keep right, keep as is\n" + f"{fmt('send_end')}: send page to end, '{fmt('arranger')}': pdf arranger, '{fmt('restart_file')}': restart file, '{fmt('prev_file')}': previous file\n" ) + self.info_label = tk.Label(master, text=instructions, justify=tk.LEFT) self.info_label.pack(pady=5, side=tk.TOP) - # self.restart_btn = tk.Button(master, text="Restart File (R)", command=self.restart_current_file) - # self.restart_btn.pack(pady=2, side=tk.TOP) - self.page_label = tk.Label(master, text="", font=("Helvetica", 12)) self.page_label.pack(pady=5, side=tk.TOP) @@ -118,21 +124,43 @@ class PDFPreviewer: self.canvas.pack(fill="both", expand=True) # --- Bindings --- - self.master.bind("", self.move_line_left) - self.master.bind("", self.move_line_right) - self.master.bind("", self.confirm_and_next_page) - self.master.bind("c", self.rotate_page) - self.master.bind("C", self.rotate_all_pages) - self.master.bind(",", self.rotate_all_files) - self.master.bind("t", self.keep_left) - self.master.bind("n", self.keep_right) - self.master.bind("m", self.keep_as_is) - self.master.bind("s", self.confirm_and_next_page) - self.master.bind("r", self.discard_page) - self.master.bind("z", self.send_page_end) - self.master.bind("R", self.restart_current_file) - self.master.bind("A", self.start_arranger) - self.master.bind("P", self.go_to_previous_file) + action_map = { + "move_left": self.move_line_left, + "move_right": self.move_line_right, + "confirm_next": self.confirm_and_next_page, + "rotate_page": self.rotate_page, + "rotate_all_pages": self.rotate_all_pages, + "rotate_all_files": self.rotate_all_files, + "keep_left": self.keep_left, + "keep_right": self.keep_right, + "keep_as_is": self.keep_as_is, + "next_page": self.confirm_and_next_page, + "discard_page": self.discard_page, + "send_end": self.send_page_end, + "restart_file": self.restart_current_file, + "arranger": self.start_arranger, + "prev_file": self.go_to_previous_file, + } + + for action, key in PAGE_SPLITTER_KB.items(): + if action in action_map: + self.master.bind(key, action_map[action]) + + # self.master.bind("", self.move_line_left) + # self.master.bind("", self.move_line_right) + # self.master.bind("", self.confirm_and_next_page) + # self.master.bind("c", self.rotate_page) + # self.master.bind("C", self.rotate_all_pages) + # self.master.bind(",", self.rotate_all_files) + # self.master.bind("t", self.keep_left) + # self.master.bind("n", self.keep_right) + # self.master.bind("m", self.keep_as_is) + # self.master.bind("s", self.confirm_and_next_page) + # self.master.bind("r", self.discard_page) + # self.master.bind("z", self.send_page_end) + # self.master.bind("R", self.restart_current_file) + # self.master.bind("A", self.start_arranger) + # self.master.bind("P", self.go_to_previous_file) # Bind the resize event on the canvas diff --git a/plotting.py b/plotting.py index 9f5e9b9..72a9933 100644 --- a/plotting.py +++ b/plotting.py @@ -193,14 +193,15 @@ class ImageViewer: self.forward_stack = [] self.current_pil_image = None + from config import PLOTTING_KB # Bindings - self.root.bind('', self.on_enter) - self.root.bind('p', self.on_previous) - self.root.bind('e', self.on_edit) - self.root.bind('o', self.on_open_pdf) - self.root.bind('i', self.on_open_interro) - self.root.bind('O', self.on_open_ori_pdf) + self.root.bind(PLOTTING_KB["OK"], self.on_enter) + self.root.bind(PLOTTING_KB["previous"], self.on_previous) + self.root.bind(PLOTTING_KB["edit"], self.on_edit) + self.root.bind(PLOTTING_KB["open pdf"], self.on_open_pdf) + self.root.bind(PLOTTING_KB["open originial pdf"], self.on_open_ori_pdf) + self.root.bind(PLOTTING_KB["open eval"], self.on_open_interro) self.root.bind('', lambda e: self.root.quit()) self.label.bind('', self.on_click) @@ -279,7 +280,7 @@ class ImageViewer: if metadata.get("error"): msg = f"Error generating boxes for {json_path.name}:\n\n{metadata['error']}\n\nPlease press 'e' to fix the JSON file, then press Enter to retry." - messagebox.showerror("Processing Error", msg) + messagebox.showerror("Processing Error", msg) def on_enter(self, event): if self.is_viewing: