Compare commits

..

2 Commits

7 changed files with 83 additions and 16 deletions

View File

@ -1,7 +1,7 @@
#+title: Script
#+author: Sébastien Miquel
#+date: 14-03-2026
# Time-stamp: <01-05-26 22:35>
# Time-stamp: <07-05-26 11:33>
#+OPTIONS:
* Quézaco
@ -205,7 +205,8 @@ OU
1. =python from_tablette.py Interro= (gestion perso)
_Before_ : delete =~/SyncCopies/Annotées=, copy from the tablette to here.
_Before_ : delete =~/SyncCopies/Annotées=, copy or sync from the
tablette to here.
Une fois les corrections manuelles appliquées aux fichiers
=Concat.pdf=, il faut enregistrer le fichier annoté au même endroit,
@ -242,9 +243,13 @@ OU
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.
* Recorrection d'une seule copie (peu testé)
!! Attention, refaire ne marchera pas si tu fais une annotation non
groupée into refaire !!
@ -252,8 +257,8 @@ groupée into refaire !!
+ =python plotting.py InterroTest/Copie01.pdf=
+ =python splitting_int.py InterroTest/Copie20.pdf=
2. Créer =refaire.json=, avec un contenu comme
[["Copie01", []],
["Copie01", ["Ex 1 : 1)"]]]
: [["Copie01", []],
: ["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).

View File

@ -582,7 +582,7 @@ Here is a list of all possible labels. You need to answer with a list one of the
height = grouping.get_pdf_height(str(new_pdf_path))
grouping.create_jpg(add_label, idx, [(pid, str(new_pdf_path), height)], INPUT_DIR)
new_tasks.append((str(Path(INPUT_DIR) / add_label / f"Group_{idx+1}.jpg"),
add_label, False))
add_label, False, f"{label}(->)"))
error += f"(->){add_label}"
keep_error = True
else:
@ -603,6 +603,7 @@ def process_single_task(task_tuple, precomputed_response=None):
file_path = task_tuple[0]
label = task_tuple[1]
can_spawn_tasks = task_tuple[2] if len(task_tuple) > 2 else True
injected_error = task_tuple[3] if len(task_tuple) > 3 else ""
group_name = os.path.splitext(file_path)[0]
json_path = group_name + '.json'
@ -648,6 +649,15 @@ def process_single_task(task_tuple, precomputed_response=None):
for p in json_data:
pid = p["id"]
res = p["result"]
# Inject additional error if present
if injected_error:
if res["error"]:
res["error"] = f"{injected_error} {res['error']}"
else:
res["error"] = injected_error
yming, ymaxg, width_r = d_data[pid]
pdf_path = Path(INPUT_DIR) / f"Copie{pid}" / f"{label}.pdf"
@ -710,6 +720,8 @@ def process_single_task(task_tuple, precomputed_response=None):
tprint(f"Error decoding JSON for {file_path}", file=sys.stderr)
except Exception as e:
error_msg = f"Exception processing {file_path}: {e}"
import traceback
traceback.print_exc() # <--- Add this line to see the real crash
print(error_msg, file=sys.stderr)
with io_lock:
errors_summary.append((error_msg, file_path))

View File

@ -55,7 +55,10 @@ def compile_to_pdf(text, output_pdf_path): # 21 cm + 3.8 (dimension de la marge
def fetch_and_save_sub_text(ex_id, indices, label, text_path):
"""Fetches text for a specific sub-question and saves it to Text/{label}.tex"""
qinds = ",".join(map(str, indices))
if qinds:
url = f"http://localhost:8080/exercices/exo_q_text/{ex_id}/{qinds}"
else:
url = f"http://localhost:8080/exercices/exo_q_text/{ex_id}"
try:
with urllib.request.urlopen(url) as response:
content = response.read().decode('utf-8')
@ -71,7 +74,10 @@ def fetch_and_save_sub_text(ex_id, indices, label, text_path):
def fetch_and_save_sub_sol(ex_id, indices, label, sol_path):
"""Fetches text for a specific sub-question and saves it to Text/{label}.tex"""
qinds = ",".join(map(str, indices))
if qinds:
url = f"http://localhost:8080/exercices/exo_q_sol/{ex_id}/{qinds}"
else:
url = f"http://localhost:8080/exercices/exo_q_sol/{ex_id}"
try:
with urllib.request.urlopen(url) as response:
content = response.read().decode('utf-8')
@ -168,11 +174,11 @@ def process_directory(directory):
if not tex_files:
print(f"No .tex file found in {directory}. Looking in /Staging/Interro/")
int_name = directory[:-1] if directory.endswith("/") else directory
tex_path = os.path.join(os.path.expanduser("~"), "Prépa/Staging/Interro/", int_name, ".tex")
tex_path = os.path.join(os.path.expanduser("~"), "Prépa/Staging/Interro", f"{int_name}.tex")
if os.path.exists(tex_path):
tex_file = tex_path
else:
print("Not found.")
print("Not found in ", tex_path)
return
else:
tex_file = tex_files[0]

View File

@ -314,7 +314,7 @@ def process_copy_group(group_key, files):
# Run ThreadPool on GROUPS (Copies), not individual files
# Each thread handles one student's full exam copy sequentially
with ThreadPoolExecutor(max_workers=12) as executor:
with ThreadPoolExecutor(max_workers=16) as executor:
# Convert dict items to arguments for map
# executor.map expects a function and an iterable.
# We use a lambda or separate function to unpack the tuple if needed,

