Files

5.9 KiB

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) oder SQLite (stdlib) via DB_BACKEND Env-Var
  • API: FastAPI + Uvicorn + Jinja2
  • Docker: Multi-Stage Build nicht verwendet, simples Slim-Image
  • Dependencies: requests, icalendar, recurring-ical-events, mysql-connector-python, fastapi, uvicorn, jinja2

Projektstruktur

.
├── sync.py              # Hauptskript (alles in einer Datei)
├── api/
│   ├── main.py          # FastAPI App (REST API + HTML UI)
│   ├── database.py      # DB-Verbindung (MySQL + SQLite)
│   └── templates/
│       └── index.html   # Jinja2 Template für Web-UI
├── demo/
│   └── seed.py          # SQLite Demo-Datenbank erstellen
├── demo.sh              # Lokaler Demo-Start (ohne Docker)
├── 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 für den Sync-Teil. sync.py ist ein eigenständiges Python-Skript mit einer Endlosschleife (main()time.sleep()).

FastAPI Applikation

Die API (api/main.py) ist eine separarte FastAPI-App die als eigenständiger Container läuft:

  • Web-UI: GET / - Jinja2 Template mit anstehenden Termine und Suchfunktion
  • REST API: GET /api/events - JSON-Liste zukünftiger Events
  • Einzeln-Event: GET /api/events/{id} - Einzelnes Event als JSON
  • Health Check: GET /api/health - Gibt {"status": "ok"} zurück
  • Läuft über Uvicorn auf Port 8000
  • Teilt sich die Datenbank mit sync.py, verwendet aber eigene DB-Verbindung (api/database.py)

Datenbank-Schema

Schema wird in ensure_schema() per CREATE TABLE IF NOT EXISTS erstellt. Bei Schema-Änderungen:

  • ensure_schema() in sync.py:83 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 die Zeitzone korrekt verarbeitet wird.

Soft-Delete

Events werden nicht gelöscht, sondern mit deleted=1 markiert (mark_missing_as_deleted()).

Tägliche E-Mail-Benachrichtigung

Sendet täglich um konfigurierte Uhrzeit eine HTML-Email mit anstehenden Terminen:

  • Nur Termine mit Uhrzeit (all_day=0), keine Ganztagstermine
  • Subject: Bei 1 Termin direkt "Kalender heute: HH:MM - Termin", bei mehreren "Kalender heute: X Termine"
  • Tracking via daily_notification_log Tabelle (verhindert Doppelversand)
  • Konfiguration über SMTP_* und NOTIFY_* Umgebungsvariablen
  • Funktionen: get_today_events(), should_notify(), send_notification(), log_notification()

Wöchentliche E-Mail-Benachrichtigung (Vorab-Info)

Sendet wöchentlich (standardmäßig Freitags) eine HTML-Email mit Terminen, die auf Suchbegriffe passen:

  • Sucht nach Begriffen in summary, description und location
  • Optionale Blacklist: Begriffe die ausgeschlossen werden (z.B. WEEKLY_BLACKLISTWORDS=Ausgeschlossen1,Ausgeschlossen2)
  • Zeitraum: Samstag bis Freitag der nächsten Woche
  • Subject: Bei 1 Termin "Vorab-Info: Termin am Mo, DD.MM.", bei mehreren "Vorab-Info: X Termine nächste Woche"
  • Tracking via weekly_notification_log Tabelle (verhindert Doppelversand)
  • Nur Termine mit Uhrzeit (all_day=0), keine Ganztagstermine
  • Keine Email wenn keine Treffer
  • Konfiguration über WEEKLY_* Umgebungsvariablen
  • Funktionen: parse_weekly_searchwords(), parse_weekly_blacklistwords(), get_weekly_events(), should_send_weekly(), send_weekly_notification(), log_weekly_notification(), check_and_send_weekly_notification()

Entwicklung

Lokaler Test (Docker)

# Dependencies installieren
pip install -r requirements.txt

# .env anlegen (aus .env.example kopieren)
cp .env.example .env

# Direkt ausführen
python sync.py

Lokaler Test (Demo, ohne MariaDB)

# Startet venv, erstellt SQLite Demo-DB mit 4 Events, startet API
./demo.sh

Oder manuell:

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python demo/seed.py
DB_BACKEND=sqlite DB_PATH=demo/demo.db python -m uvicorn api.main:app --port 8000

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)

Lizenz

MIT License - Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de)

  • Vollständiger Text in LICENSE
  • Lizenz-Header in allen Python-Dateien