small fixes
This commit is contained in:
+8
-6
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user