workarea-reviews 3.0.10 → 3.1.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.json +35 -0
  3. data/.github/workflows/ci.yml +60 -0
  4. data/.gitignore +1 -2
  5. data/.rubocop.yml +3 -0
  6. data/.stylelintrc.json +8 -0
  7. data/CHANGELOG.md +5 -23
  8. data/Gemfile +4 -2
  9. data/Rakefile +4 -5
  10. data/app/controllers/workarea/admin/reports_controller.decorator +10 -0
  11. data/app/helpers/workarea/admin/reviews_helper.rb +6 -5
  12. data/app/helpers/workarea/storefront/reviews_helper.rb +1 -3
  13. data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb +34 -0
  14. data/app/mailers/workarea/admin/status_report_mailer.decorator +2 -2
  15. data/app/models/workarea/insights/most_active_reviewers.rb +34 -0
  16. data/app/models/workarea/insights/most_reviewed_products.rb +33 -0
  17. data/app/models/workarea/insights/top_rated_products.rb +33 -0
  18. data/app/models/workarea/review.rb +5 -16
  19. data/app/queries/workarea/reports/reviews_by_product.rb +86 -0
  20. data/app/queries/workarea/reports/reviews_by_user.rb +71 -0
  21. data/app/seeds/{review_seeds.rb → workarea/review_seeds.rb} +0 -0
  22. data/app/view_models/workarea/admin/dashboards/reports_view_model.decorator +11 -0
  23. data/app/view_models/workarea/admin/reports/reviews_by_product_view_model.rb +27 -0
  24. data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml +17 -0
  25. data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml +22 -0
  26. data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml +22 -0
  27. data/app/views/workarea/admin/insights/_top_rated_products.html.haml +22 -0
  28. data/app/views/workarea/admin/reports/reviews_by_product.html.haml +44 -0
  29. data/app/views/workarea/storefront/products/_rating.html.haml +1 -2
  30. data/app/views/workarea/storefront/products/_reviews.html.haml +5 -5
  31. data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +2 -2
  32. data/app/views/workarea/storefront/review_mailer/review_request.html.haml +2 -0
  33. data/app/views/workarea/storefront/review_requests/show.html.haml +2 -1
  34. data/config/initializers/append_points.rb +5 -0
  35. data/config/initializers/configuration.rb +3 -1
  36. data/config/locales/en.yml +46 -0
  37. data/config/routes.rb +4 -0
  38. data/lib/workarea/reviews/engine.rb +4 -0
  39. data/lib/workarea/reviews/version.rb +1 -1
  40. data/package.json +9 -0
  41. data/test/dummy/config/initializers/session_store.rb +3 -1
  42. data/test/helpers/workarea/storefront/reviews_helper_test.rb +1 -0
  43. data/test/integration/workarea/admin/reviews_integration_test.rb +5 -1
  44. data/test/models/workarea/insights/most_active_reviewers_test.rb +32 -0
  45. data/test/models/workarea/insights/most_reviewed_products_test.rb +32 -0
  46. data/test/models/workarea/insights/top_rated_products_test.rb +49 -0
  47. data/test/models/workarea/review_test.rb +29 -18
  48. data/test/queries/workarea/reports/reviews_by_product_test.rb +104 -0
  49. data/test/queries/workarea/reports/reviews_by_user_test.rb +102 -0
  50. data/test/system/workarea/admin/reviews_by_product_system_test.rb +60 -0
  51. data/workarea-reviews.gemspec +1 -1
  52. data/yarn.lock +3265 -0
  53. metadata +31 -7
  54. data/app/controllers/workarea/admin/import_reviews_controller.rb +0 -35
  55. data/test/helpers/workarea/admin/reviews_helper_test.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbc95c26a6972a59da6d0daa765491622783a60cfc8dd0961109b82839b0af77
4
- data.tar.gz: 9f62f3d6484cdf8002172106970954fa053784844a6fa6084b2aee3baadf8e7b
3
+ metadata.gz: 7135d1659f6d6564addc0f472fbf72a6225098a29a9de3e9a3c16af4724b364e
4
+ data.tar.gz: fdfe10e7e4e59e7bfb4b4c08f05dc2a8190acc36c2bd5602277c2298a2f4072e
5
5
  SHA512:
6
- metadata.gz: e6913aca804eeb31ab4bb877b74b2826b50596e7b3a087eb162c49d46dc6df2a162ab8ca683e8251a1c056c798d12597294125acea2f102a91150baef4b31a4c
7
- data.tar.gz: ab487315635024cb2fcbb3b6cea388188b01ad31e0cfbcaf6e9e9513f35f1ad53c035eeb67927bda4796265bf6459eed6b14028bec6faf22066c3257907df5a1
6
+ metadata.gz: 294e2feacac1639da7c4b927fd9580f8c5e000bbd93885893a1c647cd980327ff8dd6d01d83db044cfc56da5dc71f2c62825deb07f71f85e042eeb59a54babb9
7
+ data.tar.gz: 014a4986c50d89dad1f932d8506d3171c776c91aedf83e959d6bab7e9a0d365255d31ce99aa960229189a959c1e744bb2c2ba889f46863677dd3abe4acfb2bb0
@@ -0,0 +1,35 @@
1
+ {
2
+ "extends": "eslint:recommended",
3
+ "rules": {
4
+ "semi": ["error", "always"],
5
+ "eqeqeq": ["error", "always"]
6
+ },
7
+ "globals": {
8
+ "window": true,
9
+ "document": true,
10
+ "WORKAREA": true,
11
+ "$": true,
12
+ "jQuery": true,
13
+ "_": true,
14
+ "feature": true,
15
+ "JST": true,
16
+ "Turbolinks": true,
17
+ "I18n": true,
18
+ "Chart": true,
19
+ "Dropzone": true,
20
+ "strftime": true,
21
+ "Waypoint": true,
22
+ "wysihtml": true,
23
+ "LocalTime": true,
24
+ "describe": true,
25
+ "after": true,
26
+ "afterEach": true,
27
+ "before": true,
28
+ "beforeEach": true,
29
+ "it": true,
30
+ "expect": true,
31
+ "sinon": true,
32
+ "fixture": true,
33
+ "chai": true
34
+ }
35
+ }
@@ -0,0 +1,60 @@
1
+ name: CI
2
+ on: [push]
3
+
4
+ jobs:
5
+ static_analysis:
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@v1
9
+ - uses: workarea-commerce/ci/bundler-audit@v1
10
+ - uses: workarea-commerce/ci/rubocop@v1
11
+ - uses: workarea-commerce/ci/eslint@v1
12
+ with:
13
+ args: '**/*.js'
14
+ - uses: workarea-commerce/ci/stylelint@v1
15
+ with:
16
+ args: '**/*.scss'
17
+
18
+ admin_tests:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v1
22
+ - uses: actions/setup-ruby@v1
23
+ with:
24
+ ruby-version: 2.6.x
25
+ - uses: workarea-commerce/ci/test@v1
26
+ with:
27
+ command: bin/rails app:workarea:test:admin
28
+
29
+ core_tests:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v1
33
+ - uses: actions/setup-ruby@v1
34
+ with:
35
+ ruby-version: 2.6.x
36
+ - uses: workarea-commerce/ci/test@v1
37
+ with:
38
+ command: bin/rails app:workarea:test:core
39
+
40
+ storefront_tests:
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v1
44
+ - uses: actions/setup-ruby@v1
45
+ with:
46
+ ruby-version: 2.6.x
47
+ - uses: workarea-commerce/ci/test@v1
48
+ with:
49
+ command: bin/rails app:workarea:test:storefront
50
+
51
+ plugins_tests:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v1
55
+ - uses: actions/setup-ruby@v1
56
+ with:
57
+ ruby-version: 2.6.x
58
+ - uses: workarea-commerce/ci/test@v1
59
+ with:
60
+ command: bin/rails app:workarea:test:plugins
data/.gitignore CHANGED
@@ -12,5 +12,4 @@ test/dummy/tmp/
12
12
  test/dummy/public/
13
13
  node_modules
14
14
  test/reports
15
- package.json
16
- yarn.lock
15
+ .rubocop-http*
@@ -0,0 +1,3 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/workarea-commerce/workarea/master/.rubocop.yml
3
+
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "stylelint-config-recommended-scss",
3
+ "rules": {
4
+ "block-no-empty": null,
5
+ "no-descending-specificity": null,
6
+ "property-no-unknown": [true, { "ignoreProperties": ["mso-hide"] }]
7
+ }
8
+ }
@@ -1,31 +1,13 @@
1
- Workarea Reviews 3.0.9 (2019-08-26)
1
+ Workarea Reviews 3.1.0 (2019-11-26)
2
2
  --------------------------------------------------------------------------------
3
3
 
