PyTransportNSWv2 0.8.7__tar.gz → 0.8.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyTransportNSWv2
3
- Version: 0.8.7
3
+ Version: 0.8.8
4
4
  Summary: Get detailed per-trip transport information from TransportNSW
5
5
  Home-page: https://github.com/andystewart999/TransportNSW
6
6
  Author: andystewart999
@@ -25,7 +25,7 @@ Description: # TransportNSWv2
25
25
 
26
26
  ### Parameters
27
27
  ```python
28
- .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0], [strict_transport_type = True|False], [raw_output = True|False], [journeys_to_return = 1], [route_filter = ''], [include_realtime_location = True], [include_alerts = 'none'], [alert_type = 'all'] )
28
+ .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0], [strict_transport_type = True|False], [raw_output = True|False], [journeys_to_return = 1], [route_filter = ''], [include_realtime_location = True], [include_alerts = 'none'], [alert_type = 'all'], check_stop_ids = True, forced_gtfs_uri = [])
29
29
  ```
30
30
 
31
31
  Transport types:
@@ -145,10 +145,12 @@ Description: # TransportNSWv2
145
145
 
146
146
  Note also that the origin and destination details are 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.
147
147
 
148
- Also note that the 'transport_type' filter, if present, only makes sure that at least **one** leg of the journey includes that transport type unless ```strict_transport_type``` is True, in which case the **first** leg must be of the requested type to be returned.
148
+ Also note that the ```transport_type``` filter, if present, only makes sure that at least **one** leg of the journey includes that transport type unless ```strict_transport_type``` is True, in which case the **first** leg must be of the requested type to be returned.
149
149
 
150
150
  ### Rate limits ###
151
- By default the TransportNSW API allows each API key to make 60,000 calls in a day and up to 5 calls per second. From version 0.8.7 I found a TransportNSW-maintained CSV that contains mappings of bus agency IDs to URLs so I'm using that, plus adding in a 0.75 second delay between API calls.
151
+ By default the TransportNSW API allows each API key to make 60,000 calls in a day and up to 5 calls per second. When requesting real-time location information some services required me to brute-force up to 12 (!) URIs until I found the right one which sometimes resulted in an API rate limt breach. From version 0.8.7 I found a TransportNSW-maintained CSV that contains mappings of bus agency IDs to URIs so I'm using that, plus adding in a 0.75 second delay between API calls. Alternatively, if you're confident that the origin and destination IDs are correct you can reduce your API calls by adding ```check_trip_ids = False``` in the parameters. Additionally there's a final option ```forced_gtfs_uri``` which, if you're super-confident you know what the GTFS URI is for your particular journey, will again reduce the API calls per trip query... although I'd use this one with caution! ```forced_gtfs_uri``` needs to be a single-item list, here's an example:
152
+
153
+ ```forced_gtfs_uri = ["/lightrail/innerwest"]```
152
154
 
153
155
  ## Thank you
154
156
  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!
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyTransportNSWv2
3
- Version: 0.8.7
3
+ Version: 0.8.8
4
4
  Summary: Get detailed per-trip transport information from TransportNSW
5
5
  Home-page: https://github.com/andystewart999/TransportNSW
6
6
  Author: andystewart999
@@ -25,7 +25,7 @@ Description: # TransportNSWv2
25
25
 
26
26
  ### Parameters
27
27
  ```python
28
- .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0], [strict_transport_type = True|False], [raw_output = True|False], [journeys_to_return = 1], [route_filter = ''], [include_realtime_location = True], [include_alerts = 'none'], [alert_type = 'all'] )
28
+ .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0], [strict_transport_type = True|False], [raw_output = True|False], [journeys_to_return = 1], [route_filter = ''], [include_realtime_location = True], [include_alerts = 'none'], [alert_type = 'all'], check_stop_ids = True, forced_gtfs_uri = [])
29
29
  ```
30
30
 
31
31
  Transport types:
@@ -145,10 +145,12 @@ Description: # TransportNSWv2
145
145
 
146
146
  Note also that the origin and destination details are 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.
147
147
 
148
- Also note that the 'transport_type' filter, if present, only makes sure that at least **one** leg of the journey includes that transport type unless ```strict_transport_type``` is True, in which case the **first** leg must be of the requested type to be returned.
148
+ Also note that the ```transport_type``` filter, if present, only makes sure that at least **one** leg of the journey includes that transport type unless ```strict_transport_type``` is True, in which case the **first** leg must be of the requested type to be returned.
149
149
 
