workarea-reviews 3.0.8 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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 +24 -12
  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/{reviews.rb → workarea/review_seeds.rb} +17 -6
  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/view_models/workarea/storefront/review_view_model.rb +9 -3
  25. data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml +17 -0
  26. data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml +22 -0
  27. data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml +22 -0
  28. data/app/views/workarea/admin/insights/_top_rated_products.html.haml +22 -0
  29. data/app/views/workarea/admin/reports/reviews_by_product.html.haml +44 -0
  30. data/app/views/workarea/storefront/products/_rating.html.haml +1 -2
  31. data/app/views/workarea/storefront/products/_reviews.html.haml +5 -5
  32. data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +2 -2
  33. data/app/views/workarea/storefront/review_mailer/review_request.html.haml +2 -0
  34. data/app/views/workarea/storefront/review_requests/show.html.haml +2 -1
  35. data/config/initializers/append_points.rb +5 -0
  36. data/config/initializers/configuration.rb +3 -1
  37. data/config/initializers/scheduled_jobs.rb +8 -6
  38. data/config/locales/en.yml +46 -0
  39. data/config/routes.rb +4 -0
  40. data/lib/workarea/reviews/engine.rb +4 -0
  41. data/lib/workarea/reviews/version.rb +1 -1
  42. data/package.json +9 -0
  43. data/test/dummy/config/initializers/session_store.rb +3 -1
  44. data/test/helpers/workarea/storefront/reviews_helper_test.rb +1 -1
  45. data/test/integration/workarea/admin/reviews_integration_test.rb +5 -1
  46. data/test/models/workarea/insights/most_active_reviewers_test.rb +32 -0
  47. data/test/models/workarea/insights/most_reviewed_products_test.rb +32 -0
  48. data/test/models/workarea/insights/top_rated_products_test.rb +49 -0
  49. data/test/models/workarea/review_test.rb +29 -18
  50. data/test/queries/workarea/reports/reviews_by_product_test.rb +104 -0
  51. data/test/queries/workarea/reports/reviews_by_user_test.rb +102 -0
  52. data/test/system/workarea/admin/reviews_by_product_system_test.rb +60 -0
  53. data/test/view_models/workarea/storefront/review_view_model_test.rb +46 -0
  54. data/workarea-reviews.gemspec +1 -1
  55. data/yarn.lock +3265 -0
  56. metadata +33 -8
  57. data/app/controllers/workarea/admin/import_reviews_controller.rb +0 -35
  58. 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: f30df5849c1b3184fa2814e460c4b3cdbec61c6722624c28b237a5cd0215f8b4
4
- data.tar.gz: 13ada10897237548b1401d7ae6dba2d2c485ceba45b318a3bbc56812a36551d1
3
+ metadata.gz: a07fba40d273a1fd46dfa4575601eefe0eb742fafe6da2be58c2ca07fba3b51f
4
+ data.tar.gz: 9a5f40859ec940937ac073bfc976ea1e899d05864f3cdf2e2b261b25a2963410
5
5
  SHA512:
6
- metadata.gz: 6328e092e60c5d984c60a55cb497ec98ac1f53f255d40c503a39bcd8eca60e567eae12fa77a9ec61580170b85fec3bd5336da8da89354313bda762eac4e5fbaf
7
- data.tar.gz: '0944261e9811b0695b546ec3cc38600f6f05fbba9a672f03b4e2c0e0e1f1964a516334e568050104002f10099a21d6852ac40256aaeee35fb1373728c8244656'
6
+ metadata.gz: 49005ddc4805c46ff4bcb725a1c6b30d53f616b912880c6c64404c57334e31281824ea1fe3109204981fbec7db255bc327be890d2fd536ef17a9a80e42e9efb6
7
+ data.tar.gz: 27fc7ae007ceb0b676576a8a93e270869aa8c362d76054e15faa0863911be3872eed903604132d15382fdd52cdc5f251f21ed514b6a515a4b93c24187d2290ee
@@ -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,24 +1,36 @@
1
- Workarea Reviews 3.0.8 (2019-08-21)
1
+ Workarea Reviews 3.1.2 (2020-10-30)
2
2
  --------------------------------------------------------------------------------
3
3
 
4
- * Open Source!
4
+ * Handle reviews with user_ids that do not match a user
5
5
 
6
+ REVIEWS-6
7
+
8
+ Matt Duffy
6
9
 
7
10
 
8
- Workarea Reviews 3.0.7 (2019-06-11)
11
+
12
+ Workarea Reviews 3.1.1 (2020-01-07)
9
13
  --------------------------------------------------------------------------------
10
14
 
11
- * Add Rake Task for Reconciling Verified Purchasers
15
+ * Fix failing reviews helper test
12
16
 
13
- Reviews can have a `:verified` badge associated with the content, but
14
- for those upgrading to a newer version some data needs to be changed in
15
- order to make this happen retroactively for older reviews. Add a Rake
16
- task for adding the `:verified` field to reviews where the user actually
17
- bought the product. For any reviews that are made after the upgrade,
18
- this will be automatically assigned as the review is being created.
17
+ Idk how we didn't catch this, like, 6 years ago.
19
18
 
20
- REVIEWS-146
21
- Tom Scott
19
+ REVIEWS-5
20
+ Curt Howard
21
+
22
+
23
+
24
+ Workarea Reviews 3.1.0 (2019-11-26)
25
+ --------------------------------------------------------------------------------
26
+
27
+ * Updates for v3.5 compatibility
28
+
29
+ Ben Crouse
30
+
31
+ * Initial commit on master
32
+
33
+ Curt Howard
22
34
 
23
35
 
24
36
 
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', branch: 'v3.5-stable'
data/Rakefile CHANGED
@@ -28,13 +28,12 @@ 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"'
35
34
 
36
35
  system "git tag -a v#{Workarea::Reviews::VERSION} -m 'Tagging #{Workarea::Reviews::VERSION}'"
37
- system 'git push --tags'
36
+ system 'git push origin HEAD --follow-tags'
38
37
 
39
38
  system 'gem build workarea-reviews.gemspec'
40
39
  system "gem push workarea-reviews-#{Workarea::Reviews::VERSION}.gem"
@@ -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