Skip to content

ORM und Sync

Matthias Schabhüttl edited this page Jun 12, 2026 · 1 revision

ORM und Sync (sp5lib.orm)

sp5lib.orm ist der SQLAlchemy-2.0-Layer der Bibliothek: eine datenbankagnostische Abbildung des Schichtplaner5-Datenbestands auf SQLite oder PostgreSQL, inklusive DBF→SQL-Synchronisation. Der Layer ist rein additiv — er ersetzt den DBF-Pfad nicht, sondern existiert parallel dazu:

  • DBF bleibt Source of Truth für den Original-Client und den Standardbetrieb (Datenbankformat).
  • Der ORM-Spiegel ist als Read-Mirror ausgelegt: sync_all() überträgt die Daten per Upsert in die SQL-Datenbank; ein Rückschreiben SQL → DBF ist bewusst nicht implementiert.
.DBF-Dateien ──read_dbf──► sp5lib.orm.sync ──Upsert──► SQLite / PostgreSQL
                                                            │
                                            Repositories / SP5PostgresDatabase

Engine und Session

from sp5lib.orm import get_engine, init_db
from sp5lib.orm.base import session_scope

# Entwicklung: SQLite
engine = get_engine("sqlite:///sp5.db")

# Produktion: PostgreSQL (nur die URL ändert sich)
engine = get_engine("postgresql://user:pw@localhost:5432/sp5")

init_db(engine)   # legt alle Tabellen an
  • Alle Abfragen laufen über die SQLAlchemy-ORM-API (kein Roh-SQL) — der Wechsel zwischen SQLite und PostgreSQL ist nur eine andere Verbindungs-URL.
  • Die SQLite-Engine wird mit WAL-Modus und aktivierten Foreign-Key-Pragmas konfiguriert, die PostgreSQL-Engine mit Connection-Pooling und pool_pre_ping.
  • session_scope(engine) liefert einen Kontextmanager mit Commit/Rollback; get_session(engine) eine einzelne Session.

Modelle

Die kanonischen Modelle liegen in sp5lib.orm.models und sind direkt aus sp5lib.orm importierbar. Jedes Modell hat ein to_dict(), das die echten DBF-Feldnamen spiegelt (z. B. LEAVETYPID, ENTITLEMNT, DESCRIPT) — Konsumenten erhalten also dieselben Schlüssel wie beim direkten DBF-Lesen.

Modell DBF-Quelle Inhalt
Employee 5EMPL Mitarbeiter
Group / GroupAssignment 5GROUP / 5GRASG Gruppen (hierarchisch) und Zuordnungen
Shift 5SHIFT Schichtarten
LeaveType 5LEAVT Abwesenheitsarten
Workplace 5WOPL Arbeitsplätze
ShiftAssignment 5MASHI Dienstplan-Einträge
SpecialShift 5SPSHI Sonderschichten
Absence 5ABSEN Abwesenheiten
Holiday / Period 5HOLID / 5PERIO Feiertage, Perioden
AccountBooking 5BOOK Kontobuchungen
OvertimeEntry 5OVER Überstunden
LeaveEntitlement 5LEAEN Urlaubsansprüche
ShiftDemand / SpecialDemand 5SHDEM / 5SPDEM Besetzungsbedarf
Cycle / CycleAssignment 5CYCLE / 5CYASS Schichtmodelle und Zuweisungen
Restriction 5RESTR Restriktionen
Company Mandanten-Vorgriff ohne DBF-Gegenstück (kein Sync)

Für Bestandscode bleiben die früheren Namen als Aliase importierbar: ScheduleEntry (= ShiftAssignment), Booking (= AccountBooking), OvertimeRecord (= OvertimeEntry), StaffingRequirement (= ShiftDemand).

Zusätzlich definiert sp5lib.orm.models_pg PostgreSQL-Zusatzmodelle ohne DBF-SyncUser, CycleEntry, Note, HolidayBan, ExtraCharge, Settings, ChangelogEntry — und re-exportiert alle kanonischen Modelle.


Repositories

