gtfs-realtime 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05148a6c6a4925c9072942c4ab6a57c1d8ddda4b
4
- data.tar.gz: 072e0487c28f7f7ff2483bdeac72f006dc507f8a
3
+ metadata.gz: c4fb01e9aa3b3d57def85c0ebdbda36acbc4c9ab
4
+ data.tar.gz: d3bbf13907cd3b09a0e55cada1aa16dae4ec09c9
5
5
  SHA512:
6
- metadata.gz: c2bd4c6f34c66db13ddc5eb56f27aa7238c1dc68208ce8a7ba027fb542b1b907539ebef06fccc7b042450361a2ae15e486f64183e4eca6caabf900711ca7b7e2
7
- data.tar.gz: 29ec92ae65ecbef01d633610eea3594669a32510985089f8e7675f9b82acdf37dd7b2953f3e11cfe9683eed5bf1b944df55f0ca0247e1171fb707b6ca64e80a0
6
+ metadata.gz: bfc85354de0492acac1c60bf85a1d9085b170f502f57b3f32364b06eaa8fca59237bfee4f51b00e8ea8cac52a6d49ccce18ac90f1724aa1cbad80e106276934c
7
+ data.tar.gz: ff2b51ad9471a0f11ca788363f8968446504625b6f5a0538302dbe362d37e7954705014c5f8373d4c366f9dbb683d14a68418bc03f9fd2755a308e9d95288003
data/README.md CHANGED
@@ -30,14 +30,16 @@ GTFS::Realtime.configure do |config|
30
30
  config.trip_updates_feed = "http://realtime.ripta.com:81/api/tripupdates"
31
31
  config.vehicle_positions_feed = "http://realtime.ripta.com:81/api/vehiclepositions"
32
32
  config.service_alerts_feed = "http://realtime.ripta.com:81/api/servicealerts"
33
- config.database_path = "sqlite://database.db" # leave unset to use an in-memory database
33
+ config.database_url = "sqlite3:////Users/rofreg/database.db"
34
+ # leave database_url unset to use your existing ActiveRecord DB, or
35
+ # set it to `nil` to use an in-memory SQLite database
34
36
  end
35
37
 
36
38
  # After calling 'configure', the gem loads all relevant GTFS info into a database.
37
39
  # This may take some time (up to a minute) depending on the size of the input data.
38
40
  # By default, gtfs-realtime uses an in-memory database, which requires reloading all
39
41
  # data from scratch on each launch. If you'd like to use a persistent database instead,
40
- # set 'config.database_path' above, and include a scheme/protocol path for the DB type
42
+ # set 'config.database_url' above, and include a scheme/protocol path for the DB type
41
43
  # that you would like to use. gtfs-realtime will generate the relevant tables.
42
44
 
43
45
  @nearby = GTFS::Realtime::Stop.nearby(41.834521, -71.396906)
@@ -3,6 +3,7 @@
3
3
  require "bundler/setup"
4
4
  require "gtfs/realtime"
5
5
  require "sqlite3"
6
+ require "uri"
6
7
 
7
8
  # Load the RIPTA feed as an example, with a sqlite3 database
8
9
  GTFS::Realtime.configure do |config|
@@ -10,7 +11,7 @@ GTFS::Realtime.configure do |config|
10
11
  config.trip_updates_feed = "http://realtime.ripta.com:81/api/tripupdates"
11
12
  config.vehicle_positions_feed = "http://realtime.ripta.com:81/api/vehiclepositions"
12
13
  config.service_alerts_feed = "http://realtime.ripta.com:81/api/servicealerts"
13
- config.database_path = "sqlite://database.db"
14
+ config.database_url = "sqlite3:///#{URI.escape(File.expand_path("../../database.db", __FILE__))}"
14
15
  end
15
16
 
16
17
  def reload!
@@ -29,5 +29,6 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency "gtfs-realtime-bindings"
31
31
  spec.add_dependency "gtfs"
32
- spec.add_dependency "sequel"
32
+ spec.add_dependency "activerecord"
33
+ spec.add_dependency "bulk_insert"
33
34
  end
@@ -1,8 +1,22 @@
1
1
  require "google/transit/gtfs-realtime.pb"
2
2
  require "gtfs"
3
- require "sequel"
3
+ require "active_record"
4
+ require "bulk_insert"
5
+ require "gtfs/gtfs_gem_patch"
4
6
 
