Add sink user service and specs for mass vomits (#6006)
* Add sink user service and specs for mass vomits * Use new score calculation * Lower articles' scores instead and make async * Add lower score functionality to controllers * Remove unused variable * Use correct var and add missing arg * Remove unused status * Add composite index for reactable_type and ID * Use alias instead of additional boolean * Use medium priority for sink worker * Log to honeybadger and not logs * Log the error and not a string
This commit is contained in:
parent
6356089ee0
commit
acd3b92b32
10 changed files with 84 additions and 5 deletions
|
|
@ -2,6 +2,13 @@ class Internal::ReactionsController < Internal::ApplicationController
|
|||
def update
|
||||
@reaction = Reaction.find(params[:id])
|
||||
@reaction.update(status: params[:reaction][:status])
|
||||
Moderator::SinkArticles.call(@reaction.user_id) if confirmed_vomit_reaction?
|
||||
redirect_to "/internal/reports"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def confirmed_vomit_reaction?
|
||||
@reaction.reactable_type == "User" && @reaction.status == "confirmed" && @reaction.category == "vomit"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class ReactionsController < ApplicationController
|
|||
if reaction
|
||||
current_user.touch
|
||||
reaction.destroy
|
||||
Moderator::SinkArticles.call(reaction.user_id) if vomit_reaction_on_user?(reaction)
|
||||
Notification.send_reaction_notification_without_delay(reaction, reaction.reactable.user)
|
||||
Notification.send_reaction_notification_without_delay(reaction, reaction.reactable.organization) if organization_article?(reaction)
|
||||
@result = "destroy"
|
||||
|
|
@ -62,6 +63,7 @@ class ReactionsController < ApplicationController
|
|||
category: category,
|
||||
)
|
||||
@result = "create"
|
||||
Moderator::SinkArticles.call(reaction.user_id) if vomit_reaction_on_user?(reaction)
|
||||
Notification.send_reaction_notification(reaction, reaction.reactable.user)
|
||||
Notification.send_reaction_notification(reaction, reaction.reactable.organization) if organization_article?(reaction)
|
||||
end
|
||||
|
|
@ -80,4 +82,8 @@ class ReactionsController < ApplicationController
|
|||
def organization_article?(reaction)
|
||||
reaction.reactable_type == "Article" && reaction.reactable.organization.present?
|
||||
end
|
||||
|
||||
def vomit_reaction_on_user?(reaction)
|
||||
reaction.reactable_type == "User" && reaction.category == "vomit"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -394,7 +394,8 @@ class Article < ApplicationRecord
|
|||
end
|
||||
|
||||
def update_score
|
||||
update_columns(score: reactions.sum(:points),
|
||||
new_score = reactions.sum(:points) + Reaction.where(reactable_id: user_id, reactable_type: "User").sum(:points)
|
||||
update_columns(score: new_score,
|
||||
comment_score: comments.sum(:score),
|
||||
hotness_score: BlackBox.article_hotness_score(self),
|
||||
spaminess_rating: BlackBox.calculate_spaminess(self))
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ class Reaction < ApplicationRecord
|
|||
def assign_points
|
||||
base_points = BASE_POINTS.fetch(category, 1.0)
|
||||
base_points = 0 if status == "invalid"
|
||||
base_points /= 2 if reactable_type == "User"
|
||||
base_points *= 2 if status == "confirmed"
|
||||
self.points = user ? (base_points * user.reputation_modifier) : -5
|
||||
end
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@ class User < ApplicationRecord
|
|||
validate :non_banished_username, :username_changed?
|
||||
validate :unique_including_orgs_and_podcasts, if: :username_changed?
|
||||
|
||||
alias_attribute :positive_reactions_count, :reactions_count
|
||||
|
||||
scope :dev_account, -> { find_by(id: SiteConfig.staff_user_id) }
|
||||
scope :welcoming_account, -> { find_by(id: ApplicationConfig["WELCOMING_USER_ID"]) }
|
||||
|
||||
|
|
@ -628,10 +630,6 @@ class User < ApplicationRecord
|
|||
|
||||
def featured_number; end
|
||||
|
||||
def positive_reactions_count
|
||||
reactions_count
|
||||
end
|
||||
|
||||
def user
|
||||
self
|
||||
end
|
||||
|
|
|
|||
7
app/services/moderator/sink_articles.rb
Normal file
7
app/services/moderator/sink_articles.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
module Moderator
|
||||
class SinkArticles
|
||||
def self.call(user_id)
|
||||
Moderator::SinkArticlesWorker.perform_async(user_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
20
app/workers/moderator/sink_articles_worker.rb
Normal file
20
app/workers/moderator/sink_articles_worker.rb
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
module Moderator
|
||||
class SinkArticlesWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: :medium_priority, retry: 10
|
||||
|
||||
def perform(user_id)
|
||||
user = User.find_by(id: user_id)
|
||||
return unless user
|
||||
|
||||
articles = Article.where(user: user)
|
||||
reactions = Reaction.where(reactable: articles)
|
||||
new_score = reactions.sum(:points) + Reaction.where(reactable: user).sum(:points)
|
||||
articles.update_all(score: new_score)
|
||||
rescue StandardError => e
|
||||
DataDogStatsClient.count("moderators.sink", 1, tags: ["action:failed", "user_id:#{user.id}"])
|
||||
Honeybadger.notify(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class AddReactableTypeReactableIdIndexToReactions < ActiveRecord::Migration[5.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_index :reactions, %i[reactable_id reactable_type], algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
|
|
@ -867,6 +867,7 @@ ActiveRecord::Schema.define(version: 2020_02_13_182938) do
|
|||
t.index ["category"], name: "index_reactions_on_category"
|
||||
t.index ["created_at"], name: "index_reactions_on_created_at"
|
||||
t.index ["points"], name: "index_reactions_on_points"
|
||||
t.index ["reactable_id", "reactable_type"], name: "index_reactions_on_reactable_id_and_reactable_type"
|
||||
t.index ["reactable_id"], name: "index_reactions_on_reactable_id"
|
||||
t.index ["reactable_type"], name: "index_reactions_on_reactable_type"
|
||||
t.index ["user_id"], name: "index_reactions_on_user_id"
|
||||
|
|
|
|||
31
spec/services/moderator/sink_articles_spec.rb
Normal file
31
spec/services/moderator/sink_articles_spec.rb
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Moderator::SinkArticles, type: :service do
|
||||
let(:moderator) { create(:user, :trusted) }
|
||||
let(:spam_user) do
|
||||
user = create(:user)
|
||||
create_list(:article, 3, user: user)
|
||||
user
|
||||
end
|
||||
let(:vomit_reaction) { create(:reaction, reactable: spam_user, user: moderator, category: "vomit") }
|
||||
|
||||
describe "#call" do
|
||||
it "lowers all of a user's articles' scores by 25 each if not confirmed" do
|
||||
vomit_reaction
|
||||
expect do
|
||||
sidekiq_perform_enqueued_jobs do
|
||||
described_class.call(spam_user.id)
|
||||
end
|
||||
end.to change { spam_user.articles.sum(:score) }.from(0).to(-75)
|
||||
end
|
||||
|
||||
it "lowers all of the user's articles' scores by 50 each if confirmed" do
|
||||
vomit_reaction.update(status: "confirmed")
|
||||
expect do
|
||||
sidekiq_perform_enqueued_jobs do
|
||||
described_class.call(spam_user.id)
|
||||
end
|
||||
end.to change { spam_user.articles.sum(:score) }.from(0).to(-150)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue