marty 2.5.2 → 2.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +4 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +11 -589
- data/Gemfile +9 -9
- data/Gemfile.lock +1 -1
- data/Rakefile +1 -3
- data/app/components/marty/api_auth_view.rb +3 -3
- data/app/components/marty/api_config_view.rb +8 -8
- data/app/components/marty/api_log_view.rb +16 -20
- data/app/components/marty/auth_app.rb +6 -6
- data/app/components/marty/base_rule_view.rb +27 -19
- data/app/components/marty/config_view.rb +12 -9
- data/app/components/marty/data_grid_view.rb +26 -26
- data/app/components/marty/delorean_rule_view.rb +0 -1
- data/app/components/marty/event_view.rb +27 -27
- data/app/components/marty/extras/layout.rb +26 -26
- data/app/components/marty/extras/misc.rb +2 -2
- data/app/components/marty/grid.rb +13 -13
- data/app/components/marty/grid_append_only.rb +0 -1
- data/app/components/marty/import_type_view.rb +13 -13
- data/app/components/marty/import_view.rb +17 -16
- data/app/components/marty/log_view.rb +16 -14
- data/app/components/marty/main_auth_app.rb +59 -59
- data/app/components/marty/main_auth_app/client/main_auth_app.js +3 -3
- data/app/components/marty/mcfly_grid_panel.rb +10 -10
- data/app/components/marty/new_posting_form.rb +11 -11
- data/app/components/marty/new_posting_window.rb +0 -1
- data/app/components/marty/posting_grid.rb +12 -13
- data/app/components/marty/promise_view.rb +6 -6
- data/app/components/marty/report_form.rb +50 -53
- data/app/components/marty/report_select.rb +27 -27
- data/app/components/marty/reporting.rb +4 -4
- data/app/components/marty/script_form.rb +40 -42
- data/app/components/marty/script_grid.rb +24 -24
- data/app/components/marty/script_tester.rb +40 -42
- data/app/components/marty/scripting.rb +25 -27
- data/app/components/marty/simple_app.rb +24 -9
- data/app/components/marty/tag_grid.rb +12 -13
- data/app/components/marty/user_view.rb +35 -35
- data/app/controllers/marty/application_controller.rb +3 -4
- data/app/controllers/marty/components_controller.rb +1 -1
- data/app/controllers/marty/delayed_job_controller.rb +1 -0
- data/app/controllers/marty/diagnostic/controller.rb +4 -6
- data/app/controllers/marty/job_controller.rb +6 -6
- data/app/controllers/marty/report_controller.rb +11 -11
- data/app/controllers/marty/rpc_controller.rb +15 -16
- data/app/helpers/marty/script_set.rb +4 -4
- data/app/models/marty/api_auth.rb +4 -5
- data/app/models/marty/api_config.rb +1 -1
- data/app/models/marty/base.rb +9 -8
- data/app/models/marty/base_rule.rb +18 -13
- data/app/models/marty/config.rb +4 -5
- data/app/models/marty/data_grid.rb +157 -181
- data/app/models/marty/delorean_rule.rb +63 -62
- data/app/models/marty/enum.rb +1 -1
- data/app/models/marty/event.rb +56 -59
- data/app/models/marty/helper.rb +38 -6
- data/app/models/marty/import_type.rb +6 -6
- data/app/models/marty/log.rb +3 -2
- data/app/models/marty/name_validator.rb +3 -2
- data/app/models/marty/pg_enum.rb +3 -4
- data/app/models/marty/posting.rb +20 -24
- data/app/models/marty/promise.rb +28 -30
- data/app/models/marty/script.rb +30 -28
- data/app/models/marty/tag.rb +8 -8
- data/app/models/marty/token.rb +2 -2
- data/app/models/marty/user.rb +24 -23
- data/app/models/marty/vw_promise.rb +10 -11
- data/config/routes.rb +2 -2
- data/delorean/blame_report.dl +268 -0
- data/{spec/dummy/delorean/fields.dl → delorean/marty_fields.dl} +8 -0
- data/delorean/table_report.dl +34 -0
- data/docker-compose.dummy.yml +2 -3
- data/lib/marty/aws/base.rb +8 -8
- data/lib/marty/aws/request.rb +4 -4
- data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -0
- data/lib/marty/content_handler.rb +25 -25
- data/lib/marty/data_change.rb +49 -71
- data/lib/marty/data_conversion.rb +20 -28
- data/lib/marty/data_exporter.rb +25 -28
- data/lib/marty/data_importer.rb +25 -27
- data/lib/marty/engine.rb +1 -2
- data/lib/marty/json_schema.rb +22 -24
- data/lib/marty/logger.rb +6 -9
- data/lib/marty/mcfly_model.rb +20 -24
- data/lib/marty/migrations.rb +37 -35
- data/lib/marty/monkey.rb +33 -33
- data/lib/marty/permissions.rb +18 -18
- data/lib/marty/promise_job.rb +17 -17
- data/lib/marty/promise_proxy.rb +6 -6
- data/lib/marty/relation.rb +6 -7
- data/lib/marty/rpc_call.rb +13 -12
- data/lib/marty/rule_script_set.rb +32 -28
- data/lib/marty/schema_helper.rb +37 -51
- data/lib/marty/util.rb +25 -24
- data/lib/marty/version.rb +1 -1
- data/lib/marty/xl.rb +121 -115
- data/make-dummy.mk +3 -0
- data/marty.gemspec +21 -21
- data/other/marty/api/base.rb +34 -35
- data/other/marty/diagnostic/aws/ec2_instance.rb +8 -8
- data/other/marty/diagnostic/base.rb +13 -14
- data/other/marty/diagnostic/collection.rb +2 -1
- data/other/marty/diagnostic/connections.rb +8 -6
- data/other/marty/diagnostic/database.rb +1 -0
- data/other/marty/diagnostic/delayed_job_version.rb +7 -9
- data/other/marty/diagnostic/delayed_job_worker_total_count.rb +1 -1
- data/other/marty/diagnostic/delayed_job_workers.rb +1 -1
- data/other/marty/diagnostic/environment_variables.rb +17 -15
- data/other/marty/diagnostic/fatal.rb +1 -1
- data/other/marty/diagnostic/node.rb +5 -9
- data/other/marty/diagnostic/nodes.rb +7 -5
- data/other/marty/diagnostic/packer.rb +7 -7
- data/other/marty/diagnostic/reporter.rb +24 -27
- data/other/marty/diagnostic/version.rb +3 -5
- data/script/rails +2 -1
- data/spec/controllers/application_controller_spec.rb +6 -6
- data/spec/controllers/delayed_job_controller_spec.rb +4 -4
- data/spec/controllers/diagnostic/controller_spec.rb +59 -60
- data/spec/controllers/job_controller_spec.rb +68 -69
- data/spec/controllers/rpc_controller_spec.rb +353 -359
- data/spec/controllers/rpc_import_spec.rb +15 -16
- data/spec/dummy/delorean/blame_report.dl +110 -15
- data/spec/dummy/delorean/data_report.dl +4 -4
- data/spec/dummy/delorean/marty_fields.dl +63 -0
- data/spec/dummy/delorean/table_report.dl +34 -0
- data/spec/features/auth_app_spec.rb +1 -2
- data/spec/features/data_import_spec.rb +2 -3
- data/spec/features/enum_spec.rb +42 -46
- data/spec/features/jobs_dashboard_spec.rb +14 -8
- data/spec/features/log_view_spec.rb +40 -43
- data/spec/features/reporting_spec.rb +15 -15
- data/spec/features/rule_spec.rb +195 -190
- data/spec/features/scripting_spec.rb +17 -20
- data/spec/features/scripting_test_spec.rb +32 -33
- data/spec/features/user_view_spec.rb +15 -17
- data/spec/job_helper.rb +11 -11
- data/spec/lib/data_blame_spec.rb +82 -0
- data/spec/lib/data_exporter_spec.rb +31 -32
- data/spec/lib/data_importer_spec.rb +382 -395
- data/spec/lib/delorean_query_spec.rb +117 -119
- data/spec/lib/json_schema_spec.rb +382 -392
- data/spec/lib/logger_spec.rb +23 -24
- data/spec/lib/mcfly_model_spec.rb +112 -109
- data/spec/lib/migrations_spec.rb +10 -10
- data/spec/lib/struct_compare_spec.rb +6 -6
- data/spec/lib/table_report_spec.rb +90 -0
- data/spec/lib/xl_spec.rb +63 -65
- data/spec/lib/xl_styles_spec.rb +16 -19
- data/spec/models/api_auth_spec.rb +30 -30
- data/spec/models/config_spec.rb +32 -32
- data/spec/models/data_grid_spec.rb +642 -655
- data/spec/models/event_spec.rb +96 -88
- data/spec/models/import_type_spec.rb +20 -20
- data/spec/models/posting_spec.rb +35 -35
- data/spec/models/promise_spec.rb +5 -5
- data/spec/models/rule_spec.rb +280 -269
- data/spec/models/script_spec.rb +27 -18
- data/spec/models/user_spec.rb +9 -9
- data/spec/other/diagnostic/base_spec.rb +20 -19
- data/spec/other/diagnostic/collection_spec.rb +6 -5
- data/spec/other/diagnostic/delayed_job_version_spec.rb +1 -1
- data/spec/other/diagnostic/delayed_job_workers_spec.rb +8 -8
- data/spec/other/diagnostic/reporter_spec.rb +31 -33
- data/spec/spec_helper.rb +5 -5
- data/spec/support/chromedriver.rb +3 -5
- data/spec/support/components/netzke_combobox.rb +1 -1
- data/spec/support/components/netzke_grid.rb +17 -17
- data/spec/support/custom_matchers.rb +2 -2
- data/spec/support/download_helper.rb +1 -1
- data/spec/support/helper.rb +1 -2
- data/spec/support/netzke.rb +31 -31
- data/spec/support/performance_helper.rb +8 -8
- data/spec/support/post_run_logger.rb +1 -2
- data/spec/support/setup.rb +1 -4
- data/spec/support/shared_connection.rb +2 -2
- data/spec/support/structure_compare.rb +21 -22
- data/spec/support/suite.rb +1 -2
- data/spec/support/users.rb +5 -6
- metadata +32 -26
@@ -1,5 +1,5 @@
|
|
1
1
|
class Marty::DataConversion
|
2
|
-
EXCEL_START_DATE = Date.parse('1/1/1900')-2
|
2
|
+
EXCEL_START_DATE = Date.parse('1/1/1900') - 2
|
3
3
|
|
4
4
|
FLOAT_PAT = /^-?\d+(\.\d+)?$/
|
5
5
|
|
@@ -40,8 +40,8 @@ class Marty::DataConversion
|
|
40
40
|
case type
|
41
41
|
when :boolean
|
42
42
|
case v.to_s.downcase
|
43
|
-
when
|
44
|
-
when
|
43
|
+
when 'true', '1', 'y', 't' then true
|
44
|
+
when 'false', '0', 'n', 'f' then false
|
45
45
|
else raise "unknown boolean: #{v.inspect}"
|
46
46
|
end
|
47
47
|
when :string, :text, :enum
|
@@ -58,13 +58,13 @@ class Marty::DataConversion
|
|
58
58
|
begin
|
59
59
|
v =~ FLOAT_PAT ? EXCEL_START_DATE + v.to_f :
|
60
60
|
Mcfly.is_infinity(v) ? 'infinity' : v.to_date
|
61
|
-
rescue => exc
|
61
|
+
rescue StandardError => exc
|
62
62
|
raise "date conversion failed for #{v.inspect}}"
|
63
63
|
end
|
64
64
|
when :datetime
|
65
65
|
begin
|
66
66
|
Mcfly.is_infinity(v) ? 'infinity' : v.to_datetime
|
67
|
-
rescue => exc
|
67
|
+
rescue StandardError => exc
|
68
68
|
raise "datetime conversion failed for #{v.inspect}}"
|
69
69
|
end
|
70
70
|
when :numrange, :int4range, :int8range
|
@@ -81,11 +81,13 @@ class Marty::DataConversion
|
|
81
81
|
|
82
82
|
def self.assoc_keys(klass)
|
83
83
|
return Mcfly.mcfly_uniqueness(klass) if Mcfly.has_mcfly?(klass)
|
84
|
+
|
84
85
|
# FIXME: very hacky -- picks 1st non-id attr as the association
|
85
86
|
# key for regular (non-mcfly) AR models which don't have
|
86
87
|
# MARTY_IMPORT_UNIQUENESS.
|
87
88
|
klass.const_get(:MARTY_IMPORT_UNIQUENESS) rescue [
|
88
|
-
|
89
|
+
klass.column_names.reject { |x| x == 'id' }.first.to_sym
|
90
|
+
]
|
89
91
|
end
|
90
92
|
|
91
93
|
@@associations = {}
|
@@ -95,8 +97,7 @@ class Marty::DataConversion
|
|
95
97
|
# enables find/import of its database records
|
96
98
|
|
97
99
|
@@associations[klass] ||= klass.reflect_on_all_associations.
|
98
|
-
each_with_object({}) do
|
99
|
-
|assoc, h|
|
100
|
+
each_with_object({}) do |assoc, h|
|
100
101
|
|
101
102
|
h[assoc.name.to_s] = {
|
102
103
|
assoc_keys: assoc_keys(assoc.klass),
|
@@ -119,9 +120,7 @@ class Marty::DataConversion
|
|
119
120
|
# build profile for ActiveRecord non-assoc columns -- used to
|
120
121
|
# find/import of klass database records.
|
121
122
|
|
122
|
-
@@col_types[klass] ||= klass.columns.each_with_object({}) do
|
123
|
-
|col, h|
|
124
|
-
|
123
|
+
@@col_types[klass] ||= klass.columns.each_with_object({}) do |col, h|
|
125
124
|
assoc ||= associations(klass)
|
126
125
|
acols ||= assoc_cols(klass)
|
127
126
|
|
@@ -153,12 +152,12 @@ class Marty::DataConversion
|
|
153
152
|
|
154
153
|
raise "no key_attrs for #{klass}" unless key_attrs
|
155
154
|
|
156
|
-
find_options = options.select { |k,v| key_attrs.member? k.to_sym }
|
155
|
+
find_options = options.select { |k, v| key_attrs.member? k.to_sym }
|
157
156
|
|
158
157
|
raise "no keys for #{klass} -- #{options}" if find_options.empty?
|
159
158
|
|
160
159
|
q = klass.where(find_options)
|
161
|
-
q = q.where(
|
160
|
+
q = q.where('obsoleted_dt >= ? AND created_dt < ?', dt, dt) if
|
162
161
|
dt && Mcfly.has_mcfly?(klass)
|
163
162
|
|
164
163
|
# q.count is almost always 0 or 1 => hopefully it's not too slow on PG.
|
@@ -176,20 +175,17 @@ class Marty::DataConversion
|
|
176
175
|
ctypes = col_types(klass)
|
177
176
|
assoc = associations(klass)
|
178
177
|
|
179
|
-
raise "bad row (extra columns?) -- #{row}" if row.
|
178
|
+
raise "bad row (extra columns?) -- #{row}" if row.key?(nil)
|
180
179
|
|
181
|
-
key_groups = row.keys.group_by {|x| x.to_s.split('__').first}
|
180
|
+
key_groups = row.keys.group_by { |x| x.to_s.split('__').first }
|
182
181
|
|
183
182
|
# FIXME: map all empty string values to nil --- this means that
|
184
183
|
# user can't import empty strings -- Perhaps, mapping "" -> nil
|
185
184
|
# should be optional?
|
186
|
-
row = row.each_with_object({})
|
187
|
-
|(k,v), h|
|
185
|
+
row = row.each_with_object({}) do |(k, v), h|
|
188
186
|
h[k.to_s] = v == '' ? nil : v
|
189
|
-
|
190
|
-
key_groups.each_with_object({}) do
|
191
|
-
|(ga, g), h|
|
192
|
-
|
187
|
+
end
|
188
|
+
key_groups.each_with_object({}) do |(ga, g), h|
|
193
189
|
# find the association's details
|
194
190
|
ai = assoc[ga]
|
195
191
|
|
@@ -234,7 +230,7 @@ class Marty::DataConversion
|
|
234
230
|
|
235
231
|
# If it's an Enum, use the faster cached looked mechanism
|
236
232
|
if Marty::Enum === srch_class
|
237
|
-
h[fk] = srch_class[
|
233
|
+
h[fk] = srch_class[v].id
|
238
234
|
next
|
239
235
|
end
|
240
236
|
end
|
@@ -244,9 +240,7 @@ class Marty::DataConversion
|
|
244
240
|
|
245
241
|
# build a new row map for this association, we need to convert
|
246
242
|
# it and search for it.
|
247
|
-
arow = g.each_with_object({}) do
|
248
|
-
|k, h|
|
249
|
-
|
243
|
+
arow = g.each_with_object({}) do |k, h|
|
250
244
|
# Some old exports don't provide full assoc__attr column names
|
251
245
|
# (e.g. 'xxx_name'). Instead the columns are just named by
|
252
246
|
# assoc (e.g. 'xxx').
|
@@ -263,7 +257,6 @@ class Marty::DataConversion
|
|
263
257
|
|
264
258
|
h[fk] = o_arow.id
|
265
259
|
end
|
266
|
-
|
267
260
|
end
|
268
261
|
|
269
262
|
######################################################################
|
@@ -279,8 +272,7 @@ class Marty::DataConversion
|
|
279
272
|
|
280
273
|
obj ||= klass.new
|
281
274
|
|
282
|
-
c_row.each do
|
283
|
-
|k, v|
|
275
|
+
c_row.each do |k, v|
|
284
276
|
# For each attr, check to see if it's begin changed before
|
285
277
|
# setting it. The AR obj.changed? doesn't work properly
|
286
278
|
# with array, JSON or lazy attrs.
|
data/lib/marty/data_exporter.rb
CHANGED
@@ -18,11 +18,11 @@ class Marty::DataExporter
|
|
18
18
|
|
19
19
|
keys = hash_array_keys(hl)
|
20
20
|
|
21
|
-
return keys.each_with_object({})
|
21
|
+
return keys.each_with_object({}) do |k, rh|
|
22
22
|
rh[k] = hl.map { |h| h[k] }
|
23
|
-
|
23
|
+
end if transpose
|
24
24
|
|
25
|
-
[keys.to_a] + hl.map {|h| keys.map {|k| h[k]}}
|
25
|
+
[keys.to_a] + hl.map { |h| keys.map { |k| h[k] } }
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.encode_json(s)
|
@@ -33,19 +33,19 @@ class Marty::DataExporter
|
|
33
33
|
Zlib.inflate Base64.strict_decode64(s)
|
34
34
|
end
|
35
35
|
|
36
|
-
def self.to_csv(obj, config=nil)
|
36
|
+
def self.to_csv(obj, config = nil)
|
37
37
|
obj = [obj] unless obj.respond_to? :map
|
38
38
|
|
39
39
|
config ||= {}
|
40
40
|
|
41
41
|
# if all array items are hashes, we merge them
|
42
|
-
obj = hash_array_merge(obj, config[
|
43
|
-
obj.is_a?(Array) && obj.all? {|x| x.is_a? Hash}
|
42
|
+
obj = hash_array_merge(obj, config['transpose']) if
|
43
|
+
obj.is_a?(Array) && obj.all? { |x| x.is_a? Hash }
|
44
44
|
|
45
45
|
# symbolize config keys as expected by CSV.generate
|
46
|
-
conf = config.each_with_object({})
|
47
|
-
h[k.to_sym] = v unless k.to_s ==
|
48
|
-
|
46
|
+
conf = config.each_with_object({}) do |(k, v), h|
|
47
|
+
h[k.to_sym] = v unless k.to_s == 'transpose'
|
48
|
+
end
|
49
49
|
|
50
50
|
# FIXME: very hacky to default row_sep to CRLF
|
51
51
|
conf[:row_sep] ||= "\r\n"
|
@@ -69,7 +69,7 @@ class Marty::DataExporter
|
|
69
69
|
CSV.generate(conf) do |csv|
|
70
70
|
obj.each do |x|
|
71
71
|
x = [x] unless x.respond_to? :map
|
72
|
-
csv << x.map
|
72
|
+
csv << x.map do |v|
|
73
73
|
case v
|
74
74
|
when Array, Hash
|
75
75
|
readable ? v.to_json : encode_json(v.to_json)
|
@@ -78,25 +78,24 @@ class Marty::DataExporter
|
|
78
78
|
else
|
79
79
|
v.to_s
|
80
80
|
end
|
81
|
-
|
81
|
+
end
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
86
|
def self.get_attrs_in_order(klass, attrs)
|
87
87
|
return attrs unless klass.const_defined?(:EXPORT_ORDER)
|
88
|
+
|
88
89
|
klass::EXPORT_ORDER.select { |attr| attrs.include?(attr) }
|
89
90
|
end
|
90
91
|
|
91
|
-
def self.export_attrs(klass, obj, attrs=nil, exclude_attrs=[])
|
92
|
+
def self.export_attrs(klass, obj, attrs = nil, exclude_attrs = [])
|
92
93
|
col_types = Marty::DataConversion.col_types(klass)
|
93
94
|
|
94
95
|
attr_list_raw = (attrs || col_types.keys).map(&:to_s) - exclude_attrs
|
95
96
|
attr_list = get_attrs_in_order(klass, attr_list_raw)
|
96
97
|
|
97
|
-
attr_list.map do
|
98
|
-
|c|
|
99
|
-
|
98
|
+
attr_list.map do |c|
|
100
99
|
v = obj.send(c.to_sym)
|
101
100
|
type = col_types[c]
|
102
101
|
|
@@ -104,7 +103,7 @@ class Marty::DataExporter
|
|
104
103
|
next [v] if !type.is_a?(Hash)
|
105
104
|
|
106
105
|
# no child row, return nils for each field
|
107
|
-
next [nil]*type[:assoc_keys].count if v.nil?
|
106
|
+
next [nil] * type[:assoc_keys].count if v.nil?
|
108
107
|
|
109
108
|
assoc_keys = type[:assoc_keys]
|
110
109
|
assoc_class = type[:assoc_class]
|
@@ -117,15 +116,13 @@ class Marty::DataExporter
|
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
120
|
-
def self.export_headers(klass, attrs=nil, exclude_attrs=[])
|
119
|
+
def self.export_headers(klass, attrs = nil, exclude_attrs = [])
|
121
120
|
col_types = Marty::DataConversion.col_types(klass)
|
122
121
|
|
123
122
|
attr_list_raw = (attrs || col_types.keys).map(&:to_s) - exclude_attrs
|
124
123
|
attr_list = get_attrs_in_order(klass, attr_list_raw)
|
125
124
|
|
126
|
-
attr_list.map do
|
127
|
-
|c|
|
128
|
-
|
125
|
+
attr_list.map do |c|
|
129
126
|
type = col_types[c]
|
130
127
|
|
131
128
|
next c unless type.is_a?(Hash)
|
@@ -139,30 +136,30 @@ class Marty::DataExporter
|
|
139
136
|
|
140
137
|
assoc_class = type[:assoc_class]
|
141
138
|
|
142
|
-
export_headers(assoc_class, assoc_keys).map {|k| "#{c}__#{k}"}
|
139
|
+
export_headers(assoc_class, assoc_keys).map { |k| "#{c}__#{k}" }
|
143
140
|
end
|
144
141
|
end
|
145
142
|
|
146
143
|
# Given a Mcfly klass, generate an export array. Can potentially
|
147
144
|
# use up a lot of memory if the result set is large.
|
148
|
-
def self.do_export(ts, klass, sort_field=nil, exclude_attrs=[])
|
145
|
+
def self.do_export(ts, klass, sort_field = nil, exclude_attrs = [])
|
149
146
|
query = klass
|
150
147
|
|
151
148
|
if Mcfly.has_mcfly?(klass)
|
152
149
|
ts = Mcfly.normalize_infinity(ts)
|
153
|
-
query = query.where(
|
150
|
+
query = query.where('obsoleted_dt >= ? AND created_dt < ?', ts, ts)
|
154
151
|
end
|
155
152
|
|
156
153
|
do_export_query_result(klass, query.order(sort_field || :id), exclude_attrs)
|
157
154
|
end
|
158
155
|
|
159
|
-
def self.do_export_query_result(klass, qres, exclude_attrs=[])
|
156
|
+
def self.do_export_query_result(klass, qres, exclude_attrs = [])
|
160
157
|
# strip _id from assoc fields
|
161
|
-
header = [
|
158
|
+
header = [export_headers(klass, nil, exclude_attrs).flatten]
|
162
159
|
|
163
|
-
header + qres.map
|
160
|
+
header + qres.map do |obj|
|
164
161
|
export_attrs(klass, obj, nil, exclude_attrs).flatten(1)
|
165
|
-
|
162
|
+
end
|
166
163
|
end
|
167
164
|
|
168
165
|
# Export a single object to hash -- FIXME: inefficient
|
@@ -171,6 +168,6 @@ class Marty::DataExporter
|
|
171
168
|
klass = obj.class
|
172
169
|
headers = export_headers(klass)
|
173
170
|
rec = export_attrs(klass, obj).flatten
|
174
|
-
Hash[
|
171
|
+
Hash[headers.zip(rec)]
|
175
172
|
end
|
176
173
|
end
|
data/lib/marty/data_importer.rb
CHANGED
@@ -20,21 +20,21 @@ module Marty
|
|
20
20
|
col_sep = "\t",
|
21
21
|
allow_dups = false,
|
22
22
|
preprocess_function = nil
|
23
|
-
|
24
|
-
|
25
|
-
recs =
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
recs.each_with_object(Hash.new(0))
|
23
|
+
)
|
24
|
+
|
25
|
+
recs = do_import(klass,
|
26
|
+
data,
|
27
|
+
dt,
|
28
|
+
cleaner_function,
|
29
|
+
validation_function,
|
30
|
+
col_sep,
|
31
|
+
allow_dups,
|
32
|
+
preprocess_function,
|
33
|
+
)
|
34
|
+
|
35
|
+
recs.each_with_object(Hash.new(0)) do |(op, id), h|
|
36
36
|
h[op] += 1
|
37
|
-
|
37
|
+
end
|
38
38
|
end
|
39
39
|
|
40
40
|
# Given a Mcfly klass and CSV data, import data into the database
|
@@ -50,7 +50,7 @@ module Marty
|
|
50
50
|
col_sep = "\t",
|
51
51
|
allow_dups = false,
|
52
52
|
preprocess_function = nil
|
53
|
-
|
53
|
+
)
|
54
54
|
|
55
55
|
parsed = data.is_a?(Array) ? data :
|
56
56
|
CSV.new(data, headers: true, col_sep: col_sep)
|
@@ -63,14 +63,13 @@ module Marty
|
|
63
63
|
cleaner_ids = cleaner_function ? klass.send(cleaner_function.to_sym) :
|
64
64
|
[]
|
65
65
|
|
66
|
-
raise
|
67
|
-
cleaner_ids.all? {|id| id.is_a?(Integer) }
|
66
|
+
raise 'bad cleaner function result' unless
|
67
|
+
cleaner_ids.all? { |id| id.is_a?(Integer) }
|
68
68
|
|
69
69
|
eline = 0
|
70
70
|
|
71
71
|
begin
|
72
|
-
res = parsed.each_with_index.map do
|
73
|
-
|row, line|
|
72
|
+
res = parsed.each_with_index.map do |row, line|
|
74
73
|
eline = line
|
75
74
|
|
76
75
|
# skip lines which are all nil
|
@@ -78,17 +77,16 @@ module Marty
|
|
78
77
|
|
79
78
|
Marty::DataConversion.create_or_update(klass, row, dt)
|
80
79
|
end
|
81
|
-
rescue => exc
|
80
|
+
rescue StandardError => exc
|
82
81
|
# to find problems with the importer, comment out the rescue block
|
83
82
|
raise Error.new(exc.to_s, [eline])
|
84
83
|
end
|
85
84
|
|
86
85
|
ids = {}
|
87
86
|
# raise an error if record referenced more than once.
|
88
|
-
res.each_with_index do
|
89
|
-
|(op, id), line|
|
87
|
+
res.each_with_index do |(op, id), line|
|
90
88
|
raise Error.
|
91
|
-
new(
|
89
|
+
new('record referenced more than once', [ids[id], line]) if
|
92
90
|
op != :blank && ids.member?(id) && !allow_dups
|
93
91
|
|
94
92
|
ids[id] = line
|
@@ -98,19 +96,19 @@ module Marty
|
|
98
96
|
# Validate affected rows if necessary
|
99
97
|
klass.send(validation_function.to_sym, ids.keys) if
|
100
98
|
validation_function
|
101
|
-
rescue => exc
|
99
|
+
rescue StandardError => exc
|
102
100
|
raise Error.new(exc.to_s, [])
|
103
101
|
end
|
104
102
|
|
105
103
|
remainder_ids = cleaner_ids - ids.keys
|
106
104
|
|
107
105
|
raise Error.
|
108
|
-
new(
|
109
|
-
|
106
|
+
new('Missing import data. ' +
|
107
|
+
'Please provide header line and at least one data line.', [1]) if
|
110
108
|
ids.keys.compact.count == 0
|
111
109
|
|
112
110
|
klass.delete(remainder_ids)
|
113
|
-
res + remainder_ids.map {|id| [:clean, id]}
|
111
|
+
res + remainder_ids.map { |id| [:clean, id] }
|
114
112
|
end
|
115
113
|
end
|
116
114
|
end
|
data/lib/marty/engine.rb
CHANGED
data/lib/marty/json_schema.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'json-schema'
|
2
2
|
|
3
3
|
module Marty
|
4
|
-
|
5
4
|
private
|
5
|
+
|
6
6
|
class PgEnumAttribute < JSON::Schema::Attribute
|
7
|
-
def self.validate(curr_schema, data, frag, pro, validator, opt={})
|
7
|
+
def self.validate(curr_schema, data, frag, pro, validator, opt = {})
|
8
8
|
values = nil
|
9
9
|
path = '#/' + frag.join('/')
|
10
10
|
begin
|
11
|
-
cs = curr_schema.schema[
|
11
|
+
cs = curr_schema.schema['pg_enum']
|
12
12
|
enum = cs.constantize
|
13
13
|
values = enum::VALUES
|
14
|
-
rescue => e
|
14
|
+
rescue StandardError => e
|
15
15
|
msg = "The property '#{path}': '#{cs}' is not a pg_enum class"
|
16
16
|
validation_error(pro, msg, frag, curr_schema, self, opt[:record_errors])
|
17
17
|
end
|
@@ -23,25 +23,25 @@ module Marty
|
|
23
23
|
end
|
24
24
|
|
25
25
|
class JsonSchema < JSON::Schema::Draft4
|
26
|
-
RAW_URI =
|
26
|
+
RAW_URI = 'http://json-schema.org/marty-draft/schema#'
|
27
27
|
|
28
28
|
def initialize
|
29
29
|
super
|
30
|
-
@attributes[
|
31
|
-
@formats[
|
32
|
-
@formats[
|
30
|
+
@attributes['pg_enum'] = PgEnumAttribute
|
31
|
+
@formats['date-time'] = JSON::Schema::DateTimeFormat
|
32
|
+
@formats['date'] = JSON::Schema::DateFormat
|
33
33
|
@uri = JSON::Util::URI.parse(RAW_URI)
|
34
|
-
@names = [
|
34
|
+
@names = ['marty-draft', RAW_URI]
|
35
35
|
end
|
36
36
|
|
37
|
-
JSON::Validator.register_validator(
|
37
|
+
JSON::Validator.register_validator(new)
|
38
38
|
|
39
39
|
def self.get_numbers(schema)
|
40
40
|
numbers = []
|
41
41
|
|
42
42
|
# traverse the schema, if we find a type: number, add to numbers []
|
43
|
-
trav = lambda { |tree, key, path=[]|
|
44
|
-
return tree.each do|k, v|
|
43
|
+
trav = lambda { |tree, key, path = []|
|
44
|
+
return tree.each do |k, v|
|
45
45
|
trav.call(v, k, path + [k])
|
46
46
|
end if tree.is_a?(Hash)
|
47
47
|
numbers << path[0..-2] if key == 'type' && tree == 'number'
|
@@ -50,16 +50,16 @@ module Marty
|
|
50
50
|
|
51
51
|
# convert the array stuff [ie. "items", "properties"] to :array
|
52
52
|
numbers.map do |num|
|
53
|
-
num.delete(
|
54
|
-
num.map{|n| n==
|
53
|
+
num.delete('properties')
|
54
|
+
num.map { |n| n == 'items' ? :array : n }
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
def self.fix_numbers(json, numbers)
|
59
|
-
|
60
59
|
# follow path to drill into json
|
61
|
-
drill = lambda {|tree, path|
|
60
|
+
drill = lambda { |tree, path|
|
62
61
|
return unless tree
|
62
|
+
|
63
63
|
key = path.first
|
64
64
|
val = val = tree.send(:[], key) unless key == :array
|
65
65
|
if key == :array
|
@@ -70,7 +70,7 @@ module Marty
|
|
70
70
|
end
|
71
71
|
else
|
72
72
|
# this is an array of object so continue to drill down
|
73
|
-
tree.each {|sub| drill.call(sub, path[1..-1])}
|
73
|
+
tree.each { |sub| drill.call(sub, path[1..-1]) }
|
74
74
|
end
|
75
75
|
elsif path.length == 1
|
76
76
|
# fix a non array field
|
@@ -80,24 +80,22 @@ module Marty
|
|
80
80
|
drill.call(val, path[1..-1])
|
81
81
|
end
|
82
82
|
}
|
83
|
-
numbers.each {|number| drill.call(json, number)}
|
83
|
+
numbers.each { |number| drill.call(json, number) }
|
84
84
|
end
|
85
85
|
|
86
86
|
def self.get_schema(tag, sname, node, attr)
|
87
|
-
|
88
|
-
Marty::ScriptSet.new(tag).get_engine(sname+'Schemas').
|
87
|
+
Marty::ScriptSet.new(tag).get_engine(sname + 'Schemas').
|
89
88
|
evaluate(node, attr, {})
|
90
|
-
|
89
|
+
rescue StandardError => e
|
91
90
|
id = "#{sname}/#{node} attrs=#{attr}"
|
92
91
|
|
93
92
|
# the schema DL might not exist at all, or might not define the attr
|
94
93
|
# being requested
|
95
94
|
sch_not_found = ['No such script', "undefined method `#{attr}__D'",
|
96
95
|
"node #{node} is undefined"]
|
97
|
-
msg = sch_not_found.detect{|msg| e.message.starts_with?(msg)} ?
|
96
|
+
msg = sch_not_found.detect { |msg| e.message.starts_with?(msg) } ?
|
98
97
|
'Schema not defined' : "Problem with schema: #{e.message}"
|
99
|
-
|
100
|
-
end
|
98
|
+
"Schema error for #{id}: #{msg}"
|
101
99
|
end
|
102
100
|
end
|
103
101
|
end
|