solidus_import_products 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +71 -0
  4. data/app/controllers/spree/admin/product_imports_controller.rb +35 -0
  5. data/app/jobs/import_products_job.rb +14 -0
  6. data/app/models/solidus_import_products/parser.rb +15 -0
  7. data/app/models/solidus_import_products/parser/base.rb +24 -0
  8. data/app/models/solidus_import_products/parser/csv.rb +98 -0
  9. data/app/models/spree/product_import.rb +82 -0
  10. data/app/overrides/add_import_to_admin_sidebar_menu.rb +6 -0
  11. data/app/services/solidus_import_products/create_variant.rb +109 -0
  12. data/app/services/solidus_import_products/import.rb +38 -0
  13. data/app/services/solidus_import_products/process_row.rb +93 -0
  14. data/app/services/solidus_import_products/save_product.rb +41 -0
  15. data/app/services/solidus_import_products/save_properties.rb +23 -0
  16. data/app/services/solidus_import_products/update_product.rb +45 -0
  17. data/app/views/spree/admin/product_imports/index.html.erb +71 -0
  18. data/app/views/spree/admin/product_imports/show.html.erb +37 -0
  19. data/app/views/spree/admin/shared/_import_sidebar_menu.erb +3 -0
  20. data/app/views/spree/user_mailer/product_import_results.text.erb +23 -0
  21. data/lib/generators/solidus_import_products/install/install_generator.rb +41 -0
  22. data/lib/generators/solidus_import_products/install/templates/config/initializers/solidus_import_product_settings.rb +10 -0
  23. data/lib/solidus_import_products.rb +8 -0
  24. data/lib/solidus_import_products/engine.rb +26 -0
  25. data/lib/solidus_import_products/exception.rb +19 -0
  26. data/lib/solidus_import_products/import_helper.rb +118 -0
  27. data/lib/solidus_import_products/logger.rb +15 -0
  28. data/lib/solidus_import_products/user_mailer_ext.rb +15 -0
  29. data/lib/tasks/solidus_import_products.rake +1 -0
  30. metadata +255 -0
@@ -0,0 +1,26 @@
1
+ module SolidusImportProducts
2
+ class Engine < Rails::Engine
3
+ engine_name 'import_products'
4
+
5
+ config.autoload_paths += %W[#{config.root}/lib]
6
+
7
+ def self.activate
8
+ Dir.glob(File.join(File.dirname(__FILE__), '../app/**/*_decorator*.rb')) do |c|
9
+ Rails.env.production? ? require(c) : load(c)
10
+ end
11
+
12
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/overrides/*.rb')) do |c|
13
+ Rails.application.config.cache_classes ? require(c) : load(c)
14
+ end
15
+
16
+ Spree::UserMailer.send(:include, SolidusImportProducts::UserMailerExt)
17
+
18
+ # TODO: replace the class_eval
19
+ Spree::User.class_eval do
20
+ has_many :product_imports, class_name: 'Spree::ProductImport', foreign_key: 'created_by'
21
+ end
22
+ end
23
+
24
+ config.to_prepare &method(:activate).to_proc
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ module SolidusImportProducts
2
+ module Exception
3
+ class Base < StandardError; end
4
+
5
+ class ProductError < Base; end
6
+ class VariantError < Base; end
7
+ class ImportError < Base; end
8
+ class SkuError < Base; end
9
+ class InvalidPrice < Base; end
10
+
11
+ class AbstractMthodCall < Base
12
+ def initialize(msg = 'This methid should be implemented in the subclass')
13
+ super
14
+ end
15
+ end
16
+
17
+ class InvalidParseStrategy < Base; end
18
+ end
19
+ end
@@ -0,0 +1,118 @@
1
+ require 'active_support/concern'
2
+
3
+ module SolidusImportProducts
4
+ module ImportHelper
5
+ extend ActiveSupport::Concern
6
+
7
+ require 'open-uri'
8
+
9
+ included do
10
+ ### IMAGE HELPERS ###
11
+ # find_and_attach_image_to
12
+ # This method attaches images to products. The images may come
13
+ # from a local source (i.e. on disk), or they may be online (HTTP/HTTPS).
14
+ def find_and_attach_image_to(product_or_variant, filename)
15
+ return if filename.blank?
16
+
17
+ temp_file = fetch_image(filename)
18
+ return unless temp_file
19
+ product_image = Spree::Image.new(attachment: temp_file,
20
+ viewable_id: product_or_variant.id,
21
+ viewable_type: 'Spree::Variant',
22
+ alt: '',
23
+ position: product_or_variant.images.length)
24
+
25
+ logger.log("#{product_image.viewable_id} : #{product_image.viewable_type} : #{product_image.position}", :debug)
26
+
27
+ product_or_variant.images << product_image if product_image.save
28
+ end
29
+
30
+ def fetch_image(filename)
31
+ filename =~ /\Ahttp[s]*:\/\// ? fetch_remote_image(filename) : fetch_local_image(filename)
32
+ end
33
+
34
+ # This method is used when we have a set location on disk for
35
+ # images, and the file is accessible to the script.
36
+ # It is basically just a wrapper around basic File IO methods.
37
+ def fetch_local_image(filename)
38
+ filename = Spree::ProductImport.settings[:product_image_path] + filename
39
+ unless File.exist?(filename) && File.readable?(filename)
40
+ logger.log("Image #{filename} was not found on the server, so this image was not imported.", :warn)
41
+ return nil
42
+ end
43
+ File.open(filename, 'rb')
44
+ end
45
+
46
+ # This method can be used when the filename matches the format of a URL.
47
+ # It uses open-uri to fetch the file, returning a Tempfile object if it
48
+ # is successful.
49
+ # If it fails, it in the first instance logger.logs the HTTP error (404, 500 etc)
50
+ # If it fails altogether, it logger.logs it and exits the method.
51
+ def fetch_remote_image(filename)
52
+ io = open(URI.parse(filename))
53
+ def io.original_filename
54
+ base_uri.path.split('/').last
55
+ end
56
+ return io
57
+ rescue OpenURI::HTTPError => error
58
+ logger.log("Image #{filename} retrival returned #{error.message}, so this image was not imported")
59
+ return nil
60
+ end
61
+
62
+ ### TAXON HELPERS ###
63
+
64
+ # associate_product_with_taxon
65
+ # This method accepts three formats of taxon hierarchy strings which will
66
+ # associate the given products with taxons:
67
+ # 1. A string on it's own will will just find or create the taxon and
68
+ # add the product to it. e.g. taxonomy = "Category", taxon_hierarchy = "Tools" will
69
+ # add the product to the 'Tools' category.
70
+ # 2. A item > item > item structured string will read this like a tree - allowing
71
+ # a particular taxon to be picked out
72
+ # 3. An item > item & item > item will work as above, but will associate multiple
73
+ # taxons with that product. This form should also work with format 1.
74
+ def associate_product_with_taxon(product, taxon_hierarchy, putInTop)
75
+ return if product.nil? || taxon_hierarchy.nil?
76
+
77
+ taxon_hierarchy.split(/\s*\|\s*/).each do |hierarchy|
78
+ hierarchy = hierarchy.split(/\s*>\s*/)
79
+ taxonomy = Spree::Taxonomy.where('lower(spree_taxonomies.name) = ?', hierarchy.first.downcase).first
80
+
81
+ if taxonomy.nil? && Spree::ProductImport.settings[:create_missing_taxonomies]
82
+ taxonomy = Spree::Taxonomy.create(name: hierarchy.first.capitalize)
83
+ end
84
+
85
+ unless taxonomy.valid?
86
+ logger.log(msg = "A product could not be imported - here is the information we have:\n" \
87
+ "Product: #{product.inspect}, Taxonomy: #{taxon_hierarchy}, Errors: #{taxonomy.errors.full_messages.join(', ')} \n", :error)
88
+ raise ProductError, msg
89
+ end
90
+
91
+ last_taxon = taxonomy.root
92
+
93
+ hierarchy.shift
94
+ hierarchy.each do |taxon|
95
+ last_taxon = last_taxon.children.find_or_create_by(name: taxon, taxonomy_id: taxonomy.id)
96
+ next if last_taxon.valid?
97
+ logger.log(msg = "A product could not be imported - here is the information we have:\n" \
98
+ "Product: #{product.inspect}, Taxonomy: #{taxonomy.inspect}, Taxon: #{last_taxon.inspect}, #{last_taxon.errors.full_messages.join(', ')}", :error)
99
+ raise ProductError, msg
100
+ end
101
+ # Spree only needs to know the most detailed taxonomy item
102
+ product.taxons << last_taxon unless product.taxons.include?(last_taxon)
103
+ end
104
+
105
+ # TODO: Not sure what this does.
106
+ if putInTop && defined?(SolidusSortProductsTaxon)
107
+ if SolidusSortProductsTaxon::Config.activated
108
+ unless product.put_in_taxons_top(product.taxons)
109
+ logger.log(msg = "A product could not be imported - here is the information we have:\n" \
110
+ "Product: #{product.inspect}, Taxons: #{product.taxons}, #{product.errors.full_messages.join(', ')}", :error)
111
+ raise SolidusImportProducts::Exception::ProductError, msg
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,15 @@
1
+ module SolidusImportProducts
2
+ class Logger
3
+ include Singleton
4
+
5
+ attr_accessor :logger
6
+
7
+ def initialize
8
+ self.logger = ActiveSupport::Logger.new(Spree::ProductImport.settings[:log_to])
9
+ end
10
+
11
+ def log(message, severity = :info)
12
+ logger.send severity, "[#{Time.now.to_s(:db)}] [#{severity.to_s.capitalize}] #{message}\n"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SolidusImportProducts
2
+ module UserMailerExt
3
+ def self.included(base)
4
+ base.class_eval do
5
+ def product_import_results(user, error_message = nil)
6
+ @user = user
7
+ @error_message = error_message
8
+ store = Spree::Store.default
9
+ # attachments["import_products.log"] = File.read(Spree::ProductImport.settings[:log_to]) if @error_message.nil?
10
+ mail(to: @user.email, from: from_address(store), subject: "Spree: Import Products #{error_message.nil? ? 'Success' : 'Failure'}")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ # add custom rake tasks here
metadata ADDED
@@ -0,0 +1,255 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solidus_import_products
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.1
5
+ platform: ruby
6
+ authors:
7
+ - ngelX
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: deface
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: solidus_auth_devise
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: solidus_core
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: solidus_support
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: capybara
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.17'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.17'
83
+ - !ruby/object:Gem::Dependency
84
+ name: factory_bot_rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: ffaker
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 3.7.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 3.7.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: shoulda-matchers
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.1'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.1'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: sqlite3
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: vcr
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: webmock
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 3.4.1
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 3.4.1
195
+ description:
196
+ email: ngelx@protonmail.com
197
+ executables: []
198
+ extensions: []
199
+ extra_rdoc_files: []
200
+ files:
201
+ - LICENSE
202
+ - README.md
203
+ - app/controllers/spree/admin/product_imports_controller.rb
204
+ - app/jobs/import_products_job.rb
205
+ - app/models/solidus_import_products/parser.rb
206
+ - app/models/solidus_import_products/parser/base.rb
207
+ - app/models/solidus_import_products/parser/csv.rb
208
+ - app/models/spree/product_import.rb
209
+ - app/overrides/add_import_to_admin_sidebar_menu.rb
210
+ - app/services/solidus_import_products/create_variant.rb
211
+ - app/services/solidus_import_products/import.rb
212
+ - app/services/solidus_import_products/process_row.rb
213
+ - app/services/solidus_import_products/save_product.rb
214
+ - app/services/solidus_import_products/save_properties.rb
215
+ - app/services/solidus_import_products/update_product.rb
216
+ - app/views/spree/admin/product_imports/index.html.erb
217
+ - app/views/spree/admin/product_imports/show.html.erb
218
+ - app/views/spree/admin/shared/_import_sidebar_menu.erb
219
+ - app/views/spree/user_mailer/product_import_results.text.erb
220
+ - lib/generators/solidus_import_products/install/install_generator.rb
221
+ - lib/generators/solidus_import_products/install/templates/config/initializers/solidus_import_product_settings.rb
222
+ - lib/solidus_import_products.rb
223
+ - lib/solidus_import_products/engine.rb
224
+ - lib/solidus_import_products/exception.rb
225
+ - lib/solidus_import_products/import_helper.rb
226
+ - lib/solidus_import_products/logger.rb
227
+ - lib/solidus_import_products/user_mailer_ext.rb
228
+ - lib/tasks/solidus_import_products.rake
229
+ homepage: https://github.com/ngelx/solidus_import_products
230
+ licenses:
231
+ - MIT
232
+ metadata: {}
233
+ post_install_message:
234
+ rdoc_options: []
235
+ require_paths:
236
+ - lib
237
+ required_ruby_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ version: 2.2.2
242
+ required_rubygems_version: !ruby/object:Gem::Requirement
243
+ requirements:
244
+ - - ">="
245
+ - !ruby/object:Gem::Version
246
+ version: '0'
247
+ requirements:
248
+ - none
249
+ rubyforge_project:
250
+ rubygems_version: 2.6.11
251
+ signing_key:
252
+ specification_version: 4
253
+ summary: solidus_import_products ... imports products. From a CSV file via Solidus's
254
+ Admin interface
255
+ test_files: []