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)
This commit is contained in:
rene 2026-05-11 17:23:29 +02:00
parent 2f021f54c2
commit 79fa5684b9
22 changed files with 570 additions and 58 deletions

View file

@ -87,24 +87,39 @@ async def get_weather_for_location(lat: float, lon: float) -> dict:
if temp is not None and temp > 7.0 and 3 <= month <= 10:
zecken = 'hoch' if temp > 20 else ('mittel' if temp > 12 else 'niedrig')
# Nächste Regenstunde: erstes stündliches Fenster (jetzt+1h bis +12h) mit ≥60% Niederschlag
# Nächste Regenstunde + Umschwung-Warnung
next_rain_time = None
rain_warning_time = None # Stunde mit ≥40%-Sprung gegenüber Vorststunde
already_raining = wcode >= 51
now_h = datetime.now().hour
h_times = hourly.get('time', [])
h_precip = hourly.get('precipitation_probability', [])
# Index-Liste nur für Stunden im Fenster now_h+1 … now_h+12
window = []
for t, p in zip(h_times, h_precip):
try:
entry_h = int(t[11:13])
except Exception:
continue
if entry_h <= now_h or entry_h > now_h + 12:
continue
window.append((entry_h, p if p is not None else 0))
if not already_raining:
now_h = datetime.now().hour
h_times = hourly.get('time', [])
h_precip = hourly.get('precipitation_probability', [])
for t, p in zip(h_times, h_precip):
try:
entry_h = int(t[11:13])
except Exception:
continue
if entry_h <= now_h or entry_h > now_h + 12:
continue
if p is not None and p >= 20:
for entry_h, p in window:
if next_rain_time is None and p >= 20:
next_rain_time = f"{entry_h:02d}:00"
break
# Umschwung: Sprung ≥40% von einer Stunde zur nächsten
prev_p = wcode >= 51 and 100 or (h_precip[now_h] if now_h < len(h_precip) else 0) or 0
for entry_h, p in window:
if p - prev_p >= 40:
rain_warning_time = f"{entry_h:02d}:00"
break
prev_p = p
data = {
'temp_c': temp,
'feels_like_c': feels_like,
@ -115,8 +130,9 @@ async def get_weather_for_location(lat: float, lon: float) -> dict:
'precip_prob': precip,
'uv_index': uv,
'is_day': bool(is_day),
'zecken_warnung': zecken,
'next_rain_time': next_rain_time,
'zecken_warnung': zecken,
'next_rain_time': next_rain_time,
'rain_warning_time': rain_warning_time,
}
_location_cache[key] = (now, data)
return data