importo 2.0.4 → 3.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) 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/application_job.rb +4 -0
  16. data/app/jobs/importo/import_job.rb +40 -0
  17. data/app/jobs/importo/purge_import_job.rb +14 -0
  18. data/app/mailers/importo/application_mailer.rb +2 -2
  19. data/app/models/importo/import.rb +35 -8
  20. data/app/models/importo/result.rb +5 -0
  21. data/app/services/importo/import_service.rb +1 -3
  22. data/app/services/importo/revert_service.rb +1 -1
  23. data/app/tables/importo/imports_table.rb +13 -17
  24. data/app/tables/importo/mensa_imports_table.rb +35 -0
  25. data/app/views/importo/imports/index.html.slim +6 -2
  26. data/app/views/importo/imports/new.html.slim +30 -20
  27. data/config/locales/en.yml +21 -9
  28. data/config/locales/nl.yml +10 -4
  29. data/config/routes.rb +4 -4
  30. data/db/migrate/20180409151031_create_importo_import.rb +9 -9
  31. data/db/migrate/20180628175535_add_locale_importo_import.rb +1 -1
  32. data/db/migrate/20230510051447_remove_result_from_imports.rb +5 -0
  33. data/db/migrate/20230510083043_create_importo_results.rb +11 -0
  34. data/lib/generators/importo/importer_generator.rb +1 -1
  35. data/lib/generators/importo/install_generator.rb +7 -8
  36. data/lib/generators/importo/templates/importo.rb +11 -0
  37. data/lib/generators/satis/install_generator.rb +22 -0
  38. data/lib/generators/satis/tailwind_config_generator.rb +24 -0
  39. data/lib/generators/satis/templates/config/initializers/satis.rb +13 -0
  40. data/lib/importo/acts_as_import_owner.rb +1 -1
  41. data/lib/importo/configuration.rb +52 -49
  42. data/lib/importo/engine.rb +11 -9
  43. data/lib/importo/import_column.rb +18 -6
  44. data/lib/importo/test_helpers.rb +19 -0
  45. data/lib/importo/version.rb +1 -1
  46. data/lib/importo.rb +12 -17
  47. metadata +77 -24
  48. data/app/services/importo/callback_service.rb +0 -14
  49. data/lib/generators/templates/importo.rb +0 -21
  50. /data/lib/generators/{templates → importo/templates}/README +0 -0
  51. /data/lib/generators/{templates → importo/templates}/application_importer.rb +0 -0
  52. /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,4 @@
1
+ module Importo
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ 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
@@ -0,0 +1,14 @@
1
+ module Importo
2
+ class PurgeImportJob < ApplicationJob
3
+ def perform(owner, months)
4
+ imports = Import.where(importo_ownable: owner, created_at: ..months.months.ago.beginning_of_day)
5
+
6
+ imports.each do |import|
7
+ import.original.purge
8
+ import.result.purge
9
+ end
10
+
11
+ imports.in_batches.destroy_all
12
+ end
13
+ end
14
+ 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
+