PyTransportNSWv2 0.2.4__py3-none-any.whl → 0.3.1__py3-none-any.whl

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.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.1
2
+ Name: PyTransportNSWv2
3
+ Version: 0.3.1
4
+ Summary: Get detailed per-trip transport information from TransportNSW
5
+ Home-page: https://github.com/andystewart999/TransportNSW
6
+ Author: andystewart999
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: gtfs-realtime-bindings
15
+
16
+ # TransportNSW
17
+ Python lib to access Transport NSW information.
18
+
19
+ ## How to Use
20
+
21
+ ### Get an API Key
22
+ An OpenData account and API key is required to request the data. More information on how to create the free account can be found here:
23
+ https://opendata.transport.nsw.gov.au/user-guide. You need to register an application that needs both the Trip Planner and Realtime Vehicle Positions APIs
24
+
25
+ ### Get the stop IDs
26
+ The function needs the stop IDs for the source and destination, and optionally how many minutes from now the departure should be, and if you want to filter trips by a specific transport type. The easiest way to get the stop ID is via https://transportnsw.info/stops#/. It provides the option to search for either a location or a specific platform, bus stop or ferry wharf. Regardless of if you specify a general location for the origin or destination, the return information shows the stop_id for the actual arrival and destination platform, bus stop or ferry wharf.
27
+
28
+ If it's available, the general occupancy level and the latitude and longitude of the selected journey's vehicle (train, bus, etc) will be returned.
29
+
30
+ ### API Documentation
31
+ The source API details can be found here: https://opendata.transport.nsw.gov.au/sites/default/files/2023-08/Trip%20Planner%20API%20manual-opendataproduction%20v3.2.pdf
32
+
33
+ ### Parameters
34
+ ```python
35
+ .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0])
36
+ ```
37
+
38
+ TransportNSW's trip planner can work better if you use the general location IDs (eg Central Station) rather than a specific Stop ID (eg Central Station, Platform 19) for the destination, depending on the transport type. Forcing a specific end destination sometimes results in much more complicated trips. Also note that the API expects (and returns) the Stop IDs as strings, although so far they all appear to be numeric.
39
+
40
+ ### Sample Code
41
+
42
+ The following example will return the next trip that starts from a bus stop in St. Ives (207537) five minutes from now, to Central Station's general stop ID (10101100):
43
+
44
+ **Code:**
45
+ ```python
46
+ from TransportNSW import TransportNSW
47
+ tnsw = TransportNSW()
48
+ journey = tnsw.get_trip('207537', '10101100', 'YOUR_API_KEY', 5)
49
+ print(journey)
50
+ ```
51
+ **Result:**
52
+ ```python
53
+ {"due": 3, "origin_stop_id": "207537", "origin_name": "Mona Vale Rd at Shinfield Ave, St Ives", "departure_time": "2024-05-20T21:59:48Z", "destination_stop_id": "2000338", "destination_name": "Central Station, Platform 18, Sydney", "arrival_time": "2024-05-20T22:47:36Z", "origin_transport_type": "Bus", "origin_transport_name": "Sydney Buses Network", "origin_line_name": "195", "origin_line_name_short": "195", "changes": 1, "occupancy": "MANY_SEATS", "real_time_trip_id": "2096551", "latitude": -33.72665786743164, "longitude": 151.16305541992188}
54
+ ```
55
+ Fun fact: TransportNSW's raw API output calls itself JSON, but it uses single quotes for strings in defiance of the JSON standards. When using this wrapper the output is formatted such that `jq`, for example, is happy with.
56
+
57
+ * due: the time (in minutes) before the journey starts
58
+ * origin_stop_id: the specific departure stop id
59
+ * origin_name: the name of the departure location
60
+ * departure_time: the departure time, in UTC
61
+ * destination_stop_id: the specific destination stop id
62
+ * destination_name: the name of the destination location
63
+ * arrival_time: the planned arrival time at the origin, in UTC
64
+ * origin_transport_type: the type of transport, eg train, bus, ferry etc
65
+ * origin_transport_name: the full name of the transport provider
66
+ * origin_line_name & origin_line_name_short: the full and short names of the journey
67
+ * changes: how many transport changes are needed on the journey
68
+ * occupancy: how full the vehicle is, if available
69
+ * real_time_trip_id: the unique TransportNSW id for that specific journey, if available
70
+ * latitude & longitude: The location of the vehicle, if available
71
+
72
+ Please note that the origin and destination detail is just that - information about the first and last stops on the journey at the time the request was made. We don't return any intermediate steps, transport change types etc other than the total number of changes - the assumption is that you'll know the details of your specified trip, you just want to know when the next departure is. If you need much more detailed information then I recommend that you use the full Transport NSW trip planner website or application.
73
+ Also note that the 'transport_type' filter, if present, only makes sure that at least one leg of the journey includes that transport type.
74
+
75
+ ## Thank you
76
+ Thank you Dav0815 for your TransportNSW library that the vast majority of this fork is based on. I couldn't have done it without you!
77
+ https://github.com/Dav0815/TransportNSW
@@ -0,0 +1,7 @@
1
+ TransportNSW/TransportNSW.py,sha256=dKbYIRSLJUqmJHDH5ZOdLWK5mObW4uxGb89l1Um9_-k,13395
2
+ TransportNSW/__init__.py,sha256=O3iuSkUGkkDz4C7IlqvOXJHPJTxYz0q_48-94ESlkdg,86
3
+ PyTransportNSWv2-0.3.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
4
+ PyTransportNSWv2-0.3.1.dist-info/METADATA,sha256=fFZtnw4SXZZfkjGktIEW29Qaj3Re0QDPmxXfGmM2ddk,5326
5
+ PyTransportNSWv2-0.3.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
+ PyTransportNSWv2-0.3.1.dist-info/top_level.txt,sha256=C4LQEkr85VJtG9cklBu9IFpSKRaNSADPBTSPeXIYnOw,13
7
+ PyTransportNSWv2-0.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.34.2)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -8,6 +8,7 @@ import requests.exceptions
8
8
  import requests
9
9
  import logging
10
10
  import re
11
+ import json #For the output
11
12
 
12
13
  ATTR_DUE_IN = 'due'
13
14
 
@@ -46,6 +47,7 @@ class TransportNSW(object):
46
47
  self.destination_id = None
47
48
  self.api_key = None
48
49
  self.trip_wait_time = None
50
+ self.transport_type = None
49
51
  self.info = {
50
52
  ATTR_DUE_IN : 'n/a',
51
53
  ATTR_ORIGIN_STOP_ID : 'n/a',
@@ -65,7 +67,7 @@ class TransportNSW(object):
65
67
  ATTR_LONGITUDE : 'n/a'
66
68
  }
67
69
 
68
- def get_trip(self, name_origin, name_destination , api_key, trip_wait_time = 0):
70
+ def get_trip(self, name_origin, name_destination , api_key, trip_wait_time = 0, transport_type = 0):
69
71
  """Get the latest data from Transport NSW."""
70
72
  fmt = '%Y-%m-%dT%H:%M:%SZ'
71
73
 
@@ -73,12 +75,20 @@ class TransportNSW(object):
73
75
  self.destination = name_destination
74
76
  self.api_key = api_key
75
77
  self.trip_wait_time = trip_wait_time
78
+ self.transport_type = transport_type
76
79
 
77
80
  # This query always uses the current date and time - but add in any 'trip_wait_time' minutes
78
81
  now_plus_wait = datetime.now() + timedelta(minutes = trip_wait_time)
79
82
  itdDate = now_plus_wait.strftime('%Y%m%d')
80
83
  itdTime = now_plus_wait.strftime('%H%M')
81
84
 
85
+ if transport_type == 0:
86
+ # We only need to retrieve one trip - a transport type filter hasn't been applied
87
+ numberOfTrips = 1
88
+ else:
89
+ # 5 trips seems like a safe number
90
+ numberOfTrips = 5
91
+
82
92
  # Build the entire URL
83
93
  url = \
84
94
  'https://api.transport.nsw.gov.au/v1/tp/trip?' \
@@ -86,7 +96,9 @@ class TransportNSW(object):
86
96
  '&depArrMacro=dep&itdDate=' + itdDate + '&itdTime=' + itdTime + \
87
97
  '&type_origin=any&name_origin=' + name_origin + \
88
98
  '&type_destination=any&name_destination=' + name_destination + \
89
- '&calcNumberOfTrips=1&TfNSWDM=true&version=10.2.1.42'
99
+ '&calcNumberOfTrips=' + str(numberOfTrips) + \
100
+ '&TfNSWTR=true'
101
+
90
102
  auth = 'apikey ' + self.api_key
