diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb index 1cc1965..ef0b048 100644 --- a/app/controllers/api/v1/parties_controller.rb +++ b/app/controllers/api/v1/parties_controller.rb @@ -60,7 +60,7 @@ module Api remix: true } - new_party.local_id = party_params[:local_id] if !party_params.nil? + new_party.local_id = party_params[:local_id] unless party_params.nil? if new_party.save render json: PartyBlueprint.render(new_party, view: :created, root: :party), @@ -71,58 +71,34 @@ module Api end def index - conditions = build_conditions + conditions = build_filters - @parties = Party.joins(:weapons) - .group('parties.id') - .having('count(distinct grid_weapons.weapon_id) > 2') - .where(conditions) - .where(name_quality) - .where(user_quality) - .where(original) - .order(created_at: :desc) - .paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE) - .each { |party| party.favorited = current_user ? party.is_favorited(current_user) : false } + query = build_query(conditions) + query = apply_includes(query, params[:includes]) if params[:includes].present? + query = apply_excludes(query, params[:excludes]) if params[:excludes].present? - count = Party.where(conditions).count - total_pages = count.to_f / COLLECTION_PER_PAGE > 1 ? (count.to_f / COLLECTION_PER_PAGE).ceil : 1 + @parties = fetch_parties(query) + count = calculate_count(query) + total_pages = calculate_total_pages(count) - render json: PartyBlueprint.render(@parties, - view: :collection, - root: :results, - meta: { - count: count, - total_pages: total_pages, - per_page: COLLECTION_PER_PAGE - }) + render_party_json(@parties, count, total_pages) end def favorites raise Api::V1::UnauthorizedError unless current_user - conditions = build_conditions + conditions = build_filters conditions[:favorites] = { user_id: current_user.id } - @parties = Party.joins(:favorites) - .where(conditions) - .where(name_quality) - .where(user_quality) - .where(original) - .order('favorites.created_at DESC') - .paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE) - .each { |party| party.favorited = party.is_favorited(current_user) } + query = build_query(conditions) + query = apply_includes(query, params[:includes]) if params[:includes].present? + query = apply_excludes(query, params[:excludes]) if params[:excludes].present? - count = Party.joins(:favorites).where(conditions).count - total_pages = count.to_f / COLLECTION_PER_PAGE > 1 ? (count.to_f / COLLECTION_PER_PAGE).ceil : 1 + @parties = fetch_parties(query) + count = calculate_count(query) + total_pages = calculate_total_pages(count) - render json: PartyBlueprint.render(@parties, - view: :collection, - root: :results, - meta: { - count: count, - total_pages: total_pages, - per_page: COLLECTION_PER_PAGE - }) + render_party_json(@parties, count, total_pages) end private @@ -131,70 +107,179 @@ module Api render_unauthorized_response if @party.user != current_user || @party.edit_key != edit_key end - def build_conditions + def build_filters params = request.params - unless params['recency'].blank? - start_time = (DateTime.current - params['recency'].to_i.seconds) - .to_datetime.beginning_of_day - end + start_time = build_start_time(params['recency']) - min_characters_count = params['characters_count'].blank? ? DEFAULT_MIN_CHARACTERS : params['characters_count'].to_i - min_summons_count = params['summons_count'].blank? ? DEFAULT_MIN_SUMMONS : params['summons_count'].to_i - min_weapons_count = params['weapons_count'].blank? ? DEFAULT_MIN_WEAPONS : params['weapons_count'].to_i - max_clear_time = params['max_clear_time'].blank? ? DEFAULT_MAX_CLEAR_TIME : params['max_clear_time'].to_i + min_characters_count = build_count(params['characters_count'], DEFAULT_MIN_CHARACTERS) + min_summons_count = build_count(params['summons_count'], DEFAULT_MIN_SUMMONS) + min_weapons_count = build_count(params['weapons_count'], DEFAULT_MIN_WEAPONS) + max_clear_time = build_max_clear_time(params['max_clear_time']) - {}.tap do |hash| - # Basic filters - hash[:element] = params['element'].to_i unless params['element'].blank? - hash[:raid] = params['raid'] unless params['raid'].blank? - hash[:created_at] = start_time..DateTime.current unless params['recency'].blank? - - # Advanced filters: Team parameters - hash[:full_auto] = params['full_auto'].to_i unless params['full_auto'].blank? || params['full_auto'].to_i == -1 - hash[:auto_guard] = params['auto_guard'].to_i unless params['auto_guard'].blank? || params['auto_guard'].to_i == -1 - hash[:charge_attack] = params['charge_attack'].to_i unless params['charge_attack'].blank? || params['charge_attack'].to_i == -1 - - # Turn count of 0 will not be displayed, so disallow on the frontend or set default to 1 - # How do we do the same for button count since that can reasonably be 1? - # hash[:turn_count] = params['turn_count'].to_i unless params['turn_count'].blank? || params['turn_count'].to_i <= 0 - # hash[:button_count] = params['button_count'].to_i unless params['button_count'].blank? - # hash[:clear_time] = 0..max_clear_time - - # Advanced filters: Object counts - hash[:characters_count] = min_characters_count..MAX_CHARACTERS - hash[:summons_count] = min_summons_count..MAX_SUMMONS - hash[:weapons_count] = min_weapons_count..MAX_WEAPONS - end + { + element: build_element(params['element']), + raid: params['raid'], + created_at: params['recency'].present? ? start_time..DateTime.current : nil, + full_auto: build_option(params['full_auto']), + auto_guard: build_option(params['auto_guard']), + charge_attack: build_option(params['charge_attack']), + characters_count: min_characters_count..MAX_CHARACTERS, + summons_count: min_summons_count..MAX_SUMMONS, + weapons_count: min_weapons_count..MAX_WEAPONS + }.delete_if { |_k, v| v.nil? } end - def original - "source_party_id IS NULL" unless request.params['original'].blank? || request.params['original'] == "false" + def build_start_time(recency) + return unless recency.present? + + (DateTime.current - recency.to_i.seconds).to_datetime.beginning_of_day + end + + def build_count(value, default) + value.blank? ? default : value.to_i + end + + def build_max_clear_time(value) + value.blank? ? DEFAULT_MAX_CLEAR_TIME : value.to_i + end + + def build_element(element) + element.to_i unless element.blank? + end + + def build_option(value) + value.to_i unless value.blank? || value.to_i == -1 + end + + def build_query(conditions) + Party.distinct + .joins(weapons: [:object], summons: [:object], characters: [:object]) + .group('parties.id') + .where(conditions) + .where(name_quality) + .where(user_quality) + .where(original) + end + + def includes(id) + "(\"#{id_to_table(id)}\".\"granblue_id\" = '#{id}')" + end + + def excludes(id) + "(\"#{id_to_table(id)}\".\"granblue_id\" != '#{id}')" + end + + def apply_includes(query, includes) + included = includes.split(',') + includes_condition = included.map { |id| includes(id) }.join(' AND ') + query.where(includes_condition) + end + + def apply_excludes(query, _excludes) + characters_subquery = excluded_characters.select(1).arel + summons_subquery = excluded_summons.select(1).arel + weapons_subquery = excluded_weapons.select(1).arel + + query.where(characters_subquery.exists.not) + .where(weapons_subquery.exists.not) + .where(summons_subquery.exists.not) + end + + def excluded_characters + return unless params[:excludes] + + excluded = params[:excludes].split(',').filter { |id| id[0] == '3' } + GridCharacter.joins(:object) + .where(characters: { granblue_id: excluded }) + .where('grid_characters.party_id = parties.id') + end + + def excluded_summons + return unless params[:excludes] + + excluded = params[:excludes].split(',').filter { |id| id[0] == '2' } + GridSummon.joins(:object) + .where(summons: { granblue_id: excluded }) + .where('grid_summons.party_id = parties.id') + end + + def excluded_weapons + return unless params[:excludes] + + excluded = params[:excludes].split(',').filter { |id| id[0] == '1' } + GridWeapon.joins(:object) + .where(weapons: { granblue_id: excluded }) + .where('grid_weapons.party_id = parties.id') + end + + def fetch_parties(query) + query.order(created_at: :desc) + .paginate(page: request.params[:page], per_page: COLLECTION_PER_PAGE) + .each { |party| party.favorited = current_user ? party.is_favorited(current_user) : false } + end + + def calculate_count(query) + query.count.values.sum + end + + def calculate_total_pages(count) + count.to_f / COLLECTION_PER_PAGE > 1 ? (count.to_f / COLLECTION_PER_PAGE).ceil : 1 + end + + def render_party_json(parties, count, total_pages) + render json: PartyBlueprint.render(parties, + view: :collection, + root: :results, + meta: { + count: count, + total_pages: total_pages, + per_page: COLLECTION_PER_PAGE + }) end def user_quality - "user_id IS NOT NULL" unless request.params[:user_quality].blank? || request.params[:user_quality] == "false" + 'user_id IS NOT NULL' unless request.params[:user_quality].blank? || request.params[:user_quality] == 'false' end def name_quality low_quality = [ - "Untitled", - "Remix of Untitled", - "Remix of Remix of Untitled", - "Remix of Remix of Remix of Untitled", - "Remix of Remix of Remix of Remix of Untitled", - "Remix of Remix of Remix of Remix of Remix of Untitled", - "無題", - "無題のリミックス", - "無題のリミックスのリミックス", - "無題のリミックスのリミックスのリミックス", - "無題のリミックスのリミックスのリミックスのリミックス", - "無題のリミックスのリミックスのリミックスのリミックスのリミックス" + 'Untitled', + 'Remix of Untitled', + 'Remix of Remix of Untitled', + 'Remix of Remix of Remix of Untitled', + 'Remix of Remix of Remix of Remix of Untitled', + 'Remix of Remix of Remix of Remix of Remix of Untitled', + '無題', + '無題のリミックス', + '無題のリミックスのリミックス', + '無題のリミックスのリミックスのリミックス', + '無題のリミックスのリミックスのリミックスのリミックス', + '無題のリミックスのリミックスのリミックスのリミックスのリミックス' ] joined_names = low_quality.map { |name| "'#{name}'" }.join(',') - "name NOT IN (#{joined_names})" unless request.params[:name_quality].blank? || request.params[:name_quality] == "false" + return if request.params[:name_quality].blank? || request.params[:name_quality] == 'false' + + "name NOT IN (#{joined_names})" + end + + def original + 'source_party_id IS NULL' unless request.params['original'].blank? || request.params['original'] == 'false' + end + + def id_to_table(id) + case id[0] + when '3' + table = 'characters' + when '2' + table = 'summons' + when '1' + table = 'weapons' + end + + table end def remixed_name(name)