From 33b8a0047c027a4a01959bfe5abbd07735cc9967 Mon Sep 17 00:00:00 2001 From: Jan Hoheisel Date: Wed, 24 Dec 2025 23:56:11 +0100 Subject: [PATCH] praeferenz-statistik und schonfrist --- elterndienstplaner.py | 115 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/elterndienstplaner.py b/elterndienstplaner.py index 2b617f3..132c179 100755 --- a/elterndienstplaner.py +++ b/elterndienstplaner.py @@ -479,8 +479,8 @@ class Elterndienstplaner: objective_terms = [] # Fairness-Gewichtung - gewicht_global = 10 - gewicht_lokal = 50 + gewicht_global = 40 + gewicht_lokal = 60 gewicht_f1 = gewicht_global gewicht_f2 = gewicht_lokal gewicht_f3_global = 0.25 * gewicht_global @@ -649,6 +649,114 @@ class Elterndienstplaner: faktor_summe = sum(self.dienstfaktoren[eltern][tag] for tag in self.tage) print(f" {eltern:15} {faktor_summe:.1f}") + def visualisiere_praeferenz_verletzungen( + self, + lösung: Dict[date, Dict[Dienst, List[str]]] + ) -> None: + """Visualisiert verletzte Präferenzen als Tabelle + + Args: + lösung: Die tatsächliche Lösung nach Optimierung + """ + print("\n" + "="*110) + print("PRÄFERENZ-VERLETZUNGEN") + print("="*110) + + # Sammle alle zugeteilten Dienste pro Eltern + zugeteilte_dienste = defaultdict(lambda: defaultdict(list)) # eltern -> dienst -> [dates] + for tag, tag_dienste in lösung.items(): + for dienst, eltern_liste in tag_dienste.items(): + for eltern in eltern_liste: + zugeteilte_dienste[eltern][dienst].append(tag) + + # Sammle Präferenzen strukturiert + # praeferenzen_pro_eltern_dienst[eltern][dienst] = {datum: präf_wert} + praeferenzen_pro_eltern_dienst = defaultdict(lambda: defaultdict(dict)) + for (eltern, tag, dienst), präf in self.präferenzen.items(): + praeferenzen_pro_eltern_dienst[eltern][dienst][tag] = präf + + # Berechne Verletzungen + verletzungen = defaultdict(lambda: defaultdict(lambda: {'negativ': 0, 'positiv_nicht_erfuellt': 0})) + + for eltern in sorted(self.eltern): + for dienst in self.dienste: + zugeteilte_tage = zugeteilte_dienste[eltern][dienst] + praeferenzen_dienst = praeferenzen_pro_eltern_dienst[eltern][dienst] + + if not zugeteilte_tage: + continue # Keine Dienste zugeteilt + + # a) Negative Präferenzen die verletzt wurden + for tag in zugeteilte_tage: + if tag in praeferenzen_dienst and praeferenzen_dienst[tag] == -1: + verletzungen[eltern][dienst]['negativ'] += 1 + + # b) Positive Präferenzen nicht erfüllt (Dienst an nicht-präferiertem Tag) + # Sammle alle Tage mit positiver Präferenz für diesen Dienst + positive_praef_tage = {tag for tag, präf in praeferenzen_dienst.items() if präf == 1} + + if positive_praef_tage: # Es gibt positive Präferenzen + # Prüfe ob ALLE zugeteilten Dienste an nicht-präferierten Tagen sind + for tag in zugeteilte_tage: + if tag not in positive_praef_tage: + # Dienst wurde an nicht-präferiertem Tag zugeteilt + verletzungen[eltern][dienst]['positiv_nicht_erfuellt'] += 1 + + # Tabelle ausgeben + print(f"\n{'Eltern':<20} ", end='') + for dienst in self.dienste: + print(f"{dienst.kuerzel:>12}", end='') + print() + print(f"{'':20} ", end='') + for dienst in self.dienste: + print(f"{'neg, pos':>12}", end='') + print() + print("-" * (20 + 12 * len(self.dienste))) + + gesamt_negativ = defaultdict(int) + gesamt_positiv = defaultdict(int) + + for eltern in sorted(self.eltern): + print(f"{eltern:<20} ", end='') + for dienst in self.dienste: + neg = verletzungen[eltern][dienst]['negativ'] + pos = verletzungen[eltern][dienst]['positiv_nicht_erfuellt'] + + gesamt_negativ[dienst] += neg + gesamt_positiv[dienst] += pos + + # Farbcodierung + farbe = "" + reset = "" + if neg > 0 or pos > 0: + farbe = "\033[91m" if neg > 0 else "\033[93m" # Rot für negativ, Gelb für positiv + reset = "\033[0m" + + print(f"{farbe}{neg:>3}, {pos:>3}{reset:>6}", end='') + print() + + # Summenzeile + print("-" * (20 + 12 * len(self.dienste))) + print(f"{'SUMME':<20} ", end='') + for dienst in self.dienste: + neg = gesamt_negativ[dienst] + pos = gesamt_positiv[dienst] + + farbe = "" + reset = "" + if neg > 0 or pos > 0: + farbe = "\033[91m" if neg > 0 else "\033[93m" + reset = "\033[0m" + + print(f"{farbe}{neg:>3}, {pos:>3}{reset:>6}", end='') + print() + + print("\nLegende:") + print(" neg = Anzahl negativer Präferenzen (abgelehnte Tage), die verletzt wurden") + print(" pos = Anzahl Dienste an nicht-präferierten Tagen (obwohl präferierte Tage angegeben waren)") + print(" \033[91mRot\033[0m = Negative Präferenz verletzt") + print(" \033[93mGelb\033[0m = Positive Präferenz nicht erfüllt") + def visualisiere_verteilungen( self, lösung: Dict[date, Dict[Dienst, List[str]]], @@ -765,6 +873,9 @@ def main() -> None: # Visualisierung der Verteilungen planer.visualisiere_verteilungen(lösung, ziel_lokal, ziel_global) + # Visualisierung der Präferenz-Verletzungen + planer.visualisiere_praeferenz_verletzungen(lösung) + print("\n✓ Planung erfolgreich abgeschlossen!") else: print("\n✗ Fehler: Keine gültige Lösung gefunden!")