implement super voters

This commit is contained in:
r888888888
2016-02-22 14:02:52 -08:00
parent caf4a28b02
commit 2a87aad34e
13 changed files with 189 additions and 12 deletions

View File

@@ -6,8 +6,8 @@ class DailyMaintenance
Upload.delete_all(['created_at < ?', 1.day.ago]) Upload.delete_all(['created_at < ?', 1.day.ago])
ModAction.delete_all(['created_at < ?', 3.days.ago]) ModAction.delete_all(['created_at < ?', 3.days.ago])
Delayed::Job.delete_all(['created_at < ?', 7.days.ago]) Delayed::Job.delete_all(['created_at < ?', 7.days.ago])
PostVote.delete_all(['created_at < ?', 1.month.ago]) PostVote.prune!
CommentVote.delete_all(['created_at < ?', 1.month.ago]) CommentVote.prune!
TagSubscription.process_all TagSubscription.process_all
ApiCacheGenerator.new.generate_tag_cache ApiCacheGenerator.new.generate_tag_cache
PostDisapproval.prune! PostDisapproval.prune!
@@ -15,5 +15,7 @@ class DailyMaintenance
TagAlias.update_cached_post_counts_for_all TagAlias.update_cached_post_counts_for_all
PostDisapproval.dmail_messages! PostDisapproval.dmail_messages!
Tag.clean_up_negative_post_counts! Tag.clean_up_negative_post_counts!
SuperVoter.prune!
SuperVoter.init!
end end
end end

View File

@@ -1,5 +1,7 @@
module Reports module Reports
class UserSimilarity class UserSimilarity
NOT_READY_STRING = "not ready"
attr_reader :user_id attr_reader :user_id
def initialize(user_id) def initialize(user_id)
@@ -10,6 +12,16 @@ module Reports
User.find(user_id) User.find(user_id)
end end
def prime
10.times do
if fetch_similar_user_ids == NOT_READY_STRING
sleep(60)
else
break
end
end
end
def fetch_similar_user_ids def fetch_similar_user_ids
return NotImplementedError unless Danbooru.config.report_server return NotImplementedError unless Danbooru.config.report_server

View File

@@ -12,7 +12,7 @@ class CommentVote < ActiveRecord::Base
attr_accessible :comment_id, :user_id, :score attr_accessible :comment_id, :user_id, :score
def self.prune! def self.prune!
destroy_all("created_at < ?", 14.days.ago) where("created_at < ?", 14.days.ago).delete_all
end end
def self.search(params) def self.search(params)

View File

@@ -952,14 +952,22 @@ class Post < ActiveRecord::Base
!PostVote.exists?(:user_id => user.id, :post_id => id) !PostVote.exists?(:user_id => user.id, :post_id => id)
end end
def vote_magnitude
if CurrentUser.is_super_voter?
SuperVoter::MAGNITUDE
else
1
end
end
def vote!(score) def vote!(score)
if can_be_voted_by?(CurrentUser.user) if can_be_voted_by?(CurrentUser.user)
if score == "up" if score == "up"
Post.where(:id => id).update_all("score = score + 1, up_score = up_score + 1") Post.where(:id => id).update_all("score = score + #{vote_magnitude}, up_score = up_score + #{vote_magnitude}")
self.score += 1 self.score += vote_magnitude
elsif score == "down" elsif score == "down"
Post.where(:id => id).update_all("score = score - 1, down_score = down_score - 1") Post.where(:id => id).update_all("score = score - #{vote_magnitude}, down_score = down_score - #{vote_magnitude}")
self.score -= 1 self.score -= vote_magnitude
end end
votes.create(:score => score) votes.create(:score => score)
@@ -975,11 +983,11 @@ class Post < ActiveRecord::Base
vote = votes.where("user_id = ?", CurrentUser.user.id).first vote = votes.where("user_id = ?", CurrentUser.user.id).first
if vote.score == 1 if vote.score == 1
Post.where(:id => id).update_all("score = score - 1, up_score = up_score - 1") Post.where(:id => id).update_all("score = score - #{vote_magnitude}, up_score = up_score - #{vote_magnitude}")
self.score -= 1 self.score -= vote_magnitude
else else
Post.where(:id => id).update_all("score = score + 1, down_score = down_score + 1") Post.where(:id => id).update_all("score = score + #{vote_magnitude}, down_score = down_score + #{vote_magnitude}")
self.score += 1 self.score += vote_magnitude
end end
vote.destroy vote.destroy

