PyTransportNSWv2 0.8.4__tar.gz → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyTransportNSWv2
3
- Version: 0.8.4
3
+ Version: 0.8.6
4
4
  Summary: Get detailed per-trip transport information from TransportNSW
5
5
  Home-page: https://github.com/andystewart999/TransportNSW
6
6
  Author: andystewart999
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyTransportNSWv2
3
- Version: 0.8.4
3
+ Version: 0.8.6
4
4
  Summary: Get detailed per-trip transport information from TransportNSW
5
5
  Home-page: https://github.com/andystewart999/TransportNSW
6
6
  Author: andystewart999
@@ -8,8 +8,9 @@ import requests.exceptions
8
8
  import requests
9
9
  import logging
10
10
  import re
11
- import json #For the output
11
+ import json #For the output
12
12
  import time
13
+ #from ratelimit import limits, sleep_and_retry # API rate limiting
13
14
 
14
15
  ATTR_DUE_IN = 'due'
15
16
 
@@ -115,11 +116,11 @@ class TransportNSWv2(object):
115
116
  # Send the query and return an error if something goes wrong
116
117
  try:
117
118
  response = requests.get(url, headers=header, timeout=20)
118
- except:
119
+ except Exception as ex:
119
120
  logger.error("Network or Timeout error when calling /v1/tp/stop_finder API")
120
121
  return None
121
122
 
122
- # If we get bad status code, log error and return with n/a or an empty string
123
+ # If we get bad status code, log error and return with None
123
124
  if response.status_code != 200:
124
125
  if response.status_code == 429:
125
126
  logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
@@ -135,9 +136,9 @@ class TransportNSWv2(object):
135
136
  if 'systemMessages' in result:
136
137
  logger.error("Stop ID " + stop + " does not exist")
137
138
 
138
- # Put in a quick pause here to try and make sure we stay under the 5 minute calls/second limit
139
+ # Put in a pause here to try and make sure we stay under the 5 API calls/second limit
139
140
  # Not usually an issue but if multiple processes are running multiple calls we might hit it
140
- time.sleep(0.5)
141
+ time.sleep(1.0)
141
142
 
142
143
  # 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
143
144
  # 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
@@ -158,13 +159,17 @@ class TransportNSWv2(object):
158
159
  try:
159
160
  response = requests.get(url, headers=header, timeout=20)
160
161
  except:
161
- logger.error("Network or Timeout error")
162
+ logger.error("Network or timeout error")
162
163
  return None
163
164
 
164
165
  # If we get bad status code, log error and return with n/a or an empty string
165
166
  if response.status_code != 200:
166
- logger.error("Error " + str(response.status_code) + " calling /v1/tp/trip API; check api key")
167
- return None
167
+ if response.status_code == 429:
168
+ logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
169
+ else:
170
+ logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check api key")
171
+
172
+ return None
168
173
 
169
174
  # Parse the result as a JSON object
170
175
  result = response.json()
@@ -235,6 +240,9 @@ class TransportNSWv2(object):
235
240
  if 'RealtimeTripId' in transportation['properties']:
236
241
  realtimetripid = transportation['properties']['RealtimeTripId']
237
242
 
243
+ # We're also going to need the agency_id
244
+ agencyid = transportation['operator']['id']
245
+
238
246
  # Line info
239
247
  origin_line_name_short = "unknown"
240
248
  if 'disassembledName' in transportation:
@@ -262,46 +270,42 @@ class TransportNSWv2(object):
262
270
  if ((realtimetripid != 'n/a') and (self.include_realtime_location) == True):
263
271
  # See if we can get the latitute and longitude via the Realtime Vehicle Positions API
264
272
  # Build the URL(s) - some modes have multiple GTFS sources, unforunately
265
- url_path = self.get_url(origin_mode)
266
- url_list = self.get_url_path(origin_mode)
267
- bFoundTripID = False
268
-
269
- for mode_url in url_list:
270
- url = url_path + mode_url
271
- auth = 'apikey ' + self.api_key
272
- header = {'Authorization': auth}
273
-
274
- response = requests.get(url, headers=header, timeout=10)
275
-
276
- # Only try and process the results if we got a good return code
277
- if response.status_code == 200:
278
- # Search the feed and see if we can match realtimetripid to trip_id
279
- # If we do, capture the latitude and longitude
280
- feed = gtfs_realtime_pb2.FeedMessage()
281
- feed.ParseFromString(response.content)
282
-
283
- reg = re.compile(realtimetripid)
284
-
285
- for entity in feed.entity:
286
- if bool(re.match(reg, entity.vehicle.trip.trip_id)):
287
- latitude = entity.vehicle.position.latitude
288
- longitude = entity.vehicle.position.longitude
289
-
290
- # We found it, so flag it and break out
291
- bFoundTripID = True
292
- break
293
-
294
- if bFoundTripID == True:
295
- # No need to look any further
296
- break
297
-
298
-
299
- # Put in a quick pause here to try and make sure we stay under the 5 minute calls/second limit
300
- # Not usually an issue but if multiple processes are running multiple calls we might hit it
301
- time.sleep(0.5)
302
-
303
273
 
