active_admin_import 2.1.2 → 3.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +4 -0
- data/.travis.yml +9 -0
- data/Gemfile +16 -0
- data/README.md +73 -173
- data/Rakefile +7 -2
- data/active_admin_import.gemspec +5 -3
- data/app/views/admin/import.html.erb +1 -1
- data/config/locales/en.yml +11 -4
- data/config/locales/es.yml +18 -0
- data/config/locales/it.yml +3 -1
- data/config/locales/zh-CN.yml +24 -0
- data/lib/active_admin_import/dsl.rb +39 -31
- data/lib/active_admin_import/import_result.rb +39 -0
- data/lib/active_admin_import/importer.rb +91 -44
- data/lib/active_admin_import/model.rb +80 -29
- data/lib/active_admin_import/options.rb +44 -0
- data/lib/active_admin_import/version.rb +1 -1
- data/lib/active_admin_import.rb +3 -0
- data/spec/fixtures/files/author.csv +2 -0
- data/spec/fixtures/files/author_broken_header.csv +2 -0
- data/spec/fixtures/files/author_invalid.csv +2 -0
- data/spec/fixtures/files/authors.csv +3 -0
- data/spec/fixtures/files/authors_bom.csv +3 -0
- data/spec/fixtures/files/authors_invalid_db.csv +3 -0
- data/spec/fixtures/files/authors_invalid_model.csv +3 -0
- data/spec/fixtures/files/authors_no_headers.csv +2 -0
- data/spec/fixtures/files/authors_win1251_win_endline.csv +3 -0
- data/spec/fixtures/files/authors_with_ids.csv +3 -0
- data/spec/fixtures/files/authors_with_semicolons.csv +3 -0
- data/spec/fixtures/files/empty.csv +0 -0
- data/spec/fixtures/files/only_headers.csv +1 -0
- data/spec/fixtures/files/posts.csv +4 -0
- data/spec/fixtures/files/posts_for_author.csv +3 -0
- data/spec/fixtures/files/posts_for_author_no_headers.csv +2 -0
- data/spec/import_result_spec.rb +32 -0
- data/spec/import_spec.rb +432 -0
- data/spec/model_spec.rb +5 -0
- data/spec/spec_helper.rb +72 -0
- data/spec/support/active_model_lint.rb +14 -0
- data/spec/support/admin.rb +20 -0
- data/spec/support/rails_template.rb +29 -0
- data/tasks/test.rake +6 -0
- metadata +80 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46875662660bbc61d8dce4fa218234229d0c94d7
|
4
|
+
data.tar.gz: ba7347ef7348492d137e459e52a3430d5607f051
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 390733a4c6297317e6ab84c0528d05a65e93b422c1ac84961cd38f302a3f859d5303852a937dbf88762e599a8b5d4d845f8597cdcfc50a9ed3f43e1125d86192
|
7
|
+
data.tar.gz: 459e49db7a02bdca7c5fa53d9cad1b7b8e2937ff2e223b479ee7551a8cd9b7e6993f5b4968c1a5c86e33f206467075afd9d7c7bb7c868906f4af050cae2b2754
|
data/.hound.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -2,3 +2,19 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in active_admin_importable.gemspec
|
4
4
|
gemspec
|
5
|
+
group :test do
|
6
|
+
gem 'rails', '4.1.9'
|
7
|
+
gem 'rspec-rails'
|
8
|
+
gem 'activeadmin', github: 'activeadmin' , ref: '54bede0558a99ab759f98f9b24e1b0144063a81e'
|
9
|
+
|
10
|
+
gem 'coveralls', require: false # Test coverage website. Go to https://coveralls.io
|
11
|
+
|
12
|
+
gem 'devise'
|
13
|
+
gem 'sass-rails'
|
14
|
+
gem 'sqlite3'
|
15
|
+
gem 'launchy'
|
16
|
+
gem 'database_cleaner'
|
17
|
+
gem 'capybara'
|
18
|
+
gem 'selenium-webdriver'
|
19
|
+
gem 'poltergeist'
|
20
|
+
end
|
data/README.md
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
# ActiveAdminImport
|
2
|
-
The most fastest and efficient CSV import for Active Admin
|
3
|
-
with support of validations
|
2
|
+
[The most fastest and efficient CSV import for Active Admin
|
3
|
+
with support of validations, bulk inserts and encodings handling](http://activeadmin-plugins.github.io/active_admin_import/)
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
[![Build Status](https://img.shields.io/travis/activeadmin-plugins/active_admin_import.svg)](https://travis-ci.org/activeadmin-pluginsl/active_admin_import)
|
8
|
+
[![Dependency Status](http://img.shields.io/gemnasium/activeadmin-plugins/active_admin_import.svg)](https://gemnasium.com/activeadmin-plugins/active_admin_import)
|
9
|
+
[![Coverage Status](https://coveralls.io/repos/activeadmin-plugins/active_admin_import/badge.svg)](https://coveralls.io/r/activeadmin-plugins/active_admin_import)
|
4
10
|
|
11
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/activeadmin-plugins/active_admin_import.svg)](https://codeclimate.com/github/activeadmin-plugins/active_admin_import)
|
12
|
+
[![Gem Version](http://img.shields.io/gem/v/active_admin_import.svg)](https://rubygems.org/gems/active_admin_import)
|
13
|
+
[![License](http://img.shields.io/:license-mit-blue.svg)](http://Fivell.mit-license.org)
|
5
14
|
|
6
|
-
|
15
|
+
master can be used with AA 1.0.0 and Rails >= 4.1
|
7
16
|
|
8
17
|
|
9
18
|
#Installation
|
@@ -11,203 +20,94 @@ Should work with both AA 0.6 and 1.0.0
|
|
11
20
|
Add this line to your application's Gemfile:
|
12
21
|
|
13
22
|
```ruby
|
14
|
-
gem "active_admin_import" , '2.1.
|
15
|
-
```
|
16
|
-
|
17
|
-
And then execute:
|
18
|
-
|
19
|
-
$ bundle
|
20
|
-
|
21
|
-
|
23
|
+
gem "active_admin_import" , '2.1.2'
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
"Because plain-vanilla, out-of-the-box ActiveRecord doesn’t provide support for inserting large amounts of data efficiently"
|
26
|
-
|
27
|
-
cool features of activerecord-import
|
28
|
-
|
29
|
-
activerecord-import can perform validations (fast)
|
30
|
-
activerecord-import can perform on duplicate key updates (requires mysql)
|
31
|
-
|
32
|
-
|
33
|
-
So active_admin_import features
|
25
|
+
```
|
26
|
+
or
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
CSV options
|
38
|
-
Ability to prepend CSV headers automatically
|
39
|
-
Bulk import (activerecord-import)
|
40
|
-
Ability to customize template
|
41
|
-
Callbacks support
|
42
|
-
Zip files import
|
43
|
-
and more ....
|
28
|
+
```ruby
|
29
|
+
gem "active_admin_import" , github: "activeadmin-plugins/active_admin_import"
|
44
30
|
|
45
|
-
|
31
|
+
```
|
46
32
|
|
47
|
-
|
48
|
-
# +csv_options+:: hash with column separator, row separator, etc
|
49
|
-
# +validate+:: true|false, means perfoem validations or not
|
50
|
-
# +batch_size+:: integer value of max record count inserted by 1 query/transaction
|
51
|
-
# +before_import+:: proc for before import action, hook called with importer object
|
52
|
-
# +after_import+:: proc for after import action, hook called with importer object
|
53
|
-
# +before_batch_import+:: proc for before each batch action, called with importer object
|
54
|
-
# +after_batch_import+:: proc for after each batch action, called with importer object
|
55
|
-
# +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
56
|
-
# +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
57
|
-
# +ignore+:: true|false, tells activerecord-import toto use MySQL's INSERT IGNORE ability
|
58
|
-
# +params_keys+:: params values available in callbacks
|
59
|
-
# +template+:: custom template rendering
|
60
|
-
# +template_object+:: object passing to view
|
61
|
-
# +locals+:: local variables for template
|
62
|
-
# +resource_class+:: resource class name
|
63
|
-
# +resource_label+:: resource label value
|
64
|
-
# +headers_rewrites+:: hash with key (csv header) - value (db column name) rows mapping
|
33
|
+
And then execute:
|
65
34
|
|
35
|
+
$ bundle
|
66
36
|
|
67
37
|
|
68
|
-
|
38
|
+
# active_admin_import features
|
39
|
+
<ol>
|
40
|
+
<li> Replacements/Updates support</li>
|
41
|
+
<li> Encoding handling</li>
|
42
|
+
<li> CSV options</li>
|
43
|
+
<li> Ability to describe/change CSV headers</li>
|
44
|
+
<li> Bulk import (activerecord-import)</li>
|
45
|
+
<li> Callbacks</li>
|
46
|
+
<li> Zip files</li>
|
47
|
+
<li> and more...</li>
|
48
|
+
</ol>
|
69
49
|
|
70
|
-
|
71
|
-
back: {action: :import},
|
72
|
-
csv_options: {},
|
73
|
-
template: "admin/import",
|
74
|
-
fetch_extra_options_from_params: [],
|
75
|
-
resource_class: config.resource_class,
|
76
|
-
resource_label: config.resource_label,
|
77
|
-
plural_resource_label: config.plural_resource_label,
|
78
|
-
```
|
50
|
+
|
79
51
|
|
80
|
-
|
52
|
+
#### Basic usage
|
81
53
|
|
82
|
-
```ruby
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
:before_import => proc{ Post.delete_all},
|
87
|
-
:batch_size => 1000
|
88
|
-
|
89
|
-
|
90
|
-
end
|
54
|
+
```ruby
|
55
|
+
ActiveAdmin.register Post
|
56
|
+
active_admin_import options
|
57
|
+
end
|
91
58
|
```
|
92
59
|
|
93
60
|
|
94
|
-
|
95
|
-
|
96
|
-
|
61
|
+
#### Options
|
62
|
+
Tool | Description
|
63
|
+
--------------------- | -----------
|
64
|
+
:back |resource action to redirect after processing
|
65
|
+
:csv_options |hash with column separator, row separator, etc
|
66
|
+
:validate |bool means perform validations or not
|
67
|
+
:batch_size |integer value of max record count inserted by 1 query/transaction
|
68
|
+
:batch_transaction |bool (false by default), if transaction is used when batch importing and works when :validate is set to true
|
69
|
+
:before_import |proc for before import action, hook called with importer object
|
70
|
+
:after_import |proc for after import action, hook called with importer object
|
71
|
+
:before_batch_import |proc for before each batch action, called with importer object
|
72
|
+
:after_batch_import |proc for after each batch action, called with importer object
|
73
|
+
:on_duplicate_key_update|an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
74
|
+
:timestamps |bool, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
75
|
+
:ignore |bool, tells activerecord-import toto use MySQL's INSERT IGNORE ability
|
76
|
+
:template |custom template rendering
|
77
|
+
:template_object |object passing to view
|
78
|
+
:resource_class |resource class name
|
79
|
+
:resource_label |resource label value
|
80
|
+
:plural_resource_label |pluralized resource label value (default config.plural_resource_label)
|
81
|
+
:headers_rewrites |hash with key (csv header) - value (db column name) rows mapping
|
97
82
|
|
98
|
-
```ruby
|
99
|
-
ActiveAdmin.register Post do
|
100
|
-
active_admin_import :validate => false,
|
101
|
-
:csv_options => {:col_sep => ";" },
|
102
|
-
:resource_class => ImportedPost , # we import data into another resource
|
103
|
-
:before_import => proc{ ImportedPost.delete_all },
|
104
|
-
:after_import => proc{
|
105
|
-
Post.transaction do
|
106
|
-
Post.delete_all
|
107
|
-
Post.connection.execute("INSERT INTO posts (SELECT * FROM import_posts)")
|
108
|
-
end
|
109
|
-
},
|
110
|
-
:back => proc { config.namespace.resource_for(Post).route_collection_path } # redirect to post index
|
111
|
-
end
|
112
|
-
```
|
113
83
|
|
114
84
|
|
115
|
-
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
ActiveAdmin.register Post do
|
119
|
-
active_admin_import :validate => true,
|
120
|
-
:template_object => ActiveAdminImport::Model.new(
|
121
|
-
:hint => "file will be imported with such header format: 'body','title','author'",
|
122
|
-
:csv_headers => ["body","title","author"]
|
123
|
-
)
|
124
|
-
end
|
125
|
-
```
|
126
|
-
|
127
|
-
Example4 Importing without forcing to UTF-8 and disallow archives
|
85
|
+
#### Wiki
|
128
86
|
|
87
|
+
[Check various examples](https://github.com/activeadmin-plugins/active_admin_import/wiki)
|
129
88
|
|
130
|
-
|
131
|
-
ActiveAdmin.register Post do
|
132
|
-
active_admin_import :validate => true,
|
133
|
-
:template_object => ActiveAdminImport::Model.new(
|
134
|
-
:hint => "file will be encoded to ISO-8859-1",
|
135
|
-
:force_encoding => "ISO-8859-1",
|
136
|
-
:allow_archive => false
|
137
|
-
)
|
138
|
-
end
|
139
|
-
```
|
89
|
+
## Dependencies
|
140
90
|
|
91
|
+
Tool | Description
|
92
|
+
--------------------- | -----------
|
93
|
+
[rchardet] | Character encoding auto-detection in Ruby. As smart as your browser. Open source.
|
94
|
+
[activerecord-import] | Powerful library for bulk inserting data using ActiveRecord.
|
141
95
|
|
142
|
-
|
96
|
+
[rchardet]: https://github.com/jmhodges/rchardet
|
97
|
+
[activerecord-import]: https://github.com/zdennis/activerecord-import
|
143
98
|
|
144
99
|
|
145
|
-
|
146
|
-
ActiveAdmin.register Post do
|
147
|
-
active_admin_import :validate => true,
|
148
|
-
:before_batch_import => proc { |import|
|
149
|
-
import.file #current file used
|
150
|
-
import.resource #ActiveRecord class to import to
|
151
|
-
import.options # options
|
152
|
-
import.result # result before bulk iteration
|
153
|
-
import.headers # CSV headers
|
154
|
-
import.csv_lines #lines to import
|
155
|
-
import.model #template_object instance
|
156
|
-
},
|
157
|
-
:after_batch_import => proc{ |import|
|
158
|
-
#the same
|
159
|
-
}
|
160
|
-
end
|
161
|
-
```
|
162
|
-
|
163
|
-
Example6 dynamic CSV options, template overriding
|
164
|
-
|
165
|
-
1) put overrided template to app/views/import.html.erb
|
166
|
-
|
167
|
-
```erb
|
168
|
-
|
169
|
-
<p>
|
170
|
-
<%= raw(@active_admin_import_model.hint) %>
|
171
|
-
</p>
|
172
|
-
<%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %>
|
173
|
-
<%= f.inputs do %>
|
174
|
-
<%= f.input :file, as: :file %>
|
175
|
-
<% end %>
|
176
|
-
<%= f.inputs "CSV options", :for => [:csv_options, OpenStruct.new(@active_admin_import_model.csv_options)] do |csv| %>
|
177
|
-
<% csv.with_options :input_html => {:style => 'width:40px;'} do |opts| %>
|
178
|
-
<%= opts.input :col_sep %>
|
179
|
-
<%= opts.input :row_sep %>
|
180
|
-
<%= opts.input :quote_char %>
|
181
|
-
<% end %>
|
182
|
-
<% end %>
|
183
|
-
|
184
|
-
<%= f.actions do %>
|
185
|
-
<%= f.action :submit, label: t("active_admin_import.import_btn"), button_html: {disable_with: t("active_admin_import.import_btn_disabled")} %>
|
186
|
-
<% end %>
|
187
|
-
<% end %>
|
188
|
-
|
189
|
-
```
|
100
|
+
## Contributing
|
190
101
|
|
191
|
-
|
102
|
+
1. Fork it
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create new Pull Request
|
192
107
|
|
193
|
-
```ruby
|
194
|
-
ActiveAdmin.register Post do
|
195
|
-
active_admin_import :validate => false,
|
196
|
-
:template => 'import' ,
|
197
|
-
:template_object => ActiveAdminImport::Model.new(
|
198
|
-
:hint => "specify CSV options"
|
199
|
-
:csv_options => {:col_sep => ";", :row_sep => nil, :quote_char => nil}
|
200
|
-
)
|
201
|
-
end
|
202
|
-
```
|
203
108
|
|
204
|
-
#Links
|
205
|
-
https://github.com/gregbell/active_admin
|
206
109
|
|
207
|
-
https://github.com/zdennis/activerecord-import
|
208
110
|
|
209
|
-
#Source Doc
|
210
|
-
http://rubydoc.info/gems/active_admin_import/2.1.1/
|
211
111
|
|
212
112
|
|
213
113
|
|
data/Rakefile
CHANGED
data/active_admin_import.gemspec
CHANGED
@@ -15,9 +15,11 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = ActiveAdminImport::VERSION
|
17
17
|
|
18
|
-
gem.add_runtime_dependency 'activerecord-import', '~> 0.4', '>= 0.4.1'
|
19
|
-
gem.add_runtime_dependency 'activeadmin', '>= 0.6.0'
|
20
|
-
gem.add_runtime_dependency 'rubyzip', '~> 1.0', '>= 1.0.0'
|
21
18
|
|
19
|
+
gem.add_runtime_dependency 'activerecord-import', '~> 0.8.0'
|
20
|
+
gem.add_runtime_dependency 'rchardet', '~> 1.5'
|
21
|
+
|
22
|
+
gem.add_runtime_dependency 'rubyzip', '~> 1.0', '>= 1.0.0'
|
23
|
+
gem.add_dependency "rails", ">= 4.0"
|
22
24
|
|
23
25
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<%= raw(@active_admin_import_model.hint) %>
|
3
3
|
</p>
|
4
4
|
<%= semantic_form_for @active_admin_import_model, url: {action: :do_import}, html: {multipart: true} do |f| %>
|
5
|
-
<%= f.inputs do %>
|
5
|
+
<%= f.inputs name: t("active_admin_import.details") do %>
|
6
6
|
<%= f.input :file, as: :file %>
|
7
7
|
<% end %>
|
8
8
|
<%= f.actions do %>
|
data/config/locales/en.yml
CHANGED
@@ -2,15 +2,22 @@ en:
|
|
2
2
|
active_admin:
|
3
3
|
import: "Import"
|
4
4
|
active_admin_import:
|
5
|
+
file: 'File'
|
6
|
+
file_error: "Error: %{message}"
|
5
7
|
file_format_error: "You can import only valid csv file"
|
6
8
|
file_empty_error: "You can't import empty file"
|
7
9
|
no_file_error: "Please, select file to import"
|
10
|
+
details: "Please, select file to import"
|
8
11
|
imported:
|
9
12
|
one: "Successfully imported 1 %{model}"
|
10
13
|
other: "Successfully imported %{count} %{plural_model}"
|
11
14
|
failed:
|
12
|
-
one: "Failed to import 1 %{model}"
|
13
|
-
other: "Failed to import %{count} %{plural_model}"
|
14
|
-
import_model: "Import %{
|
15
|
+
one: "Failed to import 1 %{model}: %{message}"
|
16
|
+
other: "Failed to import %{count} %{plural_model}: %{message}"
|
17
|
+
import_model: "Import %{plural_model}"
|
15
18
|
import_btn: "Import"
|
16
|
-
import_btn_disabled: "Wait..."
|
19
|
+
import_btn_disabled: "Wait..."
|
20
|
+
csv_options: "CSV options"
|
21
|
+
col_sep: 'Col sep'
|
22
|
+
row_sep: 'Row sep'
|
23
|
+
quote_char: 'Quote char'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
es:
|
2
|
+
active_admin:
|
3
|
+
import: "Importar"
|
4
|
+
active_admin_import:
|
5
|
+
file_error: "Error: %{message}"
|
6
|
+
file_format_error: "Sólo se puede importar un archivo CSV válido"
|
7
|
+
file_empty_error: "No puedes importar un archivo vacío"
|
8
|
+
no_file_error: "Seleccione un archivo para importar"
|
9
|
+
details: "Seleccione un archivo"
|
10
|
+
imported:
|
11
|
+
one: "Se ha importado exitosamente 1 %{model}"
|
12
|
+
other: "Se ha importado exitosamente %{count} %{plural_model}"
|
13
|
+
failed:
|
14
|
+
one: "No se pudo importar 1 %{model}"
|
15
|
+
other: "No se pudo importar %{count} %{plural_model}"
|
16
|
+
import_model: "Importar %{plural_model}"
|
17
|
+
import_btn: "Importar"
|
18
|
+
import_btn_disabled: "Espere..."
|
data/config/locales/it.yml
CHANGED
@@ -2,15 +2,17 @@ it:
|
|
2
2
|
active_admin:
|
3
3
|
import: "Importa"
|
4
4
|
active_admin_import:
|
5
|
+
file_error: "Errore: %{message}"
|
5
6
|
file_format_error: "Puoi importare solo CSV conformi"
|
6
7
|
no_file_error: "Per favore, seleziona un file da importare"
|
7
8
|
file_empty_error: "Non è possibile importare file vuoto"
|
9
|
+
details: "Per favore, seleziona un file da importare"
|
8
10
|
imported:
|
9
11
|
one: "Importato 1 %{model} con successo"
|
10
12
|
other: "Importati %{count} %{plural_model} con successo"
|
11
13
|
failed:
|
12
14
|
one: "Importazione di 1 %{model} fallita"
|
13
15
|
other: "Importazione di %{count} %{plural_model} fallita"
|
14
|
-
import_model: "Importa %{
|
16
|
+
import_model: "Importa %{plural_model}"
|
15
17
|
import_btn: "Importa"
|
16
18
|
import_btn_disabled: "In attesa..."
|
@@ -0,0 +1,24 @@
|
|
1
|
+
zh-CN:
|
2
|
+
active_admin:
|
3
|
+
import: "导入"
|
4
|
+
do_import: "导入"
|
5
|
+
active_admin_import:
|
6
|
+
file: '文件'
|
7
|
+
file_error: "导入错误: %{message}"
|
8
|
+
file_format_error: "请您上传 CSV 格式的文件"
|
9
|
+
file_empty_error: "您上传的文件为空"
|
10
|
+
no_file_error: "请您选择要上传的文件"
|
11
|
+
details: "请您选择要上传的文件"
|
12
|
+
imported:
|
13
|
+
one: "成功导入 1 份%{model}"
|
14
|
+
other: "成功导入 %{count} 份%{plural_model}"
|
15
|
+
failed:
|
16
|
+
one: "未能导入 1 份%{model}: %{message}"
|
17
|
+
other: "未能导入 %{count} 份%{plural_model}: %{message}"
|
18
|
+
import_model: "导入%{plural_model}"
|
19
|
+
import_btn: "导入"
|
20
|
+
import_btn_disabled: "请稍等..."
|
21
|
+
csv_options: "选项"
|
22
|
+
col_sep: '列分隔符'
|
23
|
+
row_sep: '行分隔符'
|
24
|
+
quote_char: '引用字符'
|
@@ -1,18 +1,19 @@
|
|
1
1
|
module ActiveAdminImport
|
2
2
|
module DSL
|
3
|
+
|
4
|
+
|
3
5
|
# Declares import functionality
|
4
6
|
#
|
5
7
|
# Options
|
6
8
|
# +back+:: resource action to redirect after processing
|
7
|
-
# +col_sep+:: column separator used for CSV parsing (deprecated)
|
8
|
-
# +row_sep+:: column separator used for CSV parsing (deprecated)
|
9
9
|
# +csv_options+:: hash to override default CSV options
|
10
|
-
# +validate+:: true|false, means perfoem validations or not
|
11
10
|
# +batch_size+:: integer value of max record count inserted by 1 query/transaction
|
11
|
+
# +batch_transaction+:: bool (false by default), if transaction is used when batch importing and works when :validate is set to true
|
12
12
|
# +before_import+:: proc for before import action, hook called with importer object
|
13
13
|
# +after_import+:: proc for after import action, hook called with importer object
|
14
14
|
# +before_batch_import+:: proc for before each batch action, called with importer object
|
15
15
|
# +after_batch_import+:: proc for after each batch action, called with importer object
|
16
|
+
# +validate+:: true|false, means perform validations or not
|
16
17
|
# +on_duplicate_key_update+:: an Array or Hash, tells activerecord-import to use MySQL's ON DUPLICATE KEY UPDATE ability.
|
17
18
|
# +timestamps+:: true|false, tells activerecord-import to not add timestamps (if false) even if record timestamps is disabled in ActiveRecord::Base
|
18
19
|
# +ignore+:: true|false, tells activerecord-import to use MySQL's INSERT IGNORE ability
|
@@ -20,58 +21,65 @@ module ActiveAdminImport
|
|
20
21
|
# +template_object+:: object passing to view
|
21
22
|
# +resource_class+:: resource class name, override to import to another table (default config.resource_class)
|
22
23
|
# +resource_label+:: resource label value (default config.resource_label)
|
23
|
-
# +plural_resource_label+::
|
24
|
+
# +plural_resource_label+:: pluralized resource label value (default config.plural_resource_label)
|
24
25
|
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
|
27
|
+
|
28
|
+
DEFAULT_RESULT_PROC = ->(result, options) do
|
29
|
+
|
30
|
+
model_name = options[:resource_label].downcase
|
31
|
+
plural_model_name = options[:plural_resource_label].downcase
|
32
|
+
if result.empty?
|
33
|
+
flash[:warning] = I18n.t('active_admin_import.file_empty_error')
|
34
|
+
else
|
35
|
+
if result.has_failed?
|
36
|
+
flash[:error] = I18n.t('active_admin_import.failed', count: result.failed.count, model: model_name, plural_model: plural_model_name, message: result.failed_message(limit: 5))
|
37
|
+
return if options[:batch_transaction]
|
38
|
+
end
|
39
|
+
if result.has_imported?
|
40
|
+
flash[:notice] = I18n.t('active_admin_import.imported', count: result.imported_qty, model: model_name, plural_model: plural_model_name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def active_admin_import(options = {}, &block)
|
47
|
+
options.assert_valid_keys(*Options::VALID_OPTIONS)
|
48
|
+
|
49
|
+
options = Options.options_for(config, options)
|
50
|
+
params_key = ActiveModel::Naming.param_key(options[:template_object])
|
37
51
|
|
38
52
|
collection_action :import, method: :get do
|
39
53
|
authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
|
40
|
-
|
41
|
-
@active_admin_import_model = options[:template_object] || ActiveAdminImport::Model.new
|
54
|
+
@active_admin_import_model = options[:template_object]
|
42
55
|
render template: options[:template]
|
43
56
|
end
|
44
57
|
|
45
58
|
action_item :import, only: :index do
|
46
59
|
if authorized?(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
|
47
|
-
link_to(I18n.t('active_admin_import.import_model',
|
60
|
+
link_to(I18n.t('active_admin_import.import_model', plural_model: options[:plural_resource_label]), action: :import)
|
48
61
|
end
|
49
62
|
end
|
50
63
|
|
51
64
|
collection_action :do_import, method: :post do
|
52
65
|
authorize!(ActiveAdminImport::Auth::IMPORT, active_admin_config.resource_class)
|
53
66
|
|
54
|
-
@active_admin_import_model = options[:template_object]
|
67
|
+
@active_admin_import_model = options[:template_object]
|
55
68
|
@active_admin_import_model.assign_attributes(params[params_key].try(:deep_symbolize_keys) || {})
|
56
69
|
#go back to form
|
57
70
|
return render template: options[:template] unless @active_admin_import_model.valid?
|
58
71
|
@importer = Importer.new(options[:resource_class], @active_admin_import_model, options)
|
59
72
|
begin
|
60
|
-
@importer.import
|
73
|
+
result = @importer.import
|
74
|
+
|
61
75
|
if block_given?
|
62
76
|
instance_eval &block
|
63
77
|
else
|
64
|
-
|
65
|
-
plural_model_name = options[:plural_resource_label].downcase
|
66
|
-
if @importer.result[:imported].to_i > 0
|
67
|
-
flash[:notice] = I18n.t('active_admin_import.imported', count: @importer.result[:imported].to_i, model: model_name, plural_model: plural_model_name)
|
68
|
-
end
|
69
|
-
if @importer.result[:failed].count > 0
|
70
|
-
flash[:error] = I18n.t('active_admin_import.failed', count: @importer.result[:failed].count, model: model_name, plural_model: plural_model_name)
|
71
|
-
end
|
78
|
+
instance_exec result, options, &DEFAULT_RESULT_PROC
|
72
79
|
end
|
73
|
-
rescue ActiveRecord::Import::MissingColumnError, NoMethodError => e
|
74
|
-
|
80
|
+
rescue ActiveRecord::Import::MissingColumnError, NoMethodError, ActiveRecord::StatementInvalid => e
|
81
|
+
Rails.logger.error(I18n.t('active_admin_import.file_error', message: e.message))
|
82
|
+
flash[:error] = I18n.t('active_admin_import.file_error', message: e.message[0..200])
|
75
83
|
end
|
76
84
|
redirect_to options[:back]
|
77
85
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ActiveAdminImport
|
2
|
+
class ImportResult
|
3
|
+
attr_reader :failed, :total
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@failed = []
|
7
|
+
@total = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(result, qty)
|
11
|
+
@failed += result.failed_instances
|
12
|
+
@total += qty
|
13
|
+
end
|
14
|
+
|
15
|
+
def imported_qty
|
16
|
+
total - failed.count
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_imported?
|
20
|
+
imported_qty > 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_failed?
|
24
|
+
@failed.any?
|
25
|
+
end
|
26
|
+
|
27
|
+
def empty?
|
28
|
+
total == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def failed_message(options = {})
|
32
|
+
limit = options.fetch(:limit, failed.count)
|
33
|
+
failed.first(limit).map{|record|
|
34
|
+
errors = record.errors
|
35
|
+
(errors.full_messages.zip errors.keys.map{|k| record.send k}).map{|ms| ms.join(' - ')}.join(', ')
|
36
|
+
}.join(" ; ")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|