importance 0.2.5 → 0.2.6
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/app/controllers/importance/imports_controller.rb +62 -74
- data/app/views/importance/imports/map.html.erb +8 -8
- data/config/locales/de.yml +11 -1
- data/config/locales/en.yml +11 -1
- data/config/locales/fr.yml +11 -1
- data/config/locales/it.yml +11 -1
- data/lib/importance/configuration.rb +46 -2
- data/lib/importance/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 495f2f10d4834e86a1224ba0368b4642fb136c371dc79f635cd5d2187be756f9
|
|
4
|
+
data.tar.gz: 9971a1b0c872ab7a4f1b705ff3b105b9217d95a7c1030043053f69a1ae95fc53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f73128060f9b98baac9959e8a3f94ba3e061d019b7b2d06df6145afaec892ff4271a4f4b34052c4ac700010f52ef3603da750327e20006e0f40539a4b2dbdc10
|
|
7
|
+
data.tar.gz: 131100a75c3ea0b074f3a097d759b77bd582b549c7206e469c7a180ce3b716c576bfa25fdaf0183d7b899cdce48c9297af9e71296f41d2af9021bddb65d99d4e
|
|
@@ -6,125 +6,113 @@ module Importance
|
|
|
6
6
|
# Form submission target. Persist the file and redirect to the mapping page.
|
|
7
7
|
def submit
|
|
8
8
|
upload = params[:file]
|
|
9
|
+
session[:redirect_url] = params[:redirect_url]
|
|
10
|
+
session[:importer] = params[:importer].to_sym
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
if upload.nil?
|
|
13
|
+
flash[:alert] = t("importance.errors.no_file")
|
|
14
|
+
redirect_to session[:redirect_url] and return
|
|
15
|
+
end
|
|
11
16
|
|
|
12
17
|
upload_extension = File.extname(upload.original_filename).downcase
|
|
13
18
|
supported_extensions = [ ".xlsx", ".xls", ".csv" ]
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
if !supported_extensions.include?(upload_extension)
|
|
21
|
+
flash[:alert] = t("importance.errors.invalid_file_type", types: supported_extensions.join(", "))
|
|
22
|
+
redirect_to session[:redirect_url] and return
|
|
23
|
+
end
|
|
16
24
|
|
|
17
25
|
system_tmp_dir = Dir.tmpdir
|
|
18
26
|
upload_path = upload.tempfile.path
|
|
19
27
|
persist_filename = "#{SecureRandom.uuid}#{upload_extension}"
|
|
20
28
|
|
|
21
29
|
persist_path = File.join(system_tmp_dir, persist_filename)
|
|
30
|
+
session[:path] = persist_path
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
if !File.exist?(upload_path)
|
|
33
|
+
flash[:alert] = t("importance.errors.no_file")
|
|
34
|
+
redirect_to session[:redirect_url] and return
|
|
35
|
+
end
|
|
24
36
|
|
|
25
37
|
FileUtils.mv(upload_path, persist_path)
|
|
26
38
|
|
|
27
|
-
session[:path] = persist_path
|
|
28
|
-
session[:importer] = params[:importer].to_sym
|
|
29
|
-
session[:redirect_url] = params[:redirect_url]
|
|
30
|
-
|
|
31
39
|
redirect_to map_path
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
# Mapping page. Load headers and samples, display the form.
|
|
35
43
|
def map
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
raise ArgumentError, "Importer cannot be nil" if importer.nil?
|
|
44
|
+
@layout = "Importance::#{Importance.configuration.layout.to_s.camelize}Layout".constantize
|
|
45
|
+
@importer = Importance.configuration.importers[session[:importer].to_sym]
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@full_count = worksheet.count - 1
|
|
47
|
+
if @importer.nil?
|
|
48
|
+
flash[:alert] = t("importance.errors.no_importer")
|
|
49
|
+
redirect_to session[:redirect_url] and return
|
|
50
|
+
end
|
|
45
51
|
|
|
46
|
-
@
|
|
47
|
-
@layout = "Importance::#{Importance.configuration.layout.to_s.camelize}Layout".constantize
|
|
52
|
+
@importer.add_spreadsheet(session[:path])
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
# Import page. Load the file according to the mapping and import it.
|
|
51
56
|
# Mappings param is of the form mappings[excel_column_idx] = target_attribute
|
|
52
57
|
# mappings[0] = "first_name", mappings[1] = "", mappings[2] = "last_name" ...
|
|
53
58
|
def import
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
raise ArgumentError, "Mapping cannot be nil" if mappings.nil?
|
|
59
|
+
@layout = "Importance::#{Importance.configuration.layout.to_s.camelize}Layout".constantize
|
|
60
|
+
@importer = Importance.configuration.importers[session[:importer].to_sym]
|
|
58
61
|
|
|
59
|
-
if importer.
|
|
60
|
-
|
|
62
|
+
if @importer.nil?
|
|
63
|
+
flash[:alert] = t("importance.errors.no_importer")
|
|
64
|
+
render :map and return
|
|
61
65
|
end
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
records_to_import = []
|
|
67
|
+
@importer.add_spreadsheet(session[:path])
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
instance_exec(records_to_import, &importer.perform_callback)
|
|
71
|
-
records_to_import = []
|
|
72
|
-
end
|
|
73
|
-
end
|
|
69
|
+
if params[:mappings].nil?
|
|
70
|
+
flash[:alert] = t("importance.errors.no_mappings")
|
|
71
|
+
render :map and return
|
|
72
|
+
end
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
instance_exec(records_to_import, &importer.perform_callback)
|
|
77
|
-
end
|
|
74
|
+
@mappings = params[:mappings].permit!.to_h.map { |k, v| [ k.to_i, v ] }.to_h
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
redirect_to (session[:redirect_url] || main_app.root_path), notice: "Import completed."
|
|
83
|
-
end
|
|
76
|
+
@importer.importer_attributes.each do |attribute|
|
|
77
|
+
next if !attribute.options[:required]
|
|
78
|
+
next if @mappings.values.include?(attribute.key.to_s)
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
instance_exec(e, &importer.error_callback)
|
|
88
|
-
end
|
|
80
|
+
flash[:alert] = t("importance.errors.missing_mapping", attribute: attribute.labels.first)
|
|
81
|
+
render :map and return
|
|
89
82
|
end
|
|
90
|
-
end
|
|
91
83
|
|
|
92
|
-
|
|
84
|
+
@mappings.each do |column_index, attribute_name|
|
|
85
|
+
next if attribute_name == ""
|
|
86
|
+
next if @mappings.values.count(attribute_name) <= 1
|
|
93
87
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
flash[:alert] = t("importance.errors.duplicate_mapping", attribute: attribute_name)
|
|
89
|
+
render :map and return
|
|
90
|
+
end
|
|
97
91
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def each_processed_row(mappings)
|
|
101
|
-
workbook = Roo::Spreadsheet.open(session[:path], { csv_options: { encoding: "bom|utf-8" } })
|
|
102
|
-
worksheet = workbook.sheet(0)
|
|
103
|
-
worksheet.each_with_index do |row, idx|
|
|
104
|
-
next if idx == 0 # Skip header row
|
|
105
|
-
record = process_row(row, mappings)
|
|
106
|
-
next if record.empty? || record.values.all? { |v| v.nil? || v.to_s.strip.empty? }
|
|
107
|
-
yield record
|
|
92
|
+
if @importer.setup_callback
|
|
93
|
+
instance_exec(&@importer.setup_callback)
|
|
108
94
|
end
|
|
109
|
-
end
|
|
110
95
|
|
|
111
|
-
|
|
112
|
-
# and a mapping of the form {"0"=>"first_name", "1"=>"last_name", "2"=>"", "3"=>"", "4"=>"", "5"=>"email"}
|
|
113
|
-
# into a record of the form { first_name: "Hans", last_name: "Robert", email: "hr@apple.com" }
|
|
114
|
-
def process_row(row, mappings)
|
|
115
|
-
record = {}
|
|
96
|
+
records_to_import = []
|
|
116
97
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
98
|
+
@importer.each_processed_row(session[:path], @mappings) do |record|
|
|
99
|
+
records_to_import << record
|
|
100
|
+
|
|
101
|
+
if @importer.batch && records_to_import.size >= @importer.batch
|
|
102
|
+
instance_exec(records_to_import, &@importer.perform_callback)
|
|
103
|
+
records_to_import = []
|
|
104
|
+
end
|
|
121
105
|
end
|
|
122
106
|
|
|
123
|
-
|
|
124
|
-
|
|
107
|
+
if records_to_import.any?
|
|
108
|
+
instance_exec(records_to_import, &@importer.perform_callback)
|
|
109
|
+
end
|
|
125
110
|
|
|
126
|
-
|
|
127
|
-
|
|
111
|
+
if @importer.teardown_callback
|
|
112
|
+
instance_exec(&@importer.teardown_callback)
|
|
113
|
+
else
|
|
114
|
+
redirect_to (session[:redirect_url] || main_app.root_path), notice: t("importance.success.import_completed")
|
|
115
|
+
end
|
|
128
116
|
end
|
|
129
117
|
end
|
|
130
118
|
end
|
|
@@ -4,32 +4,32 @@
|
|
|
4
4
|
<table class="importance-table <%= @layout.table_class %>">
|
|
5
5
|
<thead>
|
|
6
6
|
<tr>
|
|
7
|
-
<% @file_headers.each_with_index do |file_header, file_header_idx| %>
|
|
7
|
+
<% @importer.file_headers.each_with_index do |file_header, file_header_idx| %>
|
|
8
8
|
<th>
|
|
9
9
|
<%= t('importance.use_column_as') %>
|
|
10
10
|
<%
|
|
11
|
-
attribute_mappings = Importance::Header.match_attributes_to_headers(@importer_attributes, @file_headers)
|
|
12
|
-
default_value = Importance::Header.default_value_for_header(file_header, attribute_mappings)
|
|
11
|
+
attribute_mappings = Importance::Header.match_attributes_to_headers(@importer.importer_attributes, @importer.file_headers)
|
|
12
|
+
default_value = (@mappings && @mappings[file_header_idx]) || Importance::Header.default_value_for_header(file_header, attribute_mappings)
|
|
13
13
|
%>
|
|
14
14
|
<%= form.select "mappings[#{file_header_idx}]",
|
|
15
15
|
options_for_select(
|
|
16
16
|
[[t('importance.ignore'), ""]] +
|
|
17
|
-
@importer_attributes.map { |attr| [attr.labels.first, attr.key] },
|
|
17
|
+
@importer.importer_attributes.map { |attr| [attr.labels.first, attr.key] },
|
|
18
18
|
default_value
|
|
19
19
|
), {}, class: @layout.select_class %>
|
|
20
20
|
</th>
|
|
21
21
|
<% end %>
|
|
22
22
|
</tr>
|
|
23
23
|
<tr>
|
|
24
|
-
<% @file_headers.each do |file_header| %>
|
|
24
|
+
<% @importer.file_headers.each do |file_header| %>
|
|
25
25
|
<th><%= file_header %></th>
|
|
26
26
|
<% end %>
|
|
27
27
|
</tr>
|
|
28
28
|
</thead>
|
|
29
29
|
<tbody>
|
|
30
|
-
<% @samples.each do |sample| %>
|
|
30
|
+
<% @importer.samples.each do |sample| %>
|
|
31
31
|
<tr>
|
|
32
|
-
<% @file_headers.each_with_index do |file_header, file_header_idx| %>
|
|
32
|
+
<% @importer.file_headers.each_with_index do |file_header, file_header_idx| %>
|
|
33
33
|
<td><%= sample[file_header_idx] %></td>
|
|
34
34
|
<% end %>
|
|
35
35
|
</tr>
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
</table>
|
|
39
39
|
</div>
|
|
40
40
|
<p>
|
|
41
|
-
<%= t('importance.import_description', count: @samples.count, full_count: @full_count) %>
|
|
41
|
+
<%= t('importance.import_description', count: @importer.samples.count, full_count: @importer.full_count) %>
|
|
42
42
|
</p>
|
|
43
43
|
<% end %>
|
|
44
44
|
|
data/config/locales/de.yml
CHANGED
|
@@ -3,4 +3,14 @@ de:
|
|
|
3
3
|
use_column_as: Spalte verwenden als
|
|
4
4
|
ignore: Ignorieren
|
|
5
5
|
import: Importieren
|
|
6
|
-
import_description:
|
|
6
|
+
import_description: Es werden %{count} von %{full_count} Beispieldatensätzen angezeigt.
|
|
7
|
+
errors:
|
|
8
|
+
no_importer: Kein Importer ausgewählt.
|
|
9
|
+
no_file: Keine Datei hochgeladen.
|
|
10
|
+
invalid_file_type: Ungültiger Dateityp. Erlaubt sind %{types}.
|
|
11
|
+
no_mappings: Keine Zuordnungen angegeben.
|
|
12
|
+
duplicate_mapping: Attribut %{attribute} wird mehrfach verwendet.
|
|
13
|
+
import_failed: Import fehlgeschlagen. %{error}.
|
|
14
|
+
missing_mapping: Fehlende Zuordnung für erforderliches Attribut %{attribute}.
|
|
15
|
+
success:
|
|
16
|
+
import_completed: Import abgeschlossen.
|
data/config/locales/en.yml
CHANGED
|
@@ -3,4 +3,14 @@ en:
|
|
|
3
3
|
use_column_as: Use column as
|
|
4
4
|
ignore: Ignore
|
|
5
5
|
import: Import
|
|
6
|
-
import_description:
|
|
6
|
+
import_description: Showing %{count} of %{full_count} sample records.
|
|
7
|
+
errors:
|
|
8
|
+
no_importer: No importer selected.
|
|
9
|
+
no_file: No file uploaded.
|
|
10
|
+
invalid_file_type: Invalid file type. Allowed types are %{types}.
|
|
11
|
+
no_mappings: No mappings provided.
|
|
12
|
+
duplicate_mapping: Attribute %{attribute} is used multiple times.
|
|
13
|
+
import_failed: Import failed. %{error}.
|
|
14
|
+
missing_mapping: Missing mapping for required attribute %{attribute}.
|
|
15
|
+
success:
|
|
16
|
+
import_completed: Import completed.
|
data/config/locales/fr.yml
CHANGED
|
@@ -3,4 +3,14 @@ fr:
|
|
|
3
3
|
use_column_as: Utiliser la colonne comme
|
|
4
4
|
ignore: Ignorer
|
|
5
5
|
import: Importer
|
|
6
|
-
import_description:
|
|
6
|
+
import_description: Affichage de %{count} sur %{full_count} exemples d'enregistrements.
|
|
7
|
+
errors:
|
|
8
|
+
no_importer: Aucun importateur sélectionné.
|
|
9
|
+
no_file: Aucune fichier téléchargé.
|
|
10
|
+
invalid_file_type: Type de fichier invalide. Les types autorisés sont %{types}.
|
|
11
|
+
no_mappings: Aucune correspondance fournie.
|
|
12
|
+
duplicate_mapping: L'attribut %{attribute} est utilisé plusieurs fois.
|
|
13
|
+
import_failed: Import échoué. %{error}.
|
|
14
|
+
missing_mapping: Correspondance manquante pour l'attribut requis %{attribute}.
|
|
15
|
+
success:
|
|
16
|
+
import_completed: Import terminé.
|
data/config/locales/it.yml
CHANGED
|
@@ -3,4 +3,14 @@ it:
|
|
|
3
3
|
use_column_as: Utilizzare come
|
|
4
4
|
ignore: Ignora
|
|
5
5
|
import: Importare
|
|
6
|
-
import_description:
|
|
6
|
+
import_description: Mostrati %{count} di %{full_count} record di esempio.
|
|
7
|
+
errors:
|
|
8
|
+
no_importer: Nessun importatore selezionato.
|
|
9
|
+
no_file: Nessun file caricato.
|
|
10
|
+
invalid_file_type: Tipo di file non valido. I tipi consentiti sono %{types}.
|
|
11
|
+
no_mappings: Nessuna mappatura fornita.
|
|
12
|
+
duplicate_mapping: L'attributo %{attribute} è utilizzato più volte.
|
|
13
|
+
import_failed: Import fallito. %{error}.
|
|
14
|
+
missing_mapping: Mappatura mancante per l'attributo obbligatorio %{attribute}.
|
|
15
|
+
success:
|
|
16
|
+
import_completed: Import completato.
|
|
@@ -29,11 +29,12 @@ module Importance
|
|
|
29
29
|
@teardown_callback = nil
|
|
30
30
|
@error_callback = nil
|
|
31
31
|
@batch = false
|
|
32
|
+
@worksheet = nil
|
|
32
33
|
instance_eval(&block) if block_given?
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
def attribute(key, labels)
|
|
36
|
-
@attributes << OpenStruct.new(key: key, labels: labels)
|
|
36
|
+
def attribute(key, labels, options = {})
|
|
37
|
+
@attributes << OpenStruct.new(key: key, labels: labels, options: options)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def batch_size(size)
|
|
@@ -55,6 +56,49 @@ module Importance
|
|
|
55
56
|
def error(&block)
|
|
56
57
|
@error_callback = block
|
|
57
58
|
end
|
|
59
|
+
|
|
60
|
+
def add_spreadsheet(path)
|
|
61
|
+
workbook = Roo::Spreadsheet.open(path, { csv_options: { encoding: "bom|utf-8" } })
|
|
62
|
+
@worksheet = workbook.sheet(0)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def file_headers
|
|
66
|
+
@worksheet.row(1)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def samples
|
|
70
|
+
@worksheet.parse[1..5]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def full_count
|
|
74
|
+
@worksheet.count - 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Yields each processed row (a hash of attribute => value) to the given block.
|
|
78
|
+
# Skips empty rows (all values nil or empty).
|
|
79
|
+
def each_processed_row(path, mappings)
|
|
80
|
+
@worksheet.each_with_index do |row, idx|
|
|
81
|
+
next if idx == 0 # Skip header row
|
|
82
|
+
record = process_row(row, mappings)
|
|
83
|
+
next if record.empty? || record.values.all? { |v| v.nil? || v.to_s.strip.empty? }
|
|
84
|
+
yield record
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Turn a row of the form ["Hans", "Robert", 1970, "male", "Apple Inc.", "hr@apple.com"]
|
|
89
|
+
# and a mapping of the form {"0"=>"first_name", "1"=>"last_name", "2"=>"", "3"=>"", "4"=>"", "5"=>"email"}
|
|
90
|
+
# into a record of the form { first_name: "Hans", last_name: "Robert", email: "hr@apple.com" }
|
|
91
|
+
def process_row(row, mappings)
|
|
92
|
+
record = {}
|
|
93
|
+
|
|
94
|
+
mappings.each do |column_index, attribute_name|
|
|
95
|
+
next if attribute_name.nil? || attribute_name == ""
|
|
96
|
+
value = row[column_index.to_i]
|
|
97
|
+
record[attribute_name.to_sym] = value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
record
|
|
101
|
+
end
|
|
58
102
|
end
|
|
59
103
|
|
|
60
104
|
def self.configure
|
data/lib/importance/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: importance
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lukas_Skywalker
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|