Skip to content

Commit 734362a

Browse files
committed
Fix HE without flow (add FetchError class)
1 parent cf5a6cb commit 734362a

7 files changed

Lines changed: 87 additions & 64 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.0.10 (2026-02-17)
2+
### Fixed
3+
- Fix HE without flow (add FetchError class)
4+
15
## 1.0.9 (2025-12-18)
26
### Fixed
37
- Fix SH pegel page scraping

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 stephan192
3+
Copyright (c) 2026 stephan192
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "lhpapi"
7-
version = "1.0.9"
7+
version = "1.0.10"
88
authors = [
99
{ name="stephan192", email="stephan192@outlook.com" },
1010
]

src/lhpapi/api_utils.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
from dataclasses import dataclass
66
from datetime import UTC, datetime
7+
from json import JSONDecodeError
78
from typing import Any
89
from zoneinfo import ZoneInfo
910

1011
from bs4 import BeautifulSoup
1112
from requests import get
13+
from requests.exceptions import RequestException
1214

1315

1416
class LHPError(Exception):
@@ -22,6 +24,10 @@ def __init__(self, exception: Exception | str, location: str) -> None:
2224
super().__init__(f"{location}: {exception.__class__.__name__}: {exception}")
2325

2426

27+
class FetchError(Exception):
28+
""" "Exception occurred while fetching data via requests."""
29+
30+
2531
@dataclass
2632
class StaticData:
2733
"""Class containing the static data."""
@@ -46,22 +52,28 @@ class DynamicData:
4652

4753
def fetch_json(url: str, timeout: float = 10.0) -> Any:
4854
"""Fetch data via json."""
49-
response = get(url=url, timeout=timeout)
50-
response.raise_for_status()
51-
json_data = response.json()
52-
return json_data
55+
try:
56+
response = get(url=url, timeout=timeout)
57+
response.raise_for_status()
58+
json_data = response.json()
59+
return json_data
60+
except (RequestException, JSONDecodeError) as err:
61+
raise FetchError(err) from err
5362

5463

5564
def fetch_text(url: str, timeout: float = 10.0, forced_encoding: str = None) -> str:
5665
"""Fetch data via text."""
57-
response = get(url=url, timeout=timeout)
58-
if forced_encoding is not None:
59-
response.encoding = forced_encoding
60-
else:
61-
# Override encoding by real educated guess (required for e.g. BW)
62-
response.encoding = response.apparent_encoding
63-
response.raise_for_status()
64-
return response.text
66+
try:
67+
response = get(url=url, timeout=timeout)
68+
if forced_encoding is not None:
69+
response.encoding = forced_encoding
70+
else:
71+
# Override encoding by real educated guess (required for e.g. BW)
72+
response.encoding = response.apparent_encoding
73+
response.raise_for_status()
74+
return response.text
75+
except RequestException as err:
76+
raise FetchError(err) from err
6577

6678

