Class: OCTranspo

Inherits:
Object
  • Object
show all
Defined in:
lib/octranspo_fetch.rb

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ OCTranspo

Create a new OCTranspo

Arguments:

options[:application_id]: (String) Your application ID, assigned by OC Transpo.
options[:application_key]: (String) Your application key, assigned by OC Transpo.


15
16
17
18
19
20
21
22
23
# File 'lib/octranspo_fetch.rb', line 15

def initialize(options)
    @app_id = options[:application_id]
    @app_key = options[:application_key]
    @next_trips_cache = LruRedux::Cache.new(NEXT_TRIPS_CACHE_SIZE)
    @route_summary_cache = LruRedux::Cache.new(ROUTE_CACHE_SIZE)
    @api_calls = 0
    @cache_hits = 0
    @cache_misses = 0
end

Instance Method Details

#cache_statsObject



35
36
37
# File 'lib/octranspo_fetch.rb', line 35

def cache_stats()
    return {hits: @cache_hits, misses: @cache_misses}
end

#clear_cacheObject



25
26
27
28
# File 'lib/octranspo_fetch.rb', line 25

def clear_cache()
    @next_trips_cache.clear()
    @route_summary_cache.clear()
end

#get_next_trips_for_stop(stop, route_no, options = {}) ⇒ Object

Get the next three trips for the given stop. Note this may return data for the same route number in multiple headings.

Arguments:

stop: (String) The stop number.
route_no: (String) The route number.
options[:max_cache_time]: (Integer) Maximum cache age, in seconds.  If cached data is
  available and is newer than this, then the cached value will be returned.  Defaults
  to five minutes.


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/octranspo_fetch.rb', line 99

def get_next_trips_for_stop(stop, route_no, options={})
    max_cache_time = (options[:max_cache_time] or 60*5)

    # Return result from cache, if available
    cache_key = "#{stop}-#{route_no}"
    cached_result = @next_trips_cache[cache_key]
    if !cached_result.nil? and ((cached_result[:time] + max_cache_time) > Time.now.to_i)
        @cache_hits += 1
        return adjust_cached_trip_times(cached_result[:next_trips])
    end
    @cache_misses += 1

    xresult = fetch "GetNextTripsForStop", "stopNo=#{stop}&routeNo=#{route_no}"

    result = {
        stop: get_value(xresult, "t:StopNo"),
        stop_description: get_value(xresult, "t:StopLabel"),
        time: Time.now,
        routes: []
    }

    found_data = false

    xresult.xpath('t:Route/t:RouteDirection', OCT_NS).each do |route|
        get_error(route, "Error for route: #{route_no}")

        route_obj = {
            cached: false,
            route: get_value(route, "t:RouteNo"),
            route_label: get_value(route, "t:RouteLabel"),
            direction: get_value(route, "t:Direction"),
            request_processing_time: Time.parse(get_value(route, "t:RequestProcessingTime")),
            trips: []
        }
        route.xpath('t:Trips/t:Trip', OCT_NS).each do |trip|
            route_obj[:trips].push({
                destination: get_value(trip, "t:TripDestination"), # e.g. "Barhaven"
                start_time: get_value(trip, "t:TripStartTime"), # e.g. "14:25" TODO: parse to time
                adjusted_schedule_time: get_value(trip, "t:AdjustedScheduleTime").to_i, # Adjusted schedule time in minutes
                adjustment_age: get_value(trip, "t:AdjustmentAge").to_f, # Time since schedule was adjusted in minutes
                last_trip: (get_value(trip, "t:LastTripOfSchedule") == "true"),
                bus_type: get_value(trip, "t:BusType"),
                gps_speed: get_value(trip, "t:GPSSpeed").to_f,
                latitude: get_value(trip, "t:Latitude").to_f,
                longitude: get_value(trip, "t:Longitude").to_f
            })
        end

        if route_obj[:trips].length != 0
            # Assume that if any trips are filled in, then all the trips will be filled in?
            # Is this a safe assumption?
            found_data = true
        end

        result[:routes].push route_obj
    end

    # Sometimes OC Transpo doesn't return any data for a route, even though it should.  When
    # this happens, if we have cached data, we use that, even if it's slightly stale.
    if !found_data and !cached_result.nil?
        # Use the cached data, even if it's stale
        result = adjust_cached_trip_times(cached_result[:next_trips])
    else
        @next_trips_cache[cache_key] = {
            next_trips: result,
            time: Time.now.to_i
        }
    end


    return result
end

#get_route_summary_for_stop(stop, options = {}) ⇒ Object

Get a list of routes for a specific stop.

Returns a stop_description, routes: [{route, direction_id, direction, heading]} object. Note that route data is cached.

Arguments:

stop: (String) The stop number.
options[:max_cache_time]: (Integer) Maximum cache age, in seconds.  If cached data is
  available and is newer than this, then the cached value will be returned.  Defaults to
  one day.


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/octranspo_fetch.rb', line 50

def get_route_summary_for_stop(stop, options={})
    max_cache_time = (options[:max_cache_time] or 60*60*24)
    cached_result = @route_summary_cache[stop]
    if !cached_result.nil? and ((cached_result[:time] + max_cache_time) > Time.now.to_i)
        @cache_hits += 1
        return cached_result[:route_summary]
    end

    @cache_misses += 1

    xresult = fetch "GetRouteSummaryForStop", "stopNo=#{stop}"

    result = {
        stop: get_value(xresult, "t:StopNo"),
        stop_description: get_value(xresult, "t:StopDescription"),
        routes: []
    }

    xresult.xpath('t:Routes/t:Route', OCT_NS).each do |route|
        result[:routes].push({
            route: get_value(route, "t:RouteNo"),
            direction_id: get_value(route, "t:DirectionID"),
            direction: get_value(route, "t:Direction"),
            heading: get_value(route, "t:RouteHeading")
        })
    end

    if result[:routes].length == 0
        raise "No routes found"
    end

    @route_summary_cache[stop] = {
        route_summary: result,
        time: Time.now.to_i
    }

    return result
end

#requestsObject

Returns the number of API calls made by this instance since it was created.



31
32
33
# File 'lib/octranspo_fetch.rb', line 31

def requests()
    return @api_calls
end

#simple_get_next_trips_for_stop(stop, route_nos = nil, route_label = nil) ⇒ Object

Returns an array of ‘stop_description, route_no, route_label, direction, arrival_in_minutes, …` objects.

... is any data that would be available from a trip object from ‘get_next_trips_for_stop()` (e.g. gps_speed, latitude, longitude, etc…) Returned results are sorted in ascending arrival time.

Arguments:

stop: (String) The stop number.
route_nos: ([String]) can be a single route number, or an array of route numbers, or nil.
    If nil, then this method will call get_route_summary_for_stop to get a list of routes.
route_label: (String) If "route_label" is supplied, then only trips with a matching
    route_label will be returned.


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/octranspo_fetch.rb', line 186

def simple_get_next_trips_for_stop(stop, route_nos=nil, route_label=nil)
    answer = []
    if route_nos.nil?
        route_summary = get_route_summary_for_stop(stop)
        route_nos = route_summary[:routes].map { |e| e[:route] }

    elsif !route_nos.kind_of?(Array)
        route_nos = [route_nos] end

    route_nos.uniq.each do |route_no|
        oct_result = get_next_trips_for_stop stop, route_no
        oct_result[:routes].each do |route|
            if route_label.nil? or (route[:route_label] == route_label)
                route[:trips].each do |trip|
                    answer.push(trip.merge({
                        stop: oct_result[:stop],
                        stop_description: oct_result[:stop_description],
                        route_no: route[:route],
                        route_label: route[:route_label],
                        direction: route[:direction],
                        arrival_in_minutes: trip[:adjusted_schedule_time],
                        live: (trip[:adjustment_age] > 0)
                    }))
                end
            end
        end
    end

    answer.sort! { |a,b| a[:arrival_in_minutes] <=> b[:arrival_in_minutes] }

    return answer
end