Initial commit: PDF zu ICS Konverter
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.ics
|
||||
*.pdf
|
||||
.venv/
|
||||
__pycache__/
|
||||
98
QUICKSTART.md
Normal file
98
QUICKSTART.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# 🚀 Quick Start Guide
|
||||
|
||||
## Schnellstart - 3 Schritte
|
||||
|
||||
### 1. Programm starten
|
||||
|
||||
**macOS/Linux:**
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
Doppelklick auf `start.cmd`
|
||||
|
||||
### 2. PDF-Dateien hinzufügen
|
||||
|
||||
Kopieren Sie Ihre Dienstplan-PDF-Dateien in dieses Verzeichnis:
|
||||
```
|
||||
/home/sebastian/Dokumente/ICS-Import/
|
||||
```
|
||||
|
||||
### 3. Konvertieren und Importieren
|
||||
|
||||
Im Menü wählen Sie Option "1. PDF(s) konvertieren" und die ICS-Dateien werden automatisch erstellt.
|
||||
|
||||
---
|
||||
|
||||
## 📅 In deinen Kalender importieren
|
||||
|
||||
### Google Kalender
|
||||
1. Öffne [google.com/calendar](https://google.com/calendar)
|
||||
2. Einstellungen → Kalender importieren
|
||||
3. "Datei aussuchen" → `.ics` Datei auswählen
|
||||
4. Importieren
|
||||
|
||||
### Outlook
|
||||
1. Öffne Outlook
|
||||
2. Datei → Öffnen und exportieren → Importieren
|
||||
3. `.ics` Datei auswählen
|
||||
4. In einen Kalender importieren
|
||||
|
||||
### Thunderbird/SeaMonkey
|
||||
1. Kalender öffnen
|
||||
2. Datei → Importieren
|
||||
3. `.ics` Datei auswählen
|
||||
|
||||
### Apple Kalender (macOS/iOS)
|
||||
1. Doppelklick auf die `.ics` Datei
|
||||
2. Bestätigen Sie den Import
|
||||
|
||||
### Linux (KDE Kontact, etc.)
|
||||
1. Öffnen Sie die Kalenderanwendung
|
||||
2. Datei → Importieren
|
||||
3. `.ics` Datei auswählen
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tipps und Tricks
|
||||
|
||||
### Mehrere PDFs gleichzeitig
|
||||
Das Programm verarbeitet automatisch alle `.pdf` Dateien im Verzeichnis. Kopieren Sie einfach mehrere PDFs hinein!
|
||||
|
||||
### Zeitzone anpassen
|
||||
Falls Sie eine andere Zeitzone benötigen, bearbeiten Sie `pdf_to_ics.py`:
|
||||
```python
|
||||
tz = pytz.timezone('Europe/Berlin') # Ändern Sie diesen Wert
|
||||
```
|
||||
|
||||
Verfügbare Zeitzonen: [Liste hier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
|
||||
|
||||
### Automatische Updates
|
||||
Sobald Sie neue Dienstplan-PDFs hinzufügen und das Programm erneut ausführen, werden neue `.ics` Dateien erstellt.
|
||||
|
||||
---
|
||||
|
||||
## ❓ Häufige Probleme
|
||||
|
||||
### "Keine PDF-Dateien gefunden"
|
||||
- Überprüfen Sie, dass die PDF-Dateien im gleichen Verzeichnis wie die Skripte sind
|
||||
- Dateiname darf Leerzeichen enthalten
|
||||
|
||||
### "Keine Events gefunden"
|
||||
- Die PDF muss das richtige Format haben (ein Dienstplan mit Tabelle)
|
||||
- Kontrollieren Sie die PDF-Struktur
|
||||
|
||||
### Zeitangaben falsch
|
||||
- Die Standard-Einstellung ist Zeitzone "Europe/Berlin"
|
||||
- Falls Sie eine andere Zeitzone benötigen, siehe "Zeitzone anpassen" oben
|
||||
|
||||
---
|
||||
|
||||
## 📞 Weitere Hilfe
|
||||
|
||||
Siehe **README.md** für ausführliche Dokumentation.
|
||||
|
||||
---
|
||||
|
||||
Viel Spaß! 😊
|
||||
110
README.md
Normal file
110
README.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# PDF zu ICS Konverter - Dienstplan Importer
|
||||
|
||||
Dieses Tool extrahiert Kalenderdaten aus Dienstplan-PDFs und konvertiert sie in das iCalendar-Format (ICS), das von den meisten Kalenderanwendungen importiert werden kann.
|
||||
|
||||
## Features
|
||||
|
||||
✅ Extrahiert Dienstplan-Informationen aus PDFs
|
||||
✅ Erkennt Schicht-Zeitangaben (z.B. 04:51-15:46)
|
||||
✅ Handhabt Nachtschichten korrekt (über Mitternacht hinaus)
|
||||
✅ Erstellt Standard-konforme ICS-Dateien
|
||||
✅ Unterstützt mehrere PDFs gleichzeitig
|
||||
✅ Benutzerfreundliche Oberfläche
|
||||
|
||||
## Installation
|
||||
|
||||
Die erforderlichen Dependencies sind bereits installiert. Falls Sie das Projekt neu einrichten:
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install pdfplumber icalendar pypdf2 pytz
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Schnellstart
|
||||
|
||||
1. Kopieren Sie Ihre Dienstplan-PDF-Dateien in dieses Verzeichnis
|
||||
2. Führen Sie das Skript aus:
|
||||
|
||||
```bash
|
||||
python3 pdf_to_ics.py
|
||||
```
|
||||
|
||||
Das Tool findet automatisch alle `.pdf` Dateien und erstellt entsprechende `.ics` Dateien.
|
||||
|
||||
### Erweiterte Nutzung
|
||||
|
||||
Sie können auch direkt mit dem Python-Modul arbeiten:
|
||||
|
||||
```python
|
||||
from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan
|
||||
|
||||
# PDF verarbeiten
|
||||
dienstplan = extract_dienstplan_data('meine_pdf.pdf')
|
||||
|
||||
# ICS erstellen
|
||||
create_ics_from_dienstplan(dienstplan, 'mein_kalender.ics')
|
||||
```
|
||||
|
||||
## Dateiformat
|
||||
|
||||
### ICS-Datei importieren
|
||||
|
||||
Die erstellte `.ics` Datei kann in folgende Kalenderanwendungen importiert werden:
|
||||
|
||||
- **Outlook**: Datei → Öffnen und exportieren → Importieren
|
||||
- **Google Kalender**: Einstellungen → Kalender importieren
|
||||
- **iCal/macOS**: Doppelklick auf die .ics Datei
|
||||
- **Thunderbird**: Kalender → Kalender importieren
|
||||
- **Android**: Mit einer Kalender-App öffnen
|
||||
- **LibreOffice**: Datei → Öffnen
|
||||
|
||||
## Struktur der extrahierten Daten
|
||||
|
||||
Das Tool extrahiert folgende Informationen aus der PDF:
|
||||
|
||||
- **Name und Personalnummer**: Des Mitarbeiters
|
||||
- **Betriebshof**: Standort
|
||||
- **Sollarbeitszeit**: Gewünschte Arbeitszeit pro Monat
|
||||
- **Events**: Einzelne Schichten mit:
|
||||
- Datum
|
||||
- Dienstart (z.B. "36234", "Ruhe", "Dispo")
|
||||
- Zeitangabe (falls vorhanden)
|
||||
|
||||
## Output
|
||||
|
||||
Für jede verarbeitete PDF wird eine entsprechende ICS-Datei erstellt:
|
||||
|
||||
```
|
||||
2026-02-23 DB Köhler00100718_März2026.pdf → 2026-02-23 DB Köhler00100718_März2026.ics
|
||||
```
|
||||
|
||||
Die ICS-Datei enthält ein Event für jeden Arbeitstag mit:
|
||||
- **Titel**: Name - Dienstart
|
||||
- **Beschreibung**: Dienstart und Betriebshof
|
||||
- **Zeit**: Mit aktueller Zeitzone (Europe/Berlin)
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
### Keine Events gefunden?
|
||||
- Stellen Sie sicher, dass die PDF das erwartete Tabellenformat hat
|
||||
- Überprüfen Sie die Dateiname und die PDF-Struktur
|
||||
|
||||
### Zeitzone falsch?
|
||||
- Die aktuelle Einstellung ist Europe/Berlin
|
||||
- Zum Ändern: Bearbeiten Sie die Zeile in `pdf_to_ics.py`:
|
||||
```python
|
||||
tz = pytz.timezone('Europe/Berlin') # Ändern SIe diesen Wert
|
||||
```
|
||||
|
||||
## Technische Details
|
||||
|
||||
- **Abhängigkeiten**: pdfplumber, icalendar, pytz, pypdf2
|
||||
- **Python-Version**: 3.6+
|
||||
- **Format**: iCalendar 2.0 (RFC 5545)
|
||||
|
||||
## Lizenz
|
||||
|
||||
Dieses Tool ist zur privaten Verwendung gedacht.
|
||||
178
ZUSAMMENFASSUNG.md
Normal file
178
ZUSAMMENFASSUNG.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# 📋 Projekt-Zusammenfassung
|
||||
|
||||
## ✅ Projekt abgeschlossen!
|
||||
|
||||
Ihr PDF zu ICS Konverter für Dienstpläne ist vollständig eingerichtet und einsatzbereit.
|
||||
|
||||
---
|
||||
|
||||
## 📁 Projektstruktur
|
||||
|
||||
```
|
||||
ICS-Import/
|
||||
├── 📄 README.md ← ausführliche Dokumentation
|
||||
├── 📄 QUICKSTART.md ← schnelle Anleitung
|
||||
├── 📄 ZUSAMMENFASSUNG.md ← dieses Dokument
|
||||
│
|
||||
├── 🐍 pdf_to_ics.py ← Kern-Konvertierungsskript
|
||||
├── 🎨 menu.py ← interaktives Benutzermenü
|
||||
│
|
||||
├── 🚀 start.sh ← Startskript (macOS/Linux)
|
||||
├── 🚀 start.cmd ← Startskript (Windows)
|
||||
│
|
||||
├── 📦 .venv/ ← Python-Umgebung (automatisch)
|
||||
│
|
||||
└── 📝 PDF-Dateien & ICS-Dateien ← ihre Daten
|
||||
├── 2026-02-23 DB Köhler00100718_März2026.pdf
|
||||
└── 2026-02-23 DB Köhler00100718_März2026.ics ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Was wurde erstellt
|
||||
|
||||
### 1. **PDF-Parser** (`pdf_to_ics.py`)
|
||||
- Extrahiert Dienstplan-Informationen aus PDFs
|
||||
- Erkennt Zeitangaben und Schichtdaten
|
||||
- Erstellt valide iCalendar-Dateien
|
||||
- Unterstützt mehrere PDF-Dateien
|
||||
|
||||
### 2. **Benutzermenü** (`menu.py`)
|
||||
- Interaktive Oberfläche
|
||||
- PDF-Verzeichnis durchsuchen
|
||||
- Mehrere PDFs konvertieren
|
||||
- Fehlerbehandlung
|
||||
|
||||
### 3. **Startskripte**
|
||||
- Linux/macOS: `start.sh`
|
||||
- Windows: `start.cmd`
|
||||
- Automatische Umgebungseinrichtung
|
||||
|
||||
### 4. **Dokumentation**
|
||||
- README.md - ausführliche Anleitung
|
||||
- QUICKSTART.md - schnelle Einstieg
|
||||
- Diese Zusammenfassung
|
||||
|
||||
---
|
||||
|
||||
## 🎛️ Verwendung
|
||||
|
||||
### Von der Kommandozeile:
|
||||
```bash
|
||||
cd /home/sebastian/Dokumente/ICS-Import
|
||||
python3 pdf_to_ics.py # Alle PDFs konvertieren
|
||||
python3 menu.py # Interaktives Menü
|
||||
./start.sh # Schnellstart (Linux/macOS)
|
||||
```
|
||||
|
||||
### Von Windows:
|
||||
```
|
||||
Doppelklick auf start.cmd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📅 Funktionen der erstellten ICS-Dateien
|
||||
|
||||
✅ **Vollständige Ereignisinformationen:**
|
||||
- Titel: Name - Dienstart
|
||||
- Beschreibung: Dienstart & Betriebshof
|
||||
- Datum und Uhrzeit
|
||||
- Zeitzone: Europe/Berlin
|
||||
|
||||
✅ **Intelligente Zeitverarbeitung:**
|
||||
- Nachtschichten (über Mitternacht)
|
||||
- Ganztagesveranstaltungen
|
||||
- Automatische Zeitzone-Anpassung
|
||||
|
||||
✅ **Kalender-kompatibel mit:**
|
||||
- Google Kalender ✓
|
||||
- Outlook ✓
|
||||
- Apple Kalender ✓
|
||||
- Thunderbird ✓
|
||||
- LibreOffice ✓
|
||||
- Linux Kalender-Apps ✓
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technische Details
|
||||
|
||||
- **Sprache:** Python 3.6+
|
||||
- **Abhängigkeiten:** pdfplumber, icalendar, pytz, pypdf2
|
||||
- **Format:** iCalendar 2.0 (RFC 5545)
|
||||
- **Umgebung:** Python virtual environment (.venv)
|
||||
|
||||
### Installierte Pakete:
|
||||
```
|
||||
pdfplumber==0.10.0+ - PDF-Datenextraktion
|
||||
icalendar==5.0.0+ - ICS-Datei-Erstellung
|
||||
pytz - Zeitzonenverwaltung
|
||||
pypdf2 - PDF-Parsing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Nächste Schritte
|
||||
|
||||
### 1. **Ihre erste Konvertierung:**
|
||||
```bash
|
||||
cd /home/sebastian/Dokumente/ICS-Import
|
||||
./start.sh
|
||||
```
|
||||
Wählen Sie Option "1. PDF(s) konvertieren"
|
||||
|
||||
### 2. **ICS in Kalender importieren:**
|
||||
- Siehe README.md für Anleitung pro Kalender-App
|
||||
- Oder siehe QUICKSTART.md für schnelle Übersicht
|
||||
|
||||
### 3. **Weitere PDFs hinzufügen:**
|
||||
- Kopieren Sie PDF-Dateien in das Verzeichnis
|
||||
- Das Programm verarbeitet sie automatisch
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Fehlerbehebung
|
||||
|
||||
| Problem | Lösung |
|
||||
|---------|--------|
|
||||
| "Keine PDF gefunden" | PDFs müssen im Projektverzeichnis sein |
|
||||
| "Keine Events" | PDF muss das richtige Tabellenformat haben |
|
||||
| "Zeitzone falsch" | Bearbeiten Sie `pdf_to_ics.py` Zeile mit `pytz.timezone()` |
|
||||
| Import-Fehler in Kalender | Stelle sicher, dass die `.ics` Datei nicht beschädigt ist |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Beispiel-Output
|
||||
|
||||
```
|
||||
Verarbeite: 2026-02-23 DB Köhler00100718_März2026.pdf
|
||||
Name: Sebastian Köhler
|
||||
Personalnummer: 00100718
|
||||
Betriebshof: NSCH3
|
||||
Anzahl der Events: 31
|
||||
✓ ICS-Datei erstellt: 2026-02-23 DB Köhler00100718_März2026.ics
|
||||
```
|
||||
|
||||
Jedes Event in der ICS-Datei:
|
||||
- **Datum:** Automatisch aus PDF extrahiert
|
||||
- **Zeit:** z.B. 04:51-15:46 Uhr
|
||||
- **Titel:** z.B. "Sebastian Köhler - 36234"
|
||||
- **Beschreibung:** Dienstart & Betriebshof
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Für detaillierte Informationen:
|
||||
- **README.md** - Ausführliche Dokumentation
|
||||
- **QUICKSTART.md** - Schnelle Anleitung zum Import
|
||||
- **Python-Code** - Gut kommentiert und erweiterbar
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Fertig!
|
||||
|
||||
Ihr System ist bereit. Viel Erfolg mit der Dienstplan-Verwaltung! 📅✨
|
||||
|
||||
**Letzte Änderung:** 23. Februar 2026
|
||||
**Status:** ✅ Einsatzbereit
|
||||
201
menu.py
Normal file
201
menu.py
Normal file
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Interaktives Menü für PDF zu ICS Konvertierung
|
||||
Benutzerfreundliche Oberfläche zum Verarbeiten von Dienstplan-PDFs
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from pdf_to_ics import extract_dienstplan_data, create_ics_from_dienstplan
|
||||
|
||||
|
||||
def print_header():
|
||||
"""Zeige Programm-Header"""
|
||||
print("\n" + "="*60)
|
||||
print("PDF zu ICS Konverter - Dienstplan Importer".center(60))
|
||||
print("="*60 + "\n")
|
||||
|
||||
|
||||
def print_menu():
|
||||
"""Zeige Hauptmenü"""
|
||||
print("\nHauptmenü:")
|
||||
print("1. PDF(s) konvertieren")
|
||||
print("2. Verzeichnis durchsuchen")
|
||||
print("3. Über dieses Programm")
|
||||
print("4. Beenden")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
def list_pdf_files():
|
||||
"""Liste alle PDF-Dateien im aktuellen Verzeichnis"""
|
||||
pdf_files = list(Path('.').glob('*.pdf'))
|
||||
return pdf_files
|
||||
|
||||
|
||||
def convert_pdf(pdf_path):
|
||||
"""Konvertiere eine einzelne PDF-Datei"""
|
||||
try:
|
||||
print(f"\n▶ Verarbeite: {pdf_path}")
|
||||
|
||||
# Extrahiere Daten
|
||||
dienstplan = extract_dienstplan_data(str(pdf_path))
|
||||
|
||||
# Zeige Informationen
|
||||
print(f" Name: {dienstplan['vorname']} {dienstplan['name']}")
|
||||
print(f" Personalnummer: {dienstplan['personalnummer']}")
|
||||
print(f" Betriebshof: {dienstplan['betriebshof']}")
|
||||
print(f" Zeitraum: {dienstplan['monat_start']}")
|
||||
print(f" Sollarbeitszeit: {dienstplan['sollarbeitszeit']}")
|
||||
print(f" Events gefunden: {len(dienstplan['events'])}")
|
||||
|
||||
if not dienstplan['events']:
|
||||
print(" ⚠ Warnung: Keine Events gefunden!")
|
||||
return False
|
||||
|
||||
# Erstelle ICS-Datei
|
||||
ics_path = pdf_path.with_suffix('.ics')
|
||||
create_ics_from_dienstplan(dienstplan, str(ics_path))
|
||||
|
||||
print(f" ✓ ICS-Datei erstellt: {ics_path}")
|
||||
print(f" ✓ Erfolg!\n")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Fehler: {e}\n")
|
||||
return False
|
||||
|
||||
|
||||
def convert_multiple_pdfs():
|
||||
"""Konvertiere mehrere PDF-Dateien"""
|
||||
pdf_files = list_pdf_files()
|
||||
|
||||
if not pdf_files:
|
||||
print("\n⚠ Keine PDF-Dateien im aktuellen Verzeichnis gefunden!")
|
||||
return
|
||||
|
||||
print(f"\n✓ {len(pdf_files)} PDF-Datei(en) gefunden:\n")
|
||||
|
||||
for i, pdf in enumerate(pdf_files, 1):
|
||||
print(f"{i}. {pdf}")
|
||||
|
||||
print("\n" + "-" * 40)
|
||||
|
||||
success_count = 0
|
||||
for pdf in pdf_files:
|
||||
if convert_pdf(pdf):
|
||||
success_count += 1
|
||||
|
||||
print(f"\n{'='*40}")
|
||||
print(f"Zusammenfassung: {success_count}/{len(pdf_files)} ICS-Dateien erstellt")
|
||||
print(f"{'='*40}\n")
|
||||
|
||||
|
||||
def find_and_show_pdfs():
|
||||
"""Durchsuche Verzeichnis und zeige PDFs"""
|
||||
current_dir = Path('.')
|
||||
|
||||
print("\n📁 PDF-Dateien in diesem Verzeichnis:")
|
||||
print("-" * 40)
|
||||
|
||||
pdf_files = list(current_dir.glob('*.pdf'))
|
||||
|
||||
if not pdf_files:
|
||||
print("Keine PDF-Dateien gefunden.")
|
||||
return
|
||||
|
||||
for i, pdf in enumerate(pdf_files, 1):
|
||||
size = pdf.stat().st_size
|
||||
size_mb = size / (1024 * 1024)
|
||||
|
||||
# Versuche Größe lesbar zu machen
|
||||
if size_mb > 1:
|
||||
size_str = f"{size_mb:.2f} MB"
|
||||
else:
|
||||
size_kb = size / 1024
|
||||
size_str = f"{size_kb:.2f} KB"
|
||||
|
||||
print(f"{i}. {pdf.name:50} {size_str:>10}")
|
||||
|
||||
print("-" * 40)
|
||||
print(f"\nGesamt: {len(pdf_files)} PDF-Datei(en)\n")
|
||||
|
||||
|
||||
def show_about():
|
||||
"""Zeige Informationen über das Programm"""
|
||||
print("""
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ PDF zu ICS Konverter - Dienstplan Importer ║
|
||||
║ Version 1.0 ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
BESCHREIBUNG:
|
||||
Dieses Programm extrahiert Kalenderdaten aus Dienstplan-
|
||||
PDFs und konvertiert sie in das iCalendar-Format (ICS).
|
||||
|
||||
FEATURES:
|
||||
✓ Automatische Extraktion von Schichtdaten
|
||||
✓ Erkennung von Zeitangaben und Nachtschichten
|
||||
✓ Standard-konforme ICS-Datei-Erstellung
|
||||
✓ Unterstützung für mehrere PDFs
|
||||
|
||||
VERWENDETE LIBRARIES:
|
||||
• pdfplumber - PDF-Verarbeitung
|
||||
• icalendar - ICS-Datei-Erstellung
|
||||
• pytz - Zeitzonenverwaltung
|
||||
|
||||
IMPORT IN KALENDER:
|
||||
Die erstellten ICS-Dateien können in folgende
|
||||
Anwendungen importiert werden:
|
||||
|
||||
✓ Outlook
|
||||
✓ Google Kalender
|
||||
✓ Apple Kalender (macOS/iOS)
|
||||
✓ Thunderbird
|
||||
✓ LibreOffice Kalender
|
||||
✓ und viele andere...
|
||||
|
||||
FÜR MEHR INFORMATIONEN:
|
||||
Siehe README.md für ausführliche Dokumentation
|
||||
|
||||
""")
|
||||
|
||||
|
||||
def main():
|
||||
"""Hauptprogramm"""
|
||||
print_header()
|
||||
|
||||
while True:
|
||||
print_menu()
|
||||
choice = input("Wählen Sie eine Option (1-4): ").strip()
|
||||
|
||||
if choice == '1':
|
||||
convert_multiple_pdfs()
|
||||
input("Drücken Sie Enter zum Fortfahren...")
|
||||
|
||||
elif choice == '2':
|
||||
find_and_show_pdfs()
|
||||
input("Drücken Sie Enter zum Fortfahren...")
|
||||
|
||||
elif choice == '3':
|
||||
show_about()
|
||||
input("Drücken Sie Enter zum Fortfahren...")
|
||||
|
||||
elif choice == '4':
|
||||
print("\nAuf Wiedersehen! 👋\n")
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
print("\n✗ Ungültige Auswahl. Bitte versuchen Sie es erneut.")
|
||||
|
||||
os.system('clear' if os.name == 'posix' else 'cls')
|
||||
print_header()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n✗ Programm von Benutzer unterbrochen.\n")
|
||||
sys.exit(1)
|
||||
275
pdf_to_ics.py
Normal file
275
pdf_to_ics.py
Normal file
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PDF zu ICS Konverter für Dienstpläne
|
||||
Extrahiert Schichtinformationen aus PDFs und erstellt iCalendar-Dateien
|
||||
"""
|
||||
|
||||
import pdfplumber
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from icalendar import Calendar, Event
|
||||
from pathlib import Path
|
||||
import pytz
|
||||
|
||||
|
||||
def extract_dienstplan_data(pdf_path):
|
||||
"""
|
||||
Extrahiert Dienstplan-Daten aus einer PDF-Datei
|
||||
"""
|
||||
dienstplan = {
|
||||
'name': None,
|
||||
'vorname': None,
|
||||
'personalnummer': None,
|
||||
'betriebshof': None,
|
||||
'sollarbeitszeit': None,
|
||||
'monat_start': None,
|
||||
'monat_end': None,
|
||||
'events': []
|
||||
}
|
||||
|
||||
with pdfplumber.open(pdf_path) as pdf:
|
||||
if not pdf.pages:
|
||||
return dienstplan
|
||||
|
||||
page = pdf.pages[0]
|
||||
text = page.extract_text()
|
||||
|
||||
# Extrahiere Metadaten
|
||||
match = re.search(r'Nachname\s+(\S+)\s+Sollarbeitszeit\s+([\d:]+)', text)
|
||||
if match:
|
||||
dienstplan['name'] = match.group(1)
|
||||
dienstplan['sollarbeitszeit'] = match.group(2)
|
||||
|
||||
match = re.search(r'Vorname\s+(\S+)', text)
|
||||
if match:
|
||||
dienstplan['vorname'] = match.group(1)
|
||||
|
||||
match = re.search(r'Personalnummer\s+(\d+)', text)
|
||||
if match:
|
||||
dienstplan['personalnummer'] = match.group(1)
|
||||
|
||||
match = re.search(r'Betriebshof\s+(\S+)', text)
|
||||
if match:
|
||||
dienstplan['betriebshof'] = match.group(1)
|
||||
|
||||
# Extrahiere Datum-Range
|
||||
match = re.search(r'(\d+)\.\s+(\w+)\s+(\d{4})\s+-\s+(\d+)\.\s+(\w+)\s+(\d{4})', text)
|
||||
if match:
|
||||
start_date_str = f"{match.group(1)}.{match.group(2)}.{match.group(3)}"
|
||||
dienstplan['monat_start'] = start_date_str
|
||||
|
||||
# Extrahiere Events aus der Tabelle
|
||||
tables = page.extract_tables()
|
||||
if len(tables) >= 2:
|
||||
events = parse_dienstplan_table(tables[1], dienstplan['monat_start'])
|
||||
dienstplan['events'] = events
|
||||
|
||||
return dienstplan
|
||||
|
||||
|
||||
def parse_dienstplan_table(table, month_start_str):
|
||||
"""
|
||||
Parst die Dienstplan-Tabelle und extrahiert Events
|
||||
"""
|
||||
events = []
|
||||
|
||||
if not month_start_str:
|
||||
return events
|
||||
|
||||
# Parse das Startdatum (z.B. "1.März.2026")
|
||||
date_parts = month_start_str.split('.')
|
||||
if len(date_parts) != 3:
|
||||
return events
|
||||
|
||||
try:
|
||||
day = int(date_parts[0])
|
||||
month_name = date_parts[1]
|
||||
year = int(date_parts[2])
|
||||
except:
|
||||
return events
|
||||
|
||||
# Konvertiere Monatsnamen zu Nummern
|
||||
months = {
|
||||
'Januar': 1, 'Februar': 2, 'März': 3, 'April': 4, 'Mai': 5, 'Juni': 6,
|
||||
'Juli': 7, 'August': 8, 'September': 9, 'Oktober': 10,
|
||||
'November': 11, 'Dezember': 12
|
||||
}
|
||||
|
||||
month = months.get(month_name, 1)
|
||||
|
||||
# Erstelle Basis-Datum
|
||||
base_date = datetime(year, month, day)
|
||||
|
||||
# Überspringe die Header-Zeile (Montag, Dienstag, etc.)
|
||||
for row_idx in range(1, len(table)):
|
||||
row = table[row_idx]
|
||||
|
||||
# Iteriere über die 7 Wochentage
|
||||
for day_idx, cell in enumerate(row):
|
||||
if cell is None:
|
||||
continue
|
||||
|
||||
# Zelle kann mehrere Zeilen enthalten (Tag\nDienst\nZeit)
|
||||
lines = cell.strip().split('\n')
|
||||
|
||||
if not lines or not lines[0]:
|
||||
continue
|
||||
|
||||
# Erste Zeile ist der Tag
|
||||
try:
|
||||
day_num = int(lines[0].strip())
|
||||
except:
|
||||
continue
|
||||
|
||||
# Berechne das Datum
|
||||
event_date = base_date + timedelta(days=day_num - 1)
|
||||
|
||||
# Extrahiere Dienstart und Zeit
|
||||
service_code = ""
|
||||
start_time = None
|
||||
end_time = None
|
||||
|
||||
if len(lines) > 1:
|
||||
# Suche nach Zeitangaben (HH:MM-HH:MM)
|
||||
for line in lines[1:]:
|
||||
time_match = re.match(r'(\d{2}):(\d{2})-(\d{2}):(\d{2})', line.strip())
|
||||
if time_match:
|
||||
start_time = f"{time_match.group(1)}:{time_match.group(2)}"
|
||||
end_time = f"{time_match.group(3)}:{time_match.group(4)}"
|
||||
else:
|
||||
# Das ist der Dienstart
|
||||
if not service_code:
|
||||
service_code = line.strip()
|
||||
|
||||
# Erstelle Event
|
||||
event = {
|
||||
'date': event_date,
|
||||
'service': service_code,
|
||||
'start_time': start_time,
|
||||
'end_time': end_time
|
||||
}
|
||||
|
||||
events.append(event)
|
||||
|
||||
return events
|
||||
|
||||
|
||||
def create_ics_from_dienstplan(dienstplan, output_path=None):
|
||||
"""
|
||||
Erstellt eine ICS-Datei aus den Dienstplan-Daten
|
||||
"""
|
||||
# Erstelle Calendar
|
||||
cal = Calendar()
|
||||
cal.add('prodid', '-//Dienstplan Importer//de')
|
||||
cal.add('version', '2.0')
|
||||
cal.add('calscale', 'GREGORIAN')
|
||||
cal.add('method', 'PUBLISH')
|
||||
|
||||
# Timezone
|
||||
tz = pytz.timezone('Europe/Berlin')
|
||||
|
||||
# Füge Events hinzu
|
||||
for event_data in dienstplan['events']:
|
||||
if not event_data['service']:
|
||||
continue
|
||||
|
||||
event = Event()
|
||||
|
||||
# Titel - nur den Dienstart
|
||||
service_type = event_data['service']
|
||||
title = f"Dienst: {service_type}"
|
||||
|
||||
event.add('summary', title)
|
||||
|
||||
# Beschreibung
|
||||
description = f"Dienstart: {service_type}"
|
||||
if dienstplan['betriebshof']:
|
||||
description += f"\nBetriebshof: {dienstplan['betriebshof']}"
|
||||
|
||||
event.add('description', description)
|
||||
|
||||
# Datum und Zeit
|
||||
event_date = event_data['date']
|
||||
|
||||
if event_data['start_time'] and event_data['end_time']:
|
||||
# Mit Uhrzeit
|
||||
try:
|
||||
start_hour = int(event_data['start_time'][:2])
|
||||
start_min = int(event_data['start_time'][3:5])
|
||||
end_hour = int(event_data['end_time'][:2])
|
||||
end_min = int(event_data['end_time'][3:5])
|
||||
|
||||
# Wenn Endzeit kleiner als Startzeit, läuft Schicht in nächsten Tag
|
||||
if end_hour < start_hour:
|
||||
end_date = event_date + timedelta(days=1)
|
||||
else:
|
||||
end_date = event_date
|
||||
|
||||
start_dt = event_date.replace(hour=start_hour, minute=start_min, second=0)
|
||||
end_dt = end_date.replace(hour=end_hour, minute=end_min, second=0)
|
||||
|
||||
event.add('dtstart', tz.localize(start_dt))
|
||||
event.add('dtend', tz.localize(end_dt))
|
||||
except:
|
||||
event.add('dtstart', event_date.date())
|
||||
else:
|
||||
# Nur Datum (Ganztagesveranstaltung)
|
||||
event.add('dtstart', event_date.date())
|
||||
event.add('dtend', (event_date + timedelta(days=1)).date())
|
||||
|
||||
# UID und Metadaten
|
||||
event.add('uid', f"{event_date.isoformat()}-{event_data['service']}-{dienstplan.get('personalnummer', 'unknown')}@dienstplan")
|
||||
event.add('created', datetime.now(tz))
|
||||
event.add('dtstamp', datetime.now(tz))
|
||||
|
||||
cal.add_component(event)
|
||||
|
||||
# Speichere ICS-Datei
|
||||
if not output_path:
|
||||
output_path = 'dienstplan.ics'
|
||||
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(cal.to_ical())
|
||||
|
||||
return output_path
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Hauptfunktion
|
||||
"""
|
||||
import sys
|
||||
|
||||
# Finde alle PDF-Dateien im aktuellen Verzeichnis
|
||||
pdf_files = list(Path('.').glob('*.pdf'))
|
||||
|
||||
if not pdf_files:
|
||||
print("Keine PDF-Dateien gefunden!")
|
||||
return
|
||||
|
||||
for pdf_file in pdf_files:
|
||||
print(f"\nVerarbeite: {pdf_file}")
|
||||
|
||||
try:
|
||||
# Extrahiere Daten
|
||||
dienstplan = extract_dienstplan_data(str(pdf_file))
|
||||
|
||||
print(f"Name: {dienstplan['vorname']} {dienstplan['name']}")
|
||||
print(f"Personalnummer: {dienstplan['personalnummer']}")
|
||||
print(f"Betriebshof: {dienstplan['betriebshof']}")
|
||||
print(f"Anzahl der Events: {len(dienstplan['events'])}")
|
||||
|
||||
# Erstelle ICS-Datei
|
||||
ics_path = pdf_file.with_suffix('.ics')
|
||||
create_ics_from_dienstplan(dienstplan, str(ics_path))
|
||||
|
||||
print(f"✓ ICS-Datei erstellt: {ics_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Fehler bei {pdf_file}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
19
start.cmd
Normal file
19
start.cmd
Normal file
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
REM PDF zu ICS Konverter - Windows Startskript
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM Wechsel ins Skriptverzeichnis
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM Überprüfe, ob venv existiert
|
||||
if not exist ".venv" (
|
||||
echo Python-Umgebung wird eingerichtet...
|
||||
python3 -m venv .venv
|
||||
call .venv\Scripts\pip.exe install -q pdfplumber icalendar pypdf2 pytz
|
||||
)
|
||||
|
||||
REM Starte das Menü
|
||||
call .venv\Scripts\python.exe menu.py
|
||||
|
||||
pause
|
||||
78
start.sh
Executable file
78
start.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PDF zu ICS Konverter - Startskript
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Finde Python-Executable
|
||||
PYTHON_CMD=""
|
||||
if command -v python3 &> /dev/null; then
|
||||
PYTHON_CMD="python3"
|
||||
elif command -v python &> /dev/null; then
|
||||
PYTHON_CMD="python"
|
||||
else
|
||||
echo "❌ Fehler: Python nicht gefunden!"
|
||||
echo "Bitte installieren Sie Python 3.6 oder höher."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🐍 Nutze: $PYTHON_CMD"
|
||||
|
||||
# Erstelle venv wenn nicht vorhanden
|
||||
if [ ! -d ".venv" ]; then
|
||||
echo "📦 Python-Umgebung wird eingerichtet..."
|
||||
$PYTHON_CMD -m venv .venv --upgrade-deps || {
|
||||
echo "❌ venv konnte nicht erstellt werden"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Nutze Python aus venv
|
||||
PYTHON_VENV=".venv/bin/python"
|
||||
|
||||
# Überprüfe, ob Abhängigkeiten installiert sind
|
||||
if ! $PYTHON_VENV -c "import pdfplumber" 2>/dev/null; then
|
||||
echo "📚 Installiere Abhängigkeiten..."
|
||||
|
||||
# Nutze python -m pip statt pip direkt
|
||||
if $PYTHON_VENV -m pip install -q pdfplumber icalendar pypdf2 pytz 2>/dev/null; then
|
||||
echo "✓ Abhängigkeiten installiert"
|
||||
else
|
||||
echo "❌ Installation fehlgeschlagen"
|
||||
echo "🔧 Versuche venv neu aufzubauen..."
|
||||
rm -rf .venv
|
||||
$PYTHON_CMD -m venv .venv --upgrade-deps || {
|
||||
echo "❌ venv konnte nicht neu erstellt werden"
|
||||
exit 1
|
||||
}
|
||||
$PYTHON_VENV -m pip install -q pdfplumber icalendar pypdf2 pytz || {
|
||||
echo "❌ Abhängigkeiten konnten nicht installiert werden"
|
||||
exit 1
|
||||
}
|
||||
echo "✓ venv neu erstellt"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Starte das Skript
|
||||
if [ -f "$PYTHON_VENV" ]; then
|
||||
# Versuche zuerst das interaktive Menü, falls TTY verfügbar
|
||||
if [ -t 0 ]; then
|
||||
$PYTHON_VENV menu.py
|
||||
else
|
||||
# Sonst starte die direkte Konvertierung
|
||||
echo ""
|
||||
echo "🔄 Konvertiere PDF-Dateien..."
|
||||
echo "-----------------------------------"
|
||||
$PYTHON_VENV pdf_to_ics.py
|
||||
echo "-----------------------------------"
|
||||
echo ""
|
||||
echo "✅ Fertig!"
|
||||
fi
|
||||
else
|
||||
echo "❌ Fehler: Python-Umgebung ist beschädigt"
|
||||
echo "📁 Bitte löschen Sie das .venv Verzeichnis und versuchen Sie erneut:"
|
||||
echo " rm -rf .venv"
|
||||
echo " ./start.sh"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user