gtfs-reader 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: