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:
evazion
2020-02-29 17:04:39 -06:00
parent 475d6ae7cf
commit 9ddf408ec5
8 changed files with 128 additions and 45 deletions

View File

@@ -1,9 +1,22 @@
class ModqueueController < ApplicationController
respond_to :html, :json, :xml
before_action :approver_only
layout "sidebar"
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)
end
end

View File

@@ -17,7 +17,11 @@ module RelatedTagCalculator
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)
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 = tags.select("tags.*, overlap_count")
@@ -27,7 +31,7 @@ module RelatedTagCalculator
tags
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.sort_by { |tag_name, count| [-count, tag_name] }.map(&:first)
end

View File

@@ -44,7 +44,7 @@ module PostSetPresenters
end
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
def pattern_tags

View File

@@ -1,13 +1,21 @@
<% content_for(:layout) do %>
<div id="c-<%= params[:controller].parameterize.dasherize %>">
<div id="a-<%= params[:action].parameterize.dasherize %>" class="sidebar-container">
<aside id="sidebar">
<%= yield :sidebar %>
</aside>
<div id="a-<%= params[:action].parameterize.dasherize %>">
<% if content_for(:top_content).present? %>
<section id="top-content">
<%= yield :top_content %>
</section>
<% end %>
<section id="content">
<%= yield :content %>
</section>
<div class="sidebar-container">
<aside id="sidebar">
<%= yield :sidebar %>
</aside>
<section id="content">
<%= yield :content %>
</section>
</div>
</div>
</div>
<% end %>

View 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 %>

View 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>

View File

@@ -1,39 +1,33 @@
<% page_title "Mod Queue" %>
<% page_title "Moderation Queue" %>
<div id="c-modqueue">
<div id="a-index">
<div>
<h1>Moderation Queue</h1>
<% content_for(:top_content) do %>
<h1>Moderation Queue</h1>
<%= search_form_for(modqueue_index_path) do |f| %>
<%= f.input :tags, input_html: { value: params.dig(:search, :tags), "data-autocomplete": "tag-query" } %>
<%= f.submit "Search" %>
<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 %>
<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) %>
</p>
</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 "posts/partials/common/secondary_links" %>

View File

@@ -13,14 +13,14 @@ class RelatedTagCalculatorTest < ActiveSupport::TestCase
end
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
create(:post, tag_string: "aaa bbb ccc ddd")
create(:post, tag_string: "aaa bbb ccc")
create(:post, tag_string: "aaa bbb")
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