mirror of
https://github.com/skoelle/calender_sync.git
synced 2026-09-17 18:20:24 +00:00
demo showcase README.md
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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:00–15:00 | CONFIRMED |
|
||||||
|
| Yoga Kurs | heute 18:30–20:00 | CONFIRMED |
|
||||||
|
| Projekt-Deadline | morgen Ganztag | TENTATIVE |
|
||||||
|
| Arzttermin | übermorgen 10:00–11: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 |
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.event-location::before {
|
.event-location::before {
|
||||||
content: "\\1F4CD ";
|
content: "\1F4CD ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
|
|||||||
+17
-12
@@ -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",
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user