PyTransportNSWv2 0.8.7__tar.gz → 0.8.9__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.
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PKG-INFO +7 -4
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PyTransportNSWv2.egg-info/PKG-INFO +7 -4
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/README.md +6 -3
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/TransportNSWv2/TransportNSWv2.py +98 -85
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/setup.py +1 -1
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/LICENSE +0 -0
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PyTransportNSWv2.egg-info/SOURCES.txt +0 -0
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PyTransportNSWv2.egg-info/dependency_links.txt +0 -0
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PyTransportNSWv2.egg-info/requires.txt +0 -0
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PyTransportNSWv2.egg-info/top_level.txt +0 -0
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/TransportNSWv2/__init__.py +0 -0
- {PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: PyTransportNSWv2
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.9
|
4
4
|
Summary: Get detailed per-trip transport information from TransportNSW
|
5
5
|
Home-page: https://github.com/andystewart999/TransportNSW
|
6
6
|
Author: andystewart999
|
@@ -24,8 +24,9 @@ Description: # TransportNSWv2
|
|
24
24
|
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
|
25
25
|
|
26
26
|
### Parameters
|
27
|
+
Only the first three paramters are mandatory, the rest are optional. All parameters and their defaults are as follows:
|
27
28
|
```python
|
28
|
-
.get_trip(origin_stop_id, destination_stop_id, api_key,
|
29
|
+
.get_trip(origin_stop_id, destination_stop_id, api_key, trip_wait_time = 0, transport_type = 0, strict_transport_type = False, raw_output = 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
30
|
```
|
30
31
|
|
31
32
|
Transport types:
|
@@ -145,10 +146,12 @@ Description: # TransportNSWv2
|
|
145
146
|
|
146
147
|
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
148
|
|
148
|
-
Also note that the
|
149
|
+
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
150
|
|
150
151
|
### 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
|
152
|
+
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:
|
153
|
+
|
154
|
+
```forced_gtfs_uri = ["/lightrail/innerwest"]```
|
152
155
|
|
153
156
|
## Thank you
|
154
157
|
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.
|
3
|
+
Version: 0.8.9
|
4
4
|
Summary: Get detailed per-trip transport information from TransportNSW
|
5
5
|
Home-page: https://github.com/andystewart999/TransportNSW
|
6
6
|
Author: andystewart999
|
@@ -24,8 +24,9 @@ Description: # TransportNSWv2
|
|
24
24
|
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
|
25
25
|
|
26
26
|
### Parameters
|
27
|
+
Only the first three paramters are mandatory, the rest are optional. All parameters and their defaults are as follows:
|
27
28
|
```python
|
28
|
-
.get_trip(origin_stop_id, destination_stop_id, api_key,
|
29
|
+
.get_trip(origin_stop_id, destination_stop_id, api_key, trip_wait_time = 0, transport_type = 0, strict_transport_type = False, raw_output = 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
30
|
```
|
30
31
|
|
31
32
|
Transport types:
|
@@ -145,10 +146,12 @@ Description: # TransportNSWv2
|
|
145
146
|
|
146
147
|
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
148
|
|
148
|
-
Also note that the
|
149
|
+
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
150
|
|
150
151
|
### 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
|
152
|
+
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:
|
153
|
+
|
154
|
+
```forced_gtfs_uri = ["/lightrail/innerwest"]```
|
152
155
|
|
153
156
|
## Thank you
|
154
157
|
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!
|
@@ -16,8 +16,9 @@ If it's available, the general occupancy level and the latitude and longitude of
|
|
16
16
|
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
|
17
17
|
|
18
18
|
### Parameters
|
19
|
+
Only the first three paramters are mandatory, the rest are optional. All parameters and their defaults are as follows:
|
19
20
|
```python
|
20
|
-
.get_trip(origin_stop_id, destination_stop_id, api_key,
|
21
|
+
.get_trip(origin_stop_id, destination_stop_id, api_key, trip_wait_time = 0, transport_type = 0, strict_transport_type = False, raw_output = 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
22
|
```
|
22
23
|
|
23
24
|
Transport types:
|
@@ -137,10 +138,12 @@ Requesting multiple journeys to be returned doesn't always return that exact num
|
|
137
138
|
|
138
139
|
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
140
|
|
140
|
-
Also note that the
|
141
|
+
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
142
|
|
142
143
|
### 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
|
144
|
+
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:
|
145
|
+
|
146
|
+
```forced_gtfs_uri = ["/lightrail/innerwest"]```
|
144
147
|
|
145
148
|
## Thank you
|
146
149
|
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 = '',
|
82
|
-
|
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
|
-
|
140
|
-
|
141
|
-
|
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("
|
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
|
174
|
+
logger.error(f"Error {str(response.status_code)} calling /v1/tp/trip API; rate limit exceeded")
|
169
175
|
else:
|
170
|
-
logger.error("Error
|
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
|
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
|
-
|
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
|
-
|
241
|
-
realtimetripid = transportation['properties']['RealtimeTripId']
|
251
|
+
if 'properties' in transportation and 'RealtimeTripId' in transportation['properties']:
|
252
|
+
realtimetripid = transportation['properties']['RealtimeTripId']
|
242
253
|
|
243
|
-
|
244
|
-
|
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
|
-
|
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
|
272
|
+
if self.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
|
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
|
-
#
|
279
|
-
|
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
|
-
|
392
|
-
|
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
|
-
|
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,21 +515,22 @@ 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
|
-
|
523
|
+
# Map the alert priority to a number so we can filter later
|
513
524
|
|
514
525
|
alert_priorities = {
|
515
526
|
"all" : 0,
|
516
|
-
"
|
527
|
+
"verylow" : 1,
|
517
528
|
"low" : 2,
|
518
529
|
"normal" : 3,
|
519
530
|
"high" : 4,
|
520
|
-
"
|
531
|
+
"veryhigh" : 5
|
521
532
|
}
|
522
|
-
return alert_priorities.get(alert_priority,
|
533
|
+
return alert_priorities.get(alert_priority.lower(), 4)
|
523
534
|
|
524
535
|
|
525
536
|
def get_mode_list(self, mode, agencyid):
|
@@ -529,7 +540,8 @@ class TransportNSWv2(object):
|
|
529
540
|
"""
|
530
541
|
|
531
542
|
if mode in ["Bus", "Coach", "School bus"]:
|
532
|
-
# Use
|
543
|
+
# Use this CSV to determine the appropriate real-time location URL
|
544
|
+
# I'm hoping that this CSV resource URL is static when updated by TransportNSW!
|
533
545
|
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
546
|
|
535
547
|
# Send the query and return an error if something goes wrong
|
@@ -544,7 +556,7 @@ class TransportNSWv2(object):
|
|
544
556
|
if response.status_code == 429:
|
545
557
|
logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; rate limit exceeded")
|
546
558
|
else:
|
547
|
-
logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check
|
559
|
+
logger.error("Error " + str(response.status_code) + " calling /v1/tp/stop_finder API; check API key")
|
548
560
|
|
549
561
|
return None
|
550
562
|
|
@@ -555,6 +567,7 @@ class TransportNSWv2(object):
|
|
555
567
|
else:
|
556
568
|
return None
|
557
569
|
|
570
|
+
# 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
571
|
bus_list = ["/" + mode_path]
|
559
572
|
return bus_list
|
560
573
|
else:
|
@@ -569,7 +582,7 @@ class TransportNSWv2(object):
|
|
569
582
|
|
570
583
|
|
571
584
|
def get_due(self, estimated):
|
572
|
-
|
585
|
+
# Minutes until departure
|
573
586
|
due = 0
|
574
587
|
if estimated > datetime.utcnow():
|
575
588
|
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.
|
8
|
+
version="0.8.9",
|
9
9
|
author="andystewart999",
|
10
10
|
author_email="andy.stewart@live.com",
|
11
11
|
description="Get detailed per-trip transport information from TransportNSW",
|
File without changes
|
File without changes
|
{PyTransportNSWv2-0.8.7 → PyTransportNSWv2-0.8.9}/PyTransportNSWv2.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|