5
7
  require "gtfs/realtime/configuration"
8
+ require "gtfs/realtime/model"
9
+ require "gtfs/realtime/calendar_date"
10
+ require "gtfs/realtime/route"
11
+ require "gtfs/realtime/service_alert"
12
+ require "gtfs/realtime/shape"
13
+ require "gtfs/realtime/stop"
14
+ require "gtfs/realtime/stop_time"
15
+ require "gtfs/realtime/stop_time_update"
16
+ require "gtfs/realtime/trip"
17
+ require "gtfs/realtime/trip_update"
18
+ require "gtfs/realtime/vehicle_position"
19
+ require "gtfs/realtime/version"
6
20
 
7
21
  module GTFS
8
22
  class Realtime
@@ -17,9 +31,174 @@ module GTFS
17
31
  def configure
18
32
  yield(configuration)
19
33
 
34
+ run_migrations
20
35
  load_static_feed!
21
36
  refresh_realtime_feed!
22
37
  end
38
+
39
+ def load_static_feed!(force: false)
40
+ return if !force && GTFS::Realtime::Route.count > 0
41
+
42
+ static_data = GTFS::Source.build(@configuration.static_feed)
43
+ return unless static_data
44
+
45
+ GTFS::Realtime::Model.transaction do
46
+ GTFS::Realtime::CalendarDate.delete_all
47
+ GTFS::Realtime::CalendarDate.bulk_insert(values:
48
+ static_data.calendar_dates.collect do |calendar_date|
49
+ {
50
+ service_id: calendar_date.service_id.strip,
51
+ date: Date.strptime(calendar_date.date, "%Y%m%d"),
52
+ exception_type: calendar_date.exception_type
53
+ }
54
+ end
55
+ )
56
+
57
+ GTFS::Realtime::Route.delete_all
58
+ GTFS::Realtime::Route.bulk_insert(:id, :short_name, :long_name, :url, values:
59
+ static_data.routes.collect do |route|
60
+ {
61
+ id: route.id.strip,
62
+ short_name: route.short_name,
63
+ long_name: route.long_name,
64
+ url: route.url
65
+ }
66
+ end
67
+ )
68
+
69
+ GTFS::Realtime::Shape.delete_all
70
+ GTFS::Realtime::Shape.bulk_insert(:id, :sequence, :latitude, :longitude, values:
71
+ static_data.shapes.collect do |shape|
72
+ {
73
+ id: shape.id.strip,
74
+ sequence: shape.pt_sequence,
75
+ latitude: shape.pt_lat.to_f,
76
+ longitude: shape.pt_lon.to_f
77
+ }
78
+ end
79
+ )
80
+
81
+ GTFS::Realtime::Stop.delete_all
82
+ GTFS::Realtime::Stop.bulk_insert(:id, :name, :latitude, :longitude, values:
83
+ static_data.stops.collect do |stop|
84
+ {
85
+ id: stop.id.strip,
86
+ name: stop.name.strip,
87
+ latitude: stop.lat.to_f,
88
+ longitude: stop.lon.to_f
89
+ }
90
+ end
91
+ )
92
+
93
+ GTFS::Realtime::StopTime.delete_all
94
+ GTFS::Realtime::StopTime.bulk_insert(values:
95
+ static_data.stop_times.collect do |stop_time|
96
+ {
97
+ stop_id: stop_time.stop_id.strip,
98
+ trip_id: stop_time.trip_id.strip,
99
+ arrival_time: stop_time.arrival_time,
100
+ departure_time: stop_time.departure_time,
101
+ stop_sequence: stop_time.stop_sequence.to_i
102
+ }
103
+ end
104
+ )
105
+
106
+ GTFS::Realtime::Trip.delete_all
107
+ GTFS::Realtime::Trip.bulk_insert(:id, :headsign, :route_id, :service_id, :shape_id, :direction_id, values:
108
+ static_data.trips.collect do |trip|
109
+ {
110
+ id: trip.id.strip,
111
+ headsign: trip.headsign.strip,
112
+ route_id: trip.route_id.strip,
113
+ service_id: trip.service_id.strip,
114
+ shape_id: trip.shape_id.strip,
115
+ direction_id: trip.direction_id
116
+ }
117
+ end
118
+ )
119
+ end
120
+ end
121
+
122
+ def refresh_realtime_feed!
123
+ trip_updates = get_entities(@configuration.trip_updates_feed)
124
+ vehicle_positions = get_entities(@configuration.vehicle_positions_feed)
125
+ service_alerts = get_entities(@configuration.service_alerts_feed)
126
+
127
+ GTFS::Realtime::Model.transaction do
128
+ GTFS::Realtime::TripUpdate.delete_all
129
+ GTFS::Realtime::TripUpdate.bulk_insert(:id, :trip_id, :route_id, values:
130
+ trip_updates.collect do |trip_update|
131
+ {
132
+ id: trip_update.id.strip,
133
+ trip_id: trip_update.trip_update.trip.trip_id.strip,
134
+ route_id: trip_update.trip_update.trip.route_id.strip
135
+ }
136
+ end
137
+ )
138
+
139
+ GTFS::Realtime::StopTimeUpdate.delete_all
140
+ GTFS::Realtime::StopTimeUpdate.bulk_insert(values:
141
+ trip_updates.collect do |trip_update|
142
+ trip_update.trip_update.stop_time_update.collect do |stop_time_update|
143
+ {
144
+ trip_update_id: trip_update.id.strip,
145
+ stop_id: stop_time_update.stop_id.strip,
146
+ arrival_delay: stop_time_update.arrival ? stop_time_update.arrival.delay : nil,
147
+ arrival_time: stop_time_update.arrival ? Time.at(stop_time_update.arrival.time) : nil,
148
+ departure_delay: stop_time_update.departure ? stop_time_update.departure.delay : nil,
149
+ departure_time: stop_time_update.departure ? Time.at(stop_time_update.departure.time) : nil,
150
+ }
151
+ end
152
+ end.flatten
153
+ )
154
+
155
+ GTFS::Realtime::VehiclePosition.delete_all
156
+ GTFS::Realtime::VehiclePosition.bulk_insert(values:
157
+ vehicle_positions.collect do |vehicle|
158
+ {
159
+ trip_id: vehicle.vehicle.trip.trip_id.strip,
160
+ stop_id: vehicle.vehicle.stop_id.strip,
161
+ latitude: vehicle.vehicle.position.latitude.to_f,
162
+ longitude: vehicle.vehicle.position.longitude.to_f,
163
+ bearing: vehicle.vehicle.position.bearing.to_f,
164
+ timestamp: Time.at(vehicle.vehicle.timestamp)
165
+ }
166
+ end
167
+ )
168
+
169
+ GTFS::Realtime::ServiceAlert.delete_all
170
+ GTFS::Realtime::ServiceAlert.bulk_insert(values:
171
+ service_alerts.collect do |service_alert|
172
+ {
173
+ stop_id: service_alert.alert.informed_entity.first.stop_id.strip,
174
+ header_text: service_alert.alert.header_text.translation.first.text,
175
+ description_text: service_alert.alert.description_text.translation.first.text,
176
+ start_time: Time.at(service_alert.alert.active_period.first.start),
177
+ end_time: Time.at(service_alert.alert.active_period.first.end)
178
+ }
179
+ end
180
+ )
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def get_entities(path)
187
+ return [] if path.nil?
188
+
189
+ if File.exists?(path)
190
+ data = File.open(path, 'r'){|f| f.read}
191
+ else
192
+ data = Net::HTTP.get(URI.parse(path))
193
+ end
194
+ feed = Transit_realtime::FeedMessage.decode(data)
195
+ feed.entity # array of entities
196
+ end
197
+
198
+ def run_migrations
199
+ ActiveRecord::Migration.verbose = false
200
+ ActiveRecord::Migrator.migrate(File.expand_path("../realtime/migrations", __FILE__))
201
+ end
23
202
  end
