Fix calendar sync Phase 3 logic for missing Advoware appointments when source is Google; update README with Advoware API peculiarities
This commit is contained in:
@@ -143,6 +143,11 @@ Beide Systeme werden auf gemeinsames Format normalisiert (Berlin TZ):
|
||||
- **API-Zuverlässigkeit**: Manchmal erfolgreicher POST, aber `frNr: None` (trotz gültiger Response). 500-Fehler bei Bad Requests. Keine Timestamp-Details in Responses.
|
||||
- **Zeitzonen**: Alles implizit Berlin; Code konvertiert explizit.
|
||||
- **Andere Bugs**: `zuletztGeaendertAm` für Timestamps, aber Format unzuverlässig.
|
||||
- **DELETE Responses**: DELETE-Anfragen geben manchmal einen leeren Body zurück, was zu `JSONDecodeError` führt. Code fängt dies mit try/except ab und gibt `None` zurück, um den Sync nicht zu brechen.
|
||||
- **frNr Wiederverwendung**: frNr sind sequentiell und werden nicht wiederverwendet. Getestet durch Erstellen/Löschen/Erstellen: z.B. 85861, 85862, delete 85861, nächstes Create 85863. Kein Risiko für DB-Konflikte durch ID-Reuse.
|
||||
- **Timestamp-basierte Updates**: Um Race-Conditions und redundante Syncs zu vermeiden, werden Updates in Phase 4 nur durchgeführt, wenn der API-Timestamp der Quelle > `last_sync` (gesetzt auf den API-Timestamp nach erfolgreichem Write).
|
||||
- **Soft Deletes und Partielle Unique Indexes**: Gelöschte Termine werden mit `deleted = TRUE` markiert, nicht entfernt. Partielle Unique Indexes (z.B. `WHERE deleted = FALSE`) verhindern Duplikate für aktive Einträge.
|
||||
- **Anonymisierung**: Optionale Anonymisierung sensibler Daten (Text, Notiz, Ort) bei Advoware → Google Sync, um Datenschutz zu wahren (z.B. `text='Advoware blocked'`).
|
||||
|
||||
### Google Calendar API (Zuverlässig)
|
||||
- **Zeitformate**: `dateTime` als ISO mit TZ (z.B. `'2025-01-01T10:00:00+01:00'`), `date` für All-Day. Code parst mit `fromisoformat` und `.rstrip('Z')`.
|
||||
@@ -253,465 +258,3 @@ Cron-Step für regelmäßige Ausführung.
|
||||
|
||||
## Erweiterungen
|
||||
|
||||
- Inkrementelle Syncs basierend auf `last_sync`.
|
||||
- Mehr Strategien (z.B. 'manual').
|
||||
- Webhooks für Echtzeit.
|
||||
- Tests und Monitoring.
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das System synchronisiert Termine zwischen:
|
||||
- **Advoware**: Zentrale Terminverwaltung mit detaillierten Informationen (aber vielen API-Bugs).
|
||||
- **Google Calendar**: Benutzerfreundliche Kalenderansicht für jeden Mitarbeiter.
|
||||
- **PostgreSQL Hub**: Zentraler Datenspeicher für State, Policies und Audit-Logs.
|
||||
|
||||
## Architektur
|
||||
|
||||
### Hub-Design
|
||||
- **Single Source of Truth**: Alle Sync-Informationen werden in PostgreSQL gespeichert.
|
||||
- **Policies**: Enums für Sync-Strategien (`source_system_wins`, `last_change_wins`) und Flags für Schreibberechtigung (`advoware_write_allowed`).
|
||||
- **Status-Tracking**: `sync_status` ('pending', 'synced', 'failed') für Monitoring und Retries.
|
||||
- **Transaktionen**: Jede DB-Operation läuft in separaten Transaktionen; Fehler beeinflussen nur den aktuellen Eintrag.
|
||||
- **Soft Deletes**: Gelöschte Termine werden markiert, nicht entfernt.
|
||||
- **Phasen-basierte Verarbeitung**: Sync in 4 Phasen, um Neue, Deletes und Updates zu trennen.
|
||||
|
||||
### Sync-Phasen
|
||||
1. **Phase 1: Neue Einträge Advoware → Google** - Erstelle Google-Events für neue Advoware-Termine, dann DB-Insert.
|
||||
2. **Phase 2: Neue Einträge Google → Advoware** - Erstelle Advoware-Termine für neue Google-Events, dann DB-Insert.
|
||||
3. **Phase 3: Gelöschte Einträge identifizieren** - Handle Deletes/Recreates basierend auf Strategie.
|
||||
4. **Phase 4: Bestehende Einträge updaten** - Update bei Änderungen, basierend auf Timestamps.
|
||||
|
||||
### Datenbank-Schema
|
||||
```sql
|
||||
-- Haupt-Tabelle
|
||||
CREATE TABLE calendar_sync (
|
||||
sync_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_kuerzel VARCHAR(10) NOT NULL,
|
||||
advoware_frnr INTEGER,
|
||||
google_event_id VARCHAR(255),
|
||||
source_system source_system_enum NOT NULL,
|
||||
sync_strategy sync_strategy_enum NOT NULL DEFAULT 'source_system_wins',
|
||||
sync_status sync_status_enum NOT NULL DEFAULT 'synced',
|
||||
advoware_write_allowed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
last_sync TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Enums
|
||||
CREATE TYPE source_system_enum AS ENUM ('advoware', 'google');
|
||||
CREATE TYPE sync_strategy_enum AS ENUM ('source_system_wins', 'last_change_wins');
|
||||
CREATE TYPE sync_status_enum AS ENUM ('pending', 'synced', 'failed');
|
||||
|
||||
-- Audit-Tabelle
|
||||
CREATE TABLE calendar_sync_audit (
|
||||
id SERIAL PRIMARY KEY,
|
||||
action VARCHAR(10) NOT NULL, -- INSERT, UPDATE, DELETE
|
||||
timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indizes (angepasst für Soft Deletes)
|
||||
CREATE UNIQUE INDEX idx_calendar_sync_advoware ON calendar_sync (employee_kuerzel, advoware_frnr) WHERE advoware_frnr IS NOT NULL AND deleted = FALSE;
|
||||
CREATE UNIQUE INDEX idx_calendar_sync_google ON calendar_sync (employee_kuerzel, google_event_id) WHERE google_event_id IS NOT NULL;
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### Phasen-Details
|
||||
|
||||
#### Phase 1: Neue Einträge Advoware → Google
|
||||
- Fetch Advoware-Termine.
|
||||
- Für jede frNr, die nicht in DB (deleted=FALSE) existiert: Standardisiere Daten, erstelle Google-Event, dann INSERT in DB mit `sync_status = 'synced'`.
|
||||
- Bei Fehlern: Warnung loggen, weitermachen (nicht abbrechen).
|
||||
|
||||
#### Phase 2: Neue Einträge Google → Advoware
|
||||
- Fetch Google-Events.
|
||||
- Für jeden event_id, der nicht in DB existiert: Standardisiere Daten, erstelle Advoware-Termin, dann INSERT in DB mit `sync_status = 'synced'`.
|
||||
- Bei frNr None (API-Bug): Skippen mit Warnung.
|
||||
- Bei Fehlern: Warnung loggen, weitermachen.
|
||||
|
||||
#### Phase 3: Gelöschte Einträge identifizieren
|
||||
- Für jeden DB-Eintrag: Prüfe, ob Termin in API fehlt.
|
||||
- Bei beiden fehlend: Soft Delete.
|
||||
- Bei einem fehlend: Recreate oder propagate Delete basierend auf Strategie.
|
||||
- Bei Fehlern: `sync_status = 'failed'`, Warnung.
|
||||
|
||||
#### Phase 4: Bestehende Einträge updaten
|
||||
- Für bestehende Einträge: Vergleiche standardisierte Daten.
|
||||
- Bei Differenzen: Update basierend auf Strategie und Timestamps.
|
||||
- Bei Fehlern: `sync_status = 'failed'`, Warnung.
|
||||
|
||||
### 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`.
|
||||
|
||||
## API-Schwächen und Fuckups
|
||||
|
||||
### Advoware API (Buggy und Inkonsistent)
|
||||
- **Case Sensitivity in Responses**: Feldnamen variieren – manchmal `'frNr'`, manchmal `'frnr'` (z.B. POST-Response: `{'frnr': 123}`). Code prüft beide (`result.get('frNr') or result.get('frnr')`), um None zu vermeiden.
|
||||
- **Zeitformate**: `datum`/`datumBis` als `'YYYY-MM-DD'` oder `'YYYY-MM-DDTHH:MM:SS'`. `uhrzeitVon`/`uhrzeitBis` separat (z.B. `'09:00:00'`). Fehlt `uhrzeitVon`, Fallback 09:00; fehlt `uhrzeitBis`, 10:00. Parsing muss beide Formate handhaben.
|
||||
- **Defaults und Fehlende Felder**: Viele Felder optional; Code setzt Fallbacks (z.B. `uhrzeitVon='09:00:00'`).
|
||||
- **Recurring-Unterstützung**: Keine RRULE; nur `turnus` (0/1) und `turnusArt` (0-?). Mapping zu Google RRULE ist vereinfacht und unvollständig.
|
||||
- **API-Zuverlässigkeit**: Manchmal erfolgreicher POST, aber `frNr: None` (trotz gültiger Response). 500-Fehler bei Bad Requests. Keine Timestamp-Details in Responses.
|
||||
- **Zeitzonen**: Alles implizit Berlin; Code konvertiert explizit.
|
||||
- **Andere Bugs**: `zuletztGeaendertAm` für Timestamps, aber Format unzuverlässig.
|
||||
|
||||
### Google Calendar API (Zuverlässig)
|
||||
- **Zeitformate**: `dateTime` als ISO mit TZ (z.B. `'2025-01-01T10:00:00+01:00'`), `date` für All-Day. Code parst mit `fromisoformat` und `.rstrip('Z')`.
|
||||
- **Zeitzonen**: Explizit (z.B. `'Europe/Berlin'`); Code konvertiert zu Berlin TZ.
|
||||
- **Recurring**: RRULE in `recurrence`; vollständig unterstützt.
|
||||
- **Updates**: `updated` Timestamp für last-change.
|
||||
- **Keine bekannten Bugs**: Zuverlässig, aber Rate-Limits möglich.
|
||||
|
||||
## Step-Konfiguration
|
||||
|
||||
### calendar_sync_event_step.py
|
||||
- **Type:** event
|
||||
- **Subscribes:** calendar.sync.triggered
|
||||
- **Flows:** advoware
|
||||
|
||||
**Event Data:**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"body": {
|
||||
"employee_kuerzel": "SB" // Optional, default "AI"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### PostgreSQL
|
||||
1. PostgreSQL 17 installieren und starten (localhost-only).
|
||||
2. Datenbank erstellen: `sudo -u postgres psql -f /tmp/create_db.sql`
|
||||
3. User und Berechtigungen setzen.
|
||||
|
||||
### Google API Credentials
|
||||
1. Google Cloud Console Projekt erstellen.
|
||||
2. Google Calendar API aktivieren.
|
||||
3. Service Account erstellen.
|
||||
4. `service-account.json` im Projekt bereitstellen.
|
||||
|
||||
### Advoware API Credentials
|
||||
OAuth-ähnliche Authentifizierung.
|
||||
|
||||
### Umgebungsvariablen
|
||||
```env
|
||||
# PostgreSQL
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_USER=calendar_sync_user
|
||||
POSTGRES_PASSWORD=your_password
|
||||
POSTGRES_DB_NAME=calendar_sync_db
|
||||
|
||||
# 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_ADVOWARE_CACHE=1
|
||||
REDIS_TIMEOUT_SECONDS=5
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Manueller Sync
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/events" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type": "calendar.sync.triggered", "data": {"body": {"employee_kuerzel": "SB"}}}'
|
||||
```
|
||||
|
||||
### Automatischer Sync
|
||||
Cron-Step für regelmäßige Ausführung.
|
||||
|
||||
## Fehlerbehandlung und Logging
|
||||
|
||||
- **Transaktionen**: Pro Operation separat; Rollback nur für diese.
|
||||
- **Logging**: Detailliert (Info/Debug für API, Warnung für Fehler).
|
||||
- **API-Fehler**: Retry mit Backoff für Google; robust gegen Advoware-Bugs.
|
||||
- **Datenfehler**: Fallbacks bei Parsing-Fehlern.
|
||||
|
||||
## Sicherheit
|
||||
|
||||
- DB-User mit minimalen Berechtigungen.
|
||||
- Schreibberechtigung-Flags verhindern unbefugte Änderungen.
|
||||
- Audit-Logs für Compliance.
|
||||
|
||||
## Bekannte Probleme
|
||||
|
||||
- Recurring-Events: Begrenzte Unterstützung; Advoware hat keine RRULE.
|
||||
- Timestamps: Fehlende in Google können zu Fallback führen.
|
||||
- Performance: Bei vielen Terminen könnte Paginierung helfen.
|
||||
|
||||
## Erweiterungen
|
||||
|
||||
- Inkrementelle Syncs basierend auf `last_sync`.
|
||||
- Mehr Strategien (z.B. 'manual').
|
||||
- Webhooks für Echtzeit.
|
||||
- Tests und Monitoring.
|
||||
|
||||
### Datenbank-Schema
|
||||
```sql
|
||||
-- Haupt-Tabelle
|
||||
CREATE TABLE calendar_sync (
|
||||
sync_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
employee_kuerzel VARCHAR(10) NOT NULL,
|
||||
advoware_frnr INTEGER,
|
||||
google_event_id VARCHAR(255),
|
||||
source_system source_system_enum NOT NULL,
|
||||
sync_strategy sync_strategy_enum NOT NULL DEFAULT 'source_system_wins',
|
||||
sync_status sync_status_enum NOT NULL DEFAULT 'synced',
|
||||
advoware_write_allowed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
last_sync TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Enums
|
||||
CREATE TYPE source_system_enum AS ENUM ('advoware', 'google');
|
||||
CREATE TYPE sync_strategy_enum AS ENUM ('source_system_wins', 'last_change_wins');
|
||||
CREATE TYPE sync_status_enum AS ENUM ('pending', 'synced', 'failed');
|
||||
|
||||
-- Audit-Tabelle
|
||||
CREATE TABLE calendar_sync_audit (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sync_id UUID NOT NULL,
|
||||
action VARCHAR(10) NOT NULL, -- INSERT, UPDATE, DELETE
|
||||
timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indizes
|
||||
CREATE UNIQUE INDEX idx_calendar_sync_advoware ON calendar_sync (employee_kuerzel, advoware_frnr) WHERE advoware_frnr IS NOT NULL;
|
||||
CREATE UNIQUE INDEX idx_calendar_sync_google ON calendar_sync (employee_kuerzel, google_event_id) WHERE google_event_id IS NOT NULL;
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### Bidirektionale Synchronisation
|
||||
|
||||
#### Fetch und Standardisierung
|
||||
- Frische Daten werden von beiden APIs gefetcht (Zeitraum: 365 Tage zurück, 730 vor).
|
||||
- Daten werden standardisiert: Start/End, Text, Notiz, Ort, Dauertermin, Turnus.
|
||||
- Unterschiede werden basierend auf relevanten Feldern erkannt.
|
||||
|
||||
#### Verarbeitung bestehender Termine
|
||||
- Für jeden DB-Eintrag: Prüfe, ob Termin in beiden Systemen existiert.
|
||||
- Bei Unterschieden: Löse Konflikt basierend auf `sync_strategy`.
|
||||
- `source_system_wins`: Aktualisiere basierend auf `source_system`.
|
||||
- `last_change_wins`: Vergleiche Timestamps (`zuletztGeaendertAm` in Advoware, `updated` in Google); bei Fehlen: Fallback auf `source_system_wins`.
|
||||
- Schreibberechtigung: Nur aktualisiere Advoware, wenn `advoware_write_allowed = TRUE`.
|
||||
- Bei Fehlen in einem System: Rekreiere oder propagiere Delete basierend auf Strategie.
|
||||
|
||||
#### Neue Termine
|
||||
- Neue aus Advoware: Erstelle in Google, füge DB-Eintrag hinzu (`source_system = 'advoware'`, `advoware_write_allowed = FALSE`).
|
||||
- Neue aus Google: Erstelle in Advoware (falls erlaubt), füge DB-Eintrag hinzu (`source_system = 'google'`, `advoware_write_allowed = TRUE`).
|
||||
|
||||
#### Löschungen
|
||||
- Wenn Termin in beiden fehlt: Soft Delete in DB.
|
||||
- Wenn in einem fehlt: Propagiere Delete basierend auf Strategie und Berechtigung.
|
||||
|
||||
## API-Endpunkte
|
||||
|
||||
### Advoware API
|
||||
- `GET /api/v1/advonet/Termine?kuerzel={kuerzel}&from={date}&to={date}` - Termine eines Mitarbeiters
|
||||
- `GET /api/v1/advonet/Termine?frnr={frnr}` - Einzelner Termin (für Timestamp)
|
||||
- `POST /api/v1/advonet/Termine` - Neuen Termin erstellen
|
||||
- `PUT /api/v1/advonet/Termine` - Termin aktualisieren
|
||||
- `DELETE /api/v1/advonet/Termine?frnr={frnr}` - Termin löschen
|
||||
|
||||
**API-Schwächen:**
|
||||
- Zeitformate: `datum`/`datumBis` als `YYYY-MM-DD` oder `YYYY-MM-DDTHH:MM:SS`, `uhrzeitVon`/`uhrzeitBis` separat.
|
||||
- Defaults: `uhrzeitVon` kann fehlen, dann 09:00; `uhrzeitBis` 10:00.
|
||||
- Keine Recurring-Unterstützung (RRULE); nur `turnus`/`turnusArt` als Flags.
|
||||
|
||||
### Google Calendar API
|
||||
- Kalender-Management (erstellen, ACL setzen)
|
||||
- Events: Listen, erstellen, aktualisieren, löschen
|
||||
- Recurring: RRULE-Unterstützung
|
||||
- Timestamps: `updated` für Änderungszeit
|
||||
|
||||
## Step-Konfiguration
|
||||
|
||||
### calendar_sync_event_step.py
|
||||
- **Type:** event
|
||||
- **Subscribes:** calendar.sync.triggered
|
||||
- **Flows:** advoware
|
||||
|
||||
**Event Data:**
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"body": {
|
||||
"employee_kuerzel": "SB" // Optional, default "AI" für Test
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### PostgreSQL
|
||||
1. PostgreSQL 17 installieren und starten (localhost-only).
|
||||
2. Datenbank erstellen: `sudo -u postgres psql -f /tmp/create_db.sql`
|
||||
3. User und Berechtigungen setzen.
|
||||
|
||||
### Google API Credentials
|
||||
1. Google Cloud Console Projekt erstellen.
|
||||
2. Google Calendar API aktivieren.
|
||||
3. Service Account erstellen.
|
||||
4. `service-account.json` im Projekt bereitstellen.
|
||||
|
||||
### Advoware API Credentials
|
||||
OAuth-ähnliche Authentifizierung.
|
||||
|
||||
### Umgebungsvariablen
|
||||
```env
|
||||
# PostgreSQL
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_USER=calendar_sync_user
|
||||
POSTGRES_PASSWORD=your_password
|
||||
POSTGRES_DB_NAME=calendar_sync_db
|
||||
|
||||
# Google Calendar
|
||||
GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=service-account.json
|
||||
|
||||
# Advoware API (wie zuvor)
|
||||
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 (falls verwendet)
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_DB_ADVOWARE_CACHE=1
|
||||
REDIS_TIMEOUT_SECONDS=5
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Manueller Sync
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/events" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type": "calendar.sync.triggered", "data": {"body": {"employee_kuerzel": "SB"}}}'
|
||||
```
|
||||
|
||||
### Automatischer Sync
|
||||
Cron-Step für regelmäßige Ausführung.
|
||||
|
||||
## Datenmapping
|
||||
|
||||
### Standardisierung
|
||||
Beide Systeme werden auf gemeinsames Format normalisiert:
|
||||
```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 → Google
|
||||
- All-Day: Wenn `dauertermin=1` oder Dauer >1 Tag.
|
||||
- Recurring: RRULE aus `turnus`/`turnusArt` (vereinfacht).
|
||||
|
||||
### Google → Advoware
|
||||
- Zeit: `datum`/`uhrzeitBis` aus Start/End.
|
||||
- Recurring: `turnus=1` wenn RRULE vorhanden.
|
||||
|
||||
## Fehlerbehandlung und Logging
|
||||
|
||||
- **Transaktionen**: Rollback bei Fehlern.
|
||||
- **Logging**: Detailliertes Info/Debug-Logging für jeden Schritt.
|
||||
- **API-Fehler**: Retry mit Backoff für Google; detailliert für Advoware.
|
||||
- **Datenfehler**: Fallbacks bei Parsing-Fehlern.
|
||||
|
||||
## Sicherheit
|
||||
|
||||
- DB-User mit minimalen Berechtigungen.
|
||||
- Schreibberechtigung-Flags verhindern unbefugte Änderungen.
|
||||
- Audit-Logs für Compliance.
|
||||
|
||||
## Bekannte Probleme
|
||||
|
||||
- Recurring-Events: Begrenzte Unterstützung; Advoware hat keine RRULE.
|
||||
- Timestamps: Fehlende in Google können zu Fallback führen.
|
||||
- Performance: Bei vielen Terminen könnte Paginierung helfen.
|
||||
|
||||
## Erweiterungen
|
||||
|
||||
- Inkrementelle Syncs basierend auf `last_sync`.
|
||||
- Mehr Strategien (z.B. 'manual').
|
||||
- Webhooks für Echtzeit.
|
||||
- Tests und Monitoring.
|
||||
Reference in New Issue
Block a user