91
103
  header = {'Accept': 'application/json', 'Authorization': auth}
92
104
 
@@ -106,25 +118,36 @@ class TransportNSW(object):
106
118
 
107
119
  # Parse the result as a JSON object
108
120
  result = response.json()
109
- #print (result)
110
121
 
111
122
  # The API will always return a valid trip, so it's just a case of grabbing what we need...
112
123
  # We're only reporting on the origin and destination, it's out of scope to discuss the specifics of the ENTIRE journey
113
124
  # This isn't a route planner, just a 'how long until the next journey I've specified' tool
114
125
  # The assumption is that the travelee will know HOW to make the defined journey, they're just asking WHEN it's happening next
126
+ # All we potentially have to do is find the first trip that matches the transport_type filter
115
127
 
116
- legs = result['journeys'][0]['legs']
117
- first_leg = self.find_first_leg(legs)
118
- last_leg = self.find_last_leg(legs)
119
- changes = self.find_changes(legs)
128
+ if transport_type == 0:
129
+ # Just grab the first (and only) trip
130
+ journey = result['journeys'][0]
131
+ else:
132
+ # Look for a trip with a matching class filter in at least one of its legs. Could possibly be more stringent here, if ANY part of the journey fits the filter, it will be returned.
133
+ journey = self.find_first_journey(result['journeys'], transport_type)
120
134
 
121
- print (first_leg)
122
- print (last_leg)
123
- print (changes)
135
+ if journey is None:
136
+ logger.warning("No journey information returned")
137
+ return self.info
138
+
139
+ if journey['legs'] is None:
140
+ logger.warning("No journey information returned")
141
+ return self.info
142
+
143
+ legs = journey['legs']
144
+ first_leg = self.find_first_leg(legs, transport_type)
145
+ last_leg = self.find_last_leg(legs, transport_type)
146
+ changes = self.find_changes(legs, transport_type)
124
147
 
125
148
  origin = result['journeys'][0]['legs'][first_leg]['origin']
126
149
  # probably tidy this up when we start to get occupancy data back
127
- first_destination = result['journeys'][0]['legs'][first_leg]['destination']
150
+ first_stop = result['journeys'][0]['legs'][first_leg]['destination']
128
151
  destination = result['journeys'][0]['legs'][last_leg]['destination']
129
152
  transportation = result['journeys'][0]['legs'][first_leg]['transportation']
130
153
 
@@ -154,14 +177,19 @@ class TransportNSW(object):
154
177
  realtimetripid = transportation['properties']['RealtimeTripId']
155
178
 
156
179
  # Line info
157
- origin_line_name_short = transportation['disassembledName']
158
- origin_line_name = transportation['number']
180
+ origin_line_name_short = "unknown"
181
+ if 'disassembledName' in transportation:
182
+ origin_line_name_short = transportation['disassembledName']
183
+
184
+ origin_line_name = "unknown"
185
+ if 'number' in transportation:
186
+ origin_line_name = transportation['number']
159
187
 
160
188
  # Occupancy info, if it's there
161
189
  occupancy = 'UNKNOWN'
162
- if 'properties' in first_destination:
163
- if 'occupancy' in first_destination['properties']:
164
- occupancy = first_destination['properties']['occupancy']
190
+ if 'properties' in first_stop:
191
+ if 'occupancy' in first_stop['properties']:
192
+ occupancy = first_stop['properties']['occupancy']
165
193
 
166
194
  # Now might be a good time to see if we can also find the latitude and longitude
167
195
  # Using the Realtime Vehicle Positions API
@@ -228,45 +256,53 @@ class TransportNSW(object):
228
256
  ATTR_LATITUDE : latitude,
229
257
  ATTR_LONGITUDE : longitude
230
258
  }
231
- return self.info
259
+ return json.dumps(self.info)
232
260
 
261
+ def find_first_journey(self, journeys, journeytype):
262
+ # Find the first journey whose first leg is of the requested type
263
+ journey_count = len(journeys)
264
+ for journey in range (0, journey_count, 1):
265
+ leg = self.find_first_leg(journeys[journey]['legs'], journeytype)
266
+ if leg is not None:
267
+ return journeys[journey]
233
268
 
