cta_redux 0.2.0 → 0.3.1
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.
- 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
|