The "Best Of" mode uses a percentage ranking of posts.

This commit is contained in:
Robin Ward 2013-03-22 15:43:57 -04:00
parent ab412dd8b4
commit 9c38c13ac5
8 changed files with 49 additions and 20 deletions

View File

@ -157,7 +157,7 @@ class Post < ActiveRecord::Base
end
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
def update_flagged_posts_count

View File

@ -106,6 +106,7 @@ class SiteSetting < ActiveRecord::Base
setting(:best_of_score_threshold, 15)
setting(:best_of_posts_required, 50)
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
setting(:notification_email, 'info@discourse.org')

View File

@ -329,6 +329,7 @@ en:
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_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_long_polling: "Message bus used for notification can use long polling"

View File

@ -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+/}
# 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+/}
put 't/:slug/:topic_id' => 'topics#update', :constraints => {:topic_id => /\d+/}
put 't/:slug/:topic_id/star' => 'topics#star', :constraints => {:topic_id => /\d+/}

View File

@ -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

View File

@ -21,6 +21,15 @@ class ScoreCalculator
# First update the scores of the posts
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
exec_sql "
UPDATE topics SET has_best_of =
@ -41,7 +50,7 @@ class ScoreCalculator
private
def exec_sql(sql, params)
def exec_sql(sql, params=nil)
ActiveRecord::Base.exec_sql(sql, params)
end

View File

@ -3,19 +3,25 @@ require 'score_calculator'
describe ScoreCalculator do
before do
@post = Fabricate(:post, reads: 111)
@topic = @post.topic
end
let!(:post) { Fabricate(:post, reads: 111) }
let!(:another_post) { Fabricate(:post, topic: post.topic, reads: 222) }
let(:topic) { post.topic }
context 'with weightings' do
before do
ScoreCalculator.new(reads: 3).calculate
@post.reload
post.reload
another_post.reload
end
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
@ -23,15 +29,15 @@ describe ScoreCalculator do
it "won't update the site settings when the site settings don't match" do
ScoreCalculator.new(reads: 3).calculate
@topic.reload
@topic.has_best_of.should be_false
topic.reload
topic.has_best_of.should be_false
end
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
@topic.reload
@topic.has_best_of.should be_false
topic.reload
topic.has_best_of.should be_false
end
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)
ScoreCalculator.new(reads: 3).calculate
@topic.reload
@topic.has_best_of.should be_true
topic.reload
topic.has_best_of.should be_true
end
end

View File

@ -628,12 +628,12 @@ describe Post do
end
context 'best_of' do
let!(:p1) { Fabricate(:post, post_args.merge(score: 4)) }
let!(:p2) { Fabricate(:post, post_args.merge(score: 10)) }
let!(:p3) { Fabricate(:post, post_args.merge(score: 5)) }
let!(:p1) { Fabricate(:post, post_args.merge(score: 4, percent_rank: 0.33)) }
let!(:p2) { Fabricate(:post, post_args.merge(score: 10, percent_rank: 0.66)) }
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
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]
end