demo showcase README.md

This commit is contained in:
2026-08-10 17:01:27 +02:00
parent 946c2832ea
commit e10c6fc169
4 changed files with 63 additions and 16 deletions
+20 -3
View File
@@ -9,7 +9,7 @@ Python-basierter Google Calendar → MariaDB Synchronizer. Läuft als Docker Con
## Technologie-Stack ## Technologie-Stack
- **Sprache**: Python 3.12 - **Sprache**: Python 3.12
- **Datenbank**: MariaDB (mysql-connector-python) - **Datenbank**: MariaDB (mysql-connector-python) oder SQLite (stdlib) via `DB_BACKEND` Env-Var
- **API**: FastAPI + Uvicorn + Jinja2 - **API**: FastAPI + Uvicorn + Jinja2
- **Docker**: Multi-Stage Build nicht verwendet, simples Slim-Image - **Docker**: Multi-Stage Build nicht verwendet, simples Slim-Image
- **Dependencies**: requests, icalendar, recurring-ical-events, mysql-connector-python, fastapi, uvicorn, jinja2 - **Dependencies**: requests, icalendar, recurring-ical-events, mysql-connector-python, fastapi, uvicorn, jinja2
@@ -21,9 +21,12 @@ Python-basierter Google Calendar → MariaDB Synchronizer. Läuft als Docker Con
├── sync.py # Hauptskript (alles in einer Datei) ├── sync.py # Hauptskript (alles in einer Datei)
├── api/ ├── api/
│ ├── main.py # FastAPI App (REST API + HTML UI) │ ├── main.py # FastAPI App (REST API + HTML UI)
│ ├── database.py # DB-Verbindung für die API │ ├── database.py # DB-Verbindung (MySQL + SQLite)
│ └── templates/ │ └── templates/
│ └── index.html # Jinja2 Template für Web-UI │ └── 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 ├── requirements.txt # Python Dependencies
├── Dockerfile # Docker Image Definition ├── Dockerfile # Docker Image Definition
├── docker-compose.yml # Docker Compose Konfiguration ├── docker-compose.yml # Docker Compose Konfiguration
@@ -71,7 +74,7 @@ Sendet täglich um konfigurierte Uhrzeit eine HTML-Email mit anstehenden Termine
## Entwicklung ## Entwicklung
### Lokaler Test ### Lokaler Test (Docker)
```bash ```bash
# Dependencies installieren # Dependencies installieren
pip install -r requirements.txt pip install -r requirements.txt
@@ -83,6 +86,20 @@ cp .env.example .env
python sync.py python sync.py
``` ```
### Lokaler Test (Demo, ohne MariaDB)
```bash
# Startet venv, erstellt SQLite Demo-DB mit 4 Events, startet API
./demo.sh
```
Oder manuell:
```bash
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 ### Linting & Type Checking
Keine Linting/Type-Checking Tools konfiguriert. Bei Bedarf hinzufügen: Keine Linting/Type-Checking Tools konfiguriert. Bei Bedarf hinzufügen:
- `ruff` für Linting - `ruff` für Linting
+25
View File
@@ -53,6 +53,31 @@ Läuft als Docker Container, pollt periodisch einen privaten Google Calendar ICS
docker compose up -d docker compose up -d
``` ```
## Demo / Lokaler Test (ohne Docker)
Schneller Weg um Web-UI und API lokal zu testen, ohne MariaDB oder Docker:
```bash
./demo.sh
```
Das Script erstellt eine virtuelle Python-Umgebung, installiert Dependencies, legt eine SQLite Demo-Datenbank mit 4 Beispiel-Terminen an und startet die API auf Port 8000.
**Endpoints:**
- Web-UI: http://localhost:8000/
- API JSON: http://localhost:8000/api/events
- Health: http://localhost:8000/api/health
**Demo-Events (4 Stück):**
| Termin | Zeit | Status |
|--------|------|--------|
| Team Daily | heute 14:0015:00 | CONFIRMED |
| Yoga Kurs | heute 18:3020:00 | CONFIRMED |
| Projekt-Deadline | morgen Ganztag | TENTATIVE |
| Arzttermin | übermorgen 10:0011:30 | CONFIRMED |
Die Demo verwendet SQLite (`DB_BACKEND=sqlite`) statt MariaDB. Die Events werden relativ zum heutigen Datum erstellt.
## Konfiguration ## Konfiguration
| Variable | Default | Beschreibung | | Variable | Default | Beschreibung |
+1 -1
View File
@@ -116,7 +116,7 @@
} }
.event-location::before { .event-location::before {
content: "\\1F4CD "; content: "\1F4CD ";
} }
.badge { .badge {
+17 -12
View File
@@ -42,52 +42,57 @@ def seed():
now = datetime.now().replace(second=0, microsecond=0, tzinfo=None) now = datetime.now().replace(second=0, microsecond=0, tzinfo=None)
today = now.replace(hour=0, minute=0, second=0) today = now.replace(hour=0, minute=0, second=0)
# Wenn es nach 14 Uhr ist, alle Termine einen Tag verschieben
# damit sie in der Demo immer sichtbar sind
day_offset = 1 if now.hour >= 14 else 0
base = today + timedelta(days=day_offset)
events = [ events = [
{ {
"calendar_label": "demo", "calendar_label": "demo",
"instance_key": f"team-daily-{today.strftime('%Y%m%d')}", "instance_key": f"team-daily-{base.strftime('%Y%m%d')}",
"uid": "team-daily-001@demo", "uid": "team-daily-001@demo",
"summary": "Team Daily", "summary": "Team Daily",
"description": "Tägliches Standup-Meeting mit dem gesamten Team.", "description": "Tägliches Standup-Meeting mit dem gesamten Team.",
"location": "Besprechungsraum A / Zoom", "location": "Besprechungsraum A / Zoom",
"start_at": (today + timedelta(hours=14)).strftime("%Y-%m-%d %H:%M:%S"), "start_at": (base + timedelta(hours=14)).strftime("%Y-%m-%d %H:%M:%S"),
"end_at": (today + timedelta(hours=15)).strftime("%Y-%m-%d %H:%M:%S"), "end_at": (base + timedelta(hours=15)).strftime("%Y-%m-%d %H:%M:%S"),
"all_day": 0, "all_day": 0,
"status": "CONFIRMED", "status": "CONFIRMED",
}, },
{ {
"calendar_label": "demo", "calendar_label": "demo",
"instance_key": f"yoga-{today.strftime('%Y%m%d')}", "instance_key": f"yoga-{base.strftime('%Y%m%d')}",
"uid": "yoga-002@demo", "uid": "yoga-002@demo",
"summary": "Yoga Kurs", "summary": "Yoga Kurs",
"description": "Wöchentlicher Yoga-Kurs in der Volkshochschule.", "description": "Wöchentlicher Yoga-Kurs in der Volkshochschule.",
"location": "Volkshochschule, Raum 204", "location": "Volkshochschule, Raum 204",
"start_at": (today + timedelta(hours=18, minutes=30)).strftime("%Y-%m-%d %H:%M:%S"), "start_at": (base + timedelta(hours=18, minutes=30)).strftime("%Y-%m-%d %H:%M:%S"),
"end_at": (today + timedelta(hours=20)).strftime("%Y-%m-%d %H:%M:%S"), "end_at": (base + timedelta(hours=20)).strftime("%Y-%m-%d %H:%M:%S"),
"all_day": 0, "all_day": 0,
"status": "CONFIRMED", "status": "CONFIRMED",
}, },
{ {
"calendar_label": "demo", "calendar_label": "demo",
"instance_key": f"deadline-{(today + timedelta(days=1)).strftime('%Y%m%d')}", "instance_key": f"deadline-{(base + timedelta(days=1)).strftime('%Y%m%d')}",
"uid": "deadline-003@demo", "uid": "deadline-003@demo",
"summary": "Projekt-Deadline", "summary": "Projekt-Deadline",
"description": "Abgabe des Projektberichts an die Geschäftsleitung.", "description": "Abgabe des Projektberichts an die Geschäftsleitung.",
"location": "", "location": "",
"start_at": (today + timedelta(days=1)).strftime("%Y-%m-%d 00:00:00"), "start_at": (base + timedelta(days=1)).strftime("%Y-%m-%d 00:00:00"),
"end_at": (today + timedelta(days=1)).strftime("%Y-%m-%d 23:59:59"), "end_at": (base + timedelta(days=1)).strftime("%Y-%m-%d 23:59:59"),
"all_day": 1, "all_day": 1,
"status": "TENTATIVE", "status": "TENTATIVE",
}, },
{ {
"calendar_label": "demo", "calendar_label": "demo",
"instance_key": f"arzt-{(today + timedelta(days=2)).strftime('%Y%m%d')}", "instance_key": f"arzt-{(base + timedelta(days=2)).strftime('%Y%m%d')}",
"uid": "arzt-004@demo", "uid": "arzt-004@demo",
"summary": "Arzttermin", "summary": "Arzttermin",
"description": "Routineuntersuchung beim Hausarzt. Bitte Impfpass mitbringen.", "description": "Routineuntersuchung beim Hausarzt. Bitte Impfpass mitbringen.",
"location": "Dr. Müller, Gesundheitsstraße 12", "location": "Dr. Müller, Gesundheitsstraße 12",
"start_at": (today + timedelta(days=2, hours=10)).strftime("%Y-%m-%d %H:%M:%S"), "start_at": (base + timedelta(days=2, hours=10)).strftime("%Y-%m-%d %H:%M:%S"),
"end_at": (today + timedelta(days=2, hours=11, minutes=30)).strftime("%Y-%m-%d %H:%M:%S"), "end_at": (base + timedelta(days=2, hours=11, minutes=30)).strftime("%Y-%m-%d %H:%M:%S"),
"all_day": 0, "all_day": 0,
"status": "CONFIRMED", "status": "CONFIRMED",
}, },