Skip to content

Commit

Permalink
aliases/implications: change automatic retirement rules.
Browse files Browse the repository at this point in the history
Change the rules for automatically retiring aliases and implications:

* Retire aliases to tags that are empty, or that are for a general or
  artist tag that hasn't received any new posts in the last two years.
* Retire implications from tags that are empty.
* Don't retire aliases or implications for character, copyright, or
  meta tags any more, unless the tags are empty.
  • Loading branch information
evazion committed Nov 5, 2021
1 parent 65ab7f1 commit a5f589f
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 92 deletions.
47 changes: 21 additions & 26 deletions app/logical/tag_relationship_retirement_service.rb
@@ -1,27 +1,25 @@
# Removes tag aliases and implications if they haven't had any new uploads in
# the last two years. Runs weekly. Posts a message to the forum when aliases or
# implications are retired.
# Removes inactive aliases and implications. 'Inactive' means aliases to empty
# tags, implications from empty tags, and gentag and artist aliases that
# haven't had any new posts in the last two years.
#
# Runs weekly. Posts a message to the forum when aliases or implications are
# retired.
#
# @see DanbooruMaintenance#weekly
module TagRelationshipRetirementService
module_function

THRESHOLD = 2.years

def forum_topic_title
"Retired tag aliases & implications"
end

def forum_topic_body
"This topic deals with tag relationships created two or more years ago that have not been used since. They will be retired. This topic will be updated as an automated system retires expired relationships."
end
FORUM_TOPIC_TITLE = "Retired tag aliases & implications"
FORUM_TOPIC_BODY = "This topic deals with tag relationships created two or more years ago that have not been used since. They will be retired. This topic will be updated as an automated system retires expired relationships."

def forum_topic
topic = ForumTopic.where(title: forum_topic_title).first
topic = ForumTopic.where(title: FORUM_TOPIC_TITLE).first
if topic.nil?
CurrentUser.scoped(User.system) do
topic = ForumTopic.create!(creator: User.system, title: forum_topic_title, category_id: 1)
ForumPost.create!(creator: User.system, body: forum_topic_body, topic: topic)
topic = ForumTopic.create!(creator: User.system, title: FORUM_TOPIC_TITLE, category_id: 1)
ForumPost.create!(creator: User.system, body: FORUM_TOPIC_BODY, topic: topic)
end
end
topic
Expand All @@ -30,26 +28,23 @@ def forum_topic
def find_and_retire!
messages = []

[TagAlias, TagImplication].each do |model|
each_candidate(model) do |rel|
rel.update(status: "retired")
messages << rel.retirement_message
end
inactive_relationships.each do |rel|
rel.update!(status: "retired")
messages << "The #{rel.relationship} [[#{rel.antecedent_name}]] -> [[#{rel.consequent_name}]] has been retired."
end

updater = ForumUpdater.new(forum_topic)
updater.update(messages.sort.join("\n"))
end

def each_candidate(model)
model.active.where("created_at < ?", THRESHOLD.ago).find_each do |rel|
if is_unused?(rel.consequent_name)
yield(rel)
end
end
def inactive_relationships
(inactive_aliases + TagAlias.active.empty + TagImplication.active.empty).uniq
end

def is_unused?(name)
!Post.raw_tag_match(name).exists?(["created_at > ?", THRESHOLD.ago])
def inactive_aliases
aliases = TagAlias.general.or(TagAlias.artist).active.where("tag_aliases.created_at < ?", THRESHOLD.ago)
aliases.select do |tag_alias|
!tag_alias.consequent_tag.posts.exists?(["created_at > ?", THRESHOLD.ago])
end
end
end
2 changes: 2 additions & 0 deletions app/models/tag_alias.rb
Expand Up @@ -6,6 +6,8 @@ class TagAlias < TagRelationship

before_create :delete_conflicting_relationships

scope :empty, -> { joins(:consequent_tag).merge(Tag.empty) }

def self.to_aliased(names)
names = Array(names).map(&:to_s)
return [] if names.empty?
Expand Down
2 changes: 2 additions & 0 deletions app/models/tag_implication.rb
Expand Up @@ -15,6 +15,8 @@ class TagImplication < TagRelationship
validate :meets_tag_size_requirements, on: :request
validate :has_wiki_page, on: :request

scope :empty, -> { joins(:antecedent_tag).merge(Tag.empty) }

concerning :HierarchyMethods do
class_methods do
def ancestors_of(names)
Expand Down
10 changes: 6 additions & 4 deletions app/models/tag_relationship.rb
Expand Up @@ -16,6 +16,12 @@ class TagRelationship < ApplicationRecord
scope :deleted, -> {where(status: "deleted")}
scope :retired, -> {where(status: "retired")}

# TagAlias.artist, TagAlias.general, TagAlias.character, TagAlias.copyright, TagAlias.meta
# TagImplication.artist, TagImplication.general, TagImplication.character, TagImplication.copyright, TagImplication.meta
TagCategory.categories.each do |category|
scope category, -> { joins(:consequent_tag).where(consequent_tag: { category: TagCategory.mapping[category] }) }
end

before_validation :normalize_names
validates :status, inclusion: { in: STATUSES }
validates :antecedent_name, presence: true
Expand Down Expand Up @@ -103,10 +109,6 @@ def relationship
# "TagAlias" -> "tag alias", "TagImplication" -> "tag implication"
self.class.name.underscore.tr("_", " ")
end

def retirement_message
"The #{relationship} [[#{antecedent_name}]] -> [[#{consequent_name}]] has been retired."
end
end

