Initial commit: NRW Dienstplan Generator (Variante 2)
This commit is contained in:
commit
99480bb7ff
10 changed files with 960 additions and 0 deletions
301
src/build_template.py
Normal file
301
src/build_template.py
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
"""Builds an empty Excel template for the NRW duty roster rules (Variante 2 - streng)."""
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Alignment, Font, PatternFill, Border, Side
|
||||
from openpyxl.worksheet.datavalidation import DataValidation
|
||||
from openpyxl.worksheet.table import Table, TableStyleInfo
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
TEMPLATE_PATH = Path("templates/Dienstplan_Vorlage_V2_NRW.xlsx")
|
||||
MAX_PLAN_ROWS = 400
|
||||
MAX_HOLIDAY_ROWS = 50
|
||||
|
||||
NRW_HOLIDAYS_2025 = [
|
||||
("2025-01-01", "Neujahr", "NRW"),
|
||||
("2025-04-18", "Karfreitag", "NRW"),
|
||||
("2025-04-21", "Ostermontag", "NRW"),
|
||||
("2025-05-01", "Tag der Arbeit", "NRW"),
|
||||
("2025-05-29", "Christi Himmelfahrt", "NRW"),
|
||||
("2025-06-09", "Pfingstmontag", "NRW"),
|
||||
("2025-06-19", "Fronleichnam", "NRW"),
|
||||
("2025-10-03", "Tag der Deutschen Einheit", "NRW"),
|
||||
("2025-11-01", "Allerheiligen", "NRW"),
|
||||
("2025-12-25", "1. Weihnachtstag", "NRW"),
|
||||
("2025-12-26", "2. Weihnachtstag", "NRW"),
|
||||
]
|
||||
|
||||
NRW_HOLIDAYS_2026 = [
|
||||
("2026-01-01", "Neujahr", "NRW"),
|
||||
("2026-04-03", "Karfreitag", "NRW"),
|
||||
("2026-04-06", "Ostermontag", "NRW"),
|
||||
("2026-05-01", "Tag der Arbeit", "NRW"),
|
||||
("2026-05-14", "Christi Himmelfahrt", "NRW"),
|
||||
("2026-05-25", "Pfingstmontag", "NRW"),
|
||||
("2026-06-04", "Fronleichnam", "NRW"),
|
||||
("2026-10-03", "Tag der Deutschen Einheit", "NRW"),
|
||||
("2026-11-01", "Allerheiligen", "NRW"),
|
||||
("2026-12-25", "1. Weihnachtstag", "NRW"),
|
||||
("2026-12-26", "2. Weihnachtstag", "NRW"),
|
||||
]
|
||||
|
||||
|
||||
def _style_header(ws, row=1):
|
||||
"""Apply header styling."""
|
||||
fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||
font = Font(bold=True, color="FFFFFF", size=11)
|
||||
for cell in ws[row]:
|
||||
if cell.value:
|
||||
cell.font = font
|
||||
cell.fill = fill
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
|
||||
def _populate_readme(ws):
|
||||
ws["A1"] = "NRW-Dienstplan (Variante 2 – streng)"
|
||||
ws["A1"].font = Font(bold=True, size=14)
|
||||
ws["A3"] = "Kurzregeln"
|
||||
ws["A3"].font = Font(bold=True)
|
||||
rules = [
|
||||
"WE-Tag = Fr/Sa/So/Feiertag/Vortag (BL-abhängig).",
|
||||
"Variante 2 (streng): WE werden nur vergütet, wenn im Monat ≥ 2,0 WE-Einheiten erreicht werden;",
|
||||
"dann 450 €/WE und Abzug 1,0 (Freitag zuerst). WT werden immer mit 250 € vergütet.",
|
||||
"Splits anteilig. Monat und Bundesland in 'Regeln' wählen.",
|
||||
"",
|
||||
"Schritte:",
|
||||
"1. In 'Regeln': Monat_Auswahl (erster Tag) + BL_Auswahl setzen.",
|
||||
"2. 'Feiertage' kontrollieren bzw. erweitern.",
|
||||
"3. Im Blatt 'Plan' pro Tag Datum, Mitarbeiter und Anteil (0–1) eintragen.",
|
||||
"4. Auswertung erfolgt automatisch im Blatt 'Auswertung'.",
|
||||
"5. 'Checks' zeigt Unstimmigkeiten (Summe Anteil ≠ 1, etc.).",
|
||||
]
|
||||
for idx, text in enumerate(rules, start=4):
|
||||
ws[f"A{idx}"] = text
|
||||
ws.column_dimensions["A"].width = 100
|
||||
|
||||
|
||||
def _populate_rules(ws):
|
||||
headers = ["Parameter", "Wert", "Beschreibung"]
|
||||
ws.append(headers)
|
||||
rows = [
|
||||
("Satz_WT", 250, "Euro für jeden Werktagsdienst (Mo–Do, sofern kein WE-Tag)"),
|
||||
("Satz_WE", 450, "Euro für jeden WE-Tag (Fr–So, Feiertag, Vortag Feiertag)"),
|
||||
("WE_Schwelle", 2.0, "Ab dieser WE-Anzahl wird vergütet (sonst 0 €)"),
|
||||
("Abzug_nach_WE_Schwelle", 1.0, "Einheiten, die nach Erreichen der Schwelle abgezogen werden"),
|
||||
("BL_Auswahl", "NRW", "Bundesland (steuert Feiertage)"),
|
||||
("Monat_Auswahl", date(2025, 11, 1), "Erster Tag des Zielmonats"),
|
||||
("Variante", 2, "Fix: 2 = streng (WE nur bei Schwelle ≥ 2,0)"),
|
||||
]
|
||||
for param, value, desc in rows:
|
||||
ws.append([param, value, desc])
|
||||
|
||||
ws.column_dimensions["A"].width = 26
|
||||
ws.column_dimensions["B"].width = 18
|
||||
ws.column_dimensions["C"].width = 80
|
||||
_style_header(ws)
|
||||
|
||||
|
||||
def _populate_holidays(ws):
|
||||
headers = ["Datum", "Name", "BL"]
|
||||
ws.append(headers)
|
||||
|
||||
all_holidays = NRW_HOLIDAYS_2025 + NRW_HOLIDAYS_2026
|
||||
for iso_date, name, bl in all_holidays:
|
||||
ws.append([iso_date, name, bl])
|
||||
|
||||
# Create table
|
||||
tab = Table(displayName="tblFeiertage", ref=f"A1:C{len(all_holidays)+1}")
|
||||
style = TableStyleInfo(name="TableStyleMedium9", showFirstColumn=False,
|
||||
showLastColumn=False, showRowStripes=True, showColumnStripes=False)
|
||||
tab.tableStyleInfo = style
|
||||
ws.add_table(tab)
|
||||
|
||||
ws.column_dimensions["A"].width = 14
|
||||
ws.column_dimensions["B"].width = 32
|
||||
ws.column_dimensions["C"].width = 8
|
||||
_style_header(ws)
|
||||
|
||||
|
||||
def _plan_formulas(row: int) -> dict:
|
||||
"""Return helper-column formulas for Plan sheet (Variante 2)."""
|
||||
date_cell = f"A{row}"
|
||||
anteil_cell = f"C{row}"
|
||||
|
||||
# Holiday range filtered by BL (Non-365 fallback with SUMPRODUCT)
|
||||
holiday_check = f'SUMMENPRODUKT((tblFeiertage[Datum]={date_cell})*(tblFeiertage[BL]=Regeln!$B$6))>0'
|
||||
vortag_check = f'SUMMENPRODUKT((tblFeiertage[Datum]={date_cell}+1)*(tblFeiertage[BL]=Regeln!$B$6))>0'
|
||||
|
||||
return {
|
||||
"D": f"=WENNFEHLER({holiday_check};FALSCH)", # Ist_FEIERTAG
|
||||
"E": f"=WENNFEHLER({vortag_check};FALSCH)", # Ist_VORTAG
|
||||
"F": f"=WENNFEHLER(WOCHENTAG({date_cell};2)=5;FALSCH)", # Ist_Freitag
|
||||
"G": f"=ODER($F{row};WOCHENTAG({date_cell};2)=6;WOCHENTAG({date_cell};2)=7;$D{row};$E{row})", # Ist_WE_Tag
|
||||
"H": f"=NICHT($G{row})", # Ist_WT_Tag
|
||||
"I": f"=WENN($H{row};{anteil_cell};0)", # WT_Einheit
|
||||
"J": f"=WENN(UND($G{row};$F{row});{anteil_cell};0)", # WE_Freitag_Einheit
|
||||
"K": f"=WENN(UND($G{row};NICHT($F{row}));{anteil_cell};0)", # WE_Andere_Einheit
|
||||
}
|
||||
|
||||
|
||||
def _populate_plan(ws):
|
||||
headers = [
|
||||
"Datum", "Mitarbeiter", "Anteil",
|
||||
"Ist_FEIERTAG", "Ist_VORTAG", "Ist_Freitag", "Ist_WE_Tag", "Ist_WT_Tag",
|
||||
"WT_Einheit", "WE_Freitag_Einheit", "WE_Andere_Einheit"
|
||||
]
|
||||
ws.append(headers)
|
||||
_style_header(ws)
|
||||
|
||||
for row in range(2, MAX_PLAN_ROWS + 2):
|
||||
formulas = _plan_formulas(row)
|
||||
for col_letter, formula in formulas.items():
|
||||
ws[f"{col_letter}{row}"] = formula
|
||||
|
||||
# Table
|
||||
tab = Table(displayName="tblPlan", ref=f"A1:K{MAX_PLAN_ROWS+1}")
|
||||
style = TableStyleInfo(name="TableStyleMedium2", showFirstColumn=False,
|
||||
showLastColumn=False, showRowStripes=True, showColumnStripes=False)
|
||||
tab.tableStyleInfo = style
|
||||
ws.add_table(tab)
|
||||
|
||||
# Data validation for Anteil
|
||||
dv = DataValidation(type="decimal", operator="between", formula1="0", formula2="1", allow_blank=True)
|
||||
ws.add_data_validation(dv)
|
||||
dv.add(f"C2:C{MAX_PLAN_ROWS + 1}")
|
||||
|
||||
ws.column_dimensions["A"].width = 12
|
||||
ws.column_dimensions["B"].width = 22
|
||||
ws.column_dimensions["C"].width = 10
|
||||
for col in "DEFGHIJK":
|
||||
ws.column_dimensions[col].width = 13
|
||||
|
||||
|
||||
def _populate_auswertung(ws):
|
||||
headers = [
|
||||
"Mitarbeiter", "WT_Einheiten", "WE_Freitag", "WE_Andere", "WE_Gesamt",
|
||||
"Schwelle_erreicht", "Abzug_gesamt", "Abzug_Freitag", "Abzug_Andere",
|
||||
"WE_bezahlt", "Auszahlung_WT", "Auszahlung_WE", "Auszahlung_Gesamt"
|
||||
]
|
||||
ws.append(headers)
|
||||
_style_header(ws)
|
||||
|
||||
# Manual employee list (user fills column A)
|
||||
# Row 2 onwards: formulas reference column A
|
||||
|
||||
monat_start = "Regeln!$B$7"
|
||||
monat_end = f"MONATSENDE({monat_start};0)"
|
||||
|
||||
# Create formulas for 50 rows
|
||||
for row in range(2, 52):
|
||||
name_ref = f"$A{row}"
|
||||
|
||||
# Skip if no name
|
||||
guard = f'WENN({name_ref}="";""'
|
||||
|
||||
# WT_Einheiten - using SUMMENPRODUKT for compatibility
|
||||
wt_formula = (
|
||||
f'={guard};SUMMENPRODUKT((tblPlan[Mitarbeiter]={name_ref})*'
|
||||
f'(tblPlan[Datum]>={monat_start})*(tblPlan[Datum]<={monat_end})*'
|
||||
f'(tblPlan[WT_Einheit])))'
|
||||
)
|
||||
ws[f"B{row}"] = wt_formula
|
||||
|
||||
# WE_Freitag
|
||||
we_fri_formula = (
|
||||
f'={guard};SUMMENPRODUKT((tblPlan[Mitarbeiter]={name_ref})*'
|
||||
f'(tblPlan[Datum]>={monat_start})*(tblPlan[Datum]<={monat_end})*'
|
||||
f'(tblPlan[WE_Freitag_Einheit])))'
|
||||
)
|
||||
ws[f"C{row}"] = we_fri_formula
|
||||
|
||||
# WE_Andere
|
||||
we_other_formula = (
|
||||
f'={guard};SUMMENPRODUKT((tblPlan[Mitarbeiter]={name_ref})*'
|
||||
f'(tblPlan[Datum]>={monat_start})*(tblPlan[Datum]<={monat_end})*'
|
||||
f'(tblPlan[WE_Andere_Einheit])))'
|
||||
)
|
||||
ws[f"D{row}"] = we_other_formula
|
||||
|
||||
# WE_Gesamt
|
||||
ws[f"E{row}"] = f'={guard};C{row}+D{row})'
|
||||
|
||||
# Schwelle_erreicht
|
||||
ws[f"F{row}"] = f'={guard};WENN(E{row}>=Regeln!$B$4-0,0001;"JA";"NEIN"))'
|
||||
|
||||
# Abzug_gesamt
|
||||
ws[f"G{row}"] = f'={guard};WENN(E{row}>=Regeln!$B$4-0,0001;Regeln!$B$5;0))'
|
||||
|
||||
# Abzug_Freitag
|
||||
ws[f"H{row}"] = f'={guard};MIN(G{row};C{row}))'
|
||||
|
||||
# Abzug_Andere
|
||||
ws[f"I{row}"] = f'={guard};MAX(0;G{row}-H{row}))'
|
||||
|
||||
# WE_bezahlt (Variante 2: only if threshold reached)
|
||||
ws[f"J{row}"] = f'={guard};WENN(E{row}<Regeln!$B$4-0,0001;0;(C{row}-H{row})+(D{row}-I{row})))'
|
||||
|
||||
# Auszahlung_WT
|
||||
ws[f"K{row}"] = f'={guard};B{row}*Regeln!$B$2)'
|
||||
|
||||
# Auszahlung_WE
|
||||
ws[f"L{row}"] = f'={guard};J{row}*Regeln!$B$3)'
|
||||
|
||||
# Auszahlung_Gesamt
|
||||
ws[f"M{row}"] = f'={guard};K{row}+L{row})'
|
||||
|
||||
widths = [22, 14, 14, 14, 14, 16, 14, 14, 14, 14, 14, 14, 16]
|
||||
for idx, width in enumerate(widths, start=1):
|
||||
ws.column_dimensions[get_column_letter(idx)].width = width
|
||||
|
||||
|
||||
def _populate_checks(ws):
|
||||
ws["A1"] = "Datum"
|
||||
ws["B1"] = "Summe_Anteile"
|
||||
ws["C1"] = "Status"
|
||||
_style_header(ws)
|
||||
|
||||
# Manual check list - user can add dates to check
|
||||
# Formula checks sum of Anteil for each date
|
||||
for row in range(2, 52):
|
||||
date_ref = f"A{row}"
|
||||
ws[f"B{row}"] = f'=WENN({date_ref}="";"";SUMMENPRODUKT((tblPlan[Datum]={date_ref})*(tblPlan[Anteil])))'
|
||||
ws[f"C{row}"] = f'=WENN({date_ref}="";""WENN(ABS(B{row}-1)<=0,0001;"OK";"FEHLER"))'
|
||||
|
||||
ws.column_dimensions["A"].width = 14
|
||||
ws.column_dimensions["B"].width = 16
|
||||
ws.column_dimensions["C"].width = 12
|
||||
|
||||
|
||||
def build_template():
|
||||
TEMPLATE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
wb = Workbook()
|
||||
|
||||
readme_ws = wb.active
|
||||
readme_ws.title = "README"
|
||||
_populate_readme(readme_ws)
|
||||
|
||||
rules_ws = wb.create_sheet("Regeln")
|
||||
_populate_rules(rules_ws)
|
||||
|
||||
holiday_ws = wb.create_sheet("Feiertage")
|
||||
_populate_holidays(holiday_ws)
|
||||
|
||||
plan_ws = wb.create_sheet("Plan")
|
||||
_populate_plan(plan_ws)
|
||||
|
||||
auswertung_ws = wb.create_sheet("Auswertung")
|
||||
_populate_auswertung(auswertung_ws)
|
||||
|
||||
checks_ws = wb.create_sheet("Checks")
|
||||
_populate_checks(checks_ws)
|
||||
|
||||
wb.save(TEMPLATE_PATH)
|
||||
return TEMPLATE_PATH
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
path = build_template()
|
||||
print(f"✅ Vorlage (Variante 2 – streng) erstellt: {path}")
|
||||
|
||||
76
src/fill_plan_dates.py
Normal file
76
src/fill_plan_dates.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
"""
|
||||
Füllt das Plan-Blatt automatisch mit allen Datumszeilen eines Monats vor.
|
||||
Nutzer muss nur noch Namen + Anteile eintragen.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import date, timedelta
|
||||
from openpyxl import load_workbook
|
||||
import sys
|
||||
|
||||
|
||||
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!")
|
||||
return
|
||||
|
||||
plan_ws = wb["Plan"]
|
||||
|
||||
# Startdatum
|
||||
start_date = date(year, month, 1)
|
||||
|
||||
# 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)
|
||||
|
||||
# Alle Tage durchgehen
|
||||
current_date = start_date
|
||||
row = 2 # Zeile 2 = erste Datenzeile nach Header
|
||||
|
||||
while current_date <= end_date:
|
||||
plan_ws[f"A{row}"] = current_date
|
||||
# Spalten B (Mitarbeiter) und C (Anteil) bleiben leer zum Ausfüllen
|
||||
current_date += timedelta(days=1)
|
||||
row += 1
|
||||
|
||||
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!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
template = Path("templates/Dienstplan_Vorlage_V2_NRW.xlsx")
|
||||
|
||||
if len(sys.argv) >= 3:
|
||||
year = int(sys.argv[1])
|
||||
month = int(sys.argv[2])
|
||||
else:
|
||||
# Standard: November 2025
|
||||
year = 2025
|
||||
month = 11
|
||||
|
||||
output = Path(f"output/Dienstplan_{year}_{month:02d}_NRW.xlsx")
|
||||
output.parent.mkdir(exist_ok=True)
|
||||
|
||||
if not template.exists():
|
||||
print(f"❌ Vorlage nicht gefunden: {template}")
|
||||
print(" Führe erst 'python src/build_template.py' aus!")
|
||||
sys.exit(1)
|
||||
|
||||
fill_plan_with_dates(template, output, year, month)
|
||||
63
src/main.py
Normal file
63
src/main.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"""
|
||||
Excel XLSX Generator
|
||||
Erstellt Excel-Dateien mit openpyxl
|
||||
"""
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment
|
||||
from pathlib import Path
|
||||
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"
|
||||
|
||||
# Ü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)
|
||||
|
||||
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"],
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
# Ausgabeverzeichnis erstellen
|
||||
output_dir = Path("output")
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Datei speichern
|
||||
output_file = output_dir / f"example_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
wb.save(output_file)
|
||||
|
||||
print(f"Excel-Datei erfolgreich erstellt: {output_file}")
|
||||
return output_file
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_example_excel()
|
||||
81
src/read_excel.py
Normal file
81
src/read_excel.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""
|
||||
Excel-Datei einlesen und Inhalt anzeigen
|
||||
"""
|
||||
|
||||
from openpyxl import load_workbook
|
||||
import json
|
||||
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)
|
||||
result = {}
|
||||
|
||||
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))
|
||||
|
||||
result[sheet_name] = data
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def print_excel_content(filepath):
|
||||
"""Gibt den Inhalt einer Excel-Datei formatiert aus."""
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Excel-Datei: {filepath}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
data = read_excel_to_dict(filepath)
|
||||
|
||||
for sheet_name, rows in data.items():
|
||||
print(f"\n📊 Sheet: {sheet_name}")
|
||||
print(f"{'-'*60}")
|
||||
|
||||
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}")
|
||||
|
||||
print(f"\n{'='*60}\n")
|
||||
|
||||
# Als JSON ausgeben
|
||||
print("📄 JSON-Format:")
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
# Datei als Argument übergeben
|
||||
filepath = sys.argv[1]
|
||||
else:
|
||||
# Nach neuester Datei im output-Ordner suchen
|
||||
output_dir = Path("output")
|
||||
excel_files = list(output_dir.glob("*.xlsx"))
|
||||
|
||||
if not excel_files:
|
||||
print("❌ Keine Excel-Dateien im output-Ordner gefunden!")
|
||||
print("Verwendung: python src/read_excel.py <pfad-zur-datei>")
|
||||
sys.exit(1)
|
||||
|
||||
# Neueste Datei verwenden
|
||||
filepath = max(excel_files, key=lambda p: p.stat().st_mtime)
|
||||
|
||||
print_excel_content(filepath)
|
||||
Reference in a new issue