diff --git a/README.md b/README.md index 7c10afb..6c3b62d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,20 @@ streamlit run app.py Oder einfach `start.bat` (Windows) bzw. `./start.sh` (macOS/Linux) ausführen. +## Cold Run – Headless-Generierung + +`coldrun.py` erzeugt ohne GUI einen Tätigkeitsnachweis für die erste Person – +automatisch mit der ersten Input- und Template-Datei. Ergebnis wird auf macOS +im Finder geöffnet. + +```bash +# Kommandozeile +.venv/bin/python coldrun.py + +# oder per Doppelklick (macOS) +coldrun.command +``` + ## Bedienung 1. **Input-Datei wählen** – Dropdown zeigt alle `.xlsx`-Dateien aus dem `input/`-Ordner diff --git a/coldrun.command b/coldrun.command new file mode 100755 index 0000000..e6a0cb7 --- /dev/null +++ b/coldrun.command @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Doppelklickbares Cold-Run-Script für macOS +# Nimmt automatisch die erste Input-Datei + erstes Template und erzeugt +# einen Tätigkeitsnachweis für die erste gelistete Person – ohne GUI. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +VENV_DIR=".venv" + +if [[ ! -x "$VENV_DIR/bin/python" ]]; then + echo "[FEHLER] Virtuelle Umgebung nicht gefunden." + echo "Bitte zuerst start.command ausführen, um das Projekt einzurichten." + read -p "Drücke Enter zum Beenden..." + exit 1 +fi + +echo "[INFO] Stelle sicher, dass Abhängigkeiten installiert sind…" +"$VENV_DIR/bin/pip" install -r requirements.txt --quiet 2>/dev/null || true + +echo "" +"$VENV_DIR/bin/python" coldrun.py + +echo "" +read -p "Drücke Enter zum Beenden..." diff --git a/coldrun.py b/coldrun.py new file mode 100644 index 0000000..159aef2 --- /dev/null +++ b/coldrun.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Cold-Run Tätigkeitsnachweis-Generator. + +Nimmt automatisch die erste Input-Datei und das erste Template, +erzeugt einen Tätigkeitsnachweis für die erste gelistete Person +und öffnet das Ergebnis (macOS) – ohne GUI, ohne Fragen. +""" + +import subprocess +import sys +from pathlib import Path + +# Projekt-Root +PROJECT_ROOT = Path(__file__).resolve().parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from src.config import ( + load_settings, + load_input_schema, + load_output_mapping, + get_available_input_files, + get_available_templates, +) +from src.parser import parse_input_file, group_by_employee, get_unique_employees +from src.writer import generate_activity_reports + + +def main(): + print("=" * 50) + print(" Tätigkeitsnachweis-Generator (Cold Run)") + print("=" * 50) + print() + + # ── Konfiguration laden ────────────────────────────────────────── + config_dir = PROJECT_ROOT / "config" + config = load_settings(config_dir, PROJECT_ROOT) + config.input_schema = load_input_schema(config_dir) + config.output_mapping = load_output_mapping(config_dir) + + # ── Erste Input-Datei ermitteln ─────────────────────────────────── + input_files = get_available_input_files(config) + if not input_files: + print("[FEHLER] Keine .xlsx-Dateien im input/-Ordner gefunden.") + return 1 + + input_file = input_files[0] + input_path = Path(config.input_dir) / input_file + print(f"[INFO] Input-Datei: {input_file}") + + # ── Erstes Template ermitteln ───────────────────────────────────── + template_files = get_available_templates(config) + if not template_files: + print("[FEHLER] Keine .xlsx-Dateien im template/-Ordner gefunden.") + return 1 + + template_file = template_files[0] + template_path = Path(config.template_dir) / template_file + print(f"[INFO] Template: {template_file}") + + # ── Input parsen ────────────────────────────────────────────────── + print() + print("[INFO] Parse Input-Datei…") + records = parse_input_file( + filepath=input_path, + schema=config.input_schema, + month_sheet_pattern=config.month_sheet_pattern, + skip_empty_name=config.skip_empty_name, + skip_empty_antragsnummer=config.skip_empty_antragsnummer, + ) + + employees = get_unique_employees(records) + print(f"[INFO] {len(records)} Einträge für {len(employees)} Personen gefunden.") + + # ── Erste Person ermitteln ──────────────────────────────────────── + grouped = group_by_employee(records) + first_person = sorted(grouped.keys())[0] + first_records = grouped[first_person] + print(f"[INFO] Erste Person: {first_person} ({len(first_records)} Monate)") + + # ── Reports generieren ──────────────────────────────────────────── + print() + print("[INFO] Generiere Tätigkeitsnachweis…") + single_person = {first_person: first_records} + output_dir = Path(config.output_dir) + generated = generate_activity_reports( + employee_records=single_person, + template_path=template_path, + mapping=config.output_mapping, + output_dir=output_dir, + filename_pattern=config.filename_pattern, + ) + + output_file = generated[0] + print(f"[OK] Erstellt: {output_file.name}") + print(f" Pfad: {output_file}") + + # ── Datei öffnen (macOS) ────────────────────────────────────────── + if sys.platform == "darwin": + print() + print("[INFO] Öffne Datei im Finder…") + subprocess.run(["open", str(output_file)], check=False) + + print() + print("=" * 50) + print(" Fertig.") + print("=" * 50) + return 0 + + +if __name__ == "__main__": + sys.exit(main())