workarea-google_product_feed 3.1.3

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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/CHANGELOG.md +99 -0
  4. data/Gemfile +10 -0
  5. data/README.md +61 -0
  6. data/Rakefile +53 -0
  7. data/app/models/workarea/catalog/category.decorator +7 -0
  8. data/app/models/workarea/catalog/product.decorator +7 -0
  9. data/app/models/workarea/feed/google.rb +16 -0
  10. data/app/view_models/workarea/storefront/google_product_feed_sku_view_model.rb +26 -0
  11. data/app/view_models/workarea/storefront/google_product_feed_view_model.rb +125 -0
  12. data/app/workers/workarea/exporting/google_feed.rb +90 -0
  13. data/bin/rails +18 -0
  14. data/config/initializers/00_scheduled_jobs.rb +6 -0
  15. data/config/initializers/01_configuration.rb +7 -0
  16. data/config/routes.rb +6 -0
  17. data/lib/workarea/google_product_feed.rb +32 -0
  18. data/lib/workarea/google_product_feed/engine.rb +8 -0
  19. data/lib/workarea/google_product_feed/version.rb +5 -0
  20. data/script/admin_ci +9 -0
  21. data/script/ci +11 -0
  22. data/script/core_ci +9 -0
  23. data/script/plugins_ci +9 -0
  24. data/script/storefront_ci +9 -0
  25. data/test/dummy/Rakefile +6 -0
  26. data/test/dummy/app/assets/config/manifest.js +4 -0
  27. data/test/dummy/app/assets/images/.keep +0 -0
  28. data/test/dummy/app/assets/javascripts/application.js +13 -0
  29. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  30. data/test/dummy/app/controllers/application_controller.rb +3 -0
  31. data/test/dummy/app/controllers/concerns/.keep +0 -0
  32. data/test/dummy/app/helpers/application_helper.rb +2 -0
  33. data/test/dummy/app/jobs/application_job.rb +2 -0
  34. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  35. data/test/dummy/app/models/concerns/.keep +0 -0
  36. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  37. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  38. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  39. data/test/dummy/bin/bundle +3 -0
  40. data/test/dummy/bin/rails +4 -0
  41. data/test/dummy/bin/rake +4 -0
  42. data/test/dummy/bin/setup +34 -0
  43. data/test/dummy/bin/update +29 -0
  44. data/test/dummy/config.ru +5 -0
  45. data/test/dummy/config/application.rb +20 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/cable.yml +9 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +54 -0
  50. data/test/dummy/config/environments/production.rb +86 -0
  51. data/test/dummy/config/environments/test.rb +43 -0
  52. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  53. data/test/dummy/config/initializers/assets.rb +11 -0
  54. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  55. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  56. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  57. data/test/dummy/config/initializers/inflections.rb +16 -0
  58. data/test/dummy/config/initializers/mime_types.rb +4 -0
  59. data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
  60. data/test/dummy/config/initializers/session_store.rb +3 -0
  61. data/test/dummy/config/initializers/workarea.rb +5 -0
  62. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  63. data/test/dummy/config/locales/en.yml +23 -0
  64. data/test/dummy/config/puma.rb +47 -0
  65. data/test/dummy/config/routes.rb +5 -0
  66. data/test/dummy/config/secrets.yml +22 -0
  67. data/test/dummy/config/spring.rb +6 -0
  68. data/test/dummy/lib/assets/.keep +0 -0
  69. data/test/dummy/log/.keep +0 -0
  70. data/test/dummy/public/404.html +67 -0
  71. data/test/dummy/public/422.html +67 -0
  72. data/test/dummy/public/500.html +66 -0
  73. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  74. data/test/dummy/public/apple-touch-icon.png +0 -0
  75. data/test/dummy/public/favicon.ico +0 -0
  76. data/test/test_helper.rb +13 -0
  77. data/test/view_models/workarea/storefront/google_product_feed_view_model_test.rb +46 -0
  78. data/test/workers/workarea/exporting/google_feed_test.rb +181 -0
  79. data/workarea-google_product_feed.gemspec +16 -0
  80. metadata +140 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cd02b4d78fddea69ede52bdb1f96282debd4d8f21c4c82c104c4af08b23325e3
4
+ data.tar.gz: 407d2275d33690d402795ff6ca127b3761ab63d5c7fc77a3f90c586e163602a2
5
+ SHA512:
6
+ metadata.gz: fcd0ef976f661a4aa9386878c6117d28df6c279a43db090ebe2ea79e43d0a437c601b112d8c74f6b65daed9dbbb08cebc48950ac51cd86ea5bf596fe03e01bfd
7
+ data.tar.gz: 01671aef8ac70fe1cf0586e46cc1d46421d31d2936ad7e47949bdc5005e2c7d758e7ad2ed4cb513d740dcef595c31f847d53d000a69ff3607573fbfdfdb9f3fe
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ .bundle/
2
+ Gemfile.lock
3
+ .sass-cache/
4
+ .DS_Store
5
+ log/*.log
6
+ pkg/
7
+ tmp/
8
+ test/dummy/public/system/*
9
+ test/dummy/db/*.sqlite3
10
+ test/dummy/log/*.log
11
+ test/dummy/tmp/
12
+ test/dummy/.sass-cache
13
+ coverage
14
+ .byebug_history
data/CHANGELOG.md ADDED
@@ -0,0 +1,99 @@
1
+ Workarea Google Product Feed 3.1.3 (2019-08-21)
2
+ --------------------------------------------------------------------------------
3
+
4
+ * Open Source!
5
+
6
+
7
+
8
+ Workarea Google Product Feed 3.1.2 (2019-06-25)
9
+ --------------------------------------------------------------------------------
10
+
11
+ * Update image urls in google product feed
12
+
13
+ * change image size to be :detail so the images are larger than 250
14
+ (googles requirement for apparel products)
15
+ * fix problems with tests failing with multi-site
16
+
17
+ GOOGPROD-22
18
+ Eric Pigeon
19
+
20
+
21
+
22
+ Workarea Google Product Feed 3.1.1 (2018-09-19)
23
+ --------------------------------------------------------------------------------
24
+
25
+ * Fix config values not being reset in tests
26
+
27
+ Tests can fail because values aren't reset properly.
28
+
29
+ GOOGPROD-20
30
+ Ben Crouse
31
+
32
+
33
+
34
+ Google Product Feed 3.1.0 (2018-03-21)
35
+ --------------------------------------------------------------------------------
36
+
37
+ * Add a configuration for static values in google merchant feed
38
+
39
+ GOOGPROD-16
40
+ Eric Pigeon
41
+
42
+ * Use a product's default category first
43
+
44
+ update the google taxonomy to prefer using it's default category first
45
+
46
+ GOOGPROD-18
47
+ Eric Pigeon
48
+
49
+
50
+ Google Product Feed 3.0.2 (2018-01-11)
51
+ --------------------------------------------------------------------------------
52
+
53
+ * Product feed does not include inactive products
54
+
55
+ GOOGPROD-10
56
+ Jake Beresford
57
+
58
+ * Improve implementation of Google category field
59
+
60
+ * check product, then category, then fall back to a configuration
61
+
62
+ GOOGPROD-11
63
+ Jake Beresford
64
+
65
+ * Do not export non-displayable skus to google feed
66
+
67
+ GOOGPROD-10
68
+ Jake Beresford
69
+
70
+
71
+ Google Product Feed 3.0.0 (2017-05-25)
72
+ --------------------------------------------------------------------------------
73
+
74
+ * Fixes final test.
75
+
76
+ GOOGPROD-7
77
+ Beresford, Jake
78
+
79
+ * Upgrade plugin for v3 compatibility
80
+
81
+ * Rename
82
+ * Convert rspec to minitest
83
+ * Use Variant#active? instead of Variant#purchasable?
84
+
85
+ GOOGPROD-7
86
+ Beresford, Jake
87
+
88
+
89
+ Google Product Feed 2.0.5 (2016-12-07)
90
+ --------------------------------------------------------------------------------
91
+
92
+ * Send the correct price for a variant
93
+
94
+ If a product has different pricers per variant we should use the correct
95
+ sell price for the sku rather than the minimal selling price of the
96
+ product
97
+
98
+ GOOGPROD-6
99
+ Eric Pigeon
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'workarea', source: 'https://gems.weblinc.com'
6
+
7
+ group :test do
8
+ gem 'simplecov', require: false
9
+ gem 'simplecov-bamboo', require: false
10
+ end
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ Workarea Google Production Feed
2
+ ================================================================================
3
+ Google Production Feed for the Workarea Platform
4
+
5
+
6
+ Google category
7
+ --------------------------------------------------------------------------------
8
+ The google product feed includes a field for google category. This field must map to one of the pre-defined categories.
9
+ Documentation can be found here [https://support.google.com/merchants/answer/6324436?hl=en](https://support.google.com/merchants/answer/6324436?hl=en)
10
+
11
+ The workarea platform implements this field by:
12
+ 1) Look for google\_category on the product, use this if found.
13
+ 2) Look for google\_name on the products default category
14
+ 3) Look for google\_name on categories the product is in, use this if found
15
+ 4) Use the default google category from Workarea::GoogleProductFeed.default\_category
16
+
17
+ Getting Started
18
+ --------------------------------------------------------------------------------
19
+
20
+ You must have access to a WebLinc gems server to use this gem. Add your gems server credentials to Bundler:
21
+
22
+ ```bash
23
+ $ bundle config gems.weblinc.com my_username:my_password
24
+ ```
25
+
26
+ Or set the appropriate environment variable in a shell startup file:
27
+
28
+ ```bash
29
+ $ export BUNDLE_GEMS__WEBLINC__COM='my_username:my_password'
30
+ ```
31
+
32
+ Then add the gem to your application's Gemfile specifying the source:
33
+
34
+ ```ruby
35
+ # ...
36
+ gem 'workarea-google_product_feed', source: 'https://gems.weblinc.com'
37
+ # ...
38
+ ```
39
+
40
+ Or use a source block:
41
+
42
+ ```ruby
43
+ # ...
44
+ source 'https://gems.weblinc.com' do
45
+ gem 'workarea-google_product_feed'
46
+ end
47
+ # ...
48
+ ```
49
+
50
+ Update your application's bundle.
51
+
52
+ ```bash
53
+ $ cd path/to/application ; bundle
54
+ ```
55
+
56
+ Copyright & Licensing
57
+ --------------------------------------------------------------------------------
58
+
59
+ Copyright WebLinc 2018. All rights reserved.
60
+
61
+ For licensing, contact sales@workarea.com.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ require 'rdoc/task'
9
+
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'Tasker'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.md')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
19
+ load 'rails/tasks/engine.rake'
20
+ load 'rails/tasks/statistics.rake'
21
+ load 'workarea/changelog.rake'
22
+
23
+ require 'rake/testtask'
24
+
25
+ Rake::TestTask.new(:test) do |t|
26
+ t.libs << 'lib'
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
33
+
34
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
35
+ require 'workarea/google_product_feed/version'
36
+
37
+ desc "Release version #{Workarea::GoogleProductFeed::VERSION} of the gem"
38
+ task :release do
39
+ host = "https://#{ENV['BUNDLE_GEMS__WEBLINC__COM']}@gems.weblinc.com"
40
+
41
+ #Rake::Task['workarea:changelog'].execute
42
+ #system 'git add CHANGELOG.md'
43
+ #system 'git commit -m "Update CHANGELOG"'
44
+ #system 'git push origin HEAD'
45
+
46
+ system "git tag -a v#{Workarea::GoogleProductFeed::VERSION} -m 'Tagging #{Workarea::GoogleProductFeed::VERSION}'"
47
+ system 'git push --tags'
48
+
49
+ system 'gem build workarea-google_product_feed.gemspec'
50
+ system "gem push workarea-google_product_feed-#{Workarea::GoogleProductFeed::VERSION}.gem"
51
+ system "gem push workarea-google_product_feed-#{Workarea::GoogleProductFeed::VERSION}.gem --host #{host}"
52
+ system "rm workarea-google_product_feed-#{Workarea::GoogleProductFeed::VERSION}.gem"
53
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Catalog::Category, with: :google_product_feed do
3
+ decorated do
4
+ field :google_name, type: String
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Catalog::Product, with: :google_product_feed do
3
+ decorated do
4
+ field :google_category, type: String
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module Workarea
2
+ class Feed::Google
3
+ class NoFeedError < StandardError; end
4
+
5
+ include ApplicationDocument
6
+ extend Dragonfly::Model
7
+
8
+ field :feed_uid, type: String
9
+
10
+ dragonfly_accessor :feed, app: :workarea
11
+
12
+ def self.find_first_or_initialize
13
+ first || new
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module Workarea
2
+ module Storefront
3
+ class GoogleProductFeedSkuViewModel < ApplicationViewModel
4
+ def status
5
+ model.active? ? 'in stock' : 'out of stock'
6
+ end
7
+
8
+ def color
9
+ Array.wrap(model.fetch_detail('color')).first
10
+ end
11
+
12
+ def condition
13
+ 'new'
14
+ end
15
+
16
+ def size
17
+ Array.wrap(model.fetch_detail('size')).first
18
+ end
19
+
20
+ def displayable?
21
+ return false unless options[:inventory_sku].present?
22
+ options[:inventory_sku].displayable?
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,125 @@
1
+ module Workarea
2
+ module Storefront
3
+ class GoogleProductFeedViewModel < ApplicationViewModel
4
+ module ProductImageUrl
5
+ include Workarea::ApplicationHelper
6
+ include Workarea::I18n::DefaultUrlOptions
7
+ include ActionView::Helpers::AssetUrlHelper
8
+ include Core::Engine.routes.url_helpers
9
+ extend self
10
+
11
+ def mounted_core
12
+ self
13
+ end
14
+ end
15
+ include ActionView::Helpers::SanitizeHelper
16
+ include ActionView::Helpers::TextHelper
17
+
18
+ def self.fields
19
+ [
20
+ 'item_group_id',
21
+ 'id',
22
+ 'title',
23
+ 'description',
24
+ 'link',
25
+ 'image_link',
26
+ 'availability',
27
+ 'condition',
28
+ 'brand',
29
+ 'color',
30
+ 'size',
31
+ 'google_product_category',
32
+ 'product_type',
33
+ 'price'
34
+ ] + GoogleProductFeed.static_feed_values.keys
35
+ end
36
+
37
+ delegate :sell_min_price, to: :pricing
38
+
39
+ def brand
40
+ return '' unless model.filters['brand'].present?
41
+ model.filters['brand'].join(' ')
42
+ end
43
+
44
+ def category_name
45
+ model.google_category.presence ||
46
+ category&.google_name.presence ||
47
+ Workarea::GoogleProductFeed.default_category
48
+ end
49
+
50
+ # TODO this should be changed to return sku images if present
51
+ def image
52
+ ProductImageUrl.product_image_url(images.primary, GoogleProductFeed.image_size)
53
+ end
54
+
55
+ def meta_description
56
+ if model.meta_description.present?
57
+ sanitize_description(model.meta_description)
58
+ else
59
+ sanitize_description(model.description)
60
+ end
61
+ end
62
+
63
+ def product_type
64
+ return '' unless model.filters['product_type'].present?
65
+ model.filters['product_type'].first
66
+ end
67
+
68
+ def variants
69
+ @variants ||= model.variants.map do |variant|
70
+ GoogleProductFeedSkuViewModel.wrap(
71
+ variant,
72
+ inventory_sku: inventory.for_sku(variant.sku)
73
+ )
74
+ end
75
+ end
76
+
77
+ def sku_price(sku)
78
+ pricing.for_sku(sku).sell
79
+ end
80
+
81
+ private
82
+
83
+ def category
84
+ return categorization.default_model if categorization.default_model&.google_name.present?
85
+
86
+ categorization.to_models.detect { |c| c.google_name.present? } ||
87
+ categorization.to_models.first
88
+ end
89
+
90
+ def categorization
91
+ @categorization ||= Workarea::Categorization.new(model)
92
+ end
93
+
94
+ def host
95
+ Workarea.config.host
96
+ end
97
+
98
+ def images
99
+ @images ||= Storefront::ProductViewModel::ImageCollection.new(model, options)
100
+ end
101
+
102
+ def inventory
103
+ @inventory ||= Inventory::Collection.new(model.variants.map(&:sku))
104
+ end
105
+
106
+ def pricing
107
+ @pricing ||= Pricing::Collection.new(model.variants.map(&:sku))
108
+ end
109
+
110
+ def primary_image
111
+ model.images.asc(:position).first
112
+ end
113
+
114
+ def sanitize_description(description)
115
+ return if description.nil?
116
+
117
+ # strip html and shorten to the google max of 5k chars
118
+ sanitized = truncate(strip_tags(description), length: 5000)
119
+
120
+ # strip tabs and new lines
121
+ sanitized.tr("\t\r\n", '')
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,90 @@
1
+ module Workarea
2
+ module Exporting
3
+ class GoogleFeed
4
+ include Sidekiq::Worker
5
+ include I18n::DefaultUrlOptions
6
+ include Storefront::Engine.routes.url_helpers
7
+
8
+ cattr_accessor :execution_block_size, :temp_file
9
+ self.execution_block_size = 100
10
+ self.temp_file = 'tmp/export/google_feed.txt'
11
+
12
+ sidekiq_options queue: 'low'
13
+
14
+ def perform(*args)
15
+ generate_csv
16
+ save_google_feed
17
+ end
18
+
19
+ def generate_csv
20
+ ensure_directory
21
+
22
+ @feed = CSV.open(GoogleFeed.temp_file, 'w', col_sep: "\t")
23
+ write_header
24
+ write_google_feed
25
+
26
+ ensure
27
+ @feed.close if defined?(@feed)
28
+ end
29
+
30
+ private
31
+
32
+ def ensure_directory
33
+ directory = File.dirname(GoogleFeed.temp_file)
34
+ FileUtils.mkdir_p(directory)
35
+ end
36
+
37
+ def write_header
38
+ @feed << Storefront::GoogleProductFeedViewModel.fields
39
+ end
40
+
41
+ def write_google_feed
42
+ Catalog::Product.all.each_by(GoogleFeed.execution_block_size) do |p|
43
+ product = Storefront::GoogleProductFeedViewModel.wrap(p)
44
+
45
+ product.variants.each do |sku|
46
+ next unless item_valid(product, sku)
47
+ @feed << values_array(product, sku)
48
+ end
49
+ end
50
+ end
51
+
52
+ def save_google_feed
53
+ google_feed = Feed::Google.find_first_or_initialize
54
+ google_feed.feed = @feed
55
+ google_feed.save!
56
+ end
57
+
58
+ def host
59
+ Workarea.config.host
60
+ end
61
+
62
+ def item_valid(product, sku)
63
+ sku.active? &&
64
+ sku.displayable? &&
65
+ product.active? &&
66
+ product.sku_price(sku.sku).present? &&
67
+ product.sku_price(sku.sku) > 0
68
+ end
69
+
70
+ def values_array(product, sku)
71
+ [
72
+ product.id,
73
+ sku.sku,
74
+ product.name,
75
+ product.meta_description,
76
+ product_url(product, host: host, sku: sku.sku),
77
+ product.image,
78
+ sku.status,
79
+ sku.condition,
80
+ product.brand,
81
+ sku.color,
82
+ sku.size,
83
+ product.category_name,
84
+ product.product_type,
85
+ product.sku_price(sku.sku)
86
+ ] + GoogleProductFeed.static_feed_values.values
87
+ end
88
+ end
89
+ end
90
+ end