150
150
  ### Rate limits ###
151
- By default the TransportNSW API allows each API key to make 60,000 calls in a day and up to 5 calls per second. From version 0.8.7 I found a TransportNSW-maintained CSV that contains mappings of bus agency IDs to URLs so I'm using that, plus adding in a 0.75 second delay between API calls.
151
+ By default the TransportNSW API allows each API key to make 60,000 calls in a day and up to 5 calls per second. When requesting real-time location information some services required me to brute-force up to 12 (!) URIs until I found the right one which sometimes resulted in an API rate limt breach. From version 0.8.7 I found a TransportNSW-maintained CSV that contains mappings of bus agency IDs to URIs so I'm using that, plus adding in a 0.75 second delay between API calls. Alternatively, if you're confident that the origin and destination IDs are correct you can reduce your API calls by adding ```check_trip_ids = False``` in the parameters. Additionally there's a final option ```forced_gtfs_uri``` which, if you're super-confident you know what the GTFS URI is for your particular journey, will again reduce the API calls per trip query... although I'd use this one with caution! ```forced_gtfs_uri``` needs to be a single-item list, here's an example:
152
+
153
+ ```forced_gtfs_uri = ["/lightrail/innerwest"]```
152
154
 
153
155
  ## Thank you
154
156
  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!
@@ -17,7 +17,7 @@ The source API details can be found here: https://opendata.transport.nsw.gov.au/
17
17
 
18
18
  ### Parameters
19
19
  ```python
20
- .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0], [strict_transport_type = True|False], [raw_output = True|False], [journeys_to_return = 1], [route_filter = ''], [include_realtime_location = True], [include_alerts = 'none'], [alert_type = 'all'] )
20
+ .get_trip(origin_stop_id, destination_stop_id, api_key, [trip_wait_time = 0], [transport_type = 0], [strict_transport_type = True|False], [raw_output = True|False], [journeys_to_return = 1], [route_filter = ''], [include_realtime_location = True], [include_alerts = 'none'], [alert_type = 'all'], check_stop_ids = True, forced_gtfs_uri = [])
21
21
  ```
22
22
 
23
23
  Transport types:
@@ -137,10 +137,12 @@ Requesting multiple journeys to be returned doesn't always return that exact num
137
137
 
138
138
  Note also that the origin and destination details are 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.
139
139
 
140
- Also note that the 'transport_type' filter, if present, only makes sure that at least **one** leg of the journey includes that transport type unless ```strict_transport_type``` is True, in which case the **first** leg must be of the requested type to be returned.
140
+ Also note that the ```transport_type``` filter, if present, only makes sure that at least **one** leg of the journey includes that transport type unless ```strict_transport_type``` is True, in which case the **first** leg must be of the requested type to be returned.
141
141
 
142
142
  ### Rate limits ###
143
- By default the TransportNSW API allows each API key to make 60,000 calls in a day and up to 5 calls per second. From version 0.8.7 I found a TransportNSW-maintained CSV that contains mappings of bus agency IDs to URLs so I'm using that, plus adding in a 0.75 second delay between API calls.
143
+ By default the TransportNSW API allows each API key to make 60,000 calls in a day and up to 5 calls per second. When requesting real-time location information some services required me to brute-force up to 12 (!) URIs until I found the right one which sometimes resulted in an API rate limt breach. From version 0.8.7 I found a TransportNSW-maintained CSV that contains mappings of bus agency IDs to URIs so I'm using that, plus adding in a 0.75 second delay between API calls. Alternatively, if you're confident that the origin and destination IDs are correct you can reduce your API calls by adding ```check_trip_ids = False``` in the parameters. Additionally there's a final option ```forced_gtfs_uri``` which, if you're super-confident you know what the GTFS URI is for your particular journey, will again reduce the API calls per trip query... although I'd use this one with caution! ```forced_gtfs_uri``` needs to be a single-item list, here's an example:
144
+
145
+ ```forced_gtfs_uri = ["/lightrail/innerwest"]```
144
146
 
145
147
  ## Thank you
146
148
  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!
@@ -10,7 +10,6 @@ import logging
10
10
  import re
11
11
  import json #For the output
12
12
  import time
13
- #from ratelimit import limits, sleep_and_retry # API rate limiting
14
13
 
15
14
  ATTR_DUE_IN = 'due'
16
15
 
@@ -78,8 +77,9 @@ class TransportNSWv2(object):
78
77
  }
79
78
 
80
79
  def get_trip(self, name_origin, name_destination , api_key, journey_wait_time = 0, transport_type = 0, \
81
- strict_transport_type = False, raw_output = False, journeys_to_return = 1, route_filter = '', include_realtime_location = True, include_alerts = 'none', alert_type = 'all'):
82
- # or 'lineInfo|stopInfo' etc as a list
80
+ strict_transport_type = False, raw_output = False, journeys_to_return = 1, route_filter = '', \
81
+ include_realtime_location = True, include_alerts = 'none', alert_type = 'all', check_stop_ids = True, forced_gtfs_uri = []):
82
+
83
83
  """Get the latest data from Transport NSW."""
84
84
  fmt = '%Y-%m-%dT%H:%M:%SZ'
85
85
 
@@ -101,44 +101,51 @@ class TransportNSWv2(object):
101
101
  itdDate = now_plus_wait.strftime('%Y%m%d')
102
102
  itdTime = now_plus_wait.strftime('%H%M')
103
103
 
104
- # First, check if the source and dest stops are valid
105
- stop_list = [name_origin, name_destination]
106
104
  auth = 'apikey ' + self.api_key
107
105
  header = {'Accept': 'application/json', 'Authorization': auth}
108
106
 
109
- for stop in stop_list:
110
- url = \
111
- 'https://api.transport.nsw.gov.au/v1/tp/stop_finder?' \
112
- 'outputFormat=rapidJSON&coordOutputFormat=EPSG%3A4326' \
113
- '&type_sf=stop&name_sf=' + stop + \
114
- '&TfNSWSF=true'
115
-
116
- # Send the query and return an error if something goes wrong
117
- try:
118
- response = requests.get(url, headers=header, timeout=5)
119
- except Exception as ex:
120
- logger.error("Network or Timeout error when calling /v1/tp/stop_finder API")
121
- return None
122
-
123
- # If we get bad status code, log error and return with None
124
- if response.status_code != 200:
125
- if response.status_code == 429:
126
- logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
127
- else:
128
- logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check api key")
129
-
130
- return None
131
-
132
- # Parse the result as a JSON object
133
- result = response.json()
134
-
135
- # Just a quick check - the presence of systemMessages signifies an error
136
- if 'systemMessages' in result:
137
- logger.error("Stop ID " + stop + " does not exist")
138
107
 
139
- # Put in a pause here to try and make sure we stay under the 5 API calls/second limit
140
- # Not usually an issue but if multiple processes are running multiple calls we might hit it
141
- time.sleep(1.0)
108
+ # First, check if the source and dest stops are valid unless we've been told not to
109
+ if check_stop_ids:
110
+ stop_list = [name_origin, name_destination]
111
+
112
+ for stop in stop_list:
113
+ url = \
114
+ 'https://api.transport.nsw.gov.au/v1/tp/stop_finder?' \
115
+ 'outputFormat=rapidJSON&coordOutputFormat=EPSG%3A4326' \
116
+ '&type_sf=stop&name_sf=' + stop + \
117
+ '&TfNSWSF=true'
118
+
119
+ # Send the query and return an error if something goes wrong
120
+ try:
121
+ response = requests.get(url, headers=header, timeout=5)
122
+
123
+ # If we get bad status code, log error and return with None
124
+ if response.status_code != 200:
125
+ if response.status_code == 429:
126
+ # We've exceeded the rate limit but that doesn't mean future calls won't work
127
+ # So let's assume that the stop ID is ok but we'll still raise a warning
128
+ logger.warn(f"Error {str(response.status_code)} calling /v1/tp/stop_finder API; rate limit exceeded - assuming stop is valid")
129
+ else:
130
+ # If it's an API key issue there's no point in continuing, hence returning None
131
+ logger.error(f"Error {str(response.status_code)} calling /v1/tp/stop_finder API; check API key")
132
+ return None
133
+ else:
134
+ # Parse the result as a JSON object
135
+ result = response.json()
136
+
137
+ # Just a quick check - the presence of systemMessages signifies an error, otherwise we assume it's ok
138
+ if 'systemMessages' in result:
139
+ logger.error(f"Error - Stop ID {stop} doesn't exist")
140
+ return None
141
+
142
+ # Put in a pause here to try and make sure we stay under the 5 API calls/second limit
143
+ # Not usually an issue but if multiple processes are running multiple calls we might hit it
144
+ time.sleep(1.0)
145
+
146
+ except Exception as ex:
147
+ logger.error(f"Error {str(ex)} calling /v1/tp/stop_finder API; assuming stop is valid")
148
+ return None
142
149
 
