Refactor item search JSON, add appearances
Preparing to finally move wardrobe-2020's item search to use the main app's API endpoints instead! One blocker I forgot about here: Impress 2020 has actual support for knowing an item's true appearance, like by reading the manifest and stuff, that we haven't really ported over. I feel like maybe I should pause and work on the changes to manifest-archiving that I'd been planning anyway? I'll think about it.
This commit is contained in:
parent
1a1615e0ad
commit
3f449310d6
5 changed files with 60 additions and 42 deletions
|
@ -11,9 +11,11 @@ class ItemsController < ApplicationController
|
||||||
else
|
else
|
||||||
per_page = 30
|
per_page = 30
|
||||||
end
|
end
|
||||||
|
|
||||||
@items = @query.results.paginate(
|
@items = @query.results.paginate(
|
||||||
page: params[:page], per_page: per_page)
|
page: params[:page], per_page: per_page)
|
||||||
assign_closeted!
|
assign_closeted!
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
@campaign = Fundraising::Campaign.current rescue nil
|
@campaign = Fundraising::Campaign.current rescue nil
|
||||||
|
@ -24,13 +26,14 @@ class ItemsController < ApplicationController
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
format.json {
|
format.json {
|
||||||
render json: {items: @items, total_pages: @items.total_pages,
|
render json: {
|
||||||
query: @query.to_s}
|
items: @items.as_json(
|
||||||
}
|
methods: [:nc?, :pb?, :owned?, :wanted?],
|
||||||
format.js {
|
),
|
||||||
render json: {items: @items, total_pages: @items.total_pages,
|
appearances: load_appearances,
|
||||||
query: @query.to_s},
|
total_pages: @items.total_pages,
|
||||||
callback: params[:callback]
|
query: @query.to_s,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -46,7 +49,6 @@ class ItemsController < ApplicationController
|
||||||
@campaign = Fundraising::Campaign.current rescue nil
|
@campaign = Fundraising::Campaign.current rescue nil
|
||||||
@newest_items = Item.newest.limit(18)
|
@newest_items = Item.newest.limit(18)
|
||||||
}
|
}
|
||||||
format.js { render json: {error: '$q required'}}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -100,14 +102,20 @@ class ItemsController < ApplicationController
|
||||||
def assign_closeted!
|
def assign_closeted!
|
||||||
current_user.assign_closeted_to_items!(@items) if user_signed_in?
|
current_user.assign_closeted_to_items!(@items) if user_signed_in?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def load_appearances
|
||||||
|
pet_type_name = params[:with_appearances_for]
|
||||||
|
return {} if pet_type_name.blank?
|
||||||
|
|
||||||
|
pet_type = Item::Search::Query.load_pet_type_by_name(pet_type_name)
|
||||||
|
pet_type.appearances_for(@items.map(&:id))
|
||||||
|
end
|
||||||
|
|
||||||
def search_error(e)
|
def search_error(e)
|
||||||
@items = []
|
@items = []
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { flash.now[:alert] = e.message; render }
|
format.html { flash.now[:alert] = e.message; render }
|
||||||
format.json { render :json => {error: e.message} }
|
format.json { render :json => {error: e.message} }
|
||||||
format.js { render :json => {error: e.message},
|
|
||||||
:callback => params[:callback] }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -122,7 +130,7 @@ class ItemsController < ApplicationController
|
||||||
@query = params[:q]
|
@query = params[:q]
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
elsif q.is_a?(Hash)
|
elsif q.is_a?(ActionController::Parameters)
|
||||||
@query = Item::Search::Query.from_params(q, current_user)
|
@query = Item::Search::Query.from_params(q, current_user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -153,11 +153,11 @@ class Item < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def owned?
|
def owned?
|
||||||
@owned
|
@owned || false
|
||||||
end
|
end
|
||||||
|
|
||||||
def wanted?
|
def wanted?
|
||||||
@wanted
|
@wanted || false
|
||||||
end
|
end
|
||||||
|
|
||||||
def restricted_zones(options={})
|
def restricted_zones(options={})
|
||||||
|
@ -445,8 +445,8 @@ class Item < ApplicationRecord
|
||||||
@parent_swf_asset_relationships_to_update = rels
|
@parent_swf_asset_relationships_to_update = rels
|
||||||
end
|
end
|
||||||
|
|
||||||
Body = Struct.new(:id, :species)
|
|
||||||
Appearance = Struct.new(:body, :swf_assets)
|
Appearance = Struct.new(:body, :swf_assets)
|
||||||
|
Appearance::Body = Struct.new(:id, :species)
|
||||||
def appearances
|
def appearances
|
||||||
all_swf_assets = swf_assets.to_a
|
all_swf_assets = swf_assets.to_a
|
||||||
|
|
||||||
|
@ -469,7 +469,7 @@ class Item < ApplicationRecord
|
||||||
swf_assets_by_body_id.map do |body_id, body_specific_assets|
|
swf_assets_by_body_id.map do |body_id, body_specific_assets|
|
||||||
swf_assets_for_body = body_specific_assets + swf_assets_for_all_bodies
|
swf_assets_for_body = body_specific_assets + swf_assets_for_all_bodies
|
||||||
species = Species.with_body_id(body_id).first!
|
species = Species.with_body_id(body_id).first!
|
||||||
body = Body.new(body_id, species)
|
body = Appearance::Body.new(body_id, species)
|
||||||
Appearance.new(body, swf_assets_for_body)
|
Appearance.new(body, swf_assets_for_body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,17 +46,10 @@ class Item
|
||||||
Filter.restricts(value) :
|
Filter.restricts(value) :
|
||||||
Filter.not_restricts(value))
|
Filter.not_restricts(value))
|
||||||
when 'fits'
|
when 'fits'
|
||||||
color_name, species_name = value.split('-')
|
pet_type = load_pet_type_by_name(value)
|
||||||
begin
|
|
||||||
pet_type = PetType.matching_name(color_name, species_name).first!
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
message = I18n.translate('items.search.errors.not_found.pet_type',
|
|
||||||
name1: color_name.capitalize, name2: species_name.capitalize)
|
|
||||||
raise Item::Search::Error, message
|
|
||||||
end
|
|
||||||
filters << (is_positive ?
|
filters << (is_positive ?
|
||||||
Filter.fits(pet_type.body_id, color_name, species_name) :
|
Filter.fits(pet_type.body_id, value.downcase) :
|
||||||
Filter.not_fits(pet_type.body_id, color_name, species_name))
|
Filter.not_fits(pet_type.body_id, value.downcase))
|
||||||
when 'species'
|
when 'species'
|
||||||
begin
|
begin
|
||||||
species = Species.find_by_name!(value)
|
species = Species.find_by_name!(value)
|
||||||
|
@ -139,9 +132,11 @@ class Item
|
||||||
pet_type = PetType.find(value)
|
pet_type = PetType.find(value)
|
||||||
color_name = pet_type.color.name
|
color_name = pet_type.color.name
|
||||||
species_name = pet_type.species.name
|
species_name = pet_type.species.name
|
||||||
|
# NOTE: Some color syntaxes are weird, like `fits:"polka dot-aisha"`!
|
||||||
|
value = "#{color_name}-#{species_name}"
|
||||||
filters << (is_positive ?
|
filters << (is_positive ?
|
||||||
Filter.fits(pet_type.body_id, color_name, species_name) :
|
Filter.fits(pet_type.body_id, value) :
|
||||||
Filter.not_fits(pet_type.body_id, color_name, species_name))
|
Filter.not_fits(pet_type.body_id, value))
|
||||||
when 'user_closet_hanger_ownership'
|
when 'user_closet_hanger_ownership'
|
||||||
case value
|
case value
|
||||||
when 'true'
|
when 'true'
|
||||||
|
@ -160,6 +155,18 @@ class Item
|
||||||
|
|
||||||
self.new(filters, user)
|
self.new(filters, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.load_pet_type_by_name(pet_type_string)
|
||||||
|
color_name, species_name = pet_type_string.split("-")
|
||||||
|
|
||||||
|
begin
|
||||||
|
PetType.matching_name(color_name, species_name).first!
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
message = I18n.translate('items.search.errors.not_found.pet_type',
|
||||||
|
name1: color_name.capitalize, name2: species_name.capitalize)
|
||||||
|
raise Item::Search::Error, message
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Error < Exception
|
class Error < Exception
|
||||||
|
@ -211,15 +218,11 @@ class Item
|
||||||
self.new Item.not_restricts(value), "-restricts:#{q value}"
|
self.new Item.not_restricts(value), "-restricts:#{q value}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.fits(body_id, color_name, species_name)
|
def self.fits(body_id, value)
|
||||||
# NOTE: Some color syntaxes are weird, like `fits:"polka dot-aisha"`!
|
|
||||||
value = "#{color_name.downcase}-#{species_name.downcase}"
|
|
||||||
self.new Item.fits(body_id), "fits:#{q value}"
|
self.new Item.fits(body_id), "fits:#{q value}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.not_fits(body_id, color_name, species_name)
|
def self.not_fits(body_id, value)
|
||||||
# NOTE: Some color syntaxes are weird, like `fits:"polka dot-aisha"`!
|
|
||||||
value = "#{color_name.downcase}-#{species_name.downcase}"
|
|
||||||
self.new Item.not_fits(body_id), "-fits:#{q value}"
|
self.new Item.not_fits(body_id), "-fits:#{q value}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -277,14 +280,6 @@ class Item
|
||||||
def self.q(value)
|
def self.q(value)
|
||||||
/\s/.match(value) ? '"' + value + '"' : value
|
/\s/.match(value) ? '"' + value + '"' : value
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.build_fits_filter_text(color_name, species_name)
|
|
||||||
# NOTE: Colors like "Polka Dot" must be written as
|
|
||||||
# `fits:"polka dot-aisha"`.
|
|
||||||
value = "#{color_name.downcase}-#{species_name.downcase}"
|
|
||||||
value = '"' + value + '"' if value.include? ' '
|
|
||||||
"fits:#{value}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -178,6 +178,18 @@ class PetType < ApplicationRecord
|
||||||
}.first
|
}.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def appearances_for(item_ids)
|
||||||
|
# First, load all the relationships for these items that also fit this
|
||||||
|
# body.
|
||||||
|
relationships = ParentSwfAssetRelationship.includes(:swf_asset).
|
||||||
|
where(parent_type: "Item", parent_id: item_ids).
|
||||||
|
where(swf_asset: {body_id: [body_id, 0]})
|
||||||
|
|
||||||
|
# Then, convert this into a hash from item ID to SWF assets.
|
||||||
|
assets_by_item_id = relationships.group_by(&:parent_id).
|
||||||
|
transform_values { |rels| rels.map(&:swf_asset) }
|
||||||
|
end
|
||||||
|
|
||||||
def self.all_by_ids_or_children(ids, pet_states)
|
def self.all_by_ids_or_children(ids, pet_states)
|
||||||
pet_states_by_pet_type_id = {}
|
pet_states_by_pet_type_id = {}
|
||||||
pet_states.each do |pet_state|
|
pet_states.each do |pet_state|
|
||||||
|
|
|
@ -82,7 +82,10 @@ class SwfAsset < ApplicationRecord
|
||||||
scope :object_assets, -> { where(:type => Item::SwfAssetType) }
|
scope :object_assets, -> { where(:type => Item::SwfAssetType) }
|
||||||
scope :for_item_ids, ->(item_ids) {
|
scope :for_item_ids, ->(item_ids) {
|
||||||
joins(:parent_swf_asset_relationships).
|
joins(:parent_swf_asset_relationships).
|
||||||
where(ParentSwfAssetRelationship.arel_table[:parent_id].in(item_ids))
|
where(parent_swf_asset_relationships: {
|
||||||
|
parent_type: "Item",
|
||||||
|
parent_id: item_ids,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
scope :with_parent_ids, -> {
|
scope :with_parent_ids, -> {
|
||||||
select('swf_assets.*, parents_swf_assets.parent_id')
|
select('swf_assets.*, parents_swf_assets.parent_id')
|
||||||
|
|
Loading…
Reference in a new issue