get regular trust level going, self heal inconsistent topic timings

This commit is contained in:
Sam 2013-04-05 15:29:46 +11:00
parent 6aaa0ffe5e
commit 4fbf017272
14 changed files with 236 additions and 9 deletions

View File

@ -152,6 +152,14 @@ class SiteSetting < ActiveRecord::Base
setting(:basic_requires_read_posts, 50)
setting(:basic_requires_time_spent_mins, 15)
setting(:regular_requires_topics_entered, 3)
setting(:regular_requires_read_posts, 100)
setting(:regular_requires_time_spent_mins, 60)
setting(:regular_requires_days_visited, 15)
setting(:regular_requires_likes_received, 1)
setting(:regular_requires_likes_given, 1)
setting(:regular_requires_topic_reply_count, 3)
# Entropy checks
setting(:title_min_entropy, 10)
setting(:body_min_entropy, 7)

View File

@ -171,4 +171,24 @@ class TopicUser < ActiveRecord::Base
end
def self.ensure_consistency!
exec_sql <<SQL
UPDATE topic_users t
SET
last_read_post_number = last_read,
seen_post_count = post_count
FROM (
SELECT topic_id, user_id, COUNT(*) post_count, MAX(post_number) last_read
FROM post_timings
GROUP BY topic_id, user_id
) as X
WHERE X.topic_id = t.topic_id AND
X.user_id = t.user_id AND
(
last_read_post_number <> last_read OR
seen_post_count <> post_count
)
SQL
end
end

View File

@ -201,6 +201,15 @@ JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
action.created_at = hash[:created_at]
end
action.save!
action_type = hash[:action_type]
user_id = hash[:user_id]
if action_type == LIKE
User.update_all('likes_given = likes_given + 1', id: user_id)
elsif action_type == WAS_LIKED
User.update_all('likes_received = likes_received + 1', id: user_id)
end
rescue ActiveRecord::RecordNotUnique
# can happen, don't care already logged
raise ActiveRecord::Rollback
@ -214,6 +223,14 @@ JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
action.destroy
MessageBus.publish("/user/#{hash[:user_id]}", {user_action_id: action.id, remove: true})
end
action_type = hash[:action_type]
user_id = hash[:user_id]
if action_type == LIKE
User.update_all('likes_given = likes_given - 1', id: user_id)
elsif action_type == WAS_LIKED
User.update_all('likes_received = likes_received - 1', id: user_id)
end
end
protected

View File

@ -12,8 +12,8 @@ module Clockwork
every(1.day, 'enqueue_digest_emails', at: '06:00')
every(1.day, 'category_stats', at: '04:00')
every(1.day, 'ensure_db_consistency', at: '02:00')
every(10.minutes, 'periodical_updates')
every(1.day, 'version_check')
every(1.minute, 'clockwork_heartbeat')
end

View File

@ -455,6 +455,15 @@ en:
basic_requires_topics_entered: "How many a topics a new user must enter before promotion to basic (1) trust level"
basic_requires_read_posts: "How many posts a new user must read before promotion to basic (1) trust level"
basic_requires_time_spent_mins: "How many minutes a new user must read posts before promotion to basic (1) trust level"
regular_requires_topics_entered: "How many a topics a basic user must enter before promotion to regular (2) trust level"
regular_requires_read_posts: "How many posts a basic user must read before promotion to regular (2) trust level"
regular_requires_time_spent_mins: "How many minutes a basic user must read posts before promotion to regular (2) trust level"
regular_requires_days_visited: "How many days a basic user must visit the site before promotion to regular (2) trust level"
regular_requires_likes_received: "How many likes a basic user must receive before promotion to regular (2) trust level"
regular_requires_likes_given: "How many likes a basic user must cast before promotion to regular (2) trust level"
regular_requires_topic_reply_count: "How many topics a basic user must reply to before promotion to regular (2) trust level"
visitor_max_links: "How many links a visitor can add to a post"
visitor_max_images: "How many images a visitor can add to a post"
visitor_max_mentions_per_post: "Maximum number of @name notifications a visitor can use in a post"

View File

@ -0,0 +1,70 @@
class AddUserExtras < ActiveRecord::Migration
def up
# NOTE: our user table is getting bloated, we probably want to split it for performance
# put lesser used columns into a user_extras table and frequently used ones here.
add_column :users, :likes_given, :integer, null: false, default: 0
add_column :users, :likes_received, :integer, null: false, default: 0
add_column :users, :topic_reply_count, :integer, null: false, default: 0
# NOTE: to keep migrations working through refactorings we avoid externalizing this stuff.
# even though a helper method may make sense
execute <<SQL
UPDATE users u
SET
likes_given = X.likes_given
FROM (
SELECT
a.user_id,
COUNT(*) likes_given
FROM user_actions a
JOIN posts p ON p.id = a.target_post_id
WHERE p.deleted_at IS NULL AND a.action_type = 1
GROUP BY a.user_id
) as X
WHERE X.user_id = u.id
SQL
execute <<SQL
UPDATE users u
SET
likes_received = Y.likes_received
FROM (
SELECT
a.user_id,
COUNT(*) likes_received
FROM user_actions a
JOIN posts p ON p.id = a.target_post_id
WHERE p.deleted_at IS NULL AND a.action_type = 2
GROUP BY a.user_id
) as Y
WHERE Y.user_id = u.id
SQL
execute <<SQL
UPDATE users u
SET
topic_reply_count = Z.topic_reply_count
FROM (
SELECT
p.user_id,
COUNT(DISTINCT topic_id) topic_reply_count
FROM posts p
JOIN topics t on t.id = p.topic_id
WHERE t.user_id <> p.user_id AND
p.deleted_at IS NULL AND t.deleted_at IS NULL
GROUP BY p.user_id
) Z
WHERE
Z.user_id = u.id
SQL
end
def down
remove_column :users, :likes_given
remove_column :users, :likes_received
remove_column :users, :topic_reply_count
end
end

