Score more right on annotating, and ID at the left.

master
Sébastien Miquel 2026-03-10 16:34:40 +01:00
parent f247a975d8
commit aa780134ed
3 changed files with 34 additions and 7 deletions

View File

@ -301,7 +301,7 @@ def color(score):
def render_score_text(label, score, error, width_px, fontsize=18, def render_score_text(label, score, error, width_px, fontsize=18,
bg_color=(255, 255, 255, 255), bg_color=(255, 255, 255, 255),
with_error=True): with_error=True, id=None):
# 1. Calculate Color Gradient # 1. Calculate Color Gradient
# 2. Build highlight-text String & Properties # 2. Build highlight-text String & Properties
@ -313,6 +313,9 @@ def render_score_text(label, score, error, width_px, fontsize=18,
score_str += f" <{error}>" score_str += f" <{error}>"
hl_props.append({"color": "orange", "fontweight": "bold"}) hl_props.append({"color": "orange", "fontweight": "bold"})
if id:
score_str = f"{id} " + score_str
# 3. Wrap Text # 3. Wrap Text
dpi = 100 dpi = 100
fig_width = width_px / dpi fig_width = width_px / dpi
@ -353,7 +356,9 @@ def compose_label_image(base_img, label, result, hmin,
render_fn=render_latex_text, render_fn=render_latex_text,
draw_callback=None, draw_callback=None,
with_error=True, with_error=True,
with_empty=False): with_empty=False,
more_right=False,
with_id=None):
""" """
Composes the final image with annotations. Composes the final image with annotations.
@ -384,8 +389,13 @@ def compose_label_image(base_img, label, result, hmin,
# 1. Prepare Headers # 1. Prepare Headers
header_elements = [] header_elements = []
img_score = render_score_text(label, score, error, base_img.width // 2, if more_right:
fontsize=18, with_error=with_error) width = base_img.width // 2
else:
width = base_img.width // 2 - 150
img_score = render_score_text(label, score, error, width,
fontsize=18, with_error=with_error,
id=with_error)
header_elements.append({"type": "score", "img": img_score, "data": result}) header_elements.append({"type": "score", "img": img_score, "data": result})
# Global Feedbacks # Global Feedbacks
@ -405,8 +415,12 @@ def compose_label_image(base_img, label, result, hmin,
draw = ImageDraw.Draw(final_img, "RGBA") draw = ImageDraw.Draw(final_img, "RGBA")
for el in header_elements: for el in header_elements:
if el["type"] == "score" and more_right:
final_img.paste(el["img"], (150, current_y))
else:
final_img.paste(el["img"], (0, current_y)) final_img.paste(el["img"], (0, current_y))
if draw_callback: if draw_callback:
# Hook for checkboxes # Hook for checkboxes
draw_callback("header_item", draw, draw_callback("header_item", draw,

View File

@ -27,7 +27,9 @@ def render_item(item):
final_img, header_h = annotating.compose_label_image( final_img, header_h = annotating.compose_label_image(
base_img, label, content['result'], content['coordinates'][0], base_img, label, content['result'], content['coordinates'][0],
render_fn=annotating_with_checks.safe_render_latex, render_fn=annotating_with_checks.safe_render_latex,
draw_callback=cb_renderer.callback draw_callback=cb_renderer.callback,
more_right=True,
with_id=student_id
) )
if final_img is None: if final_img is None:
return None return None

View File

@ -145,6 +145,7 @@ def make_prompt(full_label):
from google import genai from google import genai
from google.genai import types from google.genai import types
import base64 import base64
import shlex
import json import json
from pathlib import Path from pathlib import Path
import os import os
@ -218,6 +219,7 @@ start_time = time.time()
overwrite = args.overwrite overwrite = args.overwrite
limit = args.limit limit = args.limit
completed_tasks = [] completed_tasks = []
errors_summary = []
# --- Lock for thread-safe file writing --- # --- Lock for thread-safe file writing ---
io_lock = threading.Lock() io_lock = threading.Lock()
@ -343,7 +345,10 @@ def process_single_task(task_tuple):
except json.JSONDecodeError: except json.JSONDecodeError:
print(f"Error decoding JSON for {file_path}", file=sys.stderr) print(f"Error decoding JSON for {file_path}", file=sys.stderr)
except Exception as e: except Exception as e:
print(f"Exception processing {file_path}: {e}", file=sys.stderr) error_msg = f"Exception processing {file_path}: {e}"
print(error_msg, file=sys.stderr)
with io_lock:
errors_summary.append((error_msg, file_path))
print(f"Starting processing on {len(tasks_to_process)} tasks with {NB_THREADS} threads...") print(f"Starting processing on {len(tasks_to_process)} tasks with {NB_THREADS} threads...")
@ -353,3 +358,9 @@ with concurrent.futures.ThreadPoolExecutor(max_workers=NB_THREADS) as executor:
end_time = time.time() end_time = time.time()
print("Time elapsed : ", end_time - start_time) print("Time elapsed : ", end_time - start_time)
print("Requests to pro / flash : ", pro_count, flash_count) print("Requests to pro / flash : ", pro_count, flash_count)
if errors_summary:
print("\n--- Summary of Exceptions ---", file=sys.stderr)
for (err, file) in errors_summary:
print(err, file=sys.stderr)
escaped_path = shlex.quote(str(file_path))
print(f"Run : python correction.py {escaped_path}")