From b524edc2ba2760d9af8d5adecd2424172627bfd9 Mon Sep 17 00:00:00 2001 From: Jan Hoheisel Date: Sun, 25 Jan 2026 22:07:39 +0100 Subject: [PATCH] Tabellarische Debugausgabe Gibt die Anzahl zugeteilter Dienste (historisch + neu) nach Dienstart und Eltern aus, sowie abweichung zum Globalen Ziel --- ausgabe.py | 96 +++++++++++++++++++++++++++++++++++++++++++ elterndienstplaner.py | 9 ++-- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/ausgabe.py b/ausgabe.py index 2195695..6bfae53 100644 --- a/ausgabe.py +++ b/ausgabe.py @@ -249,3 +249,99 @@ class ElterndienstAusgabe: print(f"Maximale Abweichung von Global-Ziel: {max_abw_global:.2f} Dienste") print(f"Maximale Abweichung von Lokal-Ziel: {max_abw_lokal:.2f} Dienste") print("\nLegende: Δ = Tatsächlich - Ziel (positiv = mehr als Ziel, negativ = weniger als Ziel)") + + def visualisiere_dienste_uebersicht( + self, + lösung: Dict[date, Dict[Dienst, List[Eltern]]] + ) -> None: + """Visualisiert die Übersicht der zugeteilten Dienste nach Optimierung + + Zeigt für jede Familie und jeden Diensttyp: + - Anzahl Dienste nach Optimierung (Historie + Planungszeitraum) + - Differenz zum globalen Ziel (Historie + Planungszeitraum) + + Args: + lösung: Die Lösung der Optimierung + """ + if self.ziel_global is None: + print("FEHLER: Globale Zielverteilung wurde nicht gesetzt!") + return + + # Berechne historische Dienste pro Eltern und Dienst + historisch = defaultdict(lambda: defaultdict(int)) + for datum, eltern, dienst in self.daten.historische_dienste: + historisch[eltern][dienst] += 1 + + # Berechne geplante Dienste (aus Lösung) + geplant = defaultdict(lambda: defaultdict(int)) + for tag_dienste in lösung.values(): + for dienst, eltern_liste in tag_dienste.items(): + for eltern in eltern_liste: + geplant[eltern][dienst] += 1 + + # Berechne globale Ziele für jeden Elternteil und Dienst + # Das globale Ziel ist: faire Verteilung über (Historie + Planungszeitraum) MINUS bereits geleistete Historie + # Also: ziel_global[eltern][dienst] ist die SOLL-Änderung im Planungszeitraum + # Tatsächliches Gesamt-Ziel = historisch[eltern][dienst] + ziel_global[eltern][dienst] + + print("\n" + "="*120) + print("ÜBERSICHT: Dienst nach der Optimierung") + print("="*120) + + + # Tabelle: NACH der Optimierung (historisch + geplant) + print("\n>>> NACH OPTIMIERUNG (historische Dienste + Planungszeitraum) <<<\n") + + # Header + print(f"{'Eltern':<20} ", end='') + for dienst in self.daten.dienste: + print(f"{dienst.kuerzel:>14} ", end='') + print(f"{'GESAMT':>14}") + print(f"{'':20} ", end='') + for dienst in self.daten.dienste: + print(f"{'Ist / Δ Ziel':>14} ", end='') + print(f"{'Ist / Δ Ziel':>14}") + print("-" * 120) + + # Datenzeilen + for eltern in sorted(self.daten.eltern): + print(f"{eltern:<20} ", end='') + + gesamt_ist = 0 + gesamt_ziel = 0 + + for dienst in self.daten.dienste: + ist_dienste = historisch[eltern][dienst] + geplant[eltern][dienst] + gesamt_ist += ist_dienste + + # Globales Ziel = historisch + ziel_global (das ist das faire Gesamt-Ziel) + ziel_gesamt = historisch[eltern][dienst] + self.ziel_global[eltern][dienst] + gesamt_ziel += ziel_gesamt + + delta = ist_dienste - ziel_gesamt + + # Farbcodierung + farbe = "" + reset = "" + if abs(delta) > 0.5: + farbe = "\033[93m" if abs(delta) <= 1.5 else "\033[91m" + reset = "\033[0m" + + print(f"{farbe}{ist_dienste:>6} / {delta:>+5.1f}{reset} ", end='') + + # Gesamt-Spalte + delta_gesamt = gesamt_ist - gesamt_ziel + farbe = "" + reset = "" + if abs(delta_gesamt) > 0.5: + farbe = "\033[93m" if abs(delta_gesamt) <= 1.5 else "\033[91m" + reset = "\033[0m" + + print(f"{farbe}{gesamt_ist:>6} / {delta_gesamt:>+5.1f}{reset}") + + print() + print("Legende:") + print(" Ist = Anzahl tatsächlich geleisteter Dienste") + print(" Δ Ziel = Differenz zum globalen fairen Ziel (positiv = mehr als fair, negativ = weniger)") + print(" \033[93mGelb\033[0m = Abweichung 0.5 - 1.5 Dienste") + print(" \033[91mRot\033[0m = Abweichung > 1.5 Dienste") diff --git a/elterndienstplaner.py b/elterndienstplaner.py index 107c518..ba09626 100755 --- a/elterndienstplaner.py +++ b/elterndienstplaner.py @@ -68,9 +68,9 @@ class Elterndienstplaner: faire_zuteilung = anteil * anzahl_dienste ziel_dienste[eltern][dienst] += faire_zuteilung - if faire_zuteilung > 0.01: - print(f" {tag}: {eltern} Faktor={self.daten.dienstfaktoren[eltern][tag]} " - f"-> {faire_zuteilung:.2f} von {anzahl_dienste} Diensten") + #if faire_zuteilung > 0.01: + # print(f" {tag}: {eltern} Faktor={self.daten.dienstfaktoren[eltern][tag]} " + # f"-> {faire_zuteilung:.2f} von {anzahl_dienste} Diensten") # 2. AKTUELLER PLANUNGSZEITRAUM: Faire Verteilung benoetigte_dienste_planungszeitraum = 0 @@ -467,7 +467,7 @@ class Elterndienstplaner: solver = None try: print("Versuche CBC Solver...") - solver = pulp.PULP_CBC_CMD(msg=0, timeLimit=10) + solver = pulp.PULP_CBC_CMD(msg=0, timeLimit=60) except: try: print("Versuche GLPK Solver...") @@ -523,6 +523,7 @@ def main() -> None: if loesung is not None: ausgabe.schreibe_ausgabe_csv(ausgabe_datei, loesung) ausgabe.drucke_statistiken(loesung) + ausgabe.visualisiere_dienste_uebersicht(loesung) ausgabe.visualisiere_verteilungen(loesung) ausgabe.visualisiere_praeferenz_verletzungen(loesung)