cta_redux 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +16 -0
- data/Rakefile +2 -0
- data/cta_redux.gemspec +32 -0
- data/data/.gitkeep +0 -0
- data/data/cta-gtfs.db.gz +0 -0
- data/lib/cta_redux/api/api_response.rb +45 -0
- data/lib/cta_redux/api/bus_tracker.rb +178 -0
- data/lib/cta_redux/api/customer_alerts.rb +68 -0
- data/lib/cta_redux/api/train_tracker.rb +89 -0
- data/lib/cta_redux/bus_tracker.rb +183 -0
- data/lib/cta_redux/customer_alerts.rb +72 -0
- data/lib/cta_redux/faraday_middleware/bus_tracker_parser.rb +46 -0
- data/lib/cta_redux/faraday_middleware/customer_alerts_parser.rb +39 -0
- data/lib/cta_redux/faraday_middleware/simple_cache.rb +32 -0
- data/lib/cta_redux/faraday_middleware/train_tracker_parser.rb +37 -0
- data/lib/cta_redux/models/agency.rb +4 -0
- data/lib/cta_redux/models/bus.rb +46 -0
- data/lib/cta_redux/models/calendar.rb +7 -0
- data/lib/cta_redux/models/route.rb +62 -0
- data/lib/cta_redux/models/shape.rb +4 -0
- data/lib/cta_redux/models/stop.rb +66 -0
- data/lib/cta_redux/models/stop_time.rb +6 -0
- data/lib/cta_redux/models/train.rb +74 -0
- data/lib/cta_redux/models/transfer.rb +6 -0
- data/lib/cta_redux/models/trip.rb +64 -0
- data/lib/cta_redux/train_tracker.rb +103 -0
- data/lib/cta_redux/version.rb +3 -0
- data/lib/cta_redux.rb +50 -0
- data/script/gtfs_to_sqlite.rb +137 -0
- data/spec/bus_tracker_spec.rb +149 -0
- data/spec/customer_alerts_spec.rb +48 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/stubs/alerts_response.xml +362 -0
- data/spec/stubs/getdirections_response.xml +16 -0
- data/spec/stubs/getpatterns_rt22_response.xml +2768 -0
- data/spec/stubs/getpredictions_rt22stpid15895_response.xml +59 -0
- data/spec/stubs/getpredictions_vid4361_response.xml +647 -0
- data/spec/stubs/getroutes_response.xml +774 -0
- data/spec/stubs/getservicebulletins_rt8_response.xml +110 -0
- data/spec/stubs/getstops_response.xml +614 -0
- data/spec/stubs/gettime_response.xml +2 -0
- data/spec/stubs/getvehicles_rt22_response.xml +239 -0
- data/spec/stubs/getvehicles_vid4394_response.xml +23 -0
- data/spec/stubs/route_status8_response.xml +1 -0
- data/spec/stubs/routes_response.xml +1 -0
- data/spec/stubs/ttarivals_stpid30141_response.xml +1 -0
- data/spec/stubs/ttfollow_run217_response.xml +1 -0
- data/spec/stubs/ttpositions_response.xml +1 -0
- data/spec/train_tracker_spec.rb +70 -0
- metadata +234 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
module CTA
|
2
|
+
class Trip < Sequel::Model
|
3
|
+
L_ROUTES = ["Brn", "G", "Pink", "P", "Org", "Red", "Blue", "Y"]
|
4
|
+
BUS_ROUTES = CTA::Trip.exclude(:route_id => L_ROUTES).select_map(:route_id).uniq
|
5
|
+
plugin :single_table_inheritance,
|
6
|
+
:route_id,
|
7
|
+
:model_map => proc { |v|
|
8
|
+
if L_ROUTES.include?(v)
|
9
|
+
'CTA::Train'
|
10
|
+
else
|
11
|
+
'CTA::Bus'
|
12
|
+
end
|
13
|
+
},
|
14
|
+
:key_map => proc { |klass|
|
15
|
+
if klass.name == 'CTA::Train'
|
16
|
+
L_ROUTES
|
17
|
+
else
|
18
|
+
BUS_ROUTES
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
set_primary_key :trip_id
|
23
|
+
|
24
|
+
many_to_one :calendar, :key => :service_id
|
25
|
+
one_to_many :stop_times, :key => :trip_id
|
26
|
+
|
27
|
+
many_to_one :route, :key => :route_id
|
28
|
+
|
29
|
+
many_to_many :stops, :left_key => :trip_id, :right_key => :stop_id, :join_table => :stop_times
|
30
|
+
|
31
|
+
# DRAGONS
|
32
|
+
# The CTA doesn't exactly honor the GTFS spec (nor do they return GTFS trip_ids
|
33
|
+
# in the API, grr). They specify multiple entries of # (schd_trip_id, block_id, service_id)
|
34
|
+
# so the only way to know which trip_id to pick is to join against stop_times and
|
35
|
+
# calendar dates, and # find out which run (according to stop_times) is happening *right now*.
|
36
|
+
# Of course, this will break if the train is delayed more the total time
|
37
|
+
# it takes to complete the run... so a delayed train will start to disappear
|
38
|
+
# as it progresses through the run. We allow for a 'fuzz factor' to account
|
39
|
+
# for this...
|
40
|
+
def self.find_active_run(run, timestamp, fuzz = false)
|
41
|
+
if self.to_s == "CTA::Train" # This is admittedly hacky.
|
42
|
+
join_str = "WHERE t.schd_trip_id = 'R#{run}'"
|
43
|
+
else
|
44
|
+
join_str = "WHERE t.route_id = '#{run}'"
|
45
|
+
end
|
46
|
+
d = timestamp.is_a?(DateTime) ? timestamp : DateTime.parse(timestamp)
|
47
|
+
wday = d.strftime("%A").downcase
|
48
|
+
end_ts = (fuzz ? (d.to_time + (60 * 60 * 6) - (90 * 60)) : d).strftime("%H:%M:%S")
|
49
|
+
Trip.with_sql(<<-SQL)
|
50
|
+
SELECT t.*
|
51
|
+
FROM trips t
|
52
|
+
JOIN stop_times st ON t.trip_id = st.trip_id
|
53
|
+
JOIN calendar c ON t.service_id = c.service_id
|
54
|
+
#{join_str}
|
55
|
+
AND c.start_date <= '#{d.to_s}'
|
56
|
+
AND c.end_date >= '#{d.to_s}'
|
57
|
+
AND c.#{wday}
|
58
|
+
GROUP BY t.trip_id, st.departure_time
|
59
|
+
HAVING MAX(st.departure_time) >= '#{end_ts}'
|
60
|
+
SQL
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module CTA
|
2
|
+
class TrainTracker
|
3
|
+
def self.connection
|
4
|
+
raise "You need to set a developer key first. Try CTA::TrainTracker.key = 'foo'." unless @key
|
5
|
+
|
6
|
+
@connection ||= Faraday.new do |faraday|
|
7
|
+
faraday.url_prefix = 'http://lapi.transitchicago.com/api/1.0/'
|
8
|
+
faraday.params = { :key => @key }
|
9
|
+
|
10
|
+
faraday.use CTA::TrainTracker::Parser, !!@debug
|
11
|
+
faraday.response :caching, SimpleCache.new(Hash.new)
|
12
|
+
faraday.adapter Faraday.default_adapter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.arrivals!(options={})
|
17
|
+
allowed_keys = [:route, :parent_station, :station, :limit]
|
18
|
+
if options.keys.any? { |k| !allowed_keys.include?(k) }
|
19
|
+
raise "Illegal option!"
|
20
|
+
end
|
21
|
+
|
22
|
+
has_map = options.has_key?(:parent_station)
|
23
|
+
has_stop = options.has_key?(:station)
|
24
|
+
|
25
|
+
route = Array.wrap(options[:route]).flatten.compact.uniq
|
26
|
+
map = Array.wrap(options[:parent_station]).flatten.compact.uniq
|
27
|
+
stop = Array.wrap(options[:station]).flatten.compact.uniq
|
28
|
+
limit = Array.wrap(options[:limit]).flatten.compact.uniq.first.to_i if options[:limit]
|
29
|
+
|
30
|
+
if route.size > 1
|
31
|
+
raise "No more than 1 route may be specified!"
|
32
|
+
end
|
33
|
+
|
34
|
+
if map.size > 1 || stop.size > 1
|
35
|
+
raise "No more than 1 station or parent_station may be specified!"
|
36
|
+
end
|
37
|
+
|
38
|
+
if !(has_map || has_stop)
|
39
|
+
raise "You must specify a station or a parent_station! Try arrivals(:station => 30280..."
|
40
|
+
end
|
41
|
+
|
42
|
+
params = {}
|
43
|
+
params.merge!({ :mapid => map.first }) if options[:parent_station]
|
44
|
+
params.merge!({ :stpid => stop.first }) if options[:station]
|
45
|
+
params.merge!({ :max => limit }) if options[:limit]
|
46
|
+
params.merge!({ :rt => route.first }) if route.any?
|
47
|
+
|
48
|
+
connection.get('ttarrivals.aspx', params)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.predictions!(options={})
|
52
|
+
self.arrivals!(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.follow!(options={})
|
56
|
+
raise "Must specify a run! Try follow(:run => 914)..." unless options.has_key?(:run)
|
57
|
+
|
58
|
+
runs = Array.wrap(options[:run]).flatten.compact.uniq
|
59
|
+
|
60
|
+
if runs.size > 1
|
61
|
+
raise "Only one run may be specified!"
|
62
|
+
end
|
63
|
+
|
64
|
+
connection.get('ttfollow.aspx', { :runnumber => runs.first })
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.locations!(options={})
|
68
|
+
unless options.has_key?(:routes)
|
69
|
+
raise "Must specify at least one route! (Try locations(:routes => [:red, :blue]) )"
|
70
|
+
end
|
71
|
+
|
72
|
+
rt = Array.wrap(options[:routes]).flatten.compact.map { |r| (CTA::Train::FRIENDLY_L_ROUTES[r] || r).to_s }
|
73
|
+
|
74
|
+
if rt.size > 8
|
75
|
+
raise "No more than 8 routes may be specified!"
|
76
|
+
end
|
77
|
+
|
78
|
+
connection.get('ttpositions.aspx', { :rt => rt.join(',') })
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.positions!(options={})
|
82
|
+
self.locations!(options)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.key
|
86
|
+
@key
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.key=(key)
|
90
|
+
@key = key
|
91
|
+
@connection = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.debug
|
95
|
+
!!@debug
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.debug=(debug)
|
99
|
+
@debug = debug
|
100
|
+
@connection = nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/cta_redux.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "cta_redux/version"
|
2
|
+
require "sequel"
|
3
|
+
require "sqlite3"
|
4
|
+
require "faraday"
|
5
|
+
require "faraday_middleware"
|
6
|
+
require "multi_xml"
|
7
|
+
require "zlib"
|
8
|
+
|
9
|
+
module CTA
|
10
|
+
base_path = File.expand_path("..", __FILE__)
|
11
|
+
data_dir = File.join(File.expand_path("../..", __FILE__), 'data')
|
12
|
+
Dir.glob("#{base_path}/cta_redux/faraday_middleware/*") { |lib| require lib }
|
13
|
+
Dir.glob("#{base_path}/cta_redux/api/*") { |lib| require lib }
|
14
|
+
require "#{base_path}/cta_redux/train_tracker.rb"
|
15
|
+
require "#{base_path}/cta_redux/bus_tracker.rb"
|
16
|
+
require "#{base_path}/cta_redux/customer_alerts.rb"
|
17
|
+
|
18
|
+
db_filename = File.join(data_dir, 'cta-gtfs.db')
|
19
|
+
|
20
|
+
# First run
|
21
|
+
if !File.exists?(db_filename)
|
22
|
+
dbf = File.open(db_filename, 'wb')
|
23
|
+
Zlib::GzipReader.open("#{db_filename}.gz") do |gz|
|
24
|
+
dbf.puts gz.read
|
25
|
+
end
|
26
|
+
dbf.close
|
27
|
+
end
|
28
|
+
|
29
|
+
DB = Sequel.sqlite(:database => db_filename, :readonly => true)
|
30
|
+
|
31
|
+
Dir.glob("#{base_path}/cta_redux/models/*") do |lib|
|
32
|
+
next if lib =~ /train/ || lib =~ /bus/
|
33
|
+
require lib
|
34
|
+
end
|
35
|
+
|
36
|
+
require "#{base_path}/cta_redux/models/train.rb"
|
37
|
+
require "#{base_path}/cta_redux/models/bus.rb"
|
38
|
+
end
|
39
|
+
|
40
|
+
class Array
|
41
|
+
def self.wrap(object)
|
42
|
+
if object.nil?
|
43
|
+
[]
|
44
|
+
elsif object.respond_to?(:to_ary)
|
45
|
+
object.to_ary || [object]
|
46
|
+
else
|
47
|
+
[object]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sqlite3'
|
4
|
+
require 'csv'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
KNOWN_COLUMN_TYPES = {
|
8
|
+
"calendar" => {
|
9
|
+
"service_id" => "INTEGER",
|
10
|
+
"monday" => "BOOLEAN",
|
11
|
+
"tuesday" => "BOOLEAN",
|
12
|
+
"wednesday" => "BOOLEAN",
|
13
|
+
"thursday" => "BOOLEAN",
|
14
|
+
"friday" => "BOOLEAN",
|
15
|
+
"saturday" => "BOOLEAN",
|
16
|
+
"sunday" => "BOOLEAN",
|
17
|
+
"start_date" => "DATE",
|
18
|
+
"end_date" => "DATE"
|
19
|
+
},
|
20
|
+
"routes" => {
|
21
|
+
"route_type" => "INTEGER"
|
22
|
+
},
|
23
|
+
"shapes" => {
|
24
|
+
"shape_id" => "INTEGER",
|
25
|
+
"shape_pt_lat" => "FLOAT",
|
26
|
+
"shape_pt_lon" => "FLOAT",
|
27
|
+
"shape_pt_sequence" => "INTEGER",
|
28
|
+
"shape_dist_traveled" => "INTEGER"
|
29
|
+
},
|
30
|
+
"stop_times" => {
|
31
|
+
"trip_id" => "INTEGER",
|
32
|
+
# This would be nice to store as a native SQL type,
|
33
|
+
# but ruby really has no notion of a time object w/o
|
34
|
+
# an associated date, because ruby stores time as epoch.
|
35
|
+
# so, in order to prevent ruby from attaching dates to our
|
36
|
+
# stop_times, we just let it store naturally as a string.
|
37
|
+
#"arrival_time" => "TIME",
|
38
|
+
#"departure_time" => "TIME",
|
39
|
+
"stop_id" => "INTEGER",
|
40
|
+
"stop_sequence" => "INTEGER",
|
41
|
+
"shape_dist_traveled" => "INTEGER",
|
42
|
+
"pickup_type" => "INTEGER"
|
43
|
+
},
|
44
|
+
"stops" => {
|
45
|
+
"stop_id" => "INTEGER",
|
46
|
+
"stop_code" => "INTEGER",
|
47
|
+
"stop_lat" => "FLOAT",
|
48
|
+
"stop_lon" => "FLOAT",
|
49
|
+
"location_type" => "INTEGER",
|
50
|
+
"parent_station" => "INTEGER",
|
51
|
+
"wheelchair_boarding" => "BOOLEAN"
|
52
|
+
},
|
53
|
+
"transfers" => {
|
54
|
+
"from_stop_id" => "INTEGER",
|
55
|
+
"to_stop_id" => "INTEGER",
|
56
|
+
"transfer_type" => "INTEGER"
|
57
|
+
},
|
58
|
+
"trips" => {
|
59
|
+
"service_id" => "INTEGER",
|
60
|
+
"trip_id" => "INTEGER",
|
61
|
+
"direction_id" => "INTEGER",
|
62
|
+
"block_id" => "INTEGER",
|
63
|
+
"shape_id" => "INTEGER",
|
64
|
+
"wheelchair_accessible" => "BOOLEAN"
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
def usage
|
69
|
+
"Usage: gtfs_to_sqlite.rb input_file output_db"
|
70
|
+
end
|
71
|
+
|
72
|
+
abort usage unless ARGV.size == 2
|
73
|
+
|
74
|
+
base_path = File.expand_path("..", __FILE__)
|
75
|
+
input_file = File.join(base_path, ARGV[0])
|
76
|
+
output_db = File.join(base_path, ARGV[1])
|
77
|
+
|
78
|
+
table = input_file.split(File::SEPARATOR).last.gsub(".txt", "")
|
79
|
+
|
80
|
+
puts input_file
|
81
|
+
abort usage unless File.exists?(input_file)
|
82
|
+
|
83
|
+
db = SQLite3::Database.new(output_db)
|
84
|
+
|
85
|
+
first_row = true
|
86
|
+
n = 0
|
87
|
+
headers = nil
|
88
|
+
|
89
|
+
print "Loading"
|
90
|
+
CSV.foreach(input_file, :headers => true) do |row|
|
91
|
+
if n % 100 == 0
|
92
|
+
print "."
|
93
|
+
end
|
94
|
+
if n % 1000 == 0
|
95
|
+
print n
|
96
|
+
end
|
97
|
+
|
98
|
+
if first_row
|
99
|
+
columns = row.headers.map do |r|
|
100
|
+
if KNOWN_COLUMN_TYPES[table] && KNOWN_COLUMN_TYPES[table][r]
|
101
|
+
col = "#{r} #{KNOWN_COLUMN_TYPES[table][r]}"
|
102
|
+
else
|
103
|
+
col = r
|
104
|
+
end
|
105
|
+
end
|
106
|
+
stmt = "CREATE TABLE #{table} (#{columns.join(',')})"
|
107
|
+
db.execute(stmt)
|
108
|
+
first_row = false
|
109
|
+
headers = row.headers
|
110
|
+
end
|
111
|
+
|
112
|
+
stmt = "INSERT INTO #{table} (#{row.headers.join(',')}) VALUES (#{row.headers.map { '?' }.join(',')})"
|
113
|
+
if row.headers.any? { |h| h =~ /date/ }
|
114
|
+
fields = []
|
115
|
+
row.headers.each_with_index do |header, index|
|
116
|
+
if header =~ /date/
|
117
|
+
fields << DateTime.parse(row.fields[index]).to_s
|
118
|
+
else
|
119
|
+
fields << row.fields[index]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
else
|
123
|
+
fields = row.fields
|
124
|
+
end
|
125
|
+
db.execute(stmt, fields)
|
126
|
+
n +=1
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "\ndone"
|
130
|
+
puts "Creating indices..."
|
131
|
+
if headers
|
132
|
+
headers.each do |h|
|
133
|
+
if h =~ /_id/
|
134
|
+
db.execute("CREATE INDEX #{table}_#{h}_index ON #{table} (#{h})")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
base_path = File.expand_path(File.dirname(__FILE__))
|
2
|
+
data_path = File.expand_path(File.join(base_path, 'stubs'))
|
3
|
+
|
4
|
+
require File.join(base_path, "..", "lib", "cta_redux")
|
5
|
+
|
6
|
+
RSpec.describe CTA::BusTracker do
|
7
|
+
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
8
|
+
stub.get('/bustime/api/v2/gettime?key=') { |env| [200, {}, File.read(File.join(data_path, 'gettime_response.xml'))] }
|
9
|
+
stub.get('/bustime/api/v2/getvehicles?key=&rt=22') { |env| [200, {}, File.read(File.join(data_path, 'getvehicles_rt22_response.xml'))] }
|
10
|
+
stub.get('/bustime/api/v2/getvehicles?key=&vid=4394') { |env| [200, {}, File.read(File.join(data_path, 'getvehicles_vid4394_response.xml'))] }
|
11
|
+
stub.get('/bustime/api/v2/getroutes?key=') { |env| [200, {}, File.read(File.join(data_path, 'getroutes_response.xml'))] }
|
12
|
+
stub.get('/bustime/api/v2/getdirections?key=&rt=22') { |env| [200, {}, File.read(File.join(data_path, 'getdirections_response.xml'))] }
|
13
|
+
stub.get('/bustime/api/v2/getstops?key=&rt=22&dir=Northbound') { |env| [200, {}, File.read(File.join(data_path, 'getstops_response.xml'))] }
|
14
|
+
stub.get('/bustime/api/v2/getpatterns?key=&rt=22') { |env| [200, {}, File.read(File.join(data_path, 'getpatterns_rt22_response.xml'))] }
|
15
|
+
stub.get('/bustime/api/v2/getpredictions?key=&rt=22&stpid=15895&top=&vid=') { |env| [200, {}, File.read(File.join(data_path, 'getpredictions_rt22stpid15895_response.xml'))] }
|
16
|
+
stub.get('/bustime/api/v2/getpredictions?key=&vid=4361') { |env| [200, {}, File.read(File.join(data_path, 'getpredictions_vid4361_response.xml'))] }
|
17
|
+
stub.get('/bustime/api/v2/getservicebulletins?key=&rt=8') { |env| [200, {}, File.read(File.join(data_path, 'getservicebulletins_rt8_response.xml'))] }
|
18
|
+
end
|
19
|
+
|
20
|
+
CTA::BusTracker.key = ''
|
21
|
+
CTA::BusTracker.connection.instance_variable_get(:@builder).delete(Faraday::Adapter::NetHttp)
|
22
|
+
CTA::BusTracker.connection.adapter :test, stubs
|
23
|
+
|
24
|
+
describe "time!" do
|
25
|
+
result = CTA::BusTracker.time!
|
26
|
+
|
27
|
+
it "returns an error code" do
|
28
|
+
expect(result.error).to be_instance_of(CTA::API::Error)
|
29
|
+
expect(result.error.code).to eq(0)
|
30
|
+
expect(result.error.message).to eq("OK")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns a time" do
|
34
|
+
expect(result.timestamp).to be_instance_of(DateTime)
|
35
|
+
expect(result.timestamp.to_s).to eq("2015-02-14T11:31:13+00:00")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "vehicles!" do
|
40
|
+
it "returns vehicles for route 22" do
|
41
|
+
result = CTA::BusTracker.vehicles!(:routes => 22)
|
42
|
+
|
43
|
+
expect(result).to be_instance_of(CTA::BusTracker::VehiclesResponse)
|
44
|
+
expect(result.vehicles.size).to eq(13)
|
45
|
+
|
46
|
+
expect(result.vehicles.first.route).to be_instance_of(CTA::Route)
|
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)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns information about one vehicle" do
|
53
|
+
result = CTA::BusTracker.vehicles!(:vehicles => 4394)
|
54
|
+
|
55
|
+
expect(result).to be_instance_of(CTA::BusTracker::VehiclesResponse)
|
56
|
+
expect(result.vehicles.size).to eq(1)
|
57
|
+
expect(result.vehicles.first.route).to be_instance_of(CTA::Route)
|
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)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "routes!" do
|
65
|
+
it "returns information about all routes" do
|
66
|
+
result = CTA::BusTracker.routes!
|
67
|
+
|
68
|
+
expect(result).to be_instance_of(CTA::BusTracker::RoutesResponse)
|
69
|
+
expect(result.routes.size).to eq(127)
|
70
|
+
expect(result.routes.first.route_id).to eq("1")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "directions!" do
|
75
|
+
it "returns two directions for route 22" do
|
76
|
+
result = CTA::BusTracker.directions!(:route => 22)
|
77
|
+
|
78
|
+
expect(result).to be_instance_of(CTA::BusTracker::DirectionsResponse)
|
79
|
+
expect(result.directions.size).to eq(2)
|
80
|
+
expect(result.directions.first.direction).to eq("Northbound")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "stops!" do
|
85
|
+
it "returns information about Northbound route 22 stops" do
|
86
|
+
result = CTA::BusTracker.stops!(:route => 22, :direction => :northbound)
|
87
|
+
|
88
|
+
expect(result).to be_instance_of(CTA::BusTracker::StopsResponse)
|
89
|
+
expect(result.stops.size).to eq(86)
|
90
|
+
expect(result.stops.first).to be_instance_of(CTA::Stop)
|
91
|
+
expect(result.stops.first.stop_name).to eq("Clark & Addison")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "patterns!" do
|
96
|
+
it "returns information about patterns for route 22" do
|
97
|
+
result = CTA::BusTracker.patterns!(:route => 22)
|
98
|
+
|
99
|
+
expect(result).to be_instance_of(CTA::BusTracker::PatternsResponse)
|
100
|
+
expect(result.patterns.first.direction).to be_instance_of(CTA::BusTracker::Direction)
|
101
|
+
expect(result.patterns.first.id).to eq(3936)
|
102
|
+
expect(result.patterns.first.points.size).to eq(124)
|
103
|
+
expect(result.patterns.first.points.first.sequence).to eq(1)
|
104
|
+
expect(result.patterns.first.points.first.stop).to be_instance_of(CTA::Stop)
|
105
|
+
expect(result.patterns.first.points.first.stop.stop_id).to eq(14096)
|
106
|
+
expect(result.patterns.first.points[1].lat).to eq(42.019043088282)
|
107
|
+
expect(result.patterns.first.points[1].type).to eq(:waypoint)
|
108
|
+
expect(result.patterns.first.points[1].sequence).to eq(2)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "predictions!" do
|
113
|
+
it "returns predictions for route 22 stop 15898" do
|
114
|
+
result = CTA::BusTracker.predictions!(:routes => 22, :stops => 15895)
|
115
|
+
|
116
|
+
expect(result).to be_instance_of(CTA::BusTracker::PredictionsResponse)
|
117
|
+
expect(result.vehicles.size).to eq(2)
|
118
|
+
expect(result.vehicles.first.route.route_id).to eq("22")
|
119
|
+
expect(result.predictions.size).to eq(2)
|
120
|
+
expect(result.predictions.first.type).to eq("D")
|
121
|
+
expect(result.predictions.first.minutes).to eq(5)
|
122
|
+
expect(result.predictions.first.arrival_time.to_s).to eq("2015-02-14T12:25:00+00:00")
|
123
|
+
expect(result.predictions.first.delayed).to eq(false)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "returns predictions for vehicle 4361" do
|
127
|
+
result = CTA::BusTracker.predictions!(:vehicles => 4361)
|
128
|
+
|
129
|
+
expect(result).to be_instance_of(CTA::BusTracker::PredictionsResponse)
|
130
|
+
expect(result.vehicles.size).to eq(30)
|
131
|
+
expect(result.vehicles.first.route.route_id).to eq("22")
|
132
|
+
expect(result.predictions.size).to eq(30)
|
133
|
+
expect(result.predictions.first.type).to eq("A")
|
134
|
+
expect(result.predictions.first.minutes).to eq(2)
|
135
|
+
expect(result.predictions.first.arrival_time.to_s).to eq("2015-02-14T12:38:00+00:00")
|
136
|
+
expect(result.predictions.first.delayed).to eq(false)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "returns bulletins for route 8" do
|
141
|
+
result = CTA::BusTracker.bulletins!(:routes => 8)
|
142
|
+
|
143
|
+
expect(result).to be_instance_of(CTA::BusTracker::ServiceBulletinsResponse)
|
144
|
+
expect(result.bulletins.size).to eq(6)
|
145
|
+
expect(result.bulletins.first.subject).to eq("#8 Halsted reroute Halsted/35th")
|
146
|
+
expect(result.bulletins.first.affected_services.size).to eq(1)
|
147
|
+
expect(result.bulletins.first.affected_services.first.route.route_id).to eq("8")
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
base_path = File.expand_path(File.dirname(__FILE__))
|
2
|
+
data_path = File.expand_path(File.join(base_path, 'stubs'))
|
3
|
+
|
4
|
+
require File.join(base_path, "..", "lib", "cta_redux")
|
5
|
+
|
6
|
+
RSpec.describe CTA::TrainTracker do
|
7
|
+
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
8
|
+
stub.get('/api/1.0/routes.aspx?type=&routeid=&stationid=') { |env| [200, {}, File.read(File.join(data_path, 'routes_response.xml'))] }
|
9
|
+
stub.get('/api/1.0/routes.aspx?type=&routeid=8&stationid=') { |env| [200, {}, File.read(File.join(data_path, 'route_status8_response.xml'))] }
|
10
|
+
stub.get('/api/1.0/alerts.aspx') { |env| [200, {}, File.read(File.join(data_path, 'alerts_response.xml'))] }
|
11
|
+
end
|
12
|
+
|
13
|
+
CTA::CustomerAlerts.connection.instance_variable_get(:@builder).delete(Faraday::Adapter::NetHttp)
|
14
|
+
CTA::CustomerAlerts.connection.adapter :test, stubs
|
15
|
+
|
16
|
+
describe "status!" do
|
17
|
+
it "returns all status information" do
|
18
|
+
response = CTA::CustomerAlerts.status!
|
19
|
+
|
20
|
+
expect(response).to be_instance_of(CTA::CustomerAlerts::RouteStatusResponse)
|
21
|
+
expect(response.routes.size).to eq(140)
|
22
|
+
expect(response.routes.first.route.route_id).to eq("Red")
|
23
|
+
expect(response.routes.first.status).to eq("Planned Work w/Reroute")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns status information for one route" do
|
27
|
+
response = CTA::CustomerAlerts.status!(:routes => 8)
|
28
|
+
|
29
|
+
expect(response).to be_instance_of(CTA::CustomerAlerts::RouteStatusResponse)
|
30
|
+
expect(response.routes.size).to eq(1)
|
31
|
+
expect(response.routes.first.route.route_id).to eq("8")
|
32
|
+
expect(response.routes.first.status).to eq("Bus Stop Relocation")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "alerts!" do
|
37
|
+
it "returns all alerts" do
|
38
|
+
response = CTA::CustomerAlerts.alerts!
|
39
|
+
|
40
|
+
expect(response).to be_instance_of(CTA::CustomerAlerts::AlertsResponse)
|
41
|
+
expect(response.alerts.size).to eq(44)
|
42
|
+
expect(response.alerts.first.alert_id).to eq(23322)
|
43
|
+
expect(response.alerts.first.category).to eq(:normal)
|
44
|
+
expect(response.alerts.first.major_alert).to eq(false)
|
45
|
+
expect(response.alerts.first.tbd).to eq(true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.expect_with :rspec do |expectations|
|
3
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
4
|
+
end
|
5
|
+
|
6
|
+
config.mock_with :rspec do |mocks|
|
7
|
+
mocks.verify_partial_doubles = true
|
8
|
+
end
|
9
|
+
|
10
|
+
if config.files_to_run.one?
|
11
|
+
config.default_formatter = 'doc'
|
12
|
+
end
|
13
|
+
|
14
|
+
config.profile_examples = 10
|
15
|
+
config.order = :random
|
16
|
+
end
|