python-openpublictransport 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/PKG-INFO +1 -1
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/pyproject.toml +4 -1
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/rejseplanen.py +53 -12
- python_openpublictransport-0.1.6/src/openpublictransport/py.typed +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/PKG-INFO +1 -1
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/SOURCES.txt +1 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/setup.cfg +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/__init__.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/const.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/exceptions.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/models.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/parsers.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/__init__.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/avv.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/base.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/beg.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/bsvg.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/bvg.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/db.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/ding.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/efa_base.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/fptf_base.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/gtfsde.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/hvv.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/kvv.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/mvv.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/national_rail.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/nta.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/nvbw.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/nwl.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/oebb.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/otp.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/otp_base.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/otp_custom.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/rmv.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/rvv.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/sbb.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/trafiklab.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/transitous.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/trias_base.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vagfr.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vbn.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vgn.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vrn.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vrr.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vvo.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vvs.py +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/dependency_links.txt +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/requires.txt +0 -0
- {python_openpublictransport-0.1.4 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-openpublictransport"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
description = "Python library for public transport APIs (EFA, OTP2, TRIAS, FPTF, HAFAS, GTFS-RT)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -25,5 +25,8 @@ dev = [
|
|
|
25
25
|
[tool.setuptools.packages.find]
|
|
26
26
|
where = ["src"]
|
|
27
27
|
|
|
28
|
+
[tool.setuptools.package-data]
|
|
29
|
+
openpublictransport = ["py.typed"]
|
|
30
|
+
|
|
28
31
|
[tool.pytest.ini_options]
|
|
29
32
|
asyncio_mode = "auto"
|
|
@@ -38,17 +38,49 @@ _PRODUCT_MAPPING: Dict[str, str] = {
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def _parse_hafas_time(date_str: str, time_str: str, tz: Any) -> Optional[datetime]:
|
|
41
|
-
"""Parse Rejseplanen date
|
|
41
|
+
"""Parse a Rejseplanen date + time into a timezone-aware datetime.
|
|
42
|
+
|
|
43
|
+
The HAFAS REST API at www.rejseplanen.dk returns ISO dates (YYYY-MM-DD) and
|
|
44
|
+
times as HH:MM:SS; older HAFAS deployments used DD.MM.YY / HH:MM. Handle both.
|
|
45
|
+
"""
|
|
42
46
|
if not date_str or not time_str:
|
|
43
47
|
return None
|
|
48
|
+
time_fmt = "%H:%M:%S" if time_str.count(":") == 2 else "%H:%M"
|
|
49
|
+
date_fmt = "%Y-%m-%d" if "-" in date_str else "%d.%m.%y"
|
|
44
50
|
try:
|
|
45
|
-
|
|
46
|
-
dt = datetime.strptime(f"{date_str} {time_str}", f"%d.%m.%y {time_fmt}")
|
|
51
|
+
dt = datetime.strptime(f"{date_str} {time_str}", f"{date_fmt} {time_fmt}")
|
|
47
52
|
return dt.replace(tzinfo=tz)
|
|
48
53
|
except ValueError:
|
|
49
54
|
return None
|
|
50
55
|
|
|
51
56
|
|
|
57
|
+
def _resolve_transport_type(stop: Dict[str, Any]) -> str:
|
|
58
|
+
"""Derive the transport type from the HAFAS product category.
|
|
59
|
+
|
|
60
|
+
The departure's top-level ``type`` is a stop type (e.g. "ST"), not the
|
|
61
|
+
product, so read the category from ``Product[].catOut`` and fall back to the
|
|
62
|
+
line name (e.g. "Bus 250S", "Metro M4", "Re 1277").
|
|
63
|
+
"""
|
|
64
|
+
products = stop.get("Product", [])
|
|
65
|
+
if isinstance(products, dict):
|
|
66
|
+
products = [products]
|
|
67
|
+
cat = ""
|
|
68
|
+
if products and isinstance(products[0], dict):
|
|
69
|
+
p = products[0]
|
|
70
|
+
cat = (p.get("catOut") or p.get("catOutL") or p.get("catIn") or "").strip()
|
|
71
|
+
text = f"{cat} {stop.get('name', '')}".lower()
|
|
72
|
+
if "bus" in text: # Bus, Togbus (rail-replacement), Natbus, Expresbus
|
|
73
|
+
return "bus"
|
|
74
|
+
if "metro" in text:
|
|
75
|
+
return "subway"
|
|
76
|
+
if "letbane" in text or "tram" in text:
|
|
77
|
+
return "tram"
|
|
78
|
+
if "færge" in text or "ferry" in text or "havnebus" in text:
|
|
79
|
+
return "ferry"
|
|
80
|
+
# Everything else (IC, ICL, Lyn, Re, Reg, Tog, S-tog, RJ, EC/ECE, …) is rail.
|
|
81
|
+
return "train"
|
|
82
|
+
|
|
83
|
+
|
|
52
84
|
class RejseplanenProvider(BaseProvider):
|
|
53
85
|
"""Rejseplanen (Denmark) provider via HAFAS REST API."""
|
|
54
86
|
|
|
@@ -122,8 +154,11 @@ class RejseplanenProvider(BaseProvider):
|
|
|
122
154
|
_LOGGER.warning("%s: API error %s: %s", self.provider_name, data.get("errorCode"), data.get("errorText"))
|
|
123
155
|
return None
|
|
124
156
|
|
|
125
|
-
|
|
126
|
-
|
|
157
|
+
# www.rejseplanen.dk returns "Departure" at the top level; older HAFAS
|
|
158
|
+
# deployments nested it under "DepartureBoard". Support both.
|
|
159
|
+
departures = data.get("Departure")
|
|
160
|
+
if departures is None:
|
|
161
|
+
departures = data.get("DepartureBoard", {}).get("Departure", [])
|
|
127
162
|
if isinstance(departures, dict):
|
|
128
163
|
departures = [departures]
|
|
129
164
|
|
|
@@ -156,8 +191,7 @@ class RejseplanenProvider(BaseProvider):
|
|
|
156
191
|
delay = max(0, int((actual_dt - planned_dt).total_seconds() / 60))
|
|
157
192
|
minutes_until = max(0, int((actual_dt - now).total_seconds() / 60))
|
|
158
193
|
|
|
159
|
-
|
|
160
|
-
transport_type = _PRODUCT_MAPPING.get(type_str, "train")
|
|
194
|
+
transport_type = _resolve_transport_type(stop)
|
|
161
195
|
|
|
162
196
|
line = stop.get("name", "")
|
|
163
197
|
destination = stop.get("direction", stop.get("finalStop", ""))
|
|
@@ -223,13 +257,20 @@ class RejseplanenProvider(BaseProvider):
|
|
|
223
257
|
if not isinstance(data, dict):
|
|
224
258
|
return []
|
|
225
259
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
260
|
+
# www.rejseplanen.dk returns "stopLocationOrCoordLocation" (a list whose
|
|
261
|
+
# entries wrap a StopLocation or CoordLocation); older HAFAS deployments
|
|
262
|
+
# used LocationList.StopLocation. Support both.
|
|
263
|
+
entries = data.get("stopLocationOrCoordLocation")
|
|
264
|
+
if entries is None:
|
|
265
|
+
entries = data.get("LocationList", {}).get("StopLocation", [])
|
|
266
|
+
if isinstance(entries, dict):
|
|
267
|
+
entries = [entries]
|
|
230
268
|
|
|
231
269
|
results = []
|
|
232
|
-
for
|
|
270
|
+
for entry in entries:
|
|
271
|
+
if not isinstance(entry, dict):
|
|
272
|
+
continue
|
|
273
|
+
loc = entry.get("StopLocation", entry)
|
|
233
274
|
if not isinstance(loc, dict):
|
|
234
275
|
continue
|
|
235
276
|
station_id = loc.get("extId") or loc.get("id", "")
|
|
File without changes
|
|
@@ -4,6 +4,7 @@ src/openpublictransport/const.py
|
|
|
4
4
|
src/openpublictransport/exceptions.py
|
|
5
5
|
src/openpublictransport/models.py
|
|
6
6
|
src/openpublictransport/parsers.py
|
|
7
|
+
src/openpublictransport/py.typed
|
|
7
8
|
src/openpublictransport/providers/__init__.py
|
|
8
9
|
src/openpublictransport/providers/avv.py
|
|
9
10
|
src/openpublictransport/providers/base.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|