importo 3.0.22 → 3.0.23
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 +6 -2
- data/app/controllers/importo/imports_controller.rb +47 -4
- data/app/importers/concerns/importable.rb +14 -6
- data/app/importers/concerns/original.rb +5 -2
- data/app/importers/importo/import_job_callback.rb +15 -4
- data/app/jobs/importo/import_job.rb +11 -9
- data/app/jobs/importo/purge_import_job.rb +3 -1
- data/app/models/importo/import.rb +10 -6
- data/app/services/importo/import_context.rb +3 -11
- data/app/services/importo/import_service.rb +4 -3
- data/app/views/importo/imports/new.html.slim +3 -1
- data/app/views/importo/imports/preview.html.slim +36 -0
- data/config/locales/en.yml +12 -1
- data/config/routes.rb +7 -4
- data/db/migrate/20251117132125_create_good_jobs.rb +40 -0
- data/db/migrate/20251117132126_create_good_job_settings.rb +20 -0
- data/db/migrate/20251117132127_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb +19 -0
- data/db/migrate/20251117132128_create_good_job_batches.rb +35 -0
- data/db/migrate/20251117132129_create_good_job_executions.rb +33 -0
- data/db/migrate/20251117132130_create_good_jobs_error_event.rb +16 -0
- data/db/migrate/20251117132131_recreate_good_job_cron_indexes_with_conditional.rb +45 -0
- data/db/migrate/20251117132132_create_good_job_labels.rb +15 -0
- data/db/migrate/20251117132133_create_good_job_labels_index.rb +22 -0
- data/db/migrate/20251117132134_remove_good_job_active_id_index.rb +21 -0
- data/db/migrate/20251117132135_create_index_good_job_jobs_for_candidate_lookup.rb +19 -0
- data/db/migrate/20251117132136_create_good_job_execution_error_backtrace.rb +15 -0
- data/db/migrate/20251117132137_create_good_job_process_lock_ids.rb +18 -0
- data/db/migrate/20251117132138_create_good_job_process_lock_indexes.rb +38 -0
- data/db/migrate/20251117132139_create_good_job_execution_duration.rb +15 -0
- data/lib/importo/adapters/sidekiq_batch_adapter.rb +2 -2
- data/lib/importo/configuration.rb +3 -0
- data/lib/importo/test_helpers.rb +5 -2
- data/lib/importo/version.rb +1 -1
- metadata +18 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd095c43d38bb324dea0b158ebe7743bd84630c526b755fc7b031e761fff8e91
|
|
4
|
+
data.tar.gz: 772c031fe4446ce6db81e22c90f57ae9fb1dbfe1cbadd8ee62e0500fd3fe7a4e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffc02a4f1a09ad49a624206c615cfcd44a81dd489c28d7c7cdbf6780b451c786e5fde236503585cbf864faaa3e870781de5286b64e10dad21d53d50d1b8e75e5
|
|
7
|
+
data.tar.gz: 75c6b5d26d837b23e5c7ac49514007121346dfb6ef1a7978e80a5105ebe41c666daf986a44c20fe3484bfefddc9d126d0fdad07e379036ccad214a4ca6aec243
|
data/README.md
CHANGED
|
@@ -92,8 +92,12 @@ Add this line to your application's Gemfile:
|
|
|
92
92
|
gem 'importo'
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
Importo
|
|
96
|
-
|
|
95
|
+
Importo needs Batch functionality, this can either be:
|
|
96
|
+
- Sidekiq Pro's batch functionality,
|
|
97
|
+
- [sidekiq-batch](https://github.com/entdec/sidekiq-batch)
|
|
98
|
+
- GoodJob's Batch
|
|
99
|
+
|
|
100
|
+
Additionally it either needs [Servitium](https://github.com/entdec/servitium) or [Facio](https://github.com/entdec/facio)
|
|
97
101
|
|
|
98
102
|
And then execute:
|
|
99
103
|
|
|
@@ -8,6 +8,24 @@ module Importo
|
|
|
8
8
|
@import = Import.new(kind: params[:kind], locale: I18n.locale)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def preview
|
|
12
|
+
@import = Import.find(params[:id])
|
|
13
|
+
if @import&.original&.attachment.present?
|
|
14
|
+
@original = Tempfile.new(['ActiveStorage', @import.original.filename.extension_with_delimiter])
|
|
15
|
+
@original.binmode
|
|
16
|
+
@import.original.download { |block| @original.write(block) }
|
|
17
|
+
@original.flush
|
|
18
|
+
@original.rewind
|
|
19
|
+
sheet = Roo::Excelx.new(@original.path)
|
|
20
|
+
@sheet_data = sheet.parse(headers: true)
|
|
21
|
+
@check_header = @sheet_data.reject{ |h| h.keys == h.values }.map{|h| h.transform_values(&:to_s).compact_blank}.reduce({}, :merge).keys
|
|
22
|
+
end
|
|
23
|
+
if @sheet_data.nil? || @sheet_data.reject{ |h| h.keys == h.values }.blank?
|
|
24
|
+
Signum.error(Current.user, text: t('.no_file'))
|
|
25
|
+
redirect_to action: :new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
11
29
|
def create
|
|
12
30
|
unless import_params
|
|
13
31
|
@import = Import.new(kind: params[:kind], locale: I18n.locale)
|
|
@@ -16,21 +34,46 @@ module Importo
|
|
|
16
34
|
return
|
|
17
35
|
end
|
|
18
36
|
@import = Import.new(import_params.merge(locale: I18n.locale,
|
|
19
|
-
|
|
20
|
-
if @import.valid? && @import.
|
|
21
|
-
|
|
37
|
+
importo_ownable: Importo.config.current_import_owner.call))
|
|
38
|
+
if params["commit"] == "upload" && @import.valid? && @import.save!
|
|
39
|
+
@import.confirm!
|
|
40
|
+
@import.schedule!
|
|
41
|
+
redirect_to importo.new_import_path(params[:kind] || @import.kind)
|
|
42
|
+
elsif params["commit"] == "preview" && @import.valid?
|
|
43
|
+
@import.save!
|
|
44
|
+
redirect_to action: :preview, id: @import.id, kind: @import.kind
|
|
22
45
|
else
|
|
23
46
|
Signum.error(Current.user, text: t(".flash.error", error: @import.errors&.full_messages&.join(".")))
|
|
24
47
|
render :new
|
|
25
48
|
end
|
|
26
49
|
end
|
|
27
50
|
|
|
51
|
+
def cancel
|
|
52
|
+
@import = Import.find(params[:id])
|
|
53
|
+
@import.original.purge if @import.concept?
|
|
54
|
+
Signum.error(Current.user, text: t('.flash.cancel', id: @import.id))
|
|
55
|
+
# flash[:notice] = t('.flash.cancel', id: @import.id)
|
|
56
|
+
redirect_to action: :new, kind: @import.kind
|
|
57
|
+
end
|
|
58
|
+
|
|
28
59
|
def undo
|
|
29
60
|
@import = Import.where(importo_ownable: Importo.config.current_import_owner.call).find(params[:id])
|
|
30
61
|
if @import.can_revert? && @import.revert
|
|
31
62
|
redirect_to action: :index, notice: "Import reverted"
|
|
32
63
|
else
|
|
33
|
-
redirect_to action: :index, alert:
|
|
64
|
+
redirect_to action: :index, alert: 'Import could not be reverted'
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def upload
|
|
69
|
+
@import = Import.find(params[:id])
|
|
70
|
+
@import.checked_columns = params[:selected_items]&.reject { |element| element == "0" }
|
|
71
|
+
@import.confirm! if @import.can_confirm?
|
|
72
|
+
if @import.valid? && @import.schedule!
|
|
73
|
+
redirect_to action: :index
|
|
74
|
+
else
|
|
75
|
+
Signum.error(Current.user, text: t('.flash.error', id: @import.id))
|
|
76
|
+
render :new
|
|
34
77
|
end
|
|
35
78
|
end
|
|
36
79
|
|
|
@@ -123,28 +123,36 @@ module Importable
|
|
|
123
123
|
#
|
|
124
124
|
# Does the actual import
|
|
125
125
|
#
|
|
126
|
-
def import!
|
|
127
|
-
raise ArgumentError,
|
|
126
|
+
def import!(checked_columns)
|
|
127
|
+
raise ArgumentError, 'Invalid data structure' unless structure_valid?
|
|
128
128
|
|
|
129
129
|
batch = Importo.config.batch_adapter.new
|
|
130
130
|
batch.description = "#{import.original.filename} - #{import.kind}"
|
|
131
131
|
batch.properties = {import_id: import.id}
|
|
132
|
-
|
|
132
|
+
if Importo.config.batch_adapter == Importo::SidekiqBatchAdapter
|
|
133
|
+
batch.on_success("Importo::ImportJobCallback")
|
|
134
|
+
else
|
|
135
|
+
batch.on_success = "Importo::ImportJobCallback"
|
|
136
|
+
end
|
|
133
137
|
|
|
134
138
|
batch.add do
|
|
135
139
|
column_with_delay = columns.select { |k, v| v.delay.present? }
|
|
136
|
-
loop_data_rows do |attributes, index|
|
|
140
|
+
loop_data_rows(checked_columns) do |attributes, index|
|
|
137
141
|
if column_with_delay.present?
|
|
138
142
|
delay = column_with_delay.filter_map do |k, v|
|
|
139
143
|
next unless attributes[k].present?
|
|
140
144
|
v.delay.call(attributes[k])
|
|
141
145
|
end
|
|
142
146
|
end
|
|
143
|
-
Importo::ImportJob.set(wait_until: (delay.max * index).seconds.from_now).
|
|
144
|
-
Importo::ImportJob.
|
|
147
|
+
Importo::ImportJob.set(wait_until: (delay.max * index).seconds.from_now).perform_later(JSON.dump(attributes), index, import.id) if delay.present?
|
|
148
|
+
Importo::ImportJob.perform_later(JSON.dump(attributes), index, import.id) unless delay.present?
|
|
145
149
|
end
|
|
146
150
|
end
|
|
147
151
|
|
|
152
|
+
if defined?(GoodJob::Batch) && Importo.config.batch_adapter == GoodJob::Batch
|
|
153
|
+
batch.enqueue
|
|
154
|
+
end
|
|
155
|
+
|
|
148
156
|
true
|
|
149
157
|
rescue => e
|
|
150
158
|
@import.result_message = "Exception: #{e.message}"
|
|
@@ -110,7 +110,7 @@ module Original
|
|
|
110
110
|
duplicate(row_hash, id)
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
def loop_data_rows
|
|
113
|
+
def loop_data_rows(checked_columns = nil)
|
|
114
114
|
(data_start_row..spreadsheet.last_row).map do |index|
|
|
115
115
|
row = cells_from_row(index, false)
|
|
116
116
|
|
|
@@ -120,7 +120,10 @@ module Original
|
|
|
120
120
|
[column, value]
|
|
121
121
|
end.to_h
|
|
122
122
|
attributes.reject! { |k, _v| headers_added_by_import.include?(k) }
|
|
123
|
-
|
|
123
|
+
if checked_columns&.dig(:checked_columns).present?
|
|
124
|
+
selected_columns = checked_columns[:checked_columns].map{|i| col_for(i)&.first}
|
|
125
|
+
attributes.reject!{|k, _v| selected_columns.exclude?(k) }
|
|
126
|
+
end
|
|
124
127
|
yield attributes, index
|
|
125
128
|
end
|
|
126
129
|
end
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
module Importo
|
|
2
|
-
class ImportJobCallback
|
|
2
|
+
class ImportJobCallback < ActiveJob::Base
|
|
3
|
+
include Sidekiq::Batch::Callback
|
|
3
4
|
include Rails.application.routes.url_helpers
|
|
4
5
|
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
import
|
|
6
|
+
def perform(batch, params)
|
|
7
|
+
import = Import.find(batch.properties[:import_id])
|
|
8
|
+
complete_import(import)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def complete_import(import)
|
|
8
12
|
if import.present?
|
|
9
13
|
results_file = import.importer.results_file
|
|
10
14
|
results_file = results_file.is_a?(StringIO) ? results_file : File.open(results_file)
|
|
@@ -24,5 +28,12 @@ module Importo
|
|
|
24
28
|
end
|
|
25
29
|
end
|
|
26
30
|
end
|
|
31
|
+
|
|
32
|
+
def on_complete(status, options)
|
|
33
|
+
options = options.deep_stringify_keys
|
|
34
|
+
import = Import.find(options["import_id"])
|
|
35
|
+
complete_import(import)
|
|
36
|
+
end
|
|
37
|
+
|
|
27
38
|
end
|
|
28
39
|
end
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
module Importo
|
|
2
|
-
class ImportJob <
|
|
2
|
+
class ImportJob < ApplicationJob
|
|
3
3
|
# No options here, gets added from the adapter
|
|
4
|
+
queue_as Importo.config.queue_name
|
|
5
|
+
include GoodJob::ActiveJobExtensions::Batches
|
|
4
6
|
|
|
5
7
|
def perform(attributes, index, import_id)
|
|
6
|
-
self.class.execute_row(attributes, index, import_id, false
|
|
8
|
+
self.class.execute_row(attributes, index, import_id, false)
|
|
7
9
|
end
|
|
8
10
|
|
|
9
|
-
def self.execute_row(attributes, index, import_id, last_attempt
|
|
11
|
+
def self.execute_row(attributes, index, import_id, last_attempt)
|
|
10
12
|
attributes = JSON.load(attributes).deep_symbolize_keys if attributes.is_a?(String)
|
|
11
13
|
|
|
12
14
|
import = Import.find(import_id)
|
|
@@ -16,13 +18,13 @@ module Importo
|
|
|
16
18
|
# - Sidekiq calls on_complete callback when all jobs ran at least once.
|
|
17
19
|
# - GoodJob calls on_complete callback when all jobs are done (including retries).
|
|
18
20
|
# i.e. this logic is only needed for sidekiq
|
|
19
|
-
if Importo.config.batch_adapter == Importo::SidekiqBatchAdapter
|
|
20
|
-
|
|
21
|
+
# if Importo.config.batch_adapter == Importo::SidekiqBatchAdapter
|
|
22
|
+
# batch = Importo::SidekiqBatchAdapter.find(bid)
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
24
|
+
# if !import.completed? && import.can_complete? && batch.finished?
|
|
25
|
+
# ImportJobCallback.new.on_complete("success", {import_id: import.id})
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
26
28
|
end
|
|
27
29
|
end
|
|
28
30
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
module Importo
|
|
2
2
|
class PurgeImportJob < ApplicationJob
|
|
3
|
-
def perform(owner, months)
|
|
3
|
+
def perform(owner, months,state = nil)
|
|
4
|
+
|
|
4
5
|
imports = Import.where(importo_ownable: owner, created_at: ..months.months.ago.beginning_of_day)
|
|
6
|
+
imports = imports.where(state: state) if state
|
|
5
7
|
|
|
6
8
|
imports.each do |import|
|
|
7
9
|
import.original.purge
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Importo
|
|
4
4
|
class Import < Importo::ApplicationRecord
|
|
5
5
|
# include ActiveStorage::Downloading
|
|
6
|
-
|
|
6
|
+
attr_accessor :checked_columns
|
|
7
7
|
belongs_to :importo_ownable, polymorphic: true
|
|
8
8
|
|
|
9
9
|
has_many :message_instances, as: :messagable
|
|
@@ -12,7 +12,6 @@ module Importo
|
|
|
12
12
|
validates :kind, presence: true
|
|
13
13
|
validates :original, presence: true
|
|
14
14
|
validate :content_validator
|
|
15
|
-
|
|
16
15
|
begin
|
|
17
16
|
has_one_attached :original
|
|
18
17
|
has_one_attached :result
|
|
@@ -20,7 +19,8 @@ module Importo
|
|
|
20
19
|
# Weird loading sequence error, is fixed by the lib/importo/helpers
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
state_machine :state, initial: :
|
|
22
|
+
state_machine :state, initial: :concept do
|
|
23
|
+
state :confirmed
|
|
24
24
|
state :importing
|
|
25
25
|
state :scheduled
|
|
26
26
|
state :completed
|
|
@@ -35,11 +35,15 @@ module Importo
|
|
|
35
35
|
after_transition any => :reverting, :do => :schedule_revert
|
|
36
36
|
|
|
37
37
|
event :schedule do
|
|
38
|
-
transition
|
|
38
|
+
transition confirmed: :scheduled
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
event :confirm do
|
|
42
|
+
transition concept: :confirmed
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
event :import do
|
|
42
|
-
transition
|
|
46
|
+
transition confirmed: :importing
|
|
43
47
|
transition scheduled: :importing
|
|
44
48
|
transition failed: :importing
|
|
45
49
|
end
|
|
@@ -111,7 +115,7 @@ module Importo
|
|
|
111
115
|
private
|
|
112
116
|
|
|
113
117
|
def schedule_import
|
|
114
|
-
|
|
118
|
+
ImportService.perform_later(import: self, checked_columns: self.checked_columns)
|
|
115
119
|
end
|
|
116
120
|
|
|
117
121
|
def schedule_revert
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Importo
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
attribute :import, type: Import, typecaster: ->(value) { value.is_a?(Import) ? value : Import.find(value) }
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
else
|
|
11
|
-
class ImportContext < ApplicationContext
|
|
12
|
-
attribute :import, :model, class_name: "Importo::Import"
|
|
13
|
-
end
|
|
14
|
-
|
|
4
|
+
class ImportContext < ApplicationContext
|
|
5
|
+
attribute :import, type: Import, typecaster: ->(value) { value.is_a?(Import) ? value : Import.find(value) }
|
|
6
|
+
attribute :checked_columns, type: Array, typecaster: ->(value){ value.is_a?(Array) ? value : [] }
|
|
15
7
|
end
|
|
16
8
|
end
|
|
@@ -4,9 +4,10 @@ module Importo
|
|
|
4
4
|
class ImportService < ApplicationService
|
|
5
5
|
def perform
|
|
6
6
|
context.import.import!
|
|
7
|
-
context.import.importer.import!
|
|
8
|
-
rescue
|
|
9
|
-
|
|
7
|
+
context.import.importer.import!(checked_columns: context.checked_columns )
|
|
8
|
+
rescue StandardError
|
|
9
|
+
context.import.failure!
|
|
10
|
+
context.fail!
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
end
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
= f.input :kind, as: :hidden
|
|
5
5
|
= sts.card :"importo_imports #{@import.kind}", title: t('.title'), icon: 'fad fa-file-spreadsheet' do |card|
|
|
6
6
|
- card.with_action
|
|
7
|
-
= f.
|
|
7
|
+
= f.button 'preview', value: 'preview', class: 'button secondary'
|
|
8
|
+
- card.with_action
|
|
9
|
+
= f.submit nil, value: 'upload'
|
|
8
10
|
|
|
9
11
|
.grid.grid-cols-12.gap-4
|
|
10
12
|
.col-span-12
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
= sts.form_with(model: false, url: upload_import_path) do |f|
|
|
2
|
+
= sts.card :"importo_imports #{@import.kind}", title: t('.title', kind: @import&.kind.capitalize), icon: 'fad fa-file-spreadsheet' do |card|
|
|
3
|
+
- card.with_action
|
|
4
|
+
/ = f.button 'Cancel', value: 'cancel'
|
|
5
|
+
= f.submit 'Upload', value: 'upload'
|
|
6
|
+
= link_to(t('.cancel'), cancel_import_path(@import), method: :post, class: 'button')
|
|
7
|
+
.px-4.sm:px-6.lg:px-8
|
|
8
|
+
/ .sm:flex.sm:items-center
|
|
9
|
+
/ .sm:flex-auto
|
|
10
|
+
/ h1.text-base.font-semibold.leading-6.text-gray-900
|
|
11
|
+
/ | Users
|
|
12
|
+
/ p.mt-2.text-sm.text-gray-700
|
|
13
|
+
/ | A list of all the users in your account including their name, title, email and role.
|
|
14
|
+
/ .mt-4.sm:ml-16.sm:mt-0.sm:flex-none
|
|
15
|
+
/ button.block.rounded-md.bg-indigo-600.px-3.py-2.text-center.text-sm.font-semibold.text-white.shadow-sm.hover:bg-indigo-500.focus-visible:outline.focus-visible:outline-2.focus-visible:outline-offset-2.focus-visible:outline-indigo-600[type="button"]
|
|
16
|
+
/ | Add user
|
|
17
|
+
.mt-8.flow-root
|
|
18
|
+
.-mx-4.-my-2.overflow-x-auto.sm:-mx-6.lg:-mx-8
|
|
19
|
+
.inline-block.min-w-full.py-2.align-middle.bg-white.dark:bg-gray-900.dark:border-gray-700
|
|
20
|
+
table.min-w-full.divide-y.divide-gray-20.border-2
|
|
21
|
+
thead.bg-gray-50
|
|
22
|
+
tr
|
|
23
|
+
- @sheet_data.first.keys.each_with_index do |header, index|
|
|
24
|
+
th.px-3.py-3.5.text-left.text-sm.font-semibold.border-2.text-gray-600[scope="col"]
|
|
25
|
+
= f.check_box :selected_items, { multiple: true, checked: @check_header.include?(header), disabled: @check_header.exclude?(header) }, ActionController::Base.helpers.strip_tags(header)
|
|
26
|
+
tr
|
|
27
|
+
- @sheet_data.first.keys.each_with_index do |header, index|
|
|
28
|
+
th.px-3.py-3.5.text-left.text-sm.font-semibold.border-2.text-gray-600[scope="col"]
|
|
29
|
+
/ = f.check_box :active, checked: @check_header.include?(header)
|
|
30
|
+
= ActionController::Base.helpers.strip_tags(header)
|
|
31
|
+
tbody.divide-y.divide-gray-200.bg-white
|
|
32
|
+
- @sheet_data.reject! { |h| h.keys == h.values }&.each do |h|
|
|
33
|
+
tr
|
|
34
|
+
- h.each do |key, value|
|
|
35
|
+
td.whitespace-nowrap.px-3.py-4.text-sm.text-gray-500.border-2
|
|
36
|
+
= value
|
data/config/locales/en.yml
CHANGED
|
@@ -2,7 +2,7 @@ en:
|
|
|
2
2
|
helpers:
|
|
3
3
|
submit:
|
|
4
4
|
importo/import:
|
|
5
|
-
create: "
|
|
5
|
+
create: "Upload"
|
|
6
6
|
importo:
|
|
7
7
|
sheet:
|
|
8
8
|
results:
|
|
@@ -14,6 +14,8 @@ en:
|
|
|
14
14
|
explanation: Purpose
|
|
15
15
|
example: Example
|
|
16
16
|
imports:
|
|
17
|
+
cancel:
|
|
18
|
+
success: "Import canceled for id %{id}, Redirecting to new"
|
|
17
19
|
index:
|
|
18
20
|
title: Import results
|
|
19
21
|
card:
|
|
@@ -38,6 +40,15 @@ en:
|
|
|
38
40
|
no_file: Import failed, please upload a file.
|
|
39
41
|
error: Import failed, there were problems %{error}.
|
|
40
42
|
success: "Import scheduled with id %{id}, you will get an email with the results."
|
|
43
|
+
preview:
|
|
44
|
+
no_file: Import failed, please upload valid file.
|
|
45
|
+
title: "%{kind} Import Preview"
|
|
46
|
+
cancel: Cancel
|
|
47
|
+
upload:
|
|
48
|
+
flash:
|
|
49
|
+
no_file: Import failed, please upload a file.
|
|
50
|
+
error: Import failed, there were problems.
|
|
51
|
+
success: "Import scheduled with id %{id}, you will get an email with the results."
|
|
41
52
|
errors:
|
|
42
53
|
parse_error: "We encountered a parse error: %{error}"
|
|
43
54
|
structure_invalid: "The structure is invalid, these are the invalid headers: %{invalid_headers}"
|
data/config/routes.rb
CHANGED
|
@@ -4,10 +4,13 @@ Importo::Engine.routes.draw do
|
|
|
4
4
|
resources :imports, except: %i[new] do
|
|
5
5
|
member do
|
|
6
6
|
post :undo
|
|
7
|
+
post :upload
|
|
8
|
+
post :cancel
|
|
7
9
|
end
|
|
8
10
|
end
|
|
9
|
-
get
|
|
10
|
-
get
|
|
11
|
-
get
|
|
12
|
-
|
|
11
|
+
get ':kind/new', to: 'imports#new', as: :new_import
|
|
12
|
+
get ':kind/sample', to: 'imports#sample', as: :sample_import
|
|
13
|
+
get ':kind/export', to: 'imports#export', as: :export
|
|
14
|
+
get ':kind/:id/preview', to: 'imports#preview', as: :preview
|
|
15
|
+
root to: 'imports#index'
|
|
13
16
|
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobs < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
# Uncomment for Postgres v12 or earlier to enable gen_random_uuid() support
|
|
6
|
+
# enable_extension 'pgcrypto'
|
|
7
|
+
|
|
8
|
+
create_table :good_jobs, id: :uuid do |t|
|
|
9
|
+
t.text :queue_name
|
|
10
|
+
t.integer :priority
|
|
11
|
+
t.jsonb :serialized_params
|
|
12
|
+
t.datetime :scheduled_at
|
|
13
|
+
t.datetime :performed_at
|
|
14
|
+
t.datetime :finished_at
|
|
15
|
+
t.text :error
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
|
|
19
|
+
t.uuid :active_job_id
|
|
20
|
+
t.text :concurrency_key
|
|
21
|
+
t.text :cron_key
|
|
22
|
+
t.uuid :retried_good_job_id
|
|
23
|
+
t.datetime :cron_at
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
create_table :good_job_processes, id: :uuid do |t|
|
|
27
|
+
t.timestamps
|
|
28
|
+
t.jsonb :state
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)", name: :index_good_jobs_on_scheduled_at
|
|
32
|
+
add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)", name: :index_good_jobs_on_queue_name_and_scheduled_at
|
|
33
|
+
add_index :good_jobs, [:active_job_id, :created_at], name: :index_good_jobs_on_active_job_id_and_created_at
|
|
34
|
+
add_index :good_jobs, :concurrency_key, where: "(finished_at IS NULL)", name: :index_good_jobs_on_concurrency_key_when_unfinished
|
|
35
|
+
add_index :good_jobs, [:cron_key, :created_at], name: :index_good_jobs_on_cron_key_and_created_at
|
|
36
|
+
add_index :good_jobs, [:cron_key, :cron_at], name: :index_good_jobs_on_cron_key_and_cron_at, unique: true
|
|
37
|
+
add_index :good_jobs, [:active_job_id], name: :index_good_jobs_on_active_job_id
|
|
38
|
+
add_index :good_jobs, [:finished_at], where: "retried_good_job_id IS NULL AND finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobSettings < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.table_exists?(:good_job_settings)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
create_table :good_job_settings, id: :uuid do |t|
|
|
14
|
+
t.timestamps
|
|
15
|
+
t.text :key
|
|
16
|
+
t.jsonb :value
|
|
17
|
+
t.index :key, unique: true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/db/migrate/20251117132127_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateIndexGoodJobsJobsOnPriorityCreatedAtWhenUnfinished < ActiveRecord::Migration[7.0]
|
|
4
|
+
disable_ddl_transaction!
|
|
5
|
+
|
|
6
|
+
def change
|
|
7
|
+
reversible do |dir|
|
|
8
|
+
dir.up do
|
|
9
|
+
# Ensure this incremental update migration is idempotent
|
|
10
|
+
# with monolithic install migration.
|
|
11
|
+
return if connection.index_name_exists?(:good_jobs, :index_good_jobs_jobs_on_priority_created_at_when_unfinished)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :good_jobs, [:priority, :created_at], order: { priority: "DESC NULLS LAST", created_at: :asc },
|
|
16
|
+
where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished,
|
|
17
|
+
algorithm: :concurrently
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobBatches < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.table_exists?(:good_job_batches)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
create_table :good_job_batches, id: :uuid do |t|
|
|
14
|
+
t.timestamps
|
|
15
|
+
t.text :description
|
|
16
|
+
t.jsonb :serialized_properties
|
|
17
|
+
t.text :on_finish
|
|
18
|
+
t.text :on_success
|
|
19
|
+
t.text :on_discard
|
|
20
|
+
t.text :callback_queue_name
|
|
21
|
+
t.integer :callback_priority
|
|
22
|
+
t.datetime :enqueued_at
|
|
23
|
+
t.datetime :discarded_at
|
|
24
|
+
t.datetime :finished_at
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
change_table :good_jobs do |t|
|
|
28
|
+
t.uuid :batch_id
|
|
29
|
+
t.uuid :batch_callback_id
|
|
30
|
+
|
|
31
|
+
t.index :batch_id, where: "batch_id IS NOT NULL"
|
|
32
|
+
t.index :batch_callback_id, where: "batch_callback_id IS NOT NULL"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobExecutions < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.table_exists?(:good_job_executions)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
create_table :good_job_executions, id: :uuid do |t|
|
|
14
|
+
t.timestamps
|
|
15
|
+
|
|
16
|
+
t.uuid :active_job_id, null: false
|
|
17
|
+
t.text :job_class
|
|
18
|
+
t.text :queue_name
|
|
19
|
+
t.jsonb :serialized_params
|
|
20
|
+
t.datetime :scheduled_at
|
|
21
|
+
t.datetime :finished_at
|
|
22
|
+
t.text :error
|
|
23
|
+
|
|
24
|
+
t.index [:active_job_id, :created_at], name: :index_good_job_executions_on_active_job_id_and_created_at
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
change_table :good_jobs do |t|
|
|
28
|
+
t.boolean :is_discrete
|
|
29
|
+
t.integer :executions_count
|
|
30
|
+
t.text :job_class
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobsErrorEvent < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.column_exists?(:good_jobs, :error_event)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_column :good_jobs, :error_event, :integer, limit: 2
|
|
14
|
+
add_column :good_job_executions, :error_event, :integer, limit: 2
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RecreateGoodJobCronIndexesWithConditional < ActiveRecord::Migration[7.0]
|
|
4
|
+
disable_ddl_transaction!
|
|
5
|
+
|
|
6
|
+
def change
|
|
7
|
+
reversible do |dir|
|
|
8
|
+
dir.up do
|
|
9
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at_cond)
|
|
10
|
+
add_index :good_jobs, [:cron_key, :created_at], where: "(cron_key IS NOT NULL)",
|
|
11
|
+
name: :index_good_jobs_on_cron_key_and_created_at_cond, algorithm: :concurrently
|
|
12
|
+
end
|
|
13
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at_cond)
|
|
14
|
+
add_index :good_jobs, [:cron_key, :cron_at], where: "(cron_key IS NOT NULL)", unique: true,
|
|
15
|
+
name: :index_good_jobs_on_cron_key_and_cron_at_cond, algorithm: :concurrently
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at)
|
|
19
|
+
remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_created_at
|
|
20
|
+
end
|
|
21
|
+
if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at)
|
|
22
|
+
remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_cron_at
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
dir.down do
|
|
27
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at)
|
|
28
|
+
add_index :good_jobs, [:cron_key, :created_at],
|
|
29
|
+
name: :index_good_jobs_on_cron_key_and_created_at, algorithm: :concurrently
|
|
30
|
+
end
|
|
31
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at)
|
|
32
|
+
add_index :good_jobs, [:cron_key, :cron_at], unique: true,
|
|
33
|
+
name: :index_good_jobs_on_cron_key_and_cron_at, algorithm: :concurrently
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_created_at_cond)
|
|
37
|
+
remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_created_at_cond
|
|
38
|
+
end
|
|
39
|
+
if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at_cond)
|
|
40
|
+
remove_index :good_jobs, name: :index_good_jobs_on_cron_key_and_cron_at_cond
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobLabels < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.column_exists?(:good_jobs, :labels)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_column :good_jobs, :labels, :text, array: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobLabelsIndex < ActiveRecord::Migration[7.0]
|
|
4
|
+
disable_ddl_transaction!
|
|
5
|
+
|
|
6
|
+
def change
|
|
7
|
+
reversible do |dir|
|
|
8
|
+
dir.up do
|
|
9
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels)
|
|
10
|
+
add_index :good_jobs, :labels, using: :gin, where: "(labels IS NOT NULL)",
|
|
11
|
+
name: :index_good_jobs_on_labels, algorithm: :concurrently
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
dir.down do
|
|
16
|
+
if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_labels)
|
|
17
|
+
remove_index :good_jobs, name: :index_good_jobs_on_labels
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RemoveGoodJobActiveIdIndex < ActiveRecord::Migration[7.0]
|
|
4
|
+
disable_ddl_transaction!
|
|
5
|
+
|
|
6
|
+
def change
|
|
7
|
+
reversible do |dir|
|
|
8
|
+
dir.up do
|
|
9
|
+
if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id)
|
|
10
|
+
remove_index :good_jobs, name: :index_good_jobs_on_active_job_id
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
dir.down do
|
|
15
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_active_job_id)
|
|
16
|
+
add_index :good_jobs, :active_job_id, name: :index_good_jobs_on_active_job_id
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateIndexGoodJobJobsForCandidateLookup < ActiveRecord::Migration[7.0]
|
|
4
|
+
disable_ddl_transaction!
|
|
5
|
+
|
|
6
|
+
def change
|
|
7
|
+
reversible do |dir|
|
|
8
|
+
dir.up do
|
|
9
|
+
# Ensure this incremental update migration is idempotent
|
|
10
|
+
# with monolithic install migration.
|
|
11
|
+
return if connection.index_name_exists?(:good_jobs, :index_good_job_jobs_for_candidate_lookup)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :good_jobs, [:priority, :created_at], order: { priority: "ASC NULLS LAST", created_at: :asc },
|
|
16
|
+
where: "finished_at IS NULL", name: :index_good_job_jobs_for_candidate_lookup,
|
|
17
|
+
algorithm: :concurrently
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobExecutionErrorBacktrace < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.column_exists?(:good_job_executions, :error_backtrace)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_column :good_job_executions, :error_backtrace, :text, array: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobProcessLockIds < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.column_exists?(:good_jobs, :locked_by_id)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_column :good_jobs, :locked_by_id, :uuid
|
|
14
|
+
add_column :good_jobs, :locked_at, :datetime
|
|
15
|
+
add_column :good_job_executions, :process_id, :uuid
|
|
16
|
+
add_column :good_job_processes, :lock_type, :integer, limit: 2
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobProcessLockIndexes < ActiveRecord::Migration[7.0]
|
|
4
|
+
disable_ddl_transaction!
|
|
5
|
+
|
|
6
|
+
def change
|
|
7
|
+
reversible do |dir|
|
|
8
|
+
dir.up do
|
|
9
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked)
|
|
10
|
+
add_index :good_jobs, [:priority, :scheduled_at],
|
|
11
|
+
order: { priority: "ASC NULLS LAST", scheduled_at: :asc },
|
|
12
|
+
where: "finished_at IS NULL AND locked_by_id IS NULL",
|
|
13
|
+
name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked,
|
|
14
|
+
algorithm: :concurrently
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id)
|
|
18
|
+
add_index :good_jobs, :locked_by_id,
|
|
19
|
+
where: "locked_by_id IS NOT NULL",
|
|
20
|
+
name: :index_good_jobs_on_locked_by_id,
|
|
21
|
+
algorithm: :concurrently
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
unless connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at)
|
|
25
|
+
add_index :good_job_executions, [:process_id, :created_at],
|
|
26
|
+
name: :index_good_job_executions_on_process_id_and_created_at,
|
|
27
|
+
algorithm: :concurrently
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
dir.down do
|
|
32
|
+
remove_index(:good_jobs, name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked)
|
|
33
|
+
remove_index(:good_jobs, name: :index_good_jobs_on_locked_by_id) if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id)
|
|
34
|
+
remove_index(:good_job_executions, name: :index_good_job_executions_on_process_id_and_created_at) if connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateGoodJobExecutionDuration < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
reversible do |dir|
|
|
6
|
+
dir.up do
|
|
7
|
+
# Ensure this incremental update migration is idempotent
|
|
8
|
+
# with monolithic install migration.
|
|
9
|
+
return if connection.column_exists?(:good_job_executions, :duration)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_column :good_job_executions, :duration, :interval
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -75,5 +75,5 @@ module Importo
|
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
Importo::ImportJob.send(:include, Sidekiq::Job)
|
|
79
|
-
Importo::ImportJob.send(:include, Importo::SidekiqBatchAdapter::ImportJobIncludes)
|
|
78
|
+
# Importo::ImportJob.send(:include, Sidekiq::Job)
|
|
79
|
+
# Importo::ImportJob.send(:include, Importo::SidekiqBatchAdapter::ImportJobIncludes)
|
|
@@ -6,6 +6,7 @@ module Importo
|
|
|
6
6
|
def option(name, default: nil, proc: false)
|
|
7
7
|
attr_writer(name)
|
|
8
8
|
schema[name] = {default: default, proc: proc}
|
|
9
|
+
|
|
9
10
|
if schema[name][:proc]
|
|
10
11
|
define_method(name) do |*params|
|
|
11
12
|
value = instance_variable_get(:"@#{name}")
|
|
@@ -44,6 +45,8 @@ module Importo
|
|
|
44
45
|
option :base_service_context, default: "::ApplicationContext"
|
|
45
46
|
option :current_import_owner, default: lambda {}
|
|
46
47
|
option :queue_name, default: :import
|
|
48
|
+
# You can either use GoodJob::Batch or Importo::SidekiqBatchAdapter
|
|
49
|
+
option :batch_adapter, default: lambda { GoodJob::Batch }, proc: true
|
|
47
50
|
|
|
48
51
|
option :admin_visible_imports, default: lambda { Importo::Import.where(importo_ownable: Importo.config.current_import_owner) }
|
|
49
52
|
option(:admin_can_destroy,
|
data/lib/importo/test_helpers.rb
CHANGED
|
@@ -28,9 +28,12 @@ module Importo
|
|
|
28
28
|
|
|
29
29
|
import.original.attach(io: sheet, filename: filename, content_type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", identify: false)
|
|
30
30
|
import.save!
|
|
31
|
-
|
|
31
|
+
import.confirm
|
|
32
|
+
import.schedule
|
|
32
33
|
ImportService.perform(import: import)
|
|
33
|
-
|
|
34
|
+
if Importo.config.batch_adapter == Importo::SidekiqBatchAdapter
|
|
35
|
+
ImportJobCallback.new.on_success(:success,{import_id: import.id})
|
|
36
|
+
end
|
|
34
37
|
import
|
|
35
38
|
end
|
|
36
39
|
end
|
data/lib/importo/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: importo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.23
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andre Meij
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2025-
|
|
12
|
+
date: 2025-11-24 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: caxlsx
|
|
@@ -95,20 +95,6 @@ dependencies:
|
|
|
95
95
|
- - "~>"
|
|
96
96
|
- !ruby/object:Gem::Version
|
|
97
97
|
version: '2'
|
|
98
|
-
- !ruby/object:Gem::Dependency
|
|
99
|
-
name: servitium
|
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
|
101
|
-
requirements:
|
|
102
|
-
- - ">="
|
|
103
|
-
- !ruby/object:Gem::Version
|
|
104
|
-
version: '1.2'
|
|
105
|
-
type: :runtime
|
|
106
|
-
prerelease: false
|
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
-
requirements:
|
|
109
|
-
- - ">="
|
|
110
|
-
- !ruby/object:Gem::Version
|
|
111
|
-
version: '1.2'
|
|
112
98
|
- !ruby/object:Gem::Dependency
|
|
113
99
|
name: signum
|
|
114
100
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -350,6 +336,7 @@ files:
|
|
|
350
336
|
- app/tables/importo/mensa_imports_table.rb
|
|
351
337
|
- app/views/importo/imports/index.html.slim
|
|
352
338
|
- app/views/importo/imports/new.html.slim
|
|
339
|
+
- app/views/importo/imports/preview.html.slim
|
|
353
340
|
- config/locales/en.yml
|
|
354
341
|
- config/locales/nl.yml
|
|
355
342
|
- config/routes.rb
|
|
@@ -358,6 +345,21 @@ files:
|
|
|
358
345
|
- db/migrate/20190827093548_add_selected_fields_to_import.rb
|
|
359
346
|
- db/migrate/20230510051447_remove_result_from_imports.rb
|
|
360
347
|
- db/migrate/20230510083043_create_importo_results.rb
|
|
348
|
+
- db/migrate/20251117132125_create_good_jobs.rb
|
|
349
|
+
- db/migrate/20251117132126_create_good_job_settings.rb
|
|
350
|
+
- db/migrate/20251117132127_create_index_good_jobs_jobs_on_priority_created_at_when_unfinished.rb
|
|
351
|
+
- db/migrate/20251117132128_create_good_job_batches.rb
|
|
352
|
+
- db/migrate/20251117132129_create_good_job_executions.rb
|
|
353
|
+
- db/migrate/20251117132130_create_good_jobs_error_event.rb
|
|
354
|
+
- db/migrate/20251117132131_recreate_good_job_cron_indexes_with_conditional.rb
|
|
355
|
+
- db/migrate/20251117132132_create_good_job_labels.rb
|
|
356
|
+
- db/migrate/20251117132133_create_good_job_labels_index.rb
|
|
357
|
+
- db/migrate/20251117132134_remove_good_job_active_id_index.rb
|
|
358
|
+
- db/migrate/20251117132135_create_index_good_job_jobs_for_candidate_lookup.rb
|
|
359
|
+
- db/migrate/20251117132136_create_good_job_execution_error_backtrace.rb
|
|
360
|
+
- db/migrate/20251117132137_create_good_job_process_lock_ids.rb
|
|
361
|
+
- db/migrate/20251117132138_create_good_job_process_lock_indexes.rb
|
|
362
|
+
- db/migrate/20251117132139_create_good_job_execution_duration.rb
|
|
361
363
|
- lib/generators/importo/USAGE
|
|
362
364
|
- lib/generators/importo/importer_generator.rb
|
|
363
365
|
- lib/generators/importo/install_generator.rb
|