* Install Rspec * Create .aidigestignore * Update rails_helper - Added sections and comments - Add support for loading via canonical.rb - Add FactoryBot syntax methods - Disable SQL logging in test environment * Move gems around * Add canonical.rb and test env CSVs We load these CSVs via canonical.rb when we run tests as a data source for canonical objects. * Remove RBS for now This is too much and we need to find the right solution * Refactor GridSummonsController and add tests * Create GridSummon factory * Refactor GridSummon and add documentation and tests * Create have_error_on.rb * Update .aidigestignore * Fix warnings * Add GridWeapons and Parties factories * Refactor GridWeapon and add documentation and tests * Create .rubocop.yml * Create no_weapon_provided_error.rb * Refactor GridWeaponsController - Refactors controller - Adds YARD documentation - Adds Rspec tests * Refactor GridSummonsController - Refactors controller - Adds YARD documentation - Adds Rspec tests * Enable shoulda/matchers * Update User factory * Update party.rb We moved updating the party's element and extra flag to inside the party. We use an after_commit hook to minimize the amount of queries we're running to do this. * Update party.rb We change setting the edit key to use the conditional assignment operator so that it doesn't get overridden when we're running tests. This shouldn't have an effect in production. * Update api_controller.rb Change render_unprocessable_entity_response to render the errors hash instead of the exception so that we get more helpful errors. * Add new errors Added NoCharacterProvidedError and NoSummonProvidedError * Add tests and docs to GridCharacter We added a factory, spec and documentation to the GridCharacter model * Ensure numericality * Move enums into GranblueEnums We don't use these yet, but it gives us a structured place to pull them from. * Refactor GridCharactersController - Refactors controller - Adds YARD documentation - Adds Rspec tests * Add debug hook and other small changes * Update grid_characters_controller.rb Removes logs * Update .gitignore * Update .aidigestignore * Refactored PartiesController - Split PartiesController into three concerns - Implemented testing for PartiesController and two concerns - Implemented fixes across other files to ensure PartiesController tests pass - Added Favorites factory * Implement SimpleCov * Refactor Party model - Refactors Party model - Adds tests - Adds documentation * Update granblue_enums.rb Remove included block
164 lines
5.5 KiB
Ruby
164 lines
5.5 KiB
Ruby
# frozen_string_literal: true
|
||
|
||
module PartyQueryingConcern
|
||
extend ActiveSupport::Concern
|
||
include PartyConstants
|
||
|
||
# Builds the base query for parties with all required associations.
|
||
def build_parties_query(favorites: false)
|
||
query = Party.includes(
|
||
{ raid: :group },
|
||
:job,
|
||
:user,
|
||
:skill0, :skill1, :skill2, :skill3,
|
||
:guidebook1, :guidebook2, :guidebook3,
|
||
{ characters: :character },
|
||
{ weapons: :weapon },
|
||
{ summons: :summon }
|
||
)
|
||
query = if favorites
|
||
query.joins(:favorites)
|
||
.where(favorites: { user_id: current_user.id })
|
||
.distinct.order(created_at: :desc)
|
||
else
|
||
query.order(visibility: :asc, created_at: :desc)
|
||
end
|
||
query = apply_filters(query)
|
||
query = apply_privacy_settings(query, favorites: false)
|
||
query = apply_includes(query, params[:includes]) if params[:includes].present?
|
||
query = apply_excludes(query, params[:excludes]) if params[:excludes].present?
|
||
query
|
||
end
|
||
|
||
# Renders paginated parties using PartyBlueprint.
|
||
def render_paginated_parties(parties)
|
||
render json: Api::V1::PartyBlueprint.render(
|
||
parties,
|
||
view: :preview,
|
||
root: :results,
|
||
meta: {
|
||
count: parties.total_entries,
|
||
total_pages: parties.total_pages,
|
||
per_page: PartyConstants::COLLECTION_PER_PAGE
|
||
},
|
||
current_user: current_user
|
||
)
|
||
end
|
||
|
||
# Applies filters to the query.
|
||
def apply_filters(query)
|
||
conditions = build_filters
|
||
|
||
query = query.where(conditions)
|
||
query = query.where(name_quality) if params[:name_quality].present?
|
||
query.where(
|
||
weapons_count: build_count(params[:weapons_count], PartyConstants::DEFAULT_MIN_WEAPONS)..PartyConstants::MAX_WEAPONS,
|
||
characters_count: build_count(params[:characters_count], PartyConstants::DEFAULT_MIN_CHARACTERS)..PartyConstants::MAX_CHARACTERS,
|
||
summons_count: build_count(params[:summons_count], PartyConstants::DEFAULT_MIN_SUMMONS)..PartyConstants::MAX_SUMMONS
|
||
)
|
||
end
|
||
|
||
# Applies privacy settings based on whether the current user is an admin.
|
||
def apply_privacy_settings(query, favorites: false)
|
||
return query if admin_mode
|
||
|
||
if favorites.present?
|
||
query.where('visibility < 3')
|
||
else
|
||
query.where(visibility: 1)
|
||
end
|
||
end
|
||
|
||
# Builds filtering conditions from request parameters.
|
||
def build_filters
|
||
{
|
||
element: params[:element].present? ? params[:element].to_i : nil,
|
||
raid_id: params[:raid],
|
||
created_at: build_date_range,
|
||
full_auto: build_option(params[:full_auto]),
|
||
auto_guard: build_option(params[:auto_guard]),
|
||
charge_attack: build_option(params[:charge_attack]),
|
||
characters_count: build_count(params[:characters_count], PartyConstants::DEFAULT_MIN_CHARACTERS)..PartyConstants::MAX_CHARACTERS,
|
||
summons_count: build_count(params[:summons_count], PartyConstants::DEFAULT_MIN_SUMMONS)..PartyConstants::MAX_SUMMONS,
|
||
weapons_count: build_count(params[:weapons_count], PartyConstants::DEFAULT_MIN_WEAPONS)..PartyConstants::MAX_WEAPONS
|
||
}.compact
|
||
end
|
||
|
||
# Returns a date range based on the recency parameter.
|
||
def build_date_range
|
||
return nil unless params[:recency].present?
|
||
|
||
start_time = DateTime.current - params[:recency].to_i.seconds
|
||
start_time.beginning_of_day..DateTime.current
|
||
end
|
||
|
||
# Returns the count value or a default if blank.
|
||
def build_count(value, default)
|
||
value.blank? ? default : value.to_i
|
||
end
|
||
|
||
# Processes an option parameter.
|
||
def build_option(value)
|
||
value.to_i unless value.blank? || value.to_i == -1
|
||
end
|
||
|
||
# Applies “includes” filtering for objects in the party.
|
||
def apply_includes(query, includes)
|
||
includes.split(',').each do |id|
|
||
grid_table, object_table = grid_table_and_object_table(id)
|
||
next unless grid_table && object_table
|
||
|
||
condition = <<-SQL.squish
|
||
EXISTS (
|
||
SELECT 1 FROM #{grid_table}
|
||
JOIN #{object_table} ON #{grid_table}.#{object_table.singularize}_id = #{object_table}.id
|
||
WHERE #{object_table}.granblue_id = ? AND #{grid_table}.party_id = parties.id
|
||
)
|
||
SQL
|
||
query = query.where(condition, id)
|
||
end
|
||
query
|
||
end
|
||
|
||
# Applies “excludes” filtering for objects in the party.
|
||
def apply_excludes(query, excludes)
|
||
excludes.split(',').each do |id|
|
||
grid_table, object_table = grid_table_and_object_table(id)
|
||
next unless grid_table && object_table
|
||
|
||
condition = <<-SQL.squish
|
||
NOT EXISTS (
|
||
SELECT 1 FROM #{grid_table}
|
||
JOIN #{object_table} ON #{grid_table}.#{object_table.singularize}_id = #{object_table}.id
|
||
WHERE #{object_table}.granblue_id = ? AND #{grid_table}.party_id = parties.id
|
||
)
|
||
SQL
|
||
query = query.where(condition, id)
|
||
end
|
||
query
|
||
end
|
||
|
||
# Maps an id’s prefix to the corresponding grid and object table names.
|
||
def grid_table_and_object_table(id)
|
||
case id[0]
|
||
when '3' then %w[grid_characters characters]
|
||
when '2' then %w[grid_summons summons]
|
||
when '1' then %w[grid_weapons weapons]
|
||
else [nil, nil]
|
||
end
|
||
end
|
||
|
||
# Returns a remixed party name based on the current party name and current_user language.
|
||
def remixed_name(name)
|
||
blanked_name = { en: name.blank? ? 'Untitled team' : name, ja: name.blank? ? '無名の編成' : name }
|
||
if current_user
|
||
case current_user.language
|
||
when 'en' then "Remix of #{blanked_name[:en]}"
|
||
when 'ja' then "#{blanked_name[:ja]}のリミックス"
|
||
else "Remix of #{blanked_name[:en]}"
|
||
end
|
||
else
|
||
"Remix of #{blanked_name[:en]}"
|
||
end
|
||
end
|
||
end
|