Compare commits

...

5 commits

Author SHA1 Message Date
87782767f8 Filter search in wardrobe-2020 by alt styles!
Yay, we finally added it, the part where we include the appearance data
for the items based on both the species/color and the alt style! Now,
switching to Faerie Acara correctly filters the search only to items
that would fit (I think literally just only body_id=0 items right now,
but we're not banking on that!)
2024-02-27 16:11:06 -08:00
421f2ce39f Use fits:nostalgic-faerie-draik filter format when we can
This only *really* shows up right now in the case where you construct
an Advanced Search form query (which only the wardrobe-2020 app does
now, and in limited form), and we return the query back (which only
gets used by the HTML view for item search, which doesn't have any way
to build one of these requests against it).

This is because, if you just type in `fits:alt-style-87305`, we always
keep your search string the same when outputting it back to you, to
avoid the weirdness of canonicalizing it and changing it up on you in
surprising ways!

But idk, this is just looking forward a bit, and keeping the system's
semantics in place. I hope someday we can bring robust text filter
and Advanced Search stuff back into the main app again, maybe!
2024-02-27 15:51:27 -08:00
18c7a34b8f Update series_name for alt styles to be null, with a fallback string
I considered this at first, but decided to keep it simple until it
turned out to matter. Oops, it already matters, lol!

I want the item search code to be able to easily tell if the series
name is real or a placeholder, so we can decide whether to build the
filter text in `fits:$series-$color-$species` form or
`fits:alt-style-$id` form.

So in this change, we keep it that `AltStyle#series_name` returns the
placeholder string if none is set, but callers can explicitly ask
whether it's a real series name or not. Will use this in our next
change!
2024-02-27 15:48:28 -08:00
9243193bc8 Add support for fits:alt-style-87305 item searches
Easy peasy!
2024-02-27 15:36:45 -08:00
d983a20989 Add support for fits:nostalgic-faerie-draik item searches
Nice and easy now that we have `series_name` in the database!

I didn't add the `fits:alt-style-12345` case yet though!
2024-02-27 15:33:08 -08:00
7 changed files with 123 additions and 32 deletions

View file

@ -121,11 +121,16 @@ class ItemsController < ApplicationController
def load_appearances
appearance_params = params[:with_appearances_for]
return {} if appearance_params.blank?
raise NotImplementedError if appearance_params[:alt_style_id].present?
pet_type = Item::Search::Query.load_pet_type_by_color_and_species(
if appearance_params[:alt_style_id].present?
target = Item::Search::Query.load_alt_style_by_id(
appearance_params[:alt_style_id])
else
target = Item::Search::Query.load_pet_type_by_color_and_species(
appearance_params[:color_id], appearance_params[:species_id])
pet_type.appearances_for(@items.map(&:id), swf_asset_includes: [:zone])
end
target.appearances_for(@items.map(&:id), swf_asset_includes: [:zone])
end
def search_error(e)

View file

@ -6,11 +6,31 @@ class AltStyle < ApplicationRecord
has_many :swf_assets, through: :parent_swf_asset_relationships
has_many :contributions, as: :contributed, inverse_of: :contributed
scope :matching_name, ->(series_name, color_name, species_name) {
color = Color.find_by_name!(color_name)
species = Species.find_by_name!(species_name)
where(series_name:, color_id: color.id, species_id: species.id)
}
def name
I18n.translate('pet_types.human_name', color_human_name: color.human_name,
species_human_name: species.human_name)
end
# If the series_name hasn't yet been set manually by support staff, show the
# string "<New?>" instead. But it won't be searchable by that string—that is,
# `fits:<New?>-faerie-draik` intentionally will not work, and the canonical
# filter name will be `fits:alt-style-IDNUMBER`, instead.
def series_name
self[:series_name] || "<New?>"
end
# You can use this to check whether `series_name` is returning the actual
# value or its placeholder value.
def has_real_series_name?
self[:series_name].present?
end
def adjective_name
"#{series_name} #{color.human_name}"
end
@ -29,6 +49,11 @@ class AltStyle < ApplicationRecord
swf_asset.image_url
end
# Given a list of item IDs, return how they look on this alt style.
def appearances_for(item_ids, ...)
Item.appearances_for(item_ids, self, ...)
end
def biology=(biology)
# TODO: This is very similar to what `PetState` does, but like… much much
# more compact? Idk if I'm missing something, or if I was just that much

View file

@ -495,6 +495,34 @@ class Item < ApplicationRecord
end
end
# Given a list of item IDs, return how they look on the given target (either
# a pet type or an alt style).
def self.appearances_for(item_ids, target, swf_asset_includes: [])
# First, load all the relationships for these items that also fit this
# body.
relationships = ParentSwfAssetRelationship.
includes(swf_asset: swf_asset_includes).
where(parent_type: "Item", parent_id: item_ids).
where(swf_asset: {body_id: [target.body_id, 0]})
pet_type_body = Appearance::Body.new(target.body_id, target.species)
all_pets_body = Appearance::Body.new(0, nil)
# 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) }
# Finally, for each item, return an appearance—even if it's empty!
item_ids.to_h do |item_id|
assets = assets_by_item_id.fetch(item_id, [])
fits_all_pets = assets.present? && assets.all? { |a| a.body_id == 0 }
body = fits_all_pets ? all_pets_body : pet_type_body
[item_id, Appearance.new(body, assets)]
end
end
def self.all_by_ids_or_children(ids, swf_assets)
swf_asset_ids = []
swf_assets_by_id = {}

View file

@ -62,6 +62,8 @@ class Item
when 'restricts'
is_positive ? Filter.restricts(value) : Filter.not_restricts(value)
when 'fits'
# First, try the `fits:blue-acara` case.
# NOTE: This will also work for `fits:"usuki girl-usul"`!
match = value.match(/^([^-]+)-([^-]+)$/)
if match.present?
color_name, species_name = match.captures
@ -70,6 +72,33 @@ class Item
Filter.fits_pet_type(pet_type, color_name:, species_name:) :
Filter.not_fits_pet_type(pet_type, color_name:, species_name:)
end
# Next, try the `fits:alt-style-87305` case.
match = value.match(/^alt-style-([0-9]+)$/)
if match.present?
alt_style_id, = match.captures
alt_style = load_alt_style_by_id(alt_style_id)
return is_positive ?
Filter.fits_alt_style(alt_style) :
Filter.not_fits_alt_style(alt_style)
end
# Next, try the `fits:nostalgic-faerie-draik` case.
# NOTE: This will also work for `fits:"nostalgic-usuki girl-usul"`!
match = value.match(/^([^-]+)-([^-]+)-([^-]+)$/)
if match.present?
series_name, color_name, species_name = match.captures
alt_style = load_alt_style_by_name(
series_name, color_name, species_name)
return is_positive ?
Filter.fits_alt_style(alt_style) :
Filter.not_fits_alt_style(alt_style)
end
# TODO: We could make `fits:acara` an alias for `species:acara`, or
# even the primary syntax?
# If none of these cases work, raise an error.
raise_search_error "not_found.fits_target", value: value
when 'species'
begin
@ -176,10 +205,19 @@ class Item
end
end
def self.load_alt_style_by_name(series_name, color_name, species_name)
begin
AltStyle.matching_name(series_name, color_name, species_name).first!
rescue ActiveRecord::RecordNotFound
raise_search_error "not_found.alt_style",
filter_text: "#{series_name}-#{color_name}-#{species_name}"
end
end
def self.load_alt_style_by_id(alt_style_id)
begin
AltStyle.find(alt_style_id)
rescue
rescue ActiveRecord::RecordNotFound
raise_search_error "not_found.alt_style",
filter_text: "alt-style-#{alt_style_id}"
end
@ -326,8 +364,18 @@ class Item
end
def self.alt_style_to_filter_text(alt_style)
# If the real series name has been set in the database by support
# staff, use that for the canonical filter text for this alt style.
# Otherwise, represent this alt style by ID.
if alt_style.has_real_series_name?
series_name = alt_style.series_name.downcase
color_name = alt_style.color.name.downcase
species_name = alt_style.species.name.downcase
"#{series_name}-#{color_name}-#{species_name}"
else
"alt-style-#{alt_style.id}"
end
end
end
end
end

View file

@ -169,30 +169,9 @@ class PetType < ApplicationRecord
}.first
end
def appearances_for(item_ids, swf_asset_includes: [])
# First, load all the relationships for these items that also fit this
# body.
relationships = ParentSwfAssetRelationship.
includes(swf_asset: swf_asset_includes).
where(parent_type: "Item", parent_id: item_ids).
where(swf_asset: {body_id: [body_id, 0]})
pet_type_body = Item::Appearance::Body.new(body_id, species)
all_pets_body = Item::Appearance::Body.new(0, nil)
# 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) }
# Finally, for each item, return an appearance—even if it's empty!
item_ids.to_h do |item_id|
assets = assets_by_item_id.fetch(item_id, [])
fits_all_pets = assets.present? && assets.all? { |a| a.body_id == 0 }
body = fits_all_pets ? all_pets_body : pet_type_body
[item_id, Item::Appearance.new(body, assets)]
end
# Given a list of item IDs, return how they look on this pet type.
def appearances_for(item_ids, ...)
Item.appearances_for(item_ids, self, ...)
end
def self.all_by_ids_or_children(ids, pet_states)

View file

@ -0,0 +1,6 @@
class ChangeDefaultForAltStylesSeriesName < ActiveRecord::Migration[7.1]
def change
change_column_null :alt_styles, :series_name, true
change_column_default :alt_styles, :series_name, from: "<New?>", to: nil
end
end

View file

@ -10,14 +10,14 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_02_27_231815) do
ActiveRecord::Schema[7.1].define(version: 2024_02_27_233743) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.integer "species_id", null: false
t.integer "color_id", null: false
t.integer "body_id", null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.string "series_name", default: "<New?>", null: false
t.string "series_name"
t.index ["color_id"], name: "index_alt_styles_on_color_id"
t.index ["species_id"], name: "index_alt_styles_on_species_id"
end