Welten (worlds.js): - Swipe-Hints beim ersten Öffnen (JETZT ← → WELT animiert, einmalig) - Kein-Hund-Onboarding: Feature-Preview-Grid statt leerer Karte - Hintergrund-Foto-Hint: Kamera-Karte wenn noch kein Tagebuchfoto - worlds-back: navigiert zu Welcome wenn kein User eingeloggt - Nach Logout: worlds-back Button sofort ausgeblendet Wetter (wetter.js): - Standort-Fehlerseite zu Motivations-Seite umgebaut - Feature-Preview: Gassi-Score, 7-Tage, Regenradar, Rekorde - CTA: Standort freigeben + Registrieren (nur für Gäste) Settings (settings.js): - Logo in Auth-Form: display:block + margin:0 auto zentriert - Header bleibt sichtbar (FAB/Zurück-Navigation funktioniert) Jobs (jobs.js): - 2-Spalten-Grid auf Mobile: auto-fit statt festes 1fr 1fr - Kein doppeltes Padding im Wrapper Backend: - weather.py, achievements.py: diary JOIN fix (d.user_id → dogs JOIN) - Neue Wetter-Badges: wetter_tapfer, jahreszeiten, schnee - Ernährungs-, Reise-, Ausgaben-Seite: diverse UX-Verbesserungen - Presse-Seite erweitert - Ban Yaro Foto-Assets (WebP + HIRES JPG)
77 lines
2.6 KiB
Python
77 lines
2.6 KiB
Python
"""BAN YARO — Widget-Snapshot + Tagesspruch Endpoints"""
|
|
|
|
import json
|
|
from datetime import date
|
|
from fastapi import APIRouter, Depends, Query
|
|
from typing import Optional
|
|
from database import db
|
|
from auth import get_current_user
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/quote")
|
|
async def daily_quote(kategorie: Optional[str] = Query(None)):
|
|
"""Liefert einen deterministischen Tagesspruch (wechselt täglich)."""
|
|
day_num = (date.today() - date(2026, 1, 1)).days
|
|
with db() as conn:
|
|
if kategorie:
|
|
rows = conn.execute(
|
|
"SELECT id, text, autor, kategorie FROM daily_quotes WHERE kategorie=?",
|
|
(kategorie,)
|
|
).fetchall()
|
|
else:
|
|
rows = conn.execute("SELECT id, text, autor, kategorie FROM daily_quotes").fetchall()
|
|
if not rows:
|
|
return {"quote": None}
|
|
q = rows[day_num % len(rows)]
|
|
return {"quote": dict(q)}
|
|
|
|
|
|
@router.get("/snapshot")
|
|
async def widget_snapshot(user=Depends(get_current_user)):
|
|
"""Liefert kompakte Widget-Daten: Hund, nächste Erinnerung, zufälliges Tagebuchbild."""
|
|
with db() as conn:
|
|
# Aktiver Hund (erster oder letzter genutzter)
|
|
dog = conn.execute(
|
|
"SELECT id, name, rasse, foto_url FROM dogs WHERE user_id=? ORDER BY id LIMIT 1",
|
|
(user["id"],)
|
|
).fetchone()
|
|
|
|
if not dog:
|
|
return {"dog": None}
|
|
|
|
dog_id = dog["id"]
|
|
|
|
# Nächste fällige Erinnerung
|
|
reminder = conn.execute(
|
|
"""SELECT bezeichnung, naechstes, typ FROM health
|
|
WHERE dog_id=? AND naechstes IS NOT NULL AND naechstes >= date('now')
|
|
ORDER BY naechstes ASC LIMIT 1""",
|
|
(dog_id,)
|
|
).fetchone()
|
|
|
|
# Zufälliges Tagebuchbild (letzte 50 Einträge mit Bild)
|
|
photos = conn.execute(
|
|
"""SELECT media_url, titel, datum FROM diary
|
|
WHERE dog_id=? AND media_url IS NOT NULL
|
|
ORDER BY datum DESC LIMIT 50""",
|
|
(dog_id,)
|
|
).fetchall()
|
|
|
|
day_num = (date.today() - date(2024, 1, 1)).days
|
|
random_photo = dict(photos[day_num % len(photos)]) if photos else None
|
|
|
|
# Anzahl überfälliger Erinnerungen
|
|
overdue = conn.execute(
|
|
"""SELECT COUNT(*) as n FROM health
|
|
WHERE dog_id=? AND naechstes IS NOT NULL AND naechstes < date('now')""",
|
|
(dog_id,)
|
|
).fetchone()["n"]
|
|
|
|
return {
|
|
"dog": dict(dog),
|
|
"reminder": dict(reminder) if reminder else None,
|
|
"random_photo": random_photo,
|
|
"overdue": overdue,
|
|
}
|