24
203
  end
25
204
  end
@@ -4,7 +4,7 @@ module GTFS
4
4
  ADDED = 1
5
5
  REMOVED = 2
6
6
 
7
- many_to_one :trip, primary_key: :service_id, key: :service_id
7
+ belongs_to :trip, primary_key: :service_id, foreign_key: :service_id
8
8
  end
9
9
  end
10
10
  end
@@ -1,17 +1,16 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class Configuration
4
- attr_accessor :static_feed, :trip_updates_feed, :vehicle_positions_feed, :service_alerts_feed, :database_path
4
+ attr_accessor :static_feed, :trip_updates_feed, :vehicle_positions_feed, :service_alerts_feed, :database_url
5
5
 
6
- def database_path=(new_path)
7
- @database_path = new_path
6
+ def database_url=(new_path)
7
+ @database_url = new_path
8
8
 
9
- # now that we know the DB path, we can initialize the database
10
- require 'gtfs/realtime/database'
11
- GTFS::Realtime::Database.path = database_path
12
-
13
- # now that we have a database, initialize all the other models
14
- require 'gtfs/realtime/bootstrap'
9
+ if @database_url
10
+ ActiveRecord::Base.establish_connection(@database_url)
11
+ else
12
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
13
+ end
15
14
  end
16
15
  end