View File

@ -186,7 +186,7 @@ class Guardian
return false if @user.blank?
return false unless can_see?(object)
return false if SiteSetting.must_approve_users?
@user.moderator?
@user.has_trust_level?(:regular) || @user.moderator?
end

View File

@ -0,0 +1,8 @@
module Jobs
# checks to see if any users need to be promoted
class EnsureDbConsistency < Jobs::Base
def execute(args)
TopicUser.ensure_consistency!
end
end
end

View File

@ -23,6 +23,8 @@ class PostCreator
# target_usernames - comma delimited list of usernames for membership (private message)
# meta_data - Topic meta data hash
def initialize(user, opts)
# TODO: we should reload user in case it is tainted, should take in a user_id as opposed to user
# If we don't do this we introduce a rather risky dependency
@user = user
@opts = opts
raise Discourse::InvalidParameters.new(:raw) if @opts[:raw].blank?
@ -113,8 +115,13 @@ class PostCreator
# Track the topic
TopicUser.auto_track(@user.id, topic.id, TopicUser.notification_reasons[:created_post])
# Update `last_posted_at` to match the post's created_at
@user.update_column(:last_posted_at, post.created_at)
if @user.id != topic.user_id
@user.topic_reply_count += 1
end
@user.last_posted_at = post.created_at
@user.save!
# Publish the post in the message bus
MessageBus.publish("/topic/#{post.topic_id}",

View File

@ -32,4 +32,17 @@ class Promotion
true
end
def review_basic
return false if @user.topics_entered < SiteSetting.regular_requires_topics_entered
return false if @user.posts_read_count < SiteSetting.regular_requires_read_posts
return false if (@user.time_read / 60) < SiteSetting.regular_requires_time_spent_mins
return false if @user.days_visited < SiteSetting.regular_requires_days_visited
return false if @user.likes_received < SiteSetting.regular_requires_likes_received
return false if @user.likes_given < SiteSetting.regular_requires_likes_given
return false if @user.topic_reply_count < SiteSetting.regular_requires_topic_reply_count
@user.trust_level = TrustLevel.levels[:regular]
@user.save
end
end

View File

@ -71,7 +71,6 @@ describe PostCreator do
creator.create
end
it 'assigns a category when supplied' do
creator_with_category.create.topic.category.should == category
end
@ -84,6 +83,18 @@ describe PostCreator do
Post.any_instance.expects(:image_sizes=).with(image_sizes)
creator_with_image_sizes.create
end
it 'increases topic response counts' do
first_post = creator.create
user2 = Fabricate(:coding_horror)
user2.topic_reply_count.should == 0
first_post.user.reload.topic_reply_count.should == 0
PostCreator.new(user2, topic_id: first_post.topic_id, raw: "this is my test post 123").create
user2.reload.topic_reply_count.should == 1
first_post.user.reload.topic_reply_count.should == 0
end
end
end

View File

@ -44,5 +44,46 @@ describe Promotion do
end
context "basic" do
let(:user) { Fabricate(:user, trust_level: TrustLevel.levels[:basic])}
let(:promotion) { Promotion.new(user) }
context 'that has done nothing' do
let!(:result) { promotion.review }
it "returns false" do
result.should be_false
end
it "has not changed the user's trust level" do
user.trust_level.should == TrustLevel.levels[:basic]
end
end
context "that has done the requisite things" do
before do
user.topics_entered = SiteSetting.regular_requires_topics_entered
user.posts_read_count = SiteSetting.regular_requires_read_posts
user.time_read = SiteSetting.regular_requires_time_spent_mins * 60
user.days_visited = SiteSetting.regular_requires_days_visited * 60
user.likes_received = SiteSetting.regular_requires_likes_received
user.likes_given = SiteSetting.regular_requires_likes_given
user.topic_reply_count = SiteSetting.regular_requires_topic_reply_count
@result = promotion.review
end
it "returns true" do
@result.should be_true
end
it "has upgraded the user to regular" do
user.trust_level.should == TrustLevel.levels[:regular]
end
end
end
end

View File

@ -207,4 +207,23 @@ describe TopicUser do
end
it "is able to self heal" do
p1 = Fabricate(:post)
p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2)
TopicUser.exec_sql("UPDATE topic_users set seen_post_count=0, last_read_post_number=0
WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id)
[p1,p2].each do |p|
PostTiming.create(topic_id: p.topic_id, post_number: p.post_number, user_id: p.user_id, msecs: 100)
end
TopicUser.ensure_consistency!
tu = TopicUser.where(user_id: p1.user_id, topic_id: p1.topic_id).first
tu.last_read_post_number.should == p2.post_number
tu.seen_post_count.should == 2
end
end

View File

@ -108,13 +108,17 @@ describe UserAction do
@likee_action = likee.user_actions.where(action_type: UserAction::WAS_LIKED).first
end
it 'should create a like action on the liker' do
it 'should result in correct data assignment' do
@liker_action.should_not be_nil
@likee_action.should_not be_nil
likee.reload.likes_received.should == 1
liker.reload.likes_given.should == 1
PostAction.remove_act(liker, post, PostActionType.types[:like])
likee.reload.likes_received.should == 0
liker.reload.likes_given.should == 0
end
it 'should create a like action on the likee' do
@likee_action.should_not be_nil
end
end
context "liking a private message" do