# frozen_string_literal: true require 'nokogiri' require 'date' require 'active_support/core_ext/hash/conversions' require 'active_support/inflector/inflections' class RIXML class << self def parse_from_file(filename) RIXML.new(File.read(filename)) end end def initialize(data) @document = Nokogiri::XML(data) @attrs = Hash.from_xml(@document.root.to_s) @document.remove_namespaces! end def product_id @attrs.dig('Research', 'Product', 'productID') end def status @attrs.dig('Research', 'Product', 'StatusInfo', 'statusType')&.downcase&.to_sym || :published end def publication_date time_str = @attrs.dig('Research', 'Product', 'StatusInfo', 'statusDateTime') || DateTime.now.to_s DateTime.strptime(time_str) end def authors org = @attrs.dig('Research', 'Product', 'Source', 'Organization') || {} if org.is_a? Array org = org.find { |v| v['primaryIndicator'] == 'Yes' } || org.first end authors = org.dig('PersonGroup', 'PersonGroupMember') || [] authors = [authors] unless authors.is_a? Array authors.map { |author| parse_info_from_author(author) } end def report_info content = @attrs.dig('Research', 'Product', 'Content') { title: content['Title'], abstract: content['Abstract'], synopsis: content['Synopsis'], file_name: content['Resource']&.dig('Name'), pages: content['Resource']&.dig('Length').to_i } end def context context = @attrs.dig('Research', 'Product', 'Context') || {} { companies: parse_companies_from_context(context), sectors: parse_sectors_from_context(context), countries: parse_countries_from_context(context) } end def xpath(path) @document.xpath(path) end private def parse_info_from_author(author) person = author&.dig('Person') { name: person['DisplayName'], first_name: person['GivenName'], middle_name: person['MiddleName'], last_name: person['FamilyName'], job_title: person['JobTitle'], email: person['ContactInfo']&.dig('Email')&.downcase } end def parse_sectors_from_context(context) list = context['ProductClassifications'].try(:[], 'SectorIndustry') return [] if list.nil? list = [list] unless list.is_a? Array list.select { |s| s['classificationType'] == 'GICS' }.map do |v| { code: v['code'].to_i, focus: v['focusLevel'].try(:downcase) == 'yes' } end end def parse_countries_from_context(context) [context['ProductClassifications']&.dig('Country')].flatten.compact.map do |country| { code: country['code'].upcase } end end def parse_companies_from_context(context) companies = [] [context['IssuerDetails']].flatten.compact.each do |issuer| [issuer['Issuer']].flatten.compact.select { |c| c['issuerType'] == 'Corporate' }.each do |company| companies << parse_company_info(company).merge(primary: company['primaryIndicator'] == 'Yes') end end companies end def parse_company_info(company) security_ids = company.dig('SecurityDetails', 'Security', 'SecurityID') security_ids = [security_ids].compact unless security_ids.is_a? Array ids = security_ids&.map do |security_id| { security_id['idType'].underscore.to_sym => security_id['idValue'] } end&.reduce({}, :merge) || [] info = { name: (company.dig('IssuerName') || {})['NameValue'] } info.merge(ids) end end