Add new upload limit system (fix #4234).
This commit is contained in:
99
app/logical/upload_limit.rb
Normal file
99
app/logical/upload_limit.rb
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
class UploadLimit
|
||||||
|
extend Memoist
|
||||||
|
|
||||||
|
INITIAL_POINTS = 1000
|
||||||
|
MAXIMUM_POINTS = 10000
|
||||||
|
|
||||||
|
attr_reader :user
|
||||||
|
|
||||||
|
def initialize(user)
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
def limited?
|
||||||
|
used_upload_slots >= upload_slots
|
||||||
|
end
|
||||||
|
|
||||||
|
def used_upload_slots
|
||||||
|
pending = user.posts.pending
|
||||||
|
early_deleted = user.posts.deleted.where("created_at >= ?", 3.days.ago)
|
||||||
|
|
||||||
|
pending.or(early_deleted).count
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_slots
|
||||||
|
upload_level + 5
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_level
|
||||||
|
UploadLimit.points_to_level(user.upload_points)
|
||||||
|
end
|
||||||
|
|
||||||
|
def approvals_on_current_level
|
||||||
|
(user.upload_points - UploadLimit.level_to_points(upload_level)) / 10
|
||||||
|
end
|
||||||
|
|
||||||
|
def approvals_for_next_level
|
||||||
|
UploadLimit.points_for_next_level(upload_level) / 10
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_limit!(post, incremental: true)
|
||||||
|
return if user.can_upload_free?
|
||||||
|
|
||||||
|
user.with_lock do
|
||||||
|
if incremental
|
||||||
|
user.upload_points += UploadLimit.upload_value(user.upload_points, post.is_deleted)
|
||||||
|
user.save!
|
||||||
|
else
|
||||||
|
user.update!(upload_points: UploadLimit.points_for_user(user))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.points_for_user(user)
|
||||||
|
points = INITIAL_POINTS
|
||||||
|
|
||||||
|
uploads = user.posts.where(is_pending: false).order(id: :asc).pluck(:is_deleted)
|
||||||
|
uploads.each do |is_deleted|
|
||||||
|
points += upload_value(points, is_deleted)
|
||||||
|
points = points.clamp(0, MAXIMUM_POINTS)
|
||||||
|
|
||||||
|
#warn "slots: %2d, points: %3d, value: %2d" % [UploadLimit.points_to_level(points) + 5, points, UploadLimit.upload_value(level, is_deleted)]
|
||||||
|
end
|
||||||
|
|
||||||
|
points
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.upload_value(current_points, is_deleted)
|
||||||
|
if is_deleted
|
||||||
|
level = points_to_level(current_points)
|
||||||
|
-1 * (points_for_next_level(level) / 3.0).round.to_i
|
||||||
|
else
|
||||||
|
10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.points_for_next_level(level)
|
||||||
|
100 + 20 * [level - 10, 0].max
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.points_to_level(points)
|
||||||
|
level = 0
|
||||||
|
|
||||||
|
loop do
|
||||||
|
points -= points_for_next_level(level)
|
||||||
|
break if points < 0
|
||||||
|
level += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
level
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.level_to_points(level)
|
||||||
|
(1..level).map do |n|
|
||||||
|
points_for_next_level(n - 1)
|
||||||
|
end.sum
|
||||||
|
end
|
||||||
|
|
||||||
|
memoize :used_upload_slots
|
||||||
|
end
|
||||||
@@ -1279,6 +1279,9 @@ class Post < ApplicationRecord
|
|||||||
# XXX This must happen *after* the `is_deleted` flag is set to true (issue #3419).
|
# XXX This must happen *after* the `is_deleted` flag is set to true (issue #3419).
|
||||||
give_favorites_to_parent(options) if options[:move_favorites]
|
give_favorites_to_parent(options) if options[:move_favorites]
|
||||||
|
|
||||||
|
is_automatic = (reason == "Unapproved in three days")
|
||||||
|
uploader.new_upload_limit.update_limit!(self, incremental: is_automatic)
|
||||||
|
|
||||||
unless options[:without_mod_action]
|
unless options[:without_mod_action]
|
||||||
ModAction.log("deleted post ##{id}, reason: #{reason}", :post_delete)
|
ModAction.log("deleted post ##{id}, reason: #{reason}", :post_delete)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,10 +26,13 @@ class PostApproval < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def approve_post
|
def approve_post
|
||||||
ModAction.log("undeleted post ##{post_id}", :post_undelete) if post.is_deleted
|
is_undeletion = post.is_deleted
|
||||||
|
|
||||||
post.flags.each(&:resolve!)
|
post.flags.each(&:resolve!)
|
||||||
post.update(approver: user, is_flagged: false, is_pending: false, is_deleted: false)
|
post.update(approver: user, is_flagged: false, is_pending: false, is_deleted: false)
|
||||||
|
ModAction.log("undeleted post ##{post_id}", :post_undelete) if is_undeletion
|
||||||
|
|
||||||
|
post.uploader.new_upload_limit.update_limit!(post, incremental: !is_undeletion)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.search(params)
|
def self.search(params)
|
||||||
|
|||||||
@@ -468,6 +468,10 @@ class User < ApplicationRecord
|
|||||||
(is_moderator? && flag.not_uploaded_by?(id)) || flag.creator_id == id
|
(is_moderator? && flag.not_uploaded_by?(id)) || flag.creator_id == id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_upload_limit
|
||||||
|
@new_upload_limit ||= UploadLimit.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
def upload_limit
|
def upload_limit
|
||||||
[max_upload_limit - used_upload_slots, 0].max
|
[max_upload_limit - used_upload_slots, 0].max
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -66,6 +66,15 @@
|
|||||||
<td><%= presenter.upload_limit(self) %> (<%= link_to_wiki "help", "about:upload_limits" %>)</td>
|
<td><%= presenter.upload_limit(self) %> (<%= link_to_wiki "help", "about:upload_limits" %>)</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>New Upload Limit</th>
|
||||||
|
<td>
|
||||||
|
<%= link_to user.new_upload_limit.used_upload_slots, posts_path(tags: "user:#{user.name} status:pending") %>
|
||||||
|
/
|
||||||
|
<%= tag.abbr user.new_upload_limit.upload_slots, title: "Next level: #{user.new_upload_limit.approvals_on_current_level} / #{user.new_upload_limit.approvals_for_next_level} approvals" %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Uploads</th>
|
<th>Uploads</th>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
5
db/migrate/20200123184743_add_upload_points_to_users.rb
Normal file
5
db/migrate/20200123184743_add_upload_points_to_users.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddUploadPointsToUsers < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :users, :upload_points, :integer, null: false, default: 1000
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2192,7 +2192,8 @@ furry -rating:s'::text,
|
|||||||
bit_prefs bigint DEFAULT 0 NOT NULL,
|
bit_prefs bigint DEFAULT 0 NOT NULL,
|
||||||
last_ip_addr inet,
|
last_ip_addr inet,
|
||||||
unread_dmail_count integer DEFAULT 0 NOT NULL,
|
unread_dmail_count integer DEFAULT 0 NOT NULL,
|
||||||
theme integer DEFAULT 0 NOT NULL
|
theme integer DEFAULT 0 NOT NULL,
|
||||||
|
upload_points integer DEFAULT 1000 NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -7366,6 +7367,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||||||
('20200117220602'),
|
('20200117220602'),
|
||||||
('20200118015014'),
|
('20200118015014'),
|
||||||
('20200119184442'),
|
('20200119184442'),
|
||||||
('20200119193110');
|
('20200119193110'),
|
||||||
|
('20200123184743');
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
script/fixes/062_initialize_upload_points.rb
Executable file
14
script/fixes/062_initialize_upload_points.rb
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require_relative "../../config/environment"
|
||||||
|
|
||||||
|
uploaders = User.where(id: Post.select(:uploader_id)).bit_prefs_match(:can_upload_free, false)
|
||||||
|
|
||||||
|
warn "uploaders=#{uploaders.count}"
|
||||||
|
uploaders.find_each.with_index do |uploader, n|
|
||||||
|
uploader.new_upload_limit.update_limit!(nil, incremental: false)
|
||||||
|
warn "n=#{n} id=#{uploader.id} name=#{uploader.name} points=#{uploader.upload_points}"
|
||||||
|
end
|
||||||
|
|
||||||
|
contributors = User.bit_prefs_match(:can_upload_free, true)
|
||||||
|
contributors.update_all(upload_points: UploadLimit::MAXIMUM_POINTS)
|
||||||
54
test/unit/upload_limit_test.rb
Normal file
54
test/unit/upload_limit_test.rb
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class UploadLimitTest < ActiveSupport::TestCase
|
||||||
|
context "Upload limits:" do
|
||||||
|
setup do
|
||||||
|
@user = create(:user, upload_points: 1000)
|
||||||
|
@approver = create(:moderator_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "a pending post that is deleted" do
|
||||||
|
should "decrease the uploader's upload points" do
|
||||||
|
@post = create(:post, uploader: @user, is_pending: true, created_at: 7.days.ago)
|
||||||
|
assert_equal(1000, @user.reload.upload_points)
|
||||||
|
|
||||||
|
PostPruner.new.prune!
|
||||||
|
assert_equal(967, @user.reload.upload_points)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "a pending post that is approved" do
|
||||||
|
should "increase the uploader's upload points" do
|
||||||
|
@post = create(:post, uploader: @user, is_pending: true, created_at: 7.days.ago)
|
||||||
|
assert_equal(1000, @user.reload.upload_points)
|
||||||
|
|
||||||
|
@post.approve!(@approver)
|
||||||
|
assert_equal(1010, @user.reload.upload_points)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "an approved post that is deleted" do
|
||||||
|
should "decrease the uploader's upload points" do
|
||||||
|
@post = create(:post, uploader: @user, is_pending: true)
|
||||||
|
assert_equal(1000, @user.reload.upload_points)
|
||||||
|
|
||||||
|
@post.approve!(@approver)
|
||||||
|
assert_equal(1010, @user.reload.upload_points)
|
||||||
|
|
||||||
|
as(@approver) { @post.delete!("bad") }
|
||||||
|
assert_equal(967, @user.reload.upload_points)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "a deleted post that is undeleted" do
|
||||||
|
should "increase the uploader's upload points" do
|
||||||
|
@post = create(:post, uploader: @user)
|
||||||
|
as(@approver) { @post.delete!("bad") }
|
||||||
|
assert_equal(967, @user.reload.upload_points)
|
||||||
|
|
||||||
|
@post.approve!(@approver)
|
||||||
|
assert_equal(1010, @user.reload.upload_points)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user