274
+ url_base_path = self.get_base_url(origin_mode)
275
+ url_mode_list = self.get_mode_list(origin_mode, agencyid)
276
+ bFoundTripID = False
304
277
 
278
+ #auth = 'apikey ' + self.api_key
279
+ #header = {'Authorization': auth}
280
+
281
+ if not url_mode_list is None:
282
+ for mode_url in url_mode_list:
283
+ url = url_base_path + mode_url
284
+ response = requests.get(url, headers=header, timeout=10)
285
+ # Only try and process the results if we got a good return code
286
+ if response.status_code == 200:
287
+ # Search the feed and see if we can match realtimetripid to trip_id
288
+ # If we do, capture the latitude and longitude
289
+ feed = gtfs_realtime_pb2.FeedMessage()
290
+ feed.ParseFromString(response.content)
291
+ reg = re.compile(realtimetripid)
292
+
293
+ for entity in feed.entity:
294
+ if bool(re.match(reg, entity.vehicle.trip.trip_id)):
295
+ latitude = entity.vehicle.position.latitude
296
+ longitude = entity.vehicle.position.longitude
297
+
298
+ # We found it, so flag it and break out
299
+ bFoundTripID = True
300
+ break
301
+
302
+ if bFoundTripID == True:
303
+ # No need to look any further
304
+ break
305
+
306
+ # Put in a quick pause here to try and make sure we stay under the 5 API calls/second limit
307
+ # Not usually an issue but if multiple processes are running multiple calls we might hit it
308
+ time.sleep(0.75)
305
309
 
306
310
  self.info = {
307
311
  ATTR_DUE_IN: due,
@@ -487,7 +491,7 @@ class TransportNSWv2(object):
487
491
  }
488
492
  return modes.get(iconId, None)
489
493
 
490
- def get_url(self, mode):
494
+ def get_base_url(self, mode):
491
495
  """Map the journey mode to the proper base real time location URL """
492
496
  v1_url = "https://api.transport.nsw.gov.au/v1/gtfs/vehiclepos"
493
497
  v2_url = "https://api.transport.nsw.gov.au/v2/gtfs/vehiclepos"
@@ -518,10 +522,39 @@ class TransportNSWv2(object):
518
522
  return alert_priorities.get(alert_priority, 99)
519
523
 
520
524
 
521
- def get_url_path(self, mode):
522
- """Map the journey mode to the proper modifier URL """
523
- # There's probably a better way of mapping the bus route/stop/line number to the correct URL to call in the GTFS API, but as of now I don't know it... hence the brute force approach, although I hate the lack of elegance
524
- bus_list = ["/buses", "/regionbuses/centralwestandorana", "/regionbuses/centralwestandorana2", "/regionbuses/newenglandnorthwest", "/regionbuses/northcoast", "/regionbuses/northcoast2", "/regionbuses/northcoast3", "/regionbuses/riverinamurray", "/regionbuses/riverinamurray2", "/regionbuses/southeasttablelands", "/regionbuses/southeasttablelands2", "/regionbuses/sydneysurrounds", "/regionbuses/newcastlehunter", "/regionbuses/farwest"]
525
+ def get_mode_list(self, mode, agencyid):
526
+ """
527
+ Map the journey mode to the proper modifier URL. If the mode is Bus, Coach or School bus then use the agency ID to invoke the GTFS datastore search API
528
+ which will give us the appropriate URL to call later - we still have to do light rail the old-fashioned, brute-force way though
529
+ """
530
+ if mode in ["Bus", "Coach", "School bus"]:
531
+ # Use the API to determine the appropriate URL
532
+ 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"
533
+
534
+ # Send the query and return an error if something goes wrong
535
+ try:
536
+ response = requests.get(url, timeout=20)
537
+ except Exception as ex:
538
+ logger.error("Error " + str(ex) + " querying GTFS URL datastore")
539
+ return None
540
+
541
+ # If we get bad status code, log error and return with None
542
+ if response.status_code != 200:
543
+ if response.status_code == 429:
544
+ logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
545
+ else:
546
+ logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check api key")
547
+
548
+ return None
549
+
550
+ # Parse the result as JSON
551
+ result = response.json()
552
+ if 'records' in result['result'] and len(result['result']['records']) > 0:
553
+ mode_path = result['result']['records'][0]['For Realtime parameter']
554
+ else:
555
+ return None
556
+
557
+ bus_list = ["/" + mode_path]
525
558
 
526
559
  url_options = {
527
560
  "Train" : ["/sydneytrains"],
@@ -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.4",
8
+ version="0.8.6",
9
9
  author="andystewart999",
10
10
  author_email="andy.stewart@live.com",
11
11
  description="Get detailed per-trip transport information from TransportNSW",