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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +17 -3
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +3 -2
- data/Gemfile +2 -1
- data/app/assets/javascripts/marty/extjs/extensions/marty.js +23 -1
- data/app/assets/stylesheets/marty/application.css +4 -1
- data/app/assets/stylesheets/marty/dark_mode.css +17 -0
- data/app/components/marty/auth_app.rb +10 -1
- data/app/components/marty/auth_app/client/auth_app.js +4 -0
- data/app/components/marty/extras/layout.rb +2 -2
- data/app/components/marty/extras/misc.rb +1 -1
- data/app/components/marty/log_view.rb +0 -1
- data/app/components/marty/main_auth_app.rb +9 -12
- data/app/components/marty/promise_view.rb +5 -0
- data/app/components/marty/promise_view/client/promise_view.js +11 -0
- data/app/components/marty/schedule_jobs_dashboard.rb +30 -96
- data/app/components/marty/schedule_jobs_grid.rb +118 -0
- data/app/controllers/marty/application_controller.rb +6 -2
- data/app/models/marty/base.rb +48 -48
- data/app/models/marty/config.rb +14 -2
- data/app/models/marty/user.rb +12 -0
- data/app/services/marty/background_job/fetch_missing_in_schedule_cron_jobs.rb +19 -0
- data/app/services/marty/enums/report.rb +18 -0
- data/app/services/marty/promises/delorean/create.rb +16 -27
- data/app/services/marty/promises/ruby/create.rb +10 -1
- data/app/views/layouts/marty/application.html.erb +4 -1
- data/app/views/marty/diagnostic/op.html.erb +3 -1
- data/config/locales/en.yml +2 -0
- data/db/migrate/512_add_promise_priority.rb +9 -0
- data/db/migrate/513_add_priority_to_promise_view.rb +44 -0
- data/db/migrate/514_remove_marty_events.rb +13 -0
- data/delorean/enum_report.dl +11 -0
- data/delorean/table_report.dl +7 -0
- data/docker-compose.dummy.yml +7 -4
- data/lib/marty.rb +5 -2
- data/lib/marty/api/base.rb +15 -9
- data/lib/marty/cache_adapters.rb +2 -0
- data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -5
- data/lib/marty/cache_adapters/memory_and_redis.rb +93 -0
- data/lib/marty/cache_adapters/redis.rb +63 -0
- data/lib/marty/delayed_job/scheduled_job_plugin.rb +33 -0
- data/lib/marty/diagnostic/database.rb +1 -2
- data/lib/marty/logger.rb +50 -17
- data/lib/marty/monkey.rb +26 -6
- data/lib/marty/promise_ruby_job.rb +2 -0
- data/lib/marty/rails_app.rb +29 -0
- data/lib/marty/railtie.rb +1 -0
- data/lib/marty/version.rb +1 -1
- data/marty.gemspec +1 -0
- data/spec/controllers/job_controller_spec.rb +2 -2
- data/spec/dummy/app/components/gemini/cm_auth_app.rb +12 -0
- data/spec/dummy/app/components/gemini/simple_view.rb +17 -0
- data/spec/dummy/app/jobs/test_failing_job.rb +14 -0
- data/spec/dummy/app/models/gemini/helper.rb +90 -1
- data/spec/dummy/config/application.rb +1 -0
- data/spec/dummy/db/migrate/20191101132729_add_activity_flag_to_simple.rb +10 -0
- data/spec/dummy/delorean/blame_report.dl +1 -0
- data/spec/dummy/delorean/enum_report.dl +1 -0
- data/spec/dummy/delorean/marty_fields.dl +1 -0
- data/spec/dummy/delorean/table_report.dl +1 -0
- data/spec/features/data_blame_report_spec.rb +66 -0
- data/spec/features/data_grid_spec.rb +1 -1
- data/spec/features/enum_values_report_spec.rb +76 -0
- data/spec/features/inline_editing_spec.rb +33 -0
- data/spec/features/rule_spec.rb +1 -1
- data/spec/features/schedule_jobs_dashboard_spec.rb +1 -1
- data/spec/features/scripting_spec.rb +1 -1
- data/spec/features/user_list_report_spec.rb +74 -0
- data/spec/fixtures/misc/struct_compare_tests.txt +15 -5
- data/spec/job_helper.rb +39 -0
- data/spec/jobs/cron_job_spec.rb +91 -0
- data/spec/lib/mcfly_model_spec.rb +9 -0
- data/spec/models/promise_spec.rb +168 -1
- data/spec/other/diagnostic/delayed_job_workers_spec.rb +1 -1
- data/spec/performance/caching_spec.rb +99 -0
- data/spec/services/background_job/fetch_missing_in_schedule_cron_jobs_spec.rb +34 -0
- data/spec/support/delayed_job_helpers.rb +3 -3
- data/spec/support/shared_connection.rb +9 -1
- data/spec/support/structure_compare.rb +19 -3
- metadata +39 -6
- data/app/components/marty/event_view.rb +0 -129
- data/app/models/marty/event.rb +0 -317
- data/spec/dummy/db/migrate/20160923183516_add_bulk_pricing_event_ops.rb +0 -8
- data/spec/dummy/delorean/blame_report.dl +0 -268
- data/spec/dummy/delorean/marty_fields.dl +0 -63
- data/spec/dummy/delorean/table_report.dl +0 -34
- data/spec/models/event_spec.rb +0 -272
data/app/models/marty/event.rb
DELETED
@@ -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"
|