View File

@ -4358,6 +4358,8 @@ conjoncture
conjugaison
conjugal
conjuguer
conjugué
conjugués
conjuration
conjuré
conjurer
@ -6097,6 +6099,7 @@ détenus
détérioration
détériorer
déterminant
déterminants
déterminante
détermination
détermine
@ -10279,6 +10282,7 @@ inacceptable
inacceptables
inaccessible
inaccoutumé
inachevé
inactif
inaction
inadmissible

View File

@ -9,7 +9,7 @@ from tkinter import messagebox
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageTk
print("o to open pdf, O original pdf, e to emacs part, i to interro, click for coordinates")
print("o to open pdf, O original pdf, e to emacs part, p to go back, i to interro, click for coordinates")
# --- Configuration & Globals ---
padding = 60
@ -79,6 +79,8 @@ def prepare_image(image_path: str, bounding_boxes, all_labels, nb_pages, last_la
current_index = all_labels.index(label)
if current_index < last_label_index or (last_label_index == -1 and current_index != 0):
color = "red"
elif current_index > last_label_index + 1:
color = "orange"
last_label_index = current_index
draw.rectangle(((abs_x_min, abs_y_min), (abs_x_max, abs_y_max)), outline=color, width=4)
@ -170,8 +172,15 @@ class ImageViewer:
self.active_copie_name = None
self.accumulated_results = None # Dict with "name" and "list"
# To go back
self.history = []
self.forward_stack = []
self.current_pil_image = None
# Bindings
self.root.bind('<Return>', 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)
@ -184,6 +193,10 @@ class ImageViewer:
def poll_queue(self):
if not self.is_viewing:
try:
# pil_image, json_path, metadata = image_queue.get_nowait()
if self.forward_stack:
pil_image, json_path, metadata = self.forward_stack.pop()
else:
pil_image, json_path, metadata = image_queue.get_nowait()
# Handle End of Stream
@ -199,6 +212,7 @@ class ImageViewer:
# Start new batch
self.active_copie_name = metadata["copie"]
self.accumulated_results = {"name": metadata["name"], "list": []}
self.history.clear()
self.display_image(pil_image, json_path, metadata)
except queue.Empty:
@ -214,7 +228,24 @@ class ImageViewer:
json.dump(self.accumulated_results, f)
self.accumulated_results = None
def on_previous(self, event):
if self.is_viewing and self.history:
print("Going back to previous image...")
prev_pil, prev_json, prev_meta, num_added = self.history.pop()
# Undo the accumulation to prevent duplicates when we hit Enter again
if self.accumulated_results and num_added > 0:
self.accumulated_results["list"] = self.accumulated_results["list"][:-num_added]
# Push current image to the forward stack so we don't lose it
self.forward_stack.append((self.current_pil_image,
self.current_json_path, self.current_meta))
# Display the previous image immediately
self.display_image(prev_pil, prev_json, prev_meta)
def display_image(self, pil_image, json_path, metadata):
self.current_pil_image = pil_image # ADD THIS LINE
self.orig_size = pil_image.size
self.scale_factor = 1.0
screen_h = self.root.winfo_screenheight() - 100
@ -233,6 +264,7 @@ class ImageViewer:
def on_enter(self, event):
if self.is_viewing:
print(f"Committing data for {self.current_json_path.name}...")
num_added = 0 # ADD THIS LINE
try:
with open(self.current_json_path, 'r') as f:
@ -245,6 +277,8 @@ class ImageViewer:
self.current_meta["schema"]
)
num_added = len(converted_items)
# Add to accumulator
if self.accumulated_results:
self.accumulated_results["list"].extend(converted_items)
@ -259,6 +293,9 @@ class ImageViewer:
messagebox.showerror("JSON Error", msg)
return # Abort advancement
self.history.append((self.current_pil_image, self.current_json_path,
self.current_meta, num_added))
# Advance UI
self.is_viewing = False
self.label.config(image="", text="Loading next...")
@ -270,13 +307,13 @@ class ImageViewer:
print(f"Opening {pdf_path}")
subprocess.Popen(['xdg-open', str(pdf_path.absolute())])
def on_open_ori_pdf(self, event):
def on_open_interro(self, event):
if self.is_viewing and self.current_json_path:
pdf_path = "/home/sebastien/Staging/Interro/" + str(base_dir) + "pdf"
pdf_path = "/home/sebastien/Prépa/Staging/Interro/" + str(base_dir) + ".pdf"
print(f"Opening {pdf_path}")
subprocess.Popen(['xdg-open', pdf_path])
def on_open_interro(self, event):
def on_open_ori_pdf(self, event):
if self.is_viewing and self.current_json_path:
new_filename = self.current_json_path.stem.split('_')[0] + ".pdf"
pdf_path = self.current_json_path.parent / "Copies Originales" / new_filename

View File

@ -14,7 +14,10 @@ def enonce_total(base_dir):
if not text_dir.is_dir():
return ""
files = [f for f in text_dir.iterdir() if f.is_file()]
# Exclude .tex and .pdf files
files = [f for f in text_dir.iterdir()
if f.is_file() and f.suffix.lower() not in ('.tex', '.pdf')]
files.sort(key=lambda f: natural_key(f.name))
output = []