184 lines
9.7 KiB
Markdown
184 lines
9.7 KiB
Markdown
# 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 |
|
||
| `CHANGELOG.md` | Changelog – wird bei jedem Version-Build erweitert |
|
||
|
||
## 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
|
||
├── CHANGELOG.md ← Changelog (wird bei jedem Version-Build erweitert)
|
||
└── requirements.txt ← streamlit, openpyxl, PyMuPDF
|
||
```
|
||
|
||
## Versionierung & Changelog
|
||
|
||
Immer wenn das Signal zum Build einer neuen Version gegeben wird (z. B. „Build Version X.Y“ oder „neue Version bauen“), wird das **Changelog-Logfile** (`CHANGELOG.md` im Projekt-Root) um die **most recent changes** erweitert. Der Eintrag enthält:
|
||
|
||
- **Versionsnummer** (z. B. `v1.2.0`)
|
||
- **Datum** des Builds
|
||
- **Neue Funktionen** (Features)
|
||
- **Behobene Fehler** (Bugfixes)
|
||
- **Sonstige Änderungen** (z. B. Refactoring, Doku-Updates)
|
||
|
||
Der Changelog wird im [Keep a Changelog](https://keepachangelog.com/)-Format gepflegt und nach Semantic Versioning (`MAJOR.MINOR.PATCH`) versioniert.
|
||
|
||
> **Wichtig**: Der Changelog-Eintrag wird **vor** dem eigentlichen Build-Vorgang erstellt, sodass die neue Version dokumentiert ist, bevor der Code ausgeliefert wird.
|
||
|
||
---
|
||
|
||
## 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
|
||
``` |