Files
motia-iii/steps/advoware_cal_sync/README.md

8.7 KiB

Advoware Calendar Sync - Event-Driven Design

Dieser Abschnitt implementiert die bidirektionale Synchronisation zwischen Advoware-Terminen und Google Calendar. Das System nutzt einen event-driven Ansatz mit Motia III v1.0-RC, 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 in der iii Console via ctx.logger.

Sync-Phasen

  1. Cron-Step: Automatische Auslösung alle 15 Minuten.
  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):

{
    '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 alle 15 Minuten und emittiert "calendar_sync_all".
  • Trigger: cron("0 */15 * * * *") (6-field: Sekunde Minute Stunde Tag Monat Wochentag)

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" Event pro Employee.
  • Trigger: queue('calendar_sync_all')

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.
  • Trigger: queue('calendar_sync')

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".
  • Trigger: http('POST', '/advoware/calendar/sync')

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 (Motia III)

calendar_sync_cron_step.py

config = {
    'name': 'Calendar Sync Cron Job',
    'flows': ['advoware'],
    'triggers': [
        cron("0 */15 * * * *")  # Alle 15 Minuten (6-field format)
    ],
    'enqueues': ['calendar_sync_all']
}

calendar_sync_all_step.py

config = {
    'name': 'Calendar Sync All Step',
    'flows': ['advoware'],
    'triggers': [
        queue('calendar_sync_all')
    ],
    'enqueues': ['calendar_sync']
}

calendar_sync_event_step.py

config = {
    'name': 'Calendar Sync Event Step',
    'flows': ['advoware'],
    'triggers': [
        queue('calendar_sync')
    ],
    'enqueues': []
}

calendar_sync_api_step.py

config = {
    'name': 'Calendar Sync API Trigger',
    'flows': ['advoware'],
    'triggers': [
        http('POST', '/advoware/calendar/sync')
    ],
    'enqueues': ['calendar_sync', 'calendar_sync_all']
}

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

# Sync für einen bestimmten Mitarbeiter
curl -X POST "http://localhost:3111/advoware/calendar/sync" \
  -H "Content-Type: application/json" \
  -d '{"kuerzel": "PB"}'

# Sync für alle Mitarbeiter
curl -X POST "http://localhost:3111/advoware/calendar/sync" \
  -H "Content-Type: application/json" \
  -d '{"kuerzel": "ALL"}'

Automatischer Sync

Cron-Step läuft automatisch alle 15 Minuten.

Fehlerbehandlung und Logging

  • Locking: Redis NX/EX verhindert parallele Syncs.
  • Logging: ctx.logger für iii Console-Sichtbarkeit.
  • API-Fehler: Retry mit Backoff.
  • Parsing-Fehler: Robuste Fallbacks.

Sicherheit

  • Service Account für Google Calendar API.
  • HMAC-512 Authentifizierung für Advoware API.
  • Redis für Concurrency-Control.

Bekannte Probleme

  • Recurring-Events: Begrenzte Unterstützung für komplexe Wiederholungen.
  • Performance: Bei vielen Terminen Paginierung beachten.
  • Timezone-Handling: Alle Operationen in Europe/Berlin TZ.

Datenfluss

Cron (alle 15min)
  → calendar_sync_cron_step
      → ctx.enqueue(topic: "calendar_sync_all")
          → calendar_sync_all_step
              → Fetch Employees from Advoware
              → For each Employee:
                  → Set Redis Lock (key: calendar_sync:employee:{kuerzel})
                  → ctx.enqueue(topic: "calendar_sync", data: {kuerzel, ...})
                      → calendar_sync_event_step
                          → Fetch Advoware Termine (frNr, datum, text, etc.)
                          → Fetch Google Calendar Events
                          → 4-Phase Sync:
                              1. New from Advoware → Google
                              2. New from Google → Advoware
                              3. Process Deletes
                              4. Process Updates
                          → Clear Redis Lock

Weitere Dokumentation

Migration Notes

Dieses System wurde von Motia v0.17 nach Motia III v1.0-RC migriert:

Wichtige Änderungen:

  • type: 'event'triggers: [queue('topic')]
  • type: 'cron'triggers: [cron('expression')] (6-field format)
  • type: 'api'triggers: [http('METHOD', 'path')]
  • context.emit()ctx.enqueue()
  • emits: [...]enqueues: [...]
  • Relative Imports → Absolute Imports mit sys.path.insert()
  • Motia Workbench → iii Console

Kompatibilität:

  • Alle 4 Steps vollständig migriert
  • Google Calendar API Integration unverändert
  • Advoware API Integration unverändert
  • Redis Locking-Mechanismus unverändert
  • Datenbank-Schema kompatibel