View File

@@ -7,6 +7,10 @@ class PostVote < ActiveRecord::Base
validates_inclusion_of :score, :in => [1, -1] validates_inclusion_of :score, :in => [1, -1]
attr_accessible :post_id, :user_id, :score attr_accessible :post_id, :user_id, :score
def self.prune!
where("created_at < ?", 30.days.ago).delete_all
end
def score=(x) def score=(x)
if x == "up" if x == "up"
write_attribute(:score, 1) write_attribute(:score, 1)

33
app/models/super_voter.rb Normal file
View File

@@ -0,0 +1,33 @@
class SuperVoter < ActiveRecord::Base
MAGNITUDE = 5
DURATION = 1.week
belongs_to :user
validates_uniqueness_of :user_id
after_create :update_user_on_create
after_destroy :update_user_on_destroy
def self.prune!
where("created_at < ?", DURATION.ago).destroy_all
end
def self.init!
report = Reports::UserSimilarity.new(User.admins.first.id)
report.prime
report.fetch_similar_user_ids.scan(/\S+/).in_groups_of(2).each do |user_id, score|
unless where("user_id = ?", user_id.to_i).exists?
create(:user_id => user_id)
end
end
end
def update_user_on_create
user.is_super_voter = true
user.save
end
def update_user_on_destroy
user.is_super_voter = false
user.save
end
end

View File

@@ -33,6 +33,7 @@ class User < ActiveRecord::Base
can_approve_posts can_approve_posts
can_upload_free can_upload_free
disable_categorized_saved_searches disable_categorized_saved_searches
is_super_voter
) )
include Danbooru::HasBitFlags include Danbooru::HasBitFlags
@@ -67,6 +68,7 @@ class User < ActiveRecord::Base
has_one :recent_ban, lambda {order("bans.id desc")}, :class_name => "Ban" has_one :recent_ban, lambda {order("bans.id desc")}, :class_name => "Ban"
has_one :api_key has_one :api_key
has_one :dmail_filter has_one :dmail_filter
has_one :super_voter
has_many :subscriptions, lambda {order("tag_subscriptions.name")}, :class_name => "TagSubscription", :foreign_key => "creator_id" has_many :subscriptions, lambda {order("tag_subscriptions.name")}, :class_name => "TagSubscription", :foreign_key => "creator_id"
has_many :note_versions, :foreign_key => "updater_id" has_many :note_versions, :foreign_key => "updater_id"
has_many :dmails, lambda {order("dmails.id desc")}, :foreign_key => "owner_id" has_many :dmails, lambda {order("dmails.id desc")}, :foreign_key => "owner_id"

View File

@@ -17,7 +17,7 @@ class UserSimilarityPresenter
def fetch def fetch
data = report.fetch_similar_user_ids data = report.fetch_similar_user_ids
if data == "not ready" if data == Reports::UserSimilarity::NOT_READY_STRING
@not_ready = true @not_ready = true
else else
@user_ids_with_scores = data.scan(/\S+/).in_groups_of(2) @user_ids_with_scores = data.scan(/\S+/).in_groups_of(2)

View File

@@ -0,0 +1,8 @@
class CreateSuperVoters < ActiveRecord::Migration
def change
create_table :super_voters do |t|
t.integer :user_id
t.timestamps null: false
end
end
end

View File

@@ -2830,6 +2830,37 @@ CREATE TABLE schema_migrations (
); );
--
-- Name: super_voters; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE super_voters (
id integer NOT NULL,
user_id integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
--
-- Name: super_voters_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE super_voters_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: super_voters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE super_voters_id_seq OWNED BY super_voters.id;
-- --
-- Name: tag_aliases; Type: TABLE; Schema: public; Owner: -; Tablespace: -- Name: tag_aliases; Type: TABLE; Schema: public; Owner: -; Tablespace:
-- --
@@ -4261,6 +4292,13 @@ ALTER TABLE ONLY posts ALTER COLUMN id SET DEFAULT nextval('posts_id_seq'::regcl
ALTER TABLE ONLY saved_searches ALTER COLUMN id SET DEFAULT nextval('saved_searches_id_seq'::regclass); ALTER TABLE ONLY saved_searches ALTER COLUMN id SET DEFAULT nextval('saved_searches_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY super_voters ALTER COLUMN id SET DEFAULT nextval('super_voters_id_seq'::regclass);
-- --
-- Name: id; Type: DEFAULT; Schema: public; Owner: - -- Name: id; Type: DEFAULT; Schema: public; Owner: -
-- --
@@ -4657,6 +4695,14 @@ ALTER TABLE ONLY saved_searches
ADD CONSTRAINT saved_searches_pkey PRIMARY KEY (id); ADD CONSTRAINT saved_searches_pkey PRIMARY KEY (id);
--
-- Name: super_voters_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY super_voters
ADD CONSTRAINT super_voters_pkey PRIMARY KEY (id);
-- --
-- Name: tag_aliases_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace: -- Name: tag_aliases_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
-- --
@@ -7313,3 +7359,5 @@ INSERT INTO schema_migrations (version) VALUES ('20160219010854');
INSERT INTO schema_migrations (version) VALUES ('20160219172840'); INSERT INTO schema_migrations (version) VALUES ('20160219172840');
INSERT INTO schema_migrations (version) VALUES ('20160222211328');

View File

@@ -0,0 +1,4 @@
FactoryGirl.define do
factory(:super_voter) do
end
end

View File

@@ -0,0 +1,38 @@
require 'test_helper'
class SuperVoterTest < ActiveSupport::TestCase
def setup
super
@user = FactoryGirl.create(:user)
end
context "#init" do
setup do
@admin = FactoryGirl.create(:admin_user)
Reports::UserSimilarity.any_instance.stubs(:fetch_similar_user_ids).returns("#{@user.id} 1")
end
should "create super voter objects" do
assert_difference("SuperVoter.count") do
SuperVoter.init!
end
end
end
context "creation" do
should "update the is_super_voter field on the user object" do
FactoryGirl.create(:super_voter, user: @user)
@user.reload
assert_equal(true, @user.is_super_voter?)
end
end
context "destruction" do
should "update the is_super_voter field on the user object" do
voter = FactoryGirl.create(:super_voter, user: @user)
voter.destroy
@user.reload
assert_equal(false, @user.is_super_voter?)
end
end
end

View File

@@ -1463,6 +1463,24 @@ class PostTest < ActiveSupport::TestCase
end end
context "Voting:" do context "Voting:" do
context "with a super voter" do
setup do
@user = FactoryGirl.create(:user)
FactoryGirl.create(:super_voter, user: @user)
@post = FactoryGirl.create(:post)
end
should "account for magnitude" do
CurrentUser.scoped(@user, "127.0.0.1") do
assert_nothing_raised {@post.vote!("up")}
assert_raises(PostVote::Error) {@post.vote!("up")}
@post.reload
assert_equal(1, PostVote.count)
assert_equal(SuperVoter::MAGNITUDE, @post.score)
end
end
end
should "not allow duplicate votes" do should "not allow duplicate votes" do
user = FactoryGirl.create(:user) user = FactoryGirl.create(:user)
post = FactoryGirl.create(:post) post = FactoryGirl.create(:post)