diff --git a/Dockerfile b/Dockerfile index 48cfbfe..1194fbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY kantine2ical.py app.py ./ +COPY templates/ templates/ RUN useradd -m appuser USER appuser diff --git a/README.md b/README.md index 3953363..fd4f327 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,14 @@ Die Termine erscheinen mit der Zeitzone Europe/Berlin um 12:00 Uhr mit allen fü Der Speiseplan kann als **externer Kalender** per URL angeboten werden. Ein Flask-Server liefert die iCal-Daten; Google Kalender und andere Clients können die URL direkt abonnieren. Im Hintergrund wird täglich nach neuen Speiseplan-PDFs gesucht und der Kalender aktualisiert. +**Startseite:** Unter der Stamm-URL (`/`) liefert der Server eine **Startseite** mit: +- der Abo-URL zum Kopieren (inkl. „Kopieren“-Button), +- einer Anleitung zur Einbettung in **Google Kalender** (Schritt für Schritt), +- Kurzhinweisen für **andere Kalender-Apps** (Outlook, Apple Kalender, Thunderbird, Android/iOS), +- der Angabe, **wann die Speisepläne zuletzt aktualisiert** wurden. + +Die eigentliche iCal-Datei für Abos und direkten Download ist unter `/calendar.ics` erreichbar. + **Voraussetzung:** Für „Von URL hinzufügen“ in Google Kalender muss die Server-URL von außen erreichbar sein (öffentliche IP, Reverse-Proxy oder z. B. ngrok für Tests). 1. Abhängigkeiten installieren (inkl. Flask): `pip install -r requirements.txt` @@ -66,8 +74,9 @@ Der Speiseplan kann als **externer Kalender** per URL angeboten werden. Ein Flas python app.py ``` Oder mit Flask-CLI: `flask --app app run --host 0.0.0.0 --port 5000` -3. Abo-URL für Google Kalender: `http://:5000/calendar.ics` (bzw. Port 5000 durch Ihren Host/Port ersetzen). -4. In Google Kalender: „Andere Kalender hinzufügen“ → „Von URL“ → obige URL eintragen. +3. Im Browser die **Startseite** aufrufen: `http://:5000/` – dort die Abo-URL kopieren und die Anleitung nutzen. +4. Direkte Abo-URL: `http://:5000/calendar.ics` (bzw. Port durch Ihren Host ersetzen). +5. In Google Kalender: „Andere Kalender hinzufügen“ → „Von URL“ → Abo-URL eintragen. **Konfiguration (optional, Umgebungsvariablen):** @@ -86,7 +95,8 @@ docker build -t kantine2ical . docker run -p 8000:8000 kantine2ical ``` -Abo-URL: `http://:8000/calendar.ics` +- **Startseite** (Anleitung + Abo-URL): `http://:8000/` +- **iCal-Abo:** `http://:8000/calendar.ics` **Mit Docker Compose:** ```bash diff --git a/app.py b/app.py index 0800ca8..63dcda5 100644 --- a/app.py +++ b/app.py @@ -8,8 +8,10 @@ import logging import os import threading import time +from datetime import datetime +from zoneinfo import ZoneInfo -from flask import Flask, Response +from flask import Flask, Response, render_template, request from kantine2ical import BASE_URL, empty_ical_bytes, refresh_speiseplan @@ -17,21 +19,26 @@ from kantine2ical import BASE_URL, empty_ical_bytes, refresh_speiseplan KANTINE_BASE_URL = os.environ.get("KANTINE_BASE_URL", BASE_URL) REFRESH_INTERVAL_SECONDS = int(os.environ.get("REFRESH_INTERVAL_SECONDS", "86400")) # 24h -app = Flask(__name__) +# Template-Ordner immer relativ zu dieser Datei (funktioniert mit Gunicorn/Docker) +_template_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates") +app = Flask(__name__, template_folder=_template_dir) _log = logging.getLogger(__name__) -# Cache: zuletzt gültige iCal-Bytes; Lock für Zugriff +# Cache: zuletzt gültige iCal-Bytes; Zeitpunkt der letzten Aktualisierung _ical_cache: bytes = empty_ical_bytes() +_last_refresh_at: datetime | None = None _cache_lock = threading.Lock() +_TIMEZONE = ZoneInfo("Europe/Berlin") def _do_refresh() -> None: """Refresh ausführen und bei Erfolg Cache aktualisieren.""" - global _ical_cache + global _ical_cache, _last_refresh_at result = refresh_speiseplan(KANTINE_BASE_URL) if result is not None: _, ical_bytes = result with _cache_lock: _ical_cache = ical_bytes + _last_refresh_at = datetime.now(_TIMEZONE) _log.info("Speiseplan-Refresh erfolgreich, Cache aktualisiert.") else: _log.warning("Speiseplan-Refresh fehlgeschlagen oder keine Daten; Cache unverändert.") @@ -68,10 +75,38 @@ def calendar_ics() -> Response: ) +def _format_last_refresh() -> str | None: + """Zeitpunkt der letzten Aktualisierung formatiert (z. B. 29.01.2025, 14:32 Uhr).""" + with _cache_lock: + t = _last_refresh_at + if t is None: + return None + return t.strftime("%d.%m.%Y, %H:%M") + " Uhr" + + @app.route("/") -def index() -> Response: - """Redirect auf calendar.ics oder gleiche Antwort wie /calendar.ics.""" - return calendar_ics() +def index(): + """Startseite mit Anleitung zur iCal-Einbettung (Google und andere).""" + base = request.url_root.rstrip("/") + calendar_url = f"{base}/calendar.ics" + last_refresh_str = _format_last_refresh() + try: + return render_template( + "index.html", + calendar_url=calendar_url, + last_refresh_str=last_refresh_str, + ) + except Exception as e: + _log.exception("Template index.html: %s", e) + return ( + f'Speiseplan iCal' + f'

Speiseplan Kantine BHZ Kiel-Wik

' + f'

Zuletzt aktualisiert: {last_refresh_str or "noch nicht"}.

' + f'

Abo-URL: {calendar_url}

' + f'

In Google Kalender: Andere Kalender → Von URL → obige URL einfügen.

', + 200, + {"Content-Type": "text/html; charset=utf-8"}, + ) def main() -> None: diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..f4b5b96 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,288 @@ + + + + + + Speiseplan Kantine BHZ Kiel-Wik – iCal-Abo + + + + + + +
+
+

Speiseplan Kantine BHZ Kiel-Wik

+

iCal-Abo für Ihren Kalender – täglich aktualisiert, alle Tagesgerichte (I.–V.) um 12:00 Uhr.

+ {% if last_refresh_str %} +

Zuletzt aktualisiert: {{ last_refresh_str }}

+ {% else %} +

Speisepläne wurden noch nicht aktualisiert.

+ {% endif %} +
+ +
+

1 Abo-URL kopieren

+

Diese URL in Ihrem Kalender als „Abonnement“ oder „Von URL hinzufügen“ eintragen:

+
+ + +
+
+ +
+

2 Google Kalender

+
    +
  1. calendar.google.com öffnen
  2. +
  3. Rechts neben „Meine Kalender“ auf + klicken → Von URL
  4. +
  5. Die obige Abo-URL einfügen und Kalender hinzufügen wählen
  6. +
  7. Der Speiseplan erscheint als eigener Kalender und wird automatisch aktualisiert
  8. +
+
+ +
+

3 Andere Kalender-Apps

+
+
+ Outlook (Web) +

Kalender → Einstellungen → Kalender hinzufügen → Abonnement; Abo-URL einfügen.

+
+
+ Apple Kalender +

Kalender → Ablage → Neues Kalender-Abonnement; Abo-URL einfügen.

+
+
+ Thunderbird +

Kalender → Neuer Kalender → Im Netzwerk; Abo-URL einfügen.

+
+
+ Android / iOS +

In den Einstellungen der Kalender-App „Kalender hinzufügen“ / „Abo per URL“; Abo-URL einfügen.

+
+
+
+ + +
+ + + +