gtfs-reader 0.2.2
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 +7 -0
- data/LICENSE +284 -0
- data/README.md +57 -0
- data/Rakefile +53 -0
- data/lib/gtfs_reader.rb +7 -0
- data/lib/gtfs_reader/bulk_feed_handler.rb +94 -0
- data/lib/gtfs_reader/config/column.rb +72 -0
- data/lib/gtfs_reader/config/defaults/gtfs_feed_definition.rb +208 -0
- data/lib/gtfs_reader/config/feed_definition.rb +55 -0
- data/lib/gtfs_reader/config/file_definition.rb +111 -0
- data/lib/gtfs_reader/config/prefixed_column_setter.rb +26 -0
- data/lib/gtfs_reader/config/source.rb +61 -0
- data/lib/gtfs_reader/config/sources.rb +25 -0
- data/lib/gtfs_reader/configuration.rb +27 -0
- data/lib/gtfs_reader/core.rb +76 -0
- data/lib/gtfs_reader/exceptions.rb +29 -0
- data/lib/gtfs_reader/feed_handler.rb +30 -0
- data/lib/gtfs_reader/file_reader.rb +120 -0
- data/lib/gtfs_reader/file_row.rb +66 -0
- data/lib/gtfs_reader/log.rb +66 -0
- data/lib/gtfs_reader/source_updater.rb +130 -0
- data/lib/gtfs_reader/version.rb +71 -0
- metadata +195 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module GtfsReader
|
5
|
+
module Log
|
6
|
+
class << self
|
7
|
+
def debug(*args, &block); log :debug, *args, &block end
|
8
|
+
def info(*args, &block); log :info, *args, &block end
|
9
|
+
def warn(*args, &block); log :warn, *args, &block end
|
10
|
+
def error(*args, &block); log :error, *args, &block end
|
11
|
+
def fatal(*args, &block); log :fatal, *args, &block end
|
12
|
+
|
13
|
+
def log(level, *args, &block)
|
14
|
+
logger.send level, *args, &block
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def logger
|
19
|
+
@logger = yield if block_given?
|
20
|
+
@logger ||= create_logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def level=(lev)
|
24
|
+
logger.level =
|
25
|
+
case lev
|
26
|
+
when :debug then logger.levels.index 'DEBUG'
|
27
|
+
when :info then logger.levels.index 'INFO'
|
28
|
+
when :warn then logger.levels.index 'WARN'
|
29
|
+
when :error then logger.levels.index 'ERROR'
|
30
|
+
when :fatal then logger.levels.index 'FATAL'
|
31
|
+
else raise "unknown log level '#{lev}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def level
|
36
|
+
logger.level
|
37
|
+
end
|
38
|
+
|
39
|
+
def quiet
|
40
|
+
old_logger = @logger
|
41
|
+
begin
|
42
|
+
@logger = NoOp.new
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
@logger = old_logger
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def create_logger
|
52
|
+
Log4r::Logger.new('GtfsReader').tap do |log|
|
53
|
+
log.outputters << Log4r::StdoutOutputter.new('log_stdout')
|
54
|
+
log.level = Log4r::INFO
|
55
|
+
log.debug { 'Starting GtfsReader...'.underline.colorize :yellow }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class NoOp
|
61
|
+
def method_missing(*args, &block)
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'zip/filesystem'
|
4
|
+
require 'csv'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
require_relative 'file_reader'
|
8
|
+
|
9
|
+
module GtfsReader
|
10
|
+
class SourceUpdater
|
11
|
+
#@param name [String] an arbitrary string describing this source
|
12
|
+
#@param source [Source]
|
13
|
+
def initialize(name, source)
|
14
|
+
@name, @source = name, source
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_callbacks
|
18
|
+
@source.before.call fetch_remote_etag if @source.before
|
19
|
+
end
|
20
|
+
|
21
|
+
# Download the data from the remote server
|
22
|
+
def read
|
23
|
+
Log.debug { " Reading #{@source.url.green}" }
|
24
|
+
@file = Tempfile.new 'gtfs'
|
25
|
+
@file.binmode
|
26
|
+
@file << open(@source.url).read
|
27
|
+
@file.rewind
|
28
|
+
#binding.pry
|
29
|
+
@zip = Zip::File.open @file
|
30
|
+
Log.debug { "Finished reading #{@source.url.green}" }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Close any streams still open
|
34
|
+
def finish
|
35
|
+
@zip.close if @zip
|
36
|
+
@file.delete if @file
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_files
|
40
|
+
@found_files = []
|
41
|
+
check_required_files
|
42
|
+
check_optional_files
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_required_files
|
46
|
+
Log.info { 'required files'.magenta }
|
47
|
+
files = @source.feed_definition.required_files
|
48
|
+
missing = check_missing_files files, :green, :red
|
49
|
+
raise RequiredFilenamesMissing, missing unless missing.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_optional_files
|
53
|
+
Log.info { 'optional files'.cyan }
|
54
|
+
files = @source.feed_definition.optional_files
|
55
|
+
check_missing_files files, :cyan, :light_yellow
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check that every file has its required columns
|
59
|
+
def check_columns
|
60
|
+
@found_files.each do |file|
|
61
|
+
@zip.file.open(file.filename) do |data|
|
62
|
+
FileReader.new data, file, validate: true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_files
|
68
|
+
@found_files.each do |file|
|
69
|
+
if @source.handlers.handler? file.name
|
70
|
+
process_from_temp_file file
|
71
|
+
else
|
72
|
+
Log.warn { "Skipping #{file.filename.yellow} (no handler)" }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def check_missing_files(expected, found_color, missing_color)
|
80
|
+
check = '✔'.colorize found_color
|
81
|
+
cross = '✘'.colorize missing_color
|
82
|
+
|
83
|
+
expected.collect do |req|
|
84
|
+
filename = req.filename
|
85
|
+
if filenames.include? filename
|
86
|
+
Log.info { "#{filename.rjust filename_width} [#{check}]" }
|
87
|
+
@found_files << req
|
88
|
+
nil
|
89
|
+
else
|
90
|
+
Log.info { "#{filename.rjust filename_width} [#{cross}]" }
|
91
|
+
filename
|
92
|
+
end
|
93
|
+
end.compact
|
94
|
+
end
|
95
|
+
|
96
|
+
def filename_width
|
97
|
+
@filename_width ||= @source.feed_definition.files.max do |a, b|
|
98
|
+
a.filename.length <=> b.filename.length
|
99
|
+
end.filename.length
|
100
|
+
end
|
101
|
+
|
102
|
+
def filenames
|
103
|
+
@filenames ||= @zip.entries.collect &:name
|
104
|
+
end
|
105
|
+
|
106
|
+
def fetch_remote_etag
|
107
|
+
url = URI @source.url
|
108
|
+
Net::HTTP.start(url.host) do |http|
|
109
|
+
/[^"]+/ === http.request_head(url.path)['etag'] and $&
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def process_from_temp_file(file)
|
114
|
+
do_parse = !GtfsReader.config.skip_parsing
|
115
|
+
hash = !!GtfsReader.config.return_hashes
|
116
|
+
|
117
|
+
Log.info "Reading file #{file.filename.cyan}..."
|
118
|
+
|
119
|
+
temp = Tempfile.new 'gtfs_file'
|
120
|
+
begin
|
121
|
+
@zip.file.open(file.filename) { |z| temp.write z.read }
|
122
|
+
temp.rewind
|
123
|
+
reader = FileReader.new temp, file, parse: do_parse, hash: hash
|
124
|
+
@source.handlers.handle_file file.name, reader
|
125
|
+
ensure
|
126
|
+
temp.close and temp.unlink
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module GtfsReader
|
2
|
+
module Version
|
3
|
+
# The following four lines are generated, so don't mess with them.
|
4
|
+
MAJOR = 0
|
5
|
+
MINOR = 2
|
6
|
+
PATCH = 2
|
7
|
+
BUILD = nil
|
8
|
+
|
9
|
+
def self.to_s
|
10
|
+
[MAJOR, MINOR, PATCH, BUILD].compact.join '.'
|
11
|
+
end
|
12
|
+
|
13
|
+
# A helper class which bumps the version number stored in this file
|
14
|
+
class Bumper
|
15
|
+
PARTS = %i[major minor patch]
|
16
|
+
PATTERN = %r[(\s+)MAJOR = \d+\s+MINOR = \d+\s+PATCH = \d+\s+BUILD = .+]
|
17
|
+
|
18
|
+
def initialize(filename=__FILE__, part)
|
19
|
+
raise "#{part} not one of #{PARTS}" unless PARTS.include? part
|
20
|
+
@filename, @part = filename, part
|
21
|
+
end
|
22
|
+
|
23
|
+
# Increase the version number and write it to this file
|
24
|
+
def bump
|
25
|
+
parts = new_version
|
26
|
+
# \1 holds a newline and the indentation from the source
|
27
|
+
text = '\1' + ["MAJOR = #{parts[:major]}",
|
28
|
+
"MINOR = #{parts[:minor]}",
|
29
|
+
"PATCH = #{parts[:patch]}",
|
30
|
+
"BUILD = #{parts[:build] || 'nil'}"].join( '\1' )
|
31
|
+
|
32
|
+
out_data = File.read( @filename ).gsub PATTERN, text
|
33
|
+
#puts out_data
|
34
|
+
File.open( @filename, 'w' ) { |out| out << out_data }
|
35
|
+
puts "Bumped version to #{to_s}"
|
36
|
+
end
|
37
|
+
|
38
|
+
#@return [String] What the new version string will be.
|
39
|
+
def to_s
|
40
|
+
p = new_version
|
41
|
+
[p[:major], p[:minor], p[:patch], p[:build]].compact.join ?.
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def new_version
|
47
|
+
@vers ||= { major: MAJOR,
|
48
|
+
minor: MINOR,
|
49
|
+
patch: PATCH,
|
50
|
+
build: BUILD }.merge new_parts
|
51
|
+
end
|
52
|
+
|
53
|
+
def new_parts
|
54
|
+
case @part
|
55
|
+
when :major then {
|
56
|
+
major: MAJOR + 1,
|
57
|
+
minor: 0,
|
58
|
+
patch: 0
|
59
|
+
}
|
60
|
+
when :minor then {
|
61
|
+
minor: MINOR + 1,
|
62
|
+
patch: 0
|
63
|
+
}
|
64
|
+
else {
|
65
|
+
patch: PATCH + 1
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gtfs-reader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Sangster
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: log4r
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubyzip
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.7'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.9'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.9'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.8'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: jeweler
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '4.2'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '4.2'
|
139
|
+
description: 'Reads and parses zip files conforming to Google''s GTFS spec. Such files
|
140
|
+
can take up quite a bit of memory when inflated, so this gem prefers to read them
|
141
|
+
as a stream of rows. GTFS Spec: https://developers.google.com/transit/gtfs'
|
142
|
+
email: jon@ertt.ca
|
143
|
+
executables: []
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files:
|
146
|
+
- LICENSE
|
147
|
+
- README.md
|
148
|
+
files:
|
149
|
+
- LICENSE
|
150
|
+
- README.md
|
151
|
+
- Rakefile
|
152
|
+
- lib/gtfs_reader.rb
|
153
|
+
- lib/gtfs_reader/bulk_feed_handler.rb
|
154
|
+
- lib/gtfs_reader/config/column.rb
|
155
|
+
- lib/gtfs_reader/config/defaults/gtfs_feed_definition.rb
|
156
|
+
- lib/gtfs_reader/config/feed_definition.rb
|
157
|
+
- lib/gtfs_reader/config/file_definition.rb
|
158
|
+
- lib/gtfs_reader/config/prefixed_column_setter.rb
|
159
|
+
- lib/gtfs_reader/config/source.rb
|
160
|
+
- lib/gtfs_reader/config/sources.rb
|
161
|
+
- lib/gtfs_reader/configuration.rb
|
162
|
+
- lib/gtfs_reader/core.rb
|
163
|
+
- lib/gtfs_reader/exceptions.rb
|
164
|
+
- lib/gtfs_reader/feed_handler.rb
|
165
|
+
- lib/gtfs_reader/file_reader.rb
|
166
|
+
- lib/gtfs_reader/file_row.rb
|
167
|
+
- lib/gtfs_reader/log.rb
|
168
|
+
- lib/gtfs_reader/source_updater.rb
|
169
|
+
- lib/gtfs_reader/version.rb
|
170
|
+
homepage: http://github.com/sangster/gtfs-reader
|
171
|
+
licenses:
|
172
|
+
- GPL 3
|
173
|
+
metadata: {}
|
174
|
+
post_install_message:
|
175
|
+
rdoc_options: []
|
176
|
+
require_paths:
|
177
|
+
- lib
|
178
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
179
|
+
requirements:
|
180
|
+
- - '>='
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '0'
|
183
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
requirements: []
|
189
|
+
rubyforge_project:
|
190
|
+
rubygems_version: 2.0.14
|
191
|
+
signing_key:
|
192
|
+
specification_version: 4
|
193
|
+
summary: Read General Transit Feed Specification zip files
|
194
|
+
test_files: []
|
195
|
+
has_rdoc:
|