17
16
  end
@@ -0,0 +1,81 @@
1
+ class CreateGtfsTables < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :gtfs_realtime_calendar_dates, id: false do |t|
4
+ t.string :service_id, index: true
5
+ t.date :date
6
+ t.integer :exception_type
7
+ end
8
+
9
+ create_table :gtfs_realtime_routes do |t|
10
+ t.string :short_name
11
+ t.string :long_name
12
+ t.string :url
13
+ end
14
+ change_column :gtfs_realtime_routes, :id, :string
15
+
16
+ create_table :gtfs_realtime_shapes, id: false do |t|
17
+ t.string :id # NOT unique
18
+ t.integer :sequence
19
+ t.float :latitude
20
+ t.float :longitude
21
+
22
+ t.index [:id, :sequence]
23
+ end
24
+
25
+ create_table :gtfs_realtime_stops do |t|
26
+ t.string :name
27
+ t.float :latitude
28
+ t.float :longitude
29
+ end
30
+ change_column :gtfs_realtime_stops, :id, :string
31
+
32
+ create_table :gtfs_realtime_stop_times, id: false do |t|
33
+ t.string :trip_id, index: true
34
+ t.string :stop_id, index: true
35
+ t.string :arrival_time
36
+ t.string :departure_time
37
+ t.integer :stop_sequence
38
+ end
39
+
40
+ create_table :gtfs_realtime_trips do |t|
41
+ t.string :headsign
42
+ t.string :route_id, index: true
43
+ t.string :service_id
44
+ t.string :shape_id
45
+ t.integer :direction_id
46
+ end
47
+ change_column :gtfs_realtime_trips, :id, :string
48
+
49
+ create_table :gtfs_realtime_trip_updates do |t|
50
+ t.string :trip_id
51
+ t.string :route_id
52
+ end
53
+ change_column :gtfs_realtime_trip_updates, :id, :string
54
+
55
+ create_table :gtfs_realtime_stop_time_updates, id: false do |t|
56
+ t.string :trip_update_id, index: true
57
+ t.string :stop_id, index: true
58
+ t.integer :arrival_delay
59
+ t.timestamp :arrival_time
60
+ t.integer :departure_delay
61
+ t.timestamp :departure_time
62
+ end
63
+
64
+ create_table :gtfs_realtime_vehicle_positions, id: false do |t|
65
+ t.string :trip_id, index: true
66
+ t.string :stop_id, index: true
67
+ t.float :latitude
68
+ t.float :longitude
69
+ t.float :bearing
70
+ t.timestamp :timestamp
71
+ end
72
+
73
+ create_table :gtfs_realtime_service_alerts, id: false do |t|
74
+ t.string :stop_id, index: true
75
+ t.string :header_text
76
+ t.text :description_text
77
+ t.timestamp :start_time
78
+ t.timestamp :end_time
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,8 @@
1
+ module GTFS
2
+ class Realtime
3
+ class Model < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ self.table_name_prefix = "gtfs_realtime_"
6
+ end
7
+ end
8
+ end
@@ -1,7 +1,7 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class ServiceAlert < GTFS::Realtime::Model
4
- many_to_one :stop
4
+ belongs_to :stop
5
5
  end
6
6
  end
7
7
  end
@@ -1,14 +1,7 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class Shape < GTFS::Realtime::Model
4
- dataset_module do
5
- def ordered_by_sequence
6
- order(:sequence)
7
- end
8
- end
9
-
10
- # order results by sequence by default
11
- set_dataset(self.ordered_by_sequence)
4
+ scope :ordered, -> { order(sequence: :ASC) }
12
5
  end
13
6
  end
14
7
  end
@@ -5,24 +5,16 @@ module GTFS
5
5
  class Stop < GTFS::Realtime::Model
6
6
  include GTFS::Realtime::Nearby
7
7
 
8
- one_to_many :service_alerts
9
- one_to_many :stop_times
10
- one_to_many :stop_time_updates
11
- many_to_many :trip_updates, join_table: :gtfs_realtime_stop_time_updates
12
- many_to_many :trips, join_table: :gtfs_realtime_stop_times
13
- many_through_many :routes, through: [
14
- [:stop_times, :stop_id, :trip_id],
15
- [:trips, :id, :route_id]
16
- ]
17
- many_through_many :active_routes, class: GTFS::Realtime::Route, through: [
18
- [:stop_time_updates, :stop_id, :trip_update_id],
19
- [:trip_updates, :id, :route_id]
20
- ]
8
+ has_many :service_alerts
9
+ has_many :stop_times
10
+ has_many :stop_time_updates
11
+ has_many :trips, through: :stop_times
12
+ has_many :trip_updates, through: :stop_times
13
+ has_many :routes, through: :trips
14
+ has_many :active_routes, through: :trip_updates, source: :route
21
15
 
22
16
  def stop_times_schedule_for(date)
23
- # TODO: .all.first is a weird syntax to do eager loading correctly. Maybe there's a better way?
24
- self_with_eager_loads = GTFS::Realtime::Stop.where(id: id).eager(stop_times: {trip: [:calendar_dates, :route, :shapes]}).all.first
25
- self_with_eager_loads.stop_times.select{|st| st.trip.active?(Date.today)}.sort_by{|st| st.departure_time}
17
+ stop_times.includes(trip: [:calendar_dates, :route, :shapes]).select{|st| st.trip.active?(date)}.sort_by{|st| st.departure_time}
26
18
  end
27
19
 
28
20
  def stop_times_for_today
@@ -1,8 +1,9 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class StopTime < GTFS::Realtime::Model
4
- many_to_one :trip
5
- many_to_one :stop
4
+ belongs_to :trip
5
+ belongs_to :trip_update, primary_key: :trip_id, foreign_key: :trip_id
6
+ belongs_to :stop
6
7
 
7
8
  attr_accessor :actual_arrival_time, :actual_arrival_delay, :actual_departure_time, :actual_departure_delay
8
9
 
@@ -38,7 +39,6 @@ module GTFS
38
39
  private
39
40
 
40
41
  def self.parse_time(time, date = Date.today)
41
- # TODO: handle case where date != Date.today
42
42
  day_adjustment = 0
43
43
  hour = time[0...2].to_i
44
44
 
@@ -48,7 +48,7 @@ module GTFS
48
48
  time[0...2] = (hour % 24).to_s.rjust(2, '0')
49
49
  end
50
50
 
51
- Time.parse(time) + day_adjustment * 60 * 60 * 24
51
+ Time.parse("#{date} #{time}").in_time_zone(Time.zone) + day_adjustment * 60 * 60 * 24
52
52
  end
53
53
  end
54
54
  end
@@ -1,10 +1,18 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class StopTimeUpdate < GTFS::Realtime::Model
4
- one_through_one :route, join_table: :gtfs_realtime_trip_updates, left_key: :id, left_primary_key: :trip_update_id, right_key: :route_id
5
- many_to_one :stop
6
- one_through_one :trip, join_table: :gtfs_realtime_trip_updates, left_key: :id, left_primary_key: :trip_update_id, right_key: :trip_id
7
- many_to_one :trip_update
4
+ belongs_to :stop
5
+ belongs_to :trip_update
6
+ has_one :trip, through: :trip_update
7
+ has_one :route, through: :trip_update
8
+
9
+ def arrival_time
10
+ super ? super.in_time_zone(Time.zone) : nil
11
+ end
12
+
13
+ def departure_time
14
+ super ? super.in_time_zone(Time.zone) : nil
15
+ end
8
16
  end
9
17
  end
10
18
  end
@@ -1,14 +1,14 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class Trip < GTFS::Realtime::Model
4
- many_to_one :route
5
- many_to_many :stops, join_table: :gtfs_realtime_stop_times
6
- one_to_many :calendar_dates, primary_key: :service_id, key: :service_id
7
- one_to_many :shapes, primary_key: :shape_id, key: :id
4
+ belongs_to :route
5
+ has_many :stop_times
6
+ has_many :stops, through: :stop_times
7
+ has_many :calendar_dates, primary_key: :service_id, foreign_key: :service_id
8
+ has_many :shapes, primary_key: :shape_id, foreign_key: :id
8
9
 
