FEATURE: Weighted reviewable user accuracy (#8156)

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

diff --git a/app/models/reviewable_score.rb b/app/models/reviewable_score.rb
index 823e893..e51af3c 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 abf717e..03c9616 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

GitHub sha: 868303e5

1 Like