spout 0.10.2 → 0.11.0.beta1
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/CHANGELOG.md +36 -0
- data/README.md +3 -30
- data/lib/spout/commands/coverage.rb +2 -1
- data/lib/spout/commands/deploy.rb +82 -77
- data/lib/spout/commands/exporter.rb +2 -3
- data/lib/spout/commands/graphs.rb +68 -67
- data/lib/spout/commands/help.rb +155 -0
- data/lib/spout/helpers/array_statistics.rb +36 -30
- data/lib/spout/helpers/chart_types.rb +2 -2
- data/lib/spout/helpers/config_reader.rb +5 -5
- data/lib/spout/helpers/json_request.rb +1 -2
- data/lib/spout/helpers/json_request_generic.rb +87 -0
- data/lib/spout/helpers/quietly.rb +2 -4
- data/lib/spout/helpers/semantic.rb +7 -11
- data/lib/spout/helpers/send_file.rb +23 -25
- data/lib/spout/helpers/subject_loader.rb +41 -32
- data/lib/spout/helpers/table_formatting.rb +7 -6
- data/lib/spout/models/bucket.rb +5 -4
- data/lib/spout/models/coverage_result.rb +1 -1
- data/lib/spout/models/dictionary.rb +3 -1
- data/lib/spout/models/domain.rb +7 -6
- data/lib/spout/models/empty.rb +17 -0
- data/lib/spout/models/form.rb +8 -5
- data/lib/spout/models/graphables/default.rb +41 -18
- data/lib/spout/models/graphables/histogram.rb +6 -7
- data/lib/spout/models/graphables.rb +3 -5
- data/lib/spout/models/option.rb +6 -2
- data/lib/spout/models/outlier_result.rb +3 -3
- data/lib/spout/models/record.rb +21 -3
- data/lib/spout/models/subject.rb +4 -7
- data/lib/spout/models/tables/choices_vs_choices.rb +29 -17
- data/lib/spout/models/tables/choices_vs_numeric.rb +19 -12
- data/lib/spout/models/tables/default.rb +19 -32
- data/lib/spout/models/tables/numeric_vs_choices.rb +9 -13
- data/lib/spout/models/tables/numeric_vs_numeric.rb +9 -11
- data/lib/spout/models/tables.rb +4 -6
- data/lib/spout/models/variable.rb +51 -13
- data/lib/spout/tasks/engine.rake +1 -1
- data/lib/spout/templates/ruby-version +1 -1
- data/lib/spout/templates/travis.yml +1 -1
- data/lib/spout/tests/domain_format.rb +2 -2
- data/lib/spout/tests/domain_name_format.rb +15 -0
- data/lib/spout/tests/form_name_format.rb +14 -0
- data/lib/spout/tests/variable_name_format.rb +14 -0
- data/lib/spout/tests.rb +18 -13
- data/lib/spout/version.rb +3 -3
- data/lib/spout/views/index.html.erb +2 -2
- data/lib/spout/views/outliers.html.erb +1 -1
- data/lib/spout.rb +13 -58
- data/spout.gemspec +14 -15
- metadata +25 -25
- data/lib/spout/commands/images.rb +0 -199
- data/lib/spout/support/javascripts/data.js +0 -17
- data/lib/spout/support/javascripts/highcharts-convert.js +0 -583
- data/lib/spout/support/javascripts/highcharts-more.js +0 -50
- data/lib/spout/support/javascripts/highstock.js +0 -353
- data/lib/spout/support/javascripts/jquery.1.9.1.min.js +0 -5
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module Spout
|
7
|
+
module Helpers
|
8
|
+
class JsonRequestGeneric
|
9
|
+
class << self
|
10
|
+
def get(url, *args)
|
11
|
+
new(url, *args).get
|
12
|
+
end
|
13
|
+
|
14
|
+
def post(url, *args)
|
15
|
+
new(url, *args).post
|
16
|
+
end
|
17
|
+
|
18
|
+
def patch(url, *args)
|
19
|
+
new(url, *args).patch
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :url
|
24
|
+
|
25
|
+
def initialize(url, args = {})
|
26
|
+
@params = nested_hash_to_params(args)
|
27
|
+
@url = URI.parse(url)
|
28
|
+
|
29
|
+
@http = Net::HTTP.new(@url.host, @url.port)
|
30
|
+
if @url.scheme == 'https'
|
31
|
+
@http.use_ssl = true
|
32
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
33
|
+
end
|
34
|
+
rescue => e
|
35
|
+
puts "Error sending JsonRequestGeneric: #{e}".colorize(:red)
|
36
|
+
end
|
37
|
+
|
38
|
+
def get
|
39
|
+
full_path = @url.path
|
40
|
+
query = ([@url.query] + @params).flatten.compact.join('&')
|
41
|
+
full_path += "?#{query}" if query.to_s != ''
|
42
|
+
response = @http.start do |http|
|
43
|
+
http.get(full_path)
|
44
|
+
end
|
45
|
+
[JSON.parse(response.body), response]
|
46
|
+
rescue => e
|
47
|
+
puts "GET Error: #{e}".colorize(:red)
|
48
|
+
end
|
49
|
+
|
50
|
+
def post
|
51
|
+
response = @http.start do |http|
|
52
|
+
http.post(@url.path, @params.flatten.compact.join('&'))
|
53
|
+
end
|
54
|
+
[JSON.parse(response.body), response]
|
55
|
+
rescue => e
|
56
|
+
puts "POST ERROR: #{e}".colorize(:red)
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def patch
|
61
|
+
@params << '_method=patch'
|
62
|
+
post
|
63
|
+
end
|
64
|
+
|
65
|
+
def nested_hash_to_params(args)
|
66
|
+
args.collect do |key, value|
|
67
|
+
key_value_to_string(key, value, nil)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def key_value_to_string(key, value, scope = nil)
|
72
|
+
current_scope = (scope ? "#{scope}[#{key}]" : key)
|
73
|
+
if value.is_a? Hash
|
74
|
+
value.collect do |k,v|
|
75
|
+
key_value_to_string(k, v, current_scope)
|
76
|
+
end.join('&')
|
77
|
+
elsif value.is_a? Array
|
78
|
+
value.collect do |v|
|
79
|
+
key_value_to_string('', v, current_scope)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
"#{current_scope}=#{CGI.escape(value.to_s)}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
-
|
2
1
|
module Spout
|
3
2
|
module Helpers
|
3
|
+
# Silences output for tests
|
4
4
|
module Quietly
|
5
|
-
|
6
5
|
# From Rails: http://apidock.com/rails/v3.2.13/Kernel/silence_stream
|
7
6
|
def silence_stream(stream)
|
8
7
|
old_stream = stream.dup
|
9
|
-
stream.reopen(RbConfig::CONFIG['host_os']
|
8
|
+
stream.reopen(/mswin|mingw/ =~ RbConfig::CONFIG['host_os'] ? 'NUL:' : '/dev/null')
|
10
9
|
stream.sync = true
|
11
10
|
yield
|
12
11
|
ensure
|
@@ -21,7 +20,6 @@ module Spout
|
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
|
-
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
@@ -1,9 +1,7 @@
|
|
1
|
-
# def dataset_folders
|
2
|
-
# Dir.entries('csvs').select{|e| File.directory? File.join('csvs', e) }.reject{|e| [".",".."].include?(e)}.sort
|
3
|
-
# end
|
4
1
|
module Spout
|
5
2
|
module Helpers
|
6
|
-
|
3
|
+
# Helps to sort semantically versioned numbers to match versions that are
|
4
|
+
# close to each other.
|
7
5
|
class Version
|
8
6
|
attr_accessor :string
|
9
7
|
attr_reader :major, :minor, :tiny, :build
|
@@ -26,7 +24,7 @@ module Spout
|
|
26
24
|
end
|
27
25
|
|
28
26
|
def build_number
|
29
|
-
(@build
|
27
|
+
(@build.nil? ? 1 : 0)
|
30
28
|
end
|
31
29
|
|
32
30
|
def rank
|
@@ -34,21 +32,21 @@ module Spout
|
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
35
|
+
# Finds compatible versions
|
37
36
|
class Semantic
|
38
|
-
|
39
37
|
attr_accessor :data_dictionary_version
|
40
38
|
|
41
39
|
def initialize(version, version_strings)
|
42
40
|
@data_dictionary_version = Spout::Helpers::Version.new(version)
|
43
|
-
@versions = version_strings.collect{ |vs| Spout::Helpers::Version.new(vs) }.sort_by(&:rank)
|
41
|
+
@versions = version_strings.collect { |vs| Spout::Helpers::Version.new(vs) }.sort_by(&:rank)
|
44
42
|
end
|
45
43
|
|
46
44
|
def valid_versions
|
47
|
-
@versions.select{ |v| v.major == major
|
45
|
+
@versions.select { |v| v.major == major && v.minor == minor }
|
48
46
|
end
|
49
47
|
|
50
48
|
def selected_folder
|
51
|
-
if valid_versions.size == 0
|
49
|
+
if valid_versions.size == 0 || valid_versions.collect(&:string).include?(version)
|
52
50
|
version
|
53
51
|
else
|
54
52
|
valid_versions.collect(&:string).last
|
@@ -74,8 +72,6 @@ module Spout
|
|
74
72
|
def build
|
75
73
|
@data_dictionary_version.build
|
76
74
|
end
|
77
|
-
|
78
75
|
end
|
79
|
-
|
80
76
|
end
|
81
77
|
end
|
@@ -13,15 +13,15 @@ module Spout
|
|
13
13
|
|
14
14
|
attr_reader :url
|
15
15
|
|
16
|
-
def initialize(url, filename, version, token,
|
17
|
-
|
16
|
+
def initialize(url, filename, version, token, slug, folder)
|
18
17
|
@params = {}
|
19
|
-
@params[
|
20
|
-
@params[
|
21
|
-
@params[
|
18
|
+
@params['version'] = version
|
19
|
+
@params['auth_token'] = token if token
|
20
|
+
@params['dataset'] = slug if slug
|
21
|
+
@params['folder'] = folder if folder
|
22
22
|
begin
|
23
|
-
file = File.open(filename,
|
24
|
-
@params[
|
23
|
+
file = File.open(filename, 'rb')
|
24
|
+
@params['file'] = file
|
25
25
|
|
26
26
|
mp = Multipart::MultipartPost.new
|
27
27
|
@query, @headers = mp.prepare_query(@params)
|
@@ -42,36 +42,33 @@ module Spout
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def post
|
45
|
-
|
46
|
-
|
47
|
-
http.post(@url.path, @query, @headers)
|
48
|
-
end
|
49
|
-
JSON.parse(response.body)
|
50
|
-
rescue
|
51
|
-
nil
|
45
|
+
response = @http.start do |http|
|
46
|
+
http.post(@url.path, @query, @headers)
|
52
47
|
end
|
48
|
+
JSON.parse(response.body)
|
49
|
+
rescue
|
50
|
+
nil
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
58
|
-
|
59
56
|
module Multipart
|
60
57
|
class Param
|
61
58
|
attr_accessor :k, :v
|
62
|
-
def initialize(
|
59
|
+
def initialize(k, v)
|
63
60
|
@k = k
|
64
61
|
@v = v
|
65
62
|
end
|
66
63
|
|
67
64
|
def to_multipart
|
68
|
-
|
65
|
+
"Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n"
|
69
66
|
end
|
70
67
|
end
|
71
68
|
|
72
69
|
class FileParam
|
73
70
|
attr_accessor :k, :filename, :content
|
74
|
-
def initialize(
|
71
|
+
def initialize(k, filename, content)
|
75
72
|
@k = k
|
76
73
|
@filename = filename
|
77
74
|
@content = content
|
@@ -79,23 +76,24 @@ module Multipart
|
|
79
76
|
|
80
77
|
def to_multipart
|
81
78
|
mime_type = 'application/octet-stream'
|
82
|
-
|
79
|
+
"Content-Disposition: form-data; name=\"#{k}\"; filename=\"#{filename}\"\r\n" + "Content-Transfer-Encoding: binary\r\n" + "Content-Type: #{mime_type}\r\n\r\n" + content + "\r\n"
|
83
80
|
end
|
84
81
|
end
|
82
|
+
|
85
83
|
class MultipartPost
|
86
84
|
BOUNDARY = 'a#41-93r1-^Õ-rule0000'
|
87
|
-
HEADER = {
|
85
|
+
HEADER = { 'Content-type' => "multipart/form-data, boundary=#{BOUNDARY} " }
|
88
86
|
|
89
|
-
def prepare_query
|
87
|
+
def prepare_query(params)
|
90
88
|
fp = []
|
91
|
-
params.each
|
89
|
+
params.each do |k, v|
|
92
90
|
if v.respond_to?(:read)
|
93
91
|
fp.push(FileParam.new(k, v.path, v.read))
|
94
92
|
else
|
95
|
-
fp.push(Param.new(k,v))
|
93
|
+
fp.push(Param.new(k, v))
|
96
94
|
end
|
97
|
-
|
98
|
-
query = fp.collect {|p| "
|
95
|
+
end
|
96
|
+
query = fp.collect { |p| "--#{BOUNDARY}\r\n" + p.to_multipart }.join('') + "--#{BOUNDARY}--"
|
99
97
|
return query, HEADER
|
100
98
|
end
|
101
99
|
end
|
@@ -4,6 +4,7 @@ require 'json'
|
|
4
4
|
|
5
5
|
require 'spout/models/subject'
|
6
6
|
require 'spout/helpers/semantic'
|
7
|
+
require 'spout/models/empty'
|
7
8
|
|
8
9
|
module Spout
|
9
10
|
module Helpers
|
@@ -32,74 +33,84 @@ module Spout
|
|
32
33
|
def load_subjects_from_csvs_part_one!
|
33
34
|
@subjects = []
|
34
35
|
|
35
|
-
available_folders = (Dir.exist?('csvs') ? Dir.entries('csvs').select{|e| File.directory? File.join('csvs', e) }.reject{|e| [
|
36
|
+
available_folders = (Dir.exist?('csvs') ? Dir.entries('csvs').select { |e| File.directory? File.join('csvs', e) }.reject { |e| ['.', '..'].include?(e) }.sort : [])
|
36
37
|
|
37
38
|
@semantic = Spout::Helpers::Semantic.new(@standard_version, available_folders)
|
38
39
|
|
39
40
|
@csv_directory = @semantic.selected_folder
|
40
41
|
|
41
|
-
|
42
|
-
@csv_files
|
42
|
+
csv_root = File.join('csvs', @csv_directory)
|
43
|
+
@csv_files = Dir.glob("#{csv_root}/**/*.csv").sort
|
44
|
+
|
45
|
+
if @csv_directory != @standard_version
|
46
|
+
puts "\n#{@csv_files.size == 0 ? 'No CSVs found' : 'Parsing files' } in " + "#{csv_root}".colorize(:white) + ' for dictionary version ' + @standard_version.to_s.colorize(:green) + "\n"
|
47
|
+
else
|
48
|
+
puts "\n#{@csv_files.size == 0 ? 'No CSVs found' : 'Parsing files' } in " + "#{csv_root}".colorize(:white) + "\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
last_folder = nil
|
52
|
+
@csv_files.each do |csv_file|
|
53
|
+
relative_path = csv_file.gsub(%r{^#{csv_root}}, '')
|
54
|
+
current_file = File.basename(relative_path)
|
55
|
+
current_folder = relative_path.gsub(/#{current_file}$/, '')
|
43
56
|
count = 1 # Includes counting the header row
|
44
|
-
|
45
|
-
|
57
|
+
puts " #{current_folder}".colorize(:white) if current_folder.to_s != '' && current_folder != last_folder
|
58
|
+
print " #{current_file}"
|
59
|
+
last_folder = current_folder
|
60
|
+
CSV.parse(File.open(csv_file, 'r:iso-8859-1:utf-8'){ |f| f.read }, headers: true, header_converters: lambda { |h| h.to_s.downcase }) do |line|
|
46
61
|
row = line.to_hash
|
47
62
|
count += 1
|
48
|
-
print "\
|
63
|
+
print "\r #{current_file} " + "##{count}".colorize(:yellow) if (count % 10 == 0)
|
49
64
|
@subjects << Spout::Models::Subject.create do |t|
|
50
65
|
t._visit = row[@visit]
|
66
|
+
t._csv = File.basename(csv_file)
|
51
67
|
|
52
|
-
row.each do |key,value|
|
68
|
+
row.each do |key, value|
|
53
69
|
method = key.to_s.downcase
|
54
|
-
|
70
|
+
next unless @valid_ids.include?(method) || @valid_ids.size == 0
|
55
71
|
unless t.respond_to?(method)
|
56
72
|
t.class.send(:define_method, "#{method}") { instance_variable_get("@#{method}") }
|
57
|
-
t.class.send(:define_method, "#{method}=") { |
|
73
|
+
t.class.send(:define_method, "#{method}=") { |v| instance_variable_set("@#{method}", v) }
|
58
74
|
end
|
59
|
-
|
60
75
|
@all_methods[method] ||= []
|
61
76
|
@all_methods[method] = @all_methods[method] | [csv_file]
|
62
|
-
|
63
|
-
|
77
|
+
if value.nil?
|
78
|
+
t.send("#{method}=", Spout::Models::Empty.new)
|
79
|
+
else
|
64
80
|
t.send("#{method}=", value)
|
65
81
|
end
|
66
82
|
end
|
67
83
|
end
|
84
|
+
|
68
85
|
# puts "Memory Used: " + (`ps -o rss -p #{$$}`.strip.split.last.to_i / 1024).to_s + " MB" if count % 1000 == 0
|
69
|
-
break if
|
86
|
+
break if !@number_of_rows.nil? && count - 1 >= @number_of_rows
|
70
87
|
end
|
71
|
-
print "\rParsing #{csv_file} - Row ##{count}"
|
72
|
-
puts "\n"
|
73
|
-
end
|
74
88
|
|
75
|
-
|
76
|
-
puts "
|
77
|
-
else
|
78
|
-
puts "#{@csv_files.size == 0 ? 'No CSVs found' : 'Using dataset' } in " + "csvs/#{@standard_version}/".colorize( :green ) + "\n\n"
|
89
|
+
print "\r #{current_file} " + "##{count}".colorize(:green)
|
90
|
+
puts "\n"
|
79
91
|
end
|
80
|
-
|
81
92
|
end
|
82
93
|
|
83
94
|
def load_subjects_from_csvs_part_two!
|
84
95
|
variable_count = @variable_files.count
|
85
|
-
print
|
96
|
+
print 'Converting numeric values to floats'
|
86
97
|
@variable_files.each_with_index do |variable_file, index|
|
87
|
-
print "\rConverting numeric values to floats:#{
|
98
|
+
print "\rConverting numeric values to floats:#{'% 3d' % ((index + 1) * 100 / variable_count)}%"
|
88
99
|
json = JSON.parse(File.read(variable_file)) rescue json = nil
|
89
100
|
next unless json
|
90
|
-
next unless @valid_ids.include?(json[
|
91
|
-
next unless
|
92
|
-
method
|
101
|
+
next unless @valid_ids.include?(json['id'].to_s.downcase) || @valid_ids.size == 0
|
102
|
+
next unless %w(numeric integer).include?(json['type'])
|
103
|
+
method = json['id'].to_s.downcase
|
93
104
|
next unless Spout::Models::Subject.method_defined?(method)
|
94
105
|
|
95
106
|
domain_json = get_domain(json)
|
96
107
|
# Make all domain options nil for numerics/integers
|
97
108
|
if domain_json
|
98
|
-
domain_values = domain_json.collect{|option_hash| option_hash['value']}
|
99
|
-
@subjects.each{ |s| domain_values.include?(s.send(method)) ? s.send("#{method}=", nil) : nil }
|
109
|
+
domain_values = domain_json.collect { |option_hash| option_hash['value'] }
|
110
|
+
@subjects.each { |s| domain_values.include?(s.send(method)) ? s.send("#{method}=", nil) : nil }
|
100
111
|
end
|
101
112
|
|
102
|
-
@subjects.each{ |s| s.send(method)
|
113
|
+
@subjects.each { |s| !s.send(method).nil? ? s.send("#{method}=", s.send("#{method}").to_f) : nil }
|
103
114
|
end
|
104
115
|
puts "\n"
|
105
116
|
@subjects
|
@@ -109,7 +120,7 @@ module Spout
|
|
109
120
|
@variable_files.each do |variable_file|
|
110
121
|
json = JSON.parse(File.read(variable_file)) rescue json = nil
|
111
122
|
next unless json
|
112
|
-
next unless [
|
123
|
+
next unless ['choices'].include?(json['type'])
|
113
124
|
domain = json['domain'].to_s.downcase
|
114
125
|
@all_domains << domain
|
115
126
|
end
|
@@ -129,8 +140,6 @@ module Spout
|
|
129
140
|
def get_domain(json)
|
130
141
|
get_json(json['domain'], 'domain')
|
131
142
|
end
|
132
|
-
|
133
|
-
|
134
143
|
end
|
135
144
|
end
|
136
145
|
end
|
@@ -1,18 +1,17 @@
|
|
1
1
|
module Spout
|
2
2
|
module Helpers
|
3
3
|
class TableFormatting
|
4
|
-
|
5
4
|
# def initialize(number)
|
6
5
|
# @number = number
|
7
6
|
# end
|
8
7
|
|
9
|
-
def self.number_with_delimiter(number, delimiter =
|
8
|
+
def self.number_with_delimiter(number, delimiter = ',')
|
10
9
|
number.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
|
11
10
|
end
|
12
11
|
|
13
12
|
# type: :count or :decimal
|
14
13
|
def self.format_number(number, type, format = nil)
|
15
|
-
if number
|
14
|
+
if number.nil?
|
16
15
|
format_nil(number)
|
17
16
|
elsif type == :count
|
18
17
|
format_count(number)
|
@@ -31,10 +30,9 @@ module Spout
|
|
31
30
|
# 1000 -> '1,000'
|
32
31
|
# Input (Numeric) -> Output (String)
|
33
32
|
def self.format_count(number)
|
34
|
-
(number == 0 || number
|
33
|
+
(number == 0 || number.nil?) ? '-' : number_with_delimiter(number)
|
35
34
|
end
|
36
35
|
|
37
|
-
|
38
36
|
# decimal:
|
39
37
|
# 0 -> '0.0'
|
40
38
|
# 10 -> '10.0'
|
@@ -43,7 +41,10 @@ module Spout
|
|
43
41
|
# 12412423.42252525 -> '12,412,423.4'
|
44
42
|
# Input (Numeric) -> Output (String)
|
45
43
|
def self.format_decimal(number, format)
|
46
|
-
|
44
|
+
precision = 1
|
45
|
+
precision = -Math.log10(number.abs).floor if number.abs < 1.0 && number != 0
|
46
|
+
|
47
|
+
number = number_with_delimiter(number.round(precision))
|
47
48
|
number = format % number if format
|
48
49
|
number
|
49
50
|
end
|
data/lib/spout/models/bucket.rb
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
module Spout
|
2
2
|
module Models
|
3
|
+
# Defines a continuous or discrete bucket for tables and graphs
|
3
4
|
class Bucket
|
4
|
-
|
5
5
|
attr_accessor :start, :stop
|
6
6
|
|
7
|
-
def initialize(start, stop)
|
7
|
+
def initialize(start, stop, discrete: false)
|
8
8
|
@start = start
|
9
9
|
@stop = stop
|
10
|
+
@discrete = discrete
|
10
11
|
end
|
11
12
|
|
12
13
|
def in_bucket?(value)
|
13
|
-
value >= @start
|
14
|
+
value >= @start && value <= @stop
|
14
15
|
end
|
15
16
|
|
16
17
|
def display_name
|
18
|
+
return "#{@start}" if @discrete
|
17
19
|
"#{@start} to #{@stop}"
|
18
20
|
end
|
19
|
-
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -51,7 +51,7 @@ module Spout
|
|
51
51
|
else
|
52
52
|
domain_file = Dir.glob("domains/**/#{@json['domain'].to_s.downcase}.json", File::FNM_CASEFOLD).first
|
53
53
|
if domain_json = JSON.parse(File.read(domain_file)) rescue false
|
54
|
-
return domain_json.
|
54
|
+
return domain_json.is_a?(Array)
|
55
55
|
end
|
56
56
|
false
|
57
57
|
end
|
@@ -4,6 +4,8 @@ require 'spout/models/form'
|
|
4
4
|
|
5
5
|
module Spout
|
6
6
|
module Models
|
7
|
+
# Creates a structure that contains a dictionaries variables, domains, and
|
8
|
+
# forms
|
7
9
|
class Dictionary
|
8
10
|
attr_accessor :variables, :domains, :forms
|
9
11
|
attr_accessor :app_path
|
@@ -44,7 +46,7 @@ module Spout
|
|
44
46
|
private
|
45
47
|
|
46
48
|
def json_files(type)
|
47
|
-
Dir.glob(File.join(@app_path, type,
|
49
|
+
Dir.glob(File.join(@app_path, type, '**', '*.json'))
|
48
50
|
end
|
49
51
|
|
50
52
|
def load_type!(method)
|
data/lib/spout/models/domain.rb
CHANGED
@@ -5,9 +5,7 @@ require 'spout/models/option'
|
|
5
5
|
|
6
6
|
module Spout
|
7
7
|
module Models
|
8
|
-
|
9
8
|
class Domain < Spout::Models::Record
|
10
|
-
|
11
9
|
attr_accessor :id, :folder, :options
|
12
10
|
attr_reader :errors
|
13
11
|
|
@@ -26,11 +24,11 @@ module Spout
|
|
26
24
|
nil
|
27
25
|
end
|
28
26
|
rescue => e
|
29
|
-
@errors << "Parsing error found in #{@id}.json: #{e.message}"
|
27
|
+
@errors << "Parsing error found in #{@id}.json: #{e.message}" unless file_name.nil?
|
30
28
|
nil
|
31
29
|
end
|
32
30
|
|
33
|
-
if json
|
31
|
+
if json.is_a? Array
|
34
32
|
@id = file_name.to_s.gsub(/^(.*)\/|\.json$/, '').downcase
|
35
33
|
@options = (json || []).collect do |option|
|
36
34
|
Spout::Models::Option.new(option)
|
@@ -38,10 +36,13 @@ module Spout
|
|
38
36
|
elsif json
|
39
37
|
@errors << "Domain must be a valid array in the following format: [\n {\n \"value\": \"1\",\n \"display_name\": \"First Choice\",\n \"description\": \"First Description\"\n },\n {\n \"value\": \"2\",\n \"display_name\": \"Second Choice\",\n \"description\": \"Second Description\"\n }\n]"
|
40
38
|
end
|
41
|
-
|
42
39
|
end
|
43
40
|
|
44
|
-
|
41
|
+
def deploy_params
|
42
|
+
{ name: id, folder: folder.to_s.gsub(%r{/$}, ''),
|
43
|
+
options: options.collect(&:deploy_params),
|
44
|
+
spout_version: Spout::VERSION::STRING }
|
45
|
+
end
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Spout
|
2
|
+
module Models
|
3
|
+
# Used for empty values, these values exist in that the column is defined
|
4
|
+
# in the CSV, however the cell is blank. This is to differentiate this
|
5
|
+
# value from nil, where the subject row exists, but the column for the
|
6
|
+
# is not specified.
|
7
|
+
class Empty
|
8
|
+
def to_f
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
'Empty'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/spout/models/form.rb
CHANGED
@@ -9,8 +9,7 @@ require 'spout/models/record'
|
|
9
9
|
module Spout
|
10
10
|
module Models
|
11
11
|
class Form < Spout::Models::Record
|
12
|
-
|
13
|
-
attr_accessor :id, :display_name, :code_book
|
12
|
+
attr_accessor :id, :folder, :display_name, :code_book
|
14
13
|
attr_accessor :errors
|
15
14
|
|
16
15
|
def initialize(file_name, dictionary_root)
|
@@ -26,8 +25,8 @@ module Spout
|
|
26
25
|
nil
|
27
26
|
end
|
28
27
|
|
29
|
-
if json
|
30
|
-
%w(
|
28
|
+
if json.is_a? Hash
|
29
|
+
%w(display_name code_book).each do |method|
|
31
30
|
instance_variable_set("@#{method}", json[method])
|
32
31
|
end
|
33
32
|
|
@@ -35,9 +34,13 @@ module Spout
|
|
35
34
|
elsif json
|
36
35
|
@errors << "Form must be a valid hash in the following format: {\n\"id\": \"FORM_ID\",\n \"display_name\": \"FORM DISPLAY NAME\",\n \"code_book\": \"FORMPDF.pdf\"\n}"
|
37
36
|
end
|
38
|
-
|
39
37
|
end
|
40
38
|
|
39
|
+
def deploy_params
|
40
|
+
{ name: id, folder: folder.to_s.gsub(%r{/$}, ''),
|
41
|
+
display_name: display_name, code_book: code_book,
|
42
|
+
spout_version: Spout::VERSION::STRING }
|
43
|
+
end
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|