4
- * Only try to add schedule job if service connections are not skipped
4
+ * Updates for v3.5 compatibility
5
5
 
6
+ Ben Crouse
6
7
 
8
+ * Initial commit on master
7
9
 
8
- Workarea Reviews 3.0.8 (2019-08-21)
9
- --------------------------------------------------------------------------------
10
-
11
- * Open Source!
12
-
13
-
14
-
15
- Workarea Reviews 3.0.7 (2019-06-11)
16
- --------------------------------------------------------------------------------
17
-
18
- * Add Rake Task for Reconciling Verified Purchasers
19
-
20
- Reviews can have a `:verified` badge associated with the content, but
21
- for those upgrading to a newer version some data needs to be changed in
22
- order to make this happen retroactively for older reviews. Add a Rake
23
- task for adding the `:verified` field to reviews where the user actually
24
- bought the product. For any reviews that are made after the upgrade,
25
- this will be automatically assigned as the review is being created.
26
-
27
- REVIEWS-146
28
- Tom Scott
10
+ Curt Howard
29
11
 
30
12
 
31
13
 
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
1
  source 'https://rubygems.org'
2
- git_source(:github) { |repo| "git@github.com:#{repo}.git" }
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
4
  gemspec
5
5
 
6
- gem 'workarea'
6
+ gem 'byebug'
7
+
8
+ gem 'workarea', github: 'workarea-commerce/workarea'
data/Rakefile CHANGED
@@ -28,16 +28,15 @@ desc "Release version #{Workarea::Reviews::VERSION} of the gem"
28
28
  task :release do
29
29
  host = "https://#{ENV['BUNDLE_GEMS__WEBLINC__COM']}@gems.weblinc.com"
30
30
 
31
- #Rake::Task['workarea:changelog'].execute
32
- #system 'git add CHANGELOG.md'
33
- #system 'git commit -m "Update CHANGELOG"'
34
- #system 'git push origin HEAD'
31
+ Rake::Task['workarea:changelog'].execute
32
+ system 'git add CHANGELOG.md'
33
+ system 'git commit -m "Update CHANGELOG"'
34
+ system 'git push origin HEAD'
35
35
 
36
36
  system "git tag -a v#{Workarea::Reviews::VERSION} -m 'Tagging #{Workarea::Reviews::VERSION}'"
37
37
  system 'git push --tags'
38
38
 
39
39
  system 'gem build workarea-reviews.gemspec'
40
- system "gem push workarea-reviews-#{Workarea::Reviews::VERSION}.gem"
41
40
  system "gem push workarea-reviews-#{Workarea::Reviews::VERSION}.gem --host #{host}"
42
41
  system "rm workarea-reviews-#{Workarea::Reviews::VERSION}.gem"
43
42
  end
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ decorate Admin::ReportsController, with: :reviews do
3
+ def reviews_by_product
4
+ @report = Admin::Reports::ReviewsByProductViewModel.wrap(
5
+ Workarea::Reports::ReviewsByProduct.new(params),
6
+ view_model_options
7
+ )
8
+ end
9
+ end
10
+ end
@@ -1,11 +1,12 @@
1
1
  module Workarea
2
2
  module Admin
3
3
  module ReviewsHelper
4
- def reviewer_info(model)
5
- return model.user_info unless model.user_id.present?
6
-
7
- link_to model.user_info,
8
- edit_user_path(model.user_id)
4
+ def reviews_report_filter_options
5
+ [
6
+ [t('workarea.admin.reports.reviews_by_product.filters.all'), nil],
7
+ [t('workarea.admin.reports.reviews_by_product.filters.approved'), 'approved'],
8
+ [t('workarea.admin.reports.reviews_by_product.filters.unapproved'), 'unapproved']
9
+ ]
9
10
  end
10
11
  end
11
12
  end
@@ -6,10 +6,8 @@ module Workarea
6
6
  empty_star_count = 5 - rating.ceil
7
7
  half_star_size = (rating % 1).round(2) * 100
8
8
  half_star_width = 20 + (half_star_size - 0) * (80.0 - 20) / (100.0 - 0)
9
- itemprop = options[:aggregate] ? 'aggregateRating' : 'reviewRating'
10
- itemtype = options[:aggregate] ? 'http://schema.org/AggregateRating' : 'http://schema.org/Rating'
11
9
 
12
- render 'workarea/storefront/products/rating', rating: rating, full_star_count: full_star_count, empty_star_count: empty_star_count, half_star_width: half_star_width, half_star_size: half_star_size, itemprop: itemprop, itemtype: itemtype
10
+ render 'workarea/storefront/products/rating', rating: rating, full_star_count: full_star_count, empty_star_count: empty_star_count, half_star_width: half_star_width, half_star_size: half_star_size
13
11
  end
14
12
 
15
13
  def display_purchase_requirement_message
@@ -0,0 +1,34 @@
1
+ module Workarea
2
+ module Storefront
3
+ module ReviewsSchemaOrgHelper
4
+ def product_schema(product, related_products: nil)
5
+ schema = super.merge(
6
+ 'review': product.reviews.map do |review|
7
+ {
8
+ '@type': 'Review',
9
+ 'author': review.user_info,
10
+ 'datePublished': review.created_at.strftime('%Y-%m-%d'),
11
+ 'description': review.body,
12
+ 'name': review.title,
13
+ 'reviewRating': {
14
+ '@type': 'Rating',
15
+ 'bestRating': '5',
16
+ 'worstRating': '1',
17
+ 'ratingValue': review.rating.round(2).to_s
18
+ }
19
+ }
20
+ end
21
+ )
22
+
23
+ if product.total_reviews > 0 && product.average_rating.present?
24
+ schema['aggregateRating'] = {
25
+ 'reviewCount': product.total_reviews.to_s,
26
+ 'ratingValue': product.average_rating.round(2).to_s
27
+ }
28
+ end
29
+
30
+ schema
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,7 +1,7 @@
1
1
  module Workarea
2
2
  decorate Admin::StatusReportMailer, with: :reviews do
3
- def report(email, date)
4
- @reviews_summary = ReviewSummary.new
3
+ def report(*)
4
+ @reviews_summary = ReviewSummary.new
5
5
  super
6
6
  end
7
7
  end
@@ -0,0 +1,34 @@
1
+ module Workarea
2
+ module Insights
3
+ class MostActiveReviewers < Base
4
+ class << self
5
+ def dashboards
6
+ %w(people marketing)
7
+ end
8
+
9
+ def generate_monthly!
10
+ results = generate_results
11
+ create!(results: results) if results.present?
12
+ end
13
+
14
+ def generate_results
15
+ report
16
+ .results
17
+ .select { |r| r['reviews'] > 0 }
18
+ .take(Workarea.config.insights_users_list_max_results)
19
+ .map { |result| result.merge(email: result['_id']) }
20
+ end
21
+
22
+ def report
23
+ Reports::ReviewsByUser.new(
24
+ starts_at: beginning_of_last_month,
25
+ ends_at: end_of_last_month,
26
+ sort_by: 'activity_score',
27
+ sort_direction: 'desc',
28
+ results_filter: 'approved'
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ module Workarea
2
+ module Insights
3
+ class MostReviewedProducts < Base
4
+ class << self
5
+ def dashboards
6
+ %w(marketing)
7
+ end
8
+
9
+ def generate_monthly!
10
+ results = generate_results
11
+ create!(results: results) if results.present?
12
+ end
13
+
14
+ def generate_results
15
+ report
16
+ .results
17
+ .select { |r| r['reviews'] > 0 }
18
+ .take(Workarea.config.insights_products_list_max_results)
19
+ .map { |result| result.merge(product_id: result['_id']) }
20
+ end
21
+
22
+ def report
23
+ Reports::ReviewsByProduct.new(
24
+ starts_at: beginning_of_last_month,
25
+ ends_at: end_of_last_month,
26
+ sort_by: 'reviews',
27
+ sort_direction: 'desc'
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Workarea
2
+ module Insights
3
+ class TopRatedProducts < Base
4
+ class << self
5
+ def dashboards
6
+ %w(marketing)
7
+ end
8
+
9
+ def generate_monthly!
10
+ results = generate_results
11
+ create!(results: results) if results.present?
12
+ end
13
+
14
+ def generate_results
15
+ report
16
+ .results
17
+ .select { |r| r['reviews'] > 0 }
18
+ .take(Workarea.config.insights_products_list_max_results)
19
+ .map { |result| result.merge(product_id: result['_id']) }
20
+ end
21
+
22
+ def report
23
+ Reports::ReviewsByProduct.new(
24
+ starts_at: beginning_of_last_month,
25
+ ends_at: end_of_last_month,
26
+ sort_by: 'weighted_average_rating',
27
+ sort_direction: 'desc'
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -95,23 +95,12 @@ module Workarea
95
95
  # @return [Float]
96
96
  #
97
97
  def self.find_sorting_score(product_id)
98
- reviews = find_for_product(product_id)
99
-
100
- votes = [ reviews.select { |r| r.rating == 1 }.length,
101
- reviews.select { |r| r.rating == 2 }.length,
102
- reviews.select { |r| r.rating == 3 }.length,
103
- reviews.select { |r| r.rating == 4 }.length,
104
- reviews.select { |r| r.rating == 5 }.length ]
105
-
106
- prior = [2, 2, 2, 2, 2]
107
-
108
- posterior = votes.zip(prior).map { |a, b| a + b }
109
- sum = posterior.inject { |a, b| a + b }
98
+ reviews = find_for_product(product_id).to_a
99
+ count = (1..5).each_with_object({}) do |i, memo|
100
+ memo[i] = reviews.select { |r| r.rating == i }.length + 2
101
+ end
110
102
 
111
- posterior.
112
- map.with_index { |v, i| (i + 1) * v }.
113
- inject { |a, b| a + b }.
114
- to_f / sum
103
+ count.sum { |rating, count| rating * count }.to_f / count.values.sum
115
104
  end
116
105
 
117
106
  def anonymous?
@@ -0,0 +1,86 @@
1
+ module Workarea
2
+ module Reports
3
+ class ReviewsByProduct
4
+ include Report
5
+
6
+ self.reporting_class = Review
7
+ self.sort_fields = %w(reviews verified average_rating weighted_average_rating)
8
+
9
+ def aggregation
10
+ [filter_results, project_used_fields, group_by_product, project_averages]
11
+ end
12
+
13
+ def filter_results
14
+ {
15
+ '$match' => {
16
+ 'created_at' => { '$gte' => starts_at, '$lte' => ends_at },
17
+ **approval_query
18
+ }
19
+ }
20
+ end
21
+
22
+ def project_used_fields
23
+ {
24
+ '$project' => {
25
+ 'product_id' => 1,
26
+ 'rating' => 1,
27
+ 'verified' => 1
28
+ }
29
+ }
30
+ end
31
+
32
+ def group_by_product
33
+ {
34
+ '$group' => {
35
+ '_id' => '$product_id',
36
+ 'reviews' => { '$sum' => 1 },
37
+ 'verified' => { '$sum' => { '$cond' => { 'if' => '$verified', 'then' => 1, 'else' => 0 } } },
38
+ 'rating_tally' => { '$sum' => '$rating' },
39
+ 'rated_5' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 5] }, 'then' => 1, 'else' => 0 } } },
40
+ 'rated_4' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 4] }, 'then' => 1, 'else' => 0 } } },
41
+ 'rated_3' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 3] }, 'then' => 1, 'else' => 0 } } },
42
+ 'rated_2' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 2] }, 'then' => 1, 'else' => 0 } } },
43
+ 'rated_1' => { '$sum' => { '$cond' => { 'if' => { '$eq' => ['$rating', 1] }, 'then' => 1, 'else' => 0 } } }
44
+ }
45
+ }
46
+ end
47
+
48
+ def project_averages
49
+ {
50
+ '$project' => {
51
+ 'product_id' => 1,
52
+ 'reviews' => 1,
53
+ 'verified' => 1,
54
+ 'average_rating' => { '$divide' => ['$rating_tally', '$reviews'] },
55
+ 'weighted_average_rating' => {
56
+ '$divide' => [
57
+ {
58
+ '$sum' => [
59
+ { '$multiply' => [{ '$sum' => [2, '$rated_5'] }, 5] },
60
+ { '$multiply' => [{ '$sum' => [2, '$rated_4'] }, 4] },
61
+ { '$multiply' => [{ '$sum' => [2, '$rated_3'] }, 3] },
62
+ { '$multiply' => [{ '$sum' => [2, '$rated_2'] }, 2] },
63
+ { '$multiply' => [{ '$sum' => [2, '$rated_1'] }, 1] }
64
+ ]
65
+ },
66
+ { '$sum' => [10, '$rated_5', '$rated_4', '$rated_3', '$rated_2', '$rated_1'] }
67
+ ]
68
+ }
69
+ }
70
+ }
71
+ end
72
+
73
+ private
74
+
75
+ def approval_query
76
+ if params[:results_filter] == 'approved'
77
+ { approved: true }
78
+ elsif params[:results_filter] == 'unapproved'
79
+ { approved: false }
80
+ else
81
+ {}
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end