234
- def find_first_leg(self, legs):
235
- # Find the first non-footpath leg
269
+ # Hmm, we didn't find one
270
+ return None
271
+
272
+
273
+ def find_first_leg(self, legs, legtype):
274
+ # Find the first leg of the requested type
236
275
  leg_count = len(legs)
237
276
  for leg in range (0, leg_count, 1):
238
277
  leg_class = legs[leg]['transportation']['product']['class']
239
- #print (leg_class)
240
- if leg_class < 100:
241
- # 100 is the class for walking, anything less is a train, bus, etc
278
+
279
+ if leg_class == legtype or legtype == 0:
242
280
  return leg
243
281
 
244
282
  # Hmm, we didn't find one
245
283
  return None
246
284
 
247
285
 
248
- def find_last_leg(self, legs):
249
- # Find the last non-footpath leg
286
+ def find_last_leg(self, legs, legtype):
287
+ # Find the last leg of the requested type
250
288
  leg_count = len(legs)
251
289
  for leg in range (leg_count - 1, -1, -1):
252
290
  leg_class = legs[leg]['transportation']['product']['class']
253
- #print (leg_class)
254
- if leg_class < 100:
255
- # 100 is the class for walking, anything less is a train, bus, etc
291
+
292
+ if leg_class == legtype or legtype == 0:
256
293
  return leg
257
294
 
258
295
  # Hmm, we didn't find one
259
296
  return None
260
297
 
261
- def find_changes(self, legs):
298
+ def find_changes(self, legs, legtype):
262
299
  # Find out how often we have to change
263
300
  changes = 0
264
301
  leg_count = len(legs)
265
302
 
266
303
  for leg in range (0, leg_count, 1):
267
304
  leg_class = legs[leg]['transportation']['product']['class']
268
- if leg_class < 100:
269
- # 100 is the class for walking, anything less is a train, bus, etc
305
+ if leg_class == legtype or legtype == 0:
270
306
  changes = changes + 1
271
307
 
272
308
  return changes - 1
@@ -280,7 +316,10 @@ class TransportNSW(object):
280
316
  5: "Bus",
281
317
  7: "Coach",
282
318
  9: "Ferry",
283
- 11: "School bus"
319
+ 11: "School bus",
320
+ 99: "Walk",
321
+ 100: "Walk",
322
+ 107: "Cycle"
284
323
  }
285
324
  return modes.get(iconId, None)
286
325
 
