From 02fb753e77bcdc662fe3479e15fbb3a61398e07b Mon Sep 17 00:00:00 2001 From: ArtOfCode- Date: Wed, 16 May 2018 22:31:21 +0100 Subject: [PATCH] It's alive! ALIIIIVE! --- app/assets/javascripts/review_queues.coffee | 3 ++ app/assets/stylesheets/review_queues.scss | 3 ++ app/controllers/review_queues_controller.rb | 47 +++++++++++++++++++ app/helpers/review_queues_helper.rb | 4 ++ app/javascript/review.js | 19 ++++++++ app/models/feedback.rb | 1 - app/models/post.rb | 23 ++++++++- app/models/review_item.rb | 12 ++++- app/models/review_queue.rb | 7 +++ app/views/layouts/application.html.erb | 4 +- app/views/posts/_review_item.html.erb | 4 ++ app/views/review_queues/_responses.html.erb | 8 ++++ app/views/review_queues/index.html.erb | 13 +++++ app/views/review_queues/queue.html.erb | 6 +++ config/routes.rb | 15 +++--- .../20180515234548_create_review_queues.rb | 1 - ...20180516122833_setup_posts_review_queue.rb | 7 +++ ...516123826_add_completed_to_review_items.rb | 7 +++ ...201704_add_description_to_review_queues.rb | 7 +++ ...3_add_description_to_posts_review_queue.rb | 7 +++ db/schema.rb | 5 +- .../review_queues_controller_test.rb | 9 ++++ 22 files changed, 196 insertions(+), 16 deletions(-) create mode 100644 app/assets/javascripts/review_queues.coffee create mode 100644 app/assets/stylesheets/review_queues.scss create mode 100644 app/controllers/review_queues_controller.rb create mode 100644 app/helpers/review_queues_helper.rb create mode 100644 app/views/posts/_review_item.html.erb create mode 100644 app/views/review_queues/_responses.html.erb create mode 100644 app/views/review_queues/index.html.erb create mode 100644 app/views/review_queues/queue.html.erb create mode 100644 db/migrate/20180516122833_setup_posts_review_queue.rb create mode 100644 db/migrate/20180516123826_add_completed_to_review_items.rb create mode 100644 db/migrate/20180516201704_add_description_to_review_queues.rb create mode 100644 db/migrate/20180516201733_add_description_to_posts_review_queue.rb create mode 100644 test/controllers/review_queues_controller_test.rb diff --git a/app/assets/javascripts/review_queues.coffee b/app/assets/javascripts/review_queues.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/review_queues.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/review_queues.scss b/app/assets/stylesheets/review_queues.scss new file mode 100644 index 000000000..48fe787b3 --- /dev/null +++ b/app/assets/stylesheets/review_queues.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the ReviewQueues controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/review_queues_controller.rb b/app/controllers/review_queues_controller.rb new file mode 100644 index 000000000..8463a0d8f --- /dev/null +++ b/app/controllers/review_queues_controller.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class ReviewQueuesController < ApplicationController + before_action :set_queue, except: [:index] + before_action :verify_permissions, except: [:index] + + def index + @queues = ReviewQueue.all.includes(:items) + end + + def queue; end + + def next_item + unreviewed = ReviewItem.unreviewed_by(@queue, current_user) + if unreviewed.empty? + render plain: "You've reviewed all available items!" + else + item = unreviewed.first + render "#{item.reviewable_type.underscore.pluralize}/_review_item.html.erb", locals: { queue: @queue, item: item }, layout: nil + end + end + + def submit + render json: { status: 'invalid' }, status: 400 unless @queue.responses.map { |r| r[1] }.include? params[:response] + @item = ReviewItem.find params[:item_id] + + ReviewResult.create user: current_user, result: params[:response], item: @item + + @item.reviewable.custom_review_action(@queue, @item, current_user, params[:response]) if @item.reviewable.respond_to? :custom_review_action + if @item.reviewable.respond_to?(:should_dq?) && @item.reviewable.should_dq?(@queue) + @item.update(completed: true) + end + + render json: { status: 'ok' } + end + + private + + def set_queue + @queue = ReviewQueue[params[:name]] + end + + def verify_permissions + return if user_signed_in? && current_user.has_role?(@queue.privileges) + not_found + end +end diff --git a/app/helpers/review_queues_helper.rb b/app/helpers/review_queues_helper.rb new file mode 100644 index 000000000..270856fe5 --- /dev/null +++ b/app/helpers/review_queues_helper.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module ReviewQueuesHelper +end diff --git a/app/javascript/review.js b/app/javascript/review.js index ce913a670..4e95d4b40 100644 --- a/app/javascript/review.js +++ b/app/javascript/review.js @@ -1,3 +1,5 @@ +import { route } from './util'; + $(() => { $(document).on('ajax:success', 'a.feedback-button[data-remote]', e => { if (!$(e.target).hasClass('on-post')) { @@ -6,3 +8,20 @@ $(() => { } }); }); + +route(/\/review\/\w+/i, async () => { + const loadNextPost = async () => { + const response = await fetch(location.pathname + '/next', { + credentials: 'include' + }); + const html = await response.text(); + $('.review-item-container').html(html); + }; + + loadNextPost(); + + $(document).on('ajax:success', '.review-submit-link', () => { + $('.review-item-container').text('Loading...'); + loadNextPost(); + }); +}); diff --git a/app/models/feedback.rb b/app/models/feedback.rb index 33ee7dc6e..35701e757 100644 --- a/app/models/feedback.rb +++ b/app/models/feedback.rb @@ -13,7 +13,6 @@ class Feedback < ApplicationRecord belongs_to :user belongs_to :invalidated_by, class_name: 'User', foreign_key: 'invalidated_by' belongs_to :api_key - has_one :review, class_name: 'ReviewResult', required: false, dependent: :destroy before_save :check_for_user_assoc before_save :check_for_dupe_feedback diff --git a/app/models/post.rb b/app/models/post.rb index 279d9a544..bf6ce1d74 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -18,7 +18,7 @@ class Post < ApplicationRecord has_many :flag_logs, dependent: :destroy has_many :flags, dependent: :destroy has_and_belongs_to_many :spam_domains - has_many :reviews, class_name: 'ReviewResult', dependent: :destroy + has_one :review_item, as: :reviewable has_many :comments, class_name: 'PostComment', dependent: :destroy scope(:includes_for_post_row, -> do @@ -39,6 +39,14 @@ class Post < ApplicationRecord scope(:undeleted, -> { where(deleted_at: nil) }) + after_commit do + if review_item.present? && should_dq?(ReviewQueue['posts']) + review_item.update(completed: true) + elsif !review_item.present? + ReviewItem.create(reviewable: self, queue: ReviewQueue['posts'], completed: false) + end + end + after_commit :parse_domains, on: :create after_create do @@ -319,4 +327,17 @@ def parse_domains domain.posts << self unless domain.posts.include? self end end + + def custom_review_action(_queue, _item, user, response) + feedbacks.create(user: user, feedback_type: response) + end + + def should_dq?(queue) + case queue.name + when 'posts' + feedbacks.count >= 2 + else + false + end + end end diff --git a/app/models/review_item.rb b/app/models/review_item.rb index e7e9c5d33..899c16fe8 100644 --- a/app/models/review_item.rb +++ b/app/models/review_item.rb @@ -2,7 +2,17 @@ class ReviewItem < ApplicationRecord belongs_to :user - belongs_to :review_queue + belongs_to :queue, class_name: 'ReviewQueue', foreign_key: 'review_queue_id' belongs_to :reviewable, polymorphic: true has_many :results, class_name: 'ReviewResult' + + validates :reviewable_type, inclusion: { in: ['Post'] } + + scope(:active, -> { where(completed: false) }) + scope(:completed, -> { where(completed: true) }) + + def self.unreviewed_by(queue, user) + joins("LEFT JOIN review_results rr ON rr.review_item_id = review_items.id AND rr.user_id = #{user.id}").where(review_items: { queue: queue }, + rr: { id: nil }) + end end diff --git a/app/models/review_queue.rb b/app/models/review_queue.rb index a4b8408ec..710a20ddc 100644 --- a/app/models/review_queue.rb +++ b/app/models/review_queue.rb @@ -4,7 +4,14 @@ class ReviewQueue < ApplicationRecord has_many :items, class_name: 'ReviewItem' has_many :results, class_name: 'ReviewResult', through: :items + serialize :responses, JSON + def self.[](key) find_by name: key end + + def should_dq?(item) + item.reviewable.should_dq?(self) if item.reviewable.respond_to? :should_dq? + false + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 969cdfe8d..fc2f8fc74 100755 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -49,8 +49,8 @@ <%= nav_link GraphsController %> <%= nav_link SearchController %> <% if user_signed_in? %> - <%= nav_link ReviewController do %> - <% review_items = Post.unreviewed.count %> + <%= nav_link ReviewQueuesController, label: 'review' do %> + <% review_items = ReviewItem.active.count %> <%# badge hides itself if it doesn’t have contents. %> <% unless review_items == 0 || !current_user.has_role?(:reviewer) %><%= review_items %><% end %> <% end %> diff --git a/app/views/posts/_review_item.html.erb b/app/views/posts/_review_item.html.erb new file mode 100644 index 000000000..efbbc5efe --- /dev/null +++ b/app/views/posts/_review_item.html.erb @@ -0,0 +1,4 @@ +
+ <%= render 'review_queues/responses', queue: queue, item: item %> + <%= render 'posts/post', post: item.reviewable, expand_post: true %> +
\ No newline at end of file diff --git a/app/views/review_queues/_responses.html.erb b/app/views/review_queues/_responses.html.erb new file mode 100644 index 000000000..ba8b735a8 --- /dev/null +++ b/app/views/review_queues/_responses.html.erb @@ -0,0 +1,8 @@ +
+
+ <% @queue.responses.each do |r| %> + <%= link_to r[0], submit_review_path(name: queue.name, item_id: item.id, response: r[1]), class: 'btn btn-primary review-submit-link', + method: :post, remote: true %> + <% end %> +
+
\ No newline at end of file diff --git a/app/views/review_queues/index.html.erb b/app/views/review_queues/index.html.erb new file mode 100644 index 000000000..e6cedaf96 --- /dev/null +++ b/app/views/review_queues/index.html.erb @@ -0,0 +1,13 @@ +

Review Queues

+ +<% @queues.each do |q| %> +
+
+

+ <%= link_to q.name.titleize, review_queue_path(q.name) %> + <%= q.items.active.count if q.items.active.count > 0 %> +

+

<%= q.description %>

+
+
+<% end %> \ No newline at end of file diff --git a/app/views/review_queues/queue.html.erb b/app/views/review_queues/queue.html.erb new file mode 100644 index 000000000..4b26b11df --- /dev/null +++ b/app/views/review_queues/queue.html.erb @@ -0,0 +1,6 @@ +

Review <%= @queue.name.titleize %>

+

<%= @queue.description %>

+ +
+ Loading... +
\ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index b7d14b870..f784a1e1c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -43,14 +43,6 @@ post ':id/update_mod_sites', to: 'users#update_mod_sites', as: :update_mod_sites end - scope '/review' do - root to: 'review#index', as: :review - post 'feedback', to: 'review#add_feedback', as: :review_feedback - post 'skip', to: 'review#skip', as: :review_skip - get 'history', to: 'review#history', as: :review_history - post 'unskip', to: 'review#delete_skip', as: :review_unskip - end - get 'spammers', to: 'stack_exchange_users#index' get 'spammers/sites', to: 'stack_exchange_users#sites', as: :spammers_site_index get 'spammers/site', to: 'stack_exchange_users#on_site', as: :spammers_on_site @@ -375,6 +367,13 @@ get 'processing', to: 'dashboard#data_processing', as: :data_processing end + scope 'review' do + root to: 'review_queues#index', as: :review_queues + get ':name', to: 'review_queues#queue', as: :review_queue + get ':name/next', to: 'review_queues#next_item', as: :next_review_item + post ':name/:item_id', to: 'review_queues#submit', as: :submit_review + end + # This should always be right at the end of this file, so that it doesn't override other routes. mount API::Base => '/api' end diff --git a/db/migrate/20180515234548_create_review_queues.rb b/db/migrate/20180515234548_create_review_queues.rb index a005df59a..d5cec5442 100644 --- a/db/migrate/20180515234548_create_review_queues.rb +++ b/db/migrate/20180515234548_create_review_queues.rb @@ -6,7 +6,6 @@ def change t.string :name t.string :privileges t.text :responses - t.integer :reviews_per_item t.timestamps end diff --git a/db/migrate/20180516122833_setup_posts_review_queue.rb b/db/migrate/20180516122833_setup_posts_review_queue.rb new file mode 100644 index 000000000..3831cfa77 --- /dev/null +++ b/db/migrate/20180516122833_setup_posts_review_queue.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class SetupPostsReviewQueue < ActiveRecord::Migration[5.2] + def change + ReviewQueue.create name: 'posts', privileges: 'reviewer', responses: [['True Positive', 'tp'], ['False Positive', 'fp'], %w[NAA naa]] + end +end diff --git a/db/migrate/20180516123826_add_completed_to_review_items.rb b/db/migrate/20180516123826_add_completed_to_review_items.rb new file mode 100644 index 000000000..7617fdad0 --- /dev/null +++ b/db/migrate/20180516123826_add_completed_to_review_items.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddCompletedToReviewItems < ActiveRecord::Migration[5.2] + def change + add_column :review_items, :completed, :boolean + end +end diff --git a/db/migrate/20180516201704_add_description_to_review_queues.rb b/db/migrate/20180516201704_add_description_to_review_queues.rb new file mode 100644 index 000000000..c44da3daf --- /dev/null +++ b/db/migrate/20180516201704_add_description_to_review_queues.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddDescriptionToReviewQueues < ActiveRecord::Migration[5.2] + def change + add_column :review_queues, :description, :text + end +end diff --git a/db/migrate/20180516201733_add_description_to_posts_review_queue.rb b/db/migrate/20180516201733_add_description_to_posts_review_queue.rb new file mode 100644 index 000000000..f827f5dba --- /dev/null +++ b/db/migrate/20180516201733_add_description_to_posts_review_queue.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddDescriptionToPostsReviewQueue < ActiveRecord::Migration[5.2] + def change + ReviewQueue['posts'].update(description: 'Review new reports as they come in and add feedback to them.') + end +end diff --git a/db/schema.rb b/db/schema.rb index 14e5471ac..47d2a158f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_05_15_234932) do +ActiveRecord::Schema.define(version: 2018_05_16_201733) do create_table "abuse_comments", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t| t.bigint "user_id" @@ -374,6 +374,7 @@ t.bigint "reviewable_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "completed" t.index ["review_queue_id"], name: "index_review_items_on_review_queue_id" t.index ["reviewable_type", "reviewable_id"], name: "index_review_items_on_reviewable_type_and_reviewable_id" end @@ -382,9 +383,9 @@ t.string "name" t.string "privileges" t.text "responses" - t.integer "reviews_per_item" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.text "description" end create_table "review_results", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t| diff --git a/test/controllers/review_queues_controller_test.rb b/test/controllers/review_queues_controller_test.rb new file mode 100644 index 000000000..7cade0e0c --- /dev/null +++ b/test/controllers/review_queues_controller_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class ReviewQueuesControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end