Loading 01_backend/__pycache__/ilo.cpython-39.pyc 0 → 100644 +4.76 KiB File added.No diff preview for this file type. View file 01_backend/main.py +78 −55 Original line number Diff line number Diff line import json from ilo import erstelle_schichtplan # Importiere deine Funktion from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Dict, Any from ilo import erstelle_schichtplan def run_scheduler(): app = FastAPI() # 🧩 Eingabedaten vom Frontend class Shift(BaseModel): begin: str end: str reqEmployees: str class Employee(BaseModel): name: str capacity: str availableDays: List[str] class FrontendRequest(BaseModel): shiftCount: str shifts: List[Shift] employeeCount: str employees: List[Employee] maxWeeklyHours: str maxDailyHours: str restingPeriod: str @app.post("/api/plane-schichtplan") def plane_schichtplan(data: FrontendRequest): 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}") # 🔁 1. Konvertiere ins Backend-Format personal_data = {} for emp in data.employees: personal_data[emp.name] = { "Verfuegbar": {day: True for day in emp.availableDays}, "MaxWoche": int(emp.capacity), } # Schichtanforderungen auf alle Wochentage anwenden tage = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] schichtanforderungen = {} for tag in tage: schichtanforderungen[tag] = {} for idx, shift in enumerate(data.shifts): schichtanforderungen[tag][f"S{idx+1}"] = { "beginn": shift.begin, "ende": shift.end, "anzahl": int(shift.reqEmployees), "dauer": _berechne_dauer(shift.begin, shift.end) } arbeitszeitregeln = { "MaxTagesstunden": int(data.maxDailyHours), "Ruhezeit": int(data.restingPeriod), "MaxWoche": int(data.maxWeeklyHours) } # 🧠 2. Aufruf der Planungslogik success, result = erstelle_schichtplan(personal_data, schichtanforderungen, arbeitszeitregeln) if not success: raise HTTPException(status_code=400, detail=result) return { "status": "ok", "schichtplan": result } except Exception as e: print(f"Ein unerwarteter Fehler ist aufgetreten: {e}") raise HTTPException(status_code=500, detail=str(e)) # ⏱ Hilfsfunktion zur Dauerberechnung def _berechne_dauer(start: str, ende: str) -> int: from datetime import datetime, timedelta if __name__ == '__main__': run_scheduler() No newline at end of file fmt = "%H:%M" s = datetime.strptime(start, fmt) e = datetime.strptime(ende, fmt) if e <= s: e += timedelta(days=1) return int((e - s).seconds // 3600) 02_frontend/src/App.jsx +75 −18 Original line number Diff line number Diff line import { useState } from 'react' import { useState, useRef } from 'react' import logo from './assets/pfannkuchentorte_trans.png' import './App.css' function App() { const [shiftCount, setShiftCount] = useState(0) const [employeeCount, setEmployeeCount] = useState(0) const formRef = useRef(null); function sendConstraints(formData) { const sendConstraints = (e) => { e.preventDefault(); const formData = new FormData(formRef.current); console.log(formData) console.log(formData.get("TU01")) const data = { shiftCount: shiftCount.toString(), shifts: [], employeeCount: employeeCount.toString(), employees: [], maxWeeklyHours: formData.get("maxWeeklyHours"), maxDailyHours: formData.get("maxDailyHours"), restingPeriod: formData.get("restingPeriod") } for (let i = 1; i <= shiftCount; i++){ data.shifts.push({ begin: formData.get(`shiftStart${i}`), end: formData.get(`shiftEnd${i}`), reqEmployees: formData.get('reqEmployees${i}') }); } for (let i = 1; i <= employeeCount; i++){ const availableDays = []; const days = ["MO", "TU", "WE", "TH", "FR", "SA", "SO"]; days.forEach((day) => { console.log(`formData.get(${day}0${i})`) console.log(formData.get(`${day}0${i}`)) if (formData.get(`${day}${i}`)=="on") { availableDays.push(day); } }); data.employees.push({ name: formData.get(`name${i}`), capacity: formData.get(`capacity${i}`), availableDays }); } console.log("Daten: ", data); fetch("/api", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({}) }) .then((res) => res.json()) .then((err) => console.error("Error: ", err)) } return ( <> <div> Loading @@ -28,48 +85,48 @@ function App() { <p>Mitarbeiter: {employeeCount}</p> <button onClick={() => setEmployeeCount(employeeCount+1)}>+</button> <button onClick={() => setEmployeeCount(employeeCount-1)}>-</button> <form action="sendConstraints"> <form ref={formRef} onSubmit={sendConstraints}> {Array.from({ length: shiftCount}).map((_, index) => ( <div> <label>Start Schicht {index+1}</label> <input type="text" /> <input name={"shiftStart"+index+1} type="text" /> <label>Ende Schicht {index+1}</label> <input type="text" name="" id="" /> <input name={"shiftEnd"+index+1}type="text" id="" /> <label>Anzahl von Personen für Schicht {index+1}</label> <input type="text" /> <input name={"reqEmployees"+index+1} type="text" /> </div> ))} {Array.from({ length: employeeCount}).map((_, index) => ( <div> <label>Name: </label> <input type="text" /><br /> <input name={"name"+index+1}type="text" /><br /> <label>Kapazität (Stunden pro Woche) von Mitarbeiter {index+1}: </label> <input type="text" /><br /> <input name={"capacity"+index+1}type="text" /><br /> <label>Verfügbarkeit von Mitarbeiter {index+1}: </label><br /> <label>MO</label> <input type="checkbox" name="MO" id="" defaultChecked/><br /> <input name={"MO"+index+1} type="checkbox" id="" defaultChecked/><br /> <label>DI</label> <input type="checkbox" name="DI" id="" defaultChecked/><br /> <input name={"TU"+index+1} type="checkbox" id="" defaultChecked/><br /> <label>MI</label> <input type="checkbox" name="MI" id="" defaultChecked/><br /> <input type="checkbox" name={"WE"+index+1} id="" defaultChecked/><br /> <label>DO</label> <input type="checkbox" name="DO" id="" defaultChecked/><br /> <input type="checkbox" name={"TH"+index+1} id="" defaultChecked/><br /> <label>FR</label> <input type="checkbox" name="FR" id="" defaultChecked/><br /> <input type="checkbox" name={"FR"+index+1} id="" defaultChecked/><br /> <label>SA</label> <input type="checkbox" name="SA" id="" defaultChecked/><br /> <input type="checkbox" name={"SA"+index+1} id="" defaultChecked/><br /> <label>SO</label> <input type="checkbox" name="SO" id="" defaultChecked/><br /> <input type="checkbox" name={"SO"+index+1} id="" defaultChecked/><br /> <br /> <br /> </div> ))} <label>max. Wochenstunden pro Mitarbeiter: </label> <input type="text" name="" id="" /><br /> <input name="maxWeeklyHours" type="text" id="" /><br /> <label>max. Tagesstunden pro Mitarbeiter: </label> <input type="text" name="" id="" /><br /> <input name="maxDailyHours" type="text" id="" /><br /> <label>einzuhaltene Ruhezeit zwischen Schichten: </label> <input type="text" name="" id="" /><br /> <input name="restingPeriod" type="text" id="" /><br /> <button type="submit">Dienstplan erstellen</button> </form> </section> Loading Dockerfile.frontend +4 −4 Original line number Diff line number Diff line Loading @@ -2,17 +2,17 @@ FROM node:18 AS build WORKDIR /app # 1. Nur package.json + package-lock.json kopieren COPY frontend/package*.json ./ COPY 02_frontend/package*.json ./ # 2. npm install ausführen RUN npm install # 3. Restlichen Frontend-Code kopieren COPY frontend/ ./ COPY 02_frontend/ ./ # 4. Build ausführen RUN npm run build FROM nginx:stable-alpine COPY --from=build /app/build /usr/share/nginx/html COPY nginx/default.conf /etc/nginx/conf.d/default.conf No newline at end of file COPY --from=build /app/dist /usr/share/nginx/html COPY 00_nginx/default.conf /etc/nginx/conf.d/default.conf No newline at end of file docker-compose.yml +15 −7 Original line number Diff line number Diff line # docker-compose.yml (Korrektur der command-Zeile) version: '3.8' services: nginx: image: nginx:latest container_name: nginx ports: - "80:80" volumes: - ./00_nginx/default.conf:/etc/nginx/conf.d/default.conf - ./frontend/dist:/usr/share/nginx/html # statisches Frontend depends_on: - backend backend: build: context: . Loading @@ -17,10 +27,8 @@ services: build: context: . dockerfile: Dockerfile.frontend volumes: - ./00_nginx/defulat.conf:/etc/nginx/conf.d/default.conf container_name: frontend ports: - "80:80" # KEIN NGINX hier nötig – nur Build-Zweck # kein ports:, kein volumes: (wird nach Build in nginx gemountet) depends_on: - backend Loading
01_backend/__pycache__/ilo.cpython-39.pyc 0 → 100644 +4.76 KiB File added.No diff preview for this file type. View file
01_backend/main.py +78 −55 Original line number Diff line number Diff line import json from ilo import erstelle_schichtplan # Importiere deine Funktion from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Dict, Any from ilo import erstelle_schichtplan def run_scheduler(): app = FastAPI() # 🧩 Eingabedaten vom Frontend class Shift(BaseModel): begin: str end: str reqEmployees: str class Employee(BaseModel): name: str capacity: str availableDays: List[str] class FrontendRequest(BaseModel): shiftCount: str shifts: List[Shift] employeeCount: str employees: List[Employee] maxWeeklyHours: str maxDailyHours: str restingPeriod: str @app.post("/api/plane-schichtplan") def plane_schichtplan(data: FrontendRequest): 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}") # 🔁 1. Konvertiere ins Backend-Format personal_data = {} for emp in data.employees: personal_data[emp.name] = { "Verfuegbar": {day: True for day in emp.availableDays}, "MaxWoche": int(emp.capacity), } # Schichtanforderungen auf alle Wochentage anwenden tage = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] schichtanforderungen = {} for tag in tage: schichtanforderungen[tag] = {} for idx, shift in enumerate(data.shifts): schichtanforderungen[tag][f"S{idx+1}"] = { "beginn": shift.begin, "ende": shift.end, "anzahl": int(shift.reqEmployees), "dauer": _berechne_dauer(shift.begin, shift.end) } arbeitszeitregeln = { "MaxTagesstunden": int(data.maxDailyHours), "Ruhezeit": int(data.restingPeriod), "MaxWoche": int(data.maxWeeklyHours) } # 🧠 2. Aufruf der Planungslogik success, result = erstelle_schichtplan(personal_data, schichtanforderungen, arbeitszeitregeln) if not success: raise HTTPException(status_code=400, detail=result) return { "status": "ok", "schichtplan": result } except Exception as e: print(f"Ein unerwarteter Fehler ist aufgetreten: {e}") raise HTTPException(status_code=500, detail=str(e)) # ⏱ Hilfsfunktion zur Dauerberechnung def _berechne_dauer(start: str, ende: str) -> int: from datetime import datetime, timedelta if __name__ == '__main__': run_scheduler() No newline at end of file fmt = "%H:%M" s = datetime.strptime(start, fmt) e = datetime.strptime(ende, fmt) if e <= s: e += timedelta(days=1) return int((e - s).seconds // 3600)
02_frontend/src/App.jsx +75 −18 Original line number Diff line number Diff line import { useState } from 'react' import { useState, useRef } from 'react' import logo from './assets/pfannkuchentorte_trans.png' import './App.css' function App() { const [shiftCount, setShiftCount] = useState(0) const [employeeCount, setEmployeeCount] = useState(0) const formRef = useRef(null); function sendConstraints(formData) { const sendConstraints = (e) => { e.preventDefault(); const formData = new FormData(formRef.current); console.log(formData) console.log(formData.get("TU01")) const data = { shiftCount: shiftCount.toString(), shifts: [], employeeCount: employeeCount.toString(), employees: [], maxWeeklyHours: formData.get("maxWeeklyHours"), maxDailyHours: formData.get("maxDailyHours"), restingPeriod: formData.get("restingPeriod") } for (let i = 1; i <= shiftCount; i++){ data.shifts.push({ begin: formData.get(`shiftStart${i}`), end: formData.get(`shiftEnd${i}`), reqEmployees: formData.get('reqEmployees${i}') }); } for (let i = 1; i <= employeeCount; i++){ const availableDays = []; const days = ["MO", "TU", "WE", "TH", "FR", "SA", "SO"]; days.forEach((day) => { console.log(`formData.get(${day}0${i})`) console.log(formData.get(`${day}0${i}`)) if (formData.get(`${day}${i}`)=="on") { availableDays.push(day); } }); data.employees.push({ name: formData.get(`name${i}`), capacity: formData.get(`capacity${i}`), availableDays }); } console.log("Daten: ", data); fetch("/api", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({}) }) .then((res) => res.json()) .then((err) => console.error("Error: ", err)) } return ( <> <div> Loading @@ -28,48 +85,48 @@ function App() { <p>Mitarbeiter: {employeeCount}</p> <button onClick={() => setEmployeeCount(employeeCount+1)}>+</button> <button onClick={() => setEmployeeCount(employeeCount-1)}>-</button> <form action="sendConstraints"> <form ref={formRef} onSubmit={sendConstraints}> {Array.from({ length: shiftCount}).map((_, index) => ( <div> <label>Start Schicht {index+1}</label> <input type="text" /> <input name={"shiftStart"+index+1} type="text" /> <label>Ende Schicht {index+1}</label> <input type="text" name="" id="" /> <input name={"shiftEnd"+index+1}type="text" id="" /> <label>Anzahl von Personen für Schicht {index+1}</label> <input type="text" /> <input name={"reqEmployees"+index+1} type="text" /> </div> ))} {Array.from({ length: employeeCount}).map((_, index) => ( <div> <label>Name: </label> <input type="text" /><br /> <input name={"name"+index+1}type="text" /><br /> <label>Kapazität (Stunden pro Woche) von Mitarbeiter {index+1}: </label> <input type="text" /><br /> <input name={"capacity"+index+1}type="text" /><br /> <label>Verfügbarkeit von Mitarbeiter {index+1}: </label><br /> <label>MO</label> <input type="checkbox" name="MO" id="" defaultChecked/><br /> <input name={"MO"+index+1} type="checkbox" id="" defaultChecked/><br /> <label>DI</label> <input type="checkbox" name="DI" id="" defaultChecked/><br /> <input name={"TU"+index+1} type="checkbox" id="" defaultChecked/><br /> <label>MI</label> <input type="checkbox" name="MI" id="" defaultChecked/><br /> <input type="checkbox" name={"WE"+index+1} id="" defaultChecked/><br /> <label>DO</label> <input type="checkbox" name="DO" id="" defaultChecked/><br /> <input type="checkbox" name={"TH"+index+1} id="" defaultChecked/><br /> <label>FR</label> <input type="checkbox" name="FR" id="" defaultChecked/><br /> <input type="checkbox" name={"FR"+index+1} id="" defaultChecked/><br /> <label>SA</label> <input type="checkbox" name="SA" id="" defaultChecked/><br /> <input type="checkbox" name={"SA"+index+1} id="" defaultChecked/><br /> <label>SO</label> <input type="checkbox" name="SO" id="" defaultChecked/><br /> <input type="checkbox" name={"SO"+index+1} id="" defaultChecked/><br /> <br /> <br /> </div> ))} <label>max. Wochenstunden pro Mitarbeiter: </label> <input type="text" name="" id="" /><br /> <input name="maxWeeklyHours" type="text" id="" /><br /> <label>max. Tagesstunden pro Mitarbeiter: </label> <input type="text" name="" id="" /><br /> <input name="maxDailyHours" type="text" id="" /><br /> <label>einzuhaltene Ruhezeit zwischen Schichten: </label> <input type="text" name="" id="" /><br /> <input name="restingPeriod" type="text" id="" /><br /> <button type="submit">Dienstplan erstellen</button> </form> </section> Loading
Dockerfile.frontend +4 −4 Original line number Diff line number Diff line Loading @@ -2,17 +2,17 @@ FROM node:18 AS build WORKDIR /app # 1. Nur package.json + package-lock.json kopieren COPY frontend/package*.json ./ COPY 02_frontend/package*.json ./ # 2. npm install ausführen RUN npm install # 3. Restlichen Frontend-Code kopieren COPY frontend/ ./ COPY 02_frontend/ ./ # 4. Build ausführen RUN npm run build FROM nginx:stable-alpine COPY --from=build /app/build /usr/share/nginx/html COPY nginx/default.conf /etc/nginx/conf.d/default.conf No newline at end of file COPY --from=build /app/dist /usr/share/nginx/html COPY 00_nginx/default.conf /etc/nginx/conf.d/default.conf No newline at end of file
docker-compose.yml +15 −7 Original line number Diff line number Diff line # docker-compose.yml (Korrektur der command-Zeile) version: '3.8' services: nginx: image: nginx:latest container_name: nginx ports: - "80:80" volumes: - ./00_nginx/default.conf:/etc/nginx/conf.d/default.conf - ./frontend/dist:/usr/share/nginx/html # statisches Frontend depends_on: - backend backend: build: context: . Loading @@ -17,10 +27,8 @@ services: build: context: . dockerfile: Dockerfile.frontend volumes: - ./00_nginx/defulat.conf:/etc/nginx/conf.d/default.conf container_name: frontend ports: - "80:80" # KEIN NGINX hier nötig – nur Build-Zweck # kein ports:, kein volumes: (wird nach Build in nginx gemountet) depends_on: - backend