Files
LeanderShiftPlannerV2/Full-Doc.md
T
2026-04-28 14:27:27 +02:00

16 KiB
Raw Blame History

LeanderShiftPlannerV2 Technische Dokumentation

Version: 2.0.2
Sprache: C# / .NET 9.0
Framework: Avalonia UI 11.2.1
Repository: https://gitea.leanderserver.de/leander19961/LeanderShiftPlannerV2


Inhaltsverzeichnis

  1. Projektübersicht
  2. Systemvoraussetzungen & Abhängigkeiten
  3. Projektstruktur
  4. Datenmodell
  5. Services (Geschäftslogik)
  6. Berechnungsalgorithmus
  7. Datei-I/O (FileIO)
  8. Benutzeroberfläche (View)
  9. Ausgabedateien
  10. Hinweise & Einschränkungen

1. Projektübersicht

LeanderShiftPlannerV2 ist eine Desktop-Anwendung zur automatisierten Erstellung von Schichtplänen. Das Tool erlaubt das Anlegen, Verwalten und Verknüpfen von Personen und Schichten, die anschließende Berechnung aller validen Schichtplankombinationen sowie die automatische Generierung von Stundenzetteln.

Kernfunktionen:

  • Verwaltung von Personen (inkl. Wochenstunden und Stundenzettel-Quelle)
  • Verwaltung von Schichten (Wochentag, Uhrzeit, Min./Max.-Personenanzahl)
  • Bidirektionale Verknüpfung von Personen und Schichten
  • Parallele Berechnung aller gültigen Schichtplankombinationen
  • Filterung der berechneten Pläne
  • Ausgabe als PNG und/oder Textdatei
  • Automatische Generierung von Stundenzetteln (Excel-Format, optional Google Calendar-Integration)

Wichtig: Alle Stundenangaben müssen im Wochenstunden-Format erfolgen!


2. Systemvoraussetzungen & Abhängigkeiten

Anforderung Details
.NET-Version .NET 9.0
UI-Framework Avalonia UI 11.2.1 (Cross-Platform: Windows, Linux, macOS)
Ausgabetyp WinExe (Desktop-Anwendung)

NuGet-Pakete:

Paket Version Verwendung
Avalonia 11.2.1 UI-Framework
Avalonia.Desktop 11.2.1 Desktop-Unterstützung
Avalonia.Themes.Fluent 11.2.1 Fluent Design Theme
Avalonia.Fonts.Inter 11.2.1 Schriftarten
EPPlus 7.5.3 Excel-Generierung (Stundenzettel)
Google.Apis.Calendar.v3 1.69.0.3746 Google Calendar Integration
SkiaSharp 3.118.0-preview PNG-Rendering der Schichtpläne

3. Projektstruktur

LeanderShiftPlannerV2/
├── LeanderShiftPlannerV2.sln
└── LeanderShiftPlannerV2/
    ├── App.axaml / App.axaml.cs       # Avalonia-Anwendungseinstieg
    ├── Program.cs                     # Main-Einstiegspunkt
    ├── app.manifest
    ├── LeanderShiftPlannerV2.csproj
    ├── Model/                         # Datenmodelle (POCOs)
    │   ├── Person.cs
    │   ├── Shift.cs
    │   ├── ShiftPlan.cs
    │   ├── Timesheet.cs
    │   └── Filter.cs
    ├── Service/                       # Geschäftslogik / Services
    │   ├── AppService.cs              # Zentraler Service-Container
    │   ├── PersonService.cs
    │   ├── ShiftService.cs
    │   ├── ShiftPlanService.cs        # Kernalgorithmus
    │   ├── FilterService.cs
    │   ├── TimesheetService.cs
    │   └── ViewService.cs
    ├── FileIO/                        # Laden/Speichern von Daten
    ├── View/                          # Avalonia AXAML Views
    ├── Util/                          # Hilfsfunktionen
    └── Resources/
        ├── icon/                      # Anwendungssymbol
        ├── font/                      # Eingebettete Schriften (Emblem.ttf, Roboto.ttf)
        └── templates/
            ├── timesheet.xlsx         # Excel-Vorlage für Stundenzettel
            └── googleapi.txt          # Google API-Konfiguration

4. Datenmodell

4.1 Person