9
10
  def active?(date)
10
- # can't use .where chaining b/c Sequel is weird
11
- calendar_dates.find{|cd| cd.exception_type == GTFS::Realtime::CalendarDate::ADDED && cd.date == date}
11
+ calendar_dates.where(exception_type: GTFS::Realtime::CalendarDate::ADDED, date: date).any?
12
12
  end
13
13
  end
14
14
  end
@@ -1,8 +1,8 @@
1
1
  module GTFS
2
2
  class Realtime
3
3
  class TripUpdate < GTFS::Realtime::Model
4
- many_to_one :trip
5
- many_to_one :route
4
+ belongs_to :trip
5
+ belongs_to :route
6
6
  end
7
7
  end
8
8
  end
@@ -5,8 +5,8 @@ module GTFS
5
5
  class VehiclePosition < GTFS::Realtime::Model
6
6
  include GTFS::Realtime::Nearby
7
7
 
8
- many_to_one :stop
9
- many_to_one :trip
8
+ belongs_to :stop
9
+ belongs_to :trip
10
10
  end
11
11
  end
12
12
  end
@@ -1,5 +1,5 @@
1
1
  module GTFS
2
2
  class Realtime
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gtfs-realtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Laughlin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-26 00:00:00.000000000 Z
11
+ date: 2016-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -109,7 +109,21 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: sequel
112
+ name: activerecord
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: bulk_insert
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -143,10 +157,10 @@ files:
143
157
  - gtfs-realtime.gemspec
144
158
  - lib/gtfs/gtfs_gem_patch.rb
145
159
  - lib/gtfs/realtime.rb
146
- - lib/gtfs/realtime/bootstrap.rb
147
160
  - lib/gtfs/realtime/calendar_date.rb
148
161
  - lib/gtfs/realtime/configuration.rb
149
- - lib/gtfs/realtime/database.rb
162
+ - lib/gtfs/realtime/migrations/001_create_gtfs_tables.rb
163
+ - lib/gtfs/realtime/model.rb
150
164
  - lib/gtfs/realtime/nearby.rb
151
165
  - lib/gtfs/realtime/route.rb
152
166
  - lib/gtfs/realtime/service_alert.rb
