spout 0.12.1 → 0.13.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +5 -5
- data/Rakefile +4 -4
- data/bin/spout +1 -1
- data/lib/spout/commands/coverage.rb +18 -18
- data/lib/spout/commands/deploy.rb +89 -89
- data/lib/spout/commands/exporter.rb +16 -16
- data/lib/spout/commands/graphs.rb +37 -37
- data/lib/spout/commands/help.rb +1 -1
- data/lib/spout/commands/importer.rb +59 -57
- data/lib/spout/commands/outliers.rb +17 -17
- data/lib/spout/commands/project_generator.rb +25 -25
- data/lib/spout/commands/update.rb +38 -38
- data/lib/spout/helpers/array_statistics.rb +7 -7
- data/lib/spout/helpers/chart_types.rb +2 -2
- data/lib/spout/helpers/config_reader.rb +21 -20
- data/lib/spout/helpers/framework.rb +11 -11
- data/lib/spout/helpers/iterators.rb +1 -3
- data/lib/spout/helpers/json_loader.rb +2 -4
- data/lib/spout/helpers/json_request.rb +11 -11
- data/lib/spout/helpers/number_helper.rb +1 -1
- data/lib/spout/helpers/quietly.rb +1 -1
- data/lib/spout/helpers/semantic.rb +1 -1
- data/lib/spout/helpers/send_file.rb +14 -14
- data/lib/spout/helpers/subject_loader.rb +24 -24
- data/lib/spout/helpers/table_formatting.rb +12 -12
- data/lib/spout/models/coverage_result.rb +8 -8
- data/lib/spout/models/dictionary.rb +10 -10
- data/lib/spout/models/domain.rb +7 -7
- data/lib/spout/models/empty.rb +1 -1
- data/lib/spout/models/form.rb +6 -6
- data/lib/spout/models/graphables/choices_vs_choices.rb +3 -5
- data/lib/spout/models/graphables/choices_vs_numeric.rb +4 -6
- data/lib/spout/models/graphables/default.rb +6 -6
- data/lib/spout/models/graphables/histogram.rb +4 -4
- data/lib/spout/models/graphables/numeric_vs_choices.rb +1 -1
- data/lib/spout/models/graphables/numeric_vs_numeric.rb +6 -6
- data/lib/spout/models/graphables.rb +14 -14
- data/lib/spout/models/outlier_result.rb +16 -18
- data/lib/spout/models/record.rb +3 -3
- data/lib/spout/models/tables/choices_vs_choices.rb +11 -11
- data/lib/spout/models/tables/choices_vs_numeric.rb +8 -8
- data/lib/spout/models/tables/default.rb +5 -5
- data/lib/spout/models/tables/numeric_vs_choices.rb +7 -7
- data/lib/spout/models/tables/numeric_vs_numeric.rb +7 -7
- data/lib/spout/models/tables.rb +11 -11
- data/lib/spout/models/variable.rb +14 -14
- data/lib/spout/tasks/engine.rake +4 -4
- data/lib/spout/templates/CHANGELOG.md.erb +1 -1
- data/lib/spout/templates/Gemfile +2 -2
- data/lib/spout/templates/Rakefile +1 -1
- data/lib/spout/templates/test/dictionary_test.rb +3 -3
- data/lib/spout/templates/test/test_helper.rb +1 -1
- data/lib/spout/tests/domain_existence_validation.rb +6 -6
- data/lib/spout/tests/domain_format.rb +1 -1
- data/lib/spout/tests/domain_name_format.rb +3 -3
- data/lib/spout/tests/domain_name_uniqueness.rb +2 -2
- data/lib/spout/tests/domain_specified.rb +1 -3
- data/lib/spout/tests/form_existence_validation.rb +2 -4
- data/lib/spout/tests/form_name_format.rb +3 -3
- data/lib/spout/tests/form_name_match.rb +1 -1
- data/lib/spout/tests/json_helper.rb +1 -1
- data/lib/spout/tests/json_validation.rb +0 -2
- data/lib/spout/tests/variable_name_format.rb +3 -3
- data/lib/spout/tests/variable_name_match.rb +1 -3
- data/lib/spout/tests/variable_name_uniqueness.rb +2 -2
- data/lib/spout/tests/variable_type_validation.rb +2 -2
- data/lib/spout/tests.rb +30 -30
- data/lib/spout/version.rb +4 -4
- data/lib/spout/views/index.html.erb +15 -15
- data/lib/spout/views/outliers.html.erb +8 -8
- data/lib/spout.rb +26 -26
- data/spout.gemspec +22 -22
- metadata +5 -5
data/lib/spout/tests.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "rubygems"
|
4
|
+
require "json"
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
6
|
+
require "minitest/autorun"
|
7
|
+
require "minitest/reporters"
|
8
|
+
require "colorize"
|
9
9
|
|
10
10
|
module Minitest
|
11
11
|
module Reporters
|
@@ -14,22 +14,22 @@ module Minitest
|
|
14
14
|
|
15
15
|
def start
|
16
16
|
super
|
17
|
-
print
|
17
|
+
print "Loaded Suite test".colorize(:white)
|
18
18
|
puts
|
19
19
|
puts
|
20
|
-
puts
|
20
|
+
puts "Started"
|
21
21
|
puts
|
22
22
|
end
|
23
23
|
|
24
24
|
def report
|
25
25
|
super
|
26
|
-
puts format(
|
26
|
+
puts format("Finished in %.5f seconds.", total_time)
|
27
27
|
puts
|
28
|
-
print format(
|
29
|
-
print format(
|
28
|
+
print format("%d tests", count).colorize(:white)
|
29
|
+
print format(", %d assertions, ", assertions)
|
30
30
|
color = failures.zero? && errors.zero? ? :green : :red
|
31
|
-
print format(
|
32
|
-
print format(
|
31
|
+
print format("%d failures, %d errors, ", failures, errors).colorize(color)
|
32
|
+
print format("%d skips", skips).colorize(:yellow)
|
33
33
|
puts
|
34
34
|
puts
|
35
35
|
end
|
@@ -37,11 +37,11 @@ module Minitest
|
|
37
37
|
def record(test)
|
38
38
|
super
|
39
39
|
if !test.skipped? && test.failure
|
40
|
-
print
|
40
|
+
print " "
|
41
41
|
print_colored_status(test)
|
42
42
|
print " #{test.name}"
|
43
43
|
puts
|
44
|
-
print
|
44
|
+
print " "
|
45
45
|
print test.failure.to_s.gsub("\n", "\n ")
|
46
46
|
puts
|
47
47
|
puts
|
@@ -74,23 +74,23 @@ end
|
|
74
74
|
|
75
75
|
Minitest::Reporters.use! Minitest::Reporters::SpoutReporter.new
|
76
76
|
|
77
|
-
require
|
78
|
-
require
|
79
|
-
require
|
80
|
-
require
|
81
|
-
require
|
82
|
-
require
|
83
|
-
require
|
84
|
-
require
|
85
|
-
require
|
86
|
-
require
|
87
|
-
require
|
88
|
-
require
|
89
|
-
require
|
90
|
-
require
|
91
|
-
require
|
77
|
+
require "spout/tests/json_validation"
|
78
|
+
require "spout/tests/domain_existence_validation"
|
79
|
+
require "spout/tests/domain_format"
|
80
|
+
require "spout/tests/domain_name_format"
|
81
|
+
require "spout/tests/domain_name_uniqueness"
|
82
|
+
require "spout/tests/domain_specified"
|
83
|
+
require "spout/tests/form_existence_validation"
|
84
|
+
require "spout/tests/form_name_format"
|
85
|
+
require "spout/tests/form_name_match"
|
86
|
+
require "spout/tests/form_name_uniqueness"
|
87
|
+
require "spout/tests/variable_display_name_length"
|
88
|
+
require "spout/tests/variable_name_format"
|
89
|
+
require "spout/tests/variable_name_match"
|
90
|
+
require "spout/tests/variable_name_uniqueness"
|
91
|
+
require "spout/tests/variable_type_validation"
|
92
92
|
|
93
|
-
require
|
93
|
+
require "spout/helpers/iterators"
|
94
94
|
|
95
95
|
module Spout
|
96
96
|
module Tests
|
data/lib/spout/version.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
module Spout
|
4
4
|
module VERSION #:nodoc:
|
5
5
|
MAJOR = 0
|
6
|
-
MINOR =
|
7
|
-
TINY =
|
8
|
-
BUILD =
|
6
|
+
MINOR = 13
|
7
|
+
TINY = 0
|
8
|
+
BUILD = "beta1" # "pre", "rc", "rc2", nil
|
9
9
|
|
10
|
-
STRING = [MAJOR, MINOR, TINY, BUILD].compact.join(
|
10
|
+
STRING = [MAJOR, MINOR, TINY, BUILD].compact.join(".").freeze
|
11
11
|
end
|
12
12
|
end
|
@@ -204,8 +204,8 @@ tfoot td {
|
|
204
204
|
</div>
|
205
205
|
</td>
|
206
206
|
<td style="text-align:center">
|
207
|
-
<% matched = @matching_results.count{|csv_files, column, scr| scr.json[
|
208
|
-
<% total_count = @matching_results.count{|csv_files, column, scr| scr.json[
|
207
|
+
<% matched = @matching_results.count{|csv_files, column, scr| scr.json["type"] == "choices" and scr.domain_test} %>
|
208
|
+
<% total_count = @matching_results.count{|csv_files, column, scr| scr.json["type"] == "choices"} %>
|
209
209
|
<% matched_percent = (total_count == 0 ? 0 : (matched * 100.0 / total_count).floor) %>
|
210
210
|
<% missing_percent = 100 - matched_percent %>
|
211
211
|
<%= number_with_delimiter(matched) %> of <%= number_with_delimiter(total_count) %>
|
@@ -236,7 +236,7 @@ tfoot td {
|
|
236
236
|
<tr>
|
237
237
|
<td>
|
238
238
|
<% csv_files.each do |csv_file| %>
|
239
|
-
<code class="<%=
|
239
|
+
<code class="<%= "success" if scr.number_of_errors == 0 %>"><%= csv_file.gsub(/^csvs\/#{@subject_loader.csv_directory}\//, "") %></code><br />
|
240
240
|
<% end %>
|
241
241
|
</td>
|
242
242
|
<td><%= column %></td>
|
@@ -252,37 +252,37 @@ tfoot td {
|
|
252
252
|
<% if scr.json_id_test %>
|
253
253
|
<code class="success">"id": "<%= column %>"</code>
|
254
254
|
<% else %>
|
255
|
-
<code>"id": <%= scr.json[
|
255
|
+
<code>"id": <%= scr.json["id"].inspect %></code>
|
256
256
|
<% end %>
|
257
257
|
<% end %>
|
258
258
|
</td>
|
259
259
|
<td>
|
260
260
|
<% if scr.file_name_test %>
|
261
|
-
<code class="<%=
|
261
|
+
<code class="<%= "success" if scr.variable_type_test %>">"type": <%= scr.json["type"].inspect %></code>
|
262
262
|
<% end %>
|
263
263
|
</td>
|
264
264
|
<td>
|
265
|
-
<% if (scr.json[
|
266
|
-
<% if scr.domain_test || scr.json[
|
267
|
-
<code class="<%=
|
265
|
+
<% if (scr.json["type"] == "choices" || scr.json["domain"].to_s.downcase.strip != "") && scr.file_name_test %>
|
266
|
+
<% if scr.domain_test || scr.json["domain"].to_s.strip == "" %>
|
267
|
+
<code class="<%= "success" if scr.domain_test %>">"domain": <%= scr.json["domain"].inspect %></code>
|
268
268
|
<% else %>
|
269
|
-
<span class="text-danger"><code><%= scr.json[
|
269
|
+
<span class="text-danger"><code><%= scr.json["domain"] %>.json</code> missing</span>
|
270
270
|
<% end %>
|
271
271
|
<% end %>
|
272
272
|
</td>
|
273
273
|
<td style="white-space:nowrap">
|
274
|
-
<% if scr.json[
|
274
|
+
<% if scr.json["type"] == "choices" || scr.json["domain"].to_s.downcase.strip != "" %>
|
275
275
|
<% unused_domain_values = scr.valid_values - scr.csv_values %>
|
276
276
|
<% unused_values = (scr.valid_values | scr.csv_values) - (scr.valid_values & scr.csv_values) %>
|
277
277
|
<% if scr.values_test && unused_values.empty? %>
|
278
278
|
<div class="text-success" style="text-align:center"><span class="glyphicon glyphicon-ok"></span></div>
|
279
279
|
<% else %>
|
280
280
|
<% (scr.valid_values + scr.csv_values.compact.sort).uniq.each do |value| %>
|
281
|
-
<% value_as_number = format(
|
282
|
-
<% next if !scr.valid_values.include?(value) && %w(numeric integer).include?(scr.json[
|
283
|
-
<% class_type =
|
284
|
-
<% class_type =
|
285
|
-
<% class_type =
|
281
|
+
<% value_as_number = format("%g", value) rescue value_as_number = nil %>
|
282
|
+
<% next if !scr.valid_values.include?(value) && %w(numeric integer).include?(scr.json["type"]) && !value_as_number.nil? %>
|
283
|
+
<% class_type = "" %>
|
284
|
+
<% class_type = "success" if scr.valid_values.include?(value) %>
|
285
|
+
<% class_type = "default" if unused_domain_values.include?(value) %>
|
286
286
|
<code class="<%= class_type %>"><%= value %></code>
|
287
287
|
<% end %>
|
288
288
|
<% end %>
|
@@ -112,12 +112,12 @@ tfoot td {
|
|
112
112
|
<% @overall_results.each do |csv_file, major_outliers, minor_outliers, total_outliers| %>
|
113
113
|
<tr>
|
114
114
|
<td><%= csv_file %></td>
|
115
|
-
<td><code class="<%= major_outliers > 0 ?
|
116
|
-
<td><code class="<%= minor_outliers > 0 ?
|
115
|
+
<td><code class="<%= major_outliers > 0 ? "danger" : "default" %>"><%= number_with_delimiter(major_outliers) %></code></td>
|
116
|
+
<td><code class="<%= minor_outliers > 0 ? "warning" : "default" %>"><%= number_with_delimiter(minor_outliers) %></code></td>
|
117
117
|
<td>
|
118
|
-
<% css_class =
|
119
|
-
<% css_class =
|
120
|
-
<% css_class =
|
118
|
+
<% css_class = "success" %>
|
119
|
+
<% css_class = "warning" if minor_outliers > 0 %>
|
120
|
+
<% css_class = "danger" if major_outliers > 0 %>
|
121
121
|
<code class="<%= css_class %>"><%= number_with_delimiter(total_outliers) %>
|
122
122
|
</td>
|
123
123
|
</tr>
|
@@ -142,9 +142,9 @@ tfoot td {
|
|
142
142
|
<tr>
|
143
143
|
<td>
|
144
144
|
<% outlier_result.csv_files.each do |csv_file| %>
|
145
|
-
<% css_class =
|
146
|
-
<% css_class =
|
147
|
-
<code class="<%= css_class %>"><%= csv_file.gsub(/^csvs\/#{@subject_loader.csv_directory}\//,
|
145
|
+
<% css_class = "success" if outlier_result.outliers.size == 0 %>
|
146
|
+
<% css_class = "warning-pale" if outlier_result.major_outliers.size == 0 and outlier_result.minor_outliers.size > 0 %>
|
147
|
+
<code class="<%= css_class %>"><%= csv_file.gsub(/^csvs\/#{@subject_loader.csv_directory}\//, "") %></code><br />
|
148
148
|
<% end %></td>
|
149
149
|
<td><%= outlier_result.method %></td>
|
150
150
|
<td><%= outlier_result.display_name.to_s[0..20] %></td>
|
data/lib/spout.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "spout/version"
|
4
4
|
|
5
|
-
require
|
5
|
+
require "spout/models/dictionary"
|
6
6
|
|
7
7
|
Spout::COMMANDS = {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
"c" => :coverage_report,
|
9
|
+
"d" => :deploy,
|
10
|
+
"e" => :exporter,
|
11
|
+
"g" => :generate_charts_and_tables,
|
12
|
+
"i" => :importer,
|
13
|
+
"n" => :new_project,
|
14
|
+
"o" => :outliers_report,
|
15
|
+
"t" => :test,
|
16
|
+
"u" => :update,
|
17
|
+
"v" => :version
|
18
18
|
}
|
19
19
|
|
20
20
|
# Launch spout commands from command line.
|
@@ -24,56 +24,56 @@ module Spout
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.new_project(argv)
|
27
|
-
require
|
27
|
+
require "spout/commands/project_generator"
|
28
28
|
Spout::Commands::ProjectGenerator.new(argv)
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.coverage_report(argv)
|
32
|
-
require
|
32
|
+
require "spout/commands/coverage"
|
33
33
|
Spout::Commands::Coverage.new(standard_version, argv)
|
34
34
|
rescue NoMemoryError
|
35
35
|
puts "[NoMemoryError] You made Spout cry... Spout doesn't run on potatoes :'-("
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.exporter(argv)
|
39
|
-
require
|
39
|
+
require "spout/commands/exporter"
|
40
40
|
Spout::Commands::Exporter.new(standard_version, argv)
|
41
41
|
end
|
42
42
|
|
43
43
|
def self.generate_charts_and_tables(argv)
|
44
44
|
argv = argv.last(argv.size - 1)
|
45
|
-
require
|
45
|
+
require "spout/commands/graphs"
|
46
46
|
Spout::Commands::Graphs.new(argv, standard_version)
|
47
47
|
end
|
48
48
|
|
49
49
|
def self.help(argv)
|
50
|
-
require
|
50
|
+
require "spout/commands/help"
|
51
51
|
Spout::Commands::Help.new(argv)
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.deploy(argv)
|
55
|
-
require
|
55
|
+
require "spout/commands/deploy"
|
56
56
|
Spout::Commands::Deploy.new(argv, standard_version)
|
57
57
|
end
|
58
58
|
|
59
59
|
def self.importer(argv)
|
60
|
-
require
|
60
|
+
require "spout/commands/importer"
|
61
61
|
Spout::Commands::Importer.new(argv)
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.outliers_report(argv)
|
65
|
-
require
|
65
|
+
require "spout/commands/outliers"
|
66
66
|
Spout::Commands::Outliers.new(standard_version, argv)
|
67
67
|
end
|
68
68
|
|
69
69
|
def self.test(_argv)
|
70
|
-
system
|
71
|
-
# require
|
70
|
+
system "bundle exec rake"
|
71
|
+
# require "spout/commands/test_runner"
|
72
72
|
# Spout::Commands::TestRunner.new(argv)
|
73
73
|
end
|
74
74
|
|
75
75
|
def self.update(argv)
|
76
|
-
require
|
76
|
+
require "spout/commands/update"
|
77
77
|
Spout::Commands::Update.start(argv)
|
78
78
|
end
|
79
79
|
|
@@ -82,9 +82,9 @@ module Spout
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def self.standard_version
|
85
|
-
version = File.open(
|
86
|
-
version ==
|
85
|
+
version = File.open("VERSION", &:readline).strip
|
86
|
+
version == "" ? "1.0.0" : version
|
87
87
|
rescue
|
88
|
-
|
88
|
+
"1.0.0"
|
89
89
|
end
|
90
90
|
end
|
data/spout.gemspec
CHANGED
@@ -8,34 +8,34 @@
|
|
8
8
|
# gem list -r spout
|
9
9
|
# gem install spout
|
10
10
|
|
11
|
-
lib = File.expand_path(
|
11
|
+
lib = File.expand_path("../lib", __FILE__)
|
12
12
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
13
|
-
require
|
13
|
+
require "spout/version"
|
14
14
|
|
15
15
|
Gem::Specification.new do |spec|
|
16
|
-
spec.name =
|
16
|
+
spec.name = "spout"
|
17
17
|
spec.version = Spout::VERSION::STRING
|
18
|
-
spec.authors = [
|
19
|
-
spec.email = [
|
20
|
-
spec.description =
|
21
|
-
spec.summary =
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
spec.homepage =
|
28
|
-
spec.license =
|
18
|
+
spec.authors = ["Remo Mueller"]
|
19
|
+
spec.email = ["remosm@gmail.com"]
|
20
|
+
spec.description = "Manage your data dictionary as a JSON repository, and easily export back to CSV."
|
21
|
+
spec.summary = "Turn your CSV data dictionary into a JSON repository. "\
|
22
|
+
"Collaborate with others to update the data dictionary "\
|
23
|
+
"in JSON format. Generate new Data Dictionary from the "\
|
24
|
+
"JSON repository. Test and validate your data "\
|
25
|
+
"dictionary using built-in tests, or add your own for "\
|
26
|
+
"further validations."
|
27
|
+
spec.homepage = "https://github.com/sleepepi/spout"
|
28
|
+
spec.license = "MIT"
|
29
29
|
|
30
|
-
spec.files = Dir[
|
30
|
+
spec.files = Dir["{bin,lib}/**/*"] + ["CHANGELOG.md", "LICENSE", "Rakefile", "README.md", "spout.gemspec"]
|
31
31
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
32
32
|
spec.test_files = spec.files.grep(%r{^(test)/})
|
33
|
-
spec.require_paths = [
|
33
|
+
spec.require_paths = ["lib"]
|
34
34
|
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_dependency
|
38
|
-
spec.add_dependency
|
39
|
-
spec.add_dependency
|
40
|
-
spec.add_dependency
|
35
|
+
spec.add_dependency "bundler", "~> 1.13"
|
36
|
+
spec.add_dependency "rake"
|
37
|
+
spec.add_dependency "minitest"
|
38
|
+
spec.add_dependency "minitest-reporters"
|
39
|
+
spec.add_dependency "json"
|
40
|
+
spec.add_dependency "colorize", "~> 0.8.1"
|
41
41
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Remo Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -205,12 +205,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
205
205
|
version: '0'
|
206
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
207
|
requirements:
|
208
|
-
- - "
|
208
|
+
- - ">"
|
209
209
|
- !ruby/object:Gem::Version
|
210
|
-
version:
|
210
|
+
version: 1.3.1
|
211
211
|
requirements: []
|
212
212
|
rubyforge_project:
|
213
|
-
rubygems_version: 2.6.
|
213
|
+
rubygems_version: 2.6.12
|
214
214
|
signing_key:
|
215
215
|
specification_version: 4
|
216
216
|
summary: Turn your CSV data dictionary into a JSON repository. Collaborate with others
|