modified: app.py
modified: static/css/style.css modified: templates/weather.html
This commit is contained in:
50
app.py
50
app.py
@@ -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"]
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user