modified: app.py

modified:   static/css/style.css
	modified:   templates/weather.html
This commit is contained in:
simon
2026-04-22 13:29:08 +02:00
parent e59e88cafe
commit c5bd9cb7e9
3 changed files with 52 additions and 9 deletions

50
app.py
View File

@@ -226,6 +226,8 @@ def get_dwd_warnings(lat, lon, state_hint=None):
"description": w.get("description", ""),
"state": w.get("state", ""),
"state_short": w.get("stateShort", ""),
"onset": w.get("onset", ""),
"expires": w.get("expires", ""),
})
filtered = []
if state_hint:
@@ -238,8 +240,10 @@ def get_dwd_warnings(lat, lon, state_hint=None):
"type": w["type"],
"headline": w["headline"],
"description": w["description"],
"onset": w.get("onset", ""),
"expires": w.get("expires", ""),
}
for w in result_source[:3]
for w in result_source[:5]
]
_warn_cache[key] = result
return result
@@ -339,6 +343,49 @@ def get_mosmix_forecast(lat, lon, hours=72):
app.logger.exception("MOSMIX forecast loading failed")
return [], {}
def filter_unrealistic_warnings(warnings, forecast):
"""
Filter out warnings that contradict the actual forecast.
E.g., frost warning when min temp is > 0°C in next 48 hours.
"""
if not warnings or not forecast:
return warnings
filtered = []
frost_keywords = {"frost", "glatte", "glatt", "eis", "schnee"}
rain_keywords = {"regen", "starkregen", "dauerregen"}
try:
temps_48h = [h.get("temp_c") for h in forecast[:48] if h.get("temp_c") is not None]
precip_48h = [h.get("precip_mm") for h in forecast[:48] if h.get("precip_mm") is not None]
min_temp_48h = min(temps_48h) if temps_48h else None
max_precip_48h = max(precip_48h) if precip_48h else 0
for w in warnings:
warn_type = _normalize_text(w.get("type", ""))
headline = _normalize_text(w.get("headline", ""))
skip = False
if min_temp_48h is not None and min_temp_48h > 2:
if any(kw in warn_type or kw in headline for kw in frost_keywords):
app.logger.info("Filtered frost warning: min_temp %.1f°C", min_temp_48h)
skip = True
if max_precip_48h < 0.1:
if any(kw in warn_type or kw in headline for kw in rain_keywords):
app.logger.info("Filtered rain warning: max_precip %.1f mm", max_precip_48h)
skip = True
if not skip:
filtered.append(w)
except Exception:
app.logger.exception("Error filtering unrealistic warnings")
return warnings
return filtered
@app.route("/")
def index():
return render_template("index.html")
@@ -395,6 +442,7 @@ def wetter():
forecast = forecast[current_idx:]
sunrise, sunset, dawn, dusk = get_sun_times(lat, lon)
warnings = get_dwd_warnings(lat, lon, state_hint=state_hint)
warnings = filter_unrealistic_warnings(warnings, forecast)
daily = {}
for h in forecast:
dt = h["datetime"]

View File

@@ -502,6 +502,7 @@ main { flex: 1; }
.warn-icon { font-size: 1.1rem; flex-shrink: 0; line-height: 1.3; }
.warn-item strong { color: inherit; }
.warn-item div { display: flex; flex-direction: column; gap: 0.2rem; }
.warn-time { color: #888; font-size: 0.7rem; margin: 0; margin-bottom: 0.25rem; font-weight: 500; }
.warn-desc { color: var(--muted2); font-size: 0.78rem; margin: 0; }
/* ═══════════════════════════════════════════════════════════

View File

@@ -76,14 +76,8 @@
<span class="warn-icon">{{ '⚠️' if w.level == 1 else ('🟠' if w.level == 2 else ('🔴' if w.level >= 3 else '⚠️')) }}</span>
<div>
<strong>{{ w.headline }}</strong>
{% if w.description %}<p class="warn-desc">{{ w.description[:140] }}{% if w.description|length > 140 %}…{% endif %}</p>{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
<!-- STÜNDLICH -->
{% if w.onset %}<p class="warn-time">{% if w.onset is string %}{{ w.onset[8:10] }}.{{ w.onset[5:7] }}. {{ w.onset[11:16] }}{% else %}{{ w.onset }}{% endif %}{% if w.expires %} {% if w.expires is string %}{{ w.expires[8:10] }}.{{ w.expires[5:7] }}. {{ w.expires[11:16] }}{% else %}{{ w.expires }}{% endif %}{% endif %}</p>{% endif %}
{% if w.description %}<p class="warn-desc">{{ w.description[:250] }}{% if w.description|length > 250 %}…{% endif %}</p>{% endif %}
<section class="section">
<h2 class="section-title">Stundenweise</h2>
<div class="hourly-strip-wrap">