marty 6.1.0 → 8.0.0

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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +17 -3
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +3 -2
  6. data/Gemfile +2 -1
  7. data/app/assets/javascripts/marty/extjs/extensions/marty.js +23 -1
  8. data/app/assets/stylesheets/marty/application.css +4 -1
  9. data/app/assets/stylesheets/marty/dark_mode.css +17 -0
  10. data/app/components/marty/auth_app.rb +10 -1
  11. data/app/components/marty/auth_app/client/auth_app.js +4 -0
  12. data/app/components/marty/extras/layout.rb +2 -2
  13. data/app/components/marty/extras/misc.rb +1 -1
  14. data/app/components/marty/log_view.rb +0 -1
  15. data/app/components/marty/main_auth_app.rb +9 -12
  16. data/app/components/marty/promise_view.rb +5 -0
  17. data/app/components/marty/promise_view/client/promise_view.js +11 -0
  18. data/app/components/marty/schedule_jobs_dashboard.rb +30 -96
  19. data/app/components/marty/schedule_jobs_grid.rb +118 -0
  20. data/app/controllers/marty/application_controller.rb +6 -2
  21. data/app/models/marty/base.rb +48 -48
  22. data/app/models/marty/config.rb +14 -2
  23. data/app/models/marty/user.rb +12 -0
  24. data/app/services/marty/background_job/fetch_missing_in_schedule_cron_jobs.rb +19 -0
  25. data/app/services/marty/enums/report.rb +18 -0
  26. data/app/services/marty/promises/delorean/create.rb +16 -27
  27. data/app/services/marty/promises/ruby/create.rb +10 -1
  28. data/app/views/layouts/marty/application.html.erb +4 -1
  29. data/app/views/marty/diagnostic/op.html.erb +3 -1
  30. data/config/locales/en.yml +2 -0
  31. data/db/migrate/512_add_promise_priority.rb +9 -0
  32. data/db/migrate/513_add_priority_to_promise_view.rb +44 -0
  33. data/db/migrate/514_remove_marty_events.rb +13 -0
  34. data/delorean/enum_report.dl +11 -0
  35. data/delorean/table_report.dl +7 -0
  36. data/docker-compose.dummy.yml +7 -4
  37. data/lib/marty.rb +5 -2
  38. data/lib/marty/api/base.rb +15 -9
  39. data/lib/marty/cache_adapters.rb +2 -0
  40. data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -5
  41. data/lib/marty/cache_adapters/memory_and_redis.rb +93 -0
  42. data/lib/marty/cache_adapters/redis.rb +63 -0
  43. data/lib/marty/delayed_job/scheduled_job_plugin.rb +33 -0
  44. data/lib/marty/diagnostic/database.rb +1 -2
  45. data/lib/marty/logger.rb +50 -17
  46. data/lib/marty/monkey.rb +26 -6
  47. data/lib/marty/promise_ruby_job.rb +2 -0
  48. data/lib/marty/rails_app.rb +29 -0
  49. data/lib/marty/railtie.rb +1 -0
  50. data/lib/marty/version.rb +1 -1
  51. data/marty.gemspec +1 -0
  52. data/spec/controllers/job_controller_spec.rb +2 -2
  53. data/spec/dummy/app/components/gemini/cm_auth_app.rb +12 -0
  54. data/spec/dummy/app/components/gemini/simple_view.rb +17 -0
  55. data/spec/dummy/app/jobs/test_failing_job.rb +14 -0
  56. data/spec/dummy/app/models/gemini/helper.rb +90 -1
  57. data/spec/dummy/config/application.rb +1 -0
  58. data/spec/dummy/db/migrate/20191101132729_add_activity_flag_to_simple.rb +10 -0
  59. data/spec/dummy/delorean/blame_report.dl +1 -0
  60. data/spec/dummy/delorean/enum_report.dl +1 -0
  61. data/spec/dummy/delorean/marty_fields.dl +1 -0
  62. data/spec/dummy/delorean/table_report.dl +1 -0
  63. data/spec/features/data_blame_report_spec.rb +66 -0
  64. data/spec/features/data_grid_spec.rb +1 -1
  65. data/spec/features/enum_values_report_spec.rb +76 -0
  66. data/spec/features/inline_editing_spec.rb +33 -0
  67. data/spec/features/rule_spec.rb +1 -1
  68. data/spec/features/schedule_jobs_dashboard_spec.rb +1 -1
  69. data/spec/features/scripting_spec.rb +1 -1
  70. data/spec/features/user_list_report_spec.rb +74 -0
  71. data/spec/fixtures/misc/struct_compare_tests.txt +15 -5
  72. data/spec/job_helper.rb +39 -0
  73. data/spec/jobs/cron_job_spec.rb +91 -0
  74. data/spec/lib/mcfly_model_spec.rb +9 -0
  75. data/spec/models/promise_spec.rb +168 -1
  76. data/spec/other/diagnostic/delayed_job_workers_spec.rb +1 -1
  77. data/spec/performance/caching_spec.rb +99 -0
  78. data/spec/services/background_job/fetch_missing_in_schedule_cron_jobs_spec.rb +34 -0
  79. data/spec/support/delayed_job_helpers.rb +3 -3
  80. data/spec/support/shared_connection.rb +9 -1
  81. data/spec/support/structure_compare.rb +19 -3
  82. metadata +39 -6
  83. data/app/components/marty/event_view.rb +0 -129
  84. data/app/models/marty/event.rb +0 -317
  85. data/spec/dummy/db/migrate/20160923183516_add_bulk_pricing_event_ops.rb +0 -8
  86. data/spec/dummy/delorean/blame_report.dl +0 -268
  87. data/spec/dummy/delorean/marty_fields.dl +0 -63
  88. data/spec/dummy/delorean/table_report.dl +0 -34
  89. data/spec/models/event_spec.rb +0 -272