6779
def fetch_soup(

src/lhpapi/he_api.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .api_utils import (
66
DynamicData,
7+
FetchError,
78
LHPError,
89
StaticData,
910
calc_stage,
@@ -51,22 +52,25 @@ def get_basic_station_data(ident: str) -> tuple[str, str, str, str]:
5152
def get_stage_levels(internal_url: str) -> list[float]:
5253
"""Get stage levels."""
5354
stage_levels = [None] * 4
54-
alarmlevels = fetch_json(internal_url + "/W/alarmlevel.json")
55-
for station_data in alarmlevels:
56-
if (
57-
"ts_name" in station_data
58-
and "data" in station_data
59-
and isinstance(station_data["data"], list)
60-
and len(station_data["data"]) > 0
61-
):
62-
# Check if ts_name is one of the desired values
63-
if station_data["ts_name"] == "Meldestufe1":
64-
stage_levels[0] = convert_to_float(station_data["data"][-1][1])
65-
elif station_data["ts_name"] == "Meldestufe2":
66-
stage_levels[1] = convert_to_float(station_data["data"][-1][1])
67-
# No equivalent to stage_levels[2] available
68-
elif station_data["ts_name"] == "Meldestufe3":
69-
stage_levels[3] = convert_to_float(station_data["data"][-1][1])
55+
try:
56+
alarmlevels = fetch_json(internal_url + "/W/alarmlevel.json")
57+
for station_data in alarmlevels:
58+
if (
59+
"ts_name" in station_data
60+
and "data" in station_data
61+
and isinstance(station_data["data"], list)
62+
and len(station_data["data"]) > 0
63+
):
64+
# Check if ts_name is one of the desired values
65+
if station_data["ts_name"] == "Meldestufe1":
66+
stage_levels[0] = convert_to_float(station_data["data"][-1][1])
67+
elif station_data["ts_name"] == "Meldestufe2":
68+
stage_levels[1] = convert_to_float(station_data["data"][-1][1])
69+
# No equivalent to stage_levels[2] available
70+
elif station_data["ts_name"] == "Meldestufe3":
71+
stage_levels[3] = convert_to_float(station_data["data"][-1][1])
72+
except FetchError:
73+
pass
7074
return stage_levels
7175

7276

@@ -107,7 +111,7 @@ def update_HE(static_data: StaticData) -> DynamicData: # pylint: disable=invali
107111
level = convert_to_float(dataset["data"][-1][1])
108112
stage = calc_stage(level, static_data.stage_levels)
109113
break
110-
except (IndexError, KeyError):
114+
except (IndexError, KeyError, FetchError):
111115
level = None
112116
stage = None
113117

@@ -122,7 +126,7 @@ def update_HE(static_data: StaticData) -> DynamicData: # pylint: disable=invali
122126
last_update_str_q = dataset["data"][-1][0]
123127
flow = convert_to_float(dataset["data"][-1][1])
124128
break
125-
except (IndexError, KeyError):
129+
except (IndexError, KeyError, FetchError):
126130
flow = None
127131

128132
last_update = None

src/lhpapi/nw_api.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .api_utils import (
66
DynamicData,
7+
FetchError,
78
LHPError,
89
StaticData,
910
calc_stage,
@@ -45,36 +46,42 @@ def get_stage_levels(internal_url: str) -> list[float]:
4546
"""Get stage levels."""
4647
stage_levels = [None] * 4
4748
nw_stages = fetch_json(internal_url + "/S/alarmlevel.json")
48-
for station_data in nw_stages:
49-
# Unfortunately the source data seems quite incomplete.
50-
# So we check if the required keys are present in the station_data dictionary:
51-
if (
52-
"ts_name" in station_data
53-
and "data" in station_data
54-
and isinstance(station_data["data"], list)
55-
and len(station_data["data"]) > 0
56-
):
57-
# Check if ts_name is one of the desired values
58-
if station_data["ts_name"] == "W.Informationswert_1":
59-
stage_levels[0] = convert_to_float(station_data["data"][-1][1])
60-
elif station_data["ts_name"] == "W.Informationswert_2":
61-
stage_levels[1] = convert_to_float(station_data["data"][-1][1])
62-
elif station_data["ts_name"] == "W.Informationswert_3":
63-
stage_levels[2] = convert_to_float(station_data["data"][-1][1])
49+
try:
50+
for station_data in nw_stages:
51+
# Unfortunately the source data seems quite incomplete.
52+
# So we check if the required keys are present in the station_data dictionary:
53+
if (
54+
"ts_name" in station_data
55+
and "data" in station_data
56+
and isinstance(station_data["data"], list)
57+
and len(station_data["data"]) > 0
58+
):
59+
# Check if ts_name is one of the desired values
60+
if station_data["ts_name"] == "W.Informationswert_1":
61+
stage_levels[0] = convert_to_float(station_data["data"][-1][1])
62+
elif station_data["ts_name"] == "W.Informationswert_2":
63+
stage_levels[1] = convert_to_float(station_data["data"][-1][1])
64+
elif station_data["ts_name"] == "W.Informationswert_3":
65+
stage_levels[2] = convert_to_float(station_data["data"][-1][1])
66+
except FetchError:
67+
pass
6468
return stage_levels
6569

6670

6771
def get_hint(internal_url: str) -> str:
6872
"""Get hint."""
6973
hint = None
70-
data = fetch_json(internal_url + "/S/week.json")
71-
if len(data[0]["AdminStatus"].strip()) > 0:
72-
hint = data[0]["AdminStatus"].strip()
73-
if len(data[0]["AdminBemerkung"].strip()) > 0:
74-
if len(hint) > 0:
75-
hint += " / " + data[0]["AdminBemerkung"].strip()
76-
else:
77-
hint = data[0]["AdminBemerkung"].strip()
74+
try:
75+
data = fetch_json(internal_url + "/S/week.json")
76+
if len(data[0]["AdminStatus"].strip()) > 0:
77+
hint = data[0]["AdminStatus"].strip()
78+
if len(data[0]["AdminBemerkung"].strip()) > 0:
79+
if len(hint) > 0:
80+
hint += " / " + data[0]["AdminBemerkung"].strip()
81+
else:
82+
hint = data[0]["AdminBemerkung"].strip()
83+
except FetchError:
84+
pass
7885
return hint
7986

8087

src/lhpapi/st_api.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
from __future__ import annotations
44

5-
import requests
6-
75
from .api_utils import (
86
DynamicData,
7+
FetchError,
98
LHPError,
109
StaticData,
1110
calc_stage,
@@ -70,8 +69,7 @@ def get_stage_levels(internal_url: str) -> list[float]:
7069
stage_levels[2] = convert_to_float(station_data["data"][-1][1])
7170
elif station_data["ts_name"] == "Alarmstufe 4":
7271
stage_levels[3] = convert_to_float(station_data["data"][-1][1])
73-
# eg, 502180/W/alarmlevel.json does not exist (404)
74-
except requests.exceptions.HTTPError:
72+
except FetchError:
7573
pass
7674
return stage_levels
7775

@@ -108,8 +106,7 @@ def update_ST(static_data: StaticData) -> DynamicData: # pylint: disable=invali
108106
last_update_str_w = data[0]["data"][-1][0]
109107
level = convert_to_float(data[0]["data"][-1][1])
110108
stage = calc_stage(level, static_data.stage_levels)
111-
# requests.exceptions.HTTPError for handling 404 etc
112-
except (IndexError, KeyError, requests.exceptions.HTTPError):
109+
except (IndexError, KeyError, FetchError):
113110
level = None
114111
stage = None
115112

@@ -120,8 +117,7 @@ def update_ST(static_data: StaticData) -> DynamicData: # pylint: disable=invali
120117
# Parse data
121118
last_update_str_q = data[0]["data"][-1][0]
122119
flow = convert_to_float(data[0]["data"][-1][1])
123-
# requests.exceptions.HTTPError for handling 404 etc
124-
except (IndexError, KeyError, requests.exceptions.HTTPError):
120+
except (IndexError, KeyError, FetchError):
125121
flow = None
126122

127123
last_update = None

0 commit comments

Comments
 (0)