fix: filter out entries without measurement values

The source sometimes returns placeholder entries (no water level, no flow) for the newest timestamps. Skip them so latest is the most recent entry with real data.
This commit is contained in:
Matthias Jacob committed 2026-10-27 23:28:53 +01:00
1 parent fc46d8fe92
commit 15c9a8cb70
1 file changed
+19 -8
+19 -8
View File
@@ -211,16 +211,25 @@ def parse_entry(entry):
), ),
} }
def parse_valid_entries(feed_entries):
# Die Quelle liefert für neueste Zeitpunkte teils Platzhalter ohne Messwerte.
entries = [parse_entry(entry) for entry in feed_entries]
return [
e for e in entries
if e["water_level_cm"] is not None or e["flow_m3s"] is not None
]
def _get_latest_for_station(station_id: str): def _get_latest_for_station(station_id: str):
cache_hit = _latest_cache.get(station_id) cache_hit = _latest_cache.get(station_id)
if cache_hit and _is_cache_fresh(cache_hit.get("fetched_at"), LATEST_CACHE_TTL_SECONDS): if cache_hit and _is_cache_fresh(cache_hit.get("fetched_at"), LATEST_CACHE_TTL_SECONDS):
return cache_hit.get("latest") return cache_hit.get("latest")
feed = feedparser.parse(BASE.format(station_id)) feed = feedparser.parse(BASE.format(station_id))
if not feed.entries: entries = parse_valid_entries(feed.entries)
if not entries:
raise ValueError("Keine Daten gefunden") raise ValueError("Keine Daten gefunden")
latest = parse_entry(feed.entries[0]) latest = entries[0]
_latest_cache[station_id] = { _latest_cache[station_id] = {
"latest": latest, "latest": latest,
"fetched_at": _now_utc(), "fetched_at": _now_utc(),
@@ -298,15 +307,17 @@ def list_pegel(limit: int | None = None):
summary="Messwerte eines Pegels abrufen", summary="Messwerte eines Pegels abrufen",
description=( description=(
"Liefert alle im RSS-Feed verfügbaren Einträge einer Station. " "Liefert alle im RSS-Feed verfügbaren Einträge einer Station. "
"`latest` ist der neueste Eintrag und entspricht `entries[0]`; " "Einträge ohne Wasserstand und ohne Durchfluss (Platzhalter der Quelle "
"für noch nicht verfügbare Messwerte) werden herausgefiltert. "
"`latest` ist der neueste verbleibende Eintrag und entspricht `entries[0]`; "
"die Reihenfolge von `entries` folgt dem Feed (neueste zuerst). " "die Reihenfolge von `entries` folgt dem Feed (neueste zuerst). "
"Pegel-IDs bestehen aus mindestens sechs Ziffern. Nicht vorhandene " "Pegel-IDs bestehen aus mindestens sechs Ziffern. Einzelne fehlende "
"Messwerte werden als `null` ausgegeben." "Messwerte werden als `null` ausgegeben."
), ),
response_description="Stationsmetadaten, neuester Messwert und Feed-Historie.", response_description="Stationsmetadaten, neuester Messwert und Feed-Historie.",
responses={ responses={
400: {"description": "Die Pegel-ID besteht nicht aus mindestens sechs Ziffern."}, 400: {"description": "Die Pegel-ID besteht nicht aus mindestens sechs Ziffern."},
404: {"description": "Für diese Pegel-ID enthält der RSS-Feed keine Einträge."}, 404: {"description": "Für diese Pegel-ID enthält der RSS-Feed keine Einträge mit Messwerten."},
}, },
) )
def get_pegel(pegel_id: str): def get_pegel(pegel_id: str):
@@ -316,10 +327,10 @@ def get_pegel(pegel_id: str):
url = BASE.format(pegel_id) url = BASE.format(pegel_id)
feed = feedparser.parse(url) feed = feedparser.parse(url)
if not feed.entries: entries = parse_valid_entries(feed.entries)
raise HTTPException(404, "Keine Daten gefunden")
entries = [parse_entry(entry) for entry in feed.entries] if not entries:
raise HTTPException(404, "Keine Daten gefunden")
feed_obj = getattr(feed, "feed", None) feed_obj = getattr(feed, "feed", None)
feed_title = feed_obj.get("title") if isinstance(feed_obj, dict) else None feed_title = feed_obj.get("title") if isinstance(feed_obj, dict) else None