Initial support for _F output file

master
Sébastien Miquel 2026-04-25 09:17:48 +02:00
parent 95e944f983
commit d7a8e03d2c
4 changed files with 106 additions and 15 deletions

View File

@ -2,6 +2,7 @@ import argparse
import math import math
import sys import sys
import os import os
import shutil
from pathlib import Path from pathlib import Path
import pandas as pd import pandas as pd
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
@ -96,6 +97,13 @@ def process_images(base_dir):
except Exception as e: except Exception as e:
print(f"Error processing image for '{student_name}': {e}") print(f"Error processing image for '{student_name}': {e}")
for pdf_path in sorted(search_path.glob("*/*.pdf")):
student_name = pdf_path.stem # Filename without extension
save_path = OUTPUT_DIR / f"{student_name}.pdf"
shutil.copy(str(pdf_path), str(save_path))
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Stamp scores on exam copies.") parser = argparse.ArgumentParser(description="Stamp scores on exam copies.")
parser.add_argument("dir", type=Path, help="Root directory containing 'A Rendre' folder") parser.add_argument("dir", type=Path, help="Root directory containing 'A Rendre' folder")

View File

@ -8,17 +8,20 @@ import subprocess
import tempfile import tempfile
import shutil import shutil
def compile_to_pdf(text, output_pdf_path): def compile_to_pdf(text, output_pdf_path): # 21 cm + 3.8 (dimension de la marge de gauche)
"""Wraps text in a standalone template and compiles it to PDF.""" """Wraps text in a standalone template and compiles it to PDF."""
latex_template = f"""\\documentclass[varwidth=21cm,margin=0.2cm]{{standalone}} latex_template = f"""\\documentclass[varwidth=24.8cm,margin=0.4cm]{{standalone}}
\\usepackage[utf8]{{inputenc}} \\usepackage[utf8]{{inputenc}}
\\usepackage[T1]{{fontenc}} \\usepackage[T1]{{fontenc}}
\\usepackage{{lmodern}} \\usepackage{{lmodern}}
\\usepackage{{amsmath, amssymb}} \\usepackage{{amsmath, amssymb}}
\\usepackage{{commands}} \\usepackage{{commands}}
\\usepackage{{graphicx}}
\\usepackage{{enumitem}} \\usepackage{{enumitem}}
\\begin{{document}} \\begin{{document}}
\\begin{{minipage}}{{24.8cm}}
{text} {text}
\\end{{minipage}}
\\end{{document}} \\end{{document}}
""" """
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:

View File

@ -73,13 +73,13 @@ def main():
dest_path = os.path.join(target_subdir, dest_folder_name) dest_path = os.path.join(target_subdir, dest_folder_name)
os.makedirs(dest_path, exist_ok=True) os.makedirs(dest_path, exist_ok=True)
links = [("Concat.jpg", f"{safe_name}.jpg"), ("score.json", "score.json")] links = [("Concat.jpg", f"{safe_name}.jpg"),("Concat_F.pdf", f"{safe_name}.pdf"), ("score.json", "score.json")]
for src_name, dst_name in links: for src_name, dst_name in links:
src_file = os.path.join(source_folder, src_name) src_file = os.path.join(source_folder, src_name)
dst_link = os.path.join(dest_path, dst_name) dst_link = os.path.join(dest_path, dst_name)
try: try:
if os.path.lexists(dst_link): os.remove(dst_link) if os.path.lexists(dst_link): os.remove(dst_link)
os.symlink(src_file, dst_link) if os.path.exists(src_file): os.symlink(src_file, dst_link)
except Exception as e: except Exception as e:
print(f"Error linking {src_name} for {dest_folder_name}: {e}") print(f"Error linking {src_name} for {dest_folder_name}: {e}")

View File

