2026-03-27 13:18:07 +01:00
2026-04-28 22:17:11 +02:00
2026-05-08 08:35:50 +02:00
2026-05-08 08:35:50 +02:00

Anwesenheit PDF Generator

Liest Anwesenheitsdaten aus einer Excel-Datei (.xlsx) und erzeugt für jeden Teilnehmer eine ausgefüllte PDF-Datei (SAB-Formular).


Voraussetzungen

  • Python 3.10 oder höher
  • pip

Installation

# Abhängigkeiten installieren
pip install -r requirements.txt

Für Entwicklung inkl. Tests:

pip install -r requirements-dev.txt

Konfiguration

Vor dem ersten Start müssen zwei Werte in config.json eingetragen werden:

Feld Beschreibung
antragsnummer SAB-Antragsnummer der Einrichtung
ort Ort für die Unterschriftszeile im PDF

Alle anderen Felder sind vorbelegt und müssen nur bei abweichender Excel-Struktur angepasst werden.

Erweiterte PDF-Optionen

Diese Schalter steuern das interne Verhalten beim PDF-Befüllen und müssen in der Regel nicht verändert werden.

pdf_use_display_values (Standard: true)

Steuert, welche Werte in Dropdown- und Auswahlfelder geschrieben werden.

Wert Verhalten
true Lesbare Anzeigewerte (z. B. "X = anwesend", "mind. 6 h", "8,60") werden ins PDF geschrieben. Der PDF-Viewer zeigt den Text direkt an.
false Rohe Export-Werte (z. B. "X", "6", "1") werden verwendet — so wie es das JavaScript im Originalformular intern macht. Sinnvoll, wenn ein externer Viewer (z. B. Adobe Acrobat) die Werte selbst aufzulösen soll.

pdf_need_appearances (Standard: false)

Steuert das NeedAppearances-Flag in der erzeugten PDF-Datei.

Wert Verhalten
false Der PDF-Renderer nutzt die im PDF gespeicherten Appearance-Streams (AP) direkt. Buttons, Footer und Summenfelder werden korrekt angezeigt. Empfohlene Einstellung.
true Der PDF-Viewer regeneriert alle Darstellungen aus den Felddefinitionen (/DA). Kann helfen, wenn ein externer Viewer einzelne Felder leer anzeigt — kann aber dazu führen, dass Buttons und Footer ausgeblendet werden.

Hinweis: Die Summenfelder erhalten beim Generieren eigene Appearance-Streams (Zwei-Pass-Schreibstrategie), sodass pdf_need_appearances: false für alle Felder funktioniert.

regression (Objekt)

Einstellungen für den automatisierten Regressions-Bildvergleich (pytest). Alle Unterfelder befinden sich in diesem Objekt.

"regression": {
    "enabled": false,
    "mask_footer": true
}
regression.enabled (Standard: false)

Aktiviert oder deaktiviert den Bildvergleich.

Wert Verhalten
false Regression-Tests werden übersprungen (pytest.skip). Standard.
true Bildvergleich wird ausgeführt. Erfordert Referenz-PDFs in tests/fixtures/.

Steuert, ob der untere Bereich (Footer) beim Pixel-Vergleich ignoriert wird.

Wert Verhalten
true Die untere ~20% jeder PDF-Seite wird ignoriert. Sinnvoll, da Template-PDFs je nach Vorlage unterschiedlich viele Footer-Zeilen (2 oder 3) haben können.
false Vollständiger Pixel-by-Pixel-Vergleich inkl. Footer-Bereich.

Verzeichnisstruktur

generate.py                  ← Einstiegspunkt
config.json                  ← Konfiguration
input/                       ← Excel-Dateien ablegen
template/
  sab.pdf                    ← PDF-Vorlage (muss vorhanden sein)
output/                      ← Generierte PDFs (werden hier abgelegt)

Verwendung

Generierung starten

python generate.py generate --file "input/Anwesenheitsliste 2026.xlsx" --month Feb

Das Programm zeigt vor der Generierung eine Vorschau und fragt zur Bestätigung:

=== Vorschau der ermittelten Werte ===
Excel-Datei   : Anwesenheitsliste 2026.xlsx
Jahr          : 2026
Monat         : Feb (02)
Antragsnummer : 12345
Ort           : Leipzig
Erstelldatum  : 27.03.2026
Teilnehmer    : 49
Fehler        : 0

Möchten Sie die PDF-Generierung starten? [j/N]

Nur bei Eingabe von j werden PDFs geschrieben.

Nur Validierung (keine PDFs erzeugen)

python generate.py validate --file "input/Anwesenheitsliste 2026.xlsx" --month Feb

Führt alle Prüfungen durch und zeigt die Vorschau — ohne Dateien zu schreiben.

PDF als Bild rendern