@@ -1,75 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: PyTransportNSWv2
3
- Version: 0.2.4
4
- Summary: Get detailed per-trip transport information from TransportNSW
5
- Home-page: https://github.com/andystewart999/TransportNSW
6
- Author: andystewart999
7
- License: UNKNOWN
8
- Platform: UNKNOWN
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Classifier: Intended Audience :: Developers
13
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
- Description-Content-Type: text/markdown
15
- Requires-Dist: gtfs-realtime-bindings
16
-
17
- # TransportNSW
18
- Python lib to access Transport NSW information.
19
-
20
- ## How to Use
21
-
22
- ### Get an API Key
23
- An OpenData account and API key is required to request the data. More information on how to create the free account can be found here:
24
- https://opendata.transport.nsw.gov.au/user-guide. You need to register an application that needs both the Trip Planner and Realtime Vehicle Positions APIs
25
-
26
- ### Get the stop IDs
27
- The library needs the stop ID for the source and destination, and optionally how many minutes from now the departure should be. The easiest way to get the stop ID is via https://transportnsw.info/stops#/. It provides the option to search for either a location or a specific platform, bus stop or ferry wharf. Regardless of if you specify a general location for the origin or destination, the return information shows the stop_id for the actual arrival and destination platform, bus stop or ferry wharf.
28
-
29
- If it's available, the general occupancy level and the latitude and longitude of the selected journey's vehicle (train, bus, etc) will be returned.
30
-
31
- ### API Documentation
32
- The source API details can be found here: https://opendata.transport.nsw.gov.au/node/601/exploreapi
33
-
34
- ### Parameters
35
- ```python
36
- .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0])
37
- ```
38
-
39
- ### Sample Code
40
- The following example will return the next trip from Gordon Station to Pymble Station, without specifying a platform.
41
-
42
- **Code:**
43
- ```python
44
- from TransportNSW import TransportNSW
45
- tnsw = TransportNSW()
46
- journey = tnsw.get_trip('10101121', '10101123', 'YOUR_API_KEY')
47
- print(journey)
48
- ```
49
- **Result:**
50
- ```python
51
- {'due': 5, 'origin_stop_id': '207263', 'origin_name': 'Gordon Station', 'origin_detail': 'Platform 3', 'departure_time': '2020-06-14T10:21:30Z', 'destination_stop_id': '2073162', 'destination_name': 'Pymble Station', 'destination_detail': 'Platform 2', 'arrival_time': '2020-06-14T10:23:30Z', 'transport_type': 'Train', 'transport_name': 'Sydney Trains Network', 'line_name': 'T1 North Shore & Western Line', 'line_name_short': 'T1', 'occupancy': 'UNKNOWN', 'real_time_trip_id': '104P.1379.110.128.T.8.61720413', 'latitude': -33.76505661010742, 'longitude': 151.1614227294922}
52
- ```
53
-
54
- * due: the time (in minutes) before the vehicle arrives
55
- * origin_stop_id: the specific departure stop id
56
- * origin_name: the name of the general departure location
57
- * origin_detail: the specific departure location
58
- * arrival_time: the arrival time at the origin
59
- * transport_type: the type of transport, eg train, bus, ferry etc
60
- * transport_name: the full name of transport providere
61
- * line_name & line_name_short: the full and short names of the journey
62
- * occupancy: how full the vehicle is
63
- * real_time_trip_id: the unique TransportNSW id for that specific journey
64
- * latitude & longitude: The location of the vehicle, if available
65
-
66
- ### To do:
67
- * Add an option to filter by specific transport type, useful if the general departure and arrival ids are being used
68
- * Add an option to show brief vs verbose information
69
-
70
- ## Thank you
71
- Thank you Dav0815 for your TransportNSW library that the vast majority of this fork is based on. I couldn't have done it without you!
72
-
73
- https://github.com/Dav0815/TransportNSW
74
-
75
-
@@ -1,8 +0,0 @@
1
- TransportNSW/TransportNSW.py,sha256=-tWeaLhuGCWD9LTcU5BO8diBBD3R-Oqm3SFd65_1DR4,11834
2
- TransportNSW/__init__.py,sha256=O3iuSkUGkkDz4C7IlqvOXJHPJTxYz0q_48-94ESlkdg,86
3
- TransportNSW/test.py,sha256=Kr60DTJwfChdN3GBmyu4jEaubxK0-QncSeG4K0a4yr4,805
4
- PyTransportNSWv2-0.2.4.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
5
- PyTransportNSWv2-0.2.4.dist-info/METADATA,sha256=dlNjFg3FAEaQTM4hEhQ2sVgFV6vgeNUsgWD8RVOr7PI,3792
6
- PyTransportNSWv2-0.2.4.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
7
- PyTransportNSWv2-0.2.4.dist-info/top_level.txt,sha256=C4LQEkr85VJtG9cklBu9IFpSKRaNSADPBTSPeXIYnOw,13
8
- PyTransportNSWv2-0.2.4.dist-info/RECORD,,
TransportNSW/test.py DELETED
@@ -1,25 +0,0 @@
1
- from TransportNSW import TransportNSW
2
- tnsw = TransportNSW()
3
-
4
- sApiKey = 'oWwCalKSy0ZQB5JTw7sTXlcaOjv01fto1W3v'
5
- #sStopId = '207261' #Gordon, platform 1
6
- #sStopId = '10101121' #Gordon station
7
- #sDestId = '10101122' #Pymble station
8
- sStopId = '2000336' #Central, platform 16
9
- sDestId = '2015137' #Redfern, platform 7
10
- #sStopId = '2015133' #Redfern, platform 11
11
- #sStopId = '207248' # Gordon bus stop
12
- sStopId = '207537' # St Ives stop
13
- #sDestId = '2073161' #Pymble station
14
- #sStopId = '2000225' # Circular Quay Wharf 4
15
- #sDestId = '20883' # Taronga Zoo Wharf
16
- #sStopId = '20461' # Abbotsford
17
- #sDestId = '2137186' # Cabarita
18
- sDestId = '10101100' # Central
19
- #sDestId = '10101421' # Redfern
20
-
21
- nMinDueTime = 0
22
-
23
- journey = tnsw.get_trip(sStopId, sDestId, sApiKey, nMinDueTime)
24
- print (' ')
25
- print(journey)