Add new cases for reaction weights to lead to more effective rankings (#16138)
* Base author reaction functionality * Get logic more in place * Finalize tests * Add admin clause for new user points * Add proper registered_at for seeded user * Fix regsitered_at in e2e * Add registered_at across the board in e2e tests * Update comments * Update spec/models/reaction_spec.rb Co-authored-by: Jamie Gaskins <jamie@forem.com> * Update spec/models/reaction_spec.rb Co-authored-by: Jamie Gaskins <jamie@forem.com> * Update spec/models/reaction_spec.rb Co-authored-by: Jamie Gaskins <jamie@forem.com> * Put points in constant and refactor points resave logic Co-authored-by: Jamie Gaskins <jamie@forem.com>
This commit is contained in:
parent
6a69d9bc61
commit
96a935ffec
3 changed files with 79 additions and 2 deletions
|
|
@ -16,6 +16,9 @@ class Reaction < ApplicationRecord
|
|||
REACTABLE_TYPES = %w[Comment Article User].freeze
|
||||
STATUSES = %w[valid invalid confirmed archived].freeze
|
||||
|
||||
# Days to ramp up new user points weight
|
||||
NEW_USER_RAMPUP_DAYS_COUNT = 10
|
||||
|
||||
belongs_to :reactable, polymorphic: true
|
||||
belongs_to :user
|
||||
|
||||
|
|
@ -158,9 +161,19 @@ class Reaction < ApplicationRecord
|
|||
|
||||
def assign_points
|
||||
base_points = BASE_POINTS.fetch(category, 1.0)
|
||||
|
||||
# Ajust for certain states
|
||||
base_points = 0 if status == "invalid"
|
||||
base_points /= 2 if reactable_type == "User"
|
||||
base_points *= 2 if status == "confirmed"
|
||||
|
||||
unless persisted? # Actions we only want to apply upon initial creation
|
||||
# Author's comment reaction counts for more weight on to their own posts. (5.0 vs 1.0)
|
||||
base_points *= 5 if positive_reaction_to_comment_on_own_article?
|
||||
|
||||
# New users will have their reaction weight gradually ramp by 0.1 from 0 to 1.0.
|
||||
base_points *= new_user_adjusted_points if new_untrusted_user # New users get minimal reaction weight
|
||||
end
|
||||
self.points = user ? (base_points * user.reputation_modifier) : -5
|
||||
end
|
||||
|
||||
|
|
@ -179,4 +192,18 @@ class Reaction < ApplicationRecord
|
|||
def notify_slack_channel_about_vomit_reaction
|
||||
Slack::Messengers::ReactionVomit.call(reaction: self)
|
||||
end
|
||||
|
||||
def positive_reaction_to_comment_on_own_article?
|
||||
BASE_POINTS.fetch(category, 1.0).positive? &&
|
||||
reactable_type == "Comment" &&
|
||||
reactable&.commentable&.user_id == user_id
|
||||
end
|
||||
|
||||
def new_user_adjusted_points
|
||||
((Time.current - user.registered_at).seconds.in_days / NEW_USER_RAMPUP_DAYS_COUNT)
|
||||
end
|
||||
|
||||
def new_untrusted_user
|
||||
user.registered_at > NEW_USER_RAMPUP_DAYS_COUNT.days.ago && !user.trusted? && !user.any_admin?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Reaction, type: :model do
|
||||
let(:user) { create(:user) }
|
||||
let(:user) { create(:user, registered_at: 20.days.ago) }
|
||||
let(:article) { create(:article, user: user) }
|
||||
let(:reaction) { build(:reaction, reactable: article) }
|
||||
let(:reaction) { build(:reaction, reactable: article, user: user) }
|
||||
|
||||
describe "builtin validations" do
|
||||
subject { build(:reaction, reactable: article, user: user) }
|
||||
|
|
@ -74,12 +74,54 @@ RSpec.describe Reaction, type: :model do
|
|||
expect(reaction.points).to eq(0)
|
||||
end
|
||||
|
||||
it "assigns extra 5 points if reaction is to comment on author's post" do
|
||||
comment = create(:comment, commentable: article)
|
||||
comment_reaction = create(:reaction, reactable: comment, user: user)
|
||||
expect(comment_reaction.points).to eq(5.0)
|
||||
end
|
||||
|
||||
it "does not extra 5 points if reaction is to comment on author's post" do
|
||||
second_user = create(:user)
|
||||
second_article = create(:article, user: second_user)
|
||||
comment = create(:comment, commentable: second_article)
|
||||
comment_reaction = create(:reaction, reactable: comment, user: user)
|
||||
expect(comment_reaction.points).to eq(1)
|
||||
end
|
||||
|
||||
it "assigns the correct points if reaction is confirmed" do
|
||||
reaction_points = reaction.points
|
||||
reaction.update(status: "confirmed")
|
||||
expect(reaction.points).to eq(reaction_points * 2)
|
||||
end
|
||||
|
||||
it "assigns fractional points to new users on create" do
|
||||
newish_user = create(:user, registered_at: 3.days.ago)
|
||||
reaction = create(:reaction, reactable: article, user: newish_user)
|
||||
expect(reaction.points).to be_within(0.1).of(0.3)
|
||||
end
|
||||
|
||||
it "Does not assign new fractional logic on re-save" do
|
||||
reaction.save
|
||||
original_points = reaction.points
|
||||
reaction.user.update_column(:registered_at, 7.days.ago)
|
||||
reaction.save
|
||||
expect(reaction.points).to eq(original_points)
|
||||
end
|
||||
|
||||
it "assigns full points to new users who is also trusted" do
|
||||
newish_user = create(:user, registered_at: 3.days.ago)
|
||||
newish_user.add_role(:trusted)
|
||||
create(:reaction, reactable: article, user: newish_user)
|
||||
expect(reaction.points).to be_within(0.1).of(1.0)
|
||||
end
|
||||
|
||||
it "assigns full points to new users who is admin" do
|
||||
newish_user = create(:user, registered_at: 3.days.ago)
|
||||
newish_user.add_role(:admin)
|
||||
create(:reaction, reactable: article, user: newish_user)
|
||||
expect(reaction.points).to be_within(0.1).of(1.0)
|
||||
end
|
||||
|
||||
context "when user is trusted" do
|
||||
before { reaction.user.add_role(:trusted) }
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
|
|||
username: "Admin_McAdmin",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -76,6 +77,7 @@ seeder.create_if_doesnt_exist(User, "email", "trusted-user-1@forem.local") do
|
|||
username: "trusted_user_1",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -120,6 +122,7 @@ seeder.create_if_doesnt_exist(User, "email", "change-password-user@forem.com") d
|
|||
username: "changepassworduser",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -146,6 +149,7 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v1-user@forem.local
|
|||
username: "article_editor_v1_user",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -173,6 +177,7 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v2-user@forem.local
|
|||
username: "article_editor_v2_user",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -199,6 +204,7 @@ seeder.create_if_doesnt_exist(User, "email", "notifications-user@forem.local") d
|
|||
username: "notifications_user",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -271,6 +277,7 @@ seeder.create_if_doesnt_exist(User, "email", "liquid-tags-user@forem.local") do
|
|||
username: "liquid_tags_user",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
@ -431,6 +438,7 @@ seeder.create_if_doesnt_exist(User, "email", "series-user@forem.local") do
|
|||
username: "series_user",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
confirmed_at: Time.current,
|
||||
registered_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue