modqueue: add sidebar.
Add a sidebar to the modqueue page that shows the following information: * Number of pending and flagged posts. * Number of posts disapproved for poor quality or breaking rules. * Top uploaders in the queue. * Top artist, copyright, and character tags in the queue.
This commit is contained in:
@@ -1,9 +1,22 @@
|
|||||||
class ModqueueController < ApplicationController
|
class ModqueueController < ApplicationController
|
||||||
respond_to :html, :json, :xml
|
respond_to :html, :json, :xml
|
||||||
before_action :approver_only
|
before_action :approver_only
|
||||||
|
layout "sidebar"
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@posts = Post.includes(:appeals, :disapprovals, :uploader, flags: [:creator]).reorder(id: :asc).pending_or_flagged.available_for_moderation(search_params[:hidden]).tag_match(search_params[:tags]).paginated_search(params, count_pages: true)
|
@posts = Post.includes(:appeals, :disapprovals, :uploader, flags: [:creator]).pending_or_flagged.available_for_moderation(search_params[:hidden]).tag_match(search_params[:tags])
|
||||||
|
|
||||||
|
@pending_post_count = @posts.pending.count
|
||||||
|
@flagged_post_count = @posts.flagged.count
|
||||||
|
@disapproval_reasons = PostDisapproval.where(post: @posts).where.not(reason: "disinterest").group(:reason).order(count: :desc).distinct.count(:post_id)
|
||||||
|
@uploaders = @posts.reorder(nil).group(:uploader).order(count: :desc).limit(20).count
|
||||||
|
|
||||||
|
@tags = RelatedTagCalculator.frequent_tags_for_post_relation(@posts)
|
||||||
|
@artist_tags = @tags.select { |tag| tag.category == Tag.categories.artist }.sort_by(&:overlap_count).reverse.take(10)
|
||||||
|
@copyright_tags = @tags.select { |tag| tag.category == Tag.categories.copyright }.sort_by(&:overlap_count).reverse.take(10)
|
||||||
|
@character_tags = @tags.select { |tag| tag.category == Tag.categories.character }.sort_by(&:overlap_count).reverse.take(10)
|
||||||
|
|
||||||
|
@posts = @posts.reorder(id: :asc).paginated_search(params, count_pages: true)
|
||||||
respond_with(@posts)
|
respond_with(@posts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ module RelatedTagCalculator
|
|||||||
|
|
||||||
def self.frequent_tags_for_search(tag_query, search_sample_size: 1000, category: nil)
|
def self.frequent_tags_for_search(tag_query, search_sample_size: 1000, category: nil)
|
||||||
sample_posts = Post.tag_match(tag_query).reorder(:md5).limit(search_sample_size)
|
sample_posts = Post.tag_match(tag_query).reorder(:md5).limit(search_sample_size)
|
||||||
tag_counts = Post.from(sample_posts).with_unflattened_tags.group("tag").select("tag, COUNT(*) AS overlap_count")
|
frequent_tags_for_post_relation(sample_posts, category: category)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.frequent_tags_for_post_relation(posts, category: nil)
|
||||||
|
tag_counts = Post.from(posts).with_unflattened_tags.group("tag").select("tag, COUNT(*) AS overlap_count")
|
||||||
|
|
||||||
tags = Tag.from(tag_counts).joins("JOIN tags ON tags.name = tag")
|
tags = Tag.from(tag_counts).joins("JOIN tags ON tags.name = tag")
|
||||||
tags = tags.select("tags.*, overlap_count")
|
tags = tags.select("tags.*, overlap_count")
|
||||||
@@ -27,7 +31,7 @@ module RelatedTagCalculator
|
|||||||
tags
|
tags
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.frequent_tags_for_posts(posts)
|
def self.frequent_tags_for_post_array(posts)
|
||||||
tags_with_counts = posts.flat_map(&:tag_array).group_by(&:itself).transform_values(&:size)
|
tags_with_counts = posts.flat_map(&:tag_array).group_by(&:itself).transform_values(&:size)
|
||||||
tags_with_counts.sort_by { |tag_name, count| [-count, tag_name] }.map(&:first)
|
tags_with_counts.sort_by { |tag_name, count| [-count, tag_name] }.map(&:first)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ module PostSetPresenters
|
|||||||
end
|
end
|
||||||
|
|
||||||
def frequent_tags
|
def frequent_tags
|
||||||
RelatedTagCalculator.frequent_tags_for_posts(post_set.posts).take(MAX_TAGS)
|
RelatedTagCalculator.frequent_tags_for_post_array(post_set.posts).take(MAX_TAGS)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pattern_tags
|
def pattern_tags
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
<% content_for(:layout) do %>
|
<% content_for(:layout) do %>
|
||||||
<div id="c-<%= params[:controller].parameterize.dasherize %>">
|
<div id="c-<%= params[:controller].parameterize.dasherize %>">
|
||||||
<div id="a-<%= params[:action].parameterize.dasherize %>" class="sidebar-container">
|
<div id="a-<%= params[:action].parameterize.dasherize %>">
|
||||||
<aside id="sidebar">
|
<% if content_for(:top_content).present? %>
|
||||||
<%= yield :sidebar %>
|
<section id="top-content">
|
||||||
</aside>
|
<%= yield :top_content %>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<section id="content">
|
<div class="sidebar-container">
|
||||||
<%= yield :content %>
|
<aside id="sidebar">
|
||||||
</section>
|
<%= yield :sidebar %>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section id="content">
|
||||||
|
<%= yield :content %>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
54
app/views/modqueue/_sidebar.html.erb
Normal file
54
app/views/modqueue/_sidebar.html.erb
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<% content_for(:sidebar) do %>
|
||||||
|
<h2>Search</h2>
|
||||||
|
|
||||||
|
<%= search_form_for(modqueue_index_path, classes: "one-line-form") do |f| %>
|
||||||
|
<%= f.input :tags, label: false, input_html: { placeholder: "Tags", value: params.dig(:search, :tags), "data-autocomplete": "tag-query" } %>
|
||||||
|
<%= f.button :button, name: nil, id: "search-box-submit" do %>
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= render "posts/partials/index/blacklist" %>
|
||||||
|
|
||||||
|
<p id="modqueue-sidebar-status" class="sidebar-section">
|
||||||
|
<h6>Status</h6>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<%= link_to "status:pending", modqueue_index_path(search: { tags: "status:pending" }) %>
|
||||||
|
<span class="post-count"><%= @pending_post_count %></span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link_to "status:flagged", modqueue_index_path(search: { tags: "status:flagged" }) %>
|
||||||
|
<span class="post-count"><%= @flagged_post_count %></span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<% @disapproval_reasons.each do |reason, count| %>
|
||||||
|
<li>
|
||||||
|
<%= link_to "disapproved:#{reason}", modqueue_index_path(search: { tags: "disapproved:#{reason}" }) %>
|
||||||
|
<span class="post-count"><%= count %></span>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p id="modqueue-sidebar-uploaders" class="sidebar-section">
|
||||||
|
<h6>Uploaders</h6>
|
||||||
|
<ul>
|
||||||
|
<% @uploaders.each do |uploader, count| %>
|
||||||
|
<li>
|
||||||
|
<%= link_to_user uploader %>
|
||||||
|
<%= link_to "»", modqueue_index_path(search: { tags: "user:#{uploader.name}" }) %>
|
||||||
|
<span class="post-count"><%= count %></span>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p id="modqueue-sidebar-tags" class="sidebar-section">
|
||||||
|
<h6>Tags</h6>
|
||||||
|
|
||||||
|
<%= render "tag_list", tags: @artist_tags %>
|
||||||
|
<%= render "tag_list", tags: @copyright_tags %>
|
||||||
|
<%= render "tag_list", tags: @character_tags %>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
10
app/views/modqueue/_tag_list.html.erb
Normal file
10
app/views/modqueue/_tag_list.html.erb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<ul>
|
||||||
|
<% tags.each do |tag| %>
|
||||||
|
<li>
|
||||||
|
<%= link_to "?", wiki_page_path(tag.name), class: tag_class(tag) %>
|
||||||
|
<%= link_to tag.pretty_name, posts_path(tags: tag.name), class: tag_class(tag) %>
|
||||||
|
<%= link_to "»", modqueue_index_path(search: { tags: tag.name }), class: tag_class(tag) %>
|
||||||
|
<span class="post-count"><%= tag.overlap_count %></span>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
@@ -1,39 +1,33 @@
|
|||||||
<% page_title "Mod Queue" %>
|
<% page_title "Moderation Queue" %>
|
||||||
|
|
||||||
<div id="c-modqueue">
|
<% content_for(:top_content) do %>
|
||||||
<div id="a-index">
|
<h1>Moderation Queue</h1>
|
||||||
<div>
|
|
||||||
<h1>Moderation Queue</h1>
|
|
||||||
|
|
||||||
<%= search_form_for(modqueue_index_path) do |f| %>
|
<div id="moderation-guideline" class="fixed-width-container">
|
||||||
<%= f.input :tags, input_html: { value: params.dig(:search, :tags), "data-autocomplete": "tag-query" } %>
|
<h2>Deletion Guidelines</h2>
|
||||||
<%= f.submit "Search" %>
|
|
||||||
|
<%= render "desc" %>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<% if params.dig(:search, :hidden) %>
|
||||||
|
<%= link_to "View pending posts", modqueue_index_path(search: { tags: params.dig(:search, :tags), hidden: nil }) %>.
|
||||||
|
<% else %>
|
||||||
|
<%= link_to "View hidden posts", modqueue_index_path(search: { tags: params.dig(:search, :tags), hidden: true, }) %>.
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</p>
|
||||||
<div id="moderation-guideline" class="fixed-width-container">
|
|
||||||
<h2>Deletion Guidelines</h2>
|
|
||||||
|
|
||||||
<%= render "desc" %>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<% if params.dig(:search, :hidden) %>
|
|
||||||
<%= link_to "View pending posts", modqueue_index_path(search: { tags: params.dig(:search, :tags), hidden: nil }) %>.
|
|
||||||
<% else %>
|
|
||||||
<%= link_to "View hidden posts", modqueue_index_path(search: { tags: params.dig(:search, :tags), hidden: true, }) %>.
|
|
||||||
<% end %>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%= render "posts/partials/common/inline_blacklist" %>
|
|
||||||
|
|
||||||
<% @posts.each do |post| %>
|
|
||||||
<%= render "post", post: post %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%= numbered_paginator(@posts) %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% end %>
|
||||||
|
|
||||||
|
<% content_for(:content) do %>
|
||||||
|
<h2>Posts</h2>
|
||||||
|
|
||||||
|
<% @posts.each do |post| %>
|
||||||
|
<%= render "post", post: post %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= numbered_paginator(@posts) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= render "modqueue/sidebar" %>
|
||||||
<%= render "post_disapprovals/detailed_rejection_dialog" %>
|
<%= render "post_disapprovals/detailed_rejection_dialog" %>
|
||||||
<%= render "posts/partials/common/secondary_links" %>
|
<%= render "posts/partials/common/secondary_links" %>
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ class RelatedTagCalculatorTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
context "RelatedTagCalculator" do
|
context "RelatedTagCalculator" do
|
||||||
context "#frequent_tags_for_posts" do
|
context "#frequent_tags_for_post_array" do
|
||||||
should "calculate the most frequent tags for a set of posts" do
|
should "calculate the most frequent tags for a set of posts" do
|
||||||
create(:post, tag_string: "aaa bbb ccc ddd")
|
create(:post, tag_string: "aaa bbb ccc ddd")
|
||||||
create(:post, tag_string: "aaa bbb ccc")
|
create(:post, tag_string: "aaa bbb ccc")
|
||||||
create(:post, tag_string: "aaa bbb")
|
create(:post, tag_string: "aaa bbb")
|
||||||
posts = Post.tag_match("aaa")
|
posts = Post.tag_match("aaa")
|
||||||
|
|
||||||
assert_equal(%w[aaa bbb ccc ddd], RelatedTagCalculator.frequent_tags_for_posts(posts))
|
assert_equal(%w[aaa bbb ccc ddd], RelatedTagCalculator.frequent_tags_for_post_array(posts))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user