@ -4,7 +4,7 @@ import json
import collections import collections
import concurrent.futures import concurrent.futures
from pathlib import Path from pathlib import Path
from PIL import Image from PIL import Image, ImageDraw
import threading import threading
import annotating import annotating
@ -12,6 +12,79 @@ import annotating
from utils import natural_key from utils import natural_key
from reading_annotations import detect_checks_and_notes, has_significant_notes from reading_annotations import detect_checks_and_notes, has_significant_notes
def get_extra_pdfs_as_images(root_dir, label, annotating_module):
"""Fetches Text and Sol pdfs for a given label and converts them to images."""
extra_images = []
for folder in ["Text", "Sol"]:
pdf_path = os.path.join(root_dir, folder, f"{label}.pdf")
if os.path.exists(pdf_path):
img, _, _ = annotating_module.make_base_image(pdf_path)
if img:
extra_images.append(img)
return extra_images
def save_paginated_pdf(image_groups, output_path):
"""Concatenates groups of images vertically, adding specific inner borders."""
if not image_groups:
return
max_w = max(img.width for group in image_groups for img in group)
max_page_h = int(max_w * 1.414 * 1.3)
# Calculate 0.2 cm in pixels at 100 DPI (0.2 / 2.54 inches * 100)
border_px = int((0.2 / 2.54) * 100)
pages = []
current_page_imgs = []
current_h = 0
for group in image_groups:
if not group:
continue
# Process the group to add borders
processed_group = []
for i, img in enumerate(group):
if i in (0, 1):
img = img.copy() # Do not modify the original image object in memory
draw = ImageDraw.Draw(img)
color = "black" if i == 0 else "blue"
# Draw the border inside the image edges
draw.rectangle(
[0, 0, img.width - 1, img.height - 1],
outline=color,
width=border_px
)
processed_group.append(img)
group_h = sum(img.height for img in processed_group)
if current_page_imgs and (current_h + group_h > max_page_h):
page = Image.new("RGB", (max_w, current_h), "white")
y = 0
for c_img in current_page_imgs:
page.paste(c_img, (0, y))
y += c_img.height
pages.append(page)
current_page_imgs = processed_group
current_h = group_h
else:
current_page_imgs.extend(processed_group)
current_h += group_h
if current_page_imgs:
page = Image.new("RGB", (max_w, current_h), "white")
y = 0
for c_img in current_page_imgs:
page.paste(c_img, (0, y))
y += c_img.height
pages.append(page)
if pages:
pages[0].save(output_path, "PDF", resolution=100.0, save_all=True, append_images=pages[1:])
def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, label_notes, all_labels): def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, label_notes, all_labels):
""" """
Modifies data based on actions, pastes label-specific note crops, Modifies data based on actions, pastes label-specific note crops,
@ -136,8 +209,11 @@ def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, la
else: else:
if len(result.get('feedback', [])) != 0: if len(result.get('feedback', [])) != 0:
perfect_no_comment = False perfect_no_comment = False
if not perfect_no_comment: if not perfect_no_comment:
concat_list_F.append(final_img) extras = get_extra_pdfs_as_images(root_dir, label, annotating)
extras.append(final_img)
concat_list_F.append(extras)
# --- 3. Save Final Outputs --- # --- 3. Save Final Outputs ---
with open(score_path, "w") as f: with open(score_path, "w") as f:
@ -158,17 +234,21 @@ def apply_actions_and_regenerate_grouped(root_dir, data, student_id, actions, la
logs.append(f" Saved regenerated Concat.jpg") logs.append(f" Saved regenerated Concat.jpg")
if concat_list_F: if concat_list_F:
max_w = max(i.width for i in concat_list_F) pdf_out_path = os.path.join(output_dir, "Concat_F.pdf")
total_h = sum(i.height for i in concat_list_F) save_paginated_pdf(concat_list_F, pdf_out_path)
full_img = Image.new("RGB", (max_w, total_h), "white") logs.append(f" Saved regenerated Concat_F.pdf")
y = 0 # max_w = max(i.width for i in concat_list_F)
for img in concat_list_F: # total_h = sum(i.height for i in concat_list_F)
full_img.paste(img, (0, y)) # full_img = Image.new("RGB", (max_w, total_h), "white")
y += img.height
full_img.save(os.path.join(output_dir, "Concat_F.jpg")) # y = 0
logs.append(f" Saved regenerated Concat_F.jpg") # for img in concat_list_F:
# full_img.paste(img, (0, y))
# y += img.height
# full_img.save(os.path.join(output_dir, "Concat_F.jpg"))
# logs.append(f" Saved regenerated Concat_F.jpg")
return "\n".join(logs) return "\n".join(logs)