- Created calendar_sync_utils.py with shared functions: - log_operation: Centralized logging with context support - connect_db: Database connection wrapper - get_google_service: Google Calendar API initialization - Updated imports in calendar_sync_event_step.py and audit_calendar_sync.py - Removed duplicated function definitions - Maintained function logic without changes - Improved code maintainability and reduced duplications
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.
Audit und Management Tool (audit_calendar_sync.py)
Das audit_calendar_sync.py Tool bietet umfassende Audit-, Management- und Debugging-Funktionen für die Calendar-Synchronisation. Es ermöglicht die Überprüfung der Sync-Integrität, das Aufräumen von Duplikaten und verwaisten Einträgen sowie detaillierte Abfragen einzelner Termine.
Verwendung
cd /opt/motia-app/bitbylaw
source python_modules/bin/activate
python steps/advoware_cal_sync/audit_calendar_sync.py <command> [options]
Befehle
audit <employee_kuerzel> <google|advoware> [--delete-orphaned-google]
Auditiert Sync-Einträge für einen spezifischen Mitarbeiter und prüft deren Existenz in beiden Systemen.
Parameter:
employee_kuerzel: Mitarbeiter-Kürzel (z.B. "SB", "UR")google|advoware: System, das auditiert werden soll--delete-orphaned-google: Optional, löscht Google-Events die in Google existieren aber nicht in der DB
Beispiel:
# Audit Google Calendar für Mitarbeiter SB
python audit_calendar_sync.py audit SB google
# Audit Advoware für Mitarbeiter UR mit Löschung verwaister Google-Events
python audit_calendar_sync.py audit UR google --delete-orphaned-google
Ausgabe:
- Anzahl der DB-Einträge
- Anzahl der Events im Zielsystem
- Anzahl existierender/verwaiste Einträge
- Details zu verwaisten Einträgen
delete-calendar <employee_kuerzel>
Löscht den Google Calendar für einen spezifischen Mitarbeiter (falls vorhanden).
Beispiel:
python audit_calendar_sync.py delete-calendar SB
list-all
Listet alle Google Calendars auf, einschließlich Name, ID, Primary-Status und Access-Role.
Beispiel:
python audit_calendar_sync.py list-all
Ausgabe:
=== All Google Calendars (27) ===
AW-SB (ID: abc123@group.calendar.google.com, Primary: False, Access: owner)
AW-UR (ID: def456@group.calendar.google.com, Primary: False, Access: owner)
...
find-duplicates
Findet duplizierte Google Calendars nach Namen.
Beispiel:
python audit_calendar_sync.py find-duplicates
Ausgabe:
=== Duplicate Calendars Found (2 unique names with duplicates) ===
Total duplicate calendars: 3
Calendar Name: 'AW-SB' - 2 instances
ID: abc123@group.calendar.google.com, Primary: False, Access Role: owner
ID: xyz789@group.calendar.google.com, Primary: False, Access Role: owner
delete-duplicates
Findet und löscht duplizierte Calendars (behält jeweils einen pro Namen).
Beispiel:
python audit_calendar_sync.py delete-duplicates
find-orphaned
Findet AW-* Calendars ohne entsprechende Mitarbeiter in der Datenbank.
Beispiel:
python audit_calendar_sync.py find-orphaned
cleanup-orphaned
Findet und löscht verwaiste AW-* Calendars.
Beispiel:
python audit_calendar_sync.py cleanup-orphaned
query-frnr <frnr>
Zeigt alle Sync-Informationen für eine spezifische Advoware frNr.
Beispiel:
python audit_calendar_sync.py query-frnr 79291
Ausgabe:
=== Sync Information for frNr: 79291 ===
Found 1 sync entry
Sync ID: 6ee9ba95-8aff-4868-9171-c10a8789427c
Employee: UR
Advoware frNr: 79291
Google Event ID: jao7r00j26lt1i0chk454bi9as
Source System: advoware
Sync Strategy: source_system_wins
Sync Status: synced
Last Sync: 2025-10-24 23:30:17.692668+00:00
Created: 2025-10-24 07:22:41.729295+00:00
Updated: 2025-10-24 07:22:41.729295+00:00
query-event <event_id>
Zeigt Sync-Informationen für eine spezifische Google Event ID.
Beispiel:
python audit_calendar_sync.py query-event jao7r00j26lt1i0chk454bi9as
verify-sync <frnr>
Vollständige Sync-Verifikation: Prüft einen Termin in beiden Systemen (Advoware und Google Calendar).
Beispiel:
python audit_calendar_sync.py verify-sync 79291
Ausgabe:
=== Sync Verification for frNr: 79291 ===
Employee: UR
Sync Status: synced
Last Sync: 2025-10-24 23:30:17.692668+00:00
--- Checking Advoware ---
✅ Found in Advoware:
Subject: Jour fixe iS Neomi - Teilnahme im Einzelfall
Date: 2024-06-04T17:00:00
Time: N/A
End Time: 19:00:00
End Date: 2026-02-03T00:00:00
Last Modified: 2025-09-29T11:55:43.624
frNr: 79291
--- Checking Google Calendar ---
✅ Found in Google Calendar:
Summary: Advoware (frNr: 79291)
Start: 2024-06-04T17:00:00+02:00
End: 2024-06-04T19:00:00+02:00
--- Sync Status Summary ---
✅ Synchronized: Exists in both systems
Technische Details
Datenbank-Integration
- Verwendet PostgreSQL-Verbindung aus
config.py - Tabelle:
calendar_sync - Felder:
sync_id,employee_kuerzel,advoware_frnr,google_event_id, etc.
API-Integration
- Google Calendar API:
calendarList().list()mit Paginierung (maxResults=250) - Advoware API:
GET /api/v1/advonet/TerminemitfrnrFilter - Automatische Token-Verwaltung und Fehlerbehandlung
Sicherheit
- Verwendet bestehende Service-Account und API-Credentials
- Keine zusätzlichen Berechtigungen erforderlich
Häufige Anwendungsfälle
1. Nach der Erstinstallation
# Alle Calendars auflisten
python audit_calendar_sync.py list-all
# Duplikate finden und entfernen
python audit_calendar_sync.py find-duplicates
python audit_calendar_sync.py delete-duplicates
# Verwaiste Calendars entfernen
python audit_calendar_sync.py find-orphaned
python audit_calendar_sync.py cleanup-orphaned
2. Bei Sync-Problemen
# Sync-Status für einen Mitarbeiter prüfen
python audit_calendar_sync.py audit SB google
# Einzelnen Termin verifizieren
python audit_calendar_sync.py verify-sync 79291
# Sync-Informationen abfragen
python audit_calendar_sync.py query-frnr 79291
3. Regelmäßige Wartung
# Wöchentliche Überprüfung auf Duplikate
python audit_calendar_sync.py find-duplicates
# Monatliche Bereinigung verwaister Einträge
python audit_calendar_sync.py cleanup-orphaned
Fehlerbehandlung
- API-Fehler: Automatische Retry-Logik mit Backoff
- Berechtigungsfehler: Klare Fehlermeldungen mit Lösungsvorschlägen
- Netzwerkprobleme: Timeout-Handling und Wiederholungen
- Dateninkonsistenzen: Detaillierte Logging für Debugging
Performance
- Paginierung: Automatische Handhabung großer Resultsets
- Batch-Verarbeitung: Effiziente API-Calls mit minimalen Requests
- Caching: Wiederverwendung von API-Verbindungen wo möglich
Logging
Alle Operationen werden über context.logger geloggt und sind in der Motia Workbench sichtbar. Zusätzliche Debug-Informationen werden auf der Konsole ausgegeben.