The "Best Of" mode uses a percentage ranking of posts.
This commit is contained in:
parent
ab412dd8b4
commit
9c38c13ac5
|
@ -157,7 +157,7 @@ class Post < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.best_of
|
def self.best_of
|
||||||
where("(post_number = 1) or (score >= ?)", SiteSetting.best_of_score_threshold)
|
where(["(post_number = 1) or (percent_rank <= ?)", SiteSetting.best_of_percent_filter.to_f / 100.0])
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_flagged_posts_count
|
def update_flagged_posts_count
|
||||||
|
|
|
@ -106,6 +106,7 @@ class SiteSetting < ActiveRecord::Base
|
||||||
setting(:best_of_score_threshold, 15)
|
setting(:best_of_score_threshold, 15)
|
||||||
setting(:best_of_posts_required, 50)
|
setting(:best_of_posts_required, 50)
|
||||||
setting(:best_of_likes_required, 1)
|
setting(:best_of_likes_required, 1)
|
||||||
|
setting(:best_of_percent_filter, 5)
|
||||||
|
|
||||||
# we need to think of a way to force users to enter certain settings, this is a minimal config thing
|
# we need to think of a way to force users to enter certain settings, this is a minimal config thing
|
||||||
setting(:notification_email, 'info@discourse.org')
|
setting(:notification_email, 'info@discourse.org')
|
||||||
|
|
|
@ -329,6 +329,7 @@ en:
|
||||||
best_of_score_threshold: "The minimum score of a post to be included in the 'best of'"
|
best_of_score_threshold: "The minimum score of a post to be included in the 'best of'"
|
||||||
best_of_posts_required: "Minimum posts in a topic before 'best of' mode is enabled"
|
best_of_posts_required: "Minimum posts in a topic before 'best of' mode is enabled"
|
||||||
best_of_likes_required: "Minimum likes in a topic before the 'best of' mode will be enabled"
|
best_of_likes_required: "Minimum likes in a topic before the 'best of' mode will be enabled"
|
||||||
|
best_of_percent_filter: "When a user clicks best of, show the top % of posts"
|
||||||
enable_private_messages: "Allow trust level 1 users to create private messages and conversations"
|
enable_private_messages: "Allow trust level 1 users to create private messages and conversations"
|
||||||
|
|
||||||
enable_long_polling: "Message bus used for notification can use long polling"
|
enable_long_polling: "Message bus used for notification can use long polling"
|
||||||
|
|
|
@ -171,7 +171,7 @@ Discourse::Application.routes.draw do
|
||||||
get 'threads/:topic_id/:post_number/avatar' => 'topics#avatar', :constraints => {:topic_id => /\d+/, :post_number => /\d+/}
|
get 'threads/:topic_id/:post_number/avatar' => 'topics#avatar', :constraints => {:topic_id => /\d+/, :post_number => /\d+/}
|
||||||
|
|
||||||
# Topic routes
|
# Topic routes
|
||||||
get 't/:slug/:topic_id/best_of' => 'topics#show', :constraints => {:topic_id => /\d+/, :post_number => /\d+/}
|
get 't/:slug/:topic_id/best_of' => 'topics#show', :defaults => {best_of: true}, :constraints => {:topic_id => /\d+/, :post_number => /\d+/}
|
||||||
get 't/:topic_id/best_of' => 'topics#show', :constraints => {:topic_id => /\d+/, :post_number => /\d+/}
|
get 't/:topic_id/best_of' => 'topics#show', :constraints => {:topic_id => /\d+/, :post_number => /\d+/}
|
||||||
put 't/:slug/:topic_id' => 'topics#update', :constraints => {:topic_id => /\d+/}
|
put 't/:slug/:topic_id' => 'topics#update', :constraints => {:topic_id => /\d+/}
|
||||||
put 't/:slug/:topic_id/star' => 'topics#star', :constraints => {:topic_id => /\d+/}
|
put 't/:slug/:topic_id/star' => 'topics#star', :constraints => {:topic_id => /\d+/}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
class AddPercentRankToPosts < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :posts, :percent_rank, :float, default: 1.0
|
||||||
|
|
||||||
|
execute "UPDATE posts SET percent_rank = x.percent_rank
|
||||||
|
FROM (SELECT id, percent_rank()
|
||||||
|
OVER (PARTITION BY topic_id ORDER BY SCORE DESC)
|
||||||
|
FROM posts) AS x
|
||||||
|
WHERE x.id = posts.id"
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -21,6 +21,15 @@ class ScoreCalculator
|
||||||
# First update the scores of the posts
|
# First update the scores of the posts
|
||||||
exec_sql(post_score_sql, @weightings)
|
exec_sql(post_score_sql, @weightings)
|
||||||
|
|
||||||
|
# Update the percent rankings of the posts
|
||||||
|
|
||||||
|
exec_sql("UPDATE posts SET percent_rank = x.percent_rank
|
||||||
|
FROM (SELECT id, percent_rank()
|
||||||
|
OVER (PARTITION BY topic_id ORDER BY SCORE DESC) as percent_rank
|
||||||
|
FROM posts) AS x
|
||||||
|
WHERE x.id = posts.id")
|
||||||
|
|
||||||
|
|
||||||
# Update the best of flag
|
# Update the best of flag
|
||||||
exec_sql "
|
exec_sql "
|
||||||
UPDATE topics SET has_best_of =
|
UPDATE topics SET has_best_of =
|
||||||
|
@ -41,7 +50,7 @@ class ScoreCalculator
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def exec_sql(sql, params)
|
def exec_sql(sql, params=nil)
|
||||||
ActiveRecord::Base.exec_sql(sql, params)
|
ActiveRecord::Base.exec_sql(sql, params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,19 +3,25 @@ require 'score_calculator'
|
||||||
|
|
||||||
describe ScoreCalculator do
|
describe ScoreCalculator do
|
||||||
|
|
||||||
before do
|
let!(:post) { Fabricate(:post, reads: 111) }
|
||||||
@post = Fabricate(:post, reads: 111)
|
let!(:another_post) { Fabricate(:post, topic: post.topic, reads: 222) }
|
||||||
@topic = @post.topic
|
let(:topic) { post.topic }
|
||||||
end
|
|
||||||
|
|
||||||
context 'with weightings' do
|
context 'with weightings' do
|
||||||
before do
|
before do
|
||||||
ScoreCalculator.new(reads: 3).calculate
|
ScoreCalculator.new(reads: 3).calculate
|
||||||
@post.reload
|
post.reload
|
||||||
|
another_post.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'takes the supplied weightings into effect' do
|
it 'takes the supplied weightings into effect' do
|
||||||
@post.score.should == 333
|
post.score.should == 333
|
||||||
|
another_post.score.should == 666
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates the percent_ranks" do
|
||||||
|
another_post.percent_rank.should == 0.0
|
||||||
|
post.percent_rank.should == 1.0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,15 +29,15 @@ describe ScoreCalculator do
|
||||||
|
|
||||||
it "won't update the site settings when the site settings don't match" do
|
it "won't update the site settings when the site settings don't match" do
|
||||||
ScoreCalculator.new(reads: 3).calculate
|
ScoreCalculator.new(reads: 3).calculate
|
||||||
@topic.reload
|
topic.reload
|
||||||
@topic.has_best_of.should be_false
|
topic.has_best_of.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "removes the best_of flag if the topic no longer qualifies" do
|
it "removes the best_of flag if the topic no longer qualifies" do
|
||||||
@topic.update_column(:has_best_of, true)
|
topic.update_column(:has_best_of, true)
|
||||||
ScoreCalculator.new(reads: 3).calculate
|
ScoreCalculator.new(reads: 3).calculate
|
||||||
@topic.reload
|
topic.reload
|
||||||
@topic.has_best_of.should be_false
|
topic.has_best_of.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "won't update the site settings when the site settings don't match" do
|
it "won't update the site settings when the site settings don't match" do
|
||||||
|
@ -40,8 +46,8 @@ describe ScoreCalculator do
|
||||||
SiteSetting.expects(:best_of_score_threshold).returns(100)
|
SiteSetting.expects(:best_of_score_threshold).returns(100)
|
||||||
|
|
||||||
ScoreCalculator.new(reads: 3).calculate
|
ScoreCalculator.new(reads: 3).calculate
|
||||||
@topic.reload
|
topic.reload
|
||||||
@topic.has_best_of.should be_true
|
topic.has_best_of.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -628,12 +628,12 @@ describe Post do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'best_of' do
|
context 'best_of' do
|
||||||
let!(:p1) { Fabricate(:post, post_args.merge(score: 4)) }
|
let!(:p1) { Fabricate(:post, post_args.merge(score: 4, percent_rank: 0.33)) }
|
||||||
let!(:p2) { Fabricate(:post, post_args.merge(score: 10)) }
|
let!(:p2) { Fabricate(:post, post_args.merge(score: 10, percent_rank: 0.66)) }
|
||||||
let!(:p3) { Fabricate(:post, post_args.merge(score: 5)) }
|
let!(:p3) { Fabricate(:post, post_args.merge(score: 5, percent_rank: 0.99)) }
|
||||||
|
|
||||||
it "returns the OP and posts above the threshold in best of mode" do
|
it "returns the OP and posts above the threshold in best of mode" do
|
||||||
SiteSetting.stubs(:best_of_score_threshold).returns(10)
|
SiteSetting.stubs(:best_of_percent_filter).returns(66)
|
||||||
Post.best_of.order(:post_number).should == [p1, p2]
|
Post.best_of.order(:post_number).should == [p1, p2]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue