octranspo_fetch 0.0.2 → 0.0.3

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.
Files changed (2) hide show
  1. data/lib/octranspo_fetch.rb +84 -39
  2. metadata +2 -2
@@ -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
- @trips_cache = LruRedux::Cache.new(TRIPS_CACHE_SIZE)
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
- @trips_cache.clear()
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? then return cached_result end
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] = result
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
- cache_key = "#{stop}-#{route_obj[:route]}-#{route_obj[:direction]}"
113
- if route_obj[:trips].length == 0
114
- # Sometimes OC Transpo doesn't return any data. When this happens, fetch data from the cache.
115
- trips = @trips_cache[cache_key]
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
- TRIPS_CACHE_SIZE = 100
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.2
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-21 00:00:00.000000000 Z
12
+ date: 2013-09-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri