From d9a17d23785b84d72b44b5d87c65e7af392441d5 Mon Sep 17 00:00:00 2001 From: albert Date: Tue, 5 Mar 2013 22:08:48 -0500 Subject: [PATCH] add new tag alias correction helper --- .../tag_alias_corrections_controller.rb | 21 +++++++ app/helpers/application_helper.rb | 2 +- app/logical/cache.rb | 4 +- app/logical/tag_alias_correction.rb | 60 +++++++++++++++++++ app/models/tag.rb | 10 ++-- app/models/tag_alias.rb | 13 ++-- app/views/tag_alias_corrections/show.html.erb | 25 ++++++++ app/views/tag_alias_corrections/show.json.erb | 1 + app/views/tag_aliases/index.html.erb | 4 ++ config/routes.rb | 2 + test/unit/tag_alias_correction_test.rb | 59 ++++++++++++++++++ 11 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 app/controllers/tag_alias_corrections_controller.rb create mode 100644 app/logical/tag_alias_correction.rb create mode 100644 app/views/tag_alias_corrections/show.html.erb create mode 100644 app/views/tag_alias_corrections/show.json.erb create mode 100644 test/unit/tag_alias_correction_test.rb diff --git a/app/controllers/tag_alias_corrections_controller.rb b/app/controllers/tag_alias_corrections_controller.rb new file mode 100644 index 000000000..62aea1113 --- /dev/null +++ b/app/controllers/tag_alias_corrections_controller.rb @@ -0,0 +1,21 @@ +class TagAliasCorrectionsController < ApplicationController + before_filter :moderator_only + + def new + @correction = TagAliasCorrection.new(params[:tag_alias_id]) + end + + def create + @correction = TagAliasCorrection.new(params[:tag_alias_id]) + + if params[:commit] == "Fix" + @correction.fix! + end + + redirect_to tag_alias_correction_path(:id => params[:tag_alias_id]) + end + + def show + @correction = TagAliasCorrection.new(params[:tag_alias_id]) + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index eb5752ff6..f57209ad7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -143,7 +143,7 @@ protected when "moderator/dashboards" /^\/moderator/ - when "tag_aliases" + when "tag_aliases", "tag_alias_corrections" /^\/tag_aliases/ when "tag_implications" diff --git a/app/logical/cache.rb b/app/logical/cache.rb index 4249bf8c4..c5f54bea9 100644 --- a/app/logical/cache.rb +++ b/app/logical/cache.rb @@ -40,7 +40,7 @@ class Cache start_time = Time.now value = MEMCACHE.get key.slice(0, 200) elapsed = Time.now - start_time - ActiveRecord::Base.logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key]) + ActiveRecord::Base.logger.debug('MemCache Get (%0.6f) %s -> %s' % [elapsed, key, value]) if value.nil? and block_given? then value = yield MEMCACHE.set key, value, expiry @@ -64,7 +64,7 @@ class Cache start_time = Time.now MEMCACHE.set key, value, expiry elapsed = Time.now - start_time - ActiveRecord::Base.logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key]) + ActiveRecord::Base.logger.debug('MemCache Set (%0.6f) %s -> %s' % [elapsed, key, value]) value rescue MemCache::MemCacheError => err ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}" diff --git a/app/logical/tag_alias_correction.rb b/app/logical/tag_alias_correction.rb new file mode 100644 index 000000000..3316c5ea5 --- /dev/null +++ b/app/logical/tag_alias_correction.rb @@ -0,0 +1,60 @@ +class TagAliasCorrection + attr_reader :tag_alias_id, :tag_alias, :hostname + delegate :antecedent_name, :consequent_name, :to => :tag_alias + + def initialize(tag_alias_id, hostname = Socket.gethostname) + @tag_alias_id = tag_alias_id + @tag_alias = TagAlias.find(tag_alias_id) + @hostname = hostname + end + + def to_json(options = {}) + statistics_hash.to_json + end + + def statistics_hash + @statistics_hash ||= { + "antecedent_cache" => Cache.get("ta:" + Cache.sanitize(tag_alias.antecedent_name)), + "consequent_cache" => Cache.get("ta:" + Cache.sanitize(tag_alias.consequent_name)), + "antecedent_count" => Tag.find_by_name(tag_alias.antecedent_name).try(:post_count), + "consequent_count" => Tag.find_by_name(tag_alias.consequent_name).try(:post_count) + } + end + + def fill_hash! + Net::HTTP.start(hostname, 80) do |http| + http.request_get("/tag_aliases/#{tag_alias_id}/correction.json") do |res| + if res === Net::HTTPSuccess + json = JSON.parse(res.body) + statistics_hash["antecedent_cache"] = json["antecdent_cache"] + statistics_hash["consequent_cache"] = json["consequent_cache"] + end + end + end + end + + def each_server + Danbooru.config.all_server_hosts.each do |host| + other = TagAliasCorrection.new(tag_alias_id, host) + + if host != Socket.gethostname + other.fill_hash! + end + + yield other + end + end + + def clear_cache + tag_alias.clear_cache + end + + def fix! + clear_cache + Post.raw_tag_match(tag_alias.antecedent_name).each do |post| + post.save + end + tag_alias.antecedent_tag.fix_post_count if tag_alias.antecedent_tag + tag_alias.consequent_tag.fix_post_count if tag_alias.consequent_tag + end +end diff --git a/app/models/tag.rb b/app/models/tag.rb index 884463860..b33c2f48a 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -34,12 +34,14 @@ class Tag < ActiveRecord::Base end module CountMethods - def counts_for(tag_names) - select_all_sql("SELECT name, post_count FROM tags WHERE name IN (?)", tag_names) + module ClassMethods + def counts_for(tag_names) + select_all_sql("SELECT name, post_count FROM tags WHERE name IN (?)", tag_names) + end end def fix_post_count - update_column(:post_count, Post.tag_match("#{name} status:any").count) + update_column(:post_count, Post.raw_tag_match(name).count) end end @@ -462,7 +464,7 @@ class Tag < ActiveRecord::Base end include ApiMethods - extend CountMethods + include CountMethods extend ViewCountMethods include CategoryMethods extend StatisticsMethods diff --git a/app/models/tag_alias.rb b/app/models/tag_alias.rb index d8795cec1..968cde74f 100644 --- a/app/models/tag_alias.rb +++ b/app/models/tag_alias.rb @@ -94,22 +94,17 @@ class TagAlias < ActiveRecord::Base def clear_cache(host = Socket.gethostname) Cache.delete("ta:#{Cache.sanitize(antecedent_name)}") + Cache.delete("ta:#{Cache.sanitize(consequent_name)}") end def update_cache Cache.put("ta:#{Cache.sanitize(antecedent_name)}", consequent_name) + Cache.delete("ta:#{Cache.sanitize(consequent_name)}") end def update_posts - Post.tag_match("#{antecedent_name} status:any").find_each do |post| - escaped_antecedent_name = Regexp.escape(antecedent_name) - fixed_tags = post.tag_string.sub(/(?:\A| )#{escaped_antecedent_name}(?:\Z| )/, " #{consequent_name} ").strip - - CurrentUser.scoped(creator, creator_ip_addr) do - post.update_attributes( - :tag_string => fixed_tags - ) - end + Post.raw_tag_match(antecedent_name).each do |post| + post.save end end end diff --git a/app/views/tag_alias_corrections/show.html.erb b/app/views/tag_alias_corrections/show.html.erb new file mode 100644 index 000000000..cae64dfd3 --- /dev/null +++ b/app/views/tag_alias_corrections/show.html.erb @@ -0,0 +1,25 @@ +

