From ca81eef4f5b328a4d8d9c85e92f2fdcdfabcd6a8 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Mon, 16 Dec 2024 13:54:11 +0300 Subject: [PATCH 1/9] minor cleanup of UsersController#vote_summary method --- app/controllers/users_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 973a8eb59..17b877bfb 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -576,16 +576,18 @@ def my_vote_summary def vote_summary @votes = Vote.where(recv_user: @user) \ .includes(:post).group(:date_of, :post_id, :vote_type) + @votes = @votes.select(:post_id, :vote_type) \ .select('count(*) as vote_count') \ .select('date(created_at) as date_of') + @votes = @votes.order(date_of: :desc, post_id: :desc).all \ .group_by(&:date_of).map do |k, vl| [k, vl.group_by(&:post), vl.sum { |v| v.vote_type * v.vote_count }] end \ .paginate(page: params[:page], per_page: 15) + render layout: 'without_sidebar' - @votes end def avatar From 69b63ef549125b4c0576acfe6dce432991bc277c Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Mon, 16 Dec 2024 14:17:22 +0300 Subject: [PATCH 2/9] ensured orphaned votes are filtered out from vote_summary --- app/controllers/users_controller.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 17b877bfb..2940021fb 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -574,12 +574,13 @@ def my_vote_summary end def vote_summary - @votes = Vote.where(recv_user: @user) \ - .includes(:post).group(:date_of, :post_id, :vote_type) + @votes = Vote.where(recv_user: @user) + .joins(:post) + .group(:date_of, :post_id, :vote_type) - @votes = @votes.select(:post_id, :vote_type) \ - .select('count(*) as vote_count') \ - .select('date(created_at) as date_of') + @votes = @votes.select(:post_id, :vote_type) + .select('count(*) as vote_count') + .select('date(votes.created_at) as date_of') @votes = @votes.order(date_of: :desc, post_id: :desc).all \ .group_by(&:date_of).map do |k, vl| From 4e0bccb8c6257a9568a8026be554bfb5b75f69cc Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Mon, 16 Dec 2024 14:38:34 +0300 Subject: [PATCH 3/9] ensured vote_summary route doesn't break even if the post is missing --- app/views/users/vote_summary.html.erb | 24 +++++++++++++++--------- config/locales/strings/en.votes.yml | 4 ++++ 2 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 config/locales/strings/en.votes.yml diff --git a/app/views/users/vote_summary.html.erb b/app/views/users/vote_summary.html.erb index 7fab92d3e..d1cf7dcaa 100644 --- a/app/views/users/vote_summary.html.erb +++ b/app/views/users/vote_summary.html.erb @@ -40,15 +40,21 @@
-
- <%= link_to generic_share_link(post) do %> - <%= post.post_type.is_top_level ? post.title : post.parent.title %> - <% end %> -
-
- <%= post.post_type.name %> - <%= post.category.name %> -
+ <% if post.present? %> +
+ <%= link_to generic_share_link(post) do %> + <%= post.post_type.is_top_level ? post.title : post.parent.title %> + <% end %> +
+
+ <%= post.post_type.name %> + <%= post.category.name %> +
+ <% else %> +
+ <%= I18n.t('votes.summary.post_missing') %> +
+ <% end %>
diff --git a/config/locales/strings/en.votes.yml b/config/locales/strings/en.votes.yml new file mode 100644 index 000000000..485ef2f6f --- /dev/null +++ b/config/locales/strings/en.votes.yml @@ -0,0 +1,4 @@ +en: + votes: + summary: + post_missing: 'Post not found' \ No newline at end of file From 8ad843e69b35950795ae69498430014dbbc47b04 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Mon, 16 Dec 2024 16:45:09 +0300 Subject: [PATCH 4/9] ensured vote model properly handles missing associated posts --- app/models/vote.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/models/vote.rb b/app/models/vote.rb index a7a3505ab..0a767498a 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb @@ -34,12 +34,14 @@ def reverse_rep_change end def rep_change(direction) + return unless post.present? + change = CategoryPostType.rep_changes[[post.category_id, post.post_type_id]][vote_type] || 0 recv_user.update!(reputation: recv_user.reputation + (direction * change)) end def post_not_deleted - if post.deleted? + if post&.deleted? errors.add(:base, 'Votes are locked on deleted posts') end end @@ -49,22 +51,28 @@ def check_valid end def add_counter + return unless post.present? + case vote_type when 1 post.update(upvote_count: post.upvote_count + 1) when -1 post.update(downvote_count: post.downvote_count + 1) end + post.recalc_score end def remove_counter + return unless post.present? + case vote_type when 1 post.update(upvote_count: [post.upvote_count - 1, 0].max) when -1 post.update(downvote_count: [post.downvote_count - 1, 0].max) end + post.recalc_score end end From c7499fadf9de007e558c9b53764dc1ff0d115784 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Mon, 16 Dec 2024 17:07:05 +0300 Subject: [PATCH 5/9] Vote#check_valid should not prevent deletion of orphaned votes --- app/models/vote.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/vote.rb b/app/models/vote.rb index 0a767498a..a7c42dcb3 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb @@ -47,7 +47,7 @@ def post_not_deleted end def check_valid - throw :abort unless valid? + throw :abort unless valid? || post.blank? end def add_counter From 646db76e095be60e1cd20bf5aa8318854c057337 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Mon, 16 Dec 2024 17:08:00 +0300 Subject: [PATCH 6/9] added CleanupVotes daily job for removing orphaned votes --- app/jobs/cleanup_votes_job.rb | 17 +++++++++++++++++ config/schedule.rb | 4 ++++ scripts/cleanup_votes.rb | 1 + 3 files changed, 22 insertions(+) create mode 100644 app/jobs/cleanup_votes_job.rb create mode 100644 scripts/cleanup_votes.rb diff --git a/app/jobs/cleanup_votes_job.rb b/app/jobs/cleanup_votes_job.rb new file mode 100644 index 000000000..11ef53808 --- /dev/null +++ b/app/jobs/cleanup_votes_job.rb @@ -0,0 +1,17 @@ +class CleanupVotesJob < ApplicationJob + queue_as :default + + def perform + Community.all.each do |c| + RequestContext.community = c + orphan_votes = Vote.all.reject { |v| v.post.present? } + puts "[#{c.name}] destroying #{orphan_votes.length} #{'orphan vote'.pluralize(orphan_votes.length)}" + failed = orphan_votes.reject(&:destroy) + + failed.each do |v| + puts "[#{c.name}] failed to destroy vote \"#{v.id}\"" + v.errors.each { |e| puts e.full_message } + end + end + end +end diff --git a/config/schedule.rb b/config/schedule.rb index 39d5eee0b..8b0ea73db 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -18,6 +18,10 @@ runner 'scripts/cleanup_drafts.rb' end +every 1.day, ay: '02.25' do + runner 'scripts/cleanup_votes.rb' +end + every 6.hours do runner 'scripts/recalc_abilities.rb' end diff --git a/scripts/cleanup_votes.rb b/scripts/cleanup_votes.rb new file mode 100644 index 000000000..67707824e --- /dev/null +++ b/scripts/cleanup_votes.rb @@ -0,0 +1 @@ +CleanupVotesJob.perform_later \ No newline at end of file From c1cbe8ff446c029274e960d76006de4e95247d10 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Wed, 18 Dec 2024 16:37:47 +0300 Subject: [PATCH 7/9] added audit log for when the cleanup_votes job successfully deletes orphaned votes --- app/jobs/cleanup_votes_job.rb | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/app/jobs/cleanup_votes_job.rb b/app/jobs/cleanup_votes_job.rb index 11ef53808..b54b26be7 100644 --- a/app/jobs/cleanup_votes_job.rb +++ b/app/jobs/cleanup_votes_job.rb @@ -5,12 +5,27 @@ def perform Community.all.each do |c| RequestContext.community = c orphan_votes = Vote.all.reject { |v| v.post.present? } + puts "[#{c.name}] destroying #{orphan_votes.length} #{'orphan vote'.pluralize(orphan_votes.length)}" - failed = orphan_votes.reject(&:destroy) - failed.each do |v| - puts "[#{c.name}] failed to destroy vote \"#{v.id}\"" - v.errors.each { |e| puts e.full_message } + system_user = User.find(-1) + + orphan_votes.each do |v| + result = v.destroy + + if result + AuditLog.admin_audit( + comment: "Deleted orphaned vote for user ##{v.recv_user_id} " \ + "on post ##{v.post_id} " \ + "in community ##{c.id} (#{c.name})", + event_type: 'vote_delete', + related: v, + user: system_user + ) + else + puts "[#{c.name}] failed to destroy vote \"#{v.id}\"" + v.errors.each { |e| puts e.full_message } + end end end end From b317c40151c177ec71755acd287ab96ecfd0ac2d Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 3 Jan 2025 06:02:03 +0300 Subject: [PATCH 8/9] fixed typo in scheduling vote cleanup --- config/schedule.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/schedule.rb b/config/schedule.rb index 8b0ea73db..1b1c9c343 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -18,7 +18,7 @@ runner 'scripts/cleanup_drafts.rb' end -every 1.day, ay: '02.25' do +every 1.day, at: '02:25' do runner 'scripts/cleanup_votes.rb' end From a232e80e9e0368e57915d8b3d5ac074220bd1656 Mon Sep 17 00:00:00 2001 From: Oleg Valter Date: Fri, 3 Jan 2025 22:53:22 +0300 Subject: [PATCH 9/9] reverted vote_summary vote select to eager load --- app/controllers/users_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 2940021fb..e42d54886 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -575,7 +575,7 @@ def my_vote_summary def vote_summary @votes = Vote.where(recv_user: @user) - .joins(:post) + .includes(:post) .group(:date_of, :post_id, :vote_type) @votes = @votes.select(:post_id, :vote_type)