refactoring: dienste in klasse
This commit is contained in:
parent
182b7d1aff
commit
2d3f49539c
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user