From 8c5e6ea5a0227e525185568acb6c915cab477c66 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 17 Jan 2025 11:56:40 -0800 Subject: [PATCH] Add ImportController and route This adds the controller that handles creating a full party from transformed Granblue Fantasy data --- app/controllers/api/v1/import_controller.rb | 276 ++++++++++++++++++++ config/routes.rb | 2 + 2 files changed, 278 insertions(+) create mode 100644 app/controllers/api/v1/import_controller.rb diff --git a/app/controllers/api/v1/import_controller.rb b/app/controllers/api/v1/import_controller.rb new file mode 100644 index 0000000..7d64775 --- /dev/null +++ b/app/controllers/api/v1/import_controller.rb @@ -0,0 +1,276 @@ +# frozen_string_literal: true + +module Api + module V1 + class ImportController < Api::V1::ApiController + ELEMENT_MAPPING = { + 0 => nil, + 1 => 4, + 2 => 2, + 3 => 3, + 4 => 1, + 5 => 6, + 6 => 5 + }.freeze + + def create + Rails.logger.info "[IMPORT] Starting import..." + + # Parse JSON request body + raw_body = request.raw_post + begin + raw_params = JSON.parse(raw_body) if raw_body.present? + Rails.logger.info "[IMPORT] Raw game data: #{raw_params.inspect}" + rescue JSON::ParserError => e + Rails.logger.error "[IMPORT] Invalid JSON in request body: #{e.message}" + render json: { error: 'Invalid JSON data' }, status: :bad_request + return + end + + if raw_params.nil? || !raw_params.is_a?(Hash) + Rails.logger.error "[IMPORT] Missing or invalid game data" + render json: { error: 'Missing or invalid game data' }, status: :bad_request + return + end + + # Transform game data + transformer = ::Granblue::Transformers::BaseDeckTransformer.new(raw_params) + transformed_data = transformer.transform + Rails.logger.info "[IMPORT] Transformed data: #{transformed_data.inspect}" + + # Validate transformed data + unless transformed_data[:name].present? && transformed_data[:lang].present? + Rails.logger.error "[IMPORT] Missing required fields in transformed data" + render json: { error: 'Missing required fields name or lang' }, status: :unprocessable_entity + return + end + + # Create party + party = Party.new(user: current_user) + + ActiveRecord::Base.transaction do + # Basic party data + party.name = transformed_data[:name] + party.extra = transformed_data[:extra] + party.save! + + # Process job and skills + if transformed_data[:class].present? + process_job(party, transformed_data[:class], transformed_data[:subskills]) + end + + # Process characters + if transformed_data[:characters].present? + process_characters(party, transformed_data[:characters]) + end + + # Process weapons + if transformed_data[:weapons].present? + process_weapons(party, transformed_data[:weapons]) + end + + # Process summons + if transformed_data[:summons].present? + process_summons(party, transformed_data[:summons], transformed_data[:friend_summon]) + end + + # Process sub summons + if transformed_data[:sub_summons].present? + process_sub_summons(party, transformed_data[:sub_summons]) + end + end + + # Return shortcode for redirection + render json: { shortcode: party.shortcode }, status: :created + rescue StandardError => e + Rails.logger.error "[IMPORT] Error processing import: #{e.message}" + Rails.logger.error "[IMPORT] Backtrace: #{e.backtrace.join("\n")}" + render json: { error: 'Error processing import' }, status: :unprocessable_entity + end + + private + + def process_job(party, job_name, subskills) + return unless job_name + job = Job.find_by("name_en = ? OR name_jp = ?", job_name, job_name) + unless job + Rails.logger.warn "[IMPORT] Could not find job: #{job_name}" + return + end + + party.job = job + party.save! + Rails.logger.info "[IMPORT] Assigned job=#{job_name} to party_id=#{party.id}" + + return unless subskills&.any? + subskills.each_with_index do |skill_name, idx| + next if skill_name.blank? + skill = JobSkill.find_by("(name_en = ? OR name_jp = ?) AND job_id = ?", skill_name, skill_name, job.id) + unless skill + Rails.logger.warn "[IMPORT] Could not find skill=#{skill_name} for job_id=#{job.id}" + next + end + party["skill#{idx + 1}_id"] = skill.id + Rails.logger.info "[IMPORT] Assigned skill=#{skill_name} at position #{idx + 1}" + end + end + + def process_characters(party, characters) + return unless characters&.any? + Rails.logger.info "[IMPORT] Processing #{characters.length} characters" + + characters.each_with_index do |char_data, idx| + character = Character.find_by(granblue_id: char_data[:id]) + unless character + Rails.logger.warn "[IMPORT] Character not found: #{char_data[:id]}" + next + end + + GridCharacter.create!( + party: party, + character_id: character.id, + position: idx, + uncap_level: char_data[:uncap], + perpetuity: char_data[:ringed] || false, + transcendence_step: char_data[:transcend] || 0 + ) + Rails.logger.info "[IMPORT] Added character: #{character.name_en} at position #{idx}" + end + end + + def process_weapons(party, weapons) + return unless weapons&.any? + Rails.logger.info "[IMPORT] Processing #{weapons.length} weapons" + + weapons.each_with_index do |weapon_data, idx| + weapon = Weapon.find_by(granblue_id: weapon_data[:id]) + unless weapon + Rails.logger.warn "[IMPORT] Weapon not found: #{weapon_data[:id]}" + next + end + + grid_weapon = GridWeapon.create!( + party: party, + weapon_id: weapon.id, + position: idx - 1, + mainhand: idx.zero?, + uncap_level: weapon_data[:uncap], + transcendence_step: weapon_data[:transcend] || 0, + element: weapon_data[:attr] ? ELEMENT_MAPPING[weapon_data[:attr]] : nil + ) + + process_weapon_keys(grid_weapon, weapon_data[:keys]) if weapon_data[:keys] + process_weapon_ax(grid_weapon, weapon_data[:ax]) if weapon_data[:ax] + + Rails.logger.info "[IMPORT] Added weapon: #{weapon.name_en} at position #{idx - 1}" + end + end + + def process_weapon_keys(grid_weapon, keys) + keys.each_with_index do |key_id, idx| + key = WeaponKey.find_by(granblue_id: key_id) + unless key + Rails.logger.warn "[IMPORT] WeaponKey not found: #{key_id}" + next + end + grid_weapon["weapon_key#{idx + 1}_id"] = key.id + grid_weapon.save! + end + end + + def process_weapon_ax(grid_weapon, ax_skills) + ax_skills.each_with_index do |ax, idx| + grid_weapon["ax_modifier#{idx + 1}"] = ax[:id].to_i + grid_weapon["ax_strength#{idx + 1}"] = ax[:val].to_s.gsub(/[+%]/, '').to_i + end + grid_weapon.save! + end + + def process_summons(party, summons, friend_summon = nil) + return unless summons&.any? + Rails.logger.info "[IMPORT] Processing #{summons.length} summons" + + # Main and sub summons + summons.each_with_index do |summon_data, idx| + summon = Summon.find_by(granblue_id: summon_data[:id]) + unless summon + Rails.logger.warn "[IMPORT] Summon not found: #{summon_data[:id]}" + next + end + + grid_summon = GridSummon.new( + party: party, + summon_id: summon.id, + position: idx, + main: idx.zero?, + friend: false, + uncap_level: summon_data[:uncap], + transcendence_step: summon_data[:transcend] || 0, + quick_summon: summon_data[:qs] || false + ) + + if grid_summon.save + Rails.logger.info "[IMPORT] Added summon: #{summon.name_en} at position #{idx}" + else + Rails.logger.error "[IMPORT] Failed to save summon: #{grid_summon.errors.full_messages}" + end + end + + # Friend summon if provided + process_friend_summon(party, friend_summon) if friend_summon.present? + end + + def process_friend_summon(party, friend_summon) + friend = Summon.find_by("name_en = ? OR name_jp = ?", friend_summon, friend_summon) + unless friend + Rails.logger.warn "[IMPORT] Friend summon not found: #{friend_summon}" + return + end + + grid_summon = GridSummon.new( + party: party, + summon_id: friend.id, + position: 6, + main: false, + friend: true, + uncap_level: friend.ulb ? 5 : (friend.flb ? 4 : 3) + ) + + if grid_summon.save + Rails.logger.info "[IMPORT] Added friend summon: #{friend.name_en}" + else + Rails.logger.error "[IMPORT] Failed to save friend summon: #{grid_summon.errors.full_messages}" + end + end + + def process_sub_summons(party, sub_summons) + return unless sub_summons&.any? + Rails.logger.info "[IMPORT] Processing #{sub_summons.length} sub summons" + + sub_summons.each_with_index do |summon_data, idx| + summon = Summon.find_by(granblue_id: summon_data[:id]) + unless summon + Rails.logger.warn "[IMPORT] Sub summon not found: #{summon_data[:id]}" + next + end + + grid_summon = GridSummon.new( + party: party, + summon_id: summon.id, + position: idx + 5, + main: false, + friend: false, + uncap_level: summon_data[:uncap], + transcendence_step: summon_data[:transcend] || 0 + ) + + if grid_summon.save + Rails.logger.info "[IMPORT] Added sub summon: #{summon.name_en} at position #{idx + 5}" + else + Rails.logger.error "[IMPORT] Failed to save sub summon: #{grid_summon.errors.full_messages}" + end + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index bc8811d..4a7e1dc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,8 @@ Rails.application.routes.draw do get 'version', to: 'api#version' + post 'import', to: 'import#create' + get 'users/info/:id', to: 'users#info' get 'parties/favorites', to: 'parties#favorites'