Session 2026-04-19: Navigation, Kompass, Übungsfortschritt
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"
This commit is contained in:
parent
390176383f
commit
9a78121a3e
25 changed files with 2487 additions and 248 deletions
44
backend/routes/stats.py
Normal file
44
backend/routes/stats.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
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 {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue