291 lines
9.2 KiB
Ruby
291 lines
9.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'pry'
|
|
|
|
module Granblue
|
|
module Parsers
|
|
# CharacterParser parses character data from gbf.wiki
|
|
class CharacterParser
|
|
attr_reader :granblue_id
|
|
|
|
def initialize(granblue_id: String, debug: false, use_local: false)
|
|
@character = Character.find_by(granblue_id: granblue_id)
|
|
@wiki = Granblue::Parsers::Wiki.new
|
|
@debug = debug || false
|
|
@use_local = use_local
|
|
end
|
|
|
|
# Fetches using @wiki and then processes the response
|
|
# Returns true if successful, false if not
|
|
# Raises an exception if something went wrong
|
|
def fetch(save: false)
|
|
if @use_local && @character.wiki_raw.present?
|
|
wikitext = @character.wiki_raw
|
|
return handle_fetch_success(wikitext, save)
|
|
end
|
|
|
|
response = fetch_wiki_info
|
|
return false if response.nil?
|
|
|
|
redirect = handle_redirected_string(response)
|
|
return fetch(save: save) unless redirect.nil?
|
|
|
|
handle_fetch_success(response, save)
|
|
end
|
|
|
|
private
|
|
|
|
# Determines whether or not the response is a redirect
|
|
# If it is, it will update the character's wiki_en value
|
|
def handle_redirected_string(response)
|
|
redirect = extract_redirected_string(response)
|
|
return unless redirect
|
|
|
|
@character.wiki_en = redirect
|
|
if @character.save!
|
|
ap "Saved new wiki_en value for #{@character.granblue_id}: #{redirect}" if @debug
|
|
redirect
|
|
else
|
|
ap "Unable to save new wiki_en value for #{@character.granblue_id}: #{redirect}" if @debug
|
|
nil
|
|
end
|
|
end
|
|
|
|
# Handle the response from the wiki if the response is successful
|
|
# If the save flag is set, it will persist the data to the database
|
|
def handle_fetch_success(response, save)
|
|
@character.wiki_raw = response
|
|
@character.save!
|
|
|
|
ap "#{@character.granblue_id}: Successfully fetched info for #{@character.wiki_en}" if @debug
|
|
extracted = parse_string(response)
|
|
info = parse(extracted)
|
|
persist(info) if save
|
|
true
|
|
end
|
|
|
|
# Determines whether the response string
|
|
# should be treated as a redirect
|
|
def extract_redirected_string(string)
|
|
string.match(/#REDIRECT \[\[(.*?)\]\]/)&.captures&.first
|
|
end
|
|
|
|
# Parses the response string into a hash
|
|
def parse_string(string)
|
|
lines = string.split("\n")
|
|
data = {}
|
|
stop_loop = false
|
|
|
|
lines.each do |line|
|
|
next if stop_loop
|
|
|
|
if line.include?('Gameplay Notes')
|
|
stop_loop = true
|
|
next
|
|
end
|
|
|
|
next unless line[0] == '|' && line.size > 2
|
|
|
|
key, value = line[1..].split('=', 2).map(&:strip)
|
|
data[key] = value if value
|
|
end
|
|
|
|
data
|
|
end
|
|
|
|
# Fetches data from the GranblueWiki object
|
|
def fetch_wiki_info
|
|
@wiki.fetch(@character.wiki_en)
|
|
rescue WikiError => e
|
|
ap "There was an error fetching #{e.page}: #{e.message}" if @debug
|
|
nil
|
|
end
|
|
|
|
# Iterates over all characters in the database and fetches their data
|
|
# If the save flag is set, data is saved to the database
|
|
# If the overwrite flag is set, data is fetched even if it already exists
|
|
# If the debug flag is set, additional information is printed to the console
|
|
def self.fetch_all(save: false, overwrite: false, debug: false)
|
|
errors = []
|
|
|
|
count = Character.count
|
|
Character.all.each_with_index do |c, i|
|
|
percentage = ((i + 1) / count.to_f * 100).round(2)
|
|
ap "#{percentage}%: Fetching #{c.name_en}... (#{i + 1}/#{count})" if debug
|
|
next unless c.release_date.nil? || overwrite
|
|
|
|
begin
|
|
CharacterParser.new(granblue_id: c.granblue_id,
|
|
debug: debug).fetch(save: save)
|
|
rescue WikiError => e
|
|
errors.push(e.page)
|
|
end
|
|
end
|
|
|
|
ap 'The following pages were unable to be fetched:'
|
|
ap errors
|
|
end
|
|
|
|
def self.fetch_list(list: [], save: false, overwrite: false, debug: false, start: nil)
|
|
errors = []
|
|
|
|
start_index = start.nil? ? 0 : list.index { |id| id == start }
|
|
count = list.drop(start_index).count
|
|
|
|
# ap "Start index: #{start_index}"
|
|
|
|
list.drop(start_index).each_with_index do |id, i|
|
|
chara = Character.find_by(granblue_id: id)
|
|
percentage = ((i + 1) / count.to_f * 100).round(2)
|
|
ap "#{percentage}%: Fetching #{chara.wiki_en}... (#{i + 1}/#{count})" if debug
|
|
next unless chara.release_date.nil? || overwrite
|
|
|
|
begin
|
|
WeaponParser.new(granblue_id: chara.granblue_id,
|
|
debug: debug).fetch(save: save)
|
|
rescue WikiError => e
|
|
errors.push(e.page)
|
|
end
|
|
end
|
|
|
|
ap 'The following pages were unable to be fetched:'
|
|
ap errors
|
|
end
|
|
|
|
# Parses the hash into a format that can be saved to the database
|
|
def parse(hash)
|
|
info = {}
|
|
|
|
info[:name] = { en: hash['name'], ja: hash['jpname'] }
|
|
info[:id] = hash['id']
|
|
info[:charid] = hash['charid'].scan(/\b\d{4}\b/)
|
|
|
|
info[:flb] = Granblue::Parsers::Wiki.boolean.fetch(hash['5star'], false)
|
|
info[:ulb] = hash['max_evo'].to_i == 6
|
|
|
|
info[:rarity] = Granblue::Parsers::Wiki.rarities.fetch(hash['rarity'], 0)
|
|
info[:element] = Granblue::Parsers::Wiki.elements.fetch(hash['element'], 0)
|
|
info[:gender] = Granblue::Parsers::Wiki.genders.fetch(hash['gender'], 0)
|
|
|
|
info[:proficiencies] = proficiencies_from_hash(hash['weapon'])
|
|
info[:races] = races_from_hash(hash['race'])
|
|
|
|
info[:hp] = {
|
|
min_hp: hash['min_hp'].to_i,
|
|
max_hp: hash['max_hp'].to_i,
|
|
max_hp_flb: hash['flb_hp'].to_i
|
|
}
|
|
|
|
info[:atk] = {
|
|
min_atk: hash['min_atk'].to_i,
|
|
max_atk: hash['max_atk'].to_i,
|
|
max_atk_flb: hash['flb_atk'].to_i
|
|
}
|
|
|
|
info[:dates] = {
|
|
release_date: parse_date(hash['release_date']),
|
|
flb_date: parse_date(hash['5star_date']),
|
|
ulb_date: parse_date(hash['6star_date'])
|
|
}
|
|
|
|
info[:links] = {
|
|
wiki: { en: hash['name'], ja: hash['link_jpwiki'] },
|
|
gamewith: hash['link_gamewith'],
|
|
kamigame: hash['link_kamigame']
|
|
}
|
|
|
|
info.compact
|
|
end
|
|
|
|
# Saves select fields to the database
|
|
def persist(hash)
|
|
@character.release_date = hash[:dates][:release_date]
|
|
@character.flb_date = hash[:dates][:flb_date] if hash[:dates].key?(:flb_date)
|
|
@character.ulb_date = hash[:dates][:ulb_date] if hash[:dates].key?(:ulb_date)
|
|
|
|
@character.wiki_ja = hash[:links][:wiki][:ja] if hash[:links].key?(:wiki) && hash[:links][:wiki].key?(:ja)
|
|
@character.gamewith = hash[:links][:gamewith] if hash[:links].key?(:gamewith)
|
|
@character.kamigame = hash[:links][:kamigame] if hash[:links].key?(:kamigame)
|
|
|
|
if @character.save
|
|
ap "#{@character.granblue_id}: Successfully saved info for #{@character.name_en}" if @debug
|
|
puts
|
|
true
|
|
end
|
|
|
|
false
|
|
end
|
|
|
|
# Converts proficiencies from a string to a hash
|
|
def proficiencies_from_hash(character)
|
|
character.to_s.split(',').map.with_index do |prof, i|
|
|
{ "proficiency#{i + 1}" => Granblue::Parsers::Wiki.proficiencies[prof] }
|
|
end.reduce({}, :merge)
|
|
end
|
|
|
|
# Converts races from a string to a hash
|
|
def races_from_hash(race)
|
|
race.to_s.split(',').map.with_index do |r, i|
|
|
{ "race#{i + 1}" => Granblue::Parsers::Wiki.races[r] }
|
|
end.reduce({}, :merge)
|
|
end
|
|
|
|
# Parses a date string into a Date object
|
|
def parse_date(date_str)
|
|
Date.parse(date_str) unless date_str.blank?
|
|
end
|
|
|
|
# Unused methods for now
|
|
def extract_abilities(hash)
|
|
abilities = []
|
|
hash.each do |key, value|
|
|
next unless key =~ /^a(\d+)_/
|
|
|
|
ability_number = Regexp.last_match(1).to_i
|
|
abilities[ability_number] ||= {}
|
|
|
|
case key.gsub(/^a\d+_/, '')
|
|
when 'cd'
|
|
cooldown = parse_substring(value)
|
|
abilities[ability_number]['cooldown'] = cooldown
|
|
when 'dur'
|
|
duration = parse_substring(value)
|
|
abilities[ability_number]['duration'] = duration
|
|
when 'oblevel'
|
|
obtained = parse_substring(value)
|
|
abilities[ability_number]['obtained'] = obtained
|
|
else
|
|
abilities[ability_number][key.gsub(/^a\d+_/, '')] = value
|
|
end
|
|
end
|
|
|
|
{ 'abilities' => abilities.compact }
|
|
end
|
|
|
|
def parse_substring(string)
|
|
hash = {}
|
|
|
|
string.scan(/\|([^|=]+?)=([^|]+)/) do |key, value|
|
|
value.gsub!(/\}\}$/, '') if value.include?('}}')
|
|
hash[key] = value
|
|
end
|
|
|
|
hash
|
|
end
|
|
|
|
def extract_ougis(hash)
|
|
ougi = []
|
|
hash.each do |key, value|
|
|
next unless key =~ /^ougi(\d*)_(.*)/
|
|
|
|
ougi_number = Regexp.last_match(1)
|
|
ougi_key = Regexp.last_match(2)
|
|
ougi[ougi_number.to_i] ||= {}
|
|
ougi[ougi_number.to_i][ougi_key] = value
|
|
end
|
|
|
|
{ 'ougis' => ougi.compact }
|
|
end
|
|
end
|
|
end
|
|
end
|