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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -5
  3. data/Rakefile +16 -17
  4. data/app/controllers/concerns/maintenance_standards.rb +1 -1
  5. data/app/controllers/importo/imports_controller.rb +15 -12
  6. data/app/helpers/importo/application_helper.rb +11 -11
  7. data/app/importers/concerns/exportable.rb +31 -24
  8. data/app/importers/concerns/importable.rb +144 -62
  9. data/app/importers/concerns/importer_dsl.rb +6 -14
  10. data/app/importers/concerns/original.rb +23 -23
  11. data/app/importers/concerns/result_feedback.rb +62 -44
  12. data/app/importers/concerns/revertable.rb +11 -12
  13. data/app/importers/importo/base_importer.rb +16 -4
  14. data/app/importers/importo/import_job_callback.rb +29 -0
  15. data/app/jobs/importo/import_job.rb +40 -0
  16. data/app/mailers/importo/application_mailer.rb +2 -2
  17. data/app/models/importo/import.rb +35 -8
  18. data/app/models/importo/result.rb +5 -0
  19. data/app/services/importo/import_service.rb +1 -3
  20. data/app/services/importo/revert_service.rb +1 -1
  21. data/app/tables/importo/imports_table.rb +13 -17
  22. data/app/tables/importo/mensa_imports_table.rb +35 -0
  23. data/app/views/importo/imports/index.html.slim +6 -2
  24. data/app/views/importo/imports/new.html.slim +30 -20
  25. data/config/locales/en.yml +21 -9
  26. data/config/locales/nl.yml +10 -4
  27. data/config/routes.rb +4 -4
  28. data/db/migrate/20180409151031_create_importo_import.rb +9 -9
  29. data/db/migrate/20180628175535_add_locale_importo_import.rb +1 -1
  30. data/db/migrate/20230510051447_remove_result_from_imports.rb +5 -0
  31. data/db/migrate/20230510083043_create_importo_results.rb +11 -0
  32. data/lib/generators/importo/importer_generator.rb +1 -1
  33. data/lib/generators/importo/install_generator.rb +7 -8
  34. data/lib/generators/importo/templates/importo.rb +11 -0
  35. data/lib/generators/satis/install_generator.rb +22 -0
  36. data/lib/generators/satis/tailwind_config_generator.rb +24 -0
  37. data/lib/generators/satis/templates/config/initializers/satis.rb +13 -0
  38. data/lib/importo/acts_as_import_owner.rb +1 -1
  39. data/lib/importo/configuration.rb +52 -49
  40. data/lib/importo/engine.rb +11 -9
  41. data/lib/importo/import_column.rb +18 -6
  42. data/lib/importo/test_helpers.rb +19 -0
  43. data/lib/importo/version.rb +1 -1
  44. data/lib/importo.rb +12 -17
  45. metadata +75 -24
  46. data/app/services/importo/callback_service.rb +0 -14
  47. data/lib/generators/templates/importo.rb +0 -21
  48. /data/lib/generators/{templates → importo/templates}/README +0 -0
  49. /data/lib/generators/{templates → importo/templates}/application_importer.rb +0 -0
  50. /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 'active_support/concern'
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['original']
13
- @original ||= import.attachment_changes['original']&.attachable
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(['ActiveStorage', import.original.filename.extension_with_delimiter])
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(['ActiveStorage', import.original.filename.extension_with_delimiter])
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]/, '').downcase }
84
- cells_from_row(index).reject { |header| stripped_headers.include?(header.to_s.gsub(/[^A-Za-z]/, '').downcase) }
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
- when '.csv' then
94
- Roo::CSV.new(original.path, csv_options: csv_options)
95
- when '.xls' then
96
- Roo::Excel.new(original.path)
97
- when '.xlsx' then
98
- Roo::Excelx.new(original.path)
99
- else
100
- raise "Unknown file type: #{original.path.split('/').last}"
101
- end
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::Import.where("results @> '[{\"hash\": \"#{row_hash}\", \"state\": \"success\"}]' AND id <> :id", id: id).first
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['id'] == id
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
- attributes = Hash[[attribute_names, row].transpose]
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] != false
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
- ('A'..'ZZ').to_a[number]
133
+ ("A".."ZZ").to_a[number]
134
134
  end
135
135
 
136
136
  def attribute_names
137
- return columns.keys if !includes_header? || ignore_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 'active_support/concern'
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
- # Generates a result excel file as a stream
11
- #
12
- def results_file
13
- xls = Axlsx::Package.new
14
- xls.use_shared_strings = true
15
- workbook = xls.workbook
16
- workbook.styles do |style|
17
- alert_cell = style.add_style(bg_color: 'dd7777')
18
- duplicate_cell = style.add_style(bg_color: 'ddd777')
19
-
20
- sheet = workbook.add_worksheet(name: I18n.t('importo.sheet.results.name'))
21
-
22
- headers = (header_names - headers_added_by_import) + headers_added_by_import
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
- sheet.auto_filter = "A1:#{sheet.dimension.last_cell_reference}"
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
- xls.to_stream
59
+ sheet.auto_filter = "A1:#{sheet.dimension.last_cell_reference}"
41
60
  end
42
61
 
43
- def file_name(suffix = nil)
44
- base = friendly_name || model.class.name
45
- base = base.to_s unless base.is_a?(String)
46
- base = base.gsub(/[_\s-]/, '_').pluralize.downcase
47
- "#{base}#{suffix.present? ? "_#{suffix}" : '' }.xlsx"
48
- end
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
- i = @import.results.index { |data| data[:row] == index }
55
- if i
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.find { |result| result[:row] == index } || {}).fetch(field, nil)
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 'active_support/concern'
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 StandardError => e
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['state'] == 'success' }
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['state'] == 'success'
24
+ next unless revertable_result["state"] == "success"
26
25
 
27
26
  begin
28
- undo(revertable_result['class'], revertable_result['id'], cells_from_row(revertable_result['row']))
29
- revertable_result['state'] = 'reverted'
30
- revertable_result.delete('message')
31
- revertable_result.delete('errors')
32
- rescue StandardError => e
33
- result['message'] = "Not reverted: #{e.message}"
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
- :ignore_header?, :t, to: :class
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}".to_sym
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Importo
4
4
  class ApplicationMailer < ActionMailer::Base
5
- default from: 'from@example.com'
6
- layout 'mailer'
5
+ default from: "from@example.com"
6
+ layout "mailer"
7
7
  end
8
8
  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
- CallbackService.perform_later(import: imprt, callback: transition.to_name)
31
+ imprt.importer.state_changed(imprt, transition)
33
32
  end
34
33
 
35
- after_transition any => :scheduled, do: :schedule_import
36
- after_transition any => :reverting, do: :schedule_revert
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
- errors.add(:original, I18n.t('importo.errors.structure_invalid', invalid_headers: importer.invalid_header_names.join(', '))) unless importer.structure_valid?
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('importo.errors.parse_error', error: e.message))
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
@@ -0,0 +1,5 @@
1
+ module Importo
2
+ class Result < ApplicationRecord
3
+ belongs_to :import, class_name: "Importo::Import"
4
+ end
5
+ end
@@ -3,11 +3,9 @@
3
3
  module Importo
4
4
  class ImportService < ApplicationService
5
5
  def perform
6
- sleep 1
7
-
8
6
  context.import.import!
9
7
  context.import.importer.import!
10
- rescue StandardError
8
+ rescue
11
9
  context.import.failure!
12
10
  context.fail!
13
11
  end
@@ -6,7 +6,7 @@ module Importo
6
6
  sleep 1
7
7
 
8
8
  context.import.importer.revert!
9
- rescue StandardError
9
+ rescue
10
10
  context.import.failure!
11
11
  context.fail!
12
12
  end
@@ -3,37 +3,33 @@
3
3
  class Importo::ImportsTable < ActionTable::ActionTable
4
4
  model Importo::Import
5
5
 
6
- column(:created_at) { |import| l(import.created_at.in_time_zone(Time.zone), format: :short).to_s }
7
- column(:user, filter: { 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}"] } } } ) { |import| import.importo_ownable.name }
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: '_blank') }
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: '_blank') : import.result_message }
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: '_blank', class: link[:icon]) }}
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: '', sortable: false do |import|
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: 'fa fa-undo'), importo.undo_import_path(import), data: { turbo_method: :post, turbo_confirm: 'Are you sure? This will undo this import.' })
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: '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.' })
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
- end
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 title: t('.title'), icon: 'fad fa-file-import', content_padding: false do |card|
2
- = sts.table :importo_imports
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
+