Feature: Sprint31 — 9 Features merged (Streak, Ausgaben, KI-Tierarzt, Rückrufe, Adoption, Vet+Befunde, Hundepass, Playdate, Rassenerkennung)
- Trainings-Streak: streak.py, DB training_streaks, Scheduler 19:00, Widget in welcome.js, Ping in uebungen.js
- Ausgaben-Tracker: expenses.py, expenses.js, DB expenses-Tabelle
- KI-Tierarztfragen: ki.py /tierarzt, health.js Button+Modal, DB ki_tierarzt_log
- Rückruf-Alarm: recalls.py, recalls.js, DB feed_recalls, Scheduler 08:00 RASFF
- Adoption: adoption.py, adoption.js, DB adoption_cache
- Tierarzt-Favorit + Befunde: tieraerzte.py /my-favorite+/favorite, health_docs.py, health.js, api.js, DB favorite_vets+health_documents
- Digitaler Hundepass: passport.py, dog-profile.js, main.py /pass/{token}, DB vaccinations+medications+dog_passport_meta+passport_shares, requirements.txt fpdf2
- Playdate-Matching: playdate.py, playdate.js, DB playdate_listings+playdate_requests
- Rassen-Erkennung: ki.py /rasse-erkennung (Claude Vision), dog-profile.js+wiki.js, CSS .rasse-result-card, DB ki_rasse_log
This commit is contained in:
parent
031c6028ac
commit
742ad189e8
26 changed files with 5734 additions and 27 deletions
114
backend/routes/streak.py
Normal file
114
backend/routes/streak.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
"""BAN YARO — Trainings-Streak"""
|
||||
|
||||
import datetime
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from database import db
|
||||
from auth import get_current_user
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
_today = lambda: datetime.date.today().isoformat()
|
||||
_yesterday = lambda: (datetime.date.today() - datetime.timedelta(days=1)).isoformat()
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# GET /streak/leaderboard — Top-10 Streaks (öffentliche Hunde)
|
||||
# Muss VOR /{dog_id} stehen, sonst greift der int-Parameter zuerst.
|
||||
# ------------------------------------------------------------------
|
||||
@router.get("/streak/leaderboard")
|
||||
async def get_leaderboard(user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
rows = conn.execute("""
|
||||
SELECT
|
||||
u.name AS user_name,
|
||||
d.name AS dog_name,
|
||||
d.rasse,
|
||||
d.foto_url,
|
||||
ts.current_streak
|
||||
FROM training_streaks ts
|
||||
JOIN dogs d ON d.id = ts.dog_id
|
||||
JOIN users u ON u.id = ts.user_id
|
||||
WHERE ts.current_streak > 0
|
||||
AND (d.is_public = 1 OR d.user_id = ts.user_id)
|
||||
ORDER BY ts.current_streak DESC
|
||||
LIMIT 10
|
||||
""").fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# GET /streak/{dog_id} — aktueller Streak eines Hundes
|
||||
# ------------------------------------------------------------------
|
||||
@router.get("/streak/{dog_id}")
|
||||
async def get_streak(dog_id: int, user=Depends(get_current_user)):
|
||||
uid = user["id"]
|
||||
with db() as conn:
|
||||
dog = conn.execute(
|
||||
"SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, uid)
|
||||
).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404, "Hund nicht gefunden.")
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT current_streak, longest_streak, last_training_date "
|
||||
"FROM training_streaks WHERE user_id=? AND dog_id=?",
|
||||
(uid, dog_id)
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
return {"current_streak": 0, "longest_streak": 0, "last_training_date": None}
|
||||
return dict(row)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# POST /streak/{dog_id}/ping — Training heute registrieren
|
||||
# ------------------------------------------------------------------
|
||||
@router.post("/streak/{dog_id}/ping")
|
||||
async def ping_streak(dog_id: int, user=Depends(get_current_user)):
|
||||
uid = user["id"]
|
||||
today = _today()
|
||||
yest = _yesterday()
|
||||
|
||||
with db() as conn:
|
||||
dog = conn.execute(
|
||||
"SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, uid)
|
||||
).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404, "Hund nicht gefunden.")
|
||||
|
||||
row = conn.execute(
|
||||
"SELECT current_streak, longest_streak, last_training_date "
|
||||
"FROM training_streaks WHERE user_id=? AND dog_id=?",
|
||||
(uid, dog_id)
|
||||
).fetchone()
|
||||
|
||||
if row:
|
||||
cur = row["current_streak"]
|
||||
longest = row["longest_streak"]
|
||||
last = row["last_training_date"]
|
||||
|
||||
if last == today:
|
||||
# Bereits heute gepingt — nichts tun
|
||||
return {"current_streak": cur, "longest_streak": longest, "last_training_date": last}
|
||||
elif last == yest:
|
||||
cur += 1
|
||||
else:
|
||||
cur = 1
|
||||
|
||||
longest = max(longest, cur)
|
||||
|
||||
conn.execute(
|
||||
"UPDATE training_streaks SET current_streak=?, longest_streak=?, last_training_date=? "
|
||||
"WHERE user_id=? AND dog_id=?",
|
||||
(cur, longest, today, uid, dog_id)
|
||||
)
|
||||
else:
|
||||
cur = 1
|
||||
longest = 1
|
||||
conn.execute(
|
||||
"INSERT INTO training_streaks (user_id, dog_id, current_streak, longest_streak, last_training_date) "
|
||||
"VALUES (?,?,?,?,?)",
|
||||
(uid, dog_id, cur, longest, today)
|
||||
)
|
||||
|
||||
return {"current_streak": cur, "longest_streak": longest, "last_training_date": today}
|
||||
Loading…
Add table
Add a link
Reference in a new issue