Routen-Navigation:
- POI-Marker: farbige Kreise mit Phosphor-Icons (wie Hauptkarte)
- Screensaver: Navi-Pfeil dreht sich via DeviceOrientationEvent (iOS+Android)
- Pfeil-Dämpfung: EMA α=0.12 mit Wrap-Around
- GPS-Distanz-Bug: Fortschritt nur wenn <500m zur Route
- fitBounds: User-Position nur wenn <20km von Route
- Screensaver: "zur Route" vs "verbleibend" kontextabhängig
- Richtungspfeile entlang Route (blau, max 7 Stück)
- Umkehren ins Route-Detail verschoben, Detail-Map rebuildet sich
- rk-header z-index:10 (Leaflet-Tiles liefen drüber)
- 2-Sek. Screensaver-Entsperrung
km-Tracking:
- route_walks Tabelle
- POST /api/routes/{id}/walked (≥50%)
- total_km = erstellte Routes + gelaufene route_walks
- Toast bei neuem Badge
Übungsfortschritt:
- exercise_progress + training_plan_progress Tabellen
- GET/POST /api/training/progress, /plan-progress, /suggestions
- uebungen.js: API-first + localStorage-Fallback + Auto-Migration
- Empfehlungs-Banner (regelbasiert)
- Toast bei "sitzt"
44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
from fastapi import APIRouter, Depends
|
|
from database import db
|
|
from auth import get_current_user, get_current_user_optional
|
|
|
|
router = APIRouter()
|
|
|
|
_STATS_SQL = """
|
|
SELECT u.id, u.name, u.avatar_url,
|
|
ROUND(COALESCE(SUM(r.distanz_km), 0), 1) AS total_km,
|
|
COUNT(DISTINCT r.id) AS routen,
|
|
COUNT(DISTINCT p.id) AS pois,
|
|
ROUND(COALESCE(SUM(r.distanz_km), 0), 1) * 1
|
|
+ COUNT(DISTINCT p.id) * 5
|
|
+ COUNT(DISTINCT r.id) * 10 AS punkte
|
|
FROM users u
|
|
LEFT JOIN routes r ON r.user_id = u.id AND r.is_public = 1
|
|
LEFT JOIN user_map_pois p ON p.user_id = u.id
|
|
GROUP BY u.id
|
|
"""
|
|
|
|
|
|
@router.get("/leaderboard")
|
|
async def leaderboard(_user=Depends(get_current_user_optional)):
|
|
with db() as conn:
|
|
rows = conn.execute(f"""
|
|
SELECT * FROM ({_STATS_SQL})
|
|
ORDER BY punkte DESC, total_km DESC
|
|
LIMIT 20
|
|
""").fetchall()
|
|
return [dict(r) for r in rows]
|
|
|
|
|
|
@router.get("/me")
|
|
async def my_stats(user=Depends(get_current_user)):
|
|
with db() as conn:
|
|
row = conn.execute(f"""
|
|
SELECT s.*, rank_tbl.rang FROM ({_STATS_SQL}) s
|
|
JOIN (
|
|
SELECT id, ROW_NUMBER() OVER (ORDER BY punkte DESC, total_km DESC) AS rang
|
|
FROM ({_STATS_SQL})
|
|
) rank_tbl ON rank_tbl.id = s.id
|
|
WHERE s.id = ?
|
|
""", (user["id"],)).fetchone()
|
|
return dict(row) if row else {}
|