@@ -1,178 +0,0 @@
1
- require "gtfs/gtfs_gem_patch"
2
- require "gtfs/realtime/calendar_date"
3
- require "gtfs/realtime/route"
4
- require "gtfs/realtime/service_alert"
5
- require "gtfs/realtime/shape"
6
- require "gtfs/realtime/stop"
7
- require "gtfs/realtime/stop_time"
8
- require "gtfs/realtime/stop_time_update"
9
- require "gtfs/realtime/trip"
10
- require "gtfs/realtime/trip_update"
11
- require "gtfs/realtime/vehicle_position"
12
- require "gtfs/realtime/version"
13
-
14
- module GTFS
15
- class Realtime
16
- # This is a singleton object, so everything will be on the class level
17
- class << self
18
- def load_static_feed!(force: false)
19
- return if !force && GTFS::Realtime::Route.count > 0
20
-
21
- static_data = GTFS::Source.build(@configuration.static_feed)
22
- return unless static_data
23
-
24
- GTFS::Realtime::Model.db.transaction do
25
- GTFS::Realtime::CalendarDate.dataset.delete
26
- GTFS::Realtime::CalendarDate.multi_insert(
27
- static_data.calendar_dates.collect do |calendar_date|
28
- {
29
- service_id: calendar_date.service_id.strip,
30
- date: Date.strptime(calendar_date.date, "%Y%m%d"),
31
- exception_type: calendar_date.exception_type
32
- }
33
- end
34
- )
35
-
36
- GTFS::Realtime::Route.dataset.delete
37
- GTFS::Realtime::Route.multi_insert(
38
- static_data.routes.collect do |route|
39
- {
40
- id: route.id.strip,
41
- short_name: route.short_name,
42
- long_name: route.long_name,
43
- url: route.url
44
- }
45
- end
46
- )
47
-
48
- GTFS::Realtime::Shape.dataset.delete
49
- GTFS::Realtime::Shape.multi_insert(
50
- static_data.shapes.collect do |shape|
51
- {
52
- id: shape.id.strip,
53
- sequence: shape.pt_sequence,
54
- latitude: shape.pt_lat.to_f,
55
- longitude: shape.pt_lon.to_f
56
- }
57
- end
58
- )
59
-
60
- GTFS::Realtime::Stop.dataset.delete
61
- GTFS::Realtime::Stop.multi_insert(
62
- static_data.stops.collect do |stop|
63
- {
64
- id: stop.id.strip,
65
- name: stop.name.strip,
66
- latitude: stop.lat.to_f,
67
- longitude: stop.lon.to_f
68
- }
69
- end
70
- )
71
-
72
- GTFS::Realtime::StopTime.dataset.delete
73
- GTFS::Realtime::StopTime.multi_insert(
74
- static_data.stop_times.collect do |stop_time|
75
- {
76
- stop_id: stop_time.stop_id.strip,
77
- trip_id: stop_time.trip_id.strip,
78
- arrival_time: stop_time.arrival_time,
79
- departure_time: stop_time.departure_time,
80
- stop_sequence: stop_time.stop_sequence.to_i
81
- }
82
- end
83
- )
84
-
85
- GTFS::Realtime::Trip.dataset.delete
86
- GTFS::Realtime::Trip.multi_insert(
87
- static_data.trips.collect do |trip|
88
- {
89
- id: trip.id.strip,
90
- headsign: trip.headsign.strip,
91
- route_id: trip.route_id.strip,
92
- service_id: trip.service_id.strip,
93
- shape_id: trip.shape_id.strip,
94
- direction_id: trip.direction_id
95
- }
96
- end
97
- )
98
- end
99
- end
100
-
101
- def refresh_realtime_feed!
102
- trip_updates = get_entities(@configuration.trip_updates_feed)
103
- vehicle_positions = get_entities(@configuration.vehicle_positions_feed)
104
- service_alerts = get_entities(@configuration.service_alerts_feed)
105
-
106
- GTFS::Realtime::Model.db.transaction do
107
- GTFS::Realtime::TripUpdate.dataset.delete
108
- GTFS::Realtime::TripUpdate.multi_insert(
109
- trip_updates.collect do |trip_update|
110
- {
111
- id: trip_update.id.strip,
112
- trip_id: trip_update.trip_update.trip.trip_id.strip,
113
- route_id: trip_update.trip_update.trip.route_id.strip
114
- }
115
- end
116
- )
117
-
118
- GTFS::Realtime::StopTimeUpdate.dataset.delete
119
- GTFS::Realtime::StopTimeUpdate.multi_insert(
120
- trip_updates.collect do |trip_update|
121
- trip_update.trip_update.stop_time_update.collect do |stop_time_update|
122
- {
123
- trip_update_id: trip_update.id.strip,
124
- stop_id: stop_time_update.stop_id.strip,
125
- arrival_delay: stop_time_update.arrival ? stop_time_update.arrival.delay : nil,
126
- arrival_time: stop_time_update.arrival ? Time.at(stop_time_update.arrival.time) : nil,
127
- departure_delay: stop_time_update.departure ? stop_time_update.departure.delay : nil,
128
- departure_time: stop_time_update.departure ? Time.at(stop_time_update.departure.time) : nil,
129
- }
130
- end
131
- end.flatten
132
- )
133
-
134
- GTFS::Realtime::VehiclePosition.dataset.delete
135
- GTFS::Realtime::VehiclePosition.multi_insert(
136
- vehicle_positions.collect do |vehicle|
137
- {
138
- trip_id: vehicle.vehicle.trip.trip_id.strip,
139
- stop_id: vehicle.vehicle.stop_id.strip,
140
- latitude: vehicle.vehicle.position.latitude.to_f,
141
- longitude: vehicle.vehicle.position.longitude.to_f,
142
- bearing: vehicle.vehicle.position.bearing.to_f,
143
- timestamp: Time.at(vehicle.vehicle.timestamp)
144
- }
145
- end
146
- )
147
-
148
- GTFS::Realtime::ServiceAlert.dataset.delete
149
- GTFS::Realtime::ServiceAlert.multi_insert(
150
- service_alerts.collect do |service_alert|
151
- {
152
- stop_id: service_alert.alert.informed_entity.first.stop_id.strip,
153
- header_text: service_alert.alert.header_text.translation.first.text,
154
- description_text: service_alert.alert.description_text.translation.first.text,
155
- start_time: Time.at(service_alert.alert.active_period.first.start),
156
- end_time: Time.at(service_alert.alert.active_period.first.end)
157
- }
158
- end
159
- )
160
- end
161
- end
162
-
163
- private
164
-
165
- def get_entities(path)
166
- return [] if path.nil?
167
-
168
- if File.exists?(path)
169
- data = File.open(path, 'r'){|f| f.read}
170
- else
171
- data = Net::HTTP.get(URI.parse(path))
172
- end
173
- feed = Transit_realtime::FeedMessage.decode(data)
174
- feed.entity # array of entities
175
- end
176
- end
177
- end
178
- end
@@ -1,140 +0,0 @@
1
- module GTFS
2
- class Realtime
3
- class Database
4
- class << self
5
- attr_writer :database_path
6
-
7
- def path=(new_path)
8
- @path = new_path
9
-
10
- # This script sets up an in-memory DB so that it can be used by this gem.
11
- # It also extends Sequel::Model so that Sequel may be used independently by
12
- # the parent project if desired.
13
- db = Sequel.connect(new_path || "sqlite://")
14
-
15
- # Set up all database tables
16
- db.create_table? :gtfs_realtime_calendar_dates do
17
- String :service_id
18
- Date :date
19
- Integer :exception_type
20
-
21
- index :service_id
22
- end
23
-
24
- db.create_table? :gtfs_realtime_routes do
25
- String :id, primary_key: true
26
- String :short_name
27
- String :long_name
28
- String :url
29
-
30
- index :id
31
- end
32
-
33
- db.create_table? :gtfs_realtime_shapes do
34
- String :id
35
- Integer :sequence
36
- Float :latitude
37
- Float :longitude
38
-
39
- index :id
40
- end
41
-
42
- db.create_table? :gtfs_realtime_stops do
43
- String :id, primary_key: true
44
- String :name
45
- Float :latitude
46
- Float :longitude
47
-
48
- index :id
49
- end
50
-
51
- db.create_table? :gtfs_realtime_stop_times do
52
- String :trip_id
53
- String :stop_id
54
- String :arrival_time
55
- String :departure_time
56
- Integer :stop_sequence
57
-
58
- index :trip_id
59
- index :stop_id
60
- end
61
-
62
- db.create_table? :gtfs_realtime_trips do
63
- String :id, primary_key: true
64
- String :headsign
65
- String :route_id
66
- String :service_id
67
- String :shape_id
68
- Integer :direction_id
69
-
70
- index :id
71
- index :route_id
72
- end
73
-
74
- db.create_table? :gtfs_realtime_trip_updates do
75
- String :id, primary_key: true
76
- String :trip_id
77
- String :route_id
78
-
79
- index :id
80
- end
81
-
82
- db.create_table? :gtfs_realtime_stop_time_updates do
83
- String :trip_update_id
84
- String :stop_id
85
- Integer :arrival_delay
86
- Time :arrival_time
87
- Integer :departure_delay
88
- Time :departure_time
89
-
90
- index :trip_update_id
91
- index :stop_id
92
- end
93
-
94
- db.create_table? :gtfs_realtime_vehicle_positions do
95
- String :trip_id
96
- String :stop_id
97
- Float :latitude
98
- Float :longitude
99
- Float :bearing
100
- Time :timestamp
101
-
102
- index :trip_id
103
- index :stop_id
104
- end
105
-
106
- db.create_table? :gtfs_realtime_service_alerts do
107
- String :stop_id
108
- String :header_text
109
- Text :description_text
110
- Time :start_time
111
- Time :end_time
112
-
113
- index :stop_id
114
- end
115
-
116
- # Set up all gtfs-realtime models to use this database
117
- model_classes.each do |model_class|
118
- model_class.db = db
119
- end
120
- end
121
-
122
- def model_classes
123
- ObjectSpace.each_object(::Class).select{|klass| klass <= GTFS::Realtime::Model}
124
- end
125
- end
126
- end
127
- end
128
- end
129
-
130
- # If we have not defined our model parent class yet, initialize it.
131
- if !defined?(GTFS::Realtime::Model)
132
- GTFS::Realtime::Model = Class.new(Sequel::Model)
133
- GTFS::Realtime::Model.plugin :many_through_many
134
-
135
- class GTFS::Realtime::Model
136
- def self.implicit_table_name
137
- "gtfs_realtime_#{super}".to_sym
138
- end
139
- end
140
- end