diff --git a/backend/main.py b/backend/main.py index efdadb6..13b5d3d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -119,6 +119,7 @@ from routes.stats import router as stats_router from routes.achievements import router as achievements_router from routes.training import router as training_router from routes.praise import router as praise_router +from routes.weather import router as weather_router app.include_router(auth_router, prefix="/api/auth", tags=["Auth"]) app.include_router(dogs_router, prefix="/api/dogs", tags=["Hunde"]) @@ -134,6 +135,7 @@ app.include_router(walks_router, prefix="/api/walks", tags=["Gassi-Tre app.include_router(events_router, prefix="/api/events", tags=["Events"]) app.include_router(sitting_router, prefix="/api/sitting", tags=["Sitting"]) app.include_router(osm_router, prefix="/api/osm", tags=["OSM"]) +app.include_router(weather_router, prefix="/api/weather", tags=["Wetter"]) app.include_router(forum_router, prefix="/api/forum", tags=["Forum"]) app.include_router(lost_router, prefix="/api/lost", tags=["Verlorener Hund"]) app.include_router(knigge_router, prefix="/api/knigge", tags=["Knigge"]) diff --git a/backend/routes/achievements.py b/backend/routes/achievements.py index 4ad6b98..12c13fa 100644 --- a/backend/routes/achievements.py +++ b/backend/routes/achievements.py @@ -125,10 +125,10 @@ def update_streak(user_id: int, conn): def check_and_award(user_id: int, conn): stats = conn.execute(""" SELECT ROUND( - COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? AND r.is_public=1), 0) + + COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? ), 0) + COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0), 1) AS total_km, - (SELECT COUNT(*) FROM routes r WHERE r.user_id=? AND r.is_public=1) AS routen, + (SELECT COUNT(*) FROM routes r WHERE r.user_id=? ) AS routen, (SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?) AS pois FROM (SELECT 1) """, (user_id, user_id, user_id, user_id)).fetchone() @@ -178,17 +178,17 @@ async def my_achievements(user=Depends(get_current_user)): stats = conn.execute(""" SELECT ROUND( - COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? AND r.is_public=1), 0) + + COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? ), 0) + COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0), 1) AS total_km, - (SELECT COUNT(*) FROM routes r WHERE r.user_id=? AND r.is_public=1) AS routen, + (SELECT COUNT(*) FROM routes r WHERE r.user_id=? ) AS routen, (SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?) AS pois, ROUND( - COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? AND r.is_public=1), 0) + + COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? ), 0) + COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0), 1)*1 + (SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?)*5 - + (SELECT COUNT(*) FROM routes r WHERE r.user_id=? AND r.is_public=1)*10 AS punkte + + (SELECT COUNT(*) FROM routes r WHERE r.user_id=? )*10 AS punkte FROM (SELECT 1) """, (uid, uid, uid, uid, uid, uid, uid, uid)).fetchone() @@ -208,8 +208,7 @@ async def my_achievements(user=Depends(get_current_user)): +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 + LEFT JOIN routes r ON r.user_id=u.id LEFT JOIN user_map_pois p ON p.user_id=u.id GROUP BY u.id ) WHERE punkte > ? """, (stats["punkte"] if stats else 0,)).fetchone() diff --git a/backend/routes/stats.py b/backend/routes/stats.py index 1cb8835..e1cc906 100644 --- a/backend/routes/stats.py +++ b/backend/routes/stats.py @@ -13,7 +13,7 @@ _STATS_SQL = """ + 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 routes r ON r.user_id = u.id LEFT JOIN user_map_pois p ON p.user_id = u.id GROUP BY u.id """ diff --git a/backend/routes/weather.py b/backend/routes/weather.py new file mode 100644 index 0000000..319cfd2 --- /dev/null +++ b/backend/routes/weather.py @@ -0,0 +1,20 @@ +""" +BAN YARO — Wetter-API +GET /api/weather?lat=&lon= → aktuelles Wetter + Zecken-Warnung für Nutzerstandort +""" + +from fastapi import APIRouter, Query, HTTPException +import weather as weather_module + +router = APIRouter() + + +@router.get('') +async def get_weather( + lat: float = Query(..., ge=-90, le=90), + lon: float = Query(..., ge=-180, le=180), +): + try: + return await weather_module.get_weather_for_location(lat, lon) + except Exception as exc: + raise HTTPException(503, f'Wetter nicht verfügbar: {exc}') diff --git a/backend/static/css/components.css b/backend/static/css/components.css index 6f71a25..4300fa7 100644 --- a/backend/static/css/components.css +++ b/backend/static/css/components.css @@ -2723,6 +2723,42 @@ html.modal-open { pointer-events: none; } +/* Wetter-Chip — oben rechts auf der Karte */ +.map-weather-chip { + position: absolute; + top: var(--space-3); + right: var(--space-3); + z-index: 1000; + background: rgba(255,255,255,0.92); + backdrop-filter: blur(4px); + border: 1px solid var(--c-border-light); + border-radius: var(--radius-full); + padding: 5px 12px; + font-size: 13px; + color: var(--c-text); + box-shadow: 0 2px 8px rgba(0,0,0,0.12); + display: flex; + align-items: center; + gap: var(--space-2); + pointer-events: none; + user-select: none; +} +.map-weather-chip--hidden { display: none; } +.map-weather-chip__temp { font-weight: 700; } +.map-weather-chip__desc { color: var(--c-text-secondary); font-size: 12px; } +.map-weather-chip__zecken { + background: #FEF3C7; + color: #92400E; + border-radius: var(--radius-full); + padding: 1px 7px; + font-size: 11px; + font-weight: 600; +} +.map-weather-chip__zecken--hoch { + background: #FEE2E2; + color: #991B1B; +} + /* Giftköder-Marker — pulsierend, rot, sofort erkennbar */ .poison-marker { position: relative; diff --git a/backend/static/js/app.js b/backend/static/js/app.js index da4c2ba..19066c1 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '307'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '308'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const App = (() => { diff --git a/backend/static/js/pages/map.js b/backend/static/js/pages/map.js index 4dd8d4d..f1d02cb 100644 --- a/backend/static/js/pages/map.js +++ b/backend/static/js/pages/map.js @@ -144,6 +144,7 @@ window.Page_map = (() => { _userPos = pos; if (_frankfurtTimer) { clearTimeout(_frankfurtTimer); _frankfurtTimer = null; } _map?.flyTo([pos.lat, pos.lon], 14, { duration: 1.2 }); + _loadWeather(pos.lat, pos.lon); }).catch(() => { const btn = document.getElementById('map-locate-btn'); if (btn) { @@ -195,6 +196,8 @@ window.Page_map = (() => { +
+