Commit 99d57ae8 authored by Ben Wais's avatar Ben Wais
Browse files

test

parent 9b7cece5
Loading
Loading
Loading
Loading

00_nginx/default.conf

0 → 100644
+18 −0
Original line number Diff line number Diff line
server {
    listen 80;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri /index.html;
    }

    location /api/ {
        proxy_pass http://backend:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

01_backend/__init__.py

0 → 100644
+0 −0

Empty file added.

01_backend/ilo.py

0 → 100644
+169 −0
Original line number Diff line number Diff line
from ortools.sat.python import cp_model
import json

def erstelle_schichtplan(personal_data, schicht_requirements, arbeitszeit_rules):
    """
    Erstellt einen Schichtplan basierend auf Personal, Schichtanforderungen und Arbeitszeitregeln
    mithilfe von Google OR-Tools (CP-SAT Solver).

    Args:
        personal_data (dict): Informationen über das Personal.
                         Format: {
                             'Name': {
                                 'verfügbarkeit': [Liste von Wochentagen (z.B. 'Mo', 'Di')],
                                 'status': 'Vollzeit'/'Halbtags',
                                 'MaxWoche': int (maximale Arbeitsstunden pro Woche),
                                 'MaxTag': int (maximale Arbeitsstunden pro Tag)
                             }
                         }
        schicht_requirements (dict): Anforderungen pro Tag und Schicht.
                                     Format: {
                                         'Tag': {
                                             'Schichtname': {
                                                 'anzahl_benoetigt': int,
                                                 'dauer': int (in Stunden)
                                             }
                                         }
                                     }
        arbeitszeit_rules (dict): Globale Regeln wie Mindestruhezeit (aktuell nur Platzhalter).
                                  Format: {'min_ruhezeit_stunden': int} (noch nicht vollständig implementiert)

    Returns:
        tuple: (status_erfolg, ergebnis_data)
               status_erfolg (bool): True bei erfolgreicher Lösungsfindung, False sonst.
               ergebnis_data (dict/str): Dictionary mit dem Schichtplan bei Erfolg,
                                         oder eine Fehlermeldung als String bei Misserfolg.
                                         Der Schichtplan hat folgendes Format:
                                         {
                                             'Tag': {
                                                 'Schichtname': ['Mitarbeiter1', 'Mitarbeiter2', ...],
                                                 ...
                                             },
                                             ...
                                         }
    """

    model = cp_model.CpModel()

    # --- 1. Daten Vorbereitung ---
    mitarbeiter = sorted(personal_data.keys())
    tage = sorted(schicht_requirements.keys())

    # Sammle alle eindeutigen Schichtnamen über alle Tage
    alle_schichten = set()
    for tag_data in schicht_requirements.values():
        for schicht_name in tag_data.keys():
            alle_schichten.add(schicht_name)
    schichten = sorted(list(alle_schichten))

    # --- 2. Variablen erstellen ---
    # x[p, t, s] ist eine binäre Variable, die 1 ist, wenn Person 'p' in Schicht 's' an Tag 't' arbeitet.
    x = {}
    for p in mitarbeiter:
        for t in tage:
            for s in schichten:
                # Nur Variablen für Schichten erstellen, die an diesem Tag auch existieren
                if s in schicht_requirements.get(t, {}):
                    x[(p, t, s)] = model.NewBoolVar(f'x_{p}_{t}_{s}')


    # --- 3. Nebenbedingungen (Constraints) definieren ---

    # 3.1 Jede Schicht muss mit der benötigten Anzahl an Mitarbeitern besetzt sein
    for t in tage:
        for s_name, s_details in schicht_requirements[t].items():
            benoetigt = s_details['anzahl_benoetigt']
            sum_besetzung = sum(x[(p, t, s_name)] for p in mitarbeiter if (p, t, s_name) in x)
            model.Add(sum_besetzung == benoetigt)


    # 3.2 Eine Person arbeitet maximal in einer Schicht pro Tag
    for p in mitarbeiter:
        for t in tage:
            # Summe der Variablen x für Person 'p' an Tag 't' über alle Schichten
            # muss kleiner oder gleich 1 sein (da auch frei sein kann).
            sum_schichten_pro_tag = sum(x[(p, t, s)] for s in schichten if (p, t, s) in x)
            model.Add(sum_schichten_pro_tag <= 1)


    # 3.3 Verfügbarkeit / Wochentagspräferenzen (Tage, an denen ein Mitarbeiter NICHT verfügbar ist)
    for p in mitarbeiter:
        # Die 'verfügbarkeit' in personal_data sollte die Tage enthalten, an denen die Person VERFÜGBAR ist.
        # Tage, die nicht in der Verfügbarkeitsliste sind, sind also Tage, an denen die Person NICHT arbeiten kann.
        verfuegbare_tage = personal_data[p].get('verfügbarkeit', []) # Standardwert ist leere Liste
        
        for t in tage:
            if t not in verfuegbare_tage:
                # Wenn Person 'p' an Tag 't' NICHT verfügbar ist, darf sie in keiner Schicht arbeiten.
                for s in schichten:
                    if (p, t, s) in x: # Nur wenn die Variable für diese Kombination existiert
                        model.Add(x[(p, t, s)] == 0)


    # 3.4 Maximale Arbeitszeit pro Tag
    for p in mitarbeiter:
        for t in tage:
            max_stunden_tag = personal_data[p]['MaxTag']
            # Summe der Arbeitsstunden für Person 'p' an Tag 't'
            arbeitsstunden_tag = sum(
                schicht_requirements[t][s]['dauer'] * x[(p, t, s)]
                for s in schichten
                if (p, t, s) in x and s in schicht_requirements.get(t, {})
            )
            model.Add(arbeitsstunden_tag <= max_stunden_tag)


    # 3.5 Maximale Arbeitszeit pro Woche (über den gesamten Planungszeitraum)
    for p in mitarbeiter:
        max_stunden_woche = personal_data[p]['MaxWoche']
        # Summe der Arbeitsstunden für Person 'p' über alle Tage und Schichten im Planungszeitraum
        arbeitsstunden_woche = sum(
            schicht_requirements[t][s]['dauer'] * x[(p, t, s)]
            for t in tage
            for s in schichten
            if (p, t, s) in x and s in schicht_requirements.get(t, {})
        )
        model.Add(arbeitsstunden_woche <= max_stunden_woche)

    # 3.6 Mindestruhezeit zwischen Schichten (Platzhalter, Implementierung komplexer)
    # Eine korrekte Implementierung erfordert genaue Start- und Endzeiten der Schichten
    # und eine Abbildung der Reihenfolge der Tage.
    # Für den Moment wird diese Regel nicht aktiv durchgesetzt, ist aber als zukünftiger Erweiterungspunkt notiert.
    # min_ruhezeit = arbeitszeit_rules.get('min_ruhezeit_stunden', 0)
    # print(f"Hinweis: Die Mindestruhezeit von {min_ruhezeit} Stunden ist noch nicht vollständig implementiert.")


    # --- 4. Zielfunktion definieren ---
    # Standardmäßig keine explizite Zielfunktion, da wir alle harten Constraints erfüllen müssen.
    # Der Solver findet die erste oder eine "gute" Lösung, die alle Bedingungen erfüllt.
    # Wenn Präferenzen oder Minimierung von Kosten wichtig wären, würde hier eine Summe über entsprechende
    # Variablen mit Gewichten hinzugefügt.


    # --- 5. Solver aufrufen ---
    solver = cp_model.CpSolver()
    # Optional: Zeitlimit setzen für große Probleme
    # solver.parameters.max_time_in_seconds = 60.0

    status = solver.Solve(model)

    # --- 6. Ergebnisse interpretieren ---
    schichtplan_ergebnis = {}

    if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
        for t in tage:
            schichtplan_ergebnis[t] = {}
            for s in schichten:
                if s in schicht_requirements.get(t, {}): # Nur für Schichten, die an diesem Tag relevant sind
                    zugewiesene_mitarbeiter = []
                    for p in mitarbeiter:
                        if (p, t, s) in x and solver.BooleanValue(x[(p, t, s)]):
                            zugewiesene_mitarbeiter.append(p)
                    schichtplan_ergebnis[t][s] = zugewiesene_mitarbeiter
        
        return True, schichtplan_ergebnis

    elif status == cp_model.INFEASIBLE:
        return False, "Keine Lösung gefunden. Die angegebenen Anforderungen und Regeln sind unvereinbar."
    else:
        return False, f"Solver konnte keine Lösung finden. Status: {solver.StatusName(status)}. Versuchen Sie, die Regeln zu lockern."
 No newline at end of file

01_backend/main.py

0 → 100644
+59 −0
Original line number Diff line number Diff line
import json
from ilo import erstelle_schichtplan # Importiere deine Funktion

def run_scheduler():
    try:
        # 1. Daten aus JSON-Dateien laden
        with open('personal.json', 'r', encoding='utf-8') as f:
            personal_data = json.load(f)
        
        with open('schicht_anforderungen.json', 'r', encoding='utf-8') as f:
            schicht_requirements = json.load(f)
            
        with open('arbeitszeit_regeln.json', 'r', encoding='utf-8') as f:
            arbeitszeit_rules = json.load(f)

        # 2. Schichtplan-Funktion aufrufen
        success, result = erstelle_schichtplan(personal_data, schicht_requirements, arbeitszeit_rules)

        # 3. Ergebnis verarbeiten
        if success:
            print("Schichtplan erfolgreich erstellt!")
            # Optional: Speichern des Ergebnisses in einer JSON-Datei
            with open('schichtplan_output.json', 'w', encoding='utf-8') as f:
                json.dump(result, f, indent=4, ensure_ascii=False)
            
            # Ausgabe auf der Konsole
            for tag, schichten_am_tag in result.items():
                print(f"\n--- Tag: {tag} ---")
                for schicht_name, zugewiesen in schichten_am_tag.items():
                    # Sicherstellen, dass die Schicht im Original-Requirement existiert,
                    # um die Dauer abrufen zu können.
                    dauer = schicht_requirements.get(tag, {}).get(schicht_name, {}).get('dauer', 'N/A')
                    print(f"  Schicht {schicht_name} ({dauer}h): {', '.join(zugewiesen) if zugewiesen else 'UNBESETZT'}")

            # Optional: Arbeitszeitübersicht zur Überprüfung
            print("\n--- Arbeitszeitübersicht pro Mitarbeiter ---")
            for p in personal_data.keys():
                gesamtarbeitszeit_p = 0
                for t in result.keys():
                    for s_name, zugewiesene_m in result[t].items():
                        if p in zugewiesene_m:
                            # Stellen Sie sicher, dass schicht_requirements[t][s_name]['dauer'] existiert
                            duration = schicht_requirements.get(t, {}).get(s_name, {}).get('dauer', 0)
                            gesamtarbeitszeit_p += duration
                print(f"  {p}: {gesamtarbeitszeit_p} Stunden (Max. Woche: {personal_data[p]['MaxWoche']})")


        else:
            print(f"Fehler: {result}") # Hier wird die Fehlermeldung ausgegeben

    except FileNotFoundError as e:
        print(f"Fehler: Eine der benötigten JSON-Dateien wurde nicht gefunden: {e}")
    except json.JSONDecodeError as e:
        print(f"Fehler beim Parsen der JSON-Dateien: {e}")
    except Exception as e:
        print(f"Ein unerwarteter Fehler ist aufgetreten: {e}")

if __name__ == '__main__':
    run_scheduler()
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
ortools
 No newline at end of file
Loading