From b465e57f4727a957c3ad703c593de34d58162f33 Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Sat, 1 Aug 2026 15:13:35 +0200 Subject: [PATCH] docs: add README.md and AGENTS.md --- AGENTS.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..5b8bc7e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,93 @@ +# AGENTS.md + +Richtlinien für AI-Agenten die mit dem calender_sync Projekt arbeiten. + +## Projektübersicht + +Python-basierter Google Calendar → MariaDB Synchronizer. Läuft als Docker Container im Homelab und pollt periodisch einen ICS-Feed. + +## Technologie-Stack + +- **Sprache**: Python 3.12 +- **Datenbank**: MariaDB (mysql-connector-python) +- **Docker**: Multi-Stage Build nicht verwendet, simples Slim-Image +- **Dependencies**: requests, icalendar, recurring-ical-events, mysql-connector-python + +## Projektstruktur + +``` +. +├── sync.py # Hauptskript (alles in einer Datei) +├── requirements.txt # Python Dependencies +├── Dockerfile # Docker Image Definition +├── docker-compose.yml # Docker Compose Konfiguration +├── mariadb-setup.sql # Manuelles DB-Setup Script +├── .env.example # Beispiel-Umgebungsvariablen +└── .github/workflows/ # CI/CD (Docker Build + Push) +``` + +## Wichtige Hinweise + +### Kein Framework +Das Projekt verwendet kein Web-Framework. `sync.py` ist ein eigenständiges Python-Skript mit einer Endlosschleife (`main()` → `time.sleep()`). + +### Datenbank-Schema +Schema wird in `ensure_schema()` per `CREATE TABLE IF NOT EXISTS` erstellt. Bei Schema-Änderungen: +- `ensure_schema()` in `sync.py:91` anpassen +- MariaDB-kompatibles SQL verwenden (kein PostgreSQL-Specific) +- Indexe für Performance bedenken + +### Event-Expansion +Verwendet `recurring_ical_events` Bibliothek für RRULE/EXDATE/RECURRENCE-ID Expansion. Fenster wird über `WINDOW_PAST_DAYS`/`WINDOW_FUTURE_DAYS` gesteuert. + +### UTC-Normalisierung +Alle Zeiten werden in naive UTC datetime konvertiert (`to_naive_utc()`). Bei Datumsänderungen sicherstellen, dass Zeitzone korrekt处理 wird. + +### Soft-Delete +Events werden nicht gelöscht, sondern mit `deleted=1` markiert (`mark_missing_as_deleted()`). + +## Entwicklung + +### Lokaler Test +```bash +# Dependencies installieren +pip install -r requirements.txt + +# .env anlegen (aus .env.example kopieren) +cp .env.example .env + +# Direkt ausführen +python sync.py +``` + +### Linting & Type Checking +Keine Linting/Type-Checking Tools konfiguriert. Bei Bedarf hinzufügen: +- `ruff` für Linting +- `mypy` für Type Checking + +### Tests +Keine Tests vorhanden. Bei Bedarf: +- `pytest` als Test-Framework verwenden +- Mock für `requests.get()` und `mysql.connector` erstellen +- Unit Tests für `to_naive_utc()`, `instance_key_for()`, `expand_events()` + +## Code-Style + +- Kein Docstring-Standard definiert +- Logging über `logging` Modul mit configurable Level +- Error Handling: Exceptions fangen, loggen, aber nicht verschlucken +- Keine externen Config-Libraries (nur `os.environ`) + +## Docker + +- Image basiert auf `python:3.12-slim` +- Kein Multi-Stage Build nötig (einfaches Projekt) +- `PYTHONUNBUFFERED=1` für Logging im Container +- Watchtower Label für Auto-Updates + +## CI/CD + +GitHub Actions Workflow: +- Baut Docker Image bei Push zu `main` +- Published nach `ghcr.io/skoelle/calender_sync:latest` +- Keine Tests im Workflow (derzeit) diff --git a/README.md b/README.md index b34e551..1e3e90d 100644 --- a/README.md +++ b/README.md @@ -1 +1,105 @@ # calender_sync + +Google Calendar (ICS-Feed) → MariaDB Sync fürs Homelab. + +Läuft als Docker Container, pollt periodisch einen privaten Google Calendar ICS-Feed, expandiert RRULE/EXDATE/RECURRENCE-ID und schreibt einzelne Instanzen in eine MariaDB-Datenbank. + +## Features + +- Automatische Synchronisation alle 15 Minuten (konfigurierbar) +- Expandiert wiederkehrende Events (RRULE) und Serien-Exceptions (EXDATE, RECURRENCE-ID) +- Soft-Delete: Entfernte Events werden als `deleted=1` markiert, nicht gelöscht +- Optionales Database-Bootstrap: Erstellt DB und User automatisch bei `DB_BOOTSTRAP=true` +- Zeitfenster-konfiguration für Vergangenheit/Future (standardmäßig -90 Tage / +365 Tage) + +## Voraussetzungen + +- Docker & Docker Compose +- MariaDB Instanz (z.B. als Proxmox LXC) +- Google Calendar mit privatem ICS-Feed + +## Quick Start + +1. **MariaDB Setup** + + Entweder manuell ausführen: + ```bash + mysql -u root -p < mariadb-setup.sql + ``` + + Oder automatisch beim Start (in `.env` setzen): + ``` + DB_BOOTSTRAP=true + DB_ROOT_USER=root + DB_ROOT_PASSWORD=dein_root_passwort + ``` + +2. **.env anlegen** + + ```bash + cp .env.example .env + ``` + + Variablen anpassen, insbesondere: + - `ICS_URL`: Privater ICS-Link aus den Google Calendar Einstellungen + - `DB_PASSWORD`: Sicheres Passwort für den calendar_sync User + +3. **Starten** + + ```bash + docker compose up -d + ``` + +## Konfiguration + +| Variable | Default | Beschreibung | +|----------|---------|--------------| +| `ICS_URL` | *required* | Privater ICS-Feed aus Google Calendar | +| `CALENDAR_LABEL` | `default` | Label für den Kalender (für Multi-Kalender) | +| `DB_HOST` | `mariadb.fritz.box` | MariaDB Host | +| `DB_PORT` | `3306` | MariaDB Port | +| `DB_NAME` | `calendar_sync` | Datenbank-Name | +| `DB_USER` | *required* | Datenbank-User | +| `DB_PASSWORD` | *required* | Datenbank-Passwort | +| `SYNC_INTERVAL_MINUTES` | `15` | Sync-Intervall in Minuten | +| `WINDOW_PAST_DAYS` | `90` | Wie viele Tage in die Vergangenheit synchronisieren | +| `WINDOW_FUTURE_DAYS` | `365` | Wie viele Tage in die Zukunft synchronisieren | +| `LOG_LEVEL` | `INFO` | Python Logging Level | +| `DB_BOOTSTRAP` | `false` | DB + User beim Start erstellen | +| `DB_ROOT_USER` | - | Root-User fürs Bootstrap | +| `DB_ROOT_PASSWORD` | - | Root-Passwort fürs Bootstrap | + +## Datenbank-Schema + +Tabelle `calendar_events`: + +| Spalte | Typ | Beschreibung | +|--------|-----|--------------| +| `id` | BIGINT PK | Auto-Increment | +| `calendar_label` | VARCHAR(64) | Kalender-Label | +| `instance_key` | VARCHAR(255) | SHA1-basierte eindeutige Instanz-ID | +| `uid` | VARCHAR(255) | ICS UID | +| `recurrence_id` | VARCHAR(64) | RECURRENCE-ID bei Serien-Exceptions | +| `summary` | VARCHAR(512) | Titel | +| `description` | TEXT | Beschreibung | +| `location` | VARCHAR(512) | Ort | +| `start_at` | DATETIME | Startzeit (UTC) | +| `end_at` | DATETIME | Endzeit (UTC) | +| `all_day` | TINYINT(1) | Ganz-tagig Flag | +| `status` | VARCHAR(32) | Event-Status | +| `deleted` | TINYINT(1) | Soft-Delete Flag | +| `last_seen_at` | DATETIME | Letzte Synchronisation | +| `created_at` | DATETIME | Erstellungszeitpunkt | +| `updated_at` | DATETIME | Letzte Änderung | + +## CI/CD + +GitHub Actions Workflow (`docker-publish.yml`) baut und published das Docker Image automatisch nach GitHub Container Registry: + +``` +ghcr.io/skoelle/calender_sync:latest +``` + +## Lizenz + +Keine Lizenz angegeben.