refactoring: type hints
This commit is contained in:
parent
2d3f49539c
commit
03d1c362f1
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user