refactoring: type hints

This commit is contained in:
Jan Hoheisel 2025-12-23 22:00:29 +01:00
parent 2d3f49539c
commit 03d1c362f1

View File

@ -9,34 +9,35 @@ Datum: Dezember 2025
import sys
import csv
import pulp
from datetime import datetime, timedelta
from datetime import datetime, timedelta, date
from collections import defaultdict
from typing import Dict, List, Tuple, DefaultDict, Optional
import calendar
class Dienst:
"""Repräsentiert einen Diensttyp mit allen seinen Eigenschaften"""
def __init__(self, kuerzel, name, personen_anzahl=1):
self.kuerzel = kuerzel
self.name = name
self.personen_anzahl = personen_anzahl
def __init__(self, kuerzel: str, name: str, personen_anzahl: int = 1) -> None:
self.kuerzel: str = kuerzel
self.name: str = name
self.personen_anzahl: int = personen_anzahl
def __str__(self):
def __str__(self) -> str:
return f"{self.kuerzel} ({self.name}): {self.personen_anzahl} Person(en)"
def __repr__(self):
def __repr__(self) -> str:
return f"Dienst('{self.kuerzel}', '{self.name}', {self.personen_anzahl})"
def braucht_mehrere_personen(self):
def braucht_mehrere_personen(self) -> bool:
"""Gibt True zurück, wenn mehr als eine Person benötigt wird"""
return self.personen_anzahl > 1
class Elterndienstplaner:
def __init__(self):
def __init__(self) -> None:
# Dienste als Liste definieren
self.dienste = [
self.dienste: List[Dienst] = [
Dienst('F', 'Frühstücksdienst', 1),
Dienst('P', 'Putznotdienst', 1),
Dienst('E', 'Essensausgabenotdienst', 1),
@ -45,36 +46,37 @@ class Elterndienstplaner:
]
# Datenstrukturen
self.tage = []
self.eltern = []
self.benoetigte_dienste = {} # {datum: [dienst_objekte]}
self.verfügbarkeit = {} # {(eltern, datum): bool}
self.präferenzen = {} # {(eltern, datum, dienst): 1 (bevorzugt) oder -1 (abgelehnt)}
self.dienstfaktoren = {} # {eltern: {datum: faktor}}
self.alle_zeitraeume = {} # {eltern: [(beginn, ende, faktor), ...]} - ALLE Zeiträume
self.vorherige_dienste = defaultdict(lambda: defaultdict(int)) # {eltern: {dienst: anzahl}}
self.historische_dienste = [] # [(datum, eltern, dienst), ...] - Alle historischen Dienste mit Datum
self.tage: List[date] = []
self.eltern: List[str] = []
self.benoetigte_dienste: Dict[date, List[Dienst]] = {}
self.verfügbarkeit: Dict[Tuple[str, date], bool] = {}
self.präferenzen: Dict[Tuple[str, date, Dienst], int] = {}
self.dienstfaktoren: Dict[str, Dict[date, float]] = {}
self.alle_zeitraeume: Dict[str, List[Tuple[date, date, float]]] = {}
self.vorherige_dienste: DefaultDict[str, DefaultDict[Dienst, int]] = \
defaultdict(lambda: defaultdict(int))
self.historische_dienste: List[Tuple[date, str, Dienst]] = []
def get_dienst(self, kuerzel):
def get_dienst(self, kuerzel: str) -> Optional[Dienst]:
"""Gibt das Dienst-Objekt für ein Kürzel zurück"""
for dienst in self.dienste:
if dienst.kuerzel == kuerzel:
return dienst
return None
def add_dienst(self, kuerzel, name, personen_anzahl=1):
def add_dienst(self, kuerzel: str, name: str, personen_anzahl: int = 1) -> Dienst:
"""Fügt einen neuen Dienst hinzu"""
dienst = Dienst(kuerzel, name, personen_anzahl)
self.dienste.append(dienst)
return dienst
def print_dienste_info(self):
def print_dienste_info(self) -> None:
"""Druckt Informationen über alle konfigurierten Dienste"""
print("Konfigurierte Dienste:")
for dienst in self.dienste:
print(f" {dienst}")
def lade_eingabe_csv(self, datei):
def lade_eingabe_csv(self, datei: str) -> None:
"""Lädt die eingabe.csv mit Terminen und Präferenzen"""
print(f"Lade Eingabedaten aus {datei}...")
@ -130,7 +132,7 @@ class Elterndienstplaner:
self.tage.sort()
print(f"Zeitraum: {self.tage[0]} bis {self.tage[-1]} ({len(self.tage)} Tage)")
def _parse_präferenzen(self, eltern, datum, präf_str):
def _parse_präferenzen(self, eltern: str, datum: date, präf_str: str) -> None:
"""Parst Präferenzstring wie 'F+P-E+' """
i = 0
while i < len(präf_str):
@ -150,7 +152,7 @@ class Elterndienstplaner:
else:
i += 1
def lade_eltern_csv(self, datei):
def lade_eltern_csv(self, datei: str) -> None:
"""Lädt die eltern.csv mit Dienstfaktoren"""
print(f"Lade Elterndaten aus {datei}...")
@ -195,7 +197,7 @@ class Elterndienstplaner:
print(f"Dienstfaktoren geladen für {len(self.dienstfaktoren)} Eltern")
print(f"Zeiträume gespeichert für globale Fairness-Berechnung")
def lade_vorherige_ausgaben_csv(self, datei):
def lade_vorherige_ausgaben_csv(self, datei: str) -> None:
"""Lädt vorherige-ausgaben.csv für Fairness-Constraints"""
print(f"Lade vorherige Ausgaben aus {datei}...")
@ -240,7 +242,7 @@ class Elterndienstplaner:
print(f"Vorherige Dienste geladen: {dict(self.vorherige_dienste)}")
print(f"Historische Dienste mit Datum: {len(self.historische_dienste)} Einträge")
def berechne_dienstfaktor_an_datum(self, eltern, datum):
def berechne_dienstfaktor_an_datum(self, eltern: str, datum: date) -> float:
"""Berechnet den Dienstfaktor eines Elternteils an einem bestimmten Datum"""
if eltern not in self.alle_zeitraeume:
return 0
@ -250,11 +252,12 @@ class Elterndienstplaner:
return faktor
return 0
def berechne_faire_zielverteilung(self):
def berechne_faire_zielverteilung(self) -> DefaultDict[str, DefaultDict[Dienst, float]]:
"""Berechnet die faire Zielanzahl von Diensten pro Eltern-Dienst-Kombination
basierend auf tatsächlich geleisteten historischen Diensten und deren fairer Umverteilung"""
ziel_dienste = defaultdict(lambda: defaultdict(float)) # {eltern: {dienst: anzahl}}
ziel_dienste: DefaultDict[str, DefaultDict[Dienst, float]] = \
defaultdict(lambda: defaultdict(float))
print("\nBerechne faire Zielverteilung basierend auf historischen Daten...")
@ -353,7 +356,7 @@ class Elterndienstplaner:
return ziel_dienste
def erstelle_optimierungsmodell(self):
def erstelle_optimierungsmodell(self) -> Tuple[pulp.LpProblem, Dict[Tuple[str, date, Dienst], pulp.LpVariable]]:
"""Erstellt das PuLP Optimierungsmodell"""
print("Erstelle Optimierungsmodell...")
@ -368,7 +371,7 @@ class Elterndienstplaner:
prob = pulp.LpProblem("Elterndienstplaner", pulp.LpMinimize)
# Entscheidungsvariablen: x[eltern, tag, dienst] ∈ {0,1}
x = {}
x: Dict[Tuple[str, date, Dienst], pulp.LpVariable] = {}
for eltern in self.eltern:
for tag in self.tage:
for dienst in self.dienste:
@ -547,7 +550,8 @@ class Elterndienstplaner:
print(f"Modell erstellt mit {len(x)} Variablen und {len(prob.constraints)} Constraints")
return prob, x
def löse_optimierung(self, prob, x):
def löse_optimierung(self, prob: pulp.LpProblem,
x: Dict[Tuple[str, date, Dienst], pulp.LpVariable]) -> Optional[Dict[date, Dict[Dienst, List[str]]]]:
"""Löst das Optimierungsproblem"""
print("Löse Optimierungsproblem...")
@ -574,7 +578,7 @@ class Elterndienstplaner:
return None
# Lösung extrahieren
lösung = {}
lösung: Dict[date, Dict[Dienst, List[str]]] = {}
for (eltern, tag, dienst), var in x.items():
if var.varValue and var.varValue > 0.5: # Binary variable ist 1
if tag not in lösung:
@ -585,7 +589,7 @@ class Elterndienstplaner:
return lösung
def schreibe_ausgabe_csv(self, datei, lösung):
def schreibe_ausgabe_csv(self, datei: str, lösung: Dict[date, Dict[Dienst, List[str]]]) -> None:
"""Schreibt die Lösung in die ausgabe.csv"""
print(f"Schreibe Ergebnisse nach {datei}...")
@ -613,7 +617,7 @@ class Elterndienstplaner:
print("Ausgabe erfolgreich geschrieben!")
def drucke_statistiken(self, lösung):
def drucke_statistiken(self, lösung: Dict[date, Dict[Dienst, List[str]]]) -> None:
"""Druckt Statistiken zur Lösung"""
print("\n" + "="*50)
print("STATISTIKEN")
@ -641,7 +645,7 @@ class Elterndienstplaner:
print(f" {eltern:15} {faktor_summe:.1f}")
def main():
def main() -> None:
if len(sys.argv) < 4:
print("Usage: ./elterndienstplaner.py <eingabe.csv> <eltern.csv> <ausgabe.csv> [<vorherige-ausgaben.csv>]")
sys.exit(1)