def run_project_pipeline(config: ProjectWorkflowConfig) -> ProjectWorkflowResult:
"""Execute the full project work grading letter generation pipeline.
This function orchestrates the complete workflow for creating a LaTeX
grading letter for a project work (Praxisprojekt). It extracts metadata
from the PDF, determines the appropriate formal address, and generates
a letter template.
Args:
config: Configuration object for the project workflow.
Returns:
ProjectWorkflowResult object containing paths to generated files.
Raises:
FileNotFoundError: If the provided `pdf_path` does not exist.
subprocess.CalledProcessError: If LaTeX compilation fails when `compile_pdf=True`.
Exception: Any errors raised by the LLM API (e.g., authentication issues).
"""
pdf_path = config.pdf_path
output_folder_path = config.output_folder
llm_client = config.llm_client
compile_pdf = config.compile_pdf
signature_file = config.signature_file
mark_result = config.mark
create_feedback_mail = config.create_feedback_mail
if output_folder_path is None:
output_folder = str(Path(pdf_path).parent)
else:
output_folder = str(output_folder_path)
# Create LLMClient if not provided
if llm_client is None:
llm_client = LLMClient()
print(f"Using LLM API: {llm_client.api_choice} with model: {llm_client.llm}")
# Load global config
global_config = load_global_config()
global_first_examiner = global_config.get("first_examiner")
# Extract metadata and text from PDF
print(f"Extracting metadata from {pdf_path}")
pages_text = pdf.extract_text_per_page(str(pdf_path))
metadata = extract_project_metadata(str(pdf_path), llm_client)
student_name = metadata.get("student_name", "Unknown")
student_first_name, student_last_name = split_student_name(student_name)
id_number = metadata.get("id_number", "unknown")
project_title = metadata.get("title", "Unknown")
# Use global examiner if provided, otherwise from metadata
examiner_name = global_first_examiner or metadata.get("first_examiner") or "Unbekannt"
# Robust email generation
ex_christian = metadata.get("first_examiner_christian") or ""
ex_family = metadata.get("first_examiner_family") or ""
examiner_email = f"{ex_christian}.{ex_family}@th-koeln.de"
# Prioritize work_type from config, then from metadata, then default
work_type = config.work_type or metadata.get("work_type", "Praxisprojekt")
# Determine gender from first name
print(f"Determining gender for first name: {student_first_name}")
gender = determine_gender_from_name(student_first_name, llm_client)
print(f"Detected gender: {gender}")
# Check for signature in data/
data_signature = os.path.join("data", "signature.png")
if os.path.exists(data_signature):
signature_file = data_signature
print(f"Using signature found in {data_signature}")
# Create output filename
tex_name = f"bewertung_projekt_{id_number}.tex"
tex_path = os.path.join(output_folder, tex_name)
# Generate LaTeX letter
create_project_grading_letter_tex(
filename=tex_path,
author=f"{student_name}, Matrikelnr. {id_number}",
title=project_title,
examiner=examiner_name,
contact=examiner_email,
gender=gender,
work_type=work_type,
signature_file=signature_file,
grade_mark=mark_result,
)
# Compile to PDF if requested
compiled_pdf_path = ""
if compile_pdf:
compiled_pdf_path = compile_latex_to_pdf(tex_path, output_dir=output_folder)
if compiled_pdf_path:
print(f"✅ PDF compiled: {compiled_pdf_path}")
# Generate email for Prüfungsservice
mymailgen = EmailGenerator()
grading_email_text = mymailgen.generate_final_mark_email(
evaluator_client=llm_client,
first_name=student_first_name,
last_name=student_last_name,
id_number=id_number,
examiner_name=examiner_name,
)
email_path = mymailgen.save_email_to_markdown(
output_folder=output_folder,
student_last_name=student_last_name,
id_number=id_number,
filename_prefix="bewertung_projekt_email",
)
# Generate student feedback email
student_email_path = ""
if create_feedback_mail:
print("\n📝 Generiere Feedback-Zusammenfassung...")
feedback_bullets = generate_feedback_summary(str(pdf_path), llm_client)
student_email_text = mymailgen.generate_student_feedback_email(
gender=gender,
last_name=student_last_name,
mark=mark_result if mark_result else "[NOTE]",
feedback_bulletpoints=feedback_bullets,
examiner_name=examiner_name,
)
student_email_path = mymailgen.save_email_to_markdown(
output_folder=output_folder,
student_last_name=student_last_name,
id_number=id_number,
filename_prefix="feedback_projekt_email",
)
# Create Outlook mail drafts if mark_result is provided
if mark_result is not None:
outlook_gen = OutlookMailGenerator()
# 1. Draft for Prüfungsservice
print("\n📧 Erstelle Outlook-Mail für Prüfungsservice...")
try:
outlook_gen.create_outlook_mail(
student_name=student_name,
email_text=grading_email_text,
attachment_path=compiled_pdf_path if compiled_pdf_path else None,
subject=f"Bewertung {work_type} {gender} {student_first_name} {student_last_name}",
verbose=False,
)
except Exception as e:
print(f"⚠️ Fehler beim Erstellen der Outlook-Mail (Service): {e}")
# 2. Draft for Student (only if Outlook is open)
if create_feedback_mail:
if outlook_gen.is_outlook_open():
print("\n📧 Erstelle Outlook-Mail für Studierenden...")
student_email_addr = metadata.get("student_email")
try:
outlook_gen.create_outlook_mail(
student_name=student_name,
email_text=student_email_text,
attachment_path=None,
subject=f"Feedback zu Ihrem {work_type} - {student_name}",
recipient=student_email_addr if student_email_addr else "",
verbose=False,
)
except Exception as e:
print(f"⚠️ Fehler beim Erstellen der Outlook-Mail (Student): {e}")
else:
print(
"\nℹ️ Outlook ist nicht geöffnet. Student-Feedback-Mail nur als .md gespeichert."
)
# Generate web metadata
print("\n🌐 Erstelle Web-Metadaten...")
try:
semester_name = get_semester()
web_md_path = generate_metadata_file(
output_folder=output_folder,
title=project_title,
author=student_name,
pages_text=pages_text,
llm_client=llm_client,
work_type=work_type,
semester=semester_name,
)
print(f"✅ Web-Metadaten erstellt: {web_md_path}")
except Exception as e:
print(f"⚠️ Fehler beim Erstellen der Web-Metadaten: {e}")
web_md_path = ""
return ProjectWorkflowResult(
tex_path=tex_path,
pdf_path=compiled_pdf_path,
service_email_path=email_path,
student_email_path=student_email_path,
metadata_path=web_md_path,
)