- 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
190 lines
6.0 KiB
Markdown
190 lines
6.0 KiB
Markdown
# 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
|
|
1. **Cron-Step**: Tägliche Auslösung des Syncs.
|
|
2. **All-Step**: Fetcht alle Mitarbeiter und emittiert Events pro Employee.
|
|
3. **Employee-Step**: Synchronisiert Termine für einen einzelnen Mitarbeiter.
|
|
|
|
### Datenmapping und Standardisierung
|
|
Beide Systeme werden auf gemeinsames Format normalisiert (Berlin TZ):
|
|
```python
|
|
{
|
|
'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), oder `datum` als datetime.
|
|
- End: `datumBis` + `uhrzeitBis` (Fallback 10:00), oder `datum` + 1h.
|
|
- All-Day: `dauertermin=1` oder Dauer >1 Tag.
|
|
- Recurring: `turnus`/`turnusArt` (vereinfacht, keine RRULE).
|
|
|
|
#### Google → Standard
|
|
- Start/End: `dateTime` oder `date` (All-Day).
|
|
- All-Day: `dauertermin=1` wenn All-Day oder Dauer >1 Tag.
|
|
- Recurring: RRULE aus `recurrence`.
|
|
|
|
#### Standard → Advoware
|
|
- POST/PUT: `datum`/`uhrzeitBis`/`datumBis` aus start/end.
|
|
- Defaults: `vorbereitungsDauer='00:00:00'`, `sb`/`anwalt`=employee_kuerzel.
|
|
|
|
#### Standard → Google
|
|
- All-Day: `date` statt `dateTime`, 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**: `datumBis` wird 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
|
|
```env
|
|
# 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
|
|
```bash
|
|
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".
|
|
|