python generate.py render-image --pdf "output/(1) Mustermann, Max.pdf" --page 1 --out "preview.png"

Dieser Befehl rendert eine PDF-Seite als PNG-Bild. Er nutzt dieselbe Render-Funktion wie der Regressionstest, damit CLI-Vorschau und Testvergleich technisch identisch arbeiten.

Alle Seiten auf einmal rendern:

python generate.py render-image --pdf "output/(1) Mustermann, Max.pdf" --all-pages --out "render_out"

Optionen

generate

Option Pflicht Beschreibung
--file ja Pfad zur Excel-Datei
--month ja Name des Monats-Sheets (z. B. Jan, Feb, Mrz, Apr …)
--out nein Ausgabeverzeichnis (Standard: output/ aus config.json)

validate

Option Pflicht Beschreibung
--file ja Pfad zur Excel-Datei
--month ja Name des Monats-Sheets

render-image

Option Pflicht Beschreibung
--pdf ja Pfad zur PDF-Datei
--out nein Zieldatei (Einzelseite) oder Zielordner (--all-pages)
--dpi nein Render-Auflösung in DPI (Standard: 150)
--page nein 1-basierte Seitennummer (Standard: 1)
--all-pages nein Rendert alle Seiten statt nur --page

Sheet-Namen

Die deutschen Monatsabkürzungen entsprechen den Tabellenblatt-Namen in der Excel-Datei:

Sheet Monat
Jan Januar
Feb Februar
Mrz März
Apr April
Mai Mai
Jun Juni
Jul Juli
Aug August
Sep September
Okt Oktober
Nov November
Dez Dezember

Ausgabe

Für jeden Teilnehmer wird eine PDF-Datei erstellt:

output/
  (1) Mustermann, Max.pdf
  (2) Beispiel, Anna.pdf
  ...

Die laufende Nummer entspricht der Nummerierung in Spalte A der Excel-Datei.


Tests ausführen

Alle Tests starten

pytest tests/ -v

Nur den Regression-Test. Der Test erwartet, dass im Ordner 2026_Feb die Excel-Datei und in reference/ die Ground-Truth-PDFs liegen. Er generiert PDFs in ein Temp-Verzeichnis und vergleicht sie pixelweise. Bei Abweichungen werden Diff-Bilder nach diff geschrieben.

python -m pytest tests/test_regression.py -v

Validierungsprüfungen

Das Programm prüft vor jeder Generierung:

  1. config.json vorhanden und vollständig
  2. Excel-Datei existiert und ist eine .xlsx/.xlsm-Datei
  3. Jahr aus Dateiname extrahierbar (z. B. Anwesenheitsliste 2026.xlsx)
  4. Sheet-Name bekannt (in month_mapping der config.json)
  5. Sheet existiert in der Arbeitsmappe
  6. Spalte A (lfd. Nummer) enthält Daten
  7. Headerzeile mit Name, Vorname gefunden
  8. Tagesspalten ab Spaltenindex 9 vorhanden
  9. PDF-Template unter template/sab.pdf erreichbar

Windows-Installer erstellen

Der Build-Prozess erzeugt neben dem Programm auch einen Windows-Installer (.exe), der vom SMB-Netzlaufwerk gestartet werden kann.

Voraussetzung: Inno Setup 6

Inno Setup 6 muss auf der Build-Maschine installiert sein. Installation über winget:

winget install --id JRSoftware.InnoSetup -e -s winget -i

Alternativ manueller Download: https://jrsoftware.org/isinfo.php

Installer bauen

build.bat

build.bat führt alle vier Schritte automatisch aus:

  1. Abhängigkeiten installieren (pip install)
  2. Version aktualisieren (update_version.py)
  3. Programm packen (PyInstaller → dist\AnwesenheitPDF\)
  4. Installer kompilieren (ISCC.exe → dist\AnwesenheitPDF-Setup-x.y.z.exe)

Ist Inno Setup nicht installiert, wird Schritt 4 mit einer Warnung übersprungen — der PyInstaller-Output in dist\AnwesenheitPDF\ ist trotzdem nutzbar.

Installer verteilen

Die fertige Datei dist\AnwesenheitPDF-Setup-x.y.z.exe auf das SMB-Netzlaufwerk kopieren. Von dort kann sie direkt gestartet werden — ohne vorherige lokale Kopie.

Was der Installer macht

Aktion Details
Installationspfad %ProgramFiles%\AnwesenheitPDF\ (Admin erforderlich)
Startmenü-Eintrag AnwesenheitPDF
Desktop-Verknüpfung Optional (Checkbox im Wizard, Standard: deaktiviert)
Uninstaller Registriert unter Programme und Features
Windows-Version Windows 10 oder höher
S
Description
No description provided
Readme
890 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 93.8%
Batchfile 4.1%
Inno Setup 2.1%