importo 2.0.5 → 3.0.10
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/README.md +22 -5
- data/Rakefile +16 -17
- data/app/controllers/concerns/maintenance_standards.rb +1 -1
- data/app/controllers/importo/imports_controller.rb +15 -12
- data/app/helpers/importo/application_helper.rb +11 -11
- data/app/importers/concerns/exportable.rb +31 -24
- data/app/importers/concerns/importable.rb +144 -62
- data/app/importers/concerns/importer_dsl.rb +6 -14
- data/app/importers/concerns/original.rb +23 -23
- data/app/importers/concerns/result_feedback.rb +62 -44
- data/app/importers/concerns/revertable.rb +11 -12
- data/app/importers/importo/base_importer.rb +16 -4
- data/app/importers/importo/import_job_callback.rb +29 -0
- data/app/jobs/importo/import_job.rb +40 -0
- data/app/mailers/importo/application_mailer.rb +2 -2
- data/app/models/importo/import.rb +35 -8
- data/app/models/importo/result.rb +5 -0
- data/app/services/importo/import_service.rb +1 -3
- data/app/services/importo/revert_service.rb +1 -1
- data/app/tables/importo/imports_table.rb +13 -17
- data/app/tables/importo/mensa_imports_table.rb +35 -0
- data/app/views/importo/imports/index.html.slim +6 -2
- data/app/views/importo/imports/new.html.slim +30 -20
- data/config/locales/en.yml +21 -9
- data/config/locales/nl.yml +10 -4
- data/config/routes.rb +4 -4
- data/db/migrate/20180409151031_create_importo_import.rb +9 -9
- data/db/migrate/20180628175535_add_locale_importo_import.rb +1 -1
- data/db/migrate/20230510051447_remove_result_from_imports.rb +5 -0
- data/db/migrate/20230510083043_create_importo_results.rb +11 -0
- data/lib/generators/importo/importer_generator.rb +1 -1
- data/lib/generators/importo/install_generator.rb +7 -8
- data/lib/generators/importo/templates/importo.rb +11 -0
- data/lib/generators/satis/install_generator.rb +22 -0
- data/lib/generators/satis/tailwind_config_generator.rb +24 -0
- data/lib/generators/satis/templates/config/initializers/satis.rb +13 -0
- data/lib/importo/acts_as_import_owner.rb +1 -1
- data/lib/importo/configuration.rb +52 -49
- data/lib/importo/engine.rb +11 -9
- data/lib/importo/import_column.rb +18 -6
- data/lib/importo/test_helpers.rb +19 -0
- data/lib/importo/version.rb +1 -1
- data/lib/importo.rb +12 -17
- metadata +75 -24
- data/app/services/importo/callback_service.rb +0 -14
- data/lib/generators/templates/importo.rb +0 -21
- /data/lib/generators/{templates → importo/templates}/README +0 -0
- /data/lib/generators/{templates → importo/templates}/application_importer.rb +0 -0
- /data/lib/generators/{templates → importo/templates}/importer.rb +0 -0
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
require
|
3
|
+
require "active_support/concern"
|
5
4
|
|
6
5
|
module Original
|
7
6
|
extend ActiveSupport::Concern
|
@@ -9,11 +8,11 @@ module Original
|
|
9
8
|
def original
|
10
9
|
return @original if @original && !@original.is_a?(Hash)
|
11
10
|
|
12
|
-
if import.respond_to?(:attachment_changes) && import.attachment_changes[
|
13
|
-
@original ||= import.attachment_changes[
|
11
|
+
if import.respond_to?(:attachment_changes) && import.attachment_changes["original"]
|
12
|
+
@original ||= import.attachment_changes["original"]&.attachable
|
14
13
|
|
15
14
|
if @original.is_a?(Hash)
|
16
|
-
tempfile = Tempfile.new([
|
15
|
+
tempfile = Tempfile.new(["ActiveStorage", import.original.filename.extension_with_delimiter])
|
17
16
|
tempfile.binmode
|
18
17
|
tempfile.write(@original[:io].read)
|
19
18
|
@original[:io].rewind
|
@@ -23,7 +22,7 @@ module Original
|
|
23
22
|
else
|
24
23
|
return unless import&.original&.attachment
|
25
24
|
|
26
|
-
@original = Tempfile.new([
|
25
|
+
@original = Tempfile.new(["ActiveStorage", import.original.filename.extension_with_delimiter])
|
27
26
|
@original.binmode
|
28
27
|
import.original.download { |block| @original.write(block) }
|
29
28
|
@original.flush
|
@@ -80,8 +79,8 @@ module Original
|
|
80
79
|
end
|
81
80
|
|
82
81
|
def invalid_header_names_for_row(index)
|
83
|
-
stripped_headers = allowed_header_names.map { |name| name.to_s.gsub(/[^A-Za-z]/,
|
84
|
-
cells_from_row(index).reject { |header| stripped_headers.include?(header.to_s.gsub(/[^A-Za-z]/,
|
82
|
+
stripped_headers = allowed_header_names.map { |name| name.to_s.gsub(/[^A-Za-z]/, "").downcase }
|
83
|
+
cells_from_row(index).reject { |header| stripped_headers.include?(header.to_s.gsub(/[^A-Za-z]/, "").downcase) }
|
85
84
|
end
|
86
85
|
|
87
86
|
def allowed_header_names
|
@@ -90,23 +89,23 @@ module Original
|
|
90
89
|
|
91
90
|
def spreadsheet
|
92
91
|
@spreadsheet ||= case File.extname(original.path)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
92
|
+
when ".csv"
|
93
|
+
Roo::CSV.new(original.path, csv_options: csv_options)
|
94
|
+
when ".xls"
|
95
|
+
Roo::Excel.new(original.path)
|
96
|
+
when ".xlsx"
|
97
|
+
Roo::Excelx.new(original.path)
|
98
|
+
else
|
99
|
+
raise "Unknown file type: #{original.path.split("/").last}"
|
100
|
+
end
|
102
101
|
end
|
103
102
|
|
104
103
|
def duplicate(row_hash, id)
|
105
|
-
Importo::
|
104
|
+
Importo::Result.where("details @> '[{\"hash\": \"#{row_hash}\", \"state\": \"success\"}]' AND import_id <> :import_id", import_id: id).first
|
106
105
|
end
|
107
106
|
|
108
107
|
def duplicate?(row_hash, id)
|
109
|
-
return false if allow_duplicates? || row_hash[
|
108
|
+
return false if allow_duplicates? || row_hash["id"] == id
|
110
109
|
|
111
110
|
duplicate(row_hash, id)
|
112
111
|
end
|
@@ -114,9 +113,10 @@ module Original
|
|
114
113
|
def loop_data_rows
|
115
114
|
(data_start_row..spreadsheet.last_row).map do |index|
|
116
115
|
row = cells_from_row(index, false)
|
117
|
-
|
116
|
+
|
117
|
+
attributes = Hash[[attribute_names, row].transpose].reject { |k, _| k.nil? }
|
118
118
|
attributes = attributes.map do |column, value|
|
119
|
-
value = strip_tags(value.strip) if value.respond_to?(:strip) && columns[column]&.options[:strip_tags
|
119
|
+
value = strip_tags(value.strip) if value.respond_to?(:strip) && columns[column]&.options&.[](:strip_tags) != false
|
120
120
|
[column, value]
|
121
121
|
end.to_h
|
122
122
|
attributes.reject! { |k, _v| headers_added_by_import.include?(k) }
|
@@ -130,11 +130,11 @@ module Original
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def nr_to_col(number)
|
133
|
-
(
|
133
|
+
("A".."ZZ").to_a[number]
|
134
134
|
end
|
135
135
|
|
136
136
|
def attribute_names
|
137
|
-
return columns.keys if !includes_header?
|
137
|
+
return columns.keys if !includes_header?
|
138
138
|
|
139
139
|
translated_header_names = cells_from_row(header_row)
|
140
140
|
@header_names = translated_header_names.map do |name|
|
@@ -1,62 +1,80 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_support/concern"
|
4
4
|
|
5
5
|
module ResultFeedback
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
+
#
|
9
|
+
# Generates a result excel file as a stream
|
10
|
+
#
|
11
|
+
def results_file
|
12
|
+
xls = Axlsx::Package.new
|
13
|
+
xls.use_shared_strings = true
|
14
|
+
workbook = xls.workbook
|
15
|
+
workbook.styles do |style|
|
16
|
+
alert_cell_bg_color = "dd7777"
|
17
|
+
duplicate_cell_bg_color = "ddd777"
|
8
18
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
rich_text_headers = headers.map { |header| Axlsx::RichText.new.tap { |rt| rt.add_run(header.dup, b: true) } }
|
24
|
-
sheet.add_row rich_text_headers
|
25
|
-
loop_data_rows do |attributes, index|
|
26
|
-
row_state = result(index, 'state')
|
27
|
-
|
28
|
-
style = case row_state
|
29
|
-
when 'duplicate'
|
30
|
-
duplicate_cell
|
31
|
-
when 'failure'
|
32
|
-
alert_cell
|
33
|
-
end
|
34
|
-
sheet.add_row attributes.values + results(index), style: Array.new(attributes.values.count) + Array.new(headers_added_by_import.count) { style }
|
19
|
+
sheet = workbook.add_worksheet(name: I18n.t("importo.sheet.results.name"))
|
20
|
+
headers = (header_names - headers_added_by_import) + headers_added_by_import
|
21
|
+
headers_style = headers.map do |header|
|
22
|
+
workbook.styles.add_style(bg_color: "619657")
|
23
|
+
end
|
24
|
+
rich_text_headers = headers.map { |header| Axlsx::RichText.new.tap { |rt| rt.add_run(header.dup, b: true) } }
|
25
|
+
sheet.add_row rich_text_headers, style: headers_style
|
26
|
+
loop_data_rows do |attributes, index|
|
27
|
+
row_state = result(index, :state)
|
28
|
+
bg_color = case row_state.to_s
|
29
|
+
when "duplicate"
|
30
|
+
duplicate_cell_bg_color
|
31
|
+
when "failure"
|
32
|
+
alert_cell_bg_color
|
35
33
|
end
|
36
|
-
|
37
|
-
|
34
|
+
styles = []
|
35
|
+
attributes.map do |column, value|
|
36
|
+
export_format = columns[column]&.options&.dig(:export, :format)
|
37
|
+
format_code = if export_format == "number" || (export_format.nil? && value.is_a?(Numeric))
|
38
|
+
"#"
|
39
|
+
elsif export_format == "text" || (export_format.nil? && value.is_a?(String))
|
40
|
+
"@"
|
41
|
+
elsif export_format
|
42
|
+
export_format.to_s
|
43
|
+
else
|
44
|
+
"General"
|
45
|
+
end
|
46
|
+
config_style = {}
|
47
|
+
config_style.merge!(columns[column]&.options&.[](:style)) unless columns[column]&.options&.[](:style).nil?
|
48
|
+
config_style.merge!({format_code: format_code, bg_color: bg_color})
|
49
|
+
styles << workbook.styles.add_style(config_style)
|
50
|
+
end
|
51
|
+
header_array = []
|
52
|
+
headers_added_by_import.count.times do |i|
|
53
|
+
header_array << workbook.styles.add_style(bg_color: bg_color)
|
54
|
+
end
|
55
|
+
styles += header_array
|
56
|
+
sheet.add_row attributes.values + results(index), style: styles
|
38
57
|
end
|
39
58
|
|
40
|
-
|
59
|
+
sheet.auto_filter = "A1:#{sheet.dimension.last_cell_reference}"
|
41
60
|
end
|
42
61
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
62
|
+
xls.to_stream
|
63
|
+
end
|
64
|
+
|
65
|
+
def file_name(suffix = nil)
|
66
|
+
base = friendly_name || model.class.name
|
67
|
+
base = base.to_s unless base.is_a?(String)
|
68
|
+
base = base.gsub(/[_\s-]/, "_").pluralize.downcase
|
69
|
+
"#{base}#{suffix.present? ? "_#{suffix}" : ""}.xlsx"
|
70
|
+
end
|
49
71
|
|
50
72
|
private
|
51
73
|
|
52
74
|
def register_result(index, details)
|
53
|
-
@import.results
|
54
|
-
|
55
|
-
|
56
|
-
@import.results[i].merge!(details)
|
57
|
-
else
|
58
|
-
@import.results << details.merge(row: index)
|
59
|
-
end
|
75
|
+
r = @import.results.find_or_create_by(row_index: index)
|
76
|
+
r.details = details
|
77
|
+
r.save
|
60
78
|
end
|
61
79
|
|
62
80
|
def results(index)
|
@@ -64,6 +82,6 @@ module ResultFeedback
|
|
64
82
|
end
|
65
83
|
|
66
84
|
def result(index, field)
|
67
|
-
(@import.results.
|
85
|
+
(@import.results.find_by(row_index: index)&.details || {}).fetch(field.to_s, nil)
|
68
86
|
end
|
69
87
|
end
|
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
require
|
3
|
+
require "active_support/concern"
|
5
4
|
|
6
5
|
module Revertable
|
7
6
|
extend ActiveSupport::Concern
|
@@ -10,27 +9,27 @@ module Revertable
|
|
10
9
|
undo_all
|
11
10
|
|
12
11
|
import.reverted!
|
13
|
-
rescue
|
12
|
+
rescue => e
|
14
13
|
import.result_message = "Exception: #{e.message}"
|
15
|
-
Rails.logger.error "Importo exception: #{e.message} backtrace #{e.backtrace.join(
|
14
|
+
Rails.logger.error "Importo exception: #{e.message} backtrace #{e.backtrace.join(";")}"
|
16
15
|
import.failure!
|
17
16
|
end
|
18
17
|
|
19
18
|
private
|
20
19
|
|
21
20
|
def undo_all
|
22
|
-
revertable_results = import.results.select { |result| result[
|
21
|
+
revertable_results = import.results.select { |result| result["state"] == "success" }
|
23
22
|
|
24
23
|
revertable_results.each do |revertable_result|
|
25
|
-
next unless revertable_result[
|
24
|
+
next unless revertable_result["state"] == "success"
|
26
25
|
|
27
26
|
begin
|
28
|
-
undo(revertable_result[
|
29
|
-
revertable_result[
|
30
|
-
revertable_result.delete(
|
31
|
-
revertable_result.delete(
|
32
|
-
rescue
|
33
|
-
result[
|
27
|
+
undo(revertable_result["class"], revertable_result["id"], cells_from_row(revertable_result["row"]))
|
28
|
+
revertable_result["state"] = "reverted"
|
29
|
+
revertable_result.delete("message")
|
30
|
+
revertable_result.delete("errors")
|
31
|
+
rescue => e
|
32
|
+
result["message"] = "Not reverted: #{e.message}"
|
34
33
|
end
|
35
34
|
end
|
36
35
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Importo
|
4
4
|
class BaseImporter
|
5
|
+
include ActiveSupport::Callbacks
|
5
6
|
include ActionView::Helpers::SanitizeHelper
|
6
7
|
include Importable
|
7
8
|
include Exportable
|
@@ -9,10 +10,17 @@ module Importo
|
|
9
10
|
include Original
|
10
11
|
include ResultFeedback
|
11
12
|
include ImporterDsl
|
13
|
+
|
12
14
|
# include ActiveStorage::Downloading
|
13
15
|
|
16
|
+
define_callbacks :row_import
|
17
|
+
|
18
|
+
Importo::Import.state_machine.states.map(&:name).each do |state|
|
19
|
+
define_callbacks state
|
20
|
+
end
|
21
|
+
|
14
22
|
delegate :friendly_name, :introduction, :model, :columns, :csv_options, :allow_duplicates?, :includes_header?,
|
15
|
-
|
23
|
+
:ignore_header?, :t, to: :class
|
16
24
|
attr_reader :import, :blob
|
17
25
|
|
18
26
|
def initialize(imprt = nil)
|
@@ -20,11 +28,15 @@ module Importo
|
|
20
28
|
I18n.locale = import.locale if import&.locale # Should we do this?? here??
|
21
29
|
end
|
22
30
|
|
31
|
+
def state_changed(_import, transition)
|
32
|
+
run_callbacks(transition.to_name) do
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
23
36
|
class << self
|
24
37
|
def t(key, options = {})
|
25
|
-
if I18n.exists? "importers.#{name.underscore}#{key}"
|
26
|
-
I18n.t(key,
|
27
|
-
options.merge(scope: "importers.#{name.underscore}".to_sym))
|
38
|
+
if I18n.exists? :"importers.#{name.underscore}#{key}"
|
39
|
+
I18n.t(key, options.merge(scope: :"importers.#{name.underscore}"))
|
28
40
|
end
|
29
41
|
end
|
30
42
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Importo
|
2
|
+
class ImportJobCallback
|
3
|
+
include Rails.application.routes.url_helpers
|
4
|
+
|
5
|
+
def on_complete(_status, options)
|
6
|
+
options = options.deep_stringify_keys
|
7
|
+
import = Import.find(options["import_id"])
|
8
|
+
if import.present?
|
9
|
+
import.result.attach(io: import.importer.results_file, filename: import.importer.file_name("results"),
|
10
|
+
content_type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
11
|
+
|
12
|
+
ActiveRecord::Base.uncached do
|
13
|
+
import.result_message = I18n.t("importo.importers.result_message",
|
14
|
+
nr: import.results.where("details @> ?", {state: "success"}.to_json).count, of: import.importer.send(:row_count))
|
15
|
+
end
|
16
|
+
|
17
|
+
if import.can_complete?
|
18
|
+
import.complete!
|
19
|
+
else
|
20
|
+
import.save!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_success(status, options)
|
26
|
+
on_complete(status, options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Importo
|
2
|
+
class ImportJob
|
3
|
+
include Sidekiq::Job
|
4
|
+
sidekiq_options retry: 5
|
5
|
+
# queue_as :integration
|
6
|
+
queue_as Importo.config.queue_name
|
7
|
+
|
8
|
+
sidekiq_retries_exhausted do |msg, _e|
|
9
|
+
attributes = msg["args"][0]
|
10
|
+
index = msg["args"][1]
|
11
|
+
import_id = msg["args"][2]
|
12
|
+
|
13
|
+
execute_row(attributes, index, import_id, true, msg["bid"])
|
14
|
+
end
|
15
|
+
|
16
|
+
sidekiq_retry_in do |_count, exception, _jobhash|
|
17
|
+
case exception
|
18
|
+
when Importo::RetryError
|
19
|
+
exception.delay
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform(attributes, index, import_id)
|
24
|
+
self.class.execute_row(attributes, index, import_id, false, bid)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.execute_row(attributes, index, import_id, last_attempt, bid)
|
28
|
+
attributes = JSON.load(attributes).deep_symbolize_keys if attributes.is_a?(String)
|
29
|
+
|
30
|
+
import = Import.find(import_id)
|
31
|
+
record = import.importer.process_data_row(attributes, index, last_attempt: last_attempt)
|
32
|
+
|
33
|
+
batch = Sidekiq::Batch.new(bid)
|
34
|
+
|
35
|
+
if !import.completed? && import.can_complete? && batch.status.complete?
|
36
|
+
ImportJobCallback.new.on_complete(batch.status, {import_id: import_id})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -7,6 +7,7 @@ module Importo
|
|
7
7
|
belongs_to :importo_ownable, polymorphic: true
|
8
8
|
|
9
9
|
has_many :message_instances, as: :messagable
|
10
|
+
has_many :results, class_name: "Importo::Result", dependent: :delete_all
|
10
11
|
|
11
12
|
validates :kind, presence: true
|
12
13
|
validates :original, presence: true
|
@@ -20,8 +21,6 @@ module Importo
|
|
20
21
|
end
|
21
22
|
|
22
23
|
state_machine :state, initial: :new do
|
23
|
-
audit_trail class: ResourceStateTransition, as: :resource if "ResourceStateTransition".safe_constantize
|
24
|
-
|
25
24
|
state :importing
|
26
25
|
state :scheduled
|
27
26
|
state :completed
|
@@ -29,11 +28,11 @@ module Importo
|
|
29
28
|
state :reverted
|
30
29
|
|
31
30
|
after_transition any => any do |imprt, transition|
|
32
|
-
|
31
|
+
imprt.importer.state_changed(imprt, transition)
|
33
32
|
end
|
34
33
|
|
35
|
-
after_transition any => :scheduled, do
|
36
|
-
after_transition any => :reverting, do
|
34
|
+
after_transition any => :scheduled, :do => :schedule_import
|
35
|
+
after_transition any => :reverting, :do => :schedule_revert
|
37
36
|
|
38
37
|
event :schedule do
|
39
38
|
transition new: :scheduled
|
@@ -46,7 +45,7 @@ module Importo
|
|
46
45
|
end
|
47
46
|
|
48
47
|
event :complete do
|
49
|
-
transition importing: :completed
|
48
|
+
transition importing: :completed, if: ->(import) { import.no_processing? && import.results.count == import.importer.send(:row_count) }
|
50
49
|
end
|
51
50
|
|
52
51
|
event :failure do
|
@@ -71,15 +70,43 @@ module Importo
|
|
71
70
|
end
|
72
71
|
|
73
72
|
def content_validator
|
74
|
-
|
73
|
+
unless importer.structure_valid?
|
74
|
+
errors.add(:original,
|
75
|
+
I18n.t("importo.errors.structure_invalid",
|
76
|
+
invalid_headers: importer.invalid_header_names.join(", ")))
|
77
|
+
end
|
75
78
|
rescue => e
|
76
|
-
errors.add(:original, I18n.t(
|
79
|
+
errors.add(:original, I18n.t("importo.errors.parse_error", error: e.message))
|
77
80
|
end
|
78
81
|
|
79
82
|
def importer
|
80
83
|
@importer ||= "#{kind.camelize}Importer".constantize.new(self)
|
81
84
|
end
|
82
85
|
|
86
|
+
def failure?
|
87
|
+
results.where("details @> ?", '{"state":"failure"}').any?
|
88
|
+
end
|
89
|
+
|
90
|
+
def no_failure?
|
91
|
+
results.where("details @> ?", '{"state":"failure"}').none?
|
92
|
+
end
|
93
|
+
|
94
|
+
def success?
|
95
|
+
results.where("details @> ?", '{"state":"success"}').any?
|
96
|
+
end
|
97
|
+
|
98
|
+
def no_succes?
|
99
|
+
results.where("details @> ?", '{"state":"success"}').none?
|
100
|
+
end
|
101
|
+
|
102
|
+
def processing?
|
103
|
+
results.where("details @> ?", '{"state":"processing"}').any?
|
104
|
+
end
|
105
|
+
|
106
|
+
def no_processing?
|
107
|
+
results.where("details @> ?", '{"state":"processing"}').none?
|
108
|
+
end
|
109
|
+
|
83
110
|
private
|
84
111
|
|
85
112
|
def schedule_import
|
@@ -3,37 +3,33 @@
|
|
3
3
|
class Importo::ImportsTable < ActionTable::ActionTable
|
4
4
|
model Importo::Import
|
5
5
|
|
6
|
-
column(:created_at
|
7
|
-
column(:user,
|
6
|
+
column(:created_at, html_value: proc { |import| l(import.created_at.in_time_zone(Time.zone), format: :short).to_s })
|
7
|
+
column(:user, sortable: false) { |import| import.importo_ownable.name }
|
8
8
|
column(:kind, sortable: false)
|
9
|
-
column(:original, sortable: false) { |import| link_to(import.original.filename, main_app.rails_blob_path(import.original, disposition: "attachment"), target:
|
9
|
+
column(:original, sortable: false) { |import| link_to(import.original.filename, main_app.rails_blob_path(import.original, disposition: "attachment"), target: "_blank") }
|
10
10
|
column(:state)
|
11
|
-
column(:result, sortable: false) { |import| import.result.attached? ? link_to(import.result_message, main_app.rails_blob_path(import.result, disposition: "attachment"), target:
|
12
|
-
column(:extra_links, sortable: false) { |import| Importo.config.admin_extra_links(import).map { |name, link| link_to(link[:text], link[:url], title: link[:title], target:
|
11
|
+
column(:result, sortable: false) { |import| import.result.attached? ? link_to(import.result_message, main_app.rails_blob_path(import.result, disposition: "attachment"), target: "_blank") : import.result_message }
|
12
|
+
column(:extra_links, sortable: false) { |import| Importo.config.admin_extra_links.call(import).map { |name, link| link_to(link[:text], link[:url], title: link[:title], target: "_blank", class: link[:icon]) } }
|
13
13
|
|
14
|
-
column :actions, title:
|
14
|
+
column :actions, title: "", sortable: false do |import|
|
15
15
|
content_tag(:span) do
|
16
16
|
if import.can_revert?
|
17
|
-
concat link_to(content_tag(:i, nil, class:
|
17
|
+
concat link_to(content_tag(:i, nil, class: "fa fa-undo"), importo.undo_import_path(import), data: {turbo_method: :post, turbo_confirm: "Are you sure? This will undo this import."})
|
18
18
|
end
|
19
|
-
if Importo.config.admin_can_destroy(import)
|
20
|
-
concat link_to(content_tag(:i, nil, class:
|
19
|
+
if Importo.config.admin_can_destroy.call(import)
|
20
|
+
concat link_to(content_tag(:i, nil, class: "fa fa-trash"), importo.import_path(import), class: "float-right", data: {turbo_method: :delete, turbo_confirm: "Are you sure? This will prevent duplicate imports from being detected."})
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
# filter(:importo_ownable){ parameter: :ownable, collection_proc: -> { Importo::Import.order(created_at: :desc).limit(200).map(&:importo_ownable).uniq.sort_by(&:name).map { |o| [o.name, "#{o.class.name}##{o.id}"] } } } )
|
26
|
+
|
25
27
|
initial_order :created_at, :desc
|
26
28
|
|
27
29
|
private
|
28
30
|
|
29
31
|
def scope
|
30
|
-
@scope = Importo.config.admin_visible_imports
|
31
|
-
|
32
|
-
|
33
|
-
def filtered_scope
|
34
|
-
@filtered_scope = scope
|
35
|
-
@filtered_scope = @filtered_scope.where(importo_ownable_type: params[:ownable].split('#').first, importo_ownable_id: params[:ownable].split('#').last) if params[:ownable]
|
36
|
-
|
37
|
-
@filtered_scope
|
32
|
+
@scope = Importo.config.admin_visible_imports.call
|
33
|
+
@scope
|
38
34
|
end
|
39
35
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined? Mensa
|
4
|
+
class Importo::MensaImportsTable < Mensa::Base
|
5
|
+
definition do
|
6
|
+
model Importo::Import
|
7
|
+
|
8
|
+
column(:created_at)
|
9
|
+
column(:user)
|
10
|
+
column(:kind)
|
11
|
+
column(:original)
|
12
|
+
column(:state)
|
13
|
+
column(:result)
|
14
|
+
|
15
|
+
order created_at: :desc
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def scope
|
21
|
+
@scope = Importo.config.admin_visible_imports.call
|
22
|
+
@scope = @scope.joins("LEFT JOIN users on importo_imports.importo_ownable_type = 'User' and importo_imports.importo_ownable_id = users.id ")
|
23
|
+
end
|
24
|
+
|
25
|
+
def filtered_scope
|
26
|
+
@filtered_scope = scope
|
27
|
+
@filtered_scope = @filtered_scope.where(importo_ownable_type: params[:ownable].split("#").first, importo_ownable_id: params[:ownable].split("#").last) if params[:ownable]
|
28
|
+
@filtered_scope = @filtered_scope.where(kind: params[:kind]) if params[:kind]
|
29
|
+
@filtered_scope
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
class Importo::MensaImportsTable
|
34
|
+
end
|
35
|
+
end
|
@@ -1,2 +1,6 @@
|
|
1
|
-
= sts.card
|
2
|
-
|
1
|
+
= sts.card :importo_imports, icon: 'fad fa-file-import', content_padding: false do |card|
|
2
|
+
- if defined? Mensa
|
3
|
+
= sts.mensa(:importo_mensa_imports, parameters: { kind: @import&.kind })
|
4
|
+
- else
|
5
|
+
= card.with_table(:importo_imports, parameters: { kind: @import&.kind }, custom_views: false)
|
6
|
+
|