Datei: Model/Person.cs
Implementiert: INotifyPropertyChanged, [Serializable]

Repräsentiert eine Person, die in Schichten eingeteilt werden kann.

Eigenschaft Typ Beschreibung
FirstName string Vorname der Person
Surname string Nachname der Person
Hours int Wöchentliche Arbeitsstunden (Pflichtangabe)
TimeSheetSource string Pfad/Quelle für die Stundenzettel-Daten
Shifts ObservableCollection<Shift> Liste verknüpfter Schichten (nicht serialisiert)
Timesheets List<Timesheet> Liste der Stundenzettel (nicht serialisiert)
ShiftsString string (readonly) Lesbare Darstellung aller Schichten für die UI

Methoden:

  • AddShift(Shift) / RemoveShift(Shift) / ClearShifts() Verwaltung der Schichtverknüpfungen (lösen jeweils PropertyChanged aus)
  • GetTimeSheetByMonth(string month) Gibt den Stundenzettel für einen bestimmten Monat zurück

4.2 Shift (Schicht)

Datei: Model/Shift.cs
Implementiert: INotifyPropertyChanged, [Serializable]

Repräsentiert eine einzelne Schicht an einem bestimmten Wochentag.

Eigenschaft Typ Beschreibung
Day string Wochentag (z.B. "Montag")
StartTime string Startzeit (Format: "HH:mm")
EndTime string Endzeit (Format: "HH:mm")
Duration int Dauer der Schicht in Stunden
ShiftInDay int Position der Schicht am Tag (1 = erste, 2 = zweite, 3 = dritte)
MaxPersonCount int Maximale Anzahl Personen pro Schicht
MinPersonCount int Minimale Anzahl Personen pro Schicht
Persons ObservableCollection<Person> Verknüpfte Personen (nicht serialisiert)
PersonsString string (readonly) Lesbare Darstellung aller Personen für die UI
Time string (readonly) Formatierte Zeitanzeige "HH:MM - HH:MM"

Methoden:

  • AddPerson(Person) / RemovePerson(Person) / ClearPersons() Verwaltung der Personenverknüpfungen

4.3 ShiftPlan

Datei: Model/ShiftPlan.cs

Repräsentiert einen vollständigen Schichtplan, der aus einer Kombination von Schichten über alle konfigurierten Wochentage besteht. Wird vom ShiftPlanService erzeugt.


4.4 Timesheet (Stundenzettel)

Datei: Model/Timesheet.cs

Repräsentiert den Stundenzettel einer Person für einen bestimmten Monat. Beinhaltet die Arbeitszeiterfassung und wird wahlweise aus einer lokalen Quelle oder über Google Calendar bezogen.

Eigenschaft Typ Beschreibung
Month string Monat im Format "MM.YYYY" o.ä.

4.5 Filter

Datei: Model/Filter.cs

Hält die aktuell gesetzten Filterkriterien für die Anzeige der berechneten Schichtpläne.


5. Services (Geschäftslogik)

5.1 AppService

Datei: Service/AppService.cs

Zentraler Service-Container, der alle anderen Services hält und als einheitlicher Zugriffspunkt dient. Wird bei Anwendungsstart instanziiert und an alle abhängigen Komponenten weitergegeben.

Hält Referenzen auf: PersonService, ShiftService, ShiftPlanService, FilterService, TimesheetService, ViewService


5.2 PersonService

Datei: Service/PersonService.cs

Verwaltet die Liste aller Personen sowie deren Stundenzettel-Daten.

Kernfunktionen:

  • CRUD-Operationen für Person-Objekte
  • Laden von Stundenzetteln aus einer externen Quelle (TimeSheetSource)
  • GenerateRandomTimeSheets() Generiert zufällige Stundenzettel auf Basis von Google Calendar-Einträgen und passt sie an die Wochenstunden der Person an

5.3 ShiftService

Datei: Service/ShiftService.cs

Verwaltet die Liste aller Schichten.

Kernfunktionen:

  • CRUD-Operationen für Shift-Objekte
  • GetShiftList() Gibt alle konfigurierten Schichten zurück

5.4 ShiftPlanService

Datei: Service/ShiftPlanService.cs

Der Kernservice der Anwendung. Enthält den gesamten Berechnungsalgorithmus für Schichtpläne.

