FEATURE: make top criteria formula multipliers controlled by site

setting
This commit is contained in:
Arpit Jalan 2016-01-20 22:06:18 +05:30
parent a601d4b4ba
commit 1cee914565
4 changed files with 126 additions and 5 deletions

View File

@ -147,6 +147,15 @@ class TopTopic < ActiveRecord::Base
def self.compute_top_score_for(period)
log_views_multiplier = SiteSetting.top_topics_formula_log_views_multiplier.to_f
log_views_multiplier = 2 if log_views_multiplier == 0
first_post_likes_multiplier = SiteSetting.top_topics_formula_first_post_likes_multiplier.to_f
first_post_likes_multiplier = 0.5 if first_post_likes_multiplier == 0
least_likes_per_post_multiplier = SiteSetting.top_topics_formula_least_likes_per_post_multiplier.to_f
least_likes_per_post_multiplier = 3 if least_likes_per_post_multiplier == 0
if period == :all
top_topics = "(
SELECT t.like_count all_likes_count,
@ -167,11 +176,11 @@ class TopTopic < ActiveRecord::Base
WITH top AS (
SELECT CASE
WHEN #{time_filter} THEN 0
ELSE log(GREATEST(#{period}_views_count, 1)) * 2 +
#{period}_op_likes_count * 0.5 +
ELSE log(GREATEST(#{period}_views_count, 1)) * #{log_views_multiplier} +
#{period}_op_likes_count * #{first_post_likes_multiplier} +
CASE WHEN #{period}_likes_count > 0 AND #{period}_posts_count > 0
THEN
LEAST(#{period}_likes_count / #{period}_posts_count, 3)
LEAST(#{period}_likes_count / #{period}_posts_count, #{least_likes_per_post_multiplier})
ELSE 0
END +
CASE WHEN topics.posts_count < 10 THEN

View File

@ -945,6 +945,10 @@ en:
verbose_localization: "Show extended localization tips in the UI"
previous_visit_timeout_hours: "How long a visit lasts before we consider it the 'previous' visit, in hours"
top_topics_formula_log_views_multiplier: "value of log views multiplier (n) in top topics formula: `log(views_count) * (n) + op_likes_count * 0.5 + LEAST(likes_count / posts_count, 3) + 10 + log(posts_count)`"
top_topics_formula_first_post_likes_multiplier: "value of first post likes multiplier (n) in top topics formula: `log(views_count) * 2 + op_likes_count * (n) + LEAST(likes_count / posts_count, 3) + 10 + log(posts_count)`"
top_topics_formula_least_likes_per_post_multiplier: "value of least likes per post multiplier (n) in top topics formula: `log(views_count) * 2 + op_likes_count * 0.5 + LEAST(likes_count / posts_count, (n)) + 10 + log(posts_count)`"
rate_limit_create_topic: "After creating a topic, users must wait (n) seconds before creating another topic."
rate_limit_create_post: "After posting, users must wait (n) seconds before creating another post."
rate_limit_new_user_create_topic: "After creating a topic, new users must wait (n) seconds before creating another topic."

View File

@ -803,6 +803,15 @@ developer:
verbose_localization:
default: false
client: true
top_topics_formula_log_views_multiplier:
default: 2
min: 0
top_topics_formula_first_post_likes_multiplier:
default: 0.5
min: 0
top_topics_formula_least_likes_per_post_multiplier:
default: 3
min: 0
migrate_to_new_scheme:
hidden: true
default: false

View File

@ -38,9 +38,108 @@ describe TopTopic do
it "should have top topics" do
expect(TopTopic.pluck(:topic_id)).to match_array([t1.id, t2.id])
end
end
end
describe "#compute_top_score_for" do
let(:user) { Fabricate(:user) }
let(:coding_horror) { Fabricate(:coding_horror) }
let!(:topic_1) { Fabricate(:topic, posts_count: 10, like_count: 28) }
let!(:t1_post_1) { Fabricate(:post, topic: topic_1, like_count: 28, post_number: 1)}
let!(:topic_2) { Fabricate(:topic, posts_count: 10, like_count: 20) }
let!(:t2_post_1) { Fabricate(:post, topic: topic_2, like_count: 10, post_number: 1) }
let!(:t2_post_2) { Fabricate(:post, topic: topic_2, like_count: 10) }
let!(:topic_3) { Fabricate(:topic, posts_count: 10) }
let!(:t3_post_1) { Fabricate(:post, topic_id: topic_3.id) }
let!(:t3_view_1) { TopicViewItem.add(topic_3.id, '127.0.0.1', user) }
let!(:t3_view_2) { TopicViewItem.add(topic_3.id, '127.0.0.2', coding_horror) }
# Note: all topics has 10 posts so we can skip "0 - ((10 - topics.posts_count) / 20) * #{period}_op_likes_count" calculation
it "should compute top score" do
# Default Formula: log(views_count) * {2} + op_likes_count * {0.5} + LEAST(likes_count / posts_count, {3}) + 10 + log(posts_count)
#
# topic_1 => 0 + 14 + 3 + 10 + 0 => 27
# topic_2 => 0 + 5 + 3 + 10 + 0.301029995664 => 18.301029995664
# topic_3 => 0.602059991328 + 0 + 0 + 10 + 0 => 10.602059991328
TopTopic.refresh!
top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(27)
expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(18.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328)
# when 'top_topics_formula_log_views_multiplier' setting is changed
SiteSetting.top_topics_formula_log_views_multiplier = 4
SiteSetting.top_topics_formula_first_post_likes_multiplier = 0.5 # unchanged
SiteSetting.top_topics_formula_least_likes_per_post_multiplier = 3 # unchanged
# New Formula: log(views_count) * {4} + op_likes_count * {0.5} + LEAST(likes_count / posts_count, {3}) + 10 + log(posts_count)
#
# topic_1 => 0 + 14 + 3 + 10 + 0 => 27
# topic_2 => 0 + 5 + 3 + 10 + 0.301029995664 => 18.301029995664
# topic_3 => 1.2041199826559 + 0 + 0 + 10 + 0 => 11.2041199826559
TopTopic.refresh!
top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(27)
expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(18.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(11.2041199826559)
# when 'top_topics_formula_first_post_likes_multiplier' setting is changed
SiteSetting.top_topics_formula_log_views_multiplier = 2 # unchanged
SiteSetting.top_topics_formula_first_post_likes_multiplier = 2
SiteSetting.top_topics_formula_least_likes_per_post_multiplier = 3 # unchanged
# New Formula: log(views_count) * {2} + op_likes_count * {2} + LEAST(likes_count / posts_count, {3}) + 10 + log(posts_count)
#
# topic_1 => 0 + 56 + 3 + 10 + 0 => 69
# topic_2 => 0 + 20 + 3 + 10 + 0.301029995664 => 33.301029995664
# topic_3 => 0.602059991328 + 0 + 0 + 10 + 0 => 10.602059991328
TopTopic.refresh!
top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(69)
expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(33.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328)
# when 'top_topics_formula_least_likes_per_post_multiplier' setting is changed
SiteSetting.top_topics_formula_log_views_multiplier = 2 # unchanged
SiteSetting.top_topics_formula_first_post_likes_multiplier = 0.5 # unchanged
SiteSetting.top_topics_formula_least_likes_per_post_multiplier = 6
# New Formula: log(views_count) * {2} + op_likes_count * {0.5} + LEAST(likes_count / posts_count, {6}) + 10 + log(posts_count)
#
# topic_1 => 0 + 14 + 6 + 10 + 0 => 30
# topic_2 => 0 + 5 + 6 + 10 + 0.301029995664 => 21.301029995664
# topic_3 => 0.602059991328 + 0 + 0 + 10 + 0 => 10.602059991328
TopTopic.refresh!
top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(30)
expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(21.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328)
# handles invalid string value
SiteSetting.top_topics_formula_log_views_multiplier = "not good"
SiteSetting.top_topics_formula_first_post_likes_multiplier = "not good"
SiteSetting.top_topics_formula_least_likes_per_post_multiplier = "not good"
TopTopic.refresh!
top_topics = TopTopic.all
expect(top_topics.where(topic_id: topic_1.id).pluck(:yearly_score).first).to eq(27)
expect(top_topics.where(topic_id: topic_2.id).pluck(:yearly_score).first).to eq(18.301029995664)
expect(top_topics.where(topic_id: topic_3.id).pluck(:yearly_score).first).to eq(10.602059991328)
end
end
end