@@ -1,317 +0,0 @@
1
- class Marty::Event < Marty::Base
2
- class EventValidator < ActiveModel::Validator
3
- def validate(event)
4
- event.errors[:base] << 'Must have promise_id or start_dt' unless
5
- event.promise_id || event.start_dt
6
- end
7
- end
8
-
9
- validates_presence_of :klass, :subject_id, :enum_event_operation
10
-
11
- belongs_to :promise
12
-
13
- validates_with EventValidator
14
-
15
- after_validation(on: [:create, :update]) do
16
- self.comment = comment.truncate(255) if comment
17
- end
18
-
19
- UPDATE_SQL = <<SQL
20
- UPDATE marty_events as me
21
- SET start_dt = p.start_dt,
22
- end_dt = p.end_dt
23
- FROM marty_promises p
24
- WHERE me.promise_id = p.id
25
- AND ( ( p.start_dt IS NOT NULL
26
- AND me.start_dt IS NULL
27
- )
28
- OR ( p.end_dt IS NOT NULL
29
- AND me.end_dt IS NULL
30
- )
31
- )
32
- SQL
33
- BASE_QUERY = <<SQL
34
- SELECT id,
35
- klass,
36
- subject_id,
37
- enum_event_operation,
38
- comment,
39
- start_dt,
40
- end_dt,
41
- expire_secs,
42
- error
43
- FROM marty_events
44
- SQL
45
- def self.running_query(time_now_s)
46
- "#{BASE_QUERY}
47
- WHERE start_dt >= '#{time_now_s}'::timestamp - interval '24 hours'
48
- AND (end_dt IS NULL or end_dt > '#{time_now_s}'::timestamp)
49
- AND (expire_secs IS NULL
50
- OR expire_secs > EXTRACT (EPOCH FROM '#{time_now_s}'::timestamp - start_dt))
51
- ORDER BY start_dt"
52
- end
53
-
54
- def self.op_is_running?(klass, subject_id, operation)
55
- all_running.detect do |pm|
56
- pm['klass'] == klass && pm['subject_id'].to_i == subject_id.to_i &&
57
- pm['enum_event_operation'] == operation
58
- end
59
- end
60
-
61
- def self.create_event(klass,
62
- subject_id,
63
- operation,
64
- start_dt,
65
- expire_secs,
66
- comment = nil)
67
-
68
- # use lookup_event instead of all_running which is throttled
69
- evs = lookup_event(klass, subject_id, operation)
70
- running = evs.detect do |ev|
71
- next if ev['end_dt']
72
- next true unless ev['expire_secs']
73
-
74
- (Time.zone.now - ev['start_dt']).truncate < ev['expire_secs']
75
- end
76
-
77
- raise "#{operation} is already running for #{klass}/#{subject_id}" if
78
- running
79
-
80
- create!(klass: klass,
81
- subject_id: subject_id,
82
- enum_event_operation: operation,
83
- start_dt: start_dt,
84
- expire_secs: expire_secs,
85
- comment: comment,
86
- )
87
- end
88
-
89
- def self.lookup_event(klass, subject_id, operation)
90
- update_start_and_end
91
- get_data("#{BASE_QUERY}
92
- WHERE klass = '#{klass}'
93
- AND subject_id = #{subject_id}
94
- AND enum_event_operation = '#{operation}'")
95
-
96
- # For now we return a bare hash
97
- # Marty::Event.find_by_id(hash["id"])
98
- end
99
-
100
- def self.finish_event(klass, subject_id, operation, error = false, comment = nil)
101
- raise 'error must be true or false' unless [true, false].include?(error)
102
-
103
- time_now_s = Time.zone.now.strftime('%Y-%m-%d %H:%M:%S.%6N')
104
-
105
- event = get_data(running_query(time_now_s)).detect do |ev|
106
- ev['klass'] == klass && ev['subject_id'] == subject_id.to_i &&
107
- ev['enum_event_operation'] == operation
108
- end
109
- raise "event #{klass}/#{subject_id}/#{operation} not found" unless
110
- event
111
-
112
- ev = Marty::Event.find_by_id(event['id'])
113
- raise "can't explicitly finish a promise event" if ev.promise_id
114
-
115
- ev.end_dt = Time.zone.now
116
- ev.error = error
117
- ev.comment = comment if comment
118
- ev.save!
119
- end
120
-
121
- def self.last_event(klass, subject_id, operation = nil)
122
- hash = all_running.reverse.find do |pm|
123
- pm['klass'] == klass && pm['subject_id'] == subject_id.to_i &&
124
- (operation.nil? || pm['enum_event_operation'] == operation)
125
- end
126
-
127
- return hash if hash
128
-
129
- op_sql = "AND enum_event_operation = '#{operation}'" if operation
130
-
131
- get_data("#{BASE_QUERY}
132
- WHERE klass = '#{klass}'
133
- AND subject_id = #{subject_id} #{op_sql}
134
- AND end_dt IS NOT NULL
135
- ORDER BY end_dt desc").first
136
- end
137
-
138
- def self.last_event_multi(klass, subject_ids_arg, operation = nil)
139
- subject_ids = subject_ids_arg.map(&:to_i)
140
- events = all_running.select do |pm|
141
- pm['klass'] == klass && subject_ids.include?(pm['subject_id']) &&
142
- (operation.nil? || pm['enum_event_operation'] == operation)
143
- end.group_by { |ev| ev['subject_id'] }.each_with_object({}) do |(id, evs), h|
144
- h[id] = evs.sort { |a, b| a['start_dt'] <=> b['start_dt'] }.first
145
- end
146
-
147
- running_ids = events.keys
148
- check_fin = subject_ids - running_ids
149
-
150
- if check_fin.present?
151
- op_filt = "AND enum_event_operation = '#{operation}'" if operation
152
- op_col = ', enum_event_operation' if operation
153
-
154
- fins = get_data("SELECT klass,
155
- subject_id,
156
- enum_event_operation,
157
- comment,
158
- start_dt,
159
- end_dt,
160
- expire_secs,
161
- error
162
- FROM (SELECT klass,
163
- subject_id,
164
- enum_event_operation,
165
- comment,
166
- start_dt,
167
- end_dt,
168
- expire_secs,
169
- error,
170
- ROW_NUMBER() OVER (PARTITION BY klass,
171
- subject_id
172
- #{op_col}
173
- ORDER BY end_dt DESC) rnum
174
- FROM marty_events
175
- WHERE klass = '#{klass}'
176
- AND subject_id IN (#{check_fin.join(',')})
177
- #{op_filt}
178
- AND end_dt IS NOT NULL) sub
179
- WHERE rnum = 1")
180
-
181
- fins.each do |fin|
182
- events[fin['subject_id']] = fin
183
- end
184
- end
185
- events
186
- end
187
-
188
- def self.currently_running(klass, subject_id)
189
- all_running.select do |pm|
190
- pm['klass'] == klass && pm['subject_id'] == subject_id.to_i
191
- end.map { |e| e['enum_event_operation'] }
192
- end
193
-
194
- def self.currently_running_multi(klass, subject_id_raw)
195
- subject_ids = [subject_id_raw].flatten.map(&:to_i)
196
- all_running.select do |pm|
197
- pm['klass'] == klass && subject_ids.include?(pm['subject_id'])
198
- end.each_with_object({}) do |e, h|
199
- (h[e['subject_id']] ||= []) << e['enum_event_operation']
200
- end
201
- end
202
-
203
- def self.update_comment(hash, comment)
204
- hid = hash.is_a?(Hash) ? hash['id'] : hash
205
- e = Marty::Event.find_by_id(hid)
206
- e.comment = comment
207
- e.save!
208
- end
209
-
210
- def self.pretty_op(hash)
211
- d = hash['enum_event_operation'].downcase.capitalize
212
-
213
- # && !(hash['comment'] =~ /^ERROR/)
214
- hash['end_dt'] ? d.sub(/ing/, 'ed') : d
215
- end
216
-
217
- def self.compact_end_dt(hash)
218
- hash['end_dt'] ? hash['end_dt'].strftime('%H:%M') : '---'
219
- end
220
-
221
- def self.update_start_and_end
222
- ActiveRecord::Base.connection.execute(UPDATE_SQL).cmd_tuples
223
- end
224
-
225
- def self.get_data(sql)
226
- ActiveRecord::Base.connection.execute(sql).to_a.map do |h|
227
- h['id'] = h['id'].to_i
228
- h['subject_id'] = h['subject_id'].to_i
229
- h['start_dt'] = Time.zone.parse(h['start_dt']) if h['start_dt']
230
- h['end_dt'] = Time.zone.parse(h['end_dt']) if h['end_dt']
231
- h['expire_secs'] = h['expire_secs'].to_i if h['expire_secs']
232
- h['comment'] = h['comment']
233
- h['error'] = h['error']
234
- h
235
- end
236
- end
237
- private_class_method :get_data
238
-
239
- def self.clear_cache
240
- @poll_secs = @all_running = @all_finished = nil
241
- end
242
-
243
- def self.all_running
244
- @all_running ||= { timestamp: 0, data: [] }
245
- @poll_secs ||= Marty::Config['MARTY_EVENT_POLL_SECS'] || 0
246
- time_now = Time.zone.now
247
- time_now_i = time_now.to_i
248
- time_now_s = time_now.strftime('%Y-%m-%d %H:%M:%S.%6N')
249
- upd_count = update_start_and_end
250
- if upd_count > 0 ||
251
- time_now_i - @all_running[:timestamp] > @poll_secs
252
- @all_running[:data] = get_data(running_query(time_now_s))
253
- @all_running[:timestamp] = time_now_i
254
- end
255
- @all_running[:data]
256
- end
257
- private_class_method :all_running
258
-
259
- def self.all_finished
260
- @all_finished ||= {
261
- data: {},
262
- timestamp: Time.zone.parse('00:00:00').to_i,
263
- }
264
- @poll_secs ||= Marty::Config['MARTY_EVENT_POLL_SECS'] || 0
265
- time_now_i = Time.zone.now.to_i
266
- cutoff = Time.zone.at(@all_finished[:timestamp]).
267
- strftime('%Y-%m-%d %H:%M:%S.%6N')
268
-
269
- upd_count = update_start_and_end
270
- if upd_count > 0 ||
271
- time_now_i - @all_finished[:timestamp] > @poll_secs
272
- raw = get_data("
273
- SELECT * FROM (SELECT id
274
- , klass
275
- , subject_id
276
- , enum_event_operation
277
- , start_dt
278
- , end_dt
279
- , expire_secs
280
- , comment
281
- , error
282
- , ROW_NUMBER() OVER (PARTITION BY
283
- klass
284
- , subject_id
285
- , enum_event_operation
286
- ORDER BY end_dt DESC) row_num
287
- FROM marty_events
288
- WHERE end_dt > '#{cutoff}') sub
289
- WHERE row_num = 1")
290
- @all_finished[:timestamp] = time_now_i
291
- raw.each_with_object(@all_finished[:data]) do |ev, hash|
292
- if ev['end_dt'] && ev['error'].nil?
293
- real_ev = Marty::Event.where(id: ev['id']).first
294
- promise = Marty::Promise.where(id: real_ev['promise_id']).first
295
- maybe_error = promise.result['error']
296
- ev['error'] = real_ev.error = !!maybe_error
297
- real_ev.comment = maybe_error
298
- real_ev.save!
299
- end
300
- subhash = hash[[ev['klass'], ev['subject_id']]] ||= {}
301
- subhash[ev['enum_event_operation']] =
302
- ev['end_dt'].strftime('%Y-%m-%d %H:%M:%S')
303
- end
304
- end
305
- @all_finished[:data]
306
- end
307
-
308
- def self.get_finished(klass, id)
309
- all_finished[[klass, id]]
310
- end
311
-
312
- def self.cleanup
313
- where('start_dt < ?', Time.zone.now - 48.hours).delete_all
314
- rescue StandardError => e
315
- Marty::Util.logger.error("event GC error: #{e}")
316
- end
317
- end
@@ -1,8 +0,0 @@
1
- class AddBulkPricingEventOps < ActiveRecord::Migration[4.2]
2
- disable_ddl_transaction!
3
- def change
4
- execute "ALTER TYPE enum_event_operations ADD VALUE 'PRICING';"
5
- execute "ALTER TYPE enum_event_operations ADD VALUE 'CRA';"
6
- execute "ALTER TYPE enum_event_operations ADD VALUE 'AVM';"
7
- end
8
- end
@@ -1,268 +0,0 @@
1
- import MartyFields
2
- import Styles
3
-
4
- PostingField2: MartyFields::PostingField2
5
- store = [ ["NOW", "NOW"] ] + [ [ lp.name, lp.name + ' (' + lp.comment + ')']
6
- for lp in Marty::Posting.get_latest_by_type(
7
- 20, ['BASE', 'INTRA', 'CLOSE'])]
8
-
9
- StyleRow:
10
- profile =?
11
- obj = profile["obj"]
12
- status = profile["status"]
13
- attrs = profile["attrs"]
14
- user_name = Marty::DataChange.user_name(obj.user_id)
15
- delete = profile["deleted"]
16
- create = status == "new"
17
-
18
- o_user_name = if delete then Marty::DataChange.user_name(obj.o_user_id)
19
- else ""
20
- o_dt = if delete then obj.obsoleted_dt.to_s else ""
21
- del_style = if delete then Styles::Style.bg_redish else {}
22
- mod_style = Styles::Style.bg_tan
23
- new_style = if create then Styles::Style.bg_lightgreen else {}
24
-
25
- b_style = Styles::Style.calibri + new_style
26
- b_c_style = b_style + mod_style
27
- m_style = b_style + Styles::Style.bg_lightgray + del_style
28
- m_d_style = m_style + Styles::Style.datetime
29
-
30
- row = [
31
- obj.group_id,
32
- obj.created_dt.to_s,
33
- user_name,
34
- o_dt,
35
- o_user_name,
36
- ] + [ a.value for a in attrs]
37
-
38
- row_styles = [
39
- m_style,
40
- m_d_style,
41
- m_style,
42
- m_d_style,
43
- m_style,
44
- ] + [
45
- (if a.changed then b_c_style else b_style)
46
- for a in attrs
47
- ]
48
-
49
- s_row = ["row", row, {"style": row_styles}]
50
-
51
- StyleGroup:
52
- group =?
53
- rows = [
54
- StyleRow(profile = profile).s_row
55
- for profile in group
56
- ]
57
-
58
- ModelRows:
59
- klass =?
60
- t1 =?
61
- t2 =?
62
- ids =?
63
-
64
- groups = Marty::DataChange.changes(t1, t2, klass, ids)
65
-
66
- headers = ["Group ID", "Created", "By", "Deleted", "By"] +
67
- Marty::DataChange.class_headers(klass)
68
-
69
- width = headers.length
70
- border = [
71
- "border",
72
- [0, {"off": 1}, width, {"off": 1}],
73
- Styles::Style.border_thin,
74
- ]
75
-
76
- row_groups = [
77
- StyleGroup(group = x[1]).rows + [border]
78
- for x in groups
79
- ]
80
-
81
- s_style = {"style": [Styles::Style.s_hdr] * width}
82
- s_headers = ["row", headers, s_style]
83
- rows = [s_headers] + row_groups.flatten(1)
84
- count = row_groups.length
85
- ws = if count > 0 then [klass, rows] else nil
86
-
87
- OptionalIDsField: MartyFields::TextField
88
- field_label = "Group IDs (optional)"
89
- name = "restrict_to_ids"
90
-
91
- DataBlameReport:
92
- title = "Data Blame Report"
93
-
94
- pt_name1 =?
95
- posting1 =? Marty::Posting.lookup(pt_name1)
96
- t1 = posting1.created_dt
97
-
98
- pt_name2 =?
99
- posting2 =? Marty::Posting.lookup(pt_name2)
100
- t2 = posting2.created_dt
101
-
102
- class_list =? false
103
- restrict_to_ids =? nil
104
-
105
- form = [
106
- MartyFields::PostingField1,
107
- PostingField2,
108
- MartyFields::ClassListField,
109
- OptionalIDsField
110
- ]
111
-
112
- ts = if Marty::Helper.infinity_dt(t1)
113
- then [t2, t1]
114
- else if Marty::Helper.infinity_dt(t2)
115
- then [t1, t2]
116
- else [t1, t2].sort
117
-
118
- sanitized = if class_list
119
- then Marty::DataChange.sanitize_classes(class_list)
120
- else Marty::DataChange.class_list
121
-
122
- ids = if restrict_to_ids
123
- then [ idstr.to_i for idstr in restrict_to_ids.split(',') ]
124
- else nil
125
-
126
- ids_check = if ids && ids.length > 0 && sanitized.length > 1
127
- then ERR("Can't specify Group IDs if more than one class selected")
128
- else true
129
-
130
- result = ids_check && [
131
- ModelRows(t1 = ts[0], t2 = ts[1], klass = klass,
132
- ids = ids).ws
133
- for klass in sanitized
134
- ].compact
135
-
136
- format = "xlsx"
137
-
138
- ModelSummaryRow:
139
- klass =?
140
- t1 =?
141
- t2 =?
142
-
143
- r = Marty::DataChange.change_summary(t1, t2, klass)
144
- cr = r['created']
145
- up = r['updated']
146
- dl = r['deleted']
147
-
148
- ws = if cr > 0 || up > 0 || dl > 0 then ["row", [klass, cr, up, dl]] else nil
149
-
150
- DataBlameReportSummary:
151
- title = "Summary Data Blame Report"
152
-
153
- pt_name1 =?
154
- posting1 =? Marty::Posting.lookup(pt_name1)
155
- t1 = posting1.created_dt
156
-
157
- pt_name2 =?
158
- posting2 =? Marty::Posting.lookup(pt_name2)
159
- t2 = posting2.created_dt
160
-
161
- form = [
162
- MartyFields::PostingField1,
163
- PostingField2,
164
- ]
165
-
166
- postings = [
167
- ["Posting 1", pt_name1,],
168
- ["Posting 2", pt_name2,],
169
- ]
170
-
171
- hrow = [
172
- "row", ["Data Table", "Created", "Updated", "Deleted",],
173
- Styles::Style.m_hdr_style0,
174
- ]
175
-
176
- ts = if Marty::Helper.infinity_dt(t1)
177
- then [t2, t1]
178
- else if Marty::Helper.infinity_dt(t2)
179
- then [t1, t2]
180
- else [t1, t2].sort
181
-
182
- rows = [
183
- ModelSummaryRow(t1 = ts[0], t2 = ts[1], klass = klass).ws
184
- for klass in Marty::DataChange.class_list
185
- ].compact
186
-
187
- header = [["row", r, {"style" : [Styles::Style.s_hdr]}] for r in postings]
188
- result = [[title,[hrow] + rows, {"widths" : [30]}], ["Parameters", header]]
189
- format = "xlsx"
190
-
191
- DeadReferenceReport:
192
- title = "Dead Reference Report"
193
-
194
- class_list =? false
195
- pt_name =?
196
- posting =? Marty::Posting.lookup(pt_name)
197
-
198
- sanitized = if class_list
199
- then Marty::DataChange.sanitize_classes(class_list)
200
- else Marty::DataChange.class_list
201
-
202
- form = [
203
- MartyFields::PostingField,
204
- MartyFields::ClassListField,
205
- ]
206
-
207
- result = [
208
- [[[klass, attr, obj].flatten for obj in list]
209
- for attr, list in Marty::DataChange.dead_refs(posting.created_dt,
210
- klass)].flatten(1)
211
- for klass in sanitized
212
- ].flatten(1)
213
-
214
- format = "csv"
215
-
216
- ######################################################################
217
-
218
- DataImportParam:
219
- field_label = "Input Rows"
220
- name = "data_import_field"
221
- xtype = ":textareafield"
222
- scrollable = true
223
- field_style = {
224
- "font_family": 'courier new',
225
- "font_size": '12px',
226
- }
227
- height = 600
228
-
229
- CommaSepField: MartyFields::CheckboxField
230
- name = "comma_sep"
231
- field_label = "Comma Separated"
232
-
233
- DiffReport:
234
- title = "Diff Report"
235
-
236
- class_name =? false
237
- data_import_field =?
238
- comma_sep =? false
239
-
240
- klass = Marty::Helper.constantize(class_name)
241
-
242
- col_types = Marty::Helper.get_column_types(klass)
243
- data = Marty::Helper.parse_csv_to_hash(
244
- data_import_field, comma_sep, col_types)
245
-
246
- diff = Marty::DataChange.diff(klass, data)
247
-
248
- result = [{"title" : "different",
249
- "format" : "csv",
250
- "result" : diff["different"].flatten},
251
- {"title" : "same",
252
- "format" : "csv",
253
- "result" : diff["same"]},
254
- {"title" : "only_input",
255
- "format" : "csv",
256
- "result" : diff["only_input"]},
257
- {"title" : "only_source",
258
- "format" : "csv",
259
- "result" : diff["only_source"]},
260
- ]
261
-
262
- form = [
263
- MartyFields::ClassField,
264
- DataImportParam,
265
- CommaSepField,
266
- ]
267
-
268
- format = "zip"