Tag Alias Correction: <%= @correction.antecedent_name %> -> <%= @correction.consequent_name %>

+ +

Because tag aliases are cached in memory, they may go out of sync. This action will clear out the cache and fix and lingering posts.

+ +<% @correction.each_server do |correction| %> +
+

Server: <%= Socket.gethostname %>

+ +
+<% end %> + +

You can try to fix this alias. This will clear the cache and re-save all posts associated with <%= @correction.antecedent_name %>.

+ +<%= form_tag(tag_alias_correction_path) do %> + <%= hidden_field "tag_alias_id", params[:tag_alias_id] %> + <%= submit_tag "Fix" %> + <%= submit_tag "Cancel" %> +<% end %> + +<%= render "tag_aliases/secondary_links" %> \ No newline at end of file diff --git a/app/views/tag_alias_corrections/show.json.erb b/app/views/tag_alias_corrections/show.json.erb new file mode 100644 index 000000000..65fa82d7a --- /dev/null +++ b/app/views/tag_alias_corrections/show.json.erb @@ -0,0 +1 @@ +<%= raw @correction.to_json %> diff --git a/app/views/tag_aliases/index.html.erb b/app/views/tag_aliases/index.html.erb index 8d936b427..ee7fdbf4f 100644 --- a/app/views/tag_aliases/index.html.erb +++ b/app/views/tag_aliases/index.html.erb @@ -38,6 +38,10 @@ <% if tag_alias.is_pending? %> | <%= link_to "Approve", approve_tag_alias_path(tag_alias), :remote => true, :method => :post %> <% end %> + + <% if CurrentUser.is_moderator? %> + | <%= link_to "Fix", tag_alias_correction_path(:tag_alias_id => tag_alias.id) %> + <% end %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index e0493c2b5..58e5784e7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -157,6 +157,7 @@ Danbooru::Application.routes.draw do end end resources :tag_aliases do + resource :correction, :only => [:new, :create, :show], :controller => "TagAliasCorrections" member do delete :cache post :approve @@ -165,6 +166,7 @@ Danbooru::Application.routes.draw do get :general_search end end + resource :tag_alias_correction, :only => [:new, :create, :show] resources :tag_implications do member do post :approve diff --git a/test/unit/tag_alias_correction_test.rb b/test/unit/tag_alias_correction_test.rb new file mode 100644 index 000000000..1fec55e2e --- /dev/null +++ b/test/unit/tag_alias_correction_test.rb @@ -0,0 +1,59 @@ +require 'test_helper' + +class TagAliasTest < ActiveSupport::TestCase + context "A tag alias" do + setup do + @mod = FactoryGirl.create(:moderator_user) + CurrentUser.user = @mod + CurrentUser.ip_addr = "127.0.0.1" + MEMCACHE.flush_all + Delayed::Worker.delay_jobs = false + @post = FactoryGirl.create(:post, :tag_string => "aaa") + @tag_alias = FactoryGirl.create(:tag_alias, :antecedent_name => "aaa", :consequent_name => "bbb") + end + + teardown do + CurrentUser.user = nil + CurrentUser.ip_addr = nil + end + + context "with a bad cache and post counts" do + setup do + Cache.put("ta:aaa", "zzz") + Tag.update_all("post_count = -3", "name = 'aaa'") + @correction = TagAliasCorrection.new(@tag_alias.id) + end + + should "have the correct statistics hash" do + assert_equal("zzz", @correction.statistics_hash["antecedent_cache"]) + assert_nil(@correction.statistics_hash["consequent_cache"]) + assert_equal(-3, @correction.statistics_hash["antecedent_count"]) + assert_nil(@correction.statistics_hash["consequent_count"]) + end + + should "render to json" do + assert_nothing_raised do + @correction.to_json + end + + assert_nothing_raised do + JSON.parse(@correction.to_json) + end + end + + context "that is fixed" do + setup do + @correction.fix! + end + + should "now have the correct cache" do + assert_equal("bbb", Cache.get("ta:aaa")) + end + + should "now have the correct count" do + assert_equal(0, Tag.find_by_name("aaa").post_count) + end + end + end + end +end