def antecedent_and_consequent_are_different
Expand Down
105 changes: 105 additions & 0 deletions test/jobs/retire_tag_relationships_job_test.rb
@@ -0,0 +1,105 @@
require 'test_helper'

class RetireTagRelationshipsJobTest < ActiveJob::TestCase
context "RetireTagRelationshipsJob" do
should "create a new forum topic if one doesn't already exist" do
create(:tag_alias, created_at: 3.years.ago, antecedent_name: "0", consequent_name: "1")
RetireTagRelationshipsJob.perform_now

assert_equal(true, ForumTopic.exists?(title: TagRelationshipRetirementService::FORUM_TOPIC_TITLE))
assert_equal(true, ForumPost.exists?(body: TagRelationshipRetirementService::FORUM_TOPIC_BODY))
end

should "retire inactive gentag and artist aliases" do
ta0 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "0", consequent_name: "artist")
ta1 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "1", consequent_name: "general")
ta2 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "2", consequent_name: "character")
ta3 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "3", consequent_name: "copyright")
ta4 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "4", consequent_name: "meta")

as(User.system) do
create(:post, created_at: 3.years.ago, tag_string: "art:artist")
create(:post, created_at: 3.years.ago, tag_string: "gen:general")
create(:post, created_at: 3.years.ago, tag_string: "char:character")
create(:post, created_at: 3.years.ago, tag_string: "copy:copyright")
create(:post, created_at: 3.years.ago, tag_string: "meta:meta")
end

RetireTagRelationshipsJob.perform_now

assert_equal(true, ta0.reload.is_retired?)
assert_equal(true, ta1.reload.is_retired?)
assert_equal(false, ta2.reload.is_retired?)
assert_equal(false, ta3.reload.is_retired?)
assert_equal(false, ta4.reload.is_retired?)
end

should "not retire old aliases with recent posts" do
ta0 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "0", consequent_name: "artist")
ta1 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "1", consequent_name: "general")
ta2 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "2", consequent_name: "character")
ta3 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "3", consequent_name: "copyright")
ta4 = create(:tag_alias, created_at: 3.years.ago, antecedent_name: "4", consequent_name: "meta")

as(User.system) do
create(:post, created_at: 1.week.ago, tag_string: "art:artist")
create(:post, created_at: 1.week.ago, tag_string: "gen:general")
create(:post, created_at: 1.week.ago, tag_string: "char:character")
create(:post, created_at: 1.week.ago, tag_string: "copy:copyright")
create(:post, created_at: 1.week.ago, tag_string: "meta:meta")
end

RetireTagRelationshipsJob.perform_now

assert_equal(false, ta0.reload.is_retired?)
assert_equal(false, ta1.reload.is_retired?)
assert_equal(false, ta2.reload.is_retired?)
assert_equal(false, ta3.reload.is_retired?)
assert_equal(false, ta4.reload.is_retired?)
end

should "retire empty aliases" do
create(:tag, name: "artist", post_count: 0, category: Tag.categories.artist)
create(:tag, name: "general", post_count: 0, category: Tag.categories.general)
create(:tag, name: "character", post_count: 0, category: Tag.categories.character)
create(:tag, name: "copyright", post_count: 0, category: Tag.categories.copyright)
create(:tag, name: "meta", post_count: 0, category: Tag.categories.meta)

ta0 = create(:tag_alias, antecedent_name: "0", consequent_name: "artist")
ta1 = create(:tag_alias, antecedent_name: "1", consequent_name: "general")
ta2 = create(:tag_alias, antecedent_name: "2", consequent_name: "character")
ta3 = create(:tag_alias, antecedent_name: "3", consequent_name: "copyright")
ta4 = create(:tag_alias, antecedent_name: "4", consequent_name: "meta")

RetireTagRelationshipsJob.perform_now

assert_equal(true, ta0.reload.is_retired?)
assert_equal(true, ta1.reload.is_retired?)
assert_equal(true, ta2.reload.is_retired?)
assert_equal(true, ta3.reload.is_retired?)
assert_equal(true, ta4.reload.is_retired?)
end

should "retire empty implications" do
create(:tag, name: "artist", post_count: 0, category: Tag.categories.artist)
create(:tag, name: "general", post_count: 0, category: Tag.categories.general)
create(:tag, name: "character", post_count: 0, category: Tag.categories.character)
create(:tag, name: "copyright", post_count: 0, category: Tag.categories.copyright)
create(:tag, name: "meta", post_count: 0, category: Tag.categories.meta)

ta0 = create(:tag_implication, antecedent_name: "artist", consequent_name: "1")
ta1 = create(:tag_implication, antecedent_name: "general", consequent_name: "1")
ta2 = create(:tag_implication, antecedent_name: "character", consequent_name: "1")
ta3 = create(:tag_implication, antecedent_name: "copyright", consequent_name: "1")
ta4 = create(:tag_implication, antecedent_name: "meta", consequent_name: "1")

RetireTagRelationshipsJob.perform_now

assert_equal(true, ta0.reload.is_retired?)
assert_equal(true, ta1.reload.is_retired?)
assert_equal(true, ta2.reload.is_retired?)
assert_equal(true, ta3.reload.is_retired?)
assert_equal(true, ta4.reload.is_retired?)
end
end
end
62 changes: 0 additions & 62 deletions test/unit/tag_relationship_retirement_service_test.rb

This file was deleted.

0 comments on commit a5f589f

Please sign in to comment.