cta_redux 0.2.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +26 -0
- data/README.md +1 -1
- data/lib/cta_redux/api/api_response.rb +10 -0
- data/lib/cta_redux/api/bus_tracker.rb +69 -5
- data/lib/cta_redux/api/customer_alerts.rb +47 -4
- data/lib/cta_redux/api/train_tracker.rb +31 -13
- data/lib/cta_redux/bus_tracker.rb +42 -5
- data/lib/cta_redux/customer_alerts.rb +37 -1
- data/lib/cta_redux/faraday_middleware/simple_cache.rb +2 -0
- data/lib/cta_redux/models/agency.rb +22 -0
- data/lib/cta_redux/models/bus.rb +97 -18
- data/lib/cta_redux/models/calendar.rb +28 -0
- data/lib/cta_redux/models/route.rb +71 -9
- data/lib/cta_redux/models/shape.rb +20 -0
- data/lib/cta_redux/models/stop.rb +68 -0
- data/lib/cta_redux/models/stop_time.rb +33 -0
- data/lib/cta_redux/models/train.rb +80 -16
- data/lib/cta_redux/models/transfer.rb +17 -0
- data/lib/cta_redux/models/trip.rb +54 -9
- data/lib/cta_redux/train_tracker.rb +38 -1
- data/lib/cta_redux/version.rb +1 -1
- data/spec/bus_tracker_spec.rb +4 -4
- data/spec/train_tracker_spec.rb +2 -2
- metadata +3 -2
@@ -1,6 +1,39 @@
|
|
1
1
|
module CTA
|
2
|
+
# A {http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model.html Sequel::Model}.
|
3
|
+
# This corresponds to {https://developers.google.com/transit/gtfs/reference?csw=1#stop_times_txt___Field_Definitions stop_times.txt} in the
|
4
|
+
# GTFS feed, though the CTA does not fully implement the standard.
|
5
|
+
# This object can give you the scheduled times that a vehicle should stop along a {CTA::Route} (or really, a {CTA::Trip} if you're being specific).
|
6
|
+
# It can also function as somewhat of a 'join table' between trips and stops.
|
7
|
+
# @note Current columns: [:trip_id, :arrival_time, :departure_time, :stop_id, :stop_sequence, :stop_headsign, :pickup_type, :shape_dist_traveled]
|
2
8
|
class StopTime < Sequel::Model
|
9
|
+
# @!method trip
|
10
|
+
# @return [CTA::Trip] The {CTA::Trip} associated with this {CTA::StopTime}
|
3
11
|
many_to_one :trip, :key => :trip_id
|
12
|
+
# @!method stop
|
13
|
+
# @return [CTA::Stop] The {CTA::Stop} associated with this {CTA::StopTime}
|
4
14
|
many_to_one :stop, :key => :stop_id
|
15
|
+
|
16
|
+
# @!method trip_id
|
17
|
+
# @return [Integer]
|
18
|
+
# @!method arrival_time
|
19
|
+
# @return [String]
|
20
|
+
# @note This is a string because ruby has no concept of storing a time without a date
|
21
|
+
# @!method departure_time
|
22
|
+
# @return [String]
|
23
|
+
# @note This is a string because ruby has no concept of storing a time without a date
|
24
|
+
# @!method stop_id
|
25
|
+
# @return [Integer]
|
26
|
+
# @!method stop_sequence
|
27
|
+
# @return [Integer]
|
28
|
+
# @!method stop_headsign
|
29
|
+
# @return [String]
|
30
|
+
# @!method pickup_type
|
31
|
+
# @return [Integer]
|
32
|
+
# @!method shape_dist_traveled
|
33
|
+
# @return [Integer]
|
34
|
+
alias_method :sequence, :stop_sequence
|
35
|
+
alias_method :headsign, :stop_headsign
|
36
|
+
alias_method :distance, :shape_dist_traveled
|
37
|
+
alias_method :distance_traveled, :shape_dist_traveled
|
5
38
|
end
|
6
39
|
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module CTA
|
2
|
+
# A {http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model.html Sequel::Model}, inherited from {CTA::Trip}
|
3
|
+
# This corresponds to {https://developers.google.com/transit/gtfs/reference?csw=1#trips_txt___Field_Definitions trips.txt} in the
|
4
|
+
# GTFS feed, though the CTA does not fully implement the standard.
|
5
|
+
# @note Current columns: [:route_id, :service_id, :trip_id, :direction_id, :block_id, :shape_id, :direction, :wheelchair_accessible, :schd_trip_id]
|
2
6
|
class Train < CTA::Trip
|
7
|
+
# @return [Live] Returns the {Live} data associated with this {CTA::Train} object, if available.
|
8
|
+
# @note a {CTA::Train} will only contain live data when augmented with an {API::Response}
|
9
|
+
attr_accessor :live
|
10
|
+
|
3
11
|
L_ROUTES = {
|
4
12
|
"red" => { :name => "Red",
|
5
13
|
:directions => { "1" => "Howard-bound", "5" => "95th/Dan Ryan-bound" }
|
@@ -28,27 +36,86 @@ module CTA
|
|
28
36
|
}
|
29
37
|
FRIENDLY_L_ROUTES = Hash[L_ROUTES.values.map { |r| r[:name].downcase.to_sym }.zip(L_ROUTES.keys)]
|
30
38
|
|
39
|
+
# @!method route_id
|
40
|
+
# @return [String]
|
41
|
+
# @!method service_id
|
42
|
+
# @return [Integer]
|
43
|
+
# @!method trip_id
|
44
|
+
# @return [Integer]
|
45
|
+
# @!method direction_id
|
46
|
+
# @return [Integer]
|
47
|
+
# @!method block_id
|
48
|
+
# @return [Integer]
|
49
|
+
# @!method shape_id
|
50
|
+
# @return [Integer]
|
51
|
+
# @!method direction
|
52
|
+
# @return [String]
|
53
|
+
# @!method wheelchair_accessible
|
54
|
+
# @return [true,false]
|
55
|
+
# @!method schd_trip_id
|
56
|
+
# @return [String]
|
57
|
+
alias_method :id, :route_id
|
58
|
+
alias_method :scheduled_trip_id, :schd_trip_id
|
59
|
+
alias_method :run, :schd_trip_id
|
60
|
+
|
61
|
+
# Follows a train, using the TrainTracker follow API
|
62
|
+
# @return [CTA::TrainTracker::FollowResponse] A {CTA::TrainTracker::FollowResponse} with predictions for this train
|
31
63
|
def follow!
|
32
64
|
CTA::TrainTracker.follow!(:run => self.schd_trip_id.gsub("R", ""))
|
33
65
|
end
|
34
66
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
67
|
+
class Live
|
68
|
+
# @return [Float] The current latitude of the train
|
69
|
+
attr_reader :lat
|
70
|
+
# @return [Float] The current longitude of the train
|
71
|
+
attr_reader :lon
|
72
|
+
# @return [Integer] The current heading of the train
|
73
|
+
attr_reader :heading
|
74
|
+
# @return [Array<Prediction>] An array of {Prediction} objects that correspond to predictions returned from the TrainTracker API
|
75
|
+
attr_reader :predictions
|
39
76
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
77
|
+
def initialize(position, predictions)
|
78
|
+
@lat = position["lat"].to_f
|
79
|
+
@lon = position["lon"].to_f
|
80
|
+
@heading = position["heading"].to_i
|
81
|
+
@predictions = Array.wrap(predictions).map { |p| Prediction.new(p) }
|
82
|
+
end
|
45
83
|
end
|
46
84
|
|
47
85
|
class Prediction
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
86
|
+
# @return [String] The run identifier for this train.
|
87
|
+
# @note This is returned as a string, because the API will return results like "004" and the leading zeroes are important.
|
88
|
+
attr_reader :run
|
89
|
+
# @return [CTA::Trip] The {CTA::Trip} associated with this train
|
90
|
+
attr_reader :trip
|
91
|
+
# @return [CTA::Stop] The final {CTA::Stop} of this train
|
92
|
+
attr_reader :destination
|
93
|
+
# @return [String] A human-readable direction of this train, eg "O'Hare-bound"
|
94
|
+
attr_reader :direction
|
95
|
+
# @return [CTA::Stop] The next {CTA::Stop} of this train
|
96
|
+
attr_reader :next_station
|
97
|
+
# @return [DateTime] The time this {Prediction} was generated on the TrainTracker servers
|
98
|
+
attr_reader :prediction_generated_at
|
99
|
+
# @return [DateTime] The time this train is predicted to arrive at the next_station
|
100
|
+
attr_reader :arrival_time
|
101
|
+
# @return [Integer] The number of minutes until this train arrives at the next_station
|
102
|
+
attr_reader :minutes
|
103
|
+
# @return [Integer] The number of seconds until this train arrives at the next_station
|
104
|
+
attr_reader :seconds
|
105
|
+
# @return [true,false] True if this train is considered to be 'approaching' the next_station by the CTA
|
106
|
+
attr_reader :approaching
|
107
|
+
# @return [true,false] True if this train has not yet left it's origin station and started it's run
|
108
|
+
attr_reader :scheduled
|
109
|
+
# @return [true,false] True if this train is considered to be 'delayed' by the CTA
|
110
|
+
# @note The CTA considers a train 'delayed' if it's not progressing along the tracks. This is *not* an indication
|
111
|
+
# that a predicted arrival time will be later than a scheduled arrival time (which is how most people would consider a train
|
112
|
+
# to be 'delayed'). The CTA recommends that you indicate a train is 'delayed' rather than continuing to display the last predicted
|
113
|
+
# arrival time, which may no longer be accurate.
|
114
|
+
attr_reader :delayed
|
115
|
+
# @return [String] Flags for this train. Unused at this time.
|
116
|
+
attr_reader :flags
|
117
|
+
# @return [CTA::Route] The {CTA::Route} this train is running.
|
118
|
+
attr_reader :route
|
52
119
|
|
53
120
|
def initialize(data)
|
54
121
|
@run = data["rn"]
|
@@ -63,9 +130,6 @@ module CTA
|
|
63
130
|
@delayed = (data["isDly"] == "1")
|
64
131
|
@scheduled = (data["isSch"] == "1")
|
65
132
|
@flags = data["flags"]
|
66
|
-
@lat = data["lat"].to_f
|
67
|
-
@lon = data["lon"].to_f
|
68
|
-
@heading = data["heading"].to_i
|
69
133
|
@route = @trip.route
|
70
134
|
@direction = L_ROUTES[@route.route_id.downcase][:directions][data["trDr"]]
|
71
135
|
end
|
@@ -1,6 +1,23 @@
|
|
1
1
|
module CTA
|
2
|
+
# A {http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model.html Sequel::Model}.
|
3
|
+
# This corresponds to {https://developers.google.com/transit/gtfs/reference?csw=1#transfers_txt___Field_Definitions transfers.txt} in the
|
4
|
+
# GTFS feed, though the CTA does not fully implement the standard.
|
5
|
+
# @note Current columns: [:from_stop_id, :to_stop_id, :transfer_type]
|
2
6
|
class Transfer < Sequel::Model
|
7
|
+
# @!method from_stop
|
8
|
+
# The stop to transfer *from*
|
9
|
+
# @return [CTA::Stop]
|
3
10
|
many_to_one :from_stop, :class => 'CTA::Stop', :key => :from_stop_id
|
11
|
+
# @!method to_stop
|
12
|
+
# The stop to transfer *to*
|
13
|
+
# @return [CTA::Stop]
|
4
14
|
many_to_one :to_stop, :class => 'CTA::Stop', :key => :to_stop_id
|
15
|
+
|
16
|
+
# @!method from_stop_id
|
17
|
+
# @return [Integer]
|
18
|
+
# @!method to_stop_id
|
19
|
+
# @return [Integer]
|
20
|
+
# @!method transfer_type
|
21
|
+
# @return [Integer]
|
5
22
|
end
|
6
23
|
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
module CTA
|
2
|
+
# A {http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model.html Sequel::Model}.
|
3
|
+
# This corresponds to {https://developers.google.com/transit/gtfs/reference?csw=1#trips_txt___Field_Definitions trips.txt} in the
|
4
|
+
# GTFS feed, though the CTA does not fully implement the standard.
|
5
|
+
# @note Current columns: [:route_id, :service_id, :trip_id, :direction_id, :block_id, :shape_id, :direction, :wheelchair_accessible, :schd_trip_id]
|
2
6
|
class Trip < Sequel::Model
|
3
7
|
L_ROUTES = ["Brn", "G", "Pink", "P", "Org", "Red", "Blue", "Y"]
|
4
8
|
BUS_ROUTES = CTA::Trip.exclude(:route_id => L_ROUTES).select_map(:route_id).uniq
|
@@ -21,22 +25,63 @@ module CTA
|
|
21
25
|
|
22
26
|
set_primary_key :trip_id
|
23
27
|
|
28
|
+
# @!method calendar
|
29
|
+
# @return [CTA::Calendar] The {CTA::Calendar} entry for this {CTA::Trip}. Can be used to determine
|
30
|
+
# if a given {CTA::Trip} is valid for a given date/time
|
24
31
|
many_to_one :calendar, :key => :service_id
|
32
|
+
|
33
|
+
# @!method stop_times
|
34
|
+
# @return [Array<CTA::StopTime>] The {CTA::StopTimes} that are serviced on this {CTA::Trip}
|
25
35
|
one_to_many :stop_times, :key => :trip_id
|
26
36
|
|
37
|
+
# @!method route
|
38
|
+
# @return [CTA::Route] The {CTA::Route} associated with this {CTA::Trip}
|
27
39
|
many_to_one :route, :key => :route_id
|
28
40
|
|
41
|
+
# @!method stops
|
42
|
+
# @return [Array<CTA::Stop>] All {CTA::Stop}s serviced on this {CTA::Trip}
|
29
43
|
many_to_many :stops, :left_key => :trip_id, :right_key => :stop_id, :join_table => :stop_times
|
30
44
|
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
45
|
+
# @!method route_id
|
46
|
+
# @return [String]
|
47
|
+
# @!method service_id
|
48
|
+
# @return [Integer]
|
49
|
+
# @!method trip_id
|
50
|
+
# @return [Integer]
|
51
|
+
# @!method direction_id
|
52
|
+
# @return [Integer]
|
53
|
+
# @!method block_id
|
54
|
+
# @return [Integer]
|
55
|
+
# @!method shape_id
|
56
|
+
# @return [Integer]
|
57
|
+
# @!method direction
|
58
|
+
# @return [String]
|
59
|
+
# @!method wheelchair_accessible
|
60
|
+
# @return [true,false]
|
61
|
+
# @!method schd_trip_id
|
62
|
+
# @return [String]
|
63
|
+
alias_method :id, :route_id
|
64
|
+
alias_method :scheduled_trip_id, :schd_trip_id
|
65
|
+
alias_method :run, :schd_trip_id
|
66
|
+
|
67
|
+
# Find a {CTA::Trip} that should be happening, given a timestamp and a route or run.
|
68
|
+
# The CTA does not return GTFS trip_id information in either the BusTracker or TrainTracker API, so
|
69
|
+
# it is actually somewhat difficult to associate an API response to a {CTA::Trip}. However, we
|
70
|
+
# know what *should* be happening at any given time. This method attempts a fuzzy find - internally,
|
71
|
+
# we often first try to find the exact Trip that should be happening according to the schedule, and
|
72
|
+
# then failing that we assume that the CTA is running late and look for trips that should have
|
73
|
+
# ended within the past 90 minutes. This almost always finds something.
|
74
|
+
# That said, however, it means we may sometimes find a {CTA::Trip} that's incorrect. In practical usage
|
75
|
+
# however, that doesn't matter too much - most Trips for a run service the same stops.
|
76
|
+
# However, to be safe, your program may wish to compare certain other bits of the API responses to ensure
|
77
|
+
# we found something valid. For example, almost all Brown line trains service the same stops, so
|
78
|
+
# finding the wrong Trip doesn't matter too much. However, a handful of Brown line runs throughout the dat
|
79
|
+
# actually change to Orange line trains at Midway - so, you may wish to verify that the destination of the
|
80
|
+
# Trip matches the reported destination of the API.
|
81
|
+
# Suggestions on how to approach this problem are most welcome (as are patches for better behavior).
|
82
|
+
# @param [String] run The run or route to search for
|
83
|
+
# @param [DateTime, String] timestamp The timestamp to search against.
|
84
|
+
# @param [true,false] fuzz Whether or not to do an exact schedule search or a fuzzy search.
|
40
85
|
def self.find_active_run(run, timestamp, fuzz = false)
|
41
86
|
if self.to_s == "CTA::Train" # This is admittedly hacky.
|
42
87
|
join_str = "WHERE t.schd_trip_id = 'R#{run}'"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module CTA
|
2
2
|
class TrainTracker
|
3
|
+
@cache_responses = true
|
4
|
+
|
3
5
|
# Returns the connection object we use to talk to the TrainTracker API
|
4
6
|
def self.connection
|
5
7
|
raise "You need to set a developer key first. Try CTA::TrainTracker.key = 'foo'." unless @key
|
@@ -9,7 +11,9 @@ module CTA
|
|
9
11
|
faraday.params = { :key => @key }
|
10
12
|
|
11
13
|
faraday.use CTA::TrainTracker::Parser, !!@debug
|
12
|
-
|
14
|
+
if @cache_responses
|
15
|
+
faraday.response :caching, (@cache || SimpleCache.new(Hash.new))
|
16
|
+
end
|
13
17
|
faraday.adapter Faraday.default_adapter
|
14
18
|
end
|
15
19
|
end
|
@@ -146,5 +150,38 @@ module CTA
|
|
146
150
|
@debug = debug
|
147
151
|
@connection = nil
|
148
152
|
end
|
153
|
+
|
154
|
+
# Returns whether or not cta_redux is caching responses
|
155
|
+
# @return [true, false]
|
156
|
+
def self.cache_responses
|
157
|
+
@cache_responses
|
158
|
+
end
|
159
|
+
|
160
|
+
# Sets whether or not cta_redux is caching responses
|
161
|
+
# @param [true, false] should_cache
|
162
|
+
def self.cache_responses=(should_cache)
|
163
|
+
@cache_responses = should_cache
|
164
|
+
@connection = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns the underlying cache object caching responses (if we're actually caching responses)
|
168
|
+
# @return [Object]
|
169
|
+
def self.cache
|
170
|
+
if self.cache_responses
|
171
|
+
# This is ugly
|
172
|
+
@cache || self.connection.builder.handlers.find { |x| x == FaradayMiddleware::Caching }.instance_variable_get(:@args).first
|
173
|
+
else
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Sets the underlying cache object caching responses. Any object can be used that responds to #read, #write, and #fetch
|
179
|
+
# @note Setting the cache object resets the connection. If you're using the default SimpleCache strategy (built-in 60
|
180
|
+
# second caching), then it will also *clear* the cache.
|
181
|
+
# @param [Object] cache
|
182
|
+
def self.cache=(cache)
|
183
|
+
@cache = cache
|
184
|
+
@connection = nil
|
185
|
+
end
|
149
186
|
end
|
150
187
|
end
|
data/lib/cta_redux/version.rb
CHANGED
data/spec/bus_tracker_spec.rb
CHANGED
@@ -45,8 +45,8 @@ RSpec.describe CTA::BusTracker do
|
|
45
45
|
|
46
46
|
expect(result.vehicles.first.route).to be_instance_of(CTA::Route)
|
47
47
|
expect(result.vehicles.first.route.route_id).to eq("22")
|
48
|
-
expect(result.vehicles.first.vehicle_id).to eq(4394)
|
49
|
-
expect(result.vehicles.first.pattern_distance).to eq(115)
|
48
|
+
expect(result.vehicles.first.live.vehicle_id).to eq(4394)
|
49
|
+
expect(result.vehicles.first.live.pattern_distance).to eq(115)
|
50
50
|
end
|
51
51
|
|
52
52
|
it "returns information about one vehicle" do
|
@@ -56,8 +56,8 @@ RSpec.describe CTA::BusTracker do
|
|
56
56
|
expect(result.vehicles.size).to eq(1)
|
57
57
|
expect(result.vehicles.first.route).to be_instance_of(CTA::Route)
|
58
58
|
expect(result.vehicles.first.route.route_id).to eq("22")
|
59
|
-
expect(result.vehicles.first.vehicle_id).to eq(4394)
|
60
|
-
expect(result.vehicles.first.heading).to eq(359)
|
59
|
+
expect(result.vehicles.first.live.vehicle_id).to eq(4394)
|
60
|
+
expect(result.vehicles.first.live.heading).to eq(359)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
data/spec/train_tracker_spec.rb
CHANGED
@@ -43,7 +43,7 @@ RSpec.describe CTA::TrainTracker do
|
|
43
43
|
expect(response.train).to be_instance_of(CTA::Train)
|
44
44
|
expect(response.train.route_id).to eq("Blue")
|
45
45
|
expect(response.train.route.route_id).to eq("Blue")
|
46
|
-
expect(response.train.predictions).to eq(response.predictions)
|
46
|
+
expect(response.train.live.predictions).to eq(response.predictions)
|
47
47
|
expect(response.predictions.first.direction).to eq("O'Hare-bound")
|
48
48
|
expect(response.predictions.first.destination.stop_name).to eq("O'Hare")
|
49
49
|
expect(response.predictions.first.approaching).to eq(false)
|
@@ -61,7 +61,7 @@ RSpec.describe CTA::TrainTracker do
|
|
61
61
|
expect(response.trains.size).to eq(80)
|
62
62
|
|
63
63
|
expect(response.trains.last.route.route_id).to eq("Y")
|
64
|
-
expect(response.
|
64
|
+
expect(response.trains.first.live.lon).to eq(-87.65338)
|
65
65
|
expect(response.predictions.first.route.route_id).to eq("Red")
|
66
66
|
expect(response.predictions.first.destination.stop_id).to eq(30173)
|
67
67
|
expect(response.predictions.first.trip.service_id).to eq(104701)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cta_redux
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Hayworth
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -151,6 +151,7 @@ files:
|
|
151
151
|
- ".gitignore"
|
152
152
|
- ".rspec"
|
153
153
|
- ".travis.yml"
|
154
|
+
- CONTRIBUTING.md
|
154
155
|
- Gemfile
|
155
156
|
- LICENSE
|
156
157
|
- README.md
|