# Copilot Plan: Attendance PDF Generator ## Ziel Kombiniertes Python-CLI-Tool, das Anwesenheitsdaten aus einer Excel-Datei liest und für jeden Teilnehmer eine ausgefüllte PDF-Datei erzeugt. ## Architektur ``` generate.py # Click CLI entry point config.json # Konfiguration (antragsnummer, ort, month_mapping, ...) schema.json # JSON-Schema der Konfiguration (Dokumentation) requirements.txt # openpyxl, pypdf, click requirements-dev.txt # + pytest src/ __init__.py exceptions.py # Custom Exception-Hierarchie models.py # Datenklassen: Person, AttendanceDate, MonthlyAttendance config_loader.py # config.json laden & validieren excel_reader.py # Excel → MonthlyAttendance-Objekte pdf_filler.py # MonthlyAttendance → ausgefüllte PDF validators.py # Pre-Generation Validierung + Konsolenvorschau template/ # PDF-Template (sab.pdf, wird separat bereitgestellt) output/ # Ausgabeverzeichnis für generierte PDFs tests/ __init__.py test_excel_reader.py test_pdf_filler.py test_validators.py ``` ## Datenfluss Excel-Datei → excel_reader → MonthlyAttendance → pdf_filler → PDF-Datei ## Entwurfsentscheidungen - `plan.N` = immer `"6"` (mind. 6h), unabhängig von tatsächlichen Stunden - Sheet-Auswahl: manuell via `--month ` (z.B. `--month Feb`) - Output-Benennung: `(n) Nachname, Vorname.pdf` (n = lfd. Nummer aus Spalte A) - `pauschale_5.N` = Dropdown-Wert `"1"` (= 8,60 €) wenn anwesend (X), sonst `"0"` - PDF-Template liegt in `./template/sab.pdf` - PDF-Felder für Ort und Datum: `unterschrift.ort` / `unterschrift.datum` ## Excel-Struktur - Spalte A (Index 0): lfd. Nummer - Spalte B (Index 1): Name (Format: "Nachname, Vorname") - Spalten J–AN (Index 9–39): Tageswerte (Tag 1–31) - Numerischer Wert → anwesend (X), Wert = Ist-Stunden - "K" → Krank - "KK" → Kindkrank - "F" → unentschuldigtes Fehlen - "E" → entschuldigtes Fehlen - "P" → Praktikum - "U" → Urlaub - "V" → Vorstellungsgespräch - "A" → aus Projekt ausgeschieden - Leer → Zeile überspringen ## PDF-Feld-Mapping (pro Zeile N, max 23 Zeilen) | Excel-Zelle | datum.N | anwesend.N | ist.N | plan.N | pauschale_5.N | |---|---|---|---|---|---| | Numerisch | DD.MM.YYYY | X | Wert (dt. Format) | "6" | "1" | | "K"/"KK"/etc| DD.MM.YYYY | Code | leer | leer | "0" | | Leer | (überspringen) | — | — | — | — | --- ## Windows-Installer (Inno Setup) ### Ziel Installer-EXE erstellen, die: - Von einem Windows SMB-Share aus gestartet werden kann (self-contained .exe) - Das Programm lokal unter `%ProgramFiles%\AnwesenheitPDF\` installiert - Einen Startmenü-Eintrag anlegt (optional: Desktop-Verknüpfung) - Einen Uninstaller registriert (Programme und Features) ### Tool: Inno Setup 6 - Free, open-source, weit verbreitet - Erzeugt eine einzelne selbstextrahierende .exe (ideal für SMB-Share) - Unterstützt German.isl (deutschsprachiger Wizard) - ISCC.exe (Compiler) muss auf Build-Maschine installiert sein - Installation: `winget install --id JRSoftware.InnoSetup -e -s winget -i` ### Dateien - `installer\AnwesenheitPDF.iss` — Inno Setup Script - `build.bat` — um Installer-Schritt erweitert (ruft ISCC auf) - Ausgabe: `dist\AnwesenheitPDF-Setup-x.y.z.exe` ### Ablauf build.bat (neu: Schritt 4) 1. pip install 2. update_version.py (liest Version aus config.json) 3. PyInstaller → dist\AnwesenheitPDF\ 4. Version aus config.json extrahieren → ISCC aufrufen → dist\AnwesenheitPDF-Setup-x.y.z.exe ### Install-Ziel - `{autopf}\AnwesenheitPDF\` (= Program Files, UAC-geschützt) - Requires admin (PrivilegesRequired=admin) - Startmenü: `{group}\AnwesenheitPDF` - Optional Desktop-Verknüpfung (Checkbox im Wizard)