Wichtige Methoden:

Methode Beschreibung
StartCalculation(CancellationToken) Startet die vollständige Berechnung (Listen initialisieren → Schichten generieren → Pläne berechnen)
GeneratePlannedShifts() Erzeugt alle möglichen Schichtkombinationen (Permutationen nach Personenanzahl: 1, 2 oder 3 Personen pro Schicht)
CalculateShiftPlans(CancellationToken) Kombiniert die Tagesschichten zu vollständigen Wochenplänen, parallel mit Parallel.ForEach
CheckShift(List<...>) Prüft, ob ein Schichtplan die Wochenstunden aller Personen exakt erfüllt (nicht über- oder unterschreitet)
GetShiftPlans() Gibt die aktuell gefilterte ObservableCollection<ShiftPlan> zurück
GetAllShiftPlans() Gibt alle berechneten Pläne (ungefiltert) zurück
ResetShiftPlanList() Setzt den gefilterten Stand auf alle berechneten Pläne zurück

5.5 FilterService

Datei: Service/FilterService.cs

Wendet einen Filter auf die Liste der Schichtpläne an und reduziert die angezeigte Liste entsprechend.


5.6 TimesheetService

Datei: Service/TimesheetService.cs

Generiert Stundenzettel-Dateien im Excel-Format (Data/Output/Timesheets) auf Basis der Timesheet-Daten der Personen und der bereitgestellten timesheet.xlsx-Vorlage.


5.7 ViewService

Datei: Service/ViewService.cs

Übernimmt die Darstellung eines ShiftPlan als PNG-Bild mittels SkiaSharp. Speichert alle Pläne im Verzeichnis Data/Output/ShiftPlans.


6. Berechnungsalgorithmus

Der Algorithmus in ShiftPlanService arbeitet in drei Phasen:

Phase 1 Generierung der Einzelschicht-Varianten (GeneratePlannedShifts)

Für jede konfigurierte Schicht werden alle Kombinationen von Personen gebildet, die dieser Schicht zugewiesen sind von MinPersonCount bis MaxPersonCount. Das Ergebnis ist eine Liste aller möglichen "besetzten Einzelschichten":

  • 1 Person: Jede Person einzeln
  • 2 Personen: Alle Zweier-Kombinationen (ohne Wiederholung)
  • 3 Personen: Alle Dreier-Kombinationen (ohne Wiederholung)

Phase 2 Aufbau der Tagesschichten (GenerateDayShifts)

Die Einzelschicht-Varianten werden nach Wochentag gruppiert. Innerhalb eines Tages werden die bis zu 3 möglichen Positionen (ShiftInDay = 1, 2, 3) zu Dreier-Tupeln (Shift, Shift, Shift) kombiniert. Fehlt eine Position, wird null eingesetzt.

Phase 3 Kombination zum Wochenplan & Validierung (CalculateShiftPlans)

Alle Tagesschichten werden durch verschachtelte Parallel.ForEach-Schleifen kombiniert (je ein Level pro Wochentag). Für jede entstehende Wochenplan-Kombination wird CheckShift aufgerufen:

  • Es werden die kumulierten Stunden aller Personen berechnet.
  • Ein Plan gilt nur dann als gültig, wenn jede Person exakt ihre konfigurierten Wochenstunden arbeitet (weder mehr noch weniger).
  • Gültige Pläne werden in die ObservableCollection<ShiftPlan> eingetragen.

Hinweis zur Performance: Die Berechnung ist exponentiell in der Anzahl der Schichten und Personen. Die parallele Ausführung über Parallel.ForEach nutzt alle verfügbaren CPU-Kerne. Bei komplexen Konfigurationen kann die Berechnung dennoch mehrere Sekunden bis Minuten dauern.


7. Datei-I/O (FileIO)

Das FileIO-Verzeichnis enthält Klassen zum Laden und Speichern des Anwendungszustands (Personen, Schichten) als XML-Dateien mittels XmlSerializer.

  • Personen und Schichten werden als XML serialisiert/deserialisiert.
  • Verknüpfungen (Shifts in Person, Persons in Shift) sind mit [XmlIgnore] dekoriert und werden nach dem Laden aus dem XML neu aufgebaut, um zirkuläre Referenzen zu vermeiden.

