diff --git a/app/controllers/moderator/bulk_reverts_controller.rb b/app/controllers/moderator/bulk_reverts_controller.rb index 3d708f5a9..87e956504 100644 --- a/app/controllers/moderator/bulk_reverts_controller.rb +++ b/app/controllers/moderator/bulk_reverts_controller.rb @@ -1,24 +1,35 @@ module Moderator class BulkRevertsController < ApplicationController before_filter :moderator_only + before_filter :init_constraints helper PostVersionsHelper + rescue_from BulkRevert::ConstraintTooGeneralError, :with => :tag_constraint_too_general def new - @constraints = params[:constraints] || {} end def create - @constraints = params[:constraints] || {} @bulk_revert = BulkRevert.new(@constraints) if params[:commit] == "Test" @bulk_revert.preview render action: "new" else - @bulk_revert.process! + BulkRevert.delay(:queue => "default").process(@constraints) flash[:notice] = "Reverts queued" redirect_to new_bulk_revert_path end end + + private + + def init_constraints + @constraints = params[:constraints] || {} + end + + def tag_constraint_too_general + flash[:notice] = "Your tag constraints are too general; try adding min and max version ids" + render action: "new" + end end end diff --git a/app/logical/bulk_revert.rb b/app/logical/bulk_revert.rb index 148569b09..191d446d4 100644 --- a/app/logical/bulk_revert.rb +++ b/app/logical/bulk_revert.rb @@ -1,6 +1,17 @@ class BulkRevert + BIG_QUERY_LIMIT = 5_000 attr_reader :constraints + class ConstraintTooGeneralError < Exception ; end + + def self.process(constraints) + obj = BulkRevert.new(constraints) + + obj.find_post_versions.order("updated_at, id").each do |version| + version.undo! + end + end + def initialize(constraints) @constraints = constraints end @@ -18,14 +29,19 @@ class BulkRevert if constraints[:user_id] q = q.where("post_versions.updater_id = ?", constraints[:user_id]) + end - if constraints[:added_tags] || constraints[:removed_tags] - hash = CityHash.hash64("#{constraints[:added_tags]} #{constraints{removed_tags}}").to_s(36) - sub_ids = Cache.get("br/fpv/#{hash}", 300) do - sub_ids = GoogleBigQuery::PostVersion.new.find(constraints[:user_id], constraints[:added_tags], constraints[:removed_tags]) - end - q = q.where("post_versions.id in (?)", sub_ids) + if constraints[:added_tags] || constraints[:removed_tags] + hash = CityHash.hash64("#{constraints[:added_tags]} #{constraints{removed_tags}} #{constraints[:min_version_id]} #{constraints[:max_version_id]}").to_s(36) + sub_ids = Cache.get("br/fpv/#{hash}", 300) do + GoogleBigQuery::PostVersion.new.find(constraints[:user_id], constraints[:added_tags], constraints[:removed_tags], constraints[:min_version_id], constraints[:max_version_id], BIG_QUERY_LIMIT) end + + if sub_ids.size >= BIG_QUERY_LIMIT + raise ConstraintTooGeneralError.new + end + + q = q.where("post_versions.id in (?)", sub_ids) end if constraints[:min_version_id].present? diff --git a/app/logical/google_big_query/post_version.rb b/app/logical/google_big_query/post_version.rb index be0424f55..9351a20ef 100644 --- a/app/logical/google_big_query/post_version.rb +++ b/app/logical/google_big_query/post_version.rb @@ -3,34 +3,50 @@ module GoogleBigQuery def find_removed(tag, limit = 1_000) tag = escape(tag) limit = limit.to_i - query("select id, post_id, updated_at, updater_id, updater_ip_addr, tags, added_tags, removed_tags, parent_id, rating, source from [#{data_set}.post_versions] where regexp_match(removed_tags, \"(?:^| )#{tag}(?:$| )\") order by updated_at desc limit #{limit}") + query("select id, post_id, updated_at, updater_id, updater_ip_addr, tags, added_tags, removed_tags, parent_id, rating, source from [#{data_set}.post_versions] where #{remove_tag_condition(tag)} order by updated_at desc limit #{limit}") end def find_added(tag, limit = 1_000) tag = escape(tag) limit = limit.to_i - query("select id, post_id, updated_at, updater_id, updater_ip_addr, tags, added_tags, removed_tags, parent_id, rating, source from [#{data_set}.post_versions] where regexp_match(added_tags, \"(?:^| )#{tag}(?:$| )\") order by updated_at desc limit #{limit}") + query("select id, post_id, updated_at, updater_id, updater_ip_addr, tags, added_tags, removed_tags, parent_id, rating, source from [#{data_set}.post_versions] where #{add_tag_condition(tag)} order by updated_at desc limit #{limit}") end - def find(user_id, added_tags, removed_tags, limit = 1_000) + def add_tag_condition(t) + es = escape(t) + "regexp_match(added_tags, \"(?:^| )#{es}(?:$| )\")" + end + + def remove_tag_condition(t) + es = escape(t) + "regexp_match(removed_tags, \"(?:^| )#{es}(?:$| )\")" + end + + def find(user_id, added_tags, removed_tags, min_version_id, max_version_id, limit = 1_000) constraints = [] constraints << "updater_id = #{user_id.to_i}" if added_tags added_tags.scan(/\S+/).each do |tag| - escaped = escape(tag) - constraints << "regexp_match(added_tags, \"(?:^| )#{escaped}(?:$| )\")" + constraints << add_tag_condition(tag) end end if removed_tags removed_tags.scan(/\S+/).each do |tag| - escaped = escape(tag) - constraints << "not regexp_match(added_tags, \"(?:^| )#{escaped}(?:$| )\")" + constraints << remove_tag_condition(tag) end end + if min_version_id + constraints << "id >= #{min_version_id.to_i}" + end + + if max_version_id + constraints << "id <= #{max_version_id.to_i}" + end + limit = limit.to_i sql = "select id from [#{data_set}.post_versions] where " + constraints.join(" and ") + " order by updated_at desc limit #{limit}" result = query(sql) diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb index fc3ea685a..95fd2c3df 100644 --- a/app/views/layouts/default.html.erb +++ b/app/views/layouts/default.html.erb @@ -12,7 +12,7 @@ <% if CurrentUser.user.blacklisted_tags.present? %> "> <% end %> - <% if flash[:notice] =~ /error/ %> + <% if flash[:notice] =~ /error/i %> <% end %> diff --git a/app/views/moderator/bulk_reverts/new.html.erb b/app/views/moderator/bulk_reverts/new.html.erb index af8caf1c6..9caf16328 100644 --- a/app/views/moderator/bulk_reverts/new.html.erb +++ b/app/views/moderator/bulk_reverts/new.html.erb @@ -2,9 +2,11 @@
You must preview a bulk revert before processing it. Previews may take several minutes to generate.
+ <%= form_tag(moderator_bulk_revert_path, :class => "simple_form") do %>