octranspo_fetch 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/octranspo_fetch.rb +84 -39
- metadata +2 -2
data/lib/octranspo_fetch.rb
CHANGED
@@ -15,13 +15,15 @@ class OCTranspo
|
|
15
15
|
def initialize(options)
|
16
16
|
@app_id = options[:application_id]
|
17
17
|
@app_key = options[:application_key]
|
18
|
-
@
|
18
|
+
@next_trips_cache = LruRedux::Cache.new(NEXT_TRIPS_CACHE_SIZE)
|
19
19
|
@route_summary_cache = LruRedux::Cache.new(ROUTE_CACHE_SIZE)
|
20
20
|
@api_calls = 0
|
21
|
+
@cache_hits = 0
|
22
|
+
@cache_misses = 0
|
21
23
|
end
|
22
24
|
|
23
25
|
def clear_cache()
|
24
|
-
@
|
26
|
+
@next_trips_cache.clear()
|
25
27
|
@route_summary_cache.clear()
|
26
28
|
end
|
27
29
|
|
@@ -30,6 +32,10 @@ class OCTranspo
|
|
30
32
|
return @api_calls
|
31
33
|
end
|
32
34
|
|
35
|
+
def cache_stats()
|
36
|
+
return {hits: @cache_hits, misses: @cache_misses}
|
37
|
+
end
|
38
|
+
|
33
39
|
# Get a list of routes for a specific stop.
|
34
40
|
#
|
35
41
|
# Returns a {stop, stop_description, routes: [{route, direction_id, direction, heading}]} object.
|
@@ -37,10 +43,19 @@ class OCTranspo
|
|
37
43
|
#
|
38
44
|
# Arguments:
|
39
45
|
# stop: (String) The stop number.
|
46
|
+
# options[:max_cache_time]: (Integer) Maximum cache age, in seconds. If cached data is
|
47
|
+
# available and is newer than this, then the cached value will be returned. Defaults to
|
48
|
+
# one day.
|
40
49
|
#
|
41
|
-
def get_route_summary_for_stop(stop)
|
50
|
+
def get_route_summary_for_stop(stop, options={})
|
51
|
+
max_cache_time = (options[:max_cache_time] or 60*60*24)
|
42
52
|
cached_result = @route_summary_cache[stop]
|
43
|
-
if !cached_result.nil?
|
53
|
+
if !cached_result.nil? and ((cached_result[:time] + max_cache_time) > Time.now.to_i)
|
54
|
+
@cache_hits += 1
|
55
|
+
return cached_result[:route_summary]
|
56
|
+
end
|
57
|
+
|
58
|
+
@cache_misses += 1
|
44
59
|
|
45
60
|
xresult = fetch "GetRouteSummaryForStop", "stopNo=#{stop}"
|
46
61
|
|
@@ -63,7 +78,10 @@ class OCTranspo
|
|
63
78
|
raise "No routes found"
|
64
79
|
end
|
65
80
|
|
66
|
-
@route_summary_cache[stop] =
|
81
|
+
@route_summary_cache[stop] = {
|
82
|
+
route_summary: result,
|
83
|
+
time: Time.now.to_i
|
84
|
+
}
|
67
85
|
|
68
86
|
return result
|
69
87
|
end
|
@@ -74,16 +92,33 @@ class OCTranspo
|
|
74
92
|
# Arguments:
|
75
93
|
# stop: (String) The stop number.
|
76
94
|
# route_no: (String) The route number.
|
95
|
+
# options[:max_cache_time]: (Integer) Maximum cache age, in seconds. If cached data is
|
96
|
+
# available and is newer than this, then the cached value will be returned. Defaults
|
97
|
+
# to five minutes.
|
77
98
|
#
|
78
|
-
def get_next_trips_for_stop(stop, route_no)
|
99
|
+
def get_next_trips_for_stop(stop, route_no, options={})
|
100
|
+
max_cache_time = (options[:max_cache_time] or 60*5)
|
101
|
+
|
102
|
+
# Return result from cache, if available
|
103
|
+
cache_key = "#{stop}-#{route_no}"
|
104
|
+
cached_result = @next_trips_cache[cache_key]
|
105
|
+
if !cached_result.nil? and ((cached_result[:time] + max_cache_time) > Time.now.to_i)
|
106
|
+
@cache_hits += 1
|
107
|
+
return adjust_cached_trip_times(cached_result[:next_trips])
|
108
|
+
end
|
109
|
+
@cache_misses += 1
|
110
|
+
|
79
111
|
xresult = fetch "GetNextTripsForStop", "stopNo=#{stop}&routeNo=#{route_no}"
|
80
112
|
|
81
113
|
result = {
|
82
114
|
stop: get_value(xresult, "t:StopNo"),
|
83
115
|
stop_description: get_value(xresult, "t:StopLabel"),
|
116
|
+
time: Time.now,
|
84
117
|
routes: []
|
85
118
|
}
|
86
119
|
|
120
|
+
found_data = false
|
121
|
+
|
87
122
|
xresult.xpath('t:Route/t:RouteDirection', OCT_NS).each do |route|
|
88
123
|
get_error(route, "Error for route: #{route_no}")
|
89
124
|
|
@@ -109,41 +144,27 @@ class OCTranspo
|
|
109
144
|
})
|
110
145
|
end
|
111
146
|
|
112
|
-
|
113
|
-
|
114
|
-
#
|
115
|
-
|
116
|
-
if !trips.nil?
|
117
|
-
time_delta = Time.now.to_i - trips[:time]
|
118
|
-
route_obj[:request_processing_time] += time_delta
|
119
|
-
route_obj[:trips] = deep_copy(trips[:trips])
|
120
|
-
route_obj[:cached] = true
|
121
|
-
route_obj[:trips].each do |trip|
|
122
|
-
trip[:adjusted_schedule_time] -= (time_delta.to_f / 60).round
|
123
|
-
if trip[:adjustment_age] > 0
|
124
|
-
trip[:adjustment_age] += time_delta.to_f / 60
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# Filter out results with negative arrival times, since they've probably
|
129
|
-
# already gone by.
|
130
|
-
route_obj[:trips].select! { |trip| trip[:adjusted_schedule_time] >= 0 }
|
131
|
-
|
132
|
-
else
|
133
|
-
# No data in the cache... Hrm...
|
134
|
-
end
|
135
|
-
|
136
|
-
else
|
137
|
-
# Cache the trips for later
|
138
|
-
@trips_cache[cache_key] = {
|
139
|
-
time: Time.now.to_i,
|
140
|
-
trips: route_obj[:trips]
|
141
|
-
}
|
147
|
+
if route_obj[:trips].length != 0
|
148
|
+
# Assume that if any trips are filled in, then all the trips will be filled in?
|
149
|
+
# Is this a safe assumption?
|
150
|
+
found_data = true
|
142
151
|
end
|
143
152
|
|
144
153
|
result[:routes].push route_obj
|
145
154
|
end
|
146
155
|
|
156
|
+
# Sometimes OC Transpo doesn't return any data for a route, even though it should. When
|
157
|
+
# this happens, if we have cached data, we use that, even if it's slightly stale.
|
158
|
+
if !found_data and !cached_result.nil?
|
159
|
+
# Use the cached data, even if it's stale
|
160
|
+
result = adjust_cached_trip_times(cached_result[:next_trips])
|
161
|
+
else
|
162
|
+
@next_trips_cache[cache_key] = {
|
163
|
+
next_trips: result,
|
164
|
+
time: Time.now.to_i
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
147
168
|
|
148
169
|
return result
|
149
170
|
end
|
@@ -199,14 +220,13 @@ class OCTranspo
|
|
199
220
|
|
200
221
|
BASE_URL = "https://api.octranspo1.com/v1.1"
|
201
222
|
OCT_NS = {'oct' => 'http://octranspo.com', 't' => 'http://tempuri.org/'}
|
202
|
-
|
223
|
+
NEXT_TRIPS_CACHE_SIZE = 100
|
203
224
|
ROUTE_CACHE_SIZE = 100
|
204
225
|
|
205
226
|
# Fetch and parse some data from the OC-Transpo API. Returns a nokogiri object for
|
206
227
|
# the Result within the XML document.
|
207
228
|
def fetch(resource, params)
|
208
229
|
@api_calls = (@api_calls + 1)
|
209
|
-
|
210
230
|
response = RestClient.post("#{BASE_URL}/#{resource}",
|
211
231
|
"appID=#{@app_id}&apiKey=#{@app_key}&#{params}")
|
212
232
|
|
@@ -259,5 +279,30 @@ class OCTranspo
|
|
259
279
|
def deep_copy(o)
|
260
280
|
Marshal.load(Marshal.dump(o))
|
261
281
|
end
|
262
|
-
end
|
263
282
|
|
283
|
+
# When returning cached trips, we need to adjust the `:adjustment_age` and
|
284
|
+
# `:adjusted_schedule_time` of each entry to reflect how long the object has been
|
285
|
+
# sitting in the cache.
|
286
|
+
def adjust_cached_trip_times(cached_routes)
|
287
|
+
|
288
|
+
cached_routes = deep_copy cached_routes
|
289
|
+
cached_routes[:cached] = true
|
290
|
+
|
291
|
+
time_delta = Time.now.to_i - cached_routes[:time].to_i
|
292
|
+
cached_routes[:routes].each do |route_obj|
|
293
|
+
route_obj[:trips].each do |trip|
|
294
|
+
trip[:adjusted_schedule_time] -= (time_delta.to_f / 60).round
|
295
|
+
if trip[:adjustment_age] > 0
|
296
|
+
trip[:adjustment_age] += time_delta.to_f / 60
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# Filter out results with negative arrival times, since they've probably
|
301
|
+
# already gone by.
|
302
|
+
route_obj[:trips].select! { |trip| trip[:adjusted_schedule_time] >= 0 }
|
303
|
+
end
|
304
|
+
|
305
|
+
return cached_routes
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: octranspo_fetch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-09-
|
12
|
+
date: 2013-09-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|