Add CSV-based personalized card generation
Add template-based system for generating personalized brevet cards from CSV data. Uses proper separation of concerns with template file and Python script. - Add brevetkarte-template.tex with placeholders - Add generate_cards.py to read CSV and populate template - Update Makefile with generate-personalized and build-personalized targets - Update .gitignore to exclude generated files Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
112
generate_cards.py
Executable file
112
generate_cards.py
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate personalized brevet cards from CSV data.
|
||||
"""
|
||||
import csv
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
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 generate_card_from_template(template, data):
|
||||
"""Replace 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'])
|
||||
|
||||
# Replace placeholders
|
||||
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")
|
||||
output_file = Path("brevetkarte-personalized.tex")
|
||||
|
||||
if not csv_file.exists():
|
||||
print(f"Error: {csv_file} not found!", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not template_file.exists():
|
||||
print(f"Error: {template_file} not found!", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Read template
|
||||
print(f"Reading template from {template_file}...")
|
||||
template = template_file.read_text(encoding='utf-8')
|
||||
|
||||
# Read CSV data
|
||||
print(f"Reading participant data from {csv_file}...")
|
||||
participants = []
|
||||
with open(csv_file, 'r', encoding='utf-8-sig') as f: # utf-8-sig handles BOM
|
||||
reader = csv.DictReader(f)
|
||||
for row in reader:
|
||||
if row['Startnr']: # Skip empty rows
|
||||
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 document with all cards
|
||||
cards = []
|
||||
for i, participant in enumerate(participants):
|
||||
card = generate_card_from_template(template, participant)
|
||||
cards.append(card)
|
||||
|
||||
# Combine cards with spacing/page breaks
|
||||
document_parts = []
|
||||
for i, card in enumerate(cards):
|
||||
document_parts.append(card)
|
||||
|
||||
# Add vertical space between cards on same page
|
||||
if i % 2 == 0 and i < len(cards) - 1:
|
||||
document_parts.append("\n\\vspace{0.8cm}\n\n")
|
||||
|
||||
# Add page break after every 2 cards (except at the end)
|
||||
if i % 2 == 1 and i < len(cards) - 1:
|
||||
document_parts.append("\n\\newpage\n\n")
|
||||
|
||||
# Close document
|
||||
document_parts.append("\n\\end{document}\n")
|
||||
|
||||
# Write output
|
||||
document = ''.join(document_parts)
|
||||
output_file.write_text(document, encoding='utf-8')
|
||||
|
||||
print(f"Generated {output_file}")
|
||||
print(f"Total pages: {(len(participants) + 1) // 2}")
|
||||
print(f"Compile with: make build-personalized")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user