207 lines
8.2 KiB
Markdown
207 lines
8.2 KiB
Markdown
---
|
||
description: "Use when implementing, extending, or reviewing the attendance PDF generator application. Contains the full application masterplan, Excel data model, PDF field mapping rules, and attendance status logic."
|
||
applyTo: "**/*.py"
|
||
---
|
||
|
||
# Application Masterplan: Attendance PDF Generator
|
||
|
||
## Purpose
|
||
|
||
A Python program that reads attendance data from an Excel file and generates personalized PDF reports for each participant. The program must correctly map all daily attendance values from Excel into the corresponding PDF form fields.
|
||
|
||
## Excel File Structure
|
||
|
||
- **One Excel file = one full year** of attendance data.
|
||
- **One sheet = one calendar month.**
|
||
- **Column A**: Sequential participant number (1 to n).
|
||
- **Column B**: Participant name in `Last name, First name` format.
|
||
- **Following columns**: Additional data columns, including **one column per calendar day of the month**.
|
||
|
||
Each row represents one participant. Each day column contains the attendance value for that participant on that specific day.
|
||
|
||
## PDF Output Structure
|
||
|
||
Each PDF is generated per participant, per month. The PDF contains a table with one row per working day.
|
||
|
||
| PDF Field | Description | Source |
|
||
|---|---|---|
|
||
| `person.name` | Full name of the participant | Excel column B |
|
||
| `monat_jahr` | Month and year in `MM/YYYY` format | Derived from sheet name + filename |
|
||
| `antragsnummer` | Application number | `config.json` (hardcoded) |
|
||
| `unterschrift.ort` | Location/city (signature line) | `config.json` (hardcoded) |
|
||
| `unterschrift.datum` | Creation date (`DD.MM.YYYY`) (signature line) | Current system date at generation time |
|
||
| `datum.0` – `datum.22` | Date per row in `DD.MM.YYYY` format (max 23 rows) | Derived from sheet name + year |
|
||
| `anwesend.0` – `anwesend.22` | Attendance status dropdown (see mapping below) | Excel day cell |
|
||
| `ist.0` – `ist.22` | Actual attendance in hours (`Ist-Stand Anwesenheit (in h)`) | Excel day cell (numeric) |
|
||
| `plan.0` – `plan.22` | Planned attendance in hours (`Geplante Anwesenheit lt. Konzeption (in h)`) | Always `"6"` (mind. 6h) when present |
|
||
| `pauschale_5.0` – `pauschale_5.22` | Flat rate Pauschale 8,60 € | Dropdown value `"1"` if present (`X`), else `"0"` |
|
||
|
||
## Header Field Derivation Rules
|
||
|
||
### Year
|
||
- Extracted from the **Excel filename** using a regex pattern (e.g., `Anwesenheit_2026.xlsx` → `2026`).
|
||
- The filename pattern must be configurable in `config.json`.
|
||
|
||
### Month
|
||
- Derived from the **sheet name** in the Excel file.
|
||
- German abbreviated month names are mapped to month numbers:
|
||
|
||
| Sheet Name | Month |
|
||
|---|---|
|
||
| `Jan` | 01 |
|
||
| `Feb` | 02 |
|
||
| `Mär` | 03 |
|
||
| `Apr` | 04 |
|
||
| `Mai` | 05 |
|
||
| `Jun` | 06 |
|
||
| `Jul` | 07 |
|
||
| `Aug` | 08 |
|
||
| `Sep` | 09 |
|
||
| `Okt` | 10 |
|
||
| `Nov` | 11 |
|
||
| `Dez` | 12 |
|
||
|
||
- This mapping is stored in `config.json`.
|
||
|
||
### Application Number (`antragsnummer`)
|
||
- Hardcoded static value stored in `config.json`. Never derived from Excel.
|
||
|
||
### Location (`unterschrift.ort`)
|
||
- Hardcoded static value stored in `config.json`. Never derived from Excel.
|
||
- PDF field name is configurable via `pdf_field_ort` in `config.json`.
|
||
|
||
### Creation Date (`unterschrift.datum`)
|
||
- Always the **current system date** at the time of PDF generation, formatted as `DD.MM.YYYY`.
|
||
- PDF field name is configurable via `pdf_field_erstellungsdatum` in `config.json`.
|
||
|
||
## Excel-to-PDF Field Mapping Rules
|
||
|
||
### Rule 1: Present — Numeric value (hours)
|
||
|
||
**Condition**: The Excel cell for a participant on a given day contains a **numeric value** (integer or decimal, representing hours worked).
|
||
|
||
**PDF actions**:
|
||
- `datum.N` → Set to the date in `DD.MM.YYYY` format.
|
||
- `anwesend.N` → Select `X = anwesend`.
|
||
- `ist.N` → Set to the exact numeric value from Excel (actual hours worked), with comma as decimal separator.
|
||
- `plan.N` → Always set to `"6"` (= `mind. 6h` dropdown option).
|
||
- `pauschale_5.N` → Set to `"1"` (= 8,60 € dropdown option).
|
||
|
||
### Rule 2: Sick — Value `"K"`
|
||
|
||
**Condition**: The Excel cell contains the string `K`.
|
||
|
||
**PDF actions**:
|
||
- `datum.N` → Set to the date in `DD.MM.YYYY` format.
|
||
- `anwesend.N` → Select `K = Krank`.
|
||
- `ist.N` → Leave empty.
|
||
- `plan.N` → Leave empty.
|
||
|
||
### Rule 3: Child Sick — Value `"KK"`
|
||
|
||
**Condition**: The Excel cell contains the string `KK`.
|
||
|
||
**PDF actions**:
|
||
- `datum.N` → Set to the date in `DD.MM.YYYY` format.
|
||
- `anwesend.N` → Select `KK = Kindkrank`.
|
||
- `ist.N` → Leave empty.
|
||
- `plan.N` → Leave empty.
|
||
|
||
### Rule 4: Unexcused Absence — Value `"F"`
|
||
|
||
**Condition**: The Excel cell contains the string `F`.
|
||
|
||
**PDF actions**:
|
||
- `datum.N` → Set to the date in `DD.MM.YYYY` format.
|
||
- `anwesend.N` → Select `F = unentschuldigtes Fehlen`.
|
||
- `ist.N` → Leave empty (no hours entered).
|
||
- `plan.N` → Leave empty (no planned hours entered).
|
||
|
||
### Rule 5: Flat Rate — `pauschale_5.N`
|
||
|
||
**Condition**: Applied for every row where attendance is present (`X`).
|
||
|
||
**PDF actions**:
|
||
- `pauschale_5.N` → Set to `"1"` (= 8,60 € in dropdown) if `anwesend.N` is `X = anwesend`.
|
||
- `pauschale_5.N` → Set to `"0"` for all other statuses (K, KK, F, etc.).
|
||
|
||
### Rule 6: No Entry / Empty Cell
|
||
|
||
**Condition**: The Excel cell for a given day is empty or the day does not exist in the month (e.g., February has fewer than 31 days).
|
||
|
||
**PDF actions**:
|
||
- Skip this row entirely. Do not add a date entry for this day.
|
||
|
||
## Attendance Status Dropdown Values
|
||
|
||
The `anwesend` field is a dropdown. Valid selectable values:
|
||
|
||
| Code | Meaning |
|
||
|---|---|
|
||
| `X` | `X = anwesend` (present) |
|
||
| `K` | `K = Krank` (sick) |
|
||
| `KK` | `KK = Kindkrank` (child sick) |
|
||
| `E` | `E = Entschuldigt` (excused absence) |
|
||
| `F` | `F = unentschuldigtes Fehlen` (unexcused absence) |
|
||
| `P` | `P = Praktikum` (internship) |
|
||
| `U` | `U = Urlaub` (vacation) |
|
||
| `V` | `V = Vorstellungsgespräch` (job interview) |
|
||
| `A` | `A = aus Projekt ausgeschieden` (left project) |
|
||
|
||
## Planned Hours Logic (`plan.N`)
|
||
|
||
When a participant is present (`X`), the planned hours dropdown must be set always to the value `mind. 6h`.
|
||
|
||
## General Implementation Rules
|
||
|
||
- All configuration, thresholds, field mappings, static values (`antragsnummer`, `ort`), and the month-name mapping must be stored in a `config.json` file at the project root.
|
||
- The CLI entry point is `generate.py` using Click.
|
||
- All business logic must be covered by tests in `tests/`. Tests must not depend on specific real participant names or file names.
|
||
|
||
## Pre-Generation Validation & Confirmation Workflow
|
||
|
||
Before any PDF is written, the program must execute a full validation and preview phase:
|
||
|
||
### 1. Validation Checks
|
||
|
||
All of the following must be verified. Errors are **logged** (not silently ignored) and summarized at the end:
|
||
|
||
| Check | Description |
|
||
|---|---|
|
||
| Excel file exists | The given file path resolves to a readable `.xlsx` file |
|
||
| Year extractable from filename | The filename matches the configured pattern and yields a 4-digit year |
|
||
| Sheet name valid | The sheet name exists and maps to a known German month abbreviation |
|
||
| Column A present | Sequential number column exists |
|
||
| Column B present | Name column exists |
|
||
| Day columns present | At least one day column is detected |
|
||
| Attendance values valid | Each non-empty cell contains either a numeric value or a known status code (`K`, `KK`, `F`, `E`, `P`, `U`, `V`, `A`) |
|
||
| `config.json` present and complete | All required keys (`antragsnummer`, `ort`, month mapping, filename pattern) exist |
|
||
| PDF template accessible | The PDF template file exists and is readable |
|
||
|
||
### 2. Console Preview
|
||
|
||
After validation, display a structured summary on the console showing all derived values:
|
||
|
||
```
|
||
=== Vorschau der ermittelten Werte ===
|
||
Excel-Datei : Anwesenheit_2026.xlsx
|
||
Jahr : 2026
|
||
Monat : März (03)
|
||
Antragsnummer : <value from config>
|
||
Ort : <value from config>
|
||
Erstelldatum : 27.03.2026
|
||
Teilnehmer : 12
|
||
Fehler : 0
|
||
|
||
--- Stichproben aus dem Excel-Sheet (erste 10 Tage) ---
|
||
Person 1 2 ...
|
||
Mustermann, Max 6 K ...
|
||
...
|
||
|
||
Möchten Sie die PDF-Generierung starten? [j/N]
|
||
```
|
||
|
||
- If there are validation errors, list each one clearly before the prompt.
|
||
- The process only continues if the user explicitly confirms with `j` (ja).
|
||
- If the user enters anything else or presses Enter, the process is aborted without creating any files.
|