From da06bee0ab19c683a278a787e9c87b78d7ec3993 Mon Sep 17 00:00:00 2001 From: r888888888 Date: Wed, 15 Mar 2017 15:36:48 -0700 Subject: [PATCH] revamp saved search implementation --- app/assets/javascripts/posts.js | 16 +- .../stylesheets/common/simple_form.scss | 4 + app/assets/stylesheets/common/tables.scss | 2 +- app/controllers/saved_searches_controller.rb | 23 +-- app/models/saved_search.rb | 146 +++++------------ app/models/user.rb | 19 --- app/presenters/post_set_presenters/post.rb | 6 +- app/presenters/user_presenter.rb | 4 +- app/views/layouts/default.html.erb | 2 +- app/views/posts/index.html.erb | 6 +- .../posts/partials/index/_related.html.erb | 4 - .../new.html.erb | 25 --- app/views/saved_searches/_interface.html.erb | 7 +- .../saved_searches/_secondary_links.html.erb | 4 +- app/views/saved_searches/create.js.erb | 2 +- app/views/saved_searches/destroy.js.erb | 2 +- app/views/saved_searches/edit.html.erb | 6 +- app/views/saved_searches/index.html.erb | 61 +++---- app/views/users/_post_summary.html.erb | 25 +-- app/views/users/_statistics.html.erb | 30 ++-- config/routes.rb | 3 +- ...170314235626_add_tags_to_saved_searches.rb | 7 + db/structure.sql | 150 +++++++++++++++++- .../044_initialized_saved_search_tags.rb | 10 ++ 24 files changed, 307 insertions(+), 257 deletions(-) delete mode 100644 app/views/saved_search_category_changes/new.html.erb create mode 100644 db/migrate/20170314235626_add_tags_to_saved_searches.rb create mode 100644 script/fixes/044_initialized_saved_search_tags.rb diff --git a/app/assets/javascripts/posts.js b/app/assets/javascripts/posts.js index 144dcd003..7736247f9 100644 --- a/app/assets/javascripts/posts.js +++ b/app/assets/javascripts/posts.js @@ -540,18 +540,20 @@ } Danbooru.Post.initialize_saved_searches = function() { - $("#saved_search_category").autocomplete({ - minLength: 1, + $("#saved_search_labels").autocomplete({ + minLength: 2, source: function(req, resp) { $.ajax({ - url: "/saved_searches/categories.json", + url: "/saved_searches/labels.json", + data: { + label: req.term + }, method: "get", success: function(data) { resp($.map(data, function(saved_search) { - var category = saved_search.category === null ? "uncategorized" : saved_search.category; return { - label: category, - value: category + label: saved_search.replace(/_/g, " "), + value: saved_search }; })); } @@ -575,7 +577,7 @@ }); $("#save-search").click(function() { - if (Danbooru.meta("disable-categorized-saved-searches") === "false") { + if (Danbooru.meta("disable-labeled-saved-searches") === "false") { $("#save-search-dialog").dialog("open"); } else { $.post( diff --git a/app/assets/stylesheets/common/simple_form.scss b/app/assets/stylesheets/common/simple_form.scss index 661c6c489..81b2616c4 100644 --- a/app/assets/stylesheets/common/simple_form.scss +++ b/app/assets/stylesheets/common/simple_form.scss @@ -16,6 +16,10 @@ form.simple_form { width: 20em; } + span.hint { + margin-left: 1em; + } + textarea { width: 70%; font-size: 1.2em; diff --git a/app/assets/stylesheets/common/tables.scss b/app/assets/stylesheets/common/tables.scss index a12b55e64..32877aa73 100644 --- a/app/assets/stylesheets/common/tables.scss +++ b/app/assets/stylesheets/common/tables.scss @@ -19,7 +19,7 @@ table.striped { } } - .number { + .number, .links { text-align: right; } diff --git a/app/controllers/saved_searches_controller.rb b/app/controllers/saved_searches_controller.rb index 7e1d96c6c..6e9cfc039 100644 --- a/app/controllers/saved_searches_controller.rb +++ b/app/controllers/saved_searches_controller.rb @@ -5,9 +5,11 @@ class SavedSearchesController < ApplicationController respond_to :html, :xml, :json, :js def index - @saved_searches = saved_searches.order("tag_query") - @categories = @saved_searches.group_by{|saved_search| saved_search.category.to_s} - @categories = @categories.sort_by{|category, saved_searches| category.to_s} + @saved_searches = saved_searches.order("id") + + if params[:label] + @saved_searches = saved_searches.labeled(params[:label]) + end respond_with(@saved_searches) do |format| format.xml do @@ -16,15 +18,18 @@ class SavedSearchesController < ApplicationController end end - def categories - @categories = saved_searches.select(:category).distinct - respond_with(@categories) + def labels + @labels = SavedSearch.labels_for(CurrentUser.user.id) + if params[:label] + regexp = Regexp.compile(Regexp.escape(params[:label])) + @labels = @labels.grep(regexp) + end + respond_with(@labels) end def create - @saved_search = saved_searches.create(:tag_query => params[:saved_search_tags], :category => params[:saved_search_category]) - - if params[:saved_search_disable_categories] + @saved_search = saved_searches.create(:query => params[:saved_search_tags], :label_string => params[:saved_search_labels]) + if params[:saved_search_disable_labels] CurrentUser.disable_categorized_saved_searches = true CurrentUser.save end diff --git a/app/models/saved_search.rb b/app/models/saved_search.rb index 2e552557a..924df4f2d 100644 --- a/app/models/saved_search.rb +++ b/app/models/saved_search.rb @@ -1,6 +1,4 @@ class SavedSearch < ActiveRecord::Base - UNCATEGORIZED_NAME = "uncategorized" - module ListbooruMethods extend ActiveSupport::Concern @@ -17,140 +15,72 @@ class SavedSearch < ActiveRecord::Base SqsService.new(Danbooru.config.aws_sqs_saved_search_url) end - def refresh_listbooru(user_id) - return false unless enabled? - - sqs_service.send_message("refresh\n#{user_id}") - end - - def reset_listbooru(user_id) - return false unless enabled? - - user = User.find(user_id) - - sqs_service.send_message("delete\n#{user_id}\nall\n") - - user.saved_searches.each do |saved_search| - sqs_service.send_message("create\n#{user_id}\n#{saved_search.category}\n#{saved_search.tag_query}", :delay_seconds => 30) - end - - true - end - - def rename_listbooru(user_id, old_category, new_category) - return false unless enabled? - - sqs_service.send_message("rename\n#{user_id}\n#{old_category}\n#{new_category}\n") - - true - end - - def post_ids(user_id, name = nil) + def post_ids(user_id, label = nil) return [] unless enabled? + label = normalize_label(label) if label - if name - hash_name = Cache.hash(name) - else - hash_name = nil - end - - body = Cache.get("ss-pids-#{user_id}-#{hash_name}", 60) do - params = { + Cache.get("ss-#{user_id}-#{Cache.hash(label)}", 60) do + queries = SavedSearch.queries_for(user_id, label) + json = { "key" => Danbooru.config.listbooru_auth_key, - "user_id" => user_id - } + "queries" => queries + }.to_json - if name == UNCATEGORIZED_NAME - params["name"] = nil - elsif name.present? - params["name"] = name - end + uri = URI.parse("#{Danbooru.config.listbooru_server}/v2/search") - uri = URI.parse("#{Danbooru.config.listbooru_server}/users") - uri.query = URI.encode_www_form(params) - - Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.is_a?(URI::HTTPS)) do |http| - resp = http.request_get(uri.request_uri) + body = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.is_a?(URI::HTTPS)) do |http| + resp = http.request_post(uri.request_uri, json) if resp.is_a?(Net::HTTPSuccess) resp.body else raise "HTTP error code: #{resp.code} #{resp.message}" end end + + body.to_s.scan(/\d+/).map(&:to_i) end - - body.to_s.scan(/\d+/).map(&:to_i) end end - - def update_listbooru_on_create - return unless SavedSearch.enabled? - return unless user.is_gold? - - SavedSearch.sqs_service.send_message("create\n#{user_id}\n#{category}\n#{tag_query}") - end - - def update_listbooru_on_destroy - return unless SavedSearch.enabled? - - SavedSearch.sqs_service.send_message("delete\n#{user_id}\n#{category}\n#{tag_query}") - end - - def update_listbooru_on_update - return unless SavedSearch.enabled? - return unless user.is_gold? - - SavedSearch.sqs_service.send_message("update\n#{user_id}\n#{category_was}\n#{tag_query_was}\n#{category}\n#{tag_query}") - end end include ListbooruMethods belongs_to :user - validates :tag_query, :presence => true + validates :query, :presence => true validate :validate_count - attr_accessible :tag_query, :category + attr_accessible :query, :label_string before_create :update_user_on_create - before_update :update_listbooru_on_update after_destroy :update_user_on_destroy - after_create :update_listbooru_on_create - after_destroy :update_listbooru_on_destroy - validates_uniqueness_of :tag_query, :scope => :user_id + after_save {|rec| Cache.delete(SavedSearch.cache_key(rec.user_id))} + after_destroy {|rec| Cache.delete(SavedSearch.cache_key(rec.user_id))} before_validation :normalize + scope :labeled, lambda {|label| where("labels @> string_to_array(?, '~~~~')", label)} - def self.tagged(tags) - where(:tag_query => normalize(tags)).first + def self.normalize_label(label) + label.strip.downcase.gsub(/[[:space:]]/, "_") end - def self.normalize(tag_query) - Tag.scan_query(tag_query).join(" ") + def self.labels_for(user_id) + Cache.get(cache_key(user_id)) do + SavedSearch.where(user_id: user_id).order("label").pluck("distinct unnest(labels) as label") + end end - def self.normalize_category(category) - category.to_s.strip.gsub(/\s+/, "_").downcase + def self.cache_key(user_id) + "ss-labels-#{user_id}" end - def self.rename(user_id, old_category, new_category) - user = User.find(user_id) - old_category = normalize_category(old_category) - new_category = normalize_category(new_category) - user.saved_searches.where(category: old_category).update_all(category: new_category) - rename_listbooru(user_id, old_category, new_category) - end - - def self.categories_for(user) - user.saved_searches.pluck("distinct category").map do |x| - if x.blank? - SavedSearch::UNCATEGORIZED_NAME - else - x - end + def self.queries_for(user_id, label = nil, options = {}) + if label + SavedSearch.where(user_id: user_id).labeled(label).pluck("distinct query") + else + SavedSearch.where(user_id: user_id).pluck("distinct query") end end def normalize - self.category = SavedSearch.normalize_category(category) if category - self.tag_query = SavedSearch.normalize(tag_query) + self.query = query_array.sort.join(" ") + self.labels = labels.map {|x| SavedSearch.normalize_label(x)}.reject {|x| x.blank?} end def validate_count @@ -171,7 +101,15 @@ class SavedSearch < ActiveRecord::Base end end - def tag_query_array - Tag.scan_tags(tag_query) + def query_array + Tag.scan_tags(query) + end + + def label_string + labels.join(" ") + end + + def label_string=(val) + self.labels = val.scan(/\S+/).map {|x| SavedSearch.normalize_label(x)} end end diff --git a/app/models/user.rb b/app/models/user.rb index 68e633f9f..1e9eeb41d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -849,24 +849,6 @@ class User < ActiveRecord::Base end end - module SavedSearchMethods - def unique_saved_search_categories - if SavedSearch.enabled? - categories = saved_searches.pluck(:category) - - if categories.any? {|x| x.blank?} - categories.reject! {|x| x.blank?} - categories.unshift(SavedSearch::UNCATEGORIZED_NAME) - end - - categories.uniq! - categories - else - [] - end - end - end - module SockPuppetMethods def notify_sock_puppets sock_puppet_suspects.each do |user| @@ -895,7 +877,6 @@ class User < ActiveRecord::Base include CountMethods extend SearchMethods include StatisticsMethods - include SavedSearchMethods def initialize_default_image_size self.default_image_size = "large" diff --git a/app/presenters/post_set_presenters/post.rb b/app/presenters/post_set_presenters/post.rb index e3b600f01..0b4dbd07b 100644 --- a/app/presenters/post_set_presenters/post.rb +++ b/app/presenters/post_set_presenters/post.rb @@ -12,7 +12,7 @@ module PostSetPresenters if post_set.is_pattern_search? pattern_tags elsif post_set.is_saved_search? - SavedSearch.categories_for(CurrentUser.user).map {|x| "search:#{x}"} + SavedSearch.labels_for(CurrentUser.user.id).map {|x| "search:#{x}"} elsif post_set.is_tag_subscription? post_set.tag_subscription_tags elsif post_set.is_single_tag? @@ -56,8 +56,8 @@ module PostSetPresenters RelatedTagCalculator.calculate_from_post_set_to_array(post_set).map(&:first) end - def saved_search_tags - SavedSearch.categories_for(CurrentUser.user).map {|x| "search:#{x}"} + def saved_search_labels + SavedSearch.labels_for(CurrentUser.user.id).map {|x| "search:#{x}"} end def tag_list_html(template, options = {}) diff --git a/app/presenters/user_presenter.rb b/app/presenters/user_presenter.rb index 5d14e6c3c..f757ee6b4 100644 --- a/app/presenters/user_presenter.rb +++ b/app/presenters/user_presenter.rb @@ -221,9 +221,9 @@ class UserPresenter end end - def saved_search_categories + def saved_search_labels if CurrentUser.user.id == user.id - user.unique_saved_search_categories + SavedSearch.labels_for(CurrentUser.user.id) else [] end diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb index 9ef528a5f..ed08352ec 100644 --- a/app/views/layouts/default.html.erb +++ b/app/views/layouts/default.html.erb @@ -22,7 +22,7 @@ - + <%= auto_discovery_link_tag :atom, posts_path(:format => "atom", :tags => params[:tags]) %> <%= stylesheet_link_tag "application", :media => "screen" %> <% if CurrentUser.user.custom_style.present? %> diff --git a/app/views/posts/index.html.erb b/app/views/posts/index.html.erb index 66b1c03ae..c18f7ed1c 100644 --- a/app/views/posts/index.html.erb +++ b/app/views/posts/index.html.erb @@ -52,7 +52,11 @@ <% end %> <% end %> - <%= render "posts/partials/common/secondary_links" %> + <% if params[:tags] =~ /search:/ %> + <%= render "saved_searches/secondary_links" %> + <% else %> + <%= render "posts/partials/common/secondary_links" %> + <% end %> <%= post_search_count_js %> diff --git a/app/views/posts/partials/index/_related.html.erb b/app/views/posts/partials/index/_related.html.erb index 345f16176..393a5719a 100644 --- a/app/views/posts/partials/index/_related.html.erb +++ b/app/views/posts/partials/index/_related.html.erb @@ -7,10 +7,6 @@
  • <%= link_to "Manage subscriptions", tag_subscriptions_path %>
  • <% end %> - <% if @post_set.is_saved_search? %> -
  • <%= link_to "Manage saved searches", saved_searches_path %>
  • - <% end %> -
  • <%= link_to "Random post", random_posts_path(:tags => params[:tags]), :id => "random-post", :rel => "nofollow" %>
  • <%= link_to "Mobile version", mobile_posts_path(:tags => params[:tags]) %>
  • diff --git a/app/views/saved_search_category_changes/new.html.erb b/app/views/saved_search_category_changes/new.html.erb deleted file mode 100644 index a18edda63..000000000 --- a/app/views/saved_search_category_changes/new.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -
    -
    -

    Change Category

    - - <%= form_tag(saved_search_category_change_path, :class => "simple_form") do %> -
    - - <%= text_field_tag "old", params[:old] %> -
    - -
    - - <%= text_field_tag "new" %> -
    - - <%= submit_tag %> - <% end %> -
    -
    - -<%= render "saved_searches/secondary_links" %> - -<% content_for(:page_title) do %> - Edit Category - <%= Danbooru.config.app_name %> -<% end %> diff --git a/app/views/saved_searches/_interface.html.erb b/app/views/saved_searches/_interface.html.erb index 69f0e8ce4..1720a4499 100644 --- a/app/views/saved_searches/_interface.html.erb +++ b/app/views/saved_searches/_interface.html.erb @@ -8,12 +8,11 @@ <%= hidden_field_tag "saved_search_tags", params[:tags] %>
    - + + <%= text_field_tag "saved_search_labels", "" %>
    -

    +

    <% end %> <% end %> \ No newline at end of file diff --git a/app/views/saved_searches/_secondary_links.html.erb b/app/views/saved_searches/_secondary_links.html.erb index 8a6c57c64..0b221881e 100644 --- a/app/views/saved_searches/_secondary_links.html.erb +++ b/app/views/saved_searches/_secondary_links.html.erb @@ -1,6 +1,6 @@ <% content_for(:secondary_links) do %> -
  • <%= link_to "Gallery", posts_path(:tags => "search:all") %>
  • -
  • <%= link_to "Listing", saved_searches_path %>
  • +
  • <%= link_to "View posts", posts_path(:tags => "search:all") %>
  • +
  • <%= link_to "Manage saved searches", saved_searches_path %>
  • <% end %> diff --git a/app/views/saved_searches/create.js.erb b/app/views/saved_searches/create.js.erb index 0755c27a3..87573ac1c 100644 --- a/app/views/saved_searches/create.js.erb +++ b/app/views/saved_searches/create.js.erb @@ -1,5 +1,5 @@ <% if @saved_search.errors.any? %> Danbooru.error("<%= j @saved_search.errors.full_messages.join(', ') %>"); <% else %> - Danbooru.notice("Search '<%= j @saved_search.tag_query %>' was saved"); + Danbooru.notice("Search '<%= j @saved_search.query %>' was saved"); <% end %> diff --git a/app/views/saved_searches/destroy.js.erb b/app/views/saved_searches/destroy.js.erb index 97a5fc57e..ea5b41166 100644 --- a/app/views/saved_searches/destroy.js.erb +++ b/app/views/saved_searches/destroy.js.erb @@ -1,4 +1,4 @@ -Danbooru.notice("Search '<%= j @saved_search.tag_query %>' was deleted"); +Danbooru.notice("Search '<%= j @saved_search.query %>' was deleted"); $("#saved-searches-nav").html("<%= j render('saved_searches/interface', :saved_searches => CurrentUser.user.saved_searches) %>"); Danbooru.Post.initialize_saved_searches(); $("#saved-search-<%= @saved_search.id %>").remove(); diff --git a/app/views/saved_searches/edit.html.erb b/app/views/saved_searches/edit.html.erb index 7e8eb2c85..1005282a0 100644 --- a/app/views/saved_searches/edit.html.erb +++ b/app/views/saved_searches/edit.html.erb @@ -5,9 +5,9 @@ <%= error_messages_for :saved_search %> <%= simple_form_for(@saved_search) do |f| %> - <%= f.input :tag_query %> - <%= f.input :category %> - <%= f.button :submit, :data => { :disable_with => "Submitting..." } %> + <%= f.input :query %> + <%= f.input :label_string, :label => "Labels", :hint => "A list of tags to help categorize this search. Space delimited." %> + <%= f.button :submit, :value => "Submit", :data => { :disable_with => "Submitting..." } %> <% end %> diff --git a/app/views/saved_searches/index.html.erb b/app/views/saved_searches/index.html.erb index 6a37c837c..807e8e643 100644 --- a/app/views/saved_searches/index.html.erb +++ b/app/views/saved_searches/index.html.erb @@ -1,37 +1,38 @@
    -

    Saved Searches

    +

    + Saved Searches + <% if params[:label] %> + (<%= params[:label] %>) + <% end %> +

    - <% @categories.each do |category, saved_searches| %> -

    - <% if category.present? %> - <%= link_to_if SavedSearch.posts_search_available?, category.tr("_", " "), posts_path(:tags => "search:#{category}") %> - (<%= link_to "rename", new_saved_search_category_change_path(:old => category) %>) - <% else %> - <%= link_to_if SavedSearch.posts_search_available?, SavedSearch::UNCATEGORIZED_NAME, posts_path(:tags => "search:#{SavedSearch::UNCATEGORIZED_NAME}") %> - <% end %> -

    - - - - - +
    Tags
    + + + + + + + + + + <% @saved_searches.each do |ss| %> + + + + - - - - <% saved_searches.each do |saved_search| %> - - - - - <% end %> - -
    QueryLabels
    <%= link_to ss.query, posts_path(:tags => ss.query) %> + <% ss.labels.each do |label| %> + <%= link_to label, posts_path(:tags => "search:#{label}") %> + <% end %> +
    <%= link_to saved_search.tag_query, posts_path(:tags => saved_search.tag_query) %> - <%= link_to "edit", edit_saved_search_path(saved_search) %> - | <%= link_to "delete", saved_search_path(saved_search), :method => :delete, :remote => true %> -
    - <% end %> + <% end %> + +
    diff --git a/app/views/users/_post_summary.html.erb b/app/views/users/_post_summary.html.erb index 1266c9d91..134da3d38 100644 --- a/app/views/users/_post_summary.html.erb +++ b/app/views/users/_post_summary.html.erb @@ -30,30 +30,17 @@ <% end %> -<% presenter.subscriptions.each do |subscription| %> -
    -

    - Subscription: <%= link_to subscription.pretty_name, posts_path(:tags => "sub:#{user.name}:#{subscription.name}") %> -

    - -
    - <% presenter.posts_for_subscription(subscription).each do |post| %> - <%= PostPresenter.preview(post, :tags => "sub:#{user.name}:#{subscription.name}") %> - <% end %> -
    -
    -<% end %> - <% if CurrentUser.user.id == @user.id && @user.has_saved_searches? && @user.is_gold? %> - <% presenter.saved_search_categories.each do |category| %> -