refactoring: dienste in klasse

This commit is contained in:
Jan Hoheisel 2025-12-23 21:44:05 +01:00
parent f0282a9aa9
commit bdc3205dab

View File

@ -13,21 +13,41 @@ from datetime import datetime, timedelta
from collections import defaultdict
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 __str__(self):
return f"{self.kuerzel} ({self.name}): {self.personen_anzahl} Person(en)"
def __repr__(self):
return f"Dienst('{self.kuerzel}', '{self.name}', {self.personen_anzahl})"
def braucht_mehrere_personen(self):
"""Gibt True zurück, wenn mehr als eine Person benötigt wird"""
return self.personen_anzahl > 1
class Elterndienstplaner:
def __init__(self):
self.dienste = ['F', 'P', 'E', 'K', 'A'] # Frühstück, Putz, Essen, Kochen, Elternabend
self.dienst_namen = {
'F': 'Frühstücksdienst',
'P': 'Putznotdienst',
'E': 'Essensausgabenotdienst',
'K': 'Kochen',
'A': 'Elternabend'
}
# Dienste als Liste definieren
self.dienste = [
Dienst('F', 'Frühstücksdienst', 1),
Dienst('P', 'Putznotdienst', 1),
Dienst('E', 'Essensausgabenotdienst', 1),
Dienst('K', 'Kochen', 1),
Dienst('A', 'Elternabend', 2)
]
# Datenstrukturen
self.tage = []
self.eltern = []
self.benoetigte_dienste = {} # {datum: [dienste]}
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}}
@ -35,6 +55,25 @@ class Elterndienstplaner:
self.vorherige_dienste = defaultdict(lambda: defaultdict(int)) # {eltern: {dienst: anzahl}}
self.historische_dienste = [] # [(datum, eltern, dienst), ...] - Alle historischen Dienste mit Datum
def get_dienst(self, kuerzel):
"""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):
"""Fügt einen neuen Dienst hinzu"""
dienst = Dienst(kuerzel, name, personen_anzahl)
self.dienste.append(dienst)
return dienst
def print_dienste_info(self):
"""Druckt Informationen über alle konfigurierten Dienste"""
print("Konfigurierte Dienste:")
for dienst in self.dienste:
print(f" {dienst}")
def lade_eingabe_csv(self, datei):
"""Lädt die eingabe.csv mit Terminen und Präferenzen"""
print(f"Lade Eingabedaten aus {datei}...")
@ -64,7 +103,12 @@ class Elterndienstplaner:
continue
# Benötigte Dienste
self.benoetigte_dienste[datum_obj] = list(dienste_str)
dienst_objekte = []
for kuerzel in dienste_str:
dienst = self.get_dienst(kuerzel)
if dienst:
dienst_objekte.append(dienst)
self.benoetigte_dienste[datum_obj] = dienst_objekte
# Verfügbarkeit und Präferenzen der Eltern
for i, eltern_name in enumerate(self.eltern):
@ -90,9 +134,9 @@ class Elterndienstplaner:
"""Parst Präferenzstring wie 'F+P-E+' """
i = 0
while i < len(präf_str):
if i + 1 < len(präf_str) and präf_str[i] in self.dienste:
dienst = präf_str[i]
if i + 1 < len(präf_str):
dienst = self.get_dienst(präf_str[i])
if dienst and i + 1 < len(präf_str):
if präf_str[i + 1] == '+':
self.präferenzen[(eltern, datum, dienst)] = 1
i += 2
@ -163,9 +207,9 @@ class Elterndienstplaner:
# Dienst-Spalten finden
dienst_spalten = {}
for i, col_name in enumerate(header[2:], 2): # Ab Spalte 2 (nach Datum, Wochentag)
for dienst_kürzel, dienst_name in self.dienst_namen.items():
if dienst_name.lower() in col_name.lower() or dienst_kürzel == col_name:
dienst_spalten[dienst_kürzel] = i
for dienst in self.dienste:
if dienst.name.lower() in col_name.lower() or dienst.kuerzel == col_name:
dienst_spalten[dienst] = i
break
for row in reader:
@ -219,7 +263,7 @@ class Elterndienstplaner:
print(f" Analysiere {len(historische_tage)} historische Tage mit {len(self.historische_dienste)} Diensten")
for dienst in self.dienste:
print(f" Verarbeite Dienst {dienst}...")
print(f" Verarbeite Dienst {dienst.kuerzel}...")
# 1. HISTORISCHE PERIODE: Faire Umverteilung der tatsächlich geleisteten Dienste
historische_dienste_dieses_typs = [
@ -227,7 +271,7 @@ class Elterndienstplaner:
if d == dienst
]
print(f" Gefundene historische {dienst}-Dienste: {len(historische_dienste_dieses_typs)}")
print(f" Gefundene historische {dienst.kuerzel}-Dienste: {len(historische_dienste_dieses_typs)}")
# Gruppiere nach Datum
dienste_pro_tag = defaultdict(list)
@ -265,8 +309,8 @@ class Elterndienstplaner:
if dienst in self.benoetigte_dienste.get(tag, [])
)
if dienst == 'A':
benoetigte_dienste_monat *= 2
# Multipliziere mit Anzahl benötigter Personen pro Dienst
benoetigte_dienste_monat *= dienst.personen_anzahl
if benoetigte_dienste_monat > 0:
# Gesamtdienstfaktor für aktuellen Monat
@ -295,7 +339,7 @@ class Elterndienstplaner:
) for e in self.eltern
) if len(historische_dienste_dieses_typs) > 0 else 0
print(f" {dienst}: Historisch faire Summe={total_historisch:.1f}, "
print(f" {dienst.kuerzel}: Historisch faire Summe={total_historisch:.1f}, "
f"Aktuell benötigt={benoetigte_dienste_monat}")
# Debug-Output: Detaillierte Zielverteilung
@ -305,7 +349,7 @@ class Elterndienstplaner:
if ziel_dienste[eltern][dienst] > 0.1: # Nur relevante Werte
ist = self.vorherige_dienste[eltern][dienst]
ziel = ziel_dienste[eltern][dienst]
print(f" {eltern} {dienst}: IST={ist}, FAIRE_ZIEL={ziel:.2f}, DIFF={ziel-ist:.2f}")
print(f" {eltern} {dienst.kuerzel}: IST={ist}, FAIRE_ZIEL={ziel:.2f}, DIFF={ziel-ist:.2f}")
return ziel_dienste
@ -330,7 +374,7 @@ class Elterndienstplaner:
for dienst in self.dienste:
if dienst in self.benoetigte_dienste.get(tag, []):
x[eltern, tag, dienst] = pulp.LpVariable(
f"x_{eltern.replace(' ', '_')}_{tag}_{dienst}",
f"x_{eltern.replace(' ', '_')}_{tag}_{dienst.kuerzel}",
cat='Binary'
)
@ -351,7 +395,7 @@ class Elterndienstplaner:
woche_vars.append(x[eltern, tag, dienst])
if woche_vars:
prob += pulp.lpSum(woche_vars) <= 1, f"C1_{eltern.replace(' ', '_')}_{dienst}_w{woche_nr}"
prob += pulp.lpSum(woche_vars) <= 1, f"C1_{eltern.replace(' ', '_')}_{dienst.kuerzel}_w{woche_nr}"
woche_start += timedelta(days=7)
woche_nr += 1
@ -373,7 +417,7 @@ class Elterndienstplaner:
if not self.verfügbarkeit.get((eltern, tag), True):
for dienst in self.dienste:
if (eltern, tag, dienst) in x:
prob += x[eltern, tag, dienst] == 0, f"C3_{eltern.replace(' ', '_')}_{tag}_{dienst}"
prob += x[eltern, tag, dienst] == 0, f"C3_{eltern.replace(' ', '_')}_{tag}_{dienst.kuerzel}"
# Alle benötigten Dienste müssen zugeteilt werden (flexibel)
for tag in self.tage:
@ -388,9 +432,9 @@ class Elterndienstplaner:
verfuegbare_eltern += 1
if dienst_vars:
# Genau 1 Person pro Dienst (außer Elternabend: genau 2)
benoetigte_personen = 2 if dienst == 'A' else 1
prob += pulp.lpSum(dienst_vars) == benoetigte_personen, f"Bedarf_{tag}_{dienst}"
# Anzahl benötigter Personen pro Dienst (aus Dienst-Objekt)
benoetigte_personen = dienst.personen_anzahl
prob += pulp.lpSum(dienst_vars) == benoetigte_personen, f"Bedarf_{tag}_{dienst.kuerzel}"
# FAIRNESS-CONSTRAINTS UND ZIELFUNKTION
objective_terms = []
@ -405,9 +449,9 @@ class Elterndienstplaner:
for eltern in self.eltern:
for dienst in self.dienste:
fairness_abweichung_lokal[eltern, dienst] = pulp.LpVariable(
f"fair_lokal_{eltern.replace(' ', '_')}_{dienst}", lowBound=0)
f"fair_lokal_{eltern.replace(' ', '_')}_{dienst.kuerzel}", lowBound=0)
fairness_abweichung_global[eltern, dienst] = pulp.LpVariable(
f"fair_global_{eltern.replace(' ', '_')}_{dienst}", lowBound=0)
f"fair_global_{eltern.replace(' ', '_')}_{dienst.kuerzel}", lowBound=0)
# F1: Globale Fairness & F2: Lokale Fairness
for eltern in self.eltern:
@ -430,8 +474,8 @@ class Elterndienstplaner:
1 for tag in self.tage
if dienst in self.benoetigte_dienste.get(tag, [])
)
if dienst == 'A':
benoetigte_dienste_monat *= 2
# Multipliziere mit Anzahl benötigter Personen pro Dienst
benoetigte_dienste_monat *= dienst.personen_anzahl
gesamt_dienstfaktor_monat = sum(
sum(self.dienstfaktoren.get(e, {}).get(tag, 0) for tag in self.tage)
@ -549,7 +593,7 @@ class Elterndienstplaner:
writer = csv.writer(f)
# Header schreiben
header = ['Datum', 'Wochentag'] + [self.dienst_namen[d] for d in self.dienste]
header = ['Datum', 'Wochentag'] + [dienst.name for dienst in self.dienste]
writer.writerow(header)
# Daten schreiben
@ -586,8 +630,8 @@ class Elterndienstplaner:
print("\nDienste pro Eltern:")
for eltern in sorted(self.eltern):
gesamt = sum(dienste_pro_eltern[eltern].values())
dienste_detail = ', '.join(f"{d}:{dienste_pro_eltern[eltern][d]}"
for d in self.dienste if dienste_pro_eltern[eltern][d] > 0)
dienste_detail = ', '.join(f"{dienst.kuerzel}:{dienste_pro_eltern[eltern][dienst]}"
for dienst in self.dienste if dienste_pro_eltern[eltern][dienst] > 0)
print(f" {eltern:15} {gesamt:3d} ({dienste_detail})")
# Dienstfaktor-Analyse