python-openpublictransport 0.1.5__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.
Files changed (50) hide show
  1. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/PKG-INFO +1 -1
  2. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/pyproject.toml +1 -1
  3. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/rejseplanen.py +53 -12
  4. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/PKG-INFO +1 -1
  5. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/setup.cfg +0 -0
  6. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/__init__.py +0 -0
  7. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/const.py +0 -0
  8. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/exceptions.py +0 -0
  9. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/models.py +0 -0
  10. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/parsers.py +0 -0
  11. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/__init__.py +0 -0
  12. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/avv.py +0 -0
  13. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/base.py +0 -0
  14. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/beg.py +0 -0
  15. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/bsvg.py +0 -0
  16. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/bvg.py +0 -0
  17. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/db.py +0 -0
  18. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/ding.py +0 -0
  19. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/efa_base.py +0 -0
  20. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/fptf_base.py +0 -0
  21. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/gtfsde.py +0 -0
  22. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/hvv.py +0 -0
  23. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/kvv.py +0 -0
  24. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/mvv.py +0 -0
  25. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/national_rail.py +0 -0
  26. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/nta.py +0 -0
  27. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/nvbw.py +0 -0
  28. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/nwl.py +0 -0
  29. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/oebb.py +0 -0
  30. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/otp.py +0 -0
  31. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/otp_base.py +0 -0
  32. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/otp_custom.py +0 -0
  33. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/rmv.py +0 -0
  34. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/rvv.py +0 -0
  35. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/sbb.py +0 -0
  36. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/trafiklab.py +0 -0
  37. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/transitous.py +0 -0
  38. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/trias_base.py +0 -0
  39. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vagfr.py +0 -0
  40. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vbn.py +0 -0
  41. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vgn.py +0 -0
  42. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vrn.py +0 -0
  43. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vrr.py +0 -0
  44. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vvo.py +0 -0
  45. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/providers/vvs.py +0 -0
  46. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/openpublictransport/py.typed +0 -0
  47. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/SOURCES.txt +0 -0
  48. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/dependency_links.txt +0 -0
  49. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/requires.txt +0 -0
  50. {python_openpublictransport-0.1.5 → python_openpublictransport-0.1.6}/src/python_openpublictransport.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-openpublictransport
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Python library for public transport APIs (EFA, OTP2, TRIAS, FPTF, HAFAS, GTFS-RT)
5
5
  License: MIT
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-openpublictransport"
7
- version = "0.1.5"
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" }
@@ -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 (DD.MM.YY) and time (HH:MM) into a timezone-aware datetime."""
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
- time_fmt = "%H:%M:%S" if time_str.count(":") == 2 else "%H:%M"
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
- board = data.get("DepartureBoard", {})
126
- departures = board.get("Departure", [])
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
- type_str = stop.get("type", "").upper()
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
- location_list = data.get("LocationList", {})
227
- stops = location_list.get("StopLocation", [])
228
- if isinstance(stops, dict):
229
- stops = [stops]
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 loc in stops:
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", "")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-openpublictransport
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Python library for public transport APIs (EFA, OTP2, TRIAS, FPTF, HAFAS, GTFS-RT)
5
5
  License: MIT
6
6
  Requires-Python: >=3.11