Add comprehensive error handling to Python and JavaScript files
This commit enhances error handling across the codebase with clear, user-friendly error messages: Python Files: - main.py: Added error handling for directory creation and file operations - read_excel.py: Added error handling for file loading, JSON parsing, and data output - fill_plan_dates.py: Added input validation and error handling for file operations and date calculations - calculate.py: Improved error handling in load_holidays() and process_file() with detailed warnings for invalid data - build_template.py: Added error handling for directory creation, workbook creation, and file saving JavaScript Files: - storage.js: Added comprehensive error handling for: - JSON parsing in getEmployees() and getAllDuties() - Type validation in save operations - Date conversion in getDutiesForMonth() - Data validation in saveDutiesForMonth() - Export/import operations Benefits: - Clear error messages in German for better user experience - Graceful degradation when data is corrupted - Type checking to prevent invalid data from being stored - Row-level error reporting for Excel processing - Invalid data filtering to prevent application crashes
This commit is contained in:
parent
8821bee816
commit
b9d8e7094b
6 changed files with 534 additions and 270 deletions
|
|
@ -284,33 +284,71 @@ def _populate_checks(ws):
|
|||
|
||||
|
||||
def build_template():
|
||||
TEMPLATE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
wb = Workbook()
|
||||
"""Builds the complete Excel template with all sheets and formulas."""
|
||||
try:
|
||||
# Create output directory
|
||||
try:
|
||||
TEMPLATE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Erstellen des Verzeichnisses '{TEMPLATE_PATH.parent}'")
|
||||
raise
|
||||
except OSError as e:
|
||||
print(f"❌ Fehler beim Erstellen des Verzeichnisses '{TEMPLATE_PATH.parent}': {e}")
|
||||
raise
|
||||
|
||||
readme_ws = wb.active
|
||||
readme_ws.title = "README"
|
||||
_populate_readme(readme_ws)
|
||||
# Create workbook
|
||||
try:
|
||||
wb = Workbook()
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Erstellen des Workbooks: {e}")
|
||||
raise
|
||||
|
||||
rules_ws = wb.create_sheet("Regeln")
|
||||
_populate_rules(rules_ws)
|
||||
try:
|
||||
readme_ws = wb.active
|
||||
readme_ws.title = "README"
|
||||
_populate_readme(readme_ws)
|
||||
|
||||
holiday_ws = wb.create_sheet("Feiertage")
|
||||
_populate_holidays(holiday_ws)
|
||||
rules_ws = wb.create_sheet("Regeln")
|
||||
_populate_rules(rules_ws)
|
||||
|
||||
plan_ws = wb.create_sheet("Plan")
|
||||
_populate_plan(plan_ws)
|
||||
holiday_ws = wb.create_sheet("Feiertage")
|
||||
_populate_holidays(holiday_ws)
|
||||
|
||||
auswertung_ws = wb.create_sheet("Auswertung")
|
||||
_populate_auswertung(auswertung_ws)
|
||||
plan_ws = wb.create_sheet("Plan")
|
||||
_populate_plan(plan_ws)
|
||||
|
||||
checks_ws = wb.create_sheet("Checks")
|
||||
_populate_checks(checks_ws)
|
||||
auswertung_ws = wb.create_sheet("Auswertung")
|
||||
_populate_auswertung(auswertung_ws)
|
||||
|
||||
wb.save(TEMPLATE_PATH)
|
||||
return TEMPLATE_PATH
|
||||
checks_ws = wb.create_sheet("Checks")
|
||||
_populate_checks(checks_ws)
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Erstellen der Arbeitsblätter: {e}")
|
||||
raise
|
||||
|
||||
# Save template
|
||||
try:
|
||||
wb.save(TEMPLATE_PATH)
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Speichern der Datei '{TEMPLATE_PATH}'")
|
||||
raise
|
||||
except OSError as e:
|
||||
print(f"❌ Fehler beim Speichern der Datei '{TEMPLATE_PATH}': {e}")
|
||||
raise
|
||||
|
||||
return TEMPLATE_PATH
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Unerwarteter Fehler beim Erstellen der Vorlage: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
path = build_template()
|
||||
print(f"✅ Vorlage (Variante 2 – streng) erstellt: {path}")
|
||||
try:
|
||||
path = build_template()
|
||||
print(f"✅ Vorlage (Variante 2 – streng) erstellt: {path}")
|
||||
except Exception:
|
||||
# Error already printed in build_template
|
||||
import sys
|
||||
sys.exit(1)
|
||||
|
||||
|
|
|
|||
228
src/calculate.py
228
src/calculate.py
|
|
@ -20,24 +20,38 @@ ABZUG = 2.0 # Abzug nach Erreichen der Schwelle
|
|||
def load_holidays(wb):
|
||||
"""Lädt Feiertage aus dem Feiertage-Blatt."""
|
||||
if "Feiertage" not in wb.sheetnames:
|
||||
print("⚠️ Warnung: Blatt 'Feiertage' nicht gefunden. Keine Feiertage geladen")
|
||||
return set()
|
||||
|
||||
holidays = set()
|
||||
ws = wb["Feiertage"]
|
||||
|
||||
for row in ws.iter_rows(min_row=2, values_only=True):
|
||||
if row[0] and row[2] == "NRW": # Datum und BL prüfen
|
||||
date_raw = row[0]
|
||||
if isinstance(date_raw, str):
|
||||
try:
|
||||
parsed_date = datetime.strptime(date_raw, '%d.%m.%Y').date()
|
||||
holidays.add(parsed_date)
|
||||
except:
|
||||
pass
|
||||
elif isinstance(date_raw, datetime):
|
||||
holidays.add(date_raw.date())
|
||||
elif isinstance(date_raw, date):
|
||||
holidays.add(date_raw)
|
||||
try:
|
||||
ws = wb["Feiertage"]
|
||||
|
||||
for row_num, row in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):
|
||||
try:
|
||||
if row[0] and len(row) > 2 and row[2] == "NRW": # Datum und BL prüfen
|
||||
date_raw = row[0]
|
||||
if isinstance(date_raw, str):
|
||||
try:
|
||||
parsed_date = datetime.strptime(date_raw, '%d.%m.%Y').date()
|
||||
holidays.add(parsed_date)
|
||||
except ValueError as e:
|
||||
print(f"⚠️ Warnung: Ungültiges Datumsformat in Zeile {row_num}: '{date_raw}' - {e}")
|
||||
continue
|
||||
elif isinstance(date_raw, datetime):
|
||||
holidays.add(date_raw.date())
|
||||
elif isinstance(date_raw, date):
|
||||
holidays.add(date_raw)
|
||||
except IndexError:
|
||||
print(f"⚠️ Warnung: Unvollständige Zeile {row_num} im Feiertage-Blatt übersprungen")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"⚠️ Warnung: Fehler beim Verarbeiten von Zeile {row_num}: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Laden der Feiertage: {e}")
|
||||
return set()
|
||||
|
||||
return holidays
|
||||
|
||||
|
|
@ -155,89 +169,131 @@ def calculate_verguetung(plan_data, holidays):
|
|||
def process_file(filepath):
|
||||
"""Verarbeitet die Excel-Datei und schreibt Auswertung."""
|
||||
|
||||
wb = load_workbook(filepath)
|
||||
|
||||
# Lade Feiertage
|
||||
holidays = load_holidays(wb)
|
||||
print(f"📅 {len(holidays)} Feiertage geladen")
|
||||
|
||||
# Lade Plan-Daten
|
||||
if "Plan" not in wb.sheetnames:
|
||||
print("❌ Blatt 'Plan' nicht gefunden!")
|
||||
# Load workbook
|
||||
try:
|
||||
wb = load_workbook(filepath)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Fehler: Datei '{filepath}' nicht gefunden")
|
||||
return
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Lesen der Datei '{filepath}'")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Laden der Datei '{filepath}': {e}")
|
||||
return
|
||||
|
||||
plan_ws = wb["Plan"]
|
||||
plan_data = []
|
||||
try:
|
||||
# Lade Feiertage
|
||||
holidays = load_holidays(wb)
|
||||
print(f"📅 {len(holidays)} Feiertage geladen")
|
||||
|
||||
for row in plan_ws.iter_rows(min_row=2, values_only=True):
|
||||
if row[0]: # Wenn Datum vorhanden
|
||||
datum_raw = row[0]
|
||||
mitarbeiter = row[1] if len(row) > 1 else None
|
||||
# Lade Plan-Daten
|
||||
if "Plan" not in wb.sheetnames:
|
||||
print("❌ Blatt 'Plan' nicht gefunden!")
|
||||
return
|
||||
|
||||
# Parse Datum (kann String oder date sein)
|
||||
if isinstance(datum_raw, str):
|
||||
try:
|
||||
datum = datetime.strptime(datum_raw, '%d.%m.%Y').date()
|
||||
except:
|
||||
continue
|
||||
elif isinstance(datum_raw, datetime):
|
||||
datum = datum_raw.date()
|
||||
elif isinstance(datum_raw, date):
|
||||
datum = datum_raw
|
||||
else:
|
||||
plan_ws = wb["Plan"]
|
||||
plan_data = []
|
||||
|
||||
for row_num, row in enumerate(plan_ws.iter_rows(min_row=2, values_only=True), start=2):
|
||||
try:
|
||||
if row[0]: # Wenn Datum vorhanden
|
||||
datum_raw = row[0]
|
||||
mitarbeiter = row[1] if len(row) > 1 else None
|
||||
|
||||
# Parse Datum (kann String oder date sein)
|
||||
if isinstance(datum_raw, str):
|
||||
try:
|
||||
datum = datetime.strptime(datum_raw, '%d.%m.%Y').date()
|
||||
except ValueError as e:
|
||||
print(f"⚠️ Warnung: Ungültiges Datumsformat in Zeile {row_num}: '{datum_raw}' - übersprungen")
|
||||
continue
|
||||
elif isinstance(datum_raw, datetime):
|
||||
datum = datum_raw.date()
|
||||
elif isinstance(datum_raw, date):
|
||||
datum = datum_raw
|
||||
else:
|
||||
print(f"⚠️ Warnung: Unbekannter Datumstyp in Zeile {row_num}: {type(datum_raw)} - übersprungen")
|
||||
continue
|
||||
|
||||
if mitarbeiter:
|
||||
plan_data.append((datum, mitarbeiter))
|
||||
except Exception as e:
|
||||
print(f"⚠️ Warnung: Fehler beim Verarbeiten von Plan-Zeile {row_num}: {e}")
|
||||
continue
|
||||
|
||||
if mitarbeiter:
|
||||
plan_data.append((datum, mitarbeiter))
|
||||
print(f"📋 {len(plan_data)} Einträge im Plan")
|
||||
|
||||
print(f"📋 {len(plan_data)} Einträge im Plan")
|
||||
if not plan_data:
|
||||
print("⚠️ Warnung: Keine gültigen Plan-Einträge gefunden")
|
||||
|
||||
# Berechne Vergütung
|
||||
results = calculate_verguetung(plan_data, holidays)
|
||||
# Berechne Vergütung
|
||||
try:
|
||||
results = calculate_verguetung(plan_data, holidays)
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler bei der Vergütungsberechnung: {e}")
|
||||
return
|
||||
|
||||
# Schreibe Auswertung
|
||||
if "Auswertung" not in wb.sheetnames:
|
||||
print("❌ Blatt 'Auswertung' nicht gefunden!")
|
||||
# Schreibe Auswertung
|
||||
if "Auswertung" not in wb.sheetnames:
|
||||
print("❌ Blatt 'Auswertung' nicht gefunden!")
|
||||
return
|
||||
|
||||
try:
|
||||
auswertung_ws = wb["Auswertung"]
|
||||
|
||||
# Lösche alte Daten (ab Zeile 2)
|
||||
auswertung_ws.delete_rows(2, auswertung_ws.max_row)
|
||||
|
||||
# Schreibe neue Daten
|
||||
for idx, result in enumerate(results, start=2):
|
||||
auswertung_ws[f"A{idx}"] = result['mitarbeiter']
|
||||
auswertung_ws[f"B{idx}"] = round(result['wt_einheiten'], 2)
|
||||
auswertung_ws[f"C{idx}"] = round(result['we_freitag'], 2)
|
||||
auswertung_ws[f"D{idx}"] = round(result['we_andere'], 2)
|
||||
auswertung_ws[f"E{idx}"] = round(result['we_gesamt'], 2)
|
||||
auswertung_ws[f"F{idx}"] = result['schwelle_erreicht']
|
||||
auswertung_ws[f"G{idx}"] = round(result['abzug_freitag'], 2)
|
||||
auswertung_ws[f"H{idx}"] = round(result['abzug_andere'], 2)
|
||||
auswertung_ws[f"I{idx}"] = round(result['we_bezahlt'], 2)
|
||||
auswertung_ws[f"J{idx}"] = round(result['auszahlung_wt'], 2)
|
||||
auswertung_ws[f"K{idx}"] = round(result['auszahlung_we'], 2)
|
||||
auswertung_ws[f"L{idx}"] = round(result['auszahlung_gesamt'], 2)
|
||||
|
||||
# Formatierung für Schwelle
|
||||
if result['schwelle_erreicht'] == 'JA':
|
||||
auswertung_ws[f"F{idx}"].fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
|
||||
else:
|
||||
auswertung_ws[f"F{idx}"].fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Schreiben der Auswertung: {e}")
|
||||
return
|
||||
|
||||
# Save file
|
||||
try:
|
||||
wb.save(filepath)
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Speichern der Datei '{filepath}'")
|
||||
return
|
||||
except OSError as e:
|
||||
print(f"❌ Fehler beim Speichern der Datei '{filepath}': {e}")
|
||||
return
|
||||
|
||||
print(f"\n✅ Auswertung geschrieben: {len(results)} Mitarbeiter")
|
||||
print(f" Datei: {filepath}")
|
||||
|
||||
# Zeige Zusammenfassung
|
||||
print(f"\n{'='*70}")
|
||||
print(f"{'Mitarbeiter':<20} {'WT':<8} {'WE':<8} {'Schwelle':<10} {'Gesamt':>10}")
|
||||
print(f"{'='*70}")
|
||||
for r in results:
|
||||
print(f"{r['mitarbeiter']:<20} {r['wt_einheiten']:>6.1f} {r['we_gesamt']:>6.1f} {r['schwelle_erreicht']:<10} {r['auszahlung_gesamt']:>9.2f} €")
|
||||
print(f"{'='*70}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Unerwarteter Fehler beim Verarbeiten der Datei: {e}")
|
||||
return
|
||||
|
||||
auswertung_ws = wb["Auswertung"]
|
||||
|
||||
# Lösche alte Daten (ab Zeile 2)
|
||||
auswertung_ws.delete_rows(2, auswertung_ws.max_row)
|
||||
|
||||
# Schreibe neue Daten
|
||||
for idx, result in enumerate(results, start=2):
|
||||
auswertung_ws[f"A{idx}"] = result['mitarbeiter']
|
||||
auswertung_ws[f"B{idx}"] = round(result['wt_einheiten'], 2)
|
||||
auswertung_ws[f"C{idx}"] = round(result['we_freitag'], 2)
|
||||
auswertung_ws[f"D{idx}"] = round(result['we_andere'], 2)
|
||||
auswertung_ws[f"E{idx}"] = round(result['we_gesamt'], 2)
|
||||
auswertung_ws[f"F{idx}"] = result['schwelle_erreicht']
|
||||
auswertung_ws[f"G{idx}"] = round(result['abzug_freitag'], 2)
|
||||
auswertung_ws[f"H{idx}"] = round(result['abzug_andere'], 2)
|
||||
auswertung_ws[f"I{idx}"] = round(result['we_bezahlt'], 2)
|
||||
auswertung_ws[f"J{idx}"] = round(result['auszahlung_wt'], 2)
|
||||
auswertung_ws[f"K{idx}"] = round(result['auszahlung_we'], 2)
|
||||
auswertung_ws[f"L{idx}"] = round(result['auszahlung_gesamt'], 2)
|
||||
|
||||
# Formatierung für Schwelle
|
||||
if result['schwelle_erreicht'] == 'JA':
|
||||
auswertung_ws[f"F{idx}"].fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
|
||||
else:
|
||||
auswertung_ws[f"F{idx}"].fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
|
||||
|
||||
wb.save(filepath)
|
||||
print(f"\n✅ Auswertung geschrieben: {len(results)} Mitarbeiter")
|
||||
print(f" Datei: {filepath}")
|
||||
|
||||
# Zeige Zusammenfassung
|
||||
print(f"\n{'='*70}")
|
||||
print(f"{'Mitarbeiter':<20} {'WT':<8} {'WE':<8} {'Schwelle':<10} {'Gesamt':>10}")
|
||||
print(f"{'='*70}")
|
||||
for r in results:
|
||||
print(f"{r['mitarbeiter']:<20} {r['wt_einheiten']:>6.1f} {r['we_gesamt']:>6.1f} {r['schwelle_erreicht']:<10} {r['auszahlung_gesamt']:>9.2f} €")
|
||||
print(f"{'='*70}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) >= 2:
|
||||
|
|
|
|||
|
|
@ -15,46 +15,88 @@ def fill_plan_with_dates(template_path, output_path, year, month):
|
|||
Lädt die Vorlage und füllt Spalte A (Datum) im Plan-Blatt
|
||||
mit allen Tagen des angegebenen Monats.
|
||||
"""
|
||||
wb = load_workbook(template_path)
|
||||
|
||||
# Regeln-Blatt: Monat_Auswahl setzen
|
||||
if "Regeln" in wb.sheetnames:
|
||||
regeln_ws = wb["Regeln"]
|
||||
# Zeile 7, Spalte B = Monat_Auswahl
|
||||
regeln_ws["B7"] = date(year, month, 1)
|
||||
|
||||
# Plan-Blatt füllen
|
||||
if "Plan" not in wb.sheetnames:
|
||||
print("❌ Blatt 'Plan' nicht gefunden!")
|
||||
# Validate input parameters
|
||||
if not (1 <= month <= 12):
|
||||
print(f"❌ Fehler: Ungültiger Monat '{month}'. Monat muss zwischen 1 und 12 liegen")
|
||||
return
|
||||
|
||||
plan_ws = wb["Plan"]
|
||||
if year < 1900 or year > 2100:
|
||||
print(f"❌ Fehler: Ungültiges Jahr '{year}'. Jahr muss zwischen 1900 und 2100 liegen")
|
||||
return
|
||||
|
||||
# Startdatum
|
||||
start_date = date(year, month, 1)
|
||||
# Load template workbook
|
||||
try:
|
||||
wb = load_workbook(template_path)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Fehler: Vorlagendatei '{template_path}' nicht gefunden")
|
||||
return
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Lesen der Datei '{template_path}'")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Laden der Vorlagendatei '{template_path}': {e}")
|
||||
return
|
||||
|
||||
# Letzter Tag des Monats
|
||||
if month == 12:
|
||||
end_date = date(year + 1, 1, 1) - timedelta(days=1)
|
||||
else:
|
||||
end_date = date(year, month + 1, 1) - timedelta(days=1)
|
||||
try:
|
||||
# Regeln-Blatt: Monat_Auswahl setzen
|
||||
if "Regeln" in wb.sheetnames:
|
||||
regeln_ws = wb["Regeln"]
|
||||
# Zeile 7, Spalte B = Monat_Auswahl
|
||||
regeln_ws["B7"] = date(year, month, 1)
|
||||
|
||||
# Alle Tage durchgehen
|
||||
current_date = start_date
|
||||
row = 2 # Zeile 2 = erste Datenzeile nach Header
|
||||
# Plan-Blatt füllen
|
||||
if "Plan" not in wb.sheetnames:
|
||||
print("❌ Blatt 'Plan' nicht gefunden!")
|
||||
return
|
||||
|
||||
while current_date <= end_date:
|
||||
cell = plan_ws[f"A{row}"]
|
||||
cell.value = current_date
|
||||
cell.number_format = 'DD.MM.YYYY' # Deutsches Datumsformat
|
||||
# Spalten B (Mitarbeiter) und C (Anteil) bleiben leer zum Ausfüllen
|
||||
current_date += timedelta(days=1)
|
||||
row += 1
|
||||
plan_ws = wb["Plan"]
|
||||
|
||||
wb.save(output_path)
|
||||
print(f"✅ Plan-Blatt vorbefüllt für {month:02d}/{year}")
|
||||
print(f" Ausgabe: {output_path}")
|
||||
print(f" Trage jetzt nur noch in Spalte B (Mitarbeiter) und C (Anteil) die Namen ein!")
|
||||
# Startdatum
|
||||
try:
|
||||
start_date = date(year, month, 1)
|
||||
except ValueError as e:
|
||||
print(f"❌ Fehler: Ungültiges Datum für Jahr {year}, Monat {month}: {e}")
|
||||
return
|
||||
|
||||
# Letzter Tag des Monats
|
||||
try:
|
||||
if month == 12:
|
||||
end_date = date(year + 1, 1, 1) - timedelta(days=1)
|
||||
else:
|
||||
end_date = date(year, month + 1, 1) - timedelta(days=1)
|
||||
except ValueError as e:
|
||||
print(f"❌ Fehler beim Berechnen des Enddatums: {e}")
|
||||
return
|
||||
|
||||
# Alle Tage durchgehen
|
||||
current_date = start_date
|
||||
row = 2 # Zeile 2 = erste Datenzeile nach Header
|
||||
|
||||
while current_date <= end_date:
|
||||
cell = plan_ws[f"A{row}"]
|
||||
cell.value = current_date
|
||||
cell.number_format = 'DD.MM.YYYY' # Deutsches Datumsformat
|
||||
# Spalten B (Mitarbeiter) und C (Anteil) bleiben leer zum Ausfüllen
|
||||
current_date += timedelta(days=1)
|
||||
row += 1
|
||||
|
||||
# Save output file
|
||||
try:
|
||||
wb.save(output_path)
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Speichern der Datei '{output_path}'")
|
||||
return
|
||||
except OSError as e:
|
||||
print(f"❌ Fehler beim Speichern der Datei '{output_path}': {e}")
|
||||
return
|
||||
|
||||
print(f"✅ Plan-Blatt vorbefüllt für {month:02d}/{year}")
|
||||
print(f" Ausgabe: {output_path}")
|
||||
print(f" Trage jetzt nur noch in Spalte B (Mitarbeiter) und C (Anteil) die Namen ein!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Unerwarteter Fehler beim Füllen des Plan-Blatts: {e}")
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
91
src/main.py
91
src/main.py
|
|
@ -12,51 +12,70 @@ from datetime import datetime
|
|||
def create_example_excel():
|
||||
"""Erstellt eine Beispiel-Excel-Datei mit formatierten Daten."""
|
||||
|
||||
# Neues Workbook erstellen
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Beispiel"
|
||||
try:
|
||||
# Neues Workbook erstellen
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Beispiel"
|
||||
|
||||
# Überschriften hinzufügen
|
||||
headers = ["Name", "Alter", "Stadt", "Beruf"]
|
||||
ws.append(headers)
|
||||
# Überschriften hinzufügen
|
||||
headers = ["Name", "Alter", "Stadt", "Beruf"]
|
||||
ws.append(headers)
|
||||
|
||||
# Überschriften formatieren
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
# Überschriften formatieren
|
||||
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
header_font = Font(bold=True, color="FFFFFF", size=12)
|
||||
|
||||
for cell in ws[1]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
for cell in ws[1]:
|
||||
cell.fill = header_fill
|
||||
cell.font = header_font
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
# Beispieldaten hinzufügen
|
||||
data = [
|
||||
["Max Mustermann", 30, "Berlin", "Entwickler"],
|
||||
["Erika Musterfrau", 28, "München", "Designerin"],
|
||||
["Hans Schmidt", 35, "Hamburg", "Manager"],
|
||||
["Anna Weber", 27, "Köln", "Analyst"],
|
||||
]
|
||||
# Beispieldaten hinzufügen
|
||||
data = [
|
||||
["Max Mustermann", 30, "Berlin", "Entwickler"],
|
||||
["Erika Musterfrau", 28, "München", "Designerin"],
|
||||
["Hans Schmidt", 35, "Hamburg", "Manager"],
|
||||
["Anna Weber", 27, "Köln", "Analyst"],
|
||||
]
|
||||
|
||||
for row in data:
|
||||
ws.append(row)
|
||||
for row in data:
|
||||
ws.append(row)
|
||||
|
||||
# Spaltenbreiten anpassen
|
||||
ws.column_dimensions['A'].width = 20
|
||||
ws.column_dimensions['B'].width = 10
|
||||
ws.column_dimensions['C'].width = 15
|
||||
ws.column_dimensions['D'].width = 15
|
||||
# Spaltenbreiten anpassen
|
||||
ws.column_dimensions['A'].width = 20
|
||||
ws.column_dimensions['B'].width = 10
|
||||
ws.column_dimensions['C'].width = 15
|
||||
ws.column_dimensions['D'].width = 15
|
||||
|
||||
# Ausgabeverzeichnis erstellen
|
||||
output_dir = Path("output")
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
# Ausgabeverzeichnis erstellen
|
||||
output_dir = Path("output")
|
||||
try:
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Erstellen des Verzeichnisses '{output_dir}'")
|
||||
raise
|
||||
except OSError as e:
|
||||
print(f"❌ Fehler beim Erstellen des Verzeichnisses '{output_dir}': {e}")
|
||||
raise
|
||||
|
||||
# Datei speichern
|
||||
output_file = output_dir / f"example_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
wb.save(output_file)
|
||||
# Datei speichern
|
||||
output_file = output_dir / f"example_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
try:
|
||||
wb.save(output_file)
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Speichern der Datei '{output_file}'")
|
||||
raise
|
||||
except OSError as e:
|
||||
print(f"❌ Fehler beim Speichern der Datei '{output_file}': {e}")
|
||||
raise
|
||||
|
||||
print(f"Excel-Datei erfolgreich erstellt: {output_file}")
|
||||
return output_file
|
||||
print(f"Excel-Datei erfolgreich erstellt: {output_file}")
|
||||
return output_file
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Unerwarteter Fehler beim Erstellen der Excel-Datei: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -10,20 +10,35 @@ from pathlib import Path
|
|||
def read_excel_to_dict(filepath):
|
||||
"""Liest eine Excel-Datei und gibt die Daten als Dictionary zurück."""
|
||||
|
||||
wb = load_workbook(filepath, data_only=True)
|
||||
try:
|
||||
wb = load_workbook(filepath, data_only=True)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ Fehler: Datei '{filepath}' nicht gefunden")
|
||||
raise
|
||||
except PermissionError:
|
||||
print(f"❌ Fehler: Keine Berechtigung zum Lesen der Datei '{filepath}'")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Laden der Excel-Datei '{filepath}': {e}")
|
||||
raise
|
||||
|
||||
result = {}
|
||||
|
||||
for sheet_name in wb.sheetnames:
|
||||
ws = wb[sheet_name]
|
||||
try:
|
||||
for sheet_name in wb.sheetnames:
|
||||
ws = wb[sheet_name]
|
||||
|
||||
# Daten aus dem Sheet lesen
|
||||
data = []
|
||||
for row in ws.iter_rows(values_only=True):
|
||||
# Nur Zeilen mit Inhalt
|
||||
if any(cell is not None for cell in row):
|
||||
data.append(list(row))
|
||||
# Daten aus dem Sheet lesen
|
||||
data = []
|
||||
for row in ws.iter_rows(values_only=True):
|
||||
# Nur Zeilen mit Inhalt
|
||||
if any(cell is not None for cell in row):
|
||||
data.append(list(row))
|
||||
|
||||
result[sheet_name] = data
|
||||
result[sheet_name] = data
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Lesen der Daten aus der Excel-Datei: {e}")
|
||||
raise
|
||||
|
||||
return result
|
||||
|
||||
|
|
@ -35,26 +50,38 @@ def print_excel_content(filepath):
|
|||
print(f"Excel-Datei: {filepath}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
data = read_excel_to_dict(filepath)
|
||||
try:
|
||||
data = read_excel_to_dict(filepath)
|
||||
except Exception:
|
||||
# Error already printed in read_excel_to_dict
|
||||
raise
|
||||
|
||||
for sheet_name, rows in data.items():
|
||||
print(f"\n📊 Sheet: {sheet_name}")
|
||||
print(f"{'-'*60}")
|
||||
try:
|
||||
for sheet_name, rows in data.items():
|
||||
print(f"\n📊 Sheet: {sheet_name}")
|
||||
print(f"{'-'*60}")
|
||||
|
||||
if not rows:
|
||||
print(" (leer)")
|
||||
continue
|
||||
if not rows:
|
||||
print(" (leer)")
|
||||
continue
|
||||
|
||||
# Tabelle ausgeben
|
||||
for i, row in enumerate(rows, 1):
|
||||
row_str = " | ".join(str(cell) if cell is not None else "" for cell in row)
|
||||
print(f" {i:3d}: {row_str}")
|
||||
# Tabelle ausgeben
|
||||
for i, row in enumerate(rows, 1):
|
||||
row_str = " | ".join(str(cell) if cell is not None else "" for cell in row)
|
||||
print(f" {i:3d}: {row_str}")
|
||||
|
||||
print(f"\n{'='*60}\n")
|
||||
print(f"\n{'='*60}\n")
|
||||
|
||||
# Als JSON ausgeben
|
||||
print("📄 JSON-Format:")
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
# Als JSON ausgeben
|
||||
print("📄 JSON-Format:")
|
||||
try:
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
except (TypeError, ValueError) as e:
|
||||
print(f"❌ Fehler beim Konvertieren zu JSON: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler beim Ausgeben der Excel-Daten: {e}")
|
||||
raise
|
||||
|
||||
return data
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,21 @@ class DataStorage {
|
|||
* @returns {Array} Array of employee names
|
||||
*/
|
||||
getEmployees() {
|
||||
const data = localStorage.getItem(this.STORAGE_KEY_EMPLOYEES);
|
||||
return data ? JSON.parse(data) : [];
|
||||
try {
|
||||
const data = localStorage.getItem(this.STORAGE_KEY_EMPLOYEES);
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
const parsed = JSON.parse(data);
|
||||
if (!Array.isArray(parsed)) {
|
||||
console.error('Fehler: Mitarbeiter-Daten sind kein Array. Zurücksetzen auf leeres Array');
|
||||
return [];
|
||||
}
|
||||
return parsed;
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Laden der Mitarbeiter-Daten:', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -22,7 +35,16 @@ class DataStorage {
|
|||
* @param {Array} employees - Array of employee names
|
||||
*/
|
||||
saveEmployees(employees) {
|
||||
localStorage.setItem(this.STORAGE_KEY_EMPLOYEES, JSON.stringify(employees));
|
||||
try {
|
||||
if (!Array.isArray(employees)) {
|
||||
console.error('Fehler: employees muss ein Array sein');
|
||||
throw new TypeError('employees muss ein Array sein');
|
||||
}
|
||||
localStorage.setItem(this.STORAGE_KEY_EMPLOYEES, JSON.stringify(employees));
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Speichern der Mitarbeiter-Daten:', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -63,8 +85,21 @@ class DataStorage {
|
|||
* @returns {Object} Object with structure: {employeeName: {year-month: [duties]}}
|
||||
*/
|
||||
getAllDuties() {
|
||||
const data = localStorage.getItem(this.STORAGE_KEY_DUTIES);
|
||||
return data ? JSON.parse(data) : {};
|
||||
try {
|
||||
const data = localStorage.getItem(this.STORAGE_KEY_DUTIES);
|
||||
if (!data) {
|
||||
return {};
|
||||
}
|
||||
const parsed = JSON.parse(data);
|
||||
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
||||
console.error('Fehler: Dienst-Daten sind kein gültiges Objekt. Zurücksetzen auf leeres Objekt');
|
||||
return {};
|
||||
}
|
||||
return parsed;
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Laden der Dienst-Daten:', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +107,16 @@ class DataStorage {
|
|||
* @param {Object} duties
|
||||
*/
|
||||
saveAllDuties(duties) {
|
||||
localStorage.setItem(this.STORAGE_KEY_DUTIES, JSON.stringify(duties));
|
||||
try {
|
||||
if (typeof duties !== 'object' || duties === null || Array.isArray(duties)) {
|
||||
console.error('Fehler: duties muss ein gültiges Objekt sein');
|
||||
throw new TypeError('duties muss ein gültiges Objekt sein');
|
||||
}
|
||||
localStorage.setItem(this.STORAGE_KEY_DUTIES, JSON.stringify(duties));
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Speichern der Dienst-Daten:', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -83,18 +127,35 @@ class DataStorage {
|
|||
* @returns {Array} Array of duty objects
|
||||
*/
|
||||
getDutiesForMonth(employeeName, year, month) {
|
||||
const allDuties = this.getAllDuties();
|
||||
const monthKey = `${year}-${String(month).padStart(2, '0')}`;
|
||||
try {
|
||||
const allDuties = this.getAllDuties();
|
||||
const monthKey = `${year}-${String(month).padStart(2, '0')}`;
|
||||
|
||||
if (!allDuties[employeeName] || !allDuties[employeeName][monthKey]) {
|
||||
if (!allDuties[employeeName] || !allDuties[employeeName][monthKey]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Convert date strings back to Date objects
|
||||
return allDuties[employeeName][monthKey].map(duty => {
|
||||
try {
|
||||
const dateObj = new Date(duty.date);
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
console.error(`Fehler: Ungültiges Datum für Dienst: ${duty.date}`);
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...duty,
|
||||
date: dateObj
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Konvertieren des Datums:', e);
|
||||
return null;
|
||||
}
|
||||
}).filter(duty => duty !== null); // Filter out invalid entries
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Laden der Dienste für Monat:', e);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Convert date strings back to Date objects
|
||||
return allDuties[employeeName][monthKey].map(duty => ({
|
||||
...duty,
|
||||
date: new Date(duty.date)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,20 +166,36 @@ class DataStorage {
|
|||
* @param {Array} duties - Array of duty objects
|
||||
*/
|
||||
saveDutiesForMonth(employeeName, year, month, duties) {
|
||||
const allDuties = this.getAllDuties();
|
||||
const monthKey = `${year}-${String(month).padStart(2, '0')}`;
|
||||
try {
|
||||
if (!Array.isArray(duties)) {
|
||||
console.error('Fehler: duties muss ein Array sein');
|
||||
throw new TypeError('duties muss ein Array sein');
|
||||
}
|
||||
|
||||
if (!allDuties[employeeName]) {
|
||||
allDuties[employeeName] = {};
|
||||
const allDuties = this.getAllDuties();
|
||||
const monthKey = `${year}-${String(month).padStart(2, '0')}`;
|
||||
|
||||
if (!allDuties[employeeName]) {
|
||||
allDuties[employeeName] = {};
|
||||
}
|
||||
|
||||
// Convert Date objects to strings for storage
|
||||
allDuties[employeeName][monthKey] = duties.map(duty => {
|
||||
if (!duty.date || !(duty.date instanceof Date)) {
|
||||
console.error('Fehler: Dienst hat kein gültiges Datum:', duty);
|
||||
throw new TypeError('Dienst muss ein gültiges Date-Objekt haben');
|
||||
}
|
||||
return {
|
||||
...duty,
|
||||
date: duty.date.toISOString()
|
||||
};
|
||||
});
|
||||
|
||||
this.saveAllDuties(allDuties);
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Speichern der Dienste für Monat:', e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Convert Date objects to strings for storage
|
||||
allDuties[employeeName][monthKey] = duties.map(duty => ({
|
||||
...duty,
|
||||
date: duty.date.toISOString()
|
||||
}));
|
||||
|
||||
this.saveAllDuties(allDuties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -196,10 +273,15 @@ class DataStorage {
|
|||
* @returns {string} JSON string
|
||||
*/
|
||||
exportData() {
|
||||
return JSON.stringify({
|
||||
employees: this.getEmployees(),
|
||||
duties: this.getAllDuties()
|
||||
}, null, 2);
|
||||
try {
|
||||
return JSON.stringify({
|
||||
employees: this.getEmployees(),
|
||||
duties: this.getAllDuties()
|
||||
}, null, 2);
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Exportieren der Daten:', e);
|
||||
throw new Error('Fehler beim Exportieren der Daten: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Reference in a new issue