refactoring: lokale fairness
This commit is contained in:
parent
03d1c362f1
commit
9588e75ee0
@ -252,7 +252,7 @@ class Elterndienstplaner:
|
|||||||
return faktor
|
return faktor
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def berechne_faire_zielverteilung(self) -> DefaultDict[str, DefaultDict[Dienst, float]]:
|
def berechne_faire_zielverteilung_global(self) -> DefaultDict[str, DefaultDict[Dienst, float]]:
|
||||||
"""Berechnet die faire Zielanzahl von Diensten pro Eltern-Dienst-Kombination
|
"""Berechnet die faire Zielanzahl von Diensten pro Eltern-Dienst-Kombination
|
||||||
basierend auf tatsächlich geleisteten historischen Diensten und deren fairer Umverteilung"""
|
basierend auf tatsächlich geleisteten historischen Diensten und deren fairer Umverteilung"""
|
||||||
|
|
||||||
@ -356,6 +356,55 @@ class Elterndienstplaner:
|
|||||||
|
|
||||||
return ziel_dienste
|
return ziel_dienste
|
||||||
|
|
||||||
|
def berechne_faire_zielverteilung_lokal(self) -> DefaultDict[str, DefaultDict[Dienst, float]]:
|
||||||
|
"""Berechnet die lokale faire Zielanzahl von Diensten pro Eltern-Dienst-Kombination
|
||||||
|
basierend auf Dienstfaktoren und benötigten Diensten im aktuellen Planungsmonat"""
|
||||||
|
|
||||||
|
ziel_dienste_lokal: DefaultDict[str, DefaultDict[Dienst, float]] = \
|
||||||
|
defaultdict(lambda: defaultdict(float))
|
||||||
|
|
||||||
|
print("\nBerechne lokale faire Zielverteilung für aktuellen Monat...")
|
||||||
|
|
||||||
|
# Gesamtdienstfaktor für aktuellen Monat berechnen
|
||||||
|
gesamt_dienstfaktor_monat = sum(
|
||||||
|
sum(self.dienstfaktoren.get(e, {}).get(tag, 0) for tag in self.tage)
|
||||||
|
for e in self.eltern
|
||||||
|
)
|
||||||
|
|
||||||
|
if gesamt_dienstfaktor_monat == 0:
|
||||||
|
print(" WARNUNG: Gesamtdienstfaktor ist 0, keine lokale Zielverteilung möglich")
|
||||||
|
return ziel_dienste_lokal
|
||||||
|
|
||||||
|
# Für jeden Dienst die lokale faire Verteilung berechnen
|
||||||
|
for dienst in self.dienste:
|
||||||
|
# Anzahl benötigter Dienste im aktuellen Monat
|
||||||
|
benoetigte_dienste_monat = sum(
|
||||||
|
1 for tag in self.tage
|
||||||
|
if dienst in self.benoetigte_dienste.get(tag, [])
|
||||||
|
)
|
||||||
|
# Multipliziere mit Anzahl benötigter Personen pro Dienst
|
||||||
|
benoetigte_dienste_monat *= dienst.personen_anzahl
|
||||||
|
|
||||||
|
if benoetigte_dienste_monat > 0:
|
||||||
|
print(f" {dienst.kuerzel}: {benoetigte_dienste_monat} Dienste benötigt")
|
||||||
|
|
||||||
|
for eltern in self.eltern:
|
||||||
|
# Dienstfaktor für diesen Elternteil im aktuellen Monat
|
||||||
|
monatlicher_dienstfaktor = sum(
|
||||||
|
self.dienstfaktoren.get(eltern, {}).get(tag, 0) for tag in self.tage
|
||||||
|
)
|
||||||
|
|
||||||
|
if monatlicher_dienstfaktor > 0:
|
||||||
|
anteil = monatlicher_dienstfaktor / gesamt_dienstfaktor_monat
|
||||||
|
faire_zuteilung = anteil * benoetigte_dienste_monat
|
||||||
|
ziel_dienste_lokal[eltern][dienst] = faire_zuteilung
|
||||||
|
|
||||||
|
if faire_zuteilung > 0.1: # Debug nur für relevante Werte
|
||||||
|
print(f" {eltern}: Faktor={monatlicher_dienstfaktor:.1f} "
|
||||||
|
f"-> {faire_zuteilung:.2f} Dienste")
|
||||||
|
|
||||||
|
return ziel_dienste_lokal
|
||||||
|
|
||||||
def erstelle_optimierungsmodell(self) -> Tuple[pulp.LpProblem, Dict[Tuple[str, date, Dienst], pulp.LpVariable]]:
|
def erstelle_optimierungsmodell(self) -> Tuple[pulp.LpProblem, Dict[Tuple[str, date, Dienst], pulp.LpVariable]]:
|
||||||
"""Erstellt das PuLP Optimierungsmodell"""
|
"""Erstellt das PuLP Optimierungsmodell"""
|
||||||
print("Erstelle Optimierungsmodell...")
|
print("Erstelle Optimierungsmodell...")
|
||||||
@ -442,8 +491,9 @@ class Elterndienstplaner:
|
|||||||
# FAIRNESS-CONSTRAINTS UND ZIELFUNKTION
|
# FAIRNESS-CONSTRAINTS UND ZIELFUNKTION
|
||||||
objective_terms = []
|
objective_terms = []
|
||||||
|
|
||||||
# Berechne faire Zielverteilung
|
# Berechne faire Zielverteilungen (global und lokal)
|
||||||
ziel_dienste = self.berechne_faire_zielverteilung()
|
ziel_dienste_global = self.berechne_faire_zielverteilung_global()
|
||||||
|
ziel_dienste_lokal = self.berechne_faire_zielverteilung_lokal()
|
||||||
|
|
||||||
# Hilfsvariablen für Fairness-Abweichungen
|
# Hilfsvariablen für Fairness-Abweichungen
|
||||||
fairness_abweichung_lokal = {} # F2
|
fairness_abweichung_lokal = {} # F2
|
||||||
@ -459,56 +509,35 @@ class Elterndienstplaner:
|
|||||||
# F1: Globale Fairness & F2: Lokale Fairness
|
# F1: Globale Fairness & F2: Lokale Fairness
|
||||||
for eltern in self.eltern:
|
for eltern in self.eltern:
|
||||||
for dienst in self.dienste:
|
for dienst in self.dienste:
|
||||||
# Dienstfaktor für aktuellen Monat
|
# Tatsächliche Dienste im aktuellen Monat
|
||||||
monatlicher_dienstfaktor = sum(
|
tatsaechliche_dienste_monat = pulp.lpSum(
|
||||||
self.dienstfaktoren.get(eltern, {}).get(tag, 0) for tag in self.tage
|
x[eltern, tag, dienst]
|
||||||
|
for tag in self.tage
|
||||||
|
if (eltern, tag, dienst) in x
|
||||||
)
|
)
|
||||||
|
|
||||||
if monatlicher_dienstfaktor > 0:
|
# F2: Lokale Fairness - nur aktueller Monat
|
||||||
# Tatsächliche Dienste im aktuellen Monat
|
ziel_lokal = ziel_dienste_lokal[eltern][dienst]
|
||||||
tatsaechliche_dienste_monat = pulp.lpSum(
|
if ziel_lokal > 0:
|
||||||
x[eltern, tag, dienst]
|
# Lokale Fairness-Constraints
|
||||||
for tag in self.tage
|
prob += (tatsaechliche_dienste_monat - ziel_lokal <=
|
||||||
if (eltern, tag, dienst) in x
|
fairness_abweichung_lokal[eltern, dienst])
|
||||||
)
|
prob += (ziel_lokal - tatsaechliche_dienste_monat <=
|
||||||
|
fairness_abweichung_lokal[eltern, dienst])
|
||||||
|
|
||||||
# F2: Lokale Fairness - nur aktueller Monat
|
# F1: Globale Fairness - basierend auf berechneter Zielverteilung
|
||||||
benoetigte_dienste_monat = sum(
|
ziel_global = ziel_dienste_global[eltern][dienst]
|
||||||
1 for tag in self.tage
|
vorherige_dienste = self.vorherige_dienste[eltern][dienst]
|
||||||
if dienst in self.benoetigte_dienste.get(tag, [])
|
|
||||||
)
|
|
||||||
# Multipliziere mit Anzahl benötigter Personen pro Dienst
|
|
||||||
benoetigte_dienste_monat *= dienst.personen_anzahl
|
|
||||||
|
|
||||||
gesamt_dienstfaktor_monat = sum(
|
if ziel_global > 0:
|
||||||
sum(self.dienstfaktoren.get(e, {}).get(tag, 0) for tag in self.tage)
|
# Tatsächliche Dienste global (Vergangenheit + geplant)
|
||||||
for e in self.eltern
|
total_dienste_inkl_vergangenheit = tatsaechliche_dienste_monat + vorherige_dienste
|
||||||
)
|
|
||||||
|
|
||||||
if gesamt_dienstfaktor_monat > 0 and benoetigte_dienste_monat > 0:
|
# Globale Fairness-Constraints
|
||||||
erwartete_dienste_lokal = (
|
prob += (total_dienste_inkl_vergangenheit - ziel_global <=
|
||||||
monatlicher_dienstfaktor / gesamt_dienstfaktor_monat
|
fairness_abweichung_global[eltern, dienst])
|
||||||
) * benoetigte_dienste_monat
|
prob += (ziel_global - total_dienste_inkl_vergangenheit <=
|
||||||
|
fairness_abweichung_global[eltern, dienst])
|
||||||
# F2: Lokale Fairness-Constraints
|
|
||||||
prob += (tatsaechliche_dienste_monat - erwartete_dienste_lokal <=
|
|
||||||
fairness_abweichung_lokal[eltern, dienst])
|
|
||||||
prob += (erwartete_dienste_lokal - tatsaechliche_dienste_monat <=
|
|
||||||
fairness_abweichung_lokal[eltern, dienst])
|
|
||||||
|
|
||||||
# F1: Globale Fairness - basierend auf berechneter Zielverteilung
|
|
||||||
ziel_gesamt = ziel_dienste[eltern][dienst]
|
|
||||||
vorherige_dienste = self.vorherige_dienste[eltern][dienst]
|
|
||||||
|
|
||||||
if ziel_gesamt > 0:
|
|
||||||
# Tatsächliche Dienste global (Vergangenheit + geplant)
|
|
||||||
total_dienste_inkl_vergangenheit = tatsaechliche_dienste_monat + vorherige_dienste
|
|
||||||
|
|
||||||
# F1: Globale Fairness-Constraints
|
|
||||||
prob += (total_dienste_inkl_vergangenheit - ziel_gesamt <=
|
|
||||||
fairness_abweichung_global[eltern, dienst])
|
|
||||||
prob += (ziel_gesamt - total_dienste_inkl_vergangenheit <=
|
|
||||||
fairness_abweichung_global[eltern, dienst])
|
|
||||||
|
|
||||||
# Gewichtung: Jahresanfang F1 stärker, Jahresende F2 stärker
|
# Gewichtung: Jahresanfang F1 stärker, Jahresende F2 stärker
|
||||||
# Annahme: September = Jahresanfang, Juli = Jahresende
|
# Annahme: September = Jahresanfang, Juli = Jahresende
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user