marty 2.5.2 → 2.5.4
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 +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
|