Add: München als 3. Wetter-Location + Wetter-Detail-Modal
- München als tertiärer Standort (iris-Akzent) hinzugefügt - Klick auf WeatherCard öffnet Detail-Modal mit: - 24h stündliche Prognose (horizontal scrollbar) - 7-Tage-Vorhersage mit Temperaturbalken - Wind, Feuchte, Sonnenauf/-untergang - Backend: 7-Tage statt 3-Tage Forecast, 24 Hourly-Slots pro Standort - Backend: forecast_3day → forecast Feldname-Konsistenz - Dashboard: 3-Spalten Wetter-Grid statt 4 (HourlyForecast → Modal) - Admin: Tertiärer Standort konfigurierbar - THERMAL Design: iris glow, modal animation, Portal-basiertes Modal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d9626108e6
commit
2f56be835e
13 changed files with 379 additions and 36 deletions
|
|
@ -213,7 +213,7 @@ async def fetch_weather(location: str) -> Dict[str, Any]:
|
|||
"wind_kmh": 0,
|
||||
"description": "Nicht verfügbar",
|
||||
"icon": "❓",
|
||||
"forecast_3day": [],
|
||||
"forecast": [],
|
||||
"error": None,
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ async def fetch_weather(location: str) -> Dict[str, Any]:
|
|||
"current": "temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m",
|
||||
"daily": "weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset",
|
||||
"timezone": geo["timezone"],
|
||||
"forecast_days": 3,
|
||||
"forecast_days": 7,
|
||||
},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
|
@ -266,36 +266,42 @@ async def fetch_weather(location: str) -> Dict[str, Any]:
|
|||
"error": None,
|
||||
}
|
||||
|
||||
# Step 4: Parse 3-day forecast
|
||||
# Step 4: Parse 7-day forecast
|
||||
daily = data.get("daily", {})
|
||||
dates = daily.get("time", [])
|
||||
forecast_3day: List[Dict[str, Any]] = []
|
||||
forecast: List[Dict[str, Any]] = []
|
||||
|
||||
for i, date_str in enumerate(dates[:3]):
|
||||
code = int(daily.get("weather_code", [0] * 3)[i])
|
||||
forecast_3day.append({
|
||||
for i, date_str in enumerate(dates):
|
||||
codes_list = daily.get("weather_code", [])
|
||||
code = int(codes_list[i]) if i < len(codes_list) else 0
|
||||
max_temps = daily.get("temperature_2m_max", [])
|
||||
min_temps = daily.get("temperature_2m_min", [])
|
||||
sunrises = daily.get("sunrise", [])
|
||||
sunsets = daily.get("sunset", [])
|
||||
forecast.append({
|
||||
"date": date_str,
|
||||
"max_temp": round(daily.get("temperature_2m_max", [0] * 3)[i]),
|
||||
"min_temp": round(daily.get("temperature_2m_min", [0] * 3)[i]),
|
||||
"max_temp": round(max_temps[i]) if i < len(max_temps) else 0,
|
||||
"min_temp": round(min_temps[i]) if i < len(min_temps) else 0,
|
||||
"icon": _wmo_icon(code),
|
||||
"description": _wmo_description(code),
|
||||
"sunrise": daily.get("sunrise", [""] * 3)[i].split("T")[-1] if daily.get("sunrise") else "",
|
||||
"sunset": daily.get("sunset", [""] * 3)[i].split("T")[-1] if daily.get("sunset") else "",
|
||||
"sunrise": sunrises[i].split("T")[-1] if i < len(sunrises) and sunrises[i] else "",
|
||||
"sunset": sunsets[i].split("T")[-1] if i < len(sunsets) and sunsets[i] else "",
|
||||
})
|
||||
|
||||
result["forecast_3day"] = forecast_3day
|
||||
result["forecast"] = forecast
|
||||
logger.info("[WEATHER] Fetched '%s': %d°C, %s", geo["name"], result["temp"], result["description"])
|
||||
return result
|
||||
|
||||
|
||||
async def fetch_hourly_forecast(location: str) -> List[Dict[str, Any]]:
|
||||
"""Fetch hourly forecast for the next 8 hours from Open-Meteo.
|
||||
async def fetch_hourly_forecast(location: str, max_slots: int = 8) -> List[Dict[str, Any]]:
|
||||
"""Fetch hourly forecast from Open-Meteo.
|
||||
|
||||
Args:
|
||||
location: City name or coordinates.
|
||||
max_slots: Maximum number of hourly slots to return.
|
||||
|
||||
Returns:
|
||||
List of hourly forecast dicts (max 8 entries).
|
||||
List of hourly forecast dicts.
|
||||
"""
|
||||
geo = await _geocode(location)
|
||||
if geo is None:
|
||||
|
|
@ -354,7 +360,7 @@ async def fetch_hourly_forecast(location: str) -> List[Dict[str, Any]]:
|
|||
"wind_kmh": round(winds[i]) if i < len(winds) else 0,
|
||||
})
|
||||
|
||||
if len(upcoming) >= 8:
|
||||
if len(upcoming) >= max_slots:
|
||||
break
|
||||
|
||||
logger.debug("[WEATHER] Hourly for '%s': %d slots", location, len(upcoming))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue