cta_redux 0.3.3 → 0.3.4
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/data/cta-gtfs.db.gz +0 -0
- data/lib/cta_redux/api/bus_tracker.rb +2 -2
- data/lib/cta_redux/api/train_tracker.rb +6 -6
- data/lib/cta_redux/models/bus.rb +37 -0
- data/lib/cta_redux/models/shape.rb +6 -0
- data/lib/cta_redux/models/train.rb +72 -0
- data/lib/cta_redux/models/trip.rb +4 -42
- data/lib/cta_redux/version.rb +1 -1
- data/spec/train_tracker_spec.rb +8 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 520e277cc8d9ccbb7ab6f9a619df57f2ada3c246
|
4
|
+
data.tar.gz: c6c9ec28887958b1dcd36cccdc0597365ce885f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 002852493532fcd7c94e623575bf22c0d8f5ef788932e81525bfe8115dd78efcf4c98e15e81f0fffa1a5cd9fbcb0c87e61ad92ee5d950f49588b8c8adec13cec
|
7
|
+
data.tar.gz: 448b788a0ba92cabf6722a58f8b598bbcdc47956740acf8e94e2b0c056fc701c5bcb325a386ae3970d0ba620c9e77a9c38ec80154abbbd13c5909cdcf16323b6
|
data/data/cta-gtfs.db.gz
CHANGED
Binary file
|
@@ -13,7 +13,7 @@ module CTA
|
|
13
13
|
def initialize(parsed_body, raw_body, debug)
|
14
14
|
super(parsed_body, raw_body, debug)
|
15
15
|
@vehicles = Array.wrap(parsed_body["bustime_response"]["vehicle"]).map do |v|
|
16
|
-
bus = CTA::Bus.find_active_run(v["rt"], v["tmstmp"], (v["dly"] == "true")).first
|
16
|
+
bus = CTA::Bus.find_active_run(v["rt"], v["tmstmp"], (v["dly"] == "true"), v["rtdir"]).first
|
17
17
|
bus.live = CTA::Bus::Live.new(v)
|
18
18
|
|
19
19
|
bus
|
@@ -91,7 +91,7 @@ module CTA
|
|
91
91
|
def initialize(parsed_body, raw_body, debug)
|
92
92
|
super(parsed_body, raw_body, debug)
|
93
93
|
@vehicles = Array.wrap(parsed_body["bustime_response"]["prd"]).map do |p|
|
94
|
-
bus = CTA::Bus.find_active_run(p["rt"], p["tmstmp"], (p["dly"] == "true")).first
|
94
|
+
bus = CTA::Bus.find_active_run(p["rt"], p["tmstmp"], (p["dly"] == "true"), p["rtdir"]).first
|
95
95
|
bus.live = CTA::Bus::Live.new(p, p)
|
96
96
|
|
97
97
|
bus
|
@@ -22,9 +22,9 @@ module CTA
|
|
22
22
|
|
23
23
|
@routes = eta_map.map do |rt, etas|
|
24
24
|
trains = etas.map do |t|
|
25
|
-
train = CTA::Train.find_active_run(t["rn"], self.timestamp, (t["isDly"] == "1")).first
|
25
|
+
train = CTA::Train.find_active_run(t["rn"], self.timestamp, (t["isDly"] == "1"), t["destNm"]).first
|
26
26
|
if !train
|
27
|
-
train = CTA::Train.find_active_run(t["rn"], self.timestamp, true).first
|
27
|
+
train = CTA::Train.find_active_run(t["rn"], self.timestamp, true, t["destNm"]).first
|
28
28
|
end
|
29
29
|
position = t.select { |k,v| ["lat", "lon", "heading"].include?(k) }
|
30
30
|
train.live = CTA::Train::Live.new(position, t)
|
@@ -54,9 +54,9 @@ module CTA
|
|
54
54
|
super(parsed_body, raw_body, debug)
|
55
55
|
|
56
56
|
train_info = Array.wrap(parsed_body["ctatt"]["eta"]).first
|
57
|
-
@train = CTA::Train.find_active_run(train_info["rn"], self.timestamp, (train_info["isDly"] == "1")).first
|
57
|
+
@train = CTA::Train.find_active_run(train_info["rn"], self.timestamp, (train_info["isDly"] == "1"), train_info["destNm"]).first
|
58
58
|
if !@train
|
59
|
-
@train = CTA::Train.find_active_run(train_info["rn"], self.timestamp, true).first
|
59
|
+
@train = CTA::Train.find_active_run(train_info["rn"], self.timestamp, true, train_info['destNm']).first
|
60
60
|
end
|
61
61
|
@train.live = CTA::Train::Live.new(parsed_body["ctatt"]["position"], parsed_body["ctatt"]["eta"])
|
62
62
|
@predictions = @train.live.predictions
|
@@ -79,9 +79,9 @@ module CTA
|
|
79
79
|
rt = Route.where(:route_id => route["name"].capitalize).first
|
80
80
|
|
81
81
|
trains = Array.wrap(route["train"]).map do |train|
|
82
|
-
t = CTA::Train.find_active_run(train["rn"], self.timestamp, (train["isDly"] == "1")).first
|
82
|
+
t = CTA::Train.find_active_run(train["rn"], self.timestamp, (train["isDly"] == "1"), train['destNm']).first
|
83
83
|
if !t # Sometimes the CTA doesn't report things as delayed even when they ARE
|
84
|
-
t = CTA::Train.find_active_run(train["rn"], self.timestamp, true).first
|
84
|
+
t = CTA::Train.find_active_run(train["rn"], self.timestamp, true, train['destNm']).first
|
85
85
|
end
|
86
86
|
|
87
87
|
if !t
|
data/lib/cta_redux/models/bus.rb
CHANGED
@@ -30,6 +30,43 @@ module CTA
|
|
30
30
|
alias_method :scheduled_trip_id, :schd_trip_id
|
31
31
|
alias_method :run, :schd_trip_id
|
32
32
|
|
33
|
+
# Find a {CTA::Trip} that should be happening, given a timestamp and a route or run.
|
34
|
+
# The CTA does not return GTFS trip_id information in either the BusTracker or TrainTracker API, so
|
35
|
+
# it is actually somewhat difficult to associate an API response to a {CTA::Trip}. However, we
|
36
|
+
# know what *should* be happening at any given time. This method attempts a fuzzy find - internally,
|
37
|
+
# we often first try to find the exact Trip that should be happening according to the schedule, and
|
38
|
+
# then failing that we assume that the CTA is running late and look for trips that should have
|
39
|
+
# ended within the past 90 minutes. This almost always finds something.
|
40
|
+
# That said, however, it means we may sometimes find a {CTA::Trip} that's incorrect. In practical usage
|
41
|
+
# however, that doesn't matter too much - most Trips for a run service the same stops.
|
42
|
+
# However, to be safe, your program may wish to compare certain other bits of the API responses to ensure
|
43
|
+
# we found something valid. For example, almost all Brown line trains service the same stops, so
|
44
|
+
# finding the wrong Trip doesn't matter too much. However, a handful of Brown line runs throughout the dat
|
45
|
+
# actually change to Orange line trains at Midway - so, you may wish to verify that the destination of the
|
46
|
+
# Trip matches the reported destination of the API.
|
47
|
+
# Suggestions on how to approach this problem are most welcome (as are patches for better behavior).
|
48
|
+
# @param [String] run The run or route to search for
|
49
|
+
# @param [DateTime, String] timestamp The timestamp to search against.
|
50
|
+
# @param [true,false] fuzz Whether or not to do an exact schedule search or a fuzzy search.
|
51
|
+
def self.find_active_run(run, timestamp, fuzz = false, direction = nil)
|
52
|
+
d = timestamp.is_a?(DateTime) ? timestamp : DateTime.parse(timestamp)
|
53
|
+
wday = d.strftime("%A").downcase
|
54
|
+
end_ts = (fuzz ? (d.to_time + (60 * 60 * 6) - (90 * 60)) : d).strftime("%H:%M:%S")
|
55
|
+
Trip.with_sql(<<-SQL)
|
56
|
+
SELECT t.*
|
57
|
+
FROM trips t
|
58
|
+
JOIN stop_times st ON t.trip_id = st.trip_id
|
59
|
+
JOIN calendar c ON t.service_id = c.service_id
|
60
|
+
WHERE t.route_id = '#{run}'
|
61
|
+
#{direction ? " AND t.direction = '#{direction.gsub("bound", "")}'" : '' }
|
62
|
+
AND c.start_date <= '#{d.to_s}'
|
63
|
+
AND c.end_date >= '#{d.to_s}'
|
64
|
+
AND c.#{wday}
|
65
|
+
GROUP BY t.trip_id, st.departure_time
|
66
|
+
HAVING MAX(st.departure_time) >= '#{end_ts}'
|
67
|
+
SQL
|
68
|
+
end
|
69
|
+
|
33
70
|
# Returns predictions for this {CTA::Bus}. Accepts all options for {CTA::BusTracker.predictions!}, and will merge in
|
34
71
|
# it's own vehicle_id or route_id if present.
|
35
72
|
# @param [Hash] options
|
@@ -4,6 +4,8 @@ module CTA
|
|
4
4
|
# GTFS feed, though the CTA does not fully implement the standard.
|
5
5
|
# @note Current columns: [:shape_id, :shape_pt_lat, :shape_pt_lon, :shape_pt_sequence, :shape_dist_traveled]
|
6
6
|
class Shape < Sequel::Model
|
7
|
+
set_primary_key :shape_id
|
8
|
+
|
7
9
|
# @!method shape_id
|
8
10
|
# @return [Integer]
|
9
11
|
# @!method shape_pt_lat
|
@@ -20,5 +22,9 @@ module CTA
|
|
20
22
|
alias_method :sequence, :shape_pt_sequence
|
21
23
|
alias_method :distance, :shape_dist_traveled
|
22
24
|
alias_method :distance_traveled, :shape_dist_traveled
|
25
|
+
|
26
|
+
# @!method trips
|
27
|
+
# @return [Array<CTA::Trip>] All {CTA::Trip}s related to this {CTA::Shape}
|
28
|
+
one_to_many :trips, :key => :shape_id
|
23
29
|
end
|
24
30
|
end
|
@@ -36,6 +36,22 @@ module CTA
|
|
36
36
|
}
|
37
37
|
FRIENDLY_L_ROUTES = Hash[L_ROUTES.values.map { |r| r[:name].downcase.to_sym }.zip(L_ROUTES.keys)]
|
38
38
|
|
39
|
+
ANNOYING_GREEN_RUNS = CTA::DB[:stop_times].with_sql(<<-SQL).select_map(:schd_trip_id)
|
40
|
+
SELECT DISTINCT t.schd_trip_id
|
41
|
+
FROM stop_times st
|
42
|
+
JOIN trips t ON st.trip_id = t.trip_id
|
43
|
+
WHERE t.route_id = 'G'
|
44
|
+
AND st.stop_headsign = ''
|
45
|
+
SQL
|
46
|
+
|
47
|
+
HEADSIGNS = {
|
48
|
+
"54th/Cermak" => "54 / Cermak",
|
49
|
+
"Ashland/63rd" => "Ashland / 63",
|
50
|
+
"UIC-Halsted" => "UIC",
|
51
|
+
"Harlem/Lake" => "Harlem",
|
52
|
+
"95th/Dan Ryan" => "95th",
|
53
|
+
}
|
54
|
+
|
39
55
|
# @!method route_id
|
40
56
|
# @return [String]
|
41
57
|
# @!method service_id
|
@@ -58,6 +74,62 @@ module CTA
|
|
58
74
|
alias_method :scheduled_trip_id, :schd_trip_id
|
59
75
|
alias_method :run, :schd_trip_id
|
60
76
|
|
77
|
+
# Find a {CTA::Trip} that should be happening, given a timestamp and a route or run.
|
78
|
+
# The CTA does not return GTFS trip_id information in either the BusTracker or TrainTracker API, so
|
79
|
+
# it is actually somewhat difficult to associate an API response to a {CTA::Trip}. However, we
|
80
|
+
# know what *should* be happening at any given time. This method attempts a fuzzy find - internally,
|
81
|
+
# we often first try to find the exact Trip that should be happening according to the schedule, and
|
82
|
+
# then failing that we assume that the CTA is running late and look for trips that should have
|
83
|
+
# ended within the past 90 minutes. This almost always finds something.
|
84
|
+
# That said, however, it means we may sometimes find a {CTA::Trip} that's incorrect. In practical usage
|
85
|
+
# however, that doesn't matter too much - most Trips for a run service the same stops.
|
86
|
+
# However, to be safe, your program may wish to compare certain other bits of the API responses to ensure
|
87
|
+
# we found something valid. For example, almost all Brown line trains service the same stops, so
|
88
|
+
# finding the wrong Trip doesn't matter too much. However, a handful of Brown line runs throughout the dat
|
89
|
+
# actually change to Orange line trains at Midway - so, you may wish to verify that the destination of the
|
90
|
+
# Trip matches the reported destination of the API.
|
91
|
+
# Suggestions on how to approach this problem are most welcome (as are patches for better behavior).
|
92
|
+
# @param [String] run The run or route to search for
|
93
|
+
# @param [DateTime, String] timestamp The timestamp to search against.
|
94
|
+
# @param [true,false] fuzz Whether or not to do an exact schedule search or a fuzzy search.
|
95
|
+
# @param [String] direction The stop headsign. Highly recommended to use because otherwise results may be inaccurate.
|
96
|
+
def self.find_active_run(run, timestamp, fuzz = false, direction = nil)
|
97
|
+
# The TrainTracker API sets the stop_headsign to 'Cottage Grove' but GTFS has it as blank...
|
98
|
+
if run == "516"
|
99
|
+
puts run
|
100
|
+
puts timestamp.to_s
|
101
|
+
puts fuzz.inspect
|
102
|
+
puts direction
|
103
|
+
end
|
104
|
+
if ANNOYING_GREEN_RUNS.any? { |r| r =~ /#{run}/ } && false
|
105
|
+
direction_str = <<-EOF
|
106
|
+
AND (st.stop_headsign = '#{(HEADSIGNS[direction] || direction).gsub("'", "''")}'
|
107
|
+
OR (t.route_id = 'G' AND st.stop_headsign = ''))
|
108
|
+
EOF
|
109
|
+
elsif direction
|
110
|
+
direction_str = "AND st.stop_headsign = '#{(HEADSIGNS[direction] || direction).gsub("'", "''")}'"
|
111
|
+
else
|
112
|
+
direction_str = ''
|
113
|
+
end
|
114
|
+
d = timestamp.is_a?(DateTime) ? timestamp : DateTime.parse(timestamp)
|
115
|
+
wday = d.strftime("%A").downcase
|
116
|
+
end_ts = (fuzz ? (d.to_time + (60 * 60 * 6) - (90 * 60)) : d).strftime("%H:%M:%S")
|
117
|
+
Trip.with_sql(<<-SQL)
|
118
|
+
SELECT t.*
|
119
|
+
FROM trips t
|
120
|
+
JOIN stop_times st ON t.trip_id = st.trip_id
|
121
|
+
JOIN calendar c ON t.service_id = c.service_id
|
122
|
+
WHERE t.schd_trip_id = 'R#{run}'
|
123
|
+
#{direction_str}
|
124
|
+
AND c.start_date <= '#{d.to_s}'
|
125
|
+
AND c.end_date >= '#{d.to_s}'
|
126
|
+
AND c.#{wday}
|
127
|
+
GROUP BY t.trip_id
|
128
|
+
HAVING MAX(st.departure_time) >= '#{end_ts}'
|
129
|
+
ORDER BY st.departure_time ASC
|
130
|
+
SQL
|
131
|
+
end
|
132
|
+
|
61
133
|
# Follows a train, using the TrainTracker follow API
|
62
134
|
# @return [CTA::TrainTracker::FollowResponse] A {CTA::TrainTracker::FollowResponse} with predictions for this train
|
63
135
|
def follow!
|
@@ -42,6 +42,10 @@ module CTA
|
|
42
42
|
# @return [Array<CTA::Stop>] All {CTA::Stop}s serviced on this {CTA::Trip}
|
43
43
|
many_to_many :stops, :left_key => :trip_id, :right_key => :stop_id, :join_table => :stop_times
|
44
44
|
|
45
|
+
# @!method shapes
|
46
|
+
# @return [Array<CTA::Shape>] All {CTA::Shape}s related to this {CTA::Trip}
|
47
|
+
many_to_many :shapes, :left_key => :trip_id, :right_key => :shape_id, :join_table => :trips
|
48
|
+
|
45
49
|
# @!method route_id
|
46
50
|
# @return [String]
|
47
51
|
# @!method service_id
|
@@ -63,47 +67,5 @@ module CTA
|
|
63
67
|
alias_method :id, :route_id
|
64
68
|
alias_method :scheduled_trip_id, :schd_trip_id
|
65
69
|
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.
|
85
|
-
def self.find_active_run(run, timestamp, fuzz = false)
|
86
|
-
if self.to_s == "CTA::Train" # This is admittedly hacky.
|
87
|
-
join_str = "WHERE t.schd_trip_id = 'R#{run}'"
|
88
|
-
else
|
89
|
-
join_str = "WHERE t.route_id = '#{run}'"
|
90
|
-
end
|
91
|
-
d = timestamp.is_a?(DateTime) ? timestamp : DateTime.parse(timestamp)
|
92
|
-
wday = d.strftime("%A").downcase
|
93
|
-
end_ts = (fuzz ? (d.to_time + (60 * 60 * 6) - (90 * 60)) : d).strftime("%H:%M:%S")
|
94
|
-
Trip.with_sql(<<-SQL)
|
95
|
-
SELECT t.*
|
96
|
-
FROM trips t
|
97
|
-
JOIN stop_times st ON t.trip_id = st.trip_id
|
98
|
-
JOIN calendar c ON t.service_id = c.service_id
|
99
|
-
#{join_str}
|
100
|
-
AND c.start_date <= '#{d.to_s}'
|
101
|
-
AND c.end_date >= '#{d.to_s}'
|
102
|
-
AND c.#{wday}
|
103
|
-
GROUP BY t.trip_id, st.departure_time
|
104
|
-
HAVING MAX(st.departure_time) >= '#{end_ts}'
|
105
|
-
SQL
|
106
|
-
end
|
107
|
-
|
108
70
|
end
|
109
71
|
end
|
data/lib/cta_redux/version.rb
CHANGED
data/spec/train_tracker_spec.rb
CHANGED
@@ -60,11 +60,18 @@ RSpec.describe CTA::TrainTracker do
|
|
60
60
|
expect(response.routes.size).to eq(8)
|
61
61
|
expect(response.trains.size).to eq(80)
|
62
62
|
|
63
|
-
expect(response.trains.last.route.route_id).to eq("
|
63
|
+
expect(response.trains.last.route.route_id).to eq("P")
|
64
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)
|
68
68
|
end
|
69
69
|
end
|
70
|
+
|
71
|
+
describe "bugs" do
|
72
|
+
it "uses proper station IDs for Green line runs, which are wrong from the CTA GTFS feed" do
|
73
|
+
trip = CTA::Trip[47085599996]
|
74
|
+
expect(trip.stops.map(&:stop_id)).to include(30033)
|
75
|
+
end
|
76
|
+
end
|
70
77
|
end
|
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.3.
|
4
|
+
version: 0.3.4
|
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-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|