rails_admin_import_no_encoding 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rubygems/tasks'
4
+ Gem::Tasks.new
5
+
6
+ # Piggyback off the Rails Admin rake tasks to set up the CI environment
7
+ spec = Gem::Specification.find_by_name 'rails_admin'
8
+ Dir["#{spec.gem_dir}/lib/tasks/*.rake"].each { |rake| load rake }
9
+
10
+ require 'rspec/core/rake_task'
11
+ RSpec::Core::RakeTask.new(:spec)
12
+
13
+ task test: :spec
14
+
15
+ task :default do
16
+ system("bundle exec rake spec")
17
+ end
@@ -0,0 +1,18 @@
1
+ - if @results && @results[:success].any?
2
+ .alert.alert-success.form-horizontal.denser
3
+ %fieldset
4
+ %legend
5
+ %i.icon-chevron-right
6
+ = @results[:success_message]
7
+ %ul.control-group{style: 'display: none'}
8
+ - @results[:success].each do |message|
9
+ %li= message
10
+ - if @results && @results[:error].any?
11
+ .alert.alert-danger.form-horizontal.denser
12
+ %fieldset
13
+ %legend
14
+ %i.icon-chevron-down
15
+ = @results[:error_message]
16
+ %ul.control-group
17
+ - @results[:error].each do |message|
18
+ %li= message
@@ -0,0 +1,10 @@
1
+ - if fields.any?
2
+ .form-group.control-group
3
+ %label.col-sm-2.control-label= t("admin.import.#{section}")
4
+ .col-sm-10.controls
5
+ %ul.list-unstyled
6
+ - fields.each do |field|
7
+ %li
8
+ %label= capitalize_first_letter(field.label)
9
+ %p.help-block= t("admin.import.help.#{section}")
10
+
@@ -0,0 +1,69 @@
1
+ = render "results"
2
+
3
+ = form_tag import_path(@abstract_model), :multipart => true, class: 'form-horizontal denser' do
4
+
5
+ %input{name: "send_data", type: "hidden", value: "true"}/
6
+ %fieldset
7
+ %legend
8
+ %i.icon-chevron-down
9
+ = t('admin.import.legend.fields')
10
+
11
+ = render "section", section: "model_fields", fields: @import_model.model_fields
12
+ = render "section", section: "association_fields", fields: @import_model.association_fields
13
+
14
+ %fieldset
15
+ %legend
16
+ %i.icon-chevron-down
17
+ = t('admin.import.legend.upload')
18
+ .form-group.control-group
19
+ %label.col-sm-2.control-label{for: "file"}= t("admin.import.file")
20
+ .col-sm-10.controls
21
+ = file_field_tag :file, :class => "form-control"
22
+ %p.help-block= t('admin.import.help.file_limit', limit: RailsAdminImport.config.line_item_limit)
23
+ .form-group.control-group
24
+ %label.col-sm-2.control-label{for: "encoding"}= t("admin.import.encoding")
25
+ .col-sm-10.controls
26
+ = select_tag 'encoding',
27
+ options_for_select(Encoding.name_list.sort),
28
+ include_blank: true, data: { enumeration: true }
29
+ %p.help-block= t('admin.import.help.encoding', name: 'UTF-8')
30
+ .form-group.control-group
31
+ %label.col-sm-2.control-label= t("admin.import.update_if_exists")
32
+ .col-sm-10.controls
33
+ = check_box_tag :update_if_exists, '1', RailsAdminImport.config.update_if_exists, :class => "form-control"
34
+ %p.help-block= t('admin.import.help.update_if_exists')
35
+ .form-group.control-group
36
+ %label.col-sm-2.control-label{for: "update_lookup"}= t("admin.import.update_lookup")
37
+ .col-sm-10.controls
38
+ = select_tag 'update_lookup',
39
+ options_for_select(@import_model.update_lookup_field_names,
40
+ Array.wrap(@import_model.config.mapping_key).map(&:to_s)),
41
+ multiple: true,
42
+ data: { enumeration: true }
43
+
44
+ - unless @import_model.association_fields.empty?
45
+ %fieldset
46
+ %legend
47
+ %i.icon-chevron-down
48
+ = t('admin.import.legend.mapping')
49
+
50
+ - @import_model.association_fields.each do |field|
51
+ .form-group.control-group
52
+ %label.col-sm-2.control-label
53
+ = capitalize_first_letter(field.label)
54
+ = t("admin.import.mapping")
55
+ .col-sm-10.controls
56
+ = select_tag "associations[#{field.name}]",
57
+ options_for_select(@import_model.associated_model_fields(field),
58
+ Array.wrap(@import_model.associated_config(field).mapping_key).first.to_s),
59
+ data: { enumeration: true }
60
+
61
+ %br
62
+ .form-actions
63
+ %input{type: :hidden, name: 'return_to', value: (request.params[:return_to].presence || request.referer)}
64
+ %button.btn.btn-primary{type: "submit", name: "commit", data: {disable_with: "Uploading..."} }
65
+ %i.icon-white.icon-ok
66
+ = t("admin.form.save")
67
+ %button.btn{type: "submit", name: "_continue"}
68
+ %i.icon-remove
69
+ = t("admin.form.cancel")
@@ -0,0 +1,3 @@
1
+ See the [community contributed translation section of the README](https://github.com/stephskardal/rails_admin_import#community-contributed-translations) for more languages.
2
+
3
+ To contribute a new translation, please put it in a gist and submit a pull request to link your translation in the README.
@@ -0,0 +1,50 @@
1
+
2
+ en:
3
+ admin:
4
+ actions:
5
+ import:
6
+ title: "Import"
7
+ menu: "Import"
8
+ breadcrumb: "Import"
9
+ link: "Import"
10
+ bulk_link: "Import"
11
+ done: "Imported"
12
+ import:
13
+ model_fields: "Model fields"
14
+ association_fields: "Association fields"
15
+
16
+ file: "Data file"
17
+ missing_file: "You must select a file"
18
+ format: "File format"
19
+ invalid_format: "Invalid import format."
20
+ missing_update_lookup: "Your file must contain a column for the 'Update lookup field' you selected."
21
+ invalid_json: "The JSON data should be an array of records or an object with a key '%{root_key}' set to an array of records"
22
+ update_if_exists: "Update if exists"
23
+ update_lookup: "Update lookup field(s)"
24
+ mapping: "mapping"
25
+ encoding: "Encoding"
26
+ legend:
27
+ fields: "Fields to import"
28
+ upload: "Upload file"
29
+ mapping: "Related fields mapping"
30
+ import_success:
31
+ create: "Created %{name}"
32
+ update: "Updated %{name}"
33
+ import_error:
34
+ create: "Failed to create %{name}: %{error}"
35
+ update: "Failed to update %{name}: %{error}"
36
+ general: "Error during import: %{error}"
37
+ line_item_limit: "Please limit upload file to %{limit} line items."
38
+ old_import_hook: >
39
+ The import hook %{model}.%{method} should take only 1 argument.
40
+ Data may not imported correctly.
41
+ See Upgrading section readme in Rails Admin Import.
42
+ association_not_found: "Association not found. %{error}"
43
+ help:
44
+ model_fields: "The fields above may be included in the import file."
45
+ association_fields: >
46
+ These fields map to other tables in the database, lookup via attribute selected below.
47
+ For "many" associations, you may include multiple columns with the same header in the CSV file.
48
+ update_if_exists: "Update records found with the lookup field below instead of creating new records"
49
+ file_limit: "Please limit upload file to %{limit} line items."
50
+ encoding: "Choose file encoding. Leave empty to auto-detect. Ignored for JSON."
@@ -0,0 +1,46 @@
1
+ require "rails_admin/config/actions"
2
+
3
+ module RailsAdmin
4
+ module Config
5
+ module Actions
6
+ class Import < Base
7
+ RailsAdmin::Config::Actions.register(self)
8
+
9
+ register_instance_option :collection do
10
+ true
11
+ end
12
+
13
+ register_instance_option :http_methods do
14
+ [:get, :post]
15
+ end
16
+
17
+ register_instance_option :controller do
18
+ proc do
19
+ @import_model = RailsAdminImport::ImportModel.new(@abstract_model)
20
+
21
+ if request.post?
22
+ format = RailsAdminImport::Formats.from_file(params[:file])
23
+ record_importer = RailsAdminImport::Formats.for(
24
+ format, @import_model, params)
25
+
26
+ if record_importer.valid?
27
+ importer = RailsAdminImport::Importer.new(
28
+ @import_model, params)
29
+
30
+ @results = importer.import(record_importer.each)
31
+ else
32
+ flash[:error] = record_importer.error
33
+ end
34
+ end
35
+
36
+ render action: @action.template_name
37
+ end
38
+ end
39
+
40
+ register_instance_option :link_icon do
41
+ "icon-folder-open"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ module RailsAdminImport
2
+ module Config
3
+ class LegacyModel
4
+ attr_reader :model_name
5
+ def initialize(model_name)
6
+ @model_name = model_name
7
+ end
8
+
9
+ def label(_value)
10
+ # Ignored now
11
+ # RailsAdmin object_label_method will be used
12
+ end
13
+
14
+ def mapping_key(value)
15
+ config = RailsAdmin.config(model_name)
16
+ config.mapping_key(value)
17
+ end
18
+
19
+ def excluded_fields(values)
20
+ config = RailsAdmin.config(model_name)
21
+
22
+ # Call appropriate Rails Admin field list methods
23
+ config.import do
24
+ include_all_fields
25
+ exclude_fields *values
26
+ end
27
+ end
28
+
29
+ def extra_fields(values)
30
+ config = RailsAdmin.config(model_name)
31
+
32
+ # Call appropriate Rails Admin field list methods
33
+ config.import do
34
+ include_all_fields
35
+ values.each do |value|
36
+ field value
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ require "rails_admin/config/sections/base"
2
+
3
+ module RailsAdmin
4
+ module Config
5
+ module Sections
6
+ # Configuration of the navigation view
7
+ class Import < RailsAdmin::Config::Sections::Base
8
+ register_instance_option(:mapping_key) do
9
+ :name
10
+ end
11
+
12
+ register_instance_option(:mapping_key_list) do
13
+ []
14
+ end
15
+
16
+ register_instance_option(:default_excluded_fields) do
17
+ [:id, :_id, :created_at, :updated_at, :c_at, :u_at, :deleted_at]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ section = RailsAdmin::Config::Sections::Import
25
+ name = :import
26
+
27
+ # Manually add to Rails Admin as a model configuration section until
28
+ # there is a better API to do this
29
+ RailsAdmin::Config::Model.send(:define_method, name) do |&block|
30
+ @sections = {} unless @sections
31
+ @sections[name] = section.new(self) unless @sections[name]
32
+ @sections[name].instance_eval(&block) if block
33
+ @sections[name]
34
+ end
@@ -0,0 +1,46 @@
1
+ require "rails_admin_import/config/legacy_model"
2
+
3
+ module RailsAdminImport
4
+ module Config
5
+ class << self
6
+ attr_accessor :logging
7
+ attr_accessor :line_item_limit
8
+ attr_accessor :rollback_on_error
9
+ attr_accessor :update_if_exists
10
+ attr_accessor :header_converter
11
+ attr_accessor :csv_options
12
+
13
+ # Default is to downcase headers and add underscores to convert into attribute names
14
+ HEADER_CONVERTER = lambda do |header|
15
+ # check for nil/blank headers
16
+ next if header.blank?
17
+ header.parameterize.underscore
18
+ end
19
+
20
+ def model(model_name, &block)
21
+ unless @deprecation_shown
22
+ warn "RailsAdminImport::Config#model is deprecated. " \
23
+ "Add a import section for your model inside the rails_admin " \
24
+ "config block. See the Readme.md for more details"
25
+ @deprecation_shown = true
26
+ end
27
+ legacy_config = RailsAdminImport::Config::LegacyModel.new(model_name)
28
+ legacy_config.instance_eval(&block) if block
29
+ legacy_config
30
+ end
31
+
32
+ # Reset all configurations to defaults.
33
+ def reset
34
+ @logging = false
35
+ @line_item_limit = 1000
36
+ @rollback_on_error = false
37
+ @update_if_exists = false
38
+ @header_converter = HEADER_CONVERTER
39
+ @csv_options = {}
40
+ end
41
+ end
42
+
43
+ # Set default values for configuration options on load
44
+ self.reset
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ require "rails_admin_import"
2
+ require "rchardet"
3
+ require "simple_xlsx_reader"
@@ -0,0 +1,4 @@
1
+ module RailsAdminImport
2
+ class Engine < Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,81 @@
1
+ require "csv"
2
+
3
+ module RailsAdminImport
4
+ module Formats
5
+ class CSVImporter < FileImporter
6
+ Formats.register(:csv, self)
7
+ Formats.register(:CSV, self)
8
+
9
+ autoload :CharDet, "rchardet"
10
+
11
+ def initialize(import_model, params)
12
+ super
13
+ @encoding = params[:encoding]
14
+ @header_converter = RailsAdminImport.config.header_converter
15
+ end
16
+
17
+ # A method that yields a hash of attributes for each record to import
18
+ def each_record
19
+ CSV.foreach(filename, csv_options) do |row|
20
+ attr = convert_to_attributes(row)
21
+ yield attr unless attr.all? { |field, value| value.blank? }
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def csv_options
28
+ defaults = RailsAdminImport.config.csv_options
29
+ options = {
30
+ headers: true,
31
+ header_converters: @header_converter,
32
+ encoding: encoding,
33
+ }
34
+
35
+ defaults.merge(options)
36
+ end
37
+
38
+ def encoding
39
+ from_encoding =
40
+ if !@encoding.blank?
41
+ @encoding
42
+ else
43
+ detect_encoding
44
+ end
45
+
46
+ to_encoding = import_model.abstract_model.encoding
47
+
48
+ if from_encoding && from_encoding != to_encoding
49
+ "#{from_encoding}:#{to_encoding}"
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
55
+ def detect_encoding
56
+ # Su: Remove CharlockHolmes since it require to another software http://site.icu-project.org/home
57
+ # which is not available on server
58
+ # charset = CharlockHolmes::EncodingDetector.detect File.read(filename)
59
+ # if charset[:confidence] > 0.6
60
+ # from_encoding = charset[:encoding]
61
+ # from_encoding = "UTF-8" if from_encoding == "ascii"
62
+ # end
63
+ from_encoding = "UTF-8"
64
+ from_encoding
65
+ end
66
+
67
+ def convert_to_attributes(row)
68
+ row.each_with_object({}) do |(field, value), record|
69
+ next if field.blank?
70
+ field = field.to_sym
71
+ if import_model.has_multiple_values?(field)
72
+ field = import_model.pluralize_field(field)
73
+ (record[field] ||= []) << value
74
+ else
75
+ record[field] = value
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end