Expire unused aliases and implications after 2 years of inactivity
This commit is contained in:
67
app/logical/tag_relationship_retirement_service.rb
Normal file
67
app/logical/tag_relationship_retirement_service.rb
Normal file
@@ -0,0 +1,67 @@
|
||||
module TagRelationshipRetirementService
|
||||
THRESHOLD = 2.year
|
||||
|
||||
extend self
|
||||
|
||||
def forum_topic_title
|
||||
return "Retired tag aliases & implications"
|
||||
end
|
||||
|
||||
def forum_topic_body
|
||||
return "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
|
||||
|
||||
def dry_run
|
||||
[TagAlias, TagImplication].each do |model|
|
||||
each_candidate(model) do |rel|
|
||||
puts "#{rel.relationship} #{rel.antecedent_name} -> #{rel.consequent_name} retired"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def forum_topic
|
||||
topic = ForumTopic.where(title: forum_topic_title).first
|
||||
if topic.nil?
|
||||
topic = CurrentUser.as_system do
|
||||
ForumTopic.create(title: forum_topic_title, category_id: 1, original_post_attributes: {body: forum_topic_body})
|
||||
end
|
||||
end
|
||||
return topic
|
||||
end
|
||||
|
||||
def find_and_retire!
|
||||
messages = []
|
||||
|
||||
[TagAlias, TagImplication].each do |model|
|
||||
each_candidate(model) do |rel|
|
||||
rel.update(status: "retired")
|
||||
messages << rel.retirement_message
|
||||
end
|
||||
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
|
||||
|
||||
# model.active.where("created_at < ?", SMALL_THRESHOLD.ago).find_each do |rel|
|
||||
# if is_underused?(rel.consequent_name)
|
||||
# yield(rel)
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
def is_underused?(name)
|
||||
(Tag.find_by_name(name).try(:post_count) || 0) < COUNT_THRESHOLD
|
||||
end
|
||||
|
||||
def is_unused?(name)
|
||||
return !Post.tag_match("status:any #{name}").where("created_at > ?", THRESHOLD.ago).exists?
|
||||
end
|
||||
end
|
||||
@@ -2,6 +2,7 @@ class TagAlias < TagRelationship
|
||||
before_save :ensure_tags_exist
|
||||
after_save :clear_all_cache
|
||||
after_destroy :clear_all_cache
|
||||
after_save :clear_all_cache, if: ->(rec) {rec.is_retired?}
|
||||
after_save :create_mod_action
|
||||
validates_uniqueness_of :antecedent_name
|
||||
validate :absence_of_transitive_relation
|
||||
|
||||
@@ -2,6 +2,7 @@ class TagImplication < TagRelationship
|
||||
before_save :update_descendant_names
|
||||
after_save :update_descendant_names_for_parents
|
||||
after_destroy :update_descendant_names_for_parents
|
||||
after_save :update_descendant_names_for_parents, if: ->(rec) { rec.is_retired? }
|
||||
after_save :create_mod_action
|
||||
validates_uniqueness_of :antecedent_name, :scope => :consequent_name
|
||||
validate :absence_of_circular_relation
|
||||
|
||||
@@ -13,13 +13,15 @@ class TagRelationship < ApplicationRecord
|
||||
has_one :antecedent_tag, :class_name => "Tag", :foreign_key => "name", :primary_key => "antecedent_name"
|
||||
has_one :consequent_tag, :class_name => "Tag", :foreign_key => "name", :primary_key => "consequent_name"
|
||||
|
||||
scope :active, ->{where(status: "active")}
|
||||
scope :expired, ->{where("created_at < ?", EXPIRY.days.ago)}
|
||||
scope :old, ->{where("created_at >= ? and created_at < ?", EXPIRY.days.ago, EXPIRY_WARNING.days.ago)}
|
||||
scope :pending, ->{where(status: "pending")}
|
||||
scope :retired, ->{where(status: "retired")}
|
||||
|
||||
before_validation :initialize_creator, :on => :create
|
||||
before_validation :normalize_names
|
||||
validates_format_of :status, :with => /\A(active|deleted|pending|processing|queued|error: .*)\Z/
|
||||
validates_format_of :status, :with => /\A(active|deleted|pending|processing|queued|retired|error: .*)\Z/
|
||||
validates_presence_of :creator_id, :antecedent_name, :consequent_name
|
||||
validates :creator, presence: { message: "must exist" }, if: -> { creator_id.present? }
|
||||
validates :approver, presence: { message: "must exist" }, if: -> { approver_id.present? }
|
||||
@@ -35,6 +37,10 @@ class TagRelationship < ApplicationRecord
|
||||
self.consequent_name = consequent_name.mb_chars.downcase.tr(" ", "_")
|
||||
end
|
||||
|
||||
def is_retired?
|
||||
status == "retired"
|
||||
end
|
||||
|
||||
def is_pending?
|
||||
status == "pending"
|
||||
end
|
||||
@@ -98,6 +104,8 @@ class TagRelationship < ApplicationRecord
|
||||
|
||||
if params[:status].present?
|
||||
q = q.status_matches(params[:status])
|
||||
else
|
||||
q = q.active
|
||||
end
|
||||
|
||||
if params[:category].present?
|
||||
@@ -140,6 +148,10 @@ class TagRelationship < ApplicationRecord
|
||||
"The #{relationship} [[#{antecedent_name}]] -> [[#{consequent_name}]] #{forum_link} has been rejected by @#{rejector.name}."
|
||||
end
|
||||
|
||||
def retirement_message
|
||||
"The #{relationship} [[#{antecedent_name}]] -> [[#{consequent_name}]] #{forum_link} has been retired."
|
||||
end
|
||||
|
||||
def conflict_message
|
||||
"The tag alias [[#{antecedent_name}]] -> [[#{consequent_name}]] #{forum_link} has conflicting wiki pages. [[#{consequent_name}]] should be updated to include information from [[#{antecedent_name}]] if necessary."
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="a-index">
|
||||
<%= simple_form_for(:search, method: :get, url: tag_aliases_path, defaults: { required: false }, html: { class: "inline-form" }) do |f| %>
|
||||
<%= f.input :name_matches, label: "Name", input_html: { value: params[:search][:name_matches], data: { autocomplete: "tag" } } %>
|
||||
<%= f.input :status, label: "Status", collection: ["", "Approved", "Pending"], selected: params[:search][:status] %>
|
||||
<%= f.input :status, label: "Status", collection: ["", "Approved", "Pending", "Retired"], selected: params[:search][:status] %>
|
||||
<%= f.input :category, label: "Category", collection: TagCategory.canonical_mapping.to_a, include_blank: true, selected: params[:search][:category] %>
|
||||
<%= f.input :order, label: "Order", collection: [%w[Status status], %w[Recently\ created created_at], %w[Recently\ updated updated_at], %w[Name name], %w[Tag\ count tag_count]], selected: params[:search][:order] %>
|
||||
<%= f.submit "Search" %>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="a-index">
|
||||
<%= simple_form_for(:search, method: :get, url: tag_implications_path, defaults: { required: false }, html: { class: "inline-form" }) do |f| %>
|
||||
<%= f.input :name_matches, label: "Name", input_html: { value: params[:search][:name_matches], data: { autocomplete: "tag" } } %>
|
||||
<%= f.input :status, label: "Status", collection: ["", "Approved", "Pending"], selected: params[:search][:status] %>
|
||||
<%= f.input :status, label: "Status", collection: ["", "Approved", "Pending", "Retired"], selected: params[:search][:status] %>
|
||||
<%= f.input :category, label: "Category", collection: TagCategory.canonical_mapping.to_a, include_blank: true, selected: params[:search][:category] %>
|
||||
<%= f.input :order, label: "Order", collection: [%w[Status status], %w[Recently\ created created_at], %w[Recently\ updated updated_at], %w[Name name], %w[Tag\ count tag_count]], selected: params[:search][:order] %>
|
||||
<%= f.submit "Search" %>
|
||||
|
||||
Reference in New Issue
Block a user