- Refaktorierung zu event-driven Ansatz ohne PostgreSQL Hub - Fixes für mehrtägige Termine: korrekte Verwendung von datumBis, Entfernung 24h-Limit - Per-Employee Locking mit Redis - Logging via context.logger für Motia Workbench - Neue Schritte: calendar_sync_all_step.py, calendar_sync_cron_step.py - Aktualisiertes README.md mit aktueller Architektur - Workbench-Gruppierung: advoware-calendar-sync
Advoware Calendar Sync - Event-Driven Design
Dieser Abschnitt implementiert die bidirektionale Synchronisation zwischen Advoware-Terminen und Google Calendar. Das System wurde zu einem einfachen, event-driven Ansatz refaktoriert, der auf direkten API-Calls basiert, mit Redis für Locking und Deduplikation. Es stellt sicher, dass Termine konsistent gehalten werden, mit Fokus auf Robustheit, Fehlerbehandlung und korrekte Handhabung von mehrtägigen Terminen.
Übersicht
Das System synchronisiert Termine zwischen:
- Advoware: Zentrale Terminverwaltung mit detaillierten Informationen.
- Google Calendar: Benutzerfreundliche Kalenderansicht für jeden Mitarbeiter.
Architektur
Event-Driven Design
- Direkte API-Synchronisation: Kein zentraler Hub; Sync läuft direkt zwischen APIs.
- Redis Locking: Per-Employee Locking verhindert Race-Conditions.
- Event Emission: Cron → All-Step → Employee-Step für skalierbare Verarbeitung.
- Fehlerresistenz: Einzelne Fehler stoppen nicht den gesamten Sync.
- Logging: Alle Logs erscheinen im Motia Workbench via context.logger.
Sync-Phasen
- Cron-Step: Tägliche Auslösung des Syncs.
- All-Step: Fetcht alle Mitarbeiter und emittiert Events pro Employee.
- Employee-Step: Synchronisiert Termine für einen einzelnen Mitarbeiter.
Datenmapping und Standardisierung
Beide Systeme werden auf gemeinsames Format normalisiert (Berlin TZ):
{
'start': datetime, # Berlin TZ
'end': datetime,
'text': str,
'notiz': str,
'ort': str,
'dauertermin': int, # 0/1
'turnus': int, # 0/1
'turnusArt': int,
'recurrence': str # RRULE oder None
}
Advoware → Standard
- Start:
datum+uhrzeitVon(Fallback 09:00), oderdatumals datetime. - End:
datumBis+uhrzeitBis(Fallback 10:00), oderdatum+ 1h. - All-Day:
dauertermin=1oder Dauer >1 Tag. - Recurring:
turnus/turnusArt(vereinfacht, keine RRULE).
Google → Standard
- Start/End:
dateTimeoderdate(All-Day). - All-Day:
dauertermin=1wenn All-Day oder Dauer >1 Tag. - Recurring: RRULE aus
recurrence.
Standard → Advoware
- POST/PUT:
datum/uhrzeitBis/datumBisaus start/end. - Defaults:
vorbereitungsDauer='00:00:00',sb/anwalt=employee_kuerzel.
Standard → Google
- All-Day:
datestattdateTime, end +1 Tag. - Recurring: RRULE aus
recurrence.
Funktionalität
Automatische Kalender-Erstellung
- Für jeden Advoware-Mitarbeiter wird ein Google Calendar mit dem Namen
AW-{Kuerzel}erstellt. - Beispiel: Mitarbeiter mit Kürzel "SB" → Calendar "AW-SB".
- Kalender wird mit dem Haupt-Google-Account (
lehmannundpartner@gmail.com) als Owner geteilt.
Sync-Details
Cron-Step (calendar_sync_cron_step.py)
- Läuft täglich und emittiert "calendar_sync_all".
All-Step (calendar_sync_all_step.py)
- Fetcht alle Mitarbeiter aus Advoware.
- Filtert Debug-Liste (falls konfiguriert).
- Setzt Redis-Lock pro Employee.
- Emittiert "calendar_sync_employee" pro Employee.
Employee-Step (calendar_sync_event_step.py)
- Fetcht Advoware-Termine für den Employee.
- Fetcht Google-Events für den Employee.
- Synchronisiert: Neue erstellen, Updates anwenden, Deletes handhaben.
- Verwendet Locking, um parallele Syncs zu verhindern.
API-Step (calendar_sync_api_step.py)
- Manueller Trigger für einzelnen Employee oder "ALL".
- Bei "ALL": Emittiert "calendar_sync_all".
- Bei Employee: Setzt Lock und emittiert "calendar_sync_employee".
API-Schwächen und Fixes
Advoware API
- Mehrtägige Termine:
datumBiswird korrekt für Enddatum verwendet; '00:00:00' als '23:59:59' interpretiert. - Zeitformate: Robuste Parsing mit Fallbacks.
- Keine 24h-Limit: Termine können länger als 24h sein; Google Calendar unterstützt das.
Google Calendar API
- Zeitbereiche: Akzeptiert Events >24h ohne Probleme.
- Rate Limits: Backoff-Retry implementiert.
Step-Konfiguration
calendar_sync_cron_step.py
- Type: cron
- Flows: advoware-calendar-sync
calendar_sync_all_step.py
- Type: event
- Subscribes: calendar_sync_all
- Flows: advoware-calendar-sync
calendar_sync_event_step.py
- Type: event
- Subscribes: calendar_sync_employee
- Flows: advoware-calendar-sync
calendar_sync_api_step.py
- Type: api
- Flows: advoware-calendar-sync
Setup
Umgebungsvariablen
# Google Calendar
GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=service-account.json
# Advoware API
ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/
ADVOWARE_PRODUCT_ID=64
ADVOWARE_APP_ID=your_app_id
ADVOWARE_API_KEY=your_api_key
ADVOWARE_KANZLEI=your_kanzlei
ADVOWARE_DATABASE=your_database
ADVOWARE_USER=your_user
ADVOWARE_ROLE=2
ADVOWARE_PASSWORD=your_password
ADVOWARE_TOKEN_LIFETIME_MINUTES=55
ADVOWARE_API_TIMEOUT_SECONDS=30
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB_CALENDAR_SYNC=1
REDIS_TIMEOUT_SECONDS=5
# Debug
CALENDAR_SYNC_DEBUG_EMPLOYEES=PB,AI # Optional, filter employees
Verwendung
Manueller Sync
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
-H "Content-Type: application/json" \
-d '{"kuerzel": "PB"}'
Automatischer Sync
Cron-Step läuft täglich.
Fehlerbehandlung und Logging
- Locking: Redis NX/EX verhindert parallele Syncs.
- Logging: context.logger für Workbench-Sichtbarkeit.
- API-Fehler: Retry mit Backoff.
- Parsing-Fehler: Robuste Fallbacks.
Sicherheit
- Service Account für Google.
- HMAC für Advoware.
- Redis für Locking.
Bekannte Probleme
- Recurring-Events: Begrenzte Unterstützung.
- Performance: Bei vielen Terminen Paginierung prüfen.
Letzte Änderungen
- Refaktorierung zu event-driven Design ohne PostgreSQL Hub.
- Fixes für mehrtägige Termine: Korrekte Verwendung von
datumBis. - Entfernung 24h-Limit; Google Calendar unterstützt lange Events.
- Per-Employee Locking mit Redis.
- Logging via context.logger für Workbench.
- Neue Schritte: calendar_sync_all_step.py, calendar_sync_cron_step.py.
- Workbench-Gruppierung: "advoware-calendar-sync".