small fixes

This commit is contained in:
2026-08-08 16:47:09 +02:00
parent f345f8bb4f
commit c535febbc4
6 changed files with 54 additions and 47 deletions
+6 -4
View File
@@ -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: <08-08-26 13:18> # Time-stamp: <08-08-26 16:44>
#+OPTIONS: #+OPTIONS:
* Méta * Méta
@@ -149,6 +149,8 @@ Mettre les copies scannées au format pdf dans =Interro=.
Découpe la partie gauche des copies, là où il devrait y avoir les Découpe la partie gauche des copies, là où il devrait y avoir les
labels des exercices/questions. 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= Rerun on a single file with =python cutleft.py Interro/Copies/Copie01.pdf=
** Labelisation et regroupement ** Labelisation et regroupement
@@ -174,11 +176,11 @@ Set proxy with ~export HTTPS_PROXY="http://10.0.0.1:3128"~
=python plotting.py Interro/Copies/Copie01.pdf= =python plotting.py Interro/Copies/Copie01.pdf=
It also generates les =Copie01.json=, à partir des =Copie01_01.json= It also generates les =Copie01.json=, à partir des =Copie01_01.json=
1. En cas de soucis, (par exemple les pages ne sont pas dans le bon ordre) En cas de soucis, (par exemple les pages ne sont pas dans le bon ordre)
- Réordonner les pages du fichier pdf - Réordonner les pages du fichier pdf
- Rerun =python cutleft.py Interro/Copie{id}= - Rerun =python cutleft.py Interro/Copie{id}=
- Rerun =python gemini_dir_batching.py Interro/Copie{id}= ?? À - Rerun =python gemini_dir_batching.py Interro/Copie{id}=
vérifier, pas sûr que ça marche. ?? À vérifier, pas sûr que ça marche.
3. =python splitting_int.py Interro= 3. =python splitting_int.py Interro=
Découpe les copies suivant les exercices Découpe les copies suivant les exercices
+2
View File
@@ -1,6 +1,7 @@
import sys import sys
import os import os
import json import json
import utils
import shutil import shutil
import argparse import argparse
import concurrent.futures import concurrent.futures
@@ -124,6 +125,7 @@ def main():
with open(label_groups, 'w') as f: with open(label_groups, 'w') as f:
for items in groups.values(): for items in groups.values():
f.write(",".join(items) + "\n") f.write(",".join(items) + "\n")
utils.edit_file_and_enter(label_groups)
with open(label_groups, "r") as f: with open(label_groups, "r") as f:
lines = [line.strip() for line in f if line.strip()] lines = [line.strip() for line in f if line.strip()]
+18 -23
View File
@@ -3,6 +3,7 @@ from functools import lru_cache
import os import os
import time import time
import json # Added for schema output import json # Added for schema output
import argparse
import tkinter as tk import tkinter as tk
from threading import Thread from threading import Thread
from queue import Queue, Empty from queue import Queue, Empty
@@ -14,10 +15,13 @@ DELIMITER_WIDTH = 5
DELIMITER_COLOR = (0, 0, 0) DELIMITER_COLOR = (0, 0, 0)
OUTPUT_SIZE = (1800, 1000) OUTPUT_SIZE = (1800, 1000)
if len(sys.argv) < 2: parser = argparse.ArgumentParser(description="PDF Cropper")
sys.exit("Usage: python script.py <directory_path_or_file_path>") parser.add_argument("path", help="Directory path or PDF file path")
parser.add_argument("--fullpage", action="store_true", help="Process all files in full page mode (1 page per output file)")
args = parser.parse_args()
path_arg = sys.argv[1] path_arg = args.path
fullpage_mode = args.fullpage
files = [] files = []
INPUT_DIR = "" INPUT_DIR = ""
COPIES_DIR = "" COPIES_DIR = ""
@@ -121,7 +125,6 @@ def process_single_pdf(filename, shift_offset=0, max_per_file=5):
Converts PDF to stitched images. Converts PDF to stitched images.
Returns a tuple: (preview_image_resized, list_of_split_images, schema_dict) Returns a tuple: (preview_image_resized, list_of_split_images, schema_dict)
""" """
# pdf_path = os.path.join(INPUT_DIR, filename)
try: try:
pages = get_pdf_pages(filename) pages = get_pdf_pages(filename)
cropped_images = [] cropped_images = []
@@ -164,7 +167,6 @@ def process_single_pdf(filename, shift_offset=0, max_per_file=5):
# 3. Generate Preview (All stitched together, Resized) # 3. Generate Preview (All stitched together, Resized)
full_stitch = stitch_images(cropped_images) full_stitch = stitch_images(cropped_images)
# preview_resized = full_stitch.resize(OUTPUT_SIZE, Image.LANCZOS)
preview_resized = full_stitch.resize(OUTPUT_SIZE, Image.BILINEAR) preview_resized = full_stitch.resize(OUTPUT_SIZE, Image.BILINEAR)
schema = { schema = {
@@ -190,21 +192,15 @@ def save_results(result_tuple, filename):
# --- Cleanup: Delete existing files for this PDF --- # --- Cleanup: Delete existing files for this PDF ---
for f in os.listdir(OUTPUT_DIR): for f in os.listdir(OUTPUT_DIR):
file_path = os.path.join(OUTPUT_DIR, f) file_path = os.path.join(OUTPUT_DIR, f)
# 1. Delete schema file
if f == f"{base_name}_schema.json": if f == f"{base_name}_schema.json":
os.remove(file_path) os.remove(file_path)
# 2. Delete image files (pattern: basename_01.jpg, etc.)
elif f.startswith(f"{base_name}_") and f.endswith(".jpg"): elif f.startswith(f"{base_name}_") and f.endswith(".jpg"):
# Check if the suffix is strictly numeric (e.g. "01") to avoid
# deleting unrelated files like "file_v2_01.jpg" when processing "file.pdf"
suffix = f[len(base_name)+1:-4] suffix = f[len(base_name)+1:-4]
if suffix.isdigit(): if suffix.isdigit():
os.remove(file_path) os.remove(file_path)
# ---------------------------------------------------
# Save Images # Save Images
for i, img in enumerate(splits): for i, img in enumerate(splits):
# Suffix _01, _02, etc.
suffix = f"_{i+1:02d}" suffix = f"_{i+1:02d}"
output_filename = f"{base_name}{suffix}.jpg" output_filename = f"{base_name}{suffix}.jpg"
output_path = os.path.join(OUTPUT_DIR, output_filename) output_path = os.path.join(OUTPUT_DIR, output_filename)
@@ -217,14 +213,17 @@ def save_results(result_tuple, filename):
with open(json_path, 'w') as f: with open(json_path, 'w') as f:
json.dump(schema, f, indent=4) json.dump(schema, f, indent=4)
print(f"Saved schema: {json_filename}") print(f"Saved schema: {json_filename}")
# --- GUI Application --- # --- GUI Application ---
class ImageReviewer: class ImageReviewer:
def __init__(self, file_list): def __init__(self, file_list, default_max_per_file=5):
self.files = file_list self.files = file_list
self.index = 0 self.index = 0
self.current_shift = 0 self.current_shift = 0
self.current_max_per_file = 5 self.default_max_per_file = default_max_per_file
self.current_max_per_file = default_max_per_file
self.current_preview = None # Only stores the resized preview for GUI self.current_preview = None # Only stores the resized preview for GUI
self.is_processing = False self.is_processing = False
@@ -247,8 +246,7 @@ class ImageReviewer:
self.root.bind('n', lambda e: self.on_shift(50)) self.root.bind('n', lambda e: self.on_shift(50))
self.root.bind('N', lambda e: self.on_shift(100)) self.root.bind('N', lambda e: self.on_shift(100))
self.root.bind('t', lambda e: self.on_shift(-50)) self.root.bind('t', lambda e: self.on_shift(-50))
self.root.bind('1', lambda e: self.on_set_max_pages(1)) # New Binding self.root.bind('1', lambda e: self.on_set_max_pages(1))
# Start background pre-fetcher # Start background pre-fetcher
self.bg_thread = Thread(target=self.prefetch_worker, daemon=True) self.bg_thread = Thread(target=self.prefetch_worker, daemon=True)
@@ -266,7 +264,6 @@ class ImageReviewer:
return return
self.current_max_per_file = count self.current_max_per_file = count
print(f"Setting max pages per file: {count}") print(f"Setting max pages per file: {count}")
# Trigger reprocessing with current settings
self.trigger_processing(self.files[self.index], self.current_shift) self.trigger_processing(self.files[self.index], self.current_shift)
def prefetch_worker(self): def prefetch_worker(self):
@@ -276,7 +273,7 @@ class ImageReviewer:
target = self.index + 1 target = self.index + 1
if target < len(self.files) and target != idx_to_process: if target < len(self.files) and target != idx_to_process:
fname = self.files[target] fname = self.files[target]
get_pdf_pages(fname) # Just calling it warms the lru_cache get_pdf_pages(fname)
idx_to_process = target idx_to_process = target
time.sleep(0.05) time.sleep(0.05)
@@ -290,7 +287,6 @@ class ImageReviewer:
self.is_processing = False self.is_processing = False
self.current_shift = 0 self.current_shift = 0
# Always trigger processing. If prefetched, get_pdf_pages returns instantly.
self.trigger_processing(filename, self.current_shift) self.trigger_processing(filename, self.current_shift)
def trigger_processing(self, filename, shift): def trigger_processing(self, filename, shift):
@@ -317,7 +313,6 @@ class ImageReviewer:
self.load_current_image(use_prefetch=True) self.load_current_image(use_prefetch=True)
self.is_processing = False self.is_processing = False
except Empty: except Empty:
# Check again in 100ms
self.root.after(100, lambda: self.check_manual_queue(filename)) self.root.after(100, lambda: self.check_manual_queue(filename))
def handle_processing_result(self, result, filename): def handle_processing_result(self, result, filename):
@@ -325,7 +320,6 @@ class ImageReviewer:
preview, splits, schema = result preview, splits, schema = result
self.current_preview = preview self.current_preview = preview
# Save in a background thread so the GUI updates instantly
Thread(target=save_results, args=(result, filename), daemon=True).start() Thread(target=save_results, args=(result, filename), daemon=True).start()
self.update_display(filename, schema) self.update_display(filename, schema)
@@ -350,7 +344,7 @@ class ImageReviewer:
def on_shift(self, amount): def on_shift(self, amount):
if self.is_processing: if self.is_processing:
return # Ignore keys while processing return
self.current_shift += amount self.current_shift += amount
print(f"Applying shift: {self.current_shift}") print(f"Applying shift: {self.current_shift}")
self.trigger_processing(self.files[self.index], self.current_shift) self.trigger_processing(self.files[self.index], self.current_shift)
@@ -360,12 +354,13 @@ class ImageReviewer:
return return
self.index += 1 self.index += 1
self.current_shift = 0 self.current_shift = 0
self.current_max_per_file = 5 # Reset to default self.current_max_per_file = self.default_max_per_file
self.load_current_image(use_prefetch=True) self.load_current_image(use_prefetch=True)
# --- Entry Point --- # --- Entry Point ---
if __name__ == "__main__": if __name__ == "__main__":
if not files: if not files:
print("No PDF files found.") print("No PDF files found.")
else: else:
app = ImageReviewer(files) app = ImageReviewer(files, default_max_per_file=1 if fullpage_mode else 5)
+6 -17
View File
@@ -1,6 +1,6 @@
import shlex
import re import re
import os import os
import utils
import subprocess import subprocess
import sys import sys
import argparse import argparse
@@ -148,7 +148,9 @@ def process_exam(folder_path: str, restart: bool = False):
folder = Path(folder_path) folder = Path(folder_path)
cache_dir = folder / "Cache" cache_dir = folder / "Cache"
tmp_dir = folder / "Tmp"
cache_dir.mkdir(exist_ok=True) cache_dir.mkdir(exist_ok=True)
tmp_dir.mkdir(exist_ok=True)
cache_q_file = cache_dir / "gemini_questions.json" cache_q_file = cache_dir / "gemini_questions.json"
cache_s_file = cache_dir / "gemini_solutions.json" cache_s_file = cache_dir / "gemini_solutions.json"
@@ -341,8 +343,8 @@ def process_exam(folder_path: str, restart: bool = False):
# ========================================== # ==========================================
# INITIAL GROUPING COMPUTATION # INITIAL GROUPING COMPUTATION
# ========================================== # ==========================================
items_file = folder / "exam_items.txt" items_file = tmp_dir / "exam_items.txt"
full_items_file = folder / "exam_items_full.txt" full_items_file = tmp_dir / "exam_items_full.txt"
trunc_map = {} trunc_map = {}
# --- INITIAL GROUPING COMPUTATION --- # --- INITIAL GROUPING COMPUTATION ---
@@ -469,20 +471,7 @@ def process_exam(folder_path: str, restart: bool = False):
# --- OPEN EDITOR AND PARSE --- # --- OPEN EDITOR AND PARSE ---
while True: while True:
print("Opening items file for editing...") print("Opening items file for editing...")
editor = os.environ.get("EDITOR") utils.edit_file_and_enter(items_file)
try:
if editor:
subprocess.run(shlex.split(editor) + [str(items_file)])
else:
if sys.platform.startswith("linux"):
subprocess.run(["xdg-open", str(items_file)])
elif sys.platform == "darwin":
subprocess.run(["open", str(items_file)])
else:
os.startfile(str(items_file))
input("Press ENTER here once you have saved and closed the text file...")
except Exception as e:
print(f"Error running editor: {e}")
print(f"Parsing edited items from {items_file.name}...") print(f"Parsing edited items from {items_file.name}...")
with open(items_file, "r", encoding="utf-8") as f: with open(items_file, "r", encoding="utf-8") as f:
+1
View File
@@ -180,6 +180,7 @@ def generate_request(file, labels, names, context_labels, wrong_labels):
parser = argparse.ArgumentParser(description="Process a directory or specific files using Gemini.") parser = argparse.ArgumentParser(description="Process a directory or specific files using Gemini.")
parser.add_argument("input_paths", nargs='+', help="The input directory or specific files") parser.add_argument("input_paths", nargs='+', help="The input directory or specific files")
parser.add_argument("--overwrite", action="store_true", help="Regenerate output even if it exists") parser.add_argument("--overwrite", action="store_true", help="Regenerate output even if it exists")
args = parser.parse_args() args = parser.parse_args()
# input_arg = Path(args.input_path) # input_arg = Path(args.input_path)
+18
View File
@@ -23,6 +23,24 @@ def enonce_total(base_dir):
return "".join(output) return "".join(output)
import os import os
import shlex
def edit_file_and_enter(file):
editor = os.environ.get("EDITOR")
try:
if editor:
subprocess.run(shlex.split(editor) + [str(file)])
else:
if sys.platform.startswith("linux"):
subprocess.run(["xdg-open", str(file)])
elif sys.platform == "darwin":
subprocess.run(["open", str(file)])
else:
os.startfile(str(file))
input("Press ENTER here once you have saved and closed the text file...")
except Exception as e:
print(f"Error running editor: {e}")
def pdf_image_of_enonce(root_dir, label): def pdf_image_of_enonce(root_dir, label):
pdf_path = os.path.join(root_dir, "Text2", f"{label}.pdf") pdf_path = os.path.join(root_dir, "Text2", f"{label}.pdf")