#!/usr/bin/env python3 """ Generate personalized brevet cards from CSV and event config. """ import csv import sys from pathlib import Path try: import yaml except ImportError: print("Error: PyYAML not installed. Run: pip install pyyaml", file=sys.stderr) sys.exit(1) def escape_latex(text): """Escape special LaTeX characters.""" if not text: return "" replacements = { '&': r'\&', '%': r'\%', '$': r'\$', '#': r'\#', '_': r'\_', '{': r'\{', '}': r'\}', '~': r'\textasciitilde{}', '^': r'\textasciicircum{}', '\\': r'\textbackslash{}', } result = str(text) for char, replacement in replacements.items(): result = result.replace(char, replacement) return result def load_event_config(config_file): """Load event configuration from YAML file.""" with open(config_file, 'r', encoding='utf-8') as f: return yaml.safe_load(f) def apply_event_placeholders(text, config): """Replace event-level placeholders in a template string.""" event = config.get('event', {}) replacements = { '{{EVENT_TITLE}}': escape_latex(event.get('title', '')), '{{EVENT_KM}}': escape_latex(event.get('km', '')), '{{EVENT_DATE}}': escape_latex(event.get('date', '')), '{{EVENT_START}}': escape_latex(event.get('start_location', '')), '{{EVENT_CLUB}}': escape_latex(event.get('club', '')), '{{EVENT_CLUB_NR}}': escape_latex(event.get('club_nr', '')), '{{EVENT_STARTZEIT}}': escape_latex(event.get('startzeit', '')), } for placeholder, value in replacements.items(): text = text.replace(placeholder, value) return text def generate_backside(template, config): """Fill back side template with cell content from event config.""" cells = config.get('backside', {}) result = template for row in range(1, 4): for col in range(1, 5): key = f"{row}_{col}" placeholder = f"{{{{CELL_{row}_{col}}}}}" content = cells.get(key, "") if content is None: content = "" result = result.replace(placeholder, content.strip()) return result def generate_card_from_template(template, data): """Replace participant placeholders in template with participant data.""" name = f"{escape_latex(data['Vorname'])} {escape_latex(data['Nachname'])}" street = escape_latex(data['Straße']) plz_ort = f"{escape_latex(data['PLZ'])} {escape_latex(data['Ort'])}" land = escape_latex(data['Land']) medaille = "Ja" if data['Medaille'].lower() == 'ja' else "Nein" startnr = escape_latex(data['Startnr']) card = template.replace('{{NAME}}', name) card = card.replace('{{STREET}}', street) card = card.replace('{{PLZ_ORT}}', plz_ort) card = card.replace('{{LAND}}', land) card = card.replace('{{MEDAILLE}}', medaille) card = card.replace('{{STARTNR}}', startnr) return card def main(): csv_file = Path("Export Brevetkarte.csv") template_file = Path("brevetkarte-template.tex") backside_template_file = Path("brevetkarte-rueckseite-template.tex") event_config_file = Path("event.yml") output_file = Path("brevetkarte-personalized.tex") backside_output_file = Path("brevetkarte-rueckseite.tex") for f in [csv_file, template_file, backside_template_file, event_config_file]: if not f.exists(): print(f"Error: {f} not found!", file=sys.stderr) sys.exit(1) print(f"Reading event config from {event_config_file}...") event_config = load_event_config(event_config_file) print(f"Reading template from {template_file}...") template = template_file.read_text(encoding='utf-8') template = apply_event_placeholders(template, event_config) print(f"Reading participant data from {csv_file}...") participants = [] with open(csv_file, 'r', encoding='utf-8-sig') as f: reader = csv.DictReader(f) for row in reader: if row['Startnr']: participants.append(row) if not participants: print("No participants found in CSV file!", file=sys.stderr) sys.exit(1) print(f"Found {len(participants)} participant(s)") # Generate personalized front side cards = [] for participant in participants: card = generate_card_from_template(template, participant) cards.append(card) document_parts = [] for i, card in enumerate(cards): document_parts.append(card) if i % 2 == 0 and i < len(cards) - 1: document_parts.append("\n\\vspace{0.8cm}\n\n") if i % 2 == 1 and i < len(cards) - 1: document_parts.append("\n\\newpage\n\n") document_parts.append("\n\\end{document}\n") output_file.write_text(''.join(document_parts), encoding='utf-8') print(f"Generated {output_file}") # Generate personalized back side print(f"Reading back side template from {backside_template_file}...") backside_template = backside_template_file.read_text(encoding='utf-8') backside_output = generate_backside(backside_template, event_config) backside_output_file.write_text(backside_output, encoding='utf-8') print(f"Generated {backside_output_file}") print(f"Total pages: {(len(participants) + 1) // 2}") print(f"Compile with: make build-personalized") if __name__ == "__main__": main()