Der Datenzugriff ist im Repository-Pattern gekapselt (sp5lib.orm.repository, 18 Repositories): saubere Trennung von Fachlogik und Datenzugriff, leicht testbar mit In-Memory-SQLite. Alle Repositories bieten get(id) und ein list(…) mit fachlichen Filtern (Datumsfenster, Mitarbeiter, Jahr, Gruppe, include_hidden, …). EmployeeRepository und GroupRepository haben darüber hinaus Schreibmethoden (create, update, soft_delete, search, Mitglieder-Verwaltung).

from sp5lib.orm import get_engine, init_db
from sp5lib.orm.base import session_scope
from sp5lib.orm.repository import EmployeeRepository, GroupRepository

engine = get_engine("sqlite:///sp5.db")
init_db(engine)

with session_scope(engine) as session:
    emp_repo = EmployeeRepository(session)
    grp_repo = GroupRepository(session)

    emp = emp_repo.create(name="Müller", firstname="Hans", hrsweek=38.5)
    grp = grp_repo.create(name="Frühschicht", shortname="FS")
    grp_repo.add_member(grp.id, emp.id)

    members = grp_repo.get_members(grp.id)
    results = emp_repo.search("müll")

DBF → SQL: sync_all

sp5lib.orm.sync.sync_all(engine, daten_path) spiegelt 19 DBF-Tabellen per Upsert (nach DBF-ID: vorhandene Zeilen werden aktualisiert, neue eingefügt) in einer Transaktion und liefert ein Dictionary mit den Record-Zahlen je Tabelle:

from sp5lib.orm import get_engine, init_db
from sp5lib.orm.sync import sync_all

engine = get_engine("sqlite:///sp5.db")
init_db(engine)
stats = sync_all(engine, "/pfad/zu/SP5/Daten")
print(stats)   # {"employees": …, "groups": …, …}

Dasselbe leistet ohne Python-Code das Kommando sp5lib sync (CLI).

Robustheit gegenüber Altdaten

Der Sync ist darauf ausgelegt, mit gewachsenen Realdatenbeständen durchzulaufen:

  • Gruppen-Hierarchie zweiphasig: Eltern-Verweise werden erst nach dem Anlegen aller Gruppen aufgelöst; hängende Verweise werden auf NULL gesetzt und geloggt.
  • Zuordnungstabellen (5GRASG, 5CYASS): die DBF-ID ist dort kein globaler Schlüssel — der Sync verwendet einen eigenen Autoincrement-Schlüssel, dedupliziert auf (Mitarbeiter, Gruppe) bzw. (Mitarbeiter, Zyklus, Start) und überspringt Zeilen mit unbekannten Verweisen.
  • Datumsbehaftete Tabellen: Zeilen mit leerem oder ungültigem DATE werden übersprungen und gezählt geloggt.
  • Referenzspalten (Mitarbeiter/Schicht/Abwesenheitsart/Arbeitsplatz) sind bewusst einfache Integer ohne Datenbank-Foreign-Key, damit auch inkonsistente Altdaten synchronisierbar bleiben.

Was der Spiegel nicht abdeckt

SP5Database nutzt 30 DBF-Tabellen, der Sync spiegelt 19. Ohne SQL-Gegenstück bleiben u. a. 5USER, 5NOTE, 5CYENT, 5CYEXC, 5USETT, 5XCHAR, 5HOBAN, 5EMACC, 5GRACC, 5DADEM und 5MAGRP — Benutzerkonten, Notizen und Zyklus-Details müssen auf einem reinen SQL-Backend von der Anwendung selbst gepflegt werden (die App tut das; siehe PostgreSQL-Support im App-Wiki).


PostgreSQL-Fassade

Für den Betrieb der REST-API auf PostgreSQL bietet sp5lib.pg_database die Klasse SP5PostgresDatabase mit denselben Methodensignaturen wie SP5Database — als Teilmenge (Stammdaten-CRUD, Dienstplan-Basis, Benutzer/2FA, Notizen, Statistik- und Kontoauswertungen). Die Auswertungen rechnen über dieselbe Rechenschicht wie das DBF-Backend (Berechnungen). Die Backend-Wahl zur Laufzeit übernimmt sp5lib.db_factory.get_database() anhand der Umgebungsvariablen DB_BACKEND, DATABASE_URL und SP5_DB_PATH (siehe API-Referenz).

libopenschichtplaner5 v1.7.0

Home — Startseite


Einstieg

Konzepte

Referenz

Mitwirken


Verwandte Wikis

Links

Clone this wiki locally