Compare commits
2 Commits
81be54b5f7
...
26d02b0987
| Author | SHA1 | Date |
|---|---|---|
|
|
26d02b0987 | |
|
|
428692f6c8 |
13
Readme.org
13
Readme.org
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
url = f"http://localhost:8080/exercices/exo_q_text/{ex_id}/{qinds}"
|
||||
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))
|
||||
url = f"http://localhost:8080/exercices/exo_q_sol/{ex_id}/{qinds}"
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
47
plotting.py
47
plotting.py
|
|
@ -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,7 +193,11 @@ class ImageViewer:
|
|||
def poll_queue(self):
|
||||
if not self.is_viewing:
|
||||
try:
|
||||
pil_image, json_path, metadata = image_queue.get_nowait()
|
||||
# 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
|
||||
if pil_image is None:
|
||||
|
|
@ -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
|
||||
|
|
|
|||
5
utils.py
5
utils.py
|
|
@ -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 = []
|
||||
|
|
|
|||
Loading…
Reference in New Issue