8. Benutzeroberfläche (View)

Die Oberfläche ist mit Avalonia UI und dem Fluent Theme umgesetzt. Sie besteht aus mehreren Tabs:

Tab Funktion
Persons Personen anlegen (Add new person), bearbeiten (Edit person), löschen (Delete person), Schichten zuweisen (Select shifts for person)
Shifts Schichten anlegen (Add new shift), bearbeiten (Edit shift), löschen (Delete shift), Personen zuweisen (Select persons for shift)
ShiftPlans Berechnung starten (Start calculation), Pläne filtern (Filter shiftplans), Filter zurücksetzen (Reset all filters), Plan öffnen (Open selected ShiftPlan), alle Pläne speichern (Save all ShiftPlans), Stundenzettel generieren (Generate Timesheets)

Bidirektionale Verknüpfung: Die Zuweisung von Personen und Schichten erfolgt stets automatisch in beide Richtungen. Eine über "Select shifts for person" hinzugefügte Schicht wird der Person und gleichzeitig der Person zur Schicht hinzugefügt ohne manuellen Zweischritt.


9. Ausgabedateien

Pfad Inhalt
Data/Output/ShiftPlans/ Alle Schichtpläne als .png-Dateien (via Save all ShiftPlans)
Data/Output/Timesheets/ Stundenzettel als .xlsx-Dateien (via Generate Timesheets)

10. Hinweise & Einschränkungen

  • Stundenformat: Alle Stunden müssen als Wochenstunden angegeben werden. Tagesstunden oder Monatsstunden werden nicht unterstützt.
  • Personenanzahl pro Schicht: Der Algorithmus unterstützt aktuell maximal 3 Personen pro Schicht. Höhere Werte für MaxPersonCount werden im Code nicht berücksichtigt.
  • Anzahl Wochentage: Der Algorithmus ist für genau 5 Wochentage ausgelegt (die Schleifentiefe in CalculateShiftPlans ist auf 5 verschachtelt).
  • Stundenzettel-Quelle: Die TimeSheetSource einer Person verweist auf eine externe Datenquelle (Google Calendar oder lokale Datei), die vor der Generierung verfügbar sein muss.
  • Google API: Die Konfiguration der Google Calendar API erfolgt über die eingebettete Ressource Resources/templates/googleapi.txt.
  • Performance: Bei sehr vielen Personen und Schichten kann die Anzahl der Permutationen exponentiell wachsen. Eine Abbruchmöglichkeit via CancellationToken ist implementiert.

Google API Zugangsdaten einrichten Da das Programm über deinen persönlichen Google Account auf den Kalender zugreift, musst du einmalig eigene OAuth-Zugangsdaten in der Google Cloud Console erstellen und in der Datei Resources/templates/googleapi.txt hinterlegen.

Schritt-für-Schritt-Anleitung:

  • Öffne die Google Cloud Console und melde dich mit deinem Google-Konto an.

  • Erstelle ein neues Projekt oder wähle ein bestehendes aus.

  • Navigiere zu „APIs & Dienste" → „Bibliothek" und suche nach „Google Calendar API". Klicke auf „Aktivieren".

  • Gehe zu „APIs & Dienste" → „Anmeldedaten" und klicke auf „Anmeldedaten erstellen" → „OAuth-Client-ID".

  • Falls noch nicht geschehen, wirst du aufgefordert, den OAuth-Zustimmungsbildschirm zu konfigurieren. Wähle dort „Extern" und fülle die Pflichtfelder (App-Name, Support-E-Mail) aus. Alle anderen Felder können leer bleiben.

  • Als Anwendungstyp wähle „Desktop-App" und vergib einen beliebigen Namen.

  • Nach der Erstellung siehst du deine Client-ID und dein Client-Secret. Trage beide Werte in die Datei Resources/templates/googleapi.txt ein:

    ClientId=deine-client-id ClientSecret=dein-client-secret

Beim ersten Programmstart öffnet sich einmalig ein Browser-Fenster, in dem du den Zugriff für dein Google-Konto bestätigst. Danach wird das Zugriffstoken lokal gespeichert und der Dialog erscheint nicht mehr.

Dokumentation erstellt am 28.04.2026