elterndienstplaner/datenmodell.py
2025-12-25 21:43:29 +01:00

115 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""
Datenmodell für Elterndienstplaner
Enthält alle Daten und deren Laden sowie gemeinsame Type Aliases
"""
from datetime import date
from collections import defaultdict
from typing import Dict, List, Tuple, DefaultDict, Optional, TypeAlias
import pulp
from csv_io import EingabeParser
class Dienst:
"""Repräsentiert einen Diensttyp mit allen seinen Eigenschaften"""
def __init__(self, kuerzel: str, name: str, personen_anzahl: int = 1) -> None:
self.kuerzel: str = kuerzel
self.name: str = name
self.personen_anzahl: int = personen_anzahl
def __str__(self) -> str:
return f"{self.kuerzel} ({self.name}): {self.personen_anzahl} Person(en)"
def __repr__(self) -> str:
return f"Dienst('{self.kuerzel}', '{self.name}', {self.personen_anzahl})"
def braucht_mehrere_personen(self) -> bool:
"""Gibt True zurück, wenn mehr als eine Person benötigt wird"""
return self.personen_anzahl > 1
# Eltern: Ihnen koennen Dienste zugewiesen werden
Eltern: TypeAlias = str
# Verteilung von Diensten auf Eltern.
# - Zielsumme der Dienste ueber den Planungszeitraum
# - Kann nicht-ganzzahlig und negativ sein
Zielverteilung: TypeAlias = DefaultDict[Eltern, DefaultDict[Dienst, float]]
# Entscheidungsvariablen des Optimierungsproblems
# Variable: Eltern wird am Datum Dienst zugewiesen
Entscheidungsvariablen: TypeAlias = Dict[Tuple[Eltern, date, Dienst], pulp.LpVariable]
class ElterndienstplanerDaten:
"""Datenmodell für den Elterndienstplaner"""
def __init__(self) -> None:
# Dienste als Liste definieren
self.dienste: List[Dienst] = [
Dienst('F', 'Frühstücksdienst', 1),
Dienst('P', 'Putznotdienst', 1),
Dienst('E', 'Essensausgabenotdienst', 1),
Dienst('K', 'Kochen', 1),
Dienst('A', 'Elternabend', 2)
]
# Datenstrukturen
self.planungszeitraum: List[date] = []
self.eltern: List[Eltern] = []
self.benoetigte_dienste: Dict[date, List[Dienst]] = {}
self.verfügbarkeit: Dict[Tuple[Eltern, date], bool] = {}
self.präferenzen: Dict[Tuple[Eltern, date, Dienst], int] = {}
# dienstfaktoren[eltern][tag] = faktor.
# Wenn es eltern nicht gibt -> keyerror
# Wenn es tag nicht gibt -> default 0.0
self.dienstfaktoren: Dict[Eltern, DefaultDict[date, float]] = {}
self.historische_dienste: List[Tuple[date, Eltern, Dienst]] = []
def get_dienst(self, kuerzel: str) -> Optional[Dienst]:
"""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: str, name: str, personen_anzahl: int = 1) -> Dienst:
"""Fügt einen neuen Dienst hinzu"""
dienst = Dienst(kuerzel, name, personen_anzahl)
self.dienste.append(dienst)
return dienst
def print_dienste_info(self) -> None:
"""Druckt Informationen über alle konfigurierten Dienste"""
print("Konfigurierte Dienste:")
for dienst in self.dienste:
print(f" {dienst}")
def lade_daten(
self,
eingabe_datei: str,
eltern_datei: str,
vorherige_datei: Optional[str] = None
) -> None:
"""Lädt alle benötigten CSV-Dateien
Args:
eingabe_datei: Pfad zur eingabe.csv mit Terminen und Präferenzen
eltern_datei: Pfad zur eltern.csv mit Dienstfaktoren
vorherige_datei: Optionaler Pfad zur vorherige-ausgaben.csv für Fairness-Constraints
"""
# Eingabe CSV: Termine, Präferenzen, Verfügbarkeit
self.eltern, self.planungszeitraum, self.benoetigte_dienste, self.verfügbarkeit, self.präferenzen = \
EingabeParser.parse_eingabe_csv(eingabe_datei, self.get_dienst)
# Eltern CSV: Dienstfaktoren
self.dienstfaktoren = EingabeParser.parse_eltern_csv(eltern_datei)
# Vorherige Ausgaben CSV (optional): Historische Dienste für Fairness
if vorherige_datei:
self.historische_dienste = \
EingabeParser.parse_vorherige_ausgaben_csv(vorherige_datei, self.eltern, self.dienste)