From 868303e5d9451dfc0057bd08703df9b8ee93f1d0 Mon Sep 17 00:00:00 2001 From: Mark VanLandingham Date: Fri, 4 Oct 2019 14:10:56 -0500 Subject: [PATCH] FEATURE: Weighted reviewable user accuracy (#8156) * FEATURE: Inaccurate users have negative review accuracy * FIX: disallow negative reviewable score even if the accuracy would make it negative --- app/models/reviewable_score.rb | 19 +++++++++-- spec/models/reviewable_score_spec.rb | 51 ++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/app/models/reviewable_score.rb b/app/models/reviewable_score.rb index 823e8935ce0..e51af3c0a2d 100644 --- a/app/models/reviewable_score.rb +++ b/app/models/reviewable_score.rb @@ -49,7 +49,8 @@ class ReviewableScore < ActiveRecord::Base # 1.0 + trust_level + user_accuracy_bonus # (trust_level is 5 for staff) def self.user_flag_score(user) - 1.0 + (user.staff? ? 5.0 : user.trust_level.to_f) + user_accuracy_bonus(user) + score = 1.0 + (user.staff? ? 5.0 : user.trust_level.to_f) + user_accuracy_bonus(user) + score >= 0 ? score : 0 end # A user's accuracy bonus is: @@ -68,8 +69,22 @@ class ReviewableScore < ActiveRecord::Base total = (agreed + disagreed).to_f return 0.0 if total <= 5 + accuracy_axis = 0.7 - (agreed / total) * 5.0 + percent_correct = agreed / total + positive_accuracy = percent_correct >= accuracy_axis + + bottom = positive_accuracy ? accuracy_axis : 0.0 + top = positive_accuracy ? 1.0 : accuracy_axis + + absolute_distance = positive_accuracy ? + percent_correct - bottom : + top - percent_correct + + axis_distance_multiplier = 1.0 / (top - bottom) + positivity_multiplier = positive_accuracy ? 1.0 : -1.0 + + absolute_distance * axis_distance_multiplier * positivity_multiplier * (Math.log(total, 4) * 5.0) end def reviewable_conversation diff --git a/spec/models/reviewable_score_spec.rb b/spec/models/reviewable_score_spec.rb index abf717e08f2..03c96163323 100644 --- a/spec/models/reviewable_score_spec.rb +++ b/spec/models/reviewable_score_spec.rb @@ -102,16 +102,48 @@ RSpec.describe ReviewableScore, type: :model do expect(ReviewableScore.user_accuracy_bonus(user)).to eq(0.0) end - it "returns (agreed_flags / total) * 5.0" do - user_stat.flags_agreed = 4 - user_stat.flags_disagreed = 2 - expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(3.33) + it "returns the users weighted accuracy bonus" do + user_stat.flags_agreed = 10 + user_stat.flags_disagreed = 42 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(-10.34) + + user_stat.flags_agreed = 2 + user_stat.flags_disagreed = 12 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(-7.58) + + user_stat.flags_agreed = 1 + user_stat.flags_disagreed = 6 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(-5.59) + + user_stat.flags_agreed = 2 + user_stat.flags_disagreed = 4 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(-3.39) + + user_stat.flags_agreed = 7 + user_stat.flags_disagreed = 3 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(0) + + user_stat.flags_agreed = 14 + user_stat.flags_disagreed = 6 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(0) # Ignored flags don't count user_stat.flags_agreed = 121 user_stat.flags_disagreed = 44 user_stat.flags_ignored = 4 - expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(3.66) + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(2.04) + + user_stat.flags_agreed = 9 + user_stat.flags_disagreed = 2 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(3.40) + + user_stat.flags_agreed = 25 + user_stat.flags_disagreed = 4 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(6.56) + + user_stat.flags_agreed = 120 + user_stat.flags_disagreed = 12 + expect(ReviewableScore.user_accuracy_bonus(user).floor(2)).to eq(12.27) end end @@ -138,7 +170,14 @@ RSpec.describe ReviewableScore, type: :model do user_stat.flags_agreed = 12 user_stat.flags_disagreed = 2 user_stat.flags_ignored = 2 - expect(ReviewableScore.user_flag_score(user).floor(2)).to eq(7.28) + expect(ReviewableScore.user_flag_score(user).floor(2)).to eq(7.98) + end + + it 'return 0 if the accuracy_bonus would make the score negative' do + user.trust_level = 3 + user_stat.flags_agreed = 0 + user_stat.flags_disagreed = 1000 + expect(ReviewableScore.user_flag_score(user)).to eq(0) end end end