Add search form for hidden content
Added search for comments and proposal_notifications, added tsv column for search and rake tasks to update/create tsv vector.
This commit is contained in:
committed by
Javi Martín
parent
e66b9687a2
commit
2af7e32415
@@ -1,11 +1,15 @@
|
||||
module Admin::HiddenContent
|
||||
extend ActiveSupport::Concern
|
||||
include Search
|
||||
|
||||
included do
|
||||
has_filters %w[without_confirmed_hide all with_confirmed_hide], only: :index
|
||||
end
|
||||
|
||||
def hidden_content(relation)
|
||||
relation.only_hidden.send(@current_filter).order(hidden_at: :desc).page(params[:page])
|
||||
records = relation.only_hidden
|
||||
records = records.search(@search_terms) if @search_terms.present?
|
||||
|
||||
records.send(@current_filter).order(hidden_at: :desc).page(params[:page])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,7 @@ class Comment < ApplicationRecord
|
||||
include HasPublicAuthor
|
||||
include Graphqlable
|
||||
include Notifiable
|
||||
include Searchable
|
||||
|
||||
COMMENTABLE_TYPES = %w[Debate Proposal Budget::Investment Poll Topic
|
||||
Legislation::Question Legislation::Annotation
|
||||
@@ -131,6 +132,17 @@ class Comment < ApplicationRecord
|
||||
cached_votes_up - cached_votes_down
|
||||
end
|
||||
|
||||
def searchable_values
|
||||
{
|
||||
body => "A",
|
||||
commentable&.title => "B"
|
||||
}
|
||||
end
|
||||
|
||||
def self.search(terms)
|
||||
pg_search(terms)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_body_length
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class ProposalNotification < ApplicationRecord
|
||||
include Graphqlable
|
||||
include Notifiable
|
||||
include Searchable
|
||||
|
||||
belongs_to :author, class_name: "User"
|
||||
belongs_to :proposal
|
||||
@@ -55,6 +56,17 @@ class ProposalNotification < ApplicationRecord
|
||||
update(moderated: false)
|
||||
end
|
||||
|
||||
def searchable_values
|
||||
{
|
||||
title => "A",
|
||||
body => "B"
|
||||
}
|
||||
end
|
||||
|
||||
def self.search(terms)
|
||||
pg_search(terms)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_author
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.hidden_budget_investments.index.title") %></h2>
|
||||
<%= render Admin::SearchComponent.new(label: t("admin.shared.search.label.budget_investments")) %>
|
||||
<p><%= t("admin.shared.moderated_content") %></p>
|
||||
|
||||
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_budget_investments.index" %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.hidden_comments.index.title") %></h2>
|
||||
<%= render Admin::SearchComponent.new(label: t("admin.shared.search.label.comments")) %>
|
||||
<p><%= t("admin.shared.moderated_content") %></p>
|
||||
|
||||
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_comments.index" %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.hidden_debates.index.title") %></h2>
|
||||
<%= render Admin::SearchComponent.new(label: t("admin.shared.search.label.debates")) %>
|
||||
<p><%= t("admin.shared.moderated_content") %></p>
|
||||
|
||||
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_debates.index" %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.hidden_proposal_notifications.index.title") %></h2>
|
||||
<%= render Admin::SearchComponent.new(label: t("admin.shared.search.label.proposal_notifications")) %>
|
||||
<p><%= t("admin.shared.moderated_content") %></p>
|
||||
|
||||
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_proposal_notifications.index" %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.hidden_proposals.index.title") %></h2>
|
||||
<%= render Admin::SearchComponent.new(label: t("admin.shared.search.label.proposals")) %>
|
||||
<p><%= t("admin.shared.moderated_content") %></p>
|
||||
|
||||
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_proposals.index" %>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<h2><%= t("admin.hidden_users.index.title") %></h2>
|
||||
<%= render "admin/shared/user_search", url: admin_hidden_users_path %>
|
||||
<p><%= t("admin.shared.moderated_content") %></p>
|
||||
|
||||
<%= render "shared/filter_subnav", i18n_namespace: "admin.hidden_users.index" %>
|
||||
|
||||
@@ -1346,6 +1346,7 @@ en:
|
||||
label:
|
||||
booths: "Search booth by name or location"
|
||||
budget_investments: "Search investments by title, description or heading"
|
||||
comments: "Search comments"
|
||||
debates: "Search debates by title or description"
|
||||
legislation_processes: "Search processes by title or description"
|
||||
legislation_proposals: "Search proposals by title or description"
|
||||
@@ -1355,6 +1356,7 @@ en:
|
||||
poll_questions: "Search poll questions"
|
||||
polls: "Search polls by name or description"
|
||||
proposals: "Search proposals by title, code, description or question"
|
||||
proposal_notifications: "Search notifications by title or description"
|
||||
users: "Search user by name or email"
|
||||
search: "Search"
|
||||
search_results: "Search results"
|
||||
|
||||
@@ -1345,6 +1345,7 @@ es:
|
||||
label:
|
||||
booths: "Buscar urna por nombre"
|
||||
budget_investments: "Buscar proyectos por título, descripción o partida"
|
||||
comments: "Buscar comentarios"
|
||||
debates: "Buscar debates por título o descripción"
|
||||
legislation_processes: "Buscar procesos por título o descripción"
|
||||
legislation_proposals: "Buscar propuestas por título o descripción"
|
||||
@@ -1354,6 +1355,7 @@ es:
|
||||
poll_questions: "Buscar preguntas"
|
||||
polls: "Buscar votaciones por nombre o descripción"
|
||||
proposals: "Buscar propuestas por título, código, descripción o pregunta"
|
||||
proposal_notifications: "Buscar notificaciones por título o descripción"
|
||||
users: "Buscar usuario por nombre o email"
|
||||
search: "Buscar"
|
||||
search_results: "Resultados de la búsqueda"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class AddTsvectorToCommentsAndProposalNotifications < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :comments, :tsv, :tsvector
|
||||
add_index :comments, :tsv, using: "gin"
|
||||
|
||||
add_column :proposal_notifications, :tsv, :tsvector
|
||||
add_index :proposal_notifications, :tsv, using: "gin"
|
||||
end
|
||||
end
|
||||
@@ -448,6 +448,7 @@ ActiveRecord::Schema.define(version: 2021_11_03_112944) do
|
||||
t.string "ancestry"
|
||||
t.integer "confidence_score", default: 0, null: false
|
||||
t.boolean "valuation", default: false
|
||||
t.tsvector "tsv"
|
||||
t.index ["ancestry"], name: "index_comments_on_ancestry"
|
||||
t.index ["cached_votes_down"], name: "index_comments_on_cached_votes_down"
|
||||
t.index ["cached_votes_total"], name: "index_comments_on_cached_votes_total"
|
||||
@@ -455,6 +456,7 @@ ActiveRecord::Schema.define(version: 2021_11_03_112944) do
|
||||
t.index ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type"
|
||||
t.index ["confidence_score"], name: "index_comments_on_confidence_score"
|
||||
t.index ["hidden_at"], name: "index_comments_on_hidden_at"
|
||||
t.index ["tsv"], name: "index_comments_on_tsv", using: :gin
|
||||
t.index ["user_id"], name: "index_comments_on_user_id"
|
||||
t.index ["valuation"], name: "index_comments_on_valuation"
|
||||
end
|
||||
@@ -1268,6 +1270,8 @@ ActiveRecord::Schema.define(version: 2021_11_03_112944) do
|
||||
t.datetime "hidden_at"
|
||||
t.datetime "ignored_at"
|
||||
t.datetime "confirmed_hide_at"
|
||||
t.tsvector "tsv"
|
||||
t.index ["tsv"], name: "index_proposal_notifications_on_tsv", using: :gin
|
||||
end
|
||||
|
||||
create_table "proposal_translations", id: :serial, force: :cascade do |t|
|
||||
|
||||
@@ -2,10 +2,15 @@ namespace :consul do
|
||||
desc "Runs tasks needed to upgrade to the latest version"
|
||||
task execute_release_tasks: ["settings:rename_setting_keys",
|
||||
"settings:add_new_settings",
|
||||
"execute_release_1.5.0_tasks"]
|
||||
"execute_release_1.6.0_tasks"]
|
||||
|
||||
desc "Runs tasks needed to upgrade from 1.4.0 to 1.5.0"
|
||||
task "execute_release_1.5.0_tasks": [
|
||||
"active_storage:remove_paperclip_compatibility_in_existing_attachments"
|
||||
]
|
||||
|
||||
desc "Runs tasks needed to upgrade from 1.5.0 to 1.6.0"
|
||||
task "execute_release_1.6.0_tasks": [
|
||||
"db:calculate_tsv"
|
||||
]
|
||||
end
|
||||
|
||||
@@ -5,4 +5,15 @@ namespace :db do
|
||||
I18n.enforce_available_locales = false
|
||||
load(Rails.root.join("db", "dev_seeds.rb"))
|
||||
end
|
||||
|
||||
desc "Calculates the TSV column for all comments and proposal notifications"
|
||||
task calculate_tsv: :environment do
|
||||
logger = ApplicationLogger.new
|
||||
|
||||
logger.info "Calculating tsvector for comments"
|
||||
Comment.with_hidden.find_each(&:calculate_tsvector)
|
||||
|
||||
logger.info "Calculating tsvector for proposal notifications"
|
||||
ProposalNotification.with_hidden.find_each(&:calculate_tsvector)
|
||||
end
|
||||
end
|
||||
|
||||
39
spec/lib/tasks/db_spec.rb
Normal file
39
spec/lib/tasks/db_spec.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe "rake db:calculate_tsv" do
|
||||
before { Rake::Task["db:calculate_tsv"].reenable }
|
||||
|
||||
let :run_rake_task do
|
||||
Rake.application.invoke_task("db:calculate_tsv")
|
||||
end
|
||||
|
||||
it "calculates the tsvector for comments, including hidden ones" do
|
||||
comment = create(:comment)
|
||||
hidden = create(:comment, :hidden)
|
||||
comment.update_column(:tsv, nil)
|
||||
hidden.update_column(:tsv, nil)
|
||||
|
||||
expect(comment.reload.tsv).to be nil
|
||||
expect(hidden.reload.tsv).to be nil
|
||||
|
||||
run_rake_task
|
||||
|
||||
expect(comment.reload.tsv).not_to be nil
|
||||
expect(hidden.reload.tsv).not_to be nil
|
||||
end
|
||||
|
||||
it "calculates the tsvector for proposal notifications, including hidden ones" do
|
||||
notification = create(:proposal_notification)
|
||||
hidden = create(:proposal_notification, :hidden)
|
||||
notification.update_column(:tsv, nil)
|
||||
hidden.update_column(:tsv, nil)
|
||||
|
||||
expect(notification.reload.tsv).to be nil
|
||||
expect(hidden.reload.tsv).to be nil
|
||||
|
||||
run_rake_task
|
||||
|
||||
expect(notification.reload.tsv).not_to be nil
|
||||
expect(hidden.reload.tsv).not_to be nil
|
||||
end
|
||||
end
|
||||
@@ -193,4 +193,25 @@ describe Comment do
|
||||
expect(Comment.public_for_api).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe ".search" do
|
||||
it "searches by body" do
|
||||
comment = create(:comment, body: "I agree")
|
||||
|
||||
expect(Comment.search("agree")).to eq([comment])
|
||||
end
|
||||
|
||||
it "searches by commentable title" do
|
||||
proposal = create(:proposal, title: "More wood!")
|
||||
comment = create(:comment, body: "I agree", commentable: proposal)
|
||||
|
||||
expect(Comment.search("wood")).to eq([comment])
|
||||
end
|
||||
|
||||
it "does not return non-matching records" do
|
||||
create(:comment, body: "I agree")
|
||||
|
||||
expect(Comment.search("disagree")).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,6 +42,26 @@ describe ProposalNotification do
|
||||
end
|
||||
end
|
||||
|
||||
describe ".search" do
|
||||
it "searches by title" do
|
||||
notification = create(:proposal_notification, title: "Check this!", body: "It's awesome!")
|
||||
|
||||
expect(ProposalNotification.search("Check")).to eq([notification])
|
||||
end
|
||||
|
||||
it "searches by body" do
|
||||
notification = create(:proposal_notification, title: "Check this!", body: "It's awesome!")
|
||||
|
||||
expect(ProposalNotification.search("awesome")).to eq([notification])
|
||||
end
|
||||
|
||||
it "does not return non-matching records" do
|
||||
create(:proposal_notification, title: "Check this!", body: "It's awesome!")
|
||||
|
||||
expect(ProposalNotification.search("terrible")).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "minimum interval between notifications" do
|
||||
before do
|
||||
Setting[:proposal_notification_minimum_interval_in_days] = 3
|
||||
|
||||
105
spec/system/admin/hidden_content_search_spec.rb
Normal file
105
spec/system/admin/hidden_content_search_spec.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe "Hidden content search", :admin do
|
||||
scenario "finds matching records" do
|
||||
create(:budget_investment, :hidden, title: "New football field")
|
||||
create(:budget_investment, :hidden, title: "New basketball field")
|
||||
create(:budget_investment, :hidden, title: "New sports center")
|
||||
|
||||
visit admin_hidden_budget_investments_path
|
||||
fill_in "search", with: "field"
|
||||
click_button "Search"
|
||||
|
||||
expect(page).not_to have_content "New sports center"
|
||||
expect(page).to have_content "New football field"
|
||||
expect(page).to have_content "New basketball field"
|
||||
end
|
||||
|
||||
scenario "returns no results if no records match the term" do
|
||||
create(:comment, :hidden, body: "I like this feature")
|
||||
|
||||
visit admin_hidden_comments_path
|
||||
fill_in "search", with: "love"
|
||||
click_button "Search"
|
||||
|
||||
expect(page).to have_content "There are no hidden comments"
|
||||
expect(page).not_to have_content "I like this feature"
|
||||
expect(page).not_to have_content "I hate this feature"
|
||||
end
|
||||
|
||||
scenario "returns all records when the search term is empty" do
|
||||
create(:debate, :hidden, title: "Can we make it better?")
|
||||
create(:debate, :hidden, title: "Can we make it worse?")
|
||||
|
||||
visit admin_hidden_debates_path(search: "worse")
|
||||
|
||||
expect(page).not_to have_content "Can we make it better?"
|
||||
expect(page).to have_content "Can we make it worse?"
|
||||
|
||||
fill_in "search", with: " "
|
||||
click_button "Search"
|
||||
|
||||
expect(page).to have_content "Can we make it better?"
|
||||
expect(page).to have_content "Can we make it worse?"
|
||||
expect(page).not_to have_content "There are no hidden debates"
|
||||
end
|
||||
|
||||
scenario "keeps search parameters after restoring a record" do
|
||||
create(:proposal_notification, :hidden, title: "Someone is telling you something")
|
||||
create(:proposal_notification, :hidden, title: "Someone else says whatever")
|
||||
create(:proposal_notification, :hidden, title: "Nobody is saying anything")
|
||||
|
||||
visit admin_hidden_proposal_notifications_path(search: "Someone")
|
||||
|
||||
expect(page).to have_content "Someone is telling you something"
|
||||
expect(page).to have_content "Someone else says whatever"
|
||||
expect(page).not_to have_content "Nobody is saying anything"
|
||||
|
||||
within "tr", text: "Someone is telling you something" do
|
||||
accept_confirm("Are you sure? Restore") { click_button "Restore" }
|
||||
end
|
||||
|
||||
expect(page).not_to have_content "Someone is telling you something"
|
||||
expect(page).to have_content "Someone else says whatever"
|
||||
expect(page).not_to have_content "Nobody is saying anything"
|
||||
end
|
||||
|
||||
scenario "keeps search parameters after confirming moderation" do
|
||||
create(:proposal, :hidden, title: "Reduce the incoming traffic")
|
||||
create(:proposal, :hidden, title: "Reduce pollution")
|
||||
create(:proposal, :hidden, title: "Increment pollution")
|
||||
|
||||
visit admin_hidden_proposals_path(search: "Reduce")
|
||||
|
||||
expect(page).to have_content "Reduce the incoming traffic"
|
||||
expect(page).to have_content "Reduce pollution"
|
||||
expect(page).not_to have_content "Increment pollution"
|
||||
|
||||
within("tr", text: "Reduce the incoming traffic") { click_button "Confirm moderation" }
|
||||
|
||||
expect(page).not_to have_content "Reduce the incoming traffic"
|
||||
expect(page).to have_content "Reduce pollution"
|
||||
expect(page).not_to have_content "Increment pollution"
|
||||
end
|
||||
|
||||
scenario "keeps search parameters while browsing through filters" do
|
||||
create(:user, :hidden, username: "person1")
|
||||
create(:user, :hidden, username: "alien1")
|
||||
create(:user, :hidden, :with_confirmed_hide, username: "person2")
|
||||
create(:user, :hidden, :with_confirmed_hide, username: "alien2")
|
||||
|
||||
visit admin_hidden_users_path(search: "person")
|
||||
|
||||
expect(page).to have_content "person1"
|
||||
expect(page).not_to have_content "person2"
|
||||
expect(page).not_to have_content "alien1"
|
||||
expect(page).not_to have_content "alien2"
|
||||
|
||||
click_link "Confirmed"
|
||||
|
||||
expect(page).not_to have_content "person1"
|
||||
expect(page).to have_content "person2"
|
||||
expect(page).not_to have_content "alien1"
|
||||
expect(page).not_to have_content "alien2"
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user