implement super voters
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
33
app/models/super_voter.rb
Normal 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
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
8
db/migrate/20160222211328_create_super_voters.rb
Normal file
8
db/migrate/20160222211328_create_super_voters.rb
Normal 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
|
||||||
@@ -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');
|
||||||
|
|
||||||
|
|||||||
4
test/factories/super_voter.rb
Normal file
4
test/factories/super_voter.rb
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
FactoryGirl.define do
|
||||||
|
factory(:super_voter) do
|
||||||
|
end
|
||||||
|
end
|
||||||
38
test/models/super_voter_test.rb
Normal file
38
test/models/super_voter_test.rb
Normal 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
|
||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user