banyaro/backend/routes/weather.py
rene 79fa5684b9 Feature+Fix: Referral-Admin, Pro-Gates, Karten-Layer, onDogChange, Staging-Media (SW by-v855)
Features:
- Admin: Referral-Tab (Virality Factor, Top-Werber, letzte Einladungen)
- Karte: Regenradar (RainViewer, zoom→7, color=4), Temperatur-Layer (OWM) mit Zahlen-Grid + Legende
- Wetter-Chip: Umschwung-Warnung bei ≥40%-Sprung in Niederschlagswahrscheinlichkeit
- Freundschaftsanfragen: Accept/Decline direkt in Notifications (kein Pro nötig)
- Freunde-Seite für Standard-User freigeschaltet

Pro-Gates:
- KI-Trainer, Routenvorschläge, Regenradar, Temperatur-Layer jetzt Pro-Feature
- Pro-Badge (P) auf Chips für Admins/Mods in allen Welten + Welten-einrichten
- Oranger Banner auf Pro-Seiten für Admin/Mod/Manager

Bugfixes:
- onDogChange: uebungen.js (Cache leeren + _render), trainingsplaene.js (war leer)
- robots.txt vereinfacht (nur Disallow, kein Allow-Durcheinander)
- Hintergrund-Foto: Querformat-Filter korrigiert (kein Fallback auf Hochformat)
- Staging Media: FileResponse mit korrektem MIME-Type, no-cache statt immutable
- Staging Docker: MEDIA_DIR=/data/media + /prod-media:ro Fallback-Handler
- Staging-Fix: Bild-Upload auf zweitem Hund (war Read-only file system)
2026-05-11 17:23:29 +02:00

119 lines
3.6 KiB
Python

"""
BAN YARO — Wetter-API
GET /api/weather?lat=&lon= → aktuelles Wetter + Zecken-Warnung für Nutzerstandort
"""
import os
import json
from fastapi import APIRouter, Query, HTTPException, Depends
import weather as weather_module
from auth import get_current_user
from database import db
router = APIRouter()
OWM_API_KEY = os.getenv("OPENWEATHERMAP_KEY", "")
ALLOWED_OWM_LAYERS = {"temp_new", "clouds_new", "wind_new", "pressure_new", "precipitation_new"}
@router.get('/radar-tiles')
async def radar_tile_config(user=Depends(get_current_user)):
"""Regenradar-Tile-Config (RainViewer)."""
return {"provider": "rainviewer"}
@router.get('/layer-tiles')
async def layer_tile_config(
layer: str = "temp_new",
user=Depends(get_current_user),
):
"""OWM-Tile-URL für Wetter-Layer (Key bleibt server-seitig)."""
if layer not in ALLOWED_OWM_LAYERS:
raise HTTPException(400, f"Unbekannter Layer. Erlaubt: {', '.join(ALLOWED_OWM_LAYERS)}")
if not OWM_API_KEY:
raise HTTPException(503, "OWM nicht konfiguriert.")
return {
"url": f"https://tile.openweathermap.org/map/{layer}/{{z}}/{{x}}/{{y}}.png?appid={OWM_API_KEY}",
"maxNativeZoom": 18,
"opacity": 0.6,
}
@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}')
@router.get('/forecast')
async def get_weather_forecast(
lat: float = Query(..., ge=-90, le=90),
lon: float = Query(..., ge=-180, le=180),
user=Depends(get_current_user),
):
try:
return await weather_module.get_forecast(lat, lon)
except Exception as exc:
raise HTTPException(503, f'Wettervorhersage nicht verfügbar: {exc}')
@router.get('/records')
async def weather_records(user=Depends(get_current_user)):
"""Persönliche Wetterrekorde aus diary-Einträgen mit weather_json."""
uid = user["id"]
with db() as conn:
rows = conn.execute("""
SELECT d.datum, d.weather_json, d.titel
FROM diary d
JOIN dogs dog ON dog.id = d.dog_id
WHERE dog.user_id = ? AND d.weather_json IS NOT NULL
ORDER BY d.datum ASC
""", (uid,)).fetchall()
if not rows:
return {"records": None}
entries = []
for r in rows:
try:
w = json.loads(r["weather_json"])
entries.append({
"datum": r["datum"],
"titel": r["titel"],
"temp_c": w.get("temp_c"),
"wind_kmh": w.get("wind_kmh"),
"precip_prob": w.get("precip_prob"),
"desc": w.get("desc", ""),
"weathercode": w.get("weathercode"),
})
except Exception:
pass
if not entries:
return {"records": None}
temps = [e for e in entries if e["temp_c"] is not None]
winds = [e for e in entries if e["wind_kmh"] is not None]
records = {}
if temps:
kaeltester = min(temps, key=lambda e: e["temp_c"])
heissester = max(temps, key=lambda e: e["temp_c"])
records["kaeltester"] = kaeltester
records["heissester"] = heissester
if winds:
stuermischster = max(winds, key=lambda e: e["wind_kmh"])
records["stuermischster"] = stuermischster
regen_count = sum(1 for e in entries if (e.get("precip_prob") or 0) > 60)
records["regen_eintraege"] = regen_count
records["gesamt_eintraege"] = len(entries)
return {"records": records}