# Ziel Eine Python + Streamlit App für Windows, die aus einer Input-Excel-Datei (ein Sheet pro Monat mit Mitarbeiterdaten) und einem **PDF-Template** pro Mitarbeiter eine **PDF-Datei** generiert (eine Seite pro Monat, befüllt mit dessen Daten). **Ansatz**: openpyxl für Input-Parsing + PyMuPDF (fitz) für PDF-Generierung + Streamlit-UI. > **Hintergrund**: Das ursprüngliche Excel-Template war teilweise schreibgeschützt und konnte nicht mit openpyxl befüllt werden. > Daher wurde das Template als PDF exportiert. Im PDF wurden alle zu befüllenden Textstellen mit einem Platzhalter > im Format `{Feldname}_Field` markiert (z. B. `Antragsnummer_Field`, `Name_Field`). Die App sucht nach diesen > `_Field`-Markern und ersetzt sie durch die echten Daten. # Aufgabe Erstelle eine Windows App in dessen Oberfläche aus folgenden Bestandteilen aufgebaut ist: - mittels einem DropDown-Menü kann eine Datei aus dem inputfolder gewählt werden - mittels einem DropDown-Menü kann ein PDF-Template aus dem Templatefolder gewählt werden - ein Button um den Vorgang zu starten Die Funktion der App besteht darin, das Template-PDF mit Daten aus dem Input-File zu befüllen. Im Inputfile ist für alle Monate eines Jahres eine Liste aller Mitarbeitenden und in den Spalten alle Daten zur Befüllung des Templates. # Aufbau Input Datei - In der Input Datei ist für jeden Monat eines Jahres ein Sheet gespeichert. - In jedem Sheet befindet sich eine Tabelle (felder speichern in Input Schema json) mit Mitarbeitenden. - Für jeden Mitarbeitenden wird ein PDF erzeugt, das für jeden Monat eine Seite enthält, befüllt mit den Daten des Mitarbeiters für diesen Monat. # Aufbau PDF-Template - Das PDF-Template ist eine einseitige PDF-Datei, generiert aus der ursprünglichen XLSX-Vorlage. - Jede zu befüllende Textstelle enthält einen Platzhalter: den Feldnamen gefolgt von `_Field` (z. B. `Antragsnummer_Field`, `Name_Field`, `Stellenanteil in %_Field`, …). - Die App sucht im PDF nach allen `_Field`-Texten, ermittelt deren Position und Schriftgröße, und ersetzt sie durch die tatsächlichen Datenwerte. # README In der README.md ist ein kleine Doku wie das Programm zu benutzen ist und welche Konfigurationsparameter es gibt. --- ## Phasen-Plan ### Phase 1: Projekt-Grundgerüst & Konfiguration 1. **`requirements.txt` befüllen** — `streamlit`, `openpyxl`, `PyMuPDF` 2. **`config/settings.ini` befüllen** — Pfade, Standardwerte - Abschnitte: `[paths]` (input_dir, template_dir, output_dir), `[output]` (filename_pattern → `.pdf`) 3. **`config/input-fields.json` Schema definieren** — Spalten der Input-Tabelle mit ihren exakten Überschriften - JSON-Array mit `column` (Spaltenbuchstabe), `field_name` (exakte Überschrift aus der Excel-Tabelle), `type` (string|number|date), `required` (true|false) 4. **`config/output-fields.json` Mapping definieren** — Welche Input-Felder in das PDF geschrieben werden - JSON-Array mit `source_field` (exakte Spaltenüberschrift aus der Input-Tabelle), `format` (optional, z. B. "dd.mm.yyyy") - Der Platzhalter im PDF ist immer `{source_field}_Field` – kein explizites `target_cell` mehr nötig ### Phase 2: Core-Module (`src/`) 5. **`src/__init__.py` + `src/models.py`** — Datenklassen - `EmployeeRecord`: name, vorname, nachname, antragsnummer, qualifikation, funktion, taetigkeit, stellenanteil, monat, jahr, … (Felder aus input-fields.json) - `AppConfig`: geladene settings.ini + input/output-schemas 6. **`src/config.py`** — Konfigurations-Loader - `load_settings()`: liest `config/settings.ini` mit `configparser` - `load_input_schema()`: liest `config/input-fields.json` - `load_output_mapping()`: liest `config/output-fields.json` - `get_available_input_files()`: listet `.xlsx`-Dateien in `input/` - `get_available_templates()`: listet `.pdf`-Dateien in `template/` 7. **`src/parser.py`** — Input-Datei einlesen (unverändert: openpyxl für XLSX-Input) - `parse_input_file(filepath, schema)`: öffnet Workbook, iteriert über alle Sheets (Monate) - Extrahiert Monat/Jahr aus Sheet-Namen oder Sheet-Inhalt - Für jedes Sheet: liest Zeilen anhand des Schemas, erzeugt `EmployeeRecord`-Liste - Rückgabe: `list[EmployeeRecord]` ### Phase 3: Writer & Validierung (PDF-basiert) 8. **`src/writer.py`** — PDF-Output generieren (NEU: PyMuPDF) - `generate_activity_reports(employee_records, template_path, mapping, config)`: - **Pre-Scan**: Öffnet das Template-PDF einmal und sucht nach allen `_Field`-Platzhaltern, ermittelt deren Position (bounding box) und Schriftgröße - Gruppiert Records nach Mitarbeiter - Für jeden Mitarbeiter: - Erstellt ein neues PDF - Für jeden Monats-Record: fügt eine Kopie der Template-Seite ein - Ersetzt alle `_Field`-Platzhalter durch die tatsächlichen Daten (redact + insert_text) - Speichert als `{nachname}_{vorname}_Tätigkeitsnachweis.pdf` in `output/` 9. **`src/validator.py`** — Eingabevalidierung (angepasst für PDF) - `validate_input_file(filepath, schema)`: unverändert (prüft XLSX-Input) - `validate_template(template_path, mapping)`: prüft ob alle `_Field`-Platzhalter im PDF gefunden werden ### Phase 4: Streamlit UI 10. **`app.py`** — Streamlit-Oberfläche (angepasst für PDF-Templates) - Layout: - Titel + Beschreibung - `st.selectbox` für Input-Datei (aus `input/`-Folder) - **Sofort bei Auswahl**: Validierung + Parsing der Datei - Anzeige: Anzahl gefundener Mitarbeiter, Auflistung aller enthaltenen Antragsnummern - `st.selectbox` für PDF-Template (aus `template/`-Folder) - **Sofort bei Auswahl**: Validierung des PDF-Templates (Prüfung aller `_Field`-Marker) - Anzeige: Anzahl gefundener Platzhalter-Felder - **Mitarbeiter-Liste** (erscheint nach erfolgreicher Input-Validierung) - `st.multiselect` mit allen gefundenen Mitarbeiternamen, standardmäßig alle vorselektiert - `st.button("Tätigkeitsnachweise generieren")` - Bei Button-Click: 1. Filtere Employee Records auf selektierte Mitarbeiter 2. Generiere PDFs nur für selektierte Mitarbeiter und speichere in `output/` 3. Zeige Erfolgsmeldung mit Liste der erstellten PDF-Dateien ### Phase 5: Start-Skript & Doku 11. **`start.bat`** — Windows Batch-Startdatei (unverändert) 12. **`README.md` aktualisieren** — Vollständige Doku --- ## Relevante Dateien (zu erstellen/ändern) | Datei | Aktion | |-------|--------| | `requirements.txt` | `PyMuPDF` hinzufügen | | `config/settings.ini` | filename_pattern auf `.pdf` ändern | | `config/output-fields.json` | `target_cell` entfernen (wird durch `_Field`-Marker ersetzt) | | `src/writer.py` | Komplett neu: PDF-basierte Generierung mit PyMuPDF | | `src/validator.py` | `validate_template` für PDF anpassen | | `src/config.py` | `get_available_templates` auf `.pdf` umstellen | | `app.py` | Template-Validierung für PDF anpassen | | `coldrun.py` | An PDF-Template anpassen | | `tests/test_e2e.py` | Tests für PDF-Output anpassen | | `.copilot/PLAN.md` | Dieser Plan – aktualisiert | | `.copilot/TASKS.md` | Aufgabenliste – aktualisiert | ## Projektstruktur ``` / ├── .copilot/ │ ├── PLAN.md ← diese Datei │ └── TASKS.md ← Aufgabenliste ├── input/ │ └── *.xlsx ← Tabelle mit den Input-daten ├── config/ │ ├── settings.ini ← Einstellungen der App │ ├── input-fields.json ← Schema der Inputdaten-Tabelle │ └── output-fields.json ← Mapping: Input-Feld → PDF-Platzhalter ├── output/ ← Zielordner für generierte PDF-Dateien ├── template/ │ └── *.pdf ← PDF-Template mit _Field-Platzhaltern ├── src/ │ ├── __init__.py │ ├── models.py │ ├── config.py │ ├── parser.py │ ├── writer.py ← PDF-basierte Generierung (PyMuPDF) │ └── validator.py ├── app.py ← Streamlit-App ├── coldrun.py ← CLI Cold-Run ├── start.bat ← Windows batch zum Start der App ├── README.md ← README für Programmbenutzer └── requirements.txt ← streamlit, openpyxl, PyMuPDF ``` ## Verifikation 1. **Input-Datei-Analyse**: `2025 JM.xlsx` mit openpyxl öffnen, Sheet-Namen prüfen, Spaltenüberschriften validieren 2. **PDF-Template-Analyse**: `Tätigkeitsnachweise Stellenförderung.pdf` mit PyMuPDF öffnen, alle `_Field`-Platzhalter suchen → mit `output-fields.json` abgleichen 3. **Unit-Test**: App mit vorhandener Input-Datei und PDF-Template ausführen, prüfen ob Output-PDFs korrekt befüllt sind 4. **Edge Cases**: Leere Input-Zeilen, fehlende Monate, Mitarbeiter ohne Vorname, Sonderzeichen in Namen, Umlaute im PDF ```