143
150
  # We don't control how many journeys are returned any more, so need to be careful of running out of valid journeys if there is a filter in place, particularly a strict filter
144
151
  # It would be more efficient to return one journey, check if the filter is met and then retrieve the next one via a new query if not, but for now we'll only be making use of the journeys we've been given
@@ -153,28 +160,27 @@ class TransportNSWv2(object):
153
160
  '&TfNSWTR=true'
154
161
  # '&calcNumberOfTrips=' + str(journeys_to_retrieve) + \
155
162
 
156
-
157
163
  # Send the query and return an error if something goes wrong
158
- # Otherwise store the response
164
+ # Otherwise store the response for the next steps
159
165
  try:
160
166
  response = requests.get(url, headers=header, timeout=10)
161
- except:
162
- logger.error("Network or timeout error")
167
+ except Exception as ex:
168
+ logger.error(f"Error {str(ex)} calling /v1/tp/trip API")
163
169
  return None
164
170
 
165
171
  # If we get bad status code, log error and return with n/a or an empty string
166
172
  if response.status_code != 200:
167
173
  if response.status_code == 429:
168
- logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
174
+ logger.error(f"Error {str(response.status_code)} calling /v1/tp/trip API; rate limit exceeded")
169
175
  else:
170
- logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check api key")
176
+ logger.error(f"Error {str(response.status_code)} calling /v1/tp/trip API; check API key")
171
177
 
172
178
  return None
173
179
 
174
180
  # Parse the result as a JSON object
175
181
  result = response.json()
176
182
 
177
- # The API will always return a valid trip, so it's just a case of grabbing what we need...
183
+ # The API will always return a valid trip, so it's just a case of grabbing the metadata that we need...
178
184
  # We're only reporting on the origin and destination, it's out of scope to discuss the specifics of the ENTIRE journey
179
185
  # This isn't a route planner, just a 'how long until the next journey I've specified' tool
180
186
  # The assumption is that the travelee will know HOW to make the defined journey, they're just asking WHEN it's happening next
@@ -183,9 +189,16 @@ class TransportNSWv2(object):
183
189
  if raw_output == True:
184
190
  # Just return the raw output
185
191
  return json.dumps(result)
186
- exit
187
192
 
188
- retrieved_journeys = len(result['journeys'])
193
+ # Make sure we've got at least one journey
194
+ try:
195
+ retrieved_journeys = len(result['journeys'])
196
+
197
+ except:
198
+ # Looks like an empty response
199
+ logger.error(f"Error {(str(err))} calling /v1/tp/trip API")
200
+
201
+ return None
189
202
 
190
203
  # Loop through the results applying filters where required, and generate the appropriate JSON output including an array of in-scope trips
191
204
  json_output=''
@@ -230,18 +243,16 @@ class TransportNSWv2(object):
230
243
  destination_arrival_time = destination['arrivalTimeEstimated']
231
244
 
232
245
  # Origin type info - train, bus, etc
233
- #origin_mode_temp = transportation['product']['class']
234
246
  origin_mode = self.get_mode(transportation['product']['class'])
235
247
  origin_mode_name = transportation['product']['name']
236
248
 
237
249
  # RealTimeTripID info so we can try and get the current location later
238
250
  realtimetripid = 'n/a'
239
- if 'properties' in transportation:
240
- if 'RealtimeTripId' in transportation['properties']:
241
- realtimetripid = transportation['properties']['RealtimeTripId']
251
+ if 'properties' in transportation and 'RealtimeTripId' in transportation['properties']:
252
+ realtimetripid = transportation['properties']['RealtimeTripId']
242
253
 
243
- # We're also going to need the agency_id
244
- agencyid = transportation['operator']['id']
254
+ # We're also going to need the agency_id if it's a bus journey
255
+ agencyid = transportation['operator']['id']
245
256
 
246
257
  # Line info
247
258
  origin_line_name_short = "unknown"
@@ -254,12 +265,11 @@ class TransportNSWv2(object):
254
265
 
255
266
  # Occupancy info, if it's there
256
267
  occupancy = 'unknown'
257
- if 'properties' in first_stop:
258
- if 'occupancy' in first_stop['properties']:
259
- occupancy = first_stop['properties']['occupancy']
268
+ if 'properties' in first_stop and 'occupancy' in first_stop['properties']:
269
+ occupancy = first_stop['properties']['occupancy']
260
270
 
261
271
  alerts = "[]"
262
- if (include_alerts != 'none'):
272
+ if include_alerts != 'none':
263
273
  # We'll be adding these to the returned JSON string as an array
264
274
  # Only include alerts of the specified priority or greater, and of the specified type
265
275
  alerts = self.find_alerts(legs, self.include_alerts, self.alert_type)
@@ -267,21 +277,27 @@ class TransportNSWv2(object):
267
277
  latitude = 'n/a'
268
278
  longitude = 'n/a'
269
279
 
270
- if ((realtimetripid != 'n/a') and (self.include_realtime_location) == True):
280
+ if self.include_realtime_location and realtimetripid != 'n/a':
271
281
  # See if we can get the latitute and longitude via the Realtime Vehicle Positions API
272
282
  # Build the URL(s) - some modes have multiple GTFS sources, unforunately
283
+ # Some travel modes require brute-forcing the API call a few times, so if we're sure of the URI,
284
+ # ie it's been determined elsewhere then it can be forced
273
285
 
274
- url_base_path = self.get_base_url(origin_mode)
275
- url_mode_list = self.get_mode_list(origin_mode, agencyid)
276
286
  bFoundTripID = False
287
+ url_base_path = self.get_base_url(origin_mode)
277
288
 
278
- #auth = 'apikey ' + self.api_key
279
- #header = {'Authorization': auth}
289
+ # Check for a forced URI
290
+ if not forced_gtfs_uri:
291
+ url_mode_list = self.get_mode_list(origin_mode, agencyid)
292
+ else:
293
+ # We've been forced to use a specific URI!
294
+ url_mode_list = forced_gtfs_uri
280
295
 
281
296
  if not url_mode_list is None:
282
297
  for mode_url in url_mode_list:
283
298
  url = url_base_path + mode_url
284
299
  response = requests.get(url, headers=header, timeout=10)
300
+
285
301
  # Only try and process the results if we got a good return code
286
302
  if response.status_code == 200:
287
303
  # Search the feed and see if we can match realtimetripid to trip_id
@@ -298,6 +314,12 @@ class TransportNSWv2(object):
298
314
  # We found it, so flag it and break out
299
315
  bFoundTripID = True
300
316
  break
317
+ else:
318
+ # Warn that we didn't get a good return
319
+ if response.status_code == 429:
320
+ logger.error(f"Error {str(response.status_code)} calling {url} API; rate limit exceeded")
321
+ else:
322
+ logger.error(f"Error {str(response.status_code)} calling {url} API; check API key")
301
323
 
302
324
  if bFoundTripID == True:
303
325
  # No need to look any further
@@ -348,18 +370,6 @@ class TransportNSWv2(object):
348
370
  return json_output
349
371
 
350
372
 
351
- # def find_first_journey(self, journeys, journeytype, strictness):
352
- # # Find the first journey that has a leg is of the requested type
353
- # journey_count = len(journeys)
354
- # for journey in range (0, journey_count, 1):
355
- # leg = self.find_first_leg(journeys[journey]['legs'], journeytype, strictness)
356
- # if leg is not None:
357
- # return journeys[journey]
358
- #
359
- # # Hmm, we didn't find one
360
- # return None
361
-
362
-
363
373
  def find_next_journey(self, journeys, start_journey_index, journeytype, strict, route_filter):
364
374
  # Fnd the next journey that has a leg of the requested type, and/or that satisfies the route filter
365
375
  journey_count = len(journeys)
@@ -387,10 +397,9 @@ class TransportNSWv2(object):
387
397
  origin_line_name_short = 'n/a'
388
398
  origin_line_name = 'n/a'
389
399
 
390
- if 'transportation' in legs[leg_index]:
391
- if 'disassembledName' in legs[leg_index]['transportation']:
392
- origin_line_name_short = legs[leg_index]['transportation']['disassembledName'].lower()
393
- origin_line_name = legs[leg_index]['transportation']['number'].lower()
400
+ if 'transportation' in legs[leg_index] and 'disassembledName' in legs[leg_index]['transportation']:
401
+ origin_line_name_short = legs[leg_index]['transportation']['disassembledName'].lower()
402
+ origin_line_name = legs[leg_index]['transportation']['number'].lower()
394
403
 
395
404
  if (route_filter in origin_line_name_short or route_filter in origin_line_name):
396
405
  leg_class = legs[leg_index]['transportation']['product']['class']
@@ -489,10 +498,11 @@ class TransportNSWv2(object):
489
498
  100 : "Walk",
490
499
  107 : "Cycle"
491
500
  }
501
+
492
502
  return modes.get(iconId, None)
493
503
 
494
504
  def get_base_url(self, mode):
495
- """Map the journey mode to the proper base real time location URL """
505
+ # Map the journey mode to the proper base real time location URL
496
506
  v1_url = "https://api.transport.nsw.gov.au/v1/gtfs/vehiclepos"
497
507
  v2_url = "https://api.transport.nsw.gov.au/v2/gtfs/vehiclepos"
498
508
 
@@ -505,11 +515,12 @@ class TransportNSWv2(object):
505
515
  "Ferry" : v1_url,
506
516
  "School bus" : v1_url
507
517
  }
518
+
508
519
  return url_options.get(mode, None)
509
520
 
510
521
 
511
522
  def get_alert_priority(self, alert_priority):
512
- """Map the alert priority to a number so we can filter later """
523
+ # Map the alert priority to a number so we can filter later
513
524
 
514
525
  alert_priorities = {
515
526
  "all" : 0,
@@ -519,6 +530,7 @@ class TransportNSWv2(object):
519
530
  "high" : 4,
520
531
  "veryHigh" : 5
521
532
  }
533
+
522
534
  return alert_priorities.get(alert_priority, 99)
523
535
 
524
536
 
@@ -529,7 +541,8 @@ class TransportNSWv2(object):
529
541
  """
530
542
 
531
543
  if mode in ["Bus", "Coach", "School bus"]:
532
- # Use the API to determine the appropriate URL
544
+ # Use this CSV to determine the appropriate real-time location URL
545
+ # I'm hoping that this CSV resource URL is static when updated by TransportNSW!
533
546
  url = "https://opendata.transport.nsw.gov.au/data/api/action/datastore_search?resource_id=30b850b7-f439-4e30-8072-e07ef62a2a36&filters={%22For%20Realtime%20GTFS%20agency_id%22:%22" + agencyid + "%22}&limit=1"
534
547
 
535
548
  # Send the query and return an error if something goes wrong
@@ -544,7 +557,7 @@ class TransportNSWv2(object):
544
557
  if response.status_code == 429:
545
558
  logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
546
559
  else:
547
- logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check api key")
560
+ logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check API key")
548
561
 
549
562
  return None
550
563
 
@@ -555,6 +568,7 @@ class TransportNSWv2(object):
555
568
  else:
556
569
  return None
557
570
 
571
+ # Even though there's only one URL we need to return as a list as Light Rail still has multiple URLs that need to be brute-forced, unfortunately
558
572
  bus_list = ["/" + mode_path]
559
573
  return bus_list
560
574
  else:
@@ -569,7 +583,7 @@ class TransportNSWv2(object):
569
583
 
570
584
 
571
585
  def get_due(self, estimated):
572
- """Min until departure"""
586
+ # Minutes until departure
573
587
  due = 0
574
588
  if estimated > datetime.utcnow():
575
589
  due = round((estimated - datetime.utcnow()).seconds / 60)
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="PyTransportNSWv2",
8
- version="0.8.7",
8
+ version="0.8.8",
9
9
  author="andystewart999",
10
10
  author_email="andy.stewart@live.com",
11
11
  description="Get detailed per-trip transport information from TransportNSW",