relaton-doi 1.14.2 → 1.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/lib/relaton_doi/crossref.rb +11 -424
- data/lib/relaton_doi/parser.rb +803 -0
- data/lib/relaton_doi/version.rb +1 -1
- data/lib/relaton_doi.rb +1 -2
- data/relaton-doi.gemspec +1 -1
- metadata +9 -8
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8c74297f60135d99153e2e5f7f3c4a1acb3945b43fbcc26868bf900b2e9ec3c0
         | 
| 4 | 
            +
              data.tar.gz: 6c241a6fc93c87a1751bde8772752c859ee9a000c04729ddc3910386c4827d66
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 74ef5db4deb7ff21ba09e5caa2e98c051549e81fa6d0ea1249787767d19d3cb10af911e1c51a11d6cf965fc856e2cdd7d6538b0eea19200bc43782f707550521
         | 
| 7 | 
            +
              data.tar.gz: 7b13cd9e6f765f3f0e2fff5c703ae24c9b4c3b98c96a2138b46cb0a969ee64431390a5551696c2a5a2b00b1581b3910988ef3ee40da3a7ad6527174e1660bfc0
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/lib/relaton_doi/crossref.rb
    CHANGED
    
    | @@ -1,53 +1,6 @@ | |
| 1 1 | 
             
            module RelatonDoi
         | 
| 2 | 
            -
               | 
| 3 | 
            -
                 | 
| 4 | 
            -
                  "book-chapter" => "inbook",
         | 
| 5 | 
            -
                  "book-part" => "inbook",
         | 
| 6 | 
            -
                  "book-section" => "inbook",
         | 
| 7 | 
            -
                  "book-series" => "book",
         | 
| 8 | 
            -
                  "book-set" => "book",
         | 
| 9 | 
            -
                  "book-track" => "inbook",
         | 
| 10 | 
            -
                  "component" => "misc",
         | 
| 11 | 
            -
                  "database" => "dataset",
         | 
| 12 | 
            -
                  "dissertation" => "thesis",
         | 
| 13 | 
            -
                  "edited-book" => "book",
         | 
| 14 | 
            -
                  "grant" => "misc",
         | 
| 15 | 
            -
                  "journal-article" => "article",
         | 
| 16 | 
            -
                  "journal-issue" => "journal",
         | 
| 17 | 
            -
                  "journal-volume" => "journal",
         | 
| 18 | 
            -
                  "monograph" => "book",
         | 
| 19 | 
            -
                  "other" => "misc",
         | 
| 20 | 
            -
                  "peer-review" => "article",
         | 
| 21 | 
            -
                  "posted-content" => "social_media",
         | 
| 22 | 
            -
                  "proceedings-article" => "inproceedings",
         | 
| 23 | 
            -
                  "proceedings-series" => "proceedings",
         | 
| 24 | 
            -
                  "reference-book" => "book",
         | 
| 25 | 
            -
                  "reference-entry" => "inbook",
         | 
| 26 | 
            -
                  "report-component" => "techreport",
         | 
| 27 | 
            -
                  "report-series" => "techreport",
         | 
| 28 | 
            -
                  "report" => "techreport",
         | 
| 29 | 
            -
                }.freeze
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                REALATION_TYPES = {
         | 
| 32 | 
            -
                  "is-preprint-of" => "reprintOf",
         | 
| 33 | 
            -
                  "is-review-of" => "reviewOf",
         | 
| 34 | 
            -
                  "has-review" => "hasReview",
         | 
| 35 | 
            -
                  "is-identical-to" => "identicalTo", # ?
         | 
| 36 | 
            -
                  "is-supplement-to" => "complements",
         | 
| 37 | 
            -
                }.freeze
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                #
         | 
| 40 | 
            -
                # Get a document by DOI from the CrossRef API.
         | 
| 41 | 
            -
                #
         | 
| 42 | 
            -
                # @param [String] doi The DOI.
         | 
| 43 | 
            -
                #
         | 
| 44 | 
            -
                # @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
         | 
| 45 | 
            -
                #   RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
         | 
| 46 | 
            -
                #   RelatonNist::NistBibliographicItem] The bibitem.
         | 
| 47 | 
            -
                #
         | 
| 48 | 
            -
                def self.get(doi)
         | 
| 49 | 
            -
                  new.get doi
         | 
| 50 | 
            -
                end
         | 
| 2 | 
            +
              module Crossref
         | 
| 3 | 
            +
                extend self
         | 
| 51 4 |  | 
| 52 5 | 
             
                #
         | 
| 53 6 | 
             
                # Get a document by DOI from the CrossRef API.
         | 
| @@ -61,387 +14,21 @@ module RelatonDoi | |
| 61 14 | 
             
                def get(doi)
         | 
| 62 15 | 
             
                  warn "[relaton-doi] [\"#{doi}\"] fetching..."
         | 
| 63 16 | 
             
                  id = doi.sub(%r{^doi:}, "")
         | 
| 64 | 
            -
                   | 
| 65 | 
            -
                   | 
| 66 | 
            -
                   | 
| 67 | 
            -
                  create_bibitem @message["DOI"], bibitem_hash
         | 
| 68 | 
            -
                end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                #
         | 
| 71 | 
            -
                # Create a bibitem from the bibitem hash.
         | 
| 72 | 
            -
                #
         | 
| 73 | 
            -
                # @param [String] doi The DOI.
         | 
| 74 | 
            -
                # @param [Hash] bibitem The bibitem hash.
         | 
| 75 | 
            -
                #
         | 
| 76 | 
            -
                # @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
         | 
| 77 | 
            -
                #   RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
         | 
| 78 | 
            -
                #   RelatonNist::NistBibliographicItem] The bibitem.
         | 
| 79 | 
            -
                #
         | 
| 80 | 
            -
                # @raise [RelatonDoi::Error] if the document type is not supported.
         | 
| 81 | 
            -
                #
         | 
| 82 | 
            -
                def create_bibitem(doi, bibitem) # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 83 | 
            -
                  # case @message["institution"]&.first&.fetch("acronym")&.first
         | 
| 84 | 
            -
                  case doi
         | 
| 85 | 
            -
                  when /\/nist/ then RelatonNist::NistBibliographicItem.new(**bibitem)
         | 
| 86 | 
            -
                  when /\/rfc\d+/ then RelatonIetf::IetfBibliographicItem.new(**bibitem)
         | 
| 87 | 
            -
                  when /\/0026-1394\// then RelatonBipm::BipmBibliographicItem.new(**bibitem)
         | 
| 88 | 
            -
                  # when "ISO" then RelatonIso::IsoBibliographicItem.new(**bibitem)
         | 
| 89 | 
            -
                  # when "W3C" then RelatonW3c::W3cBibliographicItem.new(**bibitem)
         | 
| 90 | 
            -
                  when /\/ieee/ then RelatonIeee::IeeeBibliographicItem.new(**bibitem)
         | 
| 91 | 
            -
                  else RelatonBib::BibliographicItem.new(**bibitem)
         | 
| 92 | 
            -
                  end
         | 
| 93 | 
            -
                end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                #
         | 
| 96 | 
            -
                # Create a bibitem hash from the message hash.
         | 
| 97 | 
            -
                #
         | 
| 98 | 
            -
                # @return [Hash] The bibitem hash.
         | 
| 99 | 
            -
                #
         | 
| 100 | 
            -
                def bibitem_hash # rubocop:disable Metrics/MethodLength
         | 
| 101 | 
            -
                  {
         | 
| 102 | 
            -
                    type: parse_type,
         | 
| 103 | 
            -
                    fetched: Date.today.to_s,
         | 
| 104 | 
            -
                    title: create_title,
         | 
| 105 | 
            -
                    docid: create_docid,
         | 
| 106 | 
            -
                    date: create_date,
         | 
| 107 | 
            -
                    link: create_link,
         | 
| 108 | 
            -
                    abstract: create_abstract,
         | 
| 109 | 
            -
                    contributor: create_contributors,
         | 
| 110 | 
            -
                    doctype: @message["type"],
         | 
| 111 | 
            -
                    place: create_place,
         | 
| 112 | 
            -
                    relation: create_relation,
         | 
| 113 | 
            -
                    extent: create_extent,
         | 
| 114 | 
            -
                    series: create_series,
         | 
| 115 | 
            -
                  }
         | 
| 116 | 
            -
                end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                #
         | 
| 119 | 
            -
                # Parse the document type.
         | 
| 120 | 
            -
                #
         | 
| 121 | 
            -
                # @return [String] The document type.
         | 
| 122 | 
            -
                #
         | 
| 123 | 
            -
                def parse_type
         | 
| 124 | 
            -
                  TYPES[@message["type"]] || @message["type"]
         | 
| 125 | 
            -
                end
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                #
         | 
| 128 | 
            -
                # Create a title and a subtitle from the message hash.
         | 
| 129 | 
            -
                #
         | 
| 130 | 
            -
                # @return [Array<RelatonBib::TypedTitleString>] The title and subtitle.
         | 
| 131 | 
            -
                #
         | 
| 132 | 
            -
                def create_title
         | 
| 133 | 
            -
                  @message["title"].map do |t|
         | 
| 134 | 
            -
                    RelatonBib::TypedTitleString.new(
         | 
| 135 | 
            -
                      type: "main", content: t, language: "en", script: "Latn",
         | 
| 136 | 
            -
                    )
         | 
| 137 | 
            -
                  end + @message["subtitle"].map do |t|
         | 
| 138 | 
            -
                    RelatonBib::TypedTitleString.new(
         | 
| 139 | 
            -
                      type: "subtitle", content: t, language: "en", script: "Latn",
         | 
| 140 | 
            -
                    )
         | 
| 141 | 
            -
                  end
         | 
| 142 | 
            -
                end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                #
         | 
| 145 | 
            -
                # Create a docid from the message hash.
         | 
| 146 | 
            -
                #
         | 
| 147 | 
            -
                # @return [Array<RelatonBib::DocumentIdentifier>] The docid.
         | 
| 148 | 
            -
                #
         | 
| 149 | 
            -
                def create_docid
         | 
| 150 | 
            -
                  %w[DOI ISBN].each_with_object([]) do |type, obj|
         | 
| 151 | 
            -
                    id = @message[type].is_a?(Array) ? @message[type].first : @message[type]
         | 
| 152 | 
            -
                    next unless id
         | 
| 153 | 
            -
             | 
| 154 | 
            -
                    primary = type == "DOI"
         | 
| 155 | 
            -
                    obj << RelatonBib::DocumentIdentifier.new(type: type, id: id, primary: primary)
         | 
| 156 | 
            -
                  end
         | 
| 17 | 
            +
                  message = get_by_id id
         | 
| 18 | 
            +
                  warn "[relaton-doi] [\"#{doi}\"] found #{message['DOI']}"
         | 
| 19 | 
            +
                  Parser.parse message
         | 
| 157 20 | 
             
                end
         | 
| 158 21 |  | 
| 159 22 | 
             
                #
         | 
| 160 | 
            -
                #  | 
| 161 | 
            -
                #
         | 
| 162 | 
            -
                # @return [Array<RelatonBib::BibliographicDate>] The dates.
         | 
| 163 | 
            -
                #
         | 
| 164 | 
            -
                def create_date
         | 
| 165 | 
            -
                  %w[created issued published approved].each_with_object([]) do |type, obj|
         | 
| 166 | 
            -
                    next unless @message[type]
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                    on = @message[type]["date-parts"][0].map { |d| d.to_s.rjust(2, "0") }.join "-"
         | 
| 169 | 
            -
                    obj << RelatonBib::BibliographicDate.new(type: type, on: on)
         | 
| 170 | 
            -
                  end
         | 
| 171 | 
            -
                end
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                #
         | 
| 174 | 
            -
                # Create a link from the message hash.
         | 
| 175 | 
            -
                #
         | 
| 176 | 
            -
                # @return [Array<RelatonBib::TypedUri>] The link.
         | 
| 177 | 
            -
                #
         | 
| 178 | 
            -
                def create_link
         | 
| 179 | 
            -
                  links = []
         | 
| 180 | 
            -
                  if @message["URL"]
         | 
| 181 | 
            -
                    links << RelatonBib::TypedUri.new(type: "DOI", content: @message["URL"])
         | 
| 182 | 
            -
                  end
         | 
| 183 | 
            -
                  return links unless @message["link"]&.any?
         | 
| 184 | 
            -
             | 
| 185 | 
            -
                  link = @message["link"].first
         | 
| 186 | 
            -
                  if link["URL"].match?(/\.pdf$/)
         | 
| 187 | 
            -
                    links << RelatonBib::TypedUri.new(type: "pdf", content: link["URL"])
         | 
| 188 | 
            -
                  end
         | 
| 189 | 
            -
                  links
         | 
| 190 | 
            -
                end
         | 
| 191 | 
            -
             | 
| 192 | 
            -
                #
         | 
| 193 | 
            -
                # Create an abstract from the message hash.
         | 
| 194 | 
            -
                #
         | 
| 195 | 
            -
                # @return [Array<RelatonBib::FormattedString>] The abstract.
         | 
| 196 | 
            -
                #
         | 
| 197 | 
            -
                def create_abstract
         | 
| 198 | 
            -
                  return [] unless @message["abstract"]
         | 
| 199 | 
            -
             | 
| 200 | 
            -
                  content = @message["abstract"]
         | 
| 201 | 
            -
                  abstract = RelatonBib::FormattedString.new(
         | 
| 202 | 
            -
                    content: content, language: "en", script: "Latn", format: "text/html",
         | 
| 203 | 
            -
                  )
         | 
| 204 | 
            -
                  [abstract]
         | 
| 205 | 
            -
                end
         | 
| 206 | 
            -
             | 
| 207 | 
            -
                #
         | 
| 208 | 
            -
                # Create contributors from the message hash.
         | 
| 209 | 
            -
                #
         | 
| 210 | 
            -
                # @return [Array<RelatonBib::ContributionInfo>] The contributors.
         | 
| 211 | 
            -
                #
         | 
| 212 | 
            -
                def create_contributors # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
         | 
| 213 | 
            -
                  contribs = %w[author editor translator].each_with_object([]) do |type, obj|
         | 
| 214 | 
            -
                    @message[type]&.each do |contrib|
         | 
| 215 | 
            -
                      obj << contributor(person(contrib), type)
         | 
| 216 | 
            -
                    end
         | 
| 217 | 
            -
                  end
         | 
| 218 | 
            -
                  contribs << contributor(org_publisher, "publisher")
         | 
| 219 | 
            -
                end
         | 
| 220 | 
            -
             | 
| 221 | 
            -
                #
         | 
| 222 | 
            -
                # Cerate an organization publisher from the message hash.
         | 
| 223 | 
            -
                #
         | 
| 224 | 
            -
                # @return [RelatonBib::Organization] The organization.
         | 
| 225 | 
            -
                #
         | 
| 226 | 
            -
                def org_publisher
         | 
| 227 | 
            -
                  pbr = @message["institution"]&.detect do |i|
         | 
| 228 | 
            -
                    @message["publisher"].include?(i["name"]) ||
         | 
| 229 | 
            -
                      i["name"].include?(@message["publisher"])
         | 
| 230 | 
            -
                  end
         | 
| 231 | 
            -
                  a = pbr["acronym"]&.first if pbr
         | 
| 232 | 
            -
                  RelatonBib::Organization.new name: @message["publisher"], abbreviation: a
         | 
| 233 | 
            -
                end
         | 
| 234 | 
            -
             | 
| 235 | 
            -
                #
         | 
| 236 | 
            -
                # Create contributor from an entity and a role type.
         | 
| 237 | 
            -
                #
         | 
| 238 | 
            -
                # @param [RelatonBib::Person, RelatonBib::Organization] entity The entity.
         | 
| 239 | 
            -
                # @param [String] type The role type.
         | 
| 240 | 
            -
                #
         | 
| 241 | 
            -
                # @return [RelatonBib::ContributionInfo] The contributor.
         | 
| 242 | 
            -
                #
         | 
| 243 | 
            -
                def contributor(entity, type)
         | 
| 244 | 
            -
                  RelatonBib::ContributionInfo.new(entity: entity, role: [type: type])
         | 
| 245 | 
            -
                end
         | 
| 246 | 
            -
             | 
| 247 | 
            -
                #
         | 
| 248 | 
            -
                # Create a person from a person hash.
         | 
| 249 | 
            -
                #
         | 
| 250 | 
            -
                # @param [Hash] person The person hash.
         | 
| 251 | 
            -
                #
         | 
| 252 | 
            -
                # @return [RelatonBib::Person] The person.
         | 
| 253 | 
            -
                #
         | 
| 254 | 
            -
                def person(person)
         | 
| 255 | 
            -
                  RelatonBib::Person.new(
         | 
| 256 | 
            -
                    name: person_name(person), affiliation: affiliation(person),
         | 
| 257 | 
            -
                    identifier: person_id(person)
         | 
| 258 | 
            -
                  )
         | 
| 259 | 
            -
                end
         | 
| 260 | 
            -
             | 
| 261 | 
            -
                #
         | 
| 262 | 
            -
                # Create person affiliations from a person hash.
         | 
| 263 | 
            -
                #
         | 
| 264 | 
            -
                # @param [Hash] person The person hash.
         | 
| 265 | 
            -
                #
         | 
| 266 | 
            -
                # @return [Array<RelatonBib::Affiliation>] The affiliations.
         | 
| 267 | 
            -
                #
         | 
| 268 | 
            -
                def affiliation(person)
         | 
| 269 | 
            -
                  (person["affiliation"] || []).map do |a|
         | 
| 270 | 
            -
                    org = RelatonBib::Organization.new(name: a["name"])
         | 
| 271 | 
            -
                    RelatonBib::Affiliation.new organization: org
         | 
| 272 | 
            -
                  end
         | 
| 273 | 
            -
                end
         | 
| 274 | 
            -
             | 
| 275 | 
            -
                #
         | 
| 276 | 
            -
                # Create a person full name from a person hash.
         | 
| 277 | 
            -
                #
         | 
| 278 | 
            -
                # @param [Hash] person The person hash.
         | 
| 279 | 
            -
                #
         | 
| 280 | 
            -
                # @return [RelatonBib::FullName] The full name.
         | 
| 281 | 
            -
                #
         | 
| 282 | 
            -
                def person_name(person)
         | 
| 283 | 
            -
                  sn = RelatonBib::LocalizedString.new(person["family"], "en", "Latn")
         | 
| 284 | 
            -
                  RelatonBib::FullName.new(
         | 
| 285 | 
            -
                    surname: sn, forename: forename(person), addition: nameaddition(person),
         | 
| 286 | 
            -
                    completename: completename(person), prefix: nameprefix(person)
         | 
| 287 | 
            -
                  )
         | 
| 288 | 
            -
                end
         | 
| 289 | 
            -
             | 
| 290 | 
            -
                #
         | 
| 291 | 
            -
                # Create a person name prefix from a person hash.
         | 
| 292 | 
            -
                #
         | 
| 293 | 
            -
                # @param [Hash] person The person hash.
         | 
| 294 | 
            -
                #
         | 
| 295 | 
            -
                # @return [Array<RelatonBib::LocalizedString>] The name prefix.
         | 
| 296 | 
            -
                #
         | 
| 297 | 
            -
                def nameprefix(person)
         | 
| 298 | 
            -
                  return [] unless person["prefix"]
         | 
| 299 | 
            -
             | 
| 300 | 
            -
                  [RelatonBib::LocalizedString.new(person["prefix"], "en", "Latn")]
         | 
| 301 | 
            -
                end
         | 
| 302 | 
            -
             | 
| 303 | 
            -
                #
         | 
| 304 | 
            -
                # Create a complete name from a person hash.
         | 
| 305 | 
            -
                #
         | 
| 306 | 
            -
                # @param [Hash] person The person hash.
         | 
| 307 | 
            -
                #
         | 
| 308 | 
            -
                # @return [RelatonBib::LocalizedString] The complete name.
         | 
| 309 | 
            -
                #
         | 
| 310 | 
            -
                def completename(person)
         | 
| 311 | 
            -
                  return unless person["name"]
         | 
| 312 | 
            -
             | 
| 313 | 
            -
                  RelatonBib::LocalizedString.new(person["name"], "en", "Latn")
         | 
| 314 | 
            -
                end
         | 
| 315 | 
            -
             | 
| 316 | 
            -
                #
         | 
| 317 | 
            -
                # Create a forename from a person hash.
         | 
| 318 | 
            -
                #
         | 
| 319 | 
            -
                # @param [Hash] person The person hash.
         | 
| 320 | 
            -
                #
         | 
| 321 | 
            -
                # @return [Array<RelatonBib::LocalizedString>] The forename.
         | 
| 322 | 
            -
                #
         | 
| 323 | 
            -
                def forename(person)
         | 
| 324 | 
            -
                  return [] unless person["given"]
         | 
| 325 | 
            -
             | 
| 326 | 
            -
                  [RelatonBib::Forename.new(content: person["given"], language: "en", script: "Latn")]
         | 
| 327 | 
            -
                end
         | 
| 328 | 
            -
             | 
| 329 | 
            -
                #
         | 
| 330 | 
            -
                # Create an addition from a person hash.
         | 
| 331 | 
            -
                #
         | 
| 332 | 
            -
                # @param [Hash] person The person hash.
         | 
| 333 | 
            -
                #
         | 
| 334 | 
            -
                # @return [Array<RelatonBib::LocalizedString>] The addition.
         | 
| 335 | 
            -
                #
         | 
| 336 | 
            -
                def nameaddition(person)
         | 
| 337 | 
            -
                  return [] unless person["suffix"]
         | 
| 338 | 
            -
             | 
| 339 | 
            -
                  [RelatonBib::LocalizedString.new(person["suffix"], "en", "Latn")]
         | 
| 340 | 
            -
                end
         | 
| 341 | 
            -
             | 
| 342 | 
            -
                #
         | 
| 343 | 
            -
                # Create a person identifier from a person hash.
         | 
| 344 | 
            -
                #
         | 
| 345 | 
            -
                # @param [Hash] person The person hash.
         | 
| 346 | 
            -
                #
         | 
| 347 | 
            -
                # @return [Array<RelatonBib::PersonIdentifier>] The person identifier.
         | 
| 348 | 
            -
                #
         | 
| 349 | 
            -
                def person_id(person)
         | 
| 350 | 
            -
                  return [] unless person["ORCID"]
         | 
| 351 | 
            -
             | 
| 352 | 
            -
                  [RelatonBib::PersonIdentifier.new("orcid", person["ORCID"])]
         | 
| 353 | 
            -
                end
         | 
| 354 | 
            -
             | 
| 355 | 
            -
                #
         | 
| 356 | 
            -
                # Create a place from the message hash.
         | 
| 357 | 
            -
                #
         | 
| 358 | 
            -
                # @return [Array<RelatonBib::Place>] The place.
         | 
| 359 | 
            -
                #
         | 
| 360 | 
            -
                def create_place
         | 
| 361 | 
            -
                  return [] unless @message["publisher-location"]
         | 
| 362 | 
            -
             | 
| 363 | 
            -
                  city, rg = @message["publisher-location"].split(", ")
         | 
| 364 | 
            -
                  region = RelatonBib::Place::RegionType.new(name: rg)
         | 
| 365 | 
            -
                  [RelatonBib::Place.new(city: city, region: [region])]
         | 
| 366 | 
            -
                end
         | 
| 367 | 
            -
             | 
| 368 | 
            -
                #
         | 
| 369 | 
            -
                # Crerate relations from the message hash.
         | 
| 370 | 
            -
                #
         | 
| 371 | 
            -
                # @return [Array<RelatonBib::DocumentRelation>] The relations.
         | 
| 372 | 
            -
                #
         | 
| 373 | 
            -
                def create_relation # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
         | 
| 374 | 
            -
                  rels = []
         | 
| 375 | 
            -
                  @message["container-title"]&.each do |ct|
         | 
| 376 | 
            -
                    contrib = included_in_editors(ct)
         | 
| 377 | 
            -
                    bib = RelatonBib::BibliographicItem.new(title: [content: ct], contributor: contrib)
         | 
| 378 | 
            -
                    rels << RelatonBib::DocumentRelation.new(type: "includedIn", bibitem: bib)
         | 
| 379 | 
            -
                  end
         | 
| 380 | 
            -
                  @message["relation"].each_with_object(rels) do |(k, v), a|
         | 
| 381 | 
            -
                    fref = RelatonBib::FormattedRef.new(content: v["id"])
         | 
| 382 | 
            -
                    bib = create_bibitem v["id"], formattedref: fref
         | 
| 383 | 
            -
                    type = REALATION_TYPES[k] || k
         | 
| 384 | 
            -
                    a << RelatonBib::DocumentRelation.new(type: type, bibitem: bib)
         | 
| 385 | 
            -
                  end
         | 
| 386 | 
            -
                end
         | 
| 387 | 
            -
             | 
| 388 | 
            -
                #
         | 
| 389 | 
            -
                # Fetch included in editors.
         | 
| 390 | 
            -
                #
         | 
| 391 | 
            -
                # @param [String] title container-title
         | 
| 392 | 
            -
                #
         | 
| 393 | 
            -
                # @return [Array<RelatonBib::ContributionInfo>] The editors contribution info.
         | 
| 394 | 
            -
                #
         | 
| 395 | 
            -
                def included_in_editors(title)
         | 
| 396 | 
            -
                  item = fetch_included_in title
         | 
| 397 | 
            -
                  return [] unless item
         | 
| 398 | 
            -
             | 
| 399 | 
            -
                  item["editor"].map { |e| contributor(person(e), "editor") }
         | 
| 400 | 
            -
                end
         | 
| 401 | 
            -
             | 
| 402 | 
            -
                #
         | 
| 403 | 
            -
                # Fetch included in relation.
         | 
| 404 | 
            -
                #
         | 
| 405 | 
            -
                # @param [String] title container-title
         | 
| 406 | 
            -
                #
         | 
| 407 | 
            -
                # @return [Hash] The included in relation item.
         | 
| 408 | 
            -
                #
         | 
| 409 | 
            -
                def fetch_included_in(title) # rubocop:disable Metrics/AbcSize
         | 
| 410 | 
            -
                  year = (@message["published"] || @message["approved"])["date-parts"][0][0]
         | 
| 411 | 
            -
                  query = "#{title}, #{@message['publisher']}, #{@message['publisher-location']}, #{year}"
         | 
| 412 | 
            -
                  resp = Faraday.get %{http://api.crossref.org/works?query.bibliographic="#{query}"&rows=5&filter=type:book}
         | 
| 413 | 
            -
                  json = JSON.parse resp.body
         | 
| 414 | 
            -
                  json["message"]["items"].detect { |i| i["title"].include?(title) && i["editor"] }
         | 
| 415 | 
            -
                end
         | 
| 416 | 
            -
             | 
| 417 | 
            -
                #
         | 
| 418 | 
            -
                # Create an extent from the message hash.
         | 
| 419 | 
            -
                #
         | 
| 420 | 
            -
                # @return [Array<RelatonBib::Locality>] The extent.
         | 
| 421 | 
            -
                #
         | 
| 422 | 
            -
                def create_extent # rubocop:disable Metrics/AbcSize
         | 
| 423 | 
            -
                  extent = []
         | 
| 424 | 
            -
                  extent << RelatonBib::Locality.new("volume", @message["volume"]) if @message["volume"]
         | 
| 425 | 
            -
                  extent << RelatonBib::Locality.new("issue", @message["issue"]) if @message["issue"]
         | 
| 426 | 
            -
                  if @message["page"]
         | 
| 427 | 
            -
                    from, to = @message["page"].split("-")
         | 
| 428 | 
            -
                    extent << RelatonBib::Locality.new("page", from, to)
         | 
| 429 | 
            -
                  end
         | 
| 430 | 
            -
                  extent.any? ? [RelatonBib::LocalityStack.new(extent)] : []
         | 
| 431 | 
            -
                end
         | 
| 432 | 
            -
             | 
| 23 | 
            +
                # Get a document by DOI from the CrossRef API.
         | 
| 433 24 | 
             
                #
         | 
| 434 | 
            -
                #  | 
| 25 | 
            +
                # @param [String] id The DOI.
         | 
| 435 26 | 
             
                #
         | 
| 436 | 
            -
                # @return [ | 
| 27 | 
            +
                # @return [Hash] The document.
         | 
| 437 28 | 
             
                #
         | 
| 438 | 
            -
                def  | 
| 439 | 
            -
                   | 
| 440 | 
            -
             | 
| 441 | 
            -
                  @message["container-title"].map do |ct|
         | 
| 442 | 
            -
                    title = RelatonBib::TypedTitleString.new content: ct
         | 
| 443 | 
            -
                    RelatonBib::Series.new title: title
         | 
| 444 | 
            -
                  end
         | 
| 29 | 
            +
                def get_by_id(id)
         | 
| 30 | 
            +
                  resp = Serrano.works ids: id
         | 
| 31 | 
            +
                  resp[0]["message"]
         | 
| 445 32 | 
             
                end
         | 
| 446 33 | 
             
              end
         | 
| 447 34 | 
             
            end
         | 
| @@ -0,0 +1,803 @@ | |
| 1 | 
            +
            module RelatonDoi
         | 
| 2 | 
            +
              class Parser
         | 
| 3 | 
            +
                COUNTRIES = %w[USA].freeze
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                TYPES = {
         | 
| 6 | 
            +
                  "book-chapter" => "inbook",
         | 
| 7 | 
            +
                  "book-part" => "inbook",
         | 
| 8 | 
            +
                  "book-section" => "inbook",
         | 
| 9 | 
            +
                  "book-series" => "book",
         | 
| 10 | 
            +
                  "book-set" => "book",
         | 
| 11 | 
            +
                  "book-track" => "inbook",
         | 
| 12 | 
            +
                  "component" => "misc",
         | 
| 13 | 
            +
                  "database" => "dataset",
         | 
| 14 | 
            +
                  "dissertation" => "thesis",
         | 
| 15 | 
            +
                  "edited-book" => "book",
         | 
| 16 | 
            +
                  "grant" => "misc",
         | 
| 17 | 
            +
                  "journal-article" => "article",
         | 
| 18 | 
            +
                  "journal-issue" => "article",
         | 
| 19 | 
            +
                  "journal-volume" => "journal",
         | 
| 20 | 
            +
                  "monograph" => "book",
         | 
| 21 | 
            +
                  "other" => "misc",
         | 
| 22 | 
            +
                  "peer-review" => "article",
         | 
| 23 | 
            +
                  "posted-content" => "dataset",
         | 
| 24 | 
            +
                  "proceedings-article" => "inproceedings",
         | 
| 25 | 
            +
                  "proceedings-series" => "proceedings",
         | 
| 26 | 
            +
                  "reference-book" => "book",
         | 
| 27 | 
            +
                  "reference-entry" => "inbook",
         | 
| 28 | 
            +
                  "report-component" => "techreport",
         | 
| 29 | 
            +
                  "report-series" => "techreport",
         | 
| 30 | 
            +
                  "report" => "techreport",
         | 
| 31 | 
            +
                }.freeze
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                REALATION_TYPES = {
         | 
| 34 | 
            +
                  "is-cited-by" => "isCitedIn",
         | 
| 35 | 
            +
                  "belongs-to" => "related",
         | 
| 36 | 
            +
                  "is-child-of" => "includedIn",
         | 
| 37 | 
            +
                  "is-expression-of" => "expressionOf",
         | 
| 38 | 
            +
                  "has-expression" => "hasExpression",
         | 
| 39 | 
            +
                  "is-manifestation-of" => "manifestationOf",
         | 
| 40 | 
            +
                  "is-manuscript-of" => "draftOf",
         | 
| 41 | 
            +
                  "has-manuscript" => "hasDraft",
         | 
| 42 | 
            +
                  "is-preprint-of" => "draftOf",
         | 
| 43 | 
            +
                  "has-preprint" => "hasDraft",
         | 
| 44 | 
            +
                  "is-replaced-by" => "obsoletedBy",
         | 
| 45 | 
            +
                  "replaces" => "obsoletes",
         | 
| 46 | 
            +
                  "is-translation-of" => "translatedFrom",
         | 
| 47 | 
            +
                  "has-translation" => "hasTranslation",
         | 
| 48 | 
            +
                  "is-version-of" => "editionOf",
         | 
| 49 | 
            +
                  "has-version" => "hasEdition",
         | 
| 50 | 
            +
                  "is-based-on" => "updates",
         | 
| 51 | 
            +
                  "is-basis-for" => "updatedBy",
         | 
| 52 | 
            +
                  "is-comment-on" => "commentaryOf",
         | 
| 53 | 
            +
                  "has-comment" => "hasCommentary",
         | 
| 54 | 
            +
                  "is-continued-by" => "hasSuccessor",
         | 
| 55 | 
            +
                  "continues" => "successorOf",
         | 
| 56 | 
            +
                  "is-derived-from" => "derives",
         | 
| 57 | 
            +
                  "has-derivation" => "derivedFrom",
         | 
| 58 | 
            +
                  "is-documented-by" => "describedBy",
         | 
| 59 | 
            +
                  "documents" => "describes",
         | 
| 60 | 
            +
                  "is-part-of" => "partOf",
         | 
| 61 | 
            +
                  "has-part" => "hasPart",
         | 
| 62 | 
            +
                  "is-review-of" => "reviewOf",
         | 
| 63 | 
            +
                  "has-review" => "hasReview",
         | 
| 64 | 
            +
                  "references" => "cites",
         | 
| 65 | 
            +
                  "is-referenced-by" => "isCitedIn",
         | 
| 66 | 
            +
                  "requires" => "hasComplement",
         | 
| 67 | 
            +
                  "is-required-by" => "complementOf",
         | 
| 68 | 
            +
                  "is-supplement-to" => "complementOf",
         | 
| 69 | 
            +
                  "is-supplemented-by" => "hasComplement",
         | 
| 70 | 
            +
                }.freeze
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                ATTRS = %i[type fetched title docid date link abstract contributor place
         | 
| 73 | 
            +
                           doctype relation extent series medium].freeze
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                #
         | 
| 76 | 
            +
                # Initialize instance.
         | 
| 77 | 
            +
                #
         | 
| 78 | 
            +
                # @param [Hash] src The source hash.
         | 
| 79 | 
            +
                #
         | 
| 80 | 
            +
                def initialize(src)
         | 
| 81 | 
            +
                  @src = src
         | 
| 82 | 
            +
                  @item = {}
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                #
         | 
| 86 | 
            +
                # Initialize instance and parse the source hash.
         | 
| 87 | 
            +
                #
         | 
| 88 | 
            +
                # @param [Hash] src The source hash.
         | 
| 89 | 
            +
                #
         | 
| 90 | 
            +
                # @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
         | 
| 91 | 
            +
                #   RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
         | 
| 92 | 
            +
                #   RelatonNist::NistBibliographicItem] The bibitem.
         | 
| 93 | 
            +
                #
         | 
| 94 | 
            +
                def self.parse(src)
         | 
| 95 | 
            +
                  new(src).parse
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                #
         | 
| 99 | 
            +
                # Parse the source hash.
         | 
| 100 | 
            +
                #
         | 
| 101 | 
            +
                # @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
         | 
| 102 | 
            +
                #   RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
         | 
| 103 | 
            +
                #   RelatonNist::NistBibliographicItem] The bibitem.
         | 
| 104 | 
            +
                #
         | 
| 105 | 
            +
                def parse
         | 
| 106 | 
            +
                  ATTRS.each { |m| @item[m] = send "parse_#{m}" }
         | 
| 107 | 
            +
                  create_bibitem @src["DOI"], @item
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                #
         | 
| 111 | 
            +
                # Create a bibitem from the bibitem hash.
         | 
| 112 | 
            +
                #
         | 
| 113 | 
            +
                # @param [String] doi The DOI.
         | 
| 114 | 
            +
                # @param [Hash] bibitem The bibitem hash.
         | 
| 115 | 
            +
                #
         | 
| 116 | 
            +
                # @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
         | 
| 117 | 
            +
                #   RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
         | 
| 118 | 
            +
                #   RelatonNist::NistBibliographicItem] The bibitem.
         | 
| 119 | 
            +
                #
         | 
| 120 | 
            +
                def create_bibitem(doi, bibitem) # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 121 | 
            +
                  case doi
         | 
| 122 | 
            +
                  when /\/nist/ then RelatonNist::NistBibliographicItem.new(**bibitem)
         | 
| 123 | 
            +
                  when /\/rfc\d+/ then RelatonIetf::IetfBibliographicItem.new(**bibitem)
         | 
| 124 | 
            +
                  when /\/0026-1394\// then RelatonBipm::BipmBibliographicItem.new(**bibitem)
         | 
| 125 | 
            +
                  when /\/ieee/ then RelatonIeee::IeeeBibliographicItem.new(**bibitem)
         | 
| 126 | 
            +
                  else RelatonBib::BibliographicItem.new(**bibitem)
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                #
         | 
| 131 | 
            +
                # Parse the type.
         | 
| 132 | 
            +
                #
         | 
| 133 | 
            +
                # @return [String] The type.
         | 
| 134 | 
            +
                #
         | 
| 135 | 
            +
                def parse_type
         | 
| 136 | 
            +
                  TYPES[@src["type"]] || @src["type"]
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                #
         | 
| 140 | 
            +
                # Parse the document type
         | 
| 141 | 
            +
                #
         | 
| 142 | 
            +
                # @return [String] The document type.
         | 
| 143 | 
            +
                #
         | 
| 144 | 
            +
                def parse_doctype
         | 
| 145 | 
            +
                  @src["type"]
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                # Parse the fetched date.
         | 
| 150 | 
            +
                #
         | 
| 151 | 
            +
                # @return [String] The fetched date.
         | 
| 152 | 
            +
                #
         | 
| 153 | 
            +
                def parse_fetched
         | 
| 154 | 
            +
                  Date.today.to_s
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                #
         | 
| 158 | 
            +
                # Parse titles from the source hash.
         | 
| 159 | 
            +
                #
         | 
| 160 | 
            +
                # @return [Array<Hash>] The titles.
         | 
| 161 | 
            +
                #
         | 
| 162 | 
            +
                def parse_title # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 163 | 
            +
                  if @src["title"].is_a?(Array) && @src["title"].any?
         | 
| 164 | 
            +
                    main_sub_titles
         | 
| 165 | 
            +
                  elsif @src["project"].is_a?(Array) && @src["project"].any?
         | 
| 166 | 
            +
                    project_titles
         | 
| 167 | 
            +
                  elsif @src["container-title"].is_a?(Array) && @src["container-title"].size > 1
         | 
| 168 | 
            +
                    @src["container-title"][0..-2].map { |t| create_title t }
         | 
| 169 | 
            +
                  else []
         | 
| 170 | 
            +
                  end
         | 
| 171 | 
            +
                end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                #
         | 
| 174 | 
            +
                # Parse main and subtitle from the source hash.
         | 
| 175 | 
            +
                #
         | 
| 176 | 
            +
                # @return [Array<Hash>] The titles.
         | 
| 177 | 
            +
                #
         | 
| 178 | 
            +
                def main_sub_titles
         | 
| 179 | 
            +
                  title = @src["title"].map { |t| create_title t }
         | 
| 180 | 
            +
                  RelatonBib.array(@src["subtitle"]).each { |t| title << create_title(t, "subtitle") }
         | 
| 181 | 
            +
                  RelatonBib.array(@src["short-title"]).each { |t| title << create_title(t, "short") }
         | 
| 182 | 
            +
                  title
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                #
         | 
| 186 | 
            +
                # Fetch titles from the projects.
         | 
| 187 | 
            +
                #
         | 
| 188 | 
            +
                # @return [Array<Hash>] The titles.
         | 
| 189 | 
            +
                #
         | 
| 190 | 
            +
                def project_titles
         | 
| 191 | 
            +
                  RelatonBib.array(@src["project"]).reduce([]) do |memo, proj|
         | 
| 192 | 
            +
                    memo + RelatonBib.array(proj["project-title"]).map { |t| create_title t["title"] }
         | 
| 193 | 
            +
                  end
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                #
         | 
| 197 | 
            +
                # Create a title from the title and type.
         | 
| 198 | 
            +
                #
         | 
| 199 | 
            +
                # @param [String] title The title content.
         | 
| 200 | 
            +
                # @param [String] type The title type. Defaults to "main".
         | 
| 201 | 
            +
                #
         | 
| 202 | 
            +
                # @return [RelatonBib::TypedTitleString] The title.
         | 
| 203 | 
            +
                #
         | 
| 204 | 
            +
                def create_title(title, type = "main")
         | 
| 205 | 
            +
                  cnt = str_cleanup title
         | 
| 206 | 
            +
                  RelatonBib::TypedTitleString.new type: type, content: cnt, script: "Latn"
         | 
| 207 | 
            +
                end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                #
         | 
| 210 | 
            +
                # Parse a docid from the source hash.
         | 
| 211 | 
            +
                #
         | 
| 212 | 
            +
                # @return [Array<RelatonBib::DocumentIdentifier>] The docid.
         | 
| 213 | 
            +
                #
         | 
| 214 | 
            +
                def parse_docid
         | 
| 215 | 
            +
                  %w[DOI ISBN ISSN].each_with_object([]) do |type, obj|
         | 
| 216 | 
            +
                    prm = type == "DOI"
         | 
| 217 | 
            +
                    RelatonBib.array(@src[type]).each do |id|
         | 
| 218 | 
            +
                      t = issn_type(type, id)
         | 
| 219 | 
            +
                      obj << RelatonBib::DocumentIdentifier.new(type: t, id: id, primary: prm)
         | 
| 220 | 
            +
                    end
         | 
| 221 | 
            +
                  end
         | 
| 222 | 
            +
                end
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                #
         | 
| 225 | 
            +
                # Create an ISSN type if it's an ISSN ID.
         | 
| 226 | 
            +
                #
         | 
| 227 | 
            +
                # @param [String] type identifier type
         | 
| 228 | 
            +
                # @param [String] id identifier
         | 
| 229 | 
            +
                #
         | 
| 230 | 
            +
                # @return [String] identifier type
         | 
| 231 | 
            +
                #
         | 
| 232 | 
            +
                def issn_type(type, id)
         | 
| 233 | 
            +
                  return type unless type == "ISSN"
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                  t = @src["issn-type"]&.find { |it| it["value"] == id }&.dig("type")
         | 
| 236 | 
            +
                  t ? "issn.#{t}" : type.downcase
         | 
| 237 | 
            +
                end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                #
         | 
| 240 | 
            +
                # Parce dates from the source hash.
         | 
| 241 | 
            +
                #
         | 
| 242 | 
            +
                # @return [Array<RelatonBib::BibliographicDate>] The dates.
         | 
| 243 | 
            +
                #
         | 
| 244 | 
            +
                def parse_date # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 245 | 
            +
                  dates = %w[issued published approved].each_with_object([]) do |type, obj|
         | 
| 246 | 
            +
                    next unless @src.dig(type, "date-parts")&.first&.compact&.any?
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                    obj << RelatonBib::BibliographicDate.new(type: type, on: date_type(type))
         | 
| 249 | 
            +
                  end
         | 
| 250 | 
            +
                  if dates.none?
         | 
| 251 | 
            +
                    dates << RelatonBib::BibliographicDate.new(type: "created", on: date_type("created"))
         | 
| 252 | 
            +
                  end
         | 
| 253 | 
            +
                  dates
         | 
| 254 | 
            +
                end
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                #
         | 
| 257 | 
            +
                # Join date parts into a string.
         | 
| 258 | 
            +
                #
         | 
| 259 | 
            +
                # @param [String] type The date type.
         | 
| 260 | 
            +
                #
         | 
| 261 | 
            +
                # @return [String] The date string.
         | 
| 262 | 
            +
                #
         | 
| 263 | 
            +
                def date_type(type)
         | 
| 264 | 
            +
                  @src[type]["date-parts"][0].map { |d| d.to_s.rjust(2, "0") }.join "-"
         | 
| 265 | 
            +
                end
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                #
         | 
| 268 | 
            +
                # Parse links from the source hash.
         | 
| 269 | 
            +
                #
         | 
| 270 | 
            +
                # @return [Array<RelatonBib::TypedUri>] The links.
         | 
| 271 | 
            +
                #
         | 
| 272 | 
            +
                def parse_link # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
         | 
| 273 | 
            +
                  disprefered_links = %w[similarity-checking text-mining]
         | 
| 274 | 
            +
                  links = []
         | 
| 275 | 
            +
                  if @src["URL"]
         | 
| 276 | 
            +
                    links << RelatonBib::TypedUri.new(type: "DOI", content: @src["URL"])
         | 
| 277 | 
            +
                  end
         | 
| 278 | 
            +
                  [@src["link"], @src.dig("resource", "primary")].flatten.compact.each do |l|
         | 
| 279 | 
            +
                    next if disprefered_links.include? l["intended-application"]
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                    type =  case l["URL"]
         | 
| 282 | 
            +
                            when /\.pdf$/ then "pdf"
         | 
| 283 | 
            +
                            # when /\/rfc\d+$|iopscience\.iop\.org|ieeexplore\.ieee\.org/
         | 
| 284 | 
            +
                            else "src"
         | 
| 285 | 
            +
                            end
         | 
| 286 | 
            +
                    links << RelatonBib::TypedUri.new(type: type, content: l["URL"]) # if type
         | 
| 287 | 
            +
                  end
         | 
| 288 | 
            +
                  links
         | 
| 289 | 
            +
                end
         | 
| 290 | 
            +
             | 
| 291 | 
            +
                #
         | 
| 292 | 
            +
                # Parse abstract from the source hash.
         | 
| 293 | 
            +
                #
         | 
| 294 | 
            +
                # @return [Array<RelatonBib::FormattedString>] The abstract.
         | 
| 295 | 
            +
                #
         | 
| 296 | 
            +
                def parse_abstract
         | 
| 297 | 
            +
                  return [] unless @src["abstract"]
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                  content = @src["abstract"]
         | 
| 300 | 
            +
                  abstract = RelatonBib::FormattedString.new(
         | 
| 301 | 
            +
                    content: content, language: "en", script: "Latn", format: "text/html",
         | 
| 302 | 
            +
                  )
         | 
| 303 | 
            +
                  [abstract]
         | 
| 304 | 
            +
                end
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                #
         | 
| 307 | 
            +
                # Parse contributors from the source hash.
         | 
| 308 | 
            +
                #
         | 
| 309 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The contributors.
         | 
| 310 | 
            +
                #
         | 
| 311 | 
            +
                def parse_contributor
         | 
| 312 | 
            +
                  contribs = author_investigators
         | 
| 313 | 
            +
                  contribs += authors_editors_translators
         | 
| 314 | 
            +
                  contribs += contribs_from_parent(contribs)
         | 
| 315 | 
            +
                  contribs << contributor(org_publisher, "publisher")
         | 
| 316 | 
            +
                  contribs += org_aurhorizer
         | 
| 317 | 
            +
                  contribs + org_enabler
         | 
| 318 | 
            +
                end
         | 
| 319 | 
            +
             | 
| 320 | 
            +
                #
         | 
| 321 | 
            +
                # Create authors investigators from the source hash.
         | 
| 322 | 
            +
                #
         | 
| 323 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The authors investigators.
         | 
| 324 | 
            +
                #
         | 
| 325 | 
            +
                def author_investigators
         | 
| 326 | 
            +
                  RelatonBib.array(@src["project"]).reduce([]) do |memo, proj|
         | 
| 327 | 
            +
                    memo + create_investigators(proj, "lead-investigator") +
         | 
| 328 | 
            +
                      create_investigators(proj, "investigator")
         | 
| 329 | 
            +
                  end
         | 
| 330 | 
            +
                end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                #
         | 
| 333 | 
            +
                # Create investigators from the project.
         | 
| 334 | 
            +
                #
         | 
| 335 | 
            +
                # @param [Hash] project The project hash.
         | 
| 336 | 
            +
                # @param [String] type The investigator type. "lead-investigator" or "investigator".
         | 
| 337 | 
            +
                #
         | 
| 338 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The investigators.
         | 
| 339 | 
            +
                #
         | 
| 340 | 
            +
                def create_investigators(project, type)
         | 
| 341 | 
            +
                  description = type.gsub("-", " ")
         | 
| 342 | 
            +
                  RelatonBib.array(project[type]).map do |inv|
         | 
| 343 | 
            +
                    contributor(create_person(inv), "author", description)
         | 
| 344 | 
            +
                  end
         | 
| 345 | 
            +
                end
         | 
| 346 | 
            +
             | 
| 347 | 
            +
                #
         | 
| 348 | 
            +
                # Create authors editors translators from the source hash.
         | 
| 349 | 
            +
                #
         | 
| 350 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The authors editors translators.
         | 
| 351 | 
            +
                #
         | 
| 352 | 
            +
                def authors_editors_translators
         | 
| 353 | 
            +
                  %w[author editor translator].each_with_object([]) do |type, a|
         | 
| 354 | 
            +
                    @src[type]&.each do |c|
         | 
| 355 | 
            +
                      contrib = if c["family"]
         | 
| 356 | 
            +
                                  create_person(c)
         | 
| 357 | 
            +
                                else
         | 
| 358 | 
            +
                                  RelatonBib::Organization.new(name: str_cleanup(c["name"]))
         | 
| 359 | 
            +
                                end
         | 
| 360 | 
            +
                      a << contributor(contrib, type)
         | 
| 361 | 
            +
                    end
         | 
| 362 | 
            +
                  end
         | 
| 363 | 
            +
                end
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                #
         | 
| 366 | 
            +
                # Fetch authors and editors from parent if they are not present in the book part.
         | 
| 367 | 
            +
                #
         | 
| 368 | 
            +
                # @param [Array<RelatonBib::ContributionInfo>] contribs present contributors
         | 
| 369 | 
            +
                #
         | 
| 370 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] contributors with authors and editors from parent
         | 
| 371 | 
            +
                #
         | 
| 372 | 
            +
                def contribs_from_parent(contribs) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 373 | 
            +
                  return [] unless %w[inbook inproceedings dataset].include?(parse_type) && @src["container-title"]
         | 
| 374 | 
            +
             | 
| 375 | 
            +
                  has_authors = contribs.any? { |c| c.role&.any? { |r| r.type == "author" } }
         | 
| 376 | 
            +
                  has_editors = contribs.any? { |c| c.role&.any? { |r| r.type == "editor" } }
         | 
| 377 | 
            +
                  return [] if has_authors && has_editors
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                  item = fetch_parent
         | 
| 380 | 
            +
                  authors = create_authors_editors(has_authors, "author", item)
         | 
| 381 | 
            +
                  editors = create_authors_editors(has_editors, "editor", item)
         | 
| 382 | 
            +
                  authors + editors
         | 
| 383 | 
            +
                end
         | 
| 384 | 
            +
             | 
| 385 | 
            +
                #
         | 
| 386 | 
            +
                # Fetch parent item from Crossref.
         | 
| 387 | 
            +
                #
         | 
| 388 | 
            +
                # @return [Hash, nil] parent item
         | 
| 389 | 
            +
                #
         | 
| 390 | 
            +
                def fetch_parent # rubocop:disable Metrics/AbcSize
         | 
| 391 | 
            +
                  query = [@src["container-title"][0], fetch_year].compact.join "+"
         | 
| 392 | 
            +
                  filter = "type:#{%w[book book-set edited-book monograph reference-book].join ',type:'}"
         | 
| 393 | 
            +
                  resp = Faraday.get "https://api.crossref.org/works?query=#{query}&filter=#{filter}"
         | 
| 394 | 
            +
                  json = JSON.parse resp.body
         | 
| 395 | 
            +
                  json["message"]["items"].detect { |i| i["title"].include? @src["container-title"][0] }
         | 
| 396 | 
            +
                end
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                #
         | 
| 399 | 
            +
                # Create authors and editors from parent item.
         | 
| 400 | 
            +
                #
         | 
| 401 | 
            +
                # @param [Boolean] has true if authors or editors are present in the book part
         | 
| 402 | 
            +
                # @param [String] type "author" or "editor"
         | 
| 403 | 
            +
                # @param [Hash, nil] item parent item
         | 
| 404 | 
            +
                #
         | 
| 405 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] authors or editors
         | 
| 406 | 
            +
                #
         | 
| 407 | 
            +
                def create_authors_editors(has, type, item)
         | 
| 408 | 
            +
                  return [] if has || !item
         | 
| 409 | 
            +
             | 
| 410 | 
            +
                  RelatonBib.array(item[type]).map { |a| contributor(create_person(a), type) }
         | 
| 411 | 
            +
                end
         | 
| 412 | 
            +
             | 
| 413 | 
            +
                #
         | 
| 414 | 
            +
                # Cerate an organization publisher from the source hash.
         | 
| 415 | 
            +
                #
         | 
| 416 | 
            +
                # @return [RelatonBib::Organization] The organization.
         | 
| 417 | 
            +
                #
         | 
| 418 | 
            +
                def org_publisher
         | 
| 419 | 
            +
                  pbr = @src["institution"]&.detect do |i|
         | 
| 420 | 
            +
                    @src["publisher"].include?(i["name"]) ||
         | 
| 421 | 
            +
                      i["name"].include?(@src["publisher"])
         | 
| 422 | 
            +
                  end
         | 
| 423 | 
            +
                  a = pbr["acronym"]&.first if pbr
         | 
| 424 | 
            +
                  RelatonBib::Organization.new name: str_cleanup(@src["publisher"]), abbreviation: a
         | 
| 425 | 
            +
                end
         | 
| 426 | 
            +
             | 
| 427 | 
            +
                #
         | 
| 428 | 
            +
                # Clean up trailing punctuation and whitespace from a string.
         | 
| 429 | 
            +
                #
         | 
| 430 | 
            +
                # @param [String] str The string to clean up.
         | 
| 431 | 
            +
                #
         | 
| 432 | 
            +
                # @return [String] The cleaned up string.
         | 
| 433 | 
            +
                #
         | 
| 434 | 
            +
                def str_cleanup(str)
         | 
| 435 | 
            +
                  str.strip.sub(/[,\/\s]+$/, "").sub(/\s:$/, "")
         | 
| 436 | 
            +
                end
         | 
| 437 | 
            +
             | 
| 438 | 
            +
                #
         | 
| 439 | 
            +
                # Parse authorizer contributor from the source hash.
         | 
| 440 | 
            +
                #
         | 
| 441 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The authorizer contributor.
         | 
| 442 | 
            +
                #
         | 
| 443 | 
            +
                def org_aurhorizer
         | 
| 444 | 
            +
                  return [] unless @src["standards-body"]
         | 
| 445 | 
            +
             | 
| 446 | 
            +
                  name, acronym = @src["standards-body"].values_at("name", "acronym")
         | 
| 447 | 
            +
                  org = RelatonBib::Organization.new name: name, abbreviation: acronym
         | 
| 448 | 
            +
                  [contributor(org, "authorizer")]
         | 
| 449 | 
            +
                end
         | 
| 450 | 
            +
             | 
| 451 | 
            +
                #
         | 
| 452 | 
            +
                # Parse enabler contributor from the source hash.
         | 
| 453 | 
            +
                #
         | 
| 454 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The enabler contributor.
         | 
| 455 | 
            +
                #
         | 
| 456 | 
            +
                def org_enabler
         | 
| 457 | 
            +
                  RelatonBib.array(@src["project"]).each_with_object([]) do |proj, memo|
         | 
| 458 | 
            +
                    proj["funding"].each do |f|
         | 
| 459 | 
            +
                      memo << create_enabler(f.dig("funder", "name"))
         | 
| 460 | 
            +
                    end
         | 
| 461 | 
            +
                  end + RelatonBib.array(@src["funder"]).map { |f| create_enabler f["name"] }
         | 
| 462 | 
            +
                end
         | 
| 463 | 
            +
             | 
| 464 | 
            +
                #
         | 
| 465 | 
            +
                # Create enabler contributor with type "enabler".
         | 
| 466 | 
            +
                #
         | 
| 467 | 
            +
                # @param [String] name <description>
         | 
| 468 | 
            +
                #
         | 
| 469 | 
            +
                # @return [RelatonBib::ContributionInfo] The enabler contributor.
         | 
| 470 | 
            +
                #
         | 
| 471 | 
            +
                def create_enabler(name)
         | 
| 472 | 
            +
                  org = RelatonBib::Organization.new name: name
         | 
| 473 | 
            +
                  contributor(org, "enabler")
         | 
| 474 | 
            +
                end
         | 
| 475 | 
            +
             | 
| 476 | 
            +
                #
         | 
| 477 | 
            +
                # Create contributor from an entity and a role type.
         | 
| 478 | 
            +
                #
         | 
| 479 | 
            +
                # @param [RelatonBib::Person, RelatonBib::Organization] entity The entity.
         | 
| 480 | 
            +
                # @param [String] type The role type.
         | 
| 481 | 
            +
                #
         | 
| 482 | 
            +
                # @return [RelatonBib::ContributionInfo] The contributor.
         | 
| 483 | 
            +
                #
         | 
| 484 | 
            +
                def contributor(entity, type, descriprion = nil)
         | 
| 485 | 
            +
                  role = { type: type }
         | 
| 486 | 
            +
                  role[:description] = [descriprion] if descriprion
         | 
| 487 | 
            +
                  RelatonBib::ContributionInfo.new(entity: entity, role: [role])
         | 
| 488 | 
            +
                end
         | 
| 489 | 
            +
             | 
| 490 | 
            +
                #
         | 
| 491 | 
            +
                # Create a person from a person hash.
         | 
| 492 | 
            +
                #
         | 
| 493 | 
            +
                # @param [Hash] person The person hash.
         | 
| 494 | 
            +
                #
         | 
| 495 | 
            +
                # @return [RelatonBib::Person] The person.
         | 
| 496 | 
            +
                #
         | 
| 497 | 
            +
                def create_person(person)
         | 
| 498 | 
            +
                  RelatonBib::Person.new(
         | 
| 499 | 
            +
                    name: create_person_name(person),
         | 
| 500 | 
            +
                    affiliation: create_affiliation(person),
         | 
| 501 | 
            +
                    identifier: person_id(person),
         | 
| 502 | 
            +
                  )
         | 
| 503 | 
            +
                end
         | 
| 504 | 
            +
             | 
| 505 | 
            +
                #
         | 
| 506 | 
            +
                # Create person affiliations from a person hash.
         | 
| 507 | 
            +
                #
         | 
| 508 | 
            +
                # @param [Hash] person The person hash.
         | 
| 509 | 
            +
                #
         | 
| 510 | 
            +
                # @return [Array<RelatonBib::Affiliation>] The affiliations.
         | 
| 511 | 
            +
                #
         | 
| 512 | 
            +
                def create_affiliation(person)
         | 
| 513 | 
            +
                  (person["affiliation"] || []).map do |a|
         | 
| 514 | 
            +
                    org = RelatonBib::Organization.new(name: a["name"])
         | 
| 515 | 
            +
                    RelatonBib::Affiliation.new organization: org
         | 
| 516 | 
            +
                  end
         | 
| 517 | 
            +
                end
         | 
| 518 | 
            +
             | 
| 519 | 
            +
                #
         | 
| 520 | 
            +
                # Create a person full name from a person hash.
         | 
| 521 | 
            +
                #
         | 
| 522 | 
            +
                # @param [Hash] person The person hash.
         | 
| 523 | 
            +
                #
         | 
| 524 | 
            +
                # @return [RelatonBib::FullName] The full name.
         | 
| 525 | 
            +
                #
         | 
| 526 | 
            +
                def create_person_name(person)
         | 
| 527 | 
            +
                  surname = titlecase(person["family"])
         | 
| 528 | 
            +
                  sn = RelatonBib::LocalizedString.new(surname, "en", "Latn")
         | 
| 529 | 
            +
                  RelatonBib::FullName.new(
         | 
| 530 | 
            +
                    surname: sn, forename: forename(person), addition: nameaddition(person),
         | 
| 531 | 
            +
                    completename: completename(person), prefix: nameprefix(person)
         | 
| 532 | 
            +
                  )
         | 
| 533 | 
            +
                end
         | 
| 534 | 
            +
             | 
| 535 | 
            +
                #
         | 
| 536 | 
            +
                # Capitalize the first letter of each word in a string except for words that
         | 
| 537 | 
            +
                #   are 2 letters or less.
         | 
| 538 | 
            +
                #
         | 
| 539 | 
            +
                # @param [<Type>] str <description>
         | 
| 540 | 
            +
                #
         | 
| 541 | 
            +
                # @return [<Type>] <description>
         | 
| 542 | 
            +
                #
         | 
| 543 | 
            +
                def titlecase(str)
         | 
| 544 | 
            +
                  str.split.map do |s|
         | 
| 545 | 
            +
                    if s.size > 2 && s.upcase == s && !/\.&/.match?(s)
         | 
| 546 | 
            +
                      s.capitalize
         | 
| 547 | 
            +
                    else
         | 
| 548 | 
            +
                      s
         | 
| 549 | 
            +
                    end
         | 
| 550 | 
            +
                  end.join " "
         | 
| 551 | 
            +
                end
         | 
| 552 | 
            +
             | 
| 553 | 
            +
                #
         | 
| 554 | 
            +
                # Create a person name prefix from a person hash.
         | 
| 555 | 
            +
                #
         | 
| 556 | 
            +
                # @param [Hash] person The person hash.
         | 
| 557 | 
            +
                #
         | 
| 558 | 
            +
                # @return [Array<RelatonBib::LocalizedString>] The name prefix.
         | 
| 559 | 
            +
                #
         | 
| 560 | 
            +
                def nameprefix(person)
         | 
| 561 | 
            +
                  return [] unless person["prefix"]
         | 
| 562 | 
            +
             | 
| 563 | 
            +
                  [RelatonBib::LocalizedString.new(person["prefix"], "en", "Latn")]
         | 
| 564 | 
            +
                end
         | 
| 565 | 
            +
             | 
| 566 | 
            +
                #
         | 
| 567 | 
            +
                # Create a complete name from a person hash.
         | 
| 568 | 
            +
                #
         | 
| 569 | 
            +
                # @param [Hash] person The person hash.
         | 
| 570 | 
            +
                #
         | 
| 571 | 
            +
                # @return [RelatonBib::LocalizedString] The complete name.
         | 
| 572 | 
            +
                #
         | 
| 573 | 
            +
                def completename(person)
         | 
| 574 | 
            +
                  return unless person["name"]
         | 
| 575 | 
            +
             | 
| 576 | 
            +
                  RelatonBib::LocalizedString.new(person["name"], "en", "Latn")
         | 
| 577 | 
            +
                end
         | 
| 578 | 
            +
             | 
| 579 | 
            +
                #
         | 
| 580 | 
            +
                # Create a forename from a person hash.
         | 
| 581 | 
            +
                #
         | 
| 582 | 
            +
                # @param [Hash] person The person hash.
         | 
| 583 | 
            +
                #
         | 
| 584 | 
            +
                # @return [Array<RelatonBib::LocalizedString>] The forename.
         | 
| 585 | 
            +
                #
         | 
| 586 | 
            +
                def forename(person)
         | 
| 587 | 
            +
                  return [] unless person["given"]
         | 
| 588 | 
            +
             | 
| 589 | 
            +
                  fname = titlecase(person["given"])
         | 
| 590 | 
            +
                  [RelatonBib::Forename.new(content: fname, language: "en", script: "Latn")]
         | 
| 591 | 
            +
                end
         | 
| 592 | 
            +
             | 
| 593 | 
            +
                #
         | 
| 594 | 
            +
                # Create an addition from a person hash.
         | 
| 595 | 
            +
                #
         | 
| 596 | 
            +
                # @param [Hash] person The person hash.
         | 
| 597 | 
            +
                #
         | 
| 598 | 
            +
                # @return [Array<RelatonBib::LocalizedString>] The addition.
         | 
| 599 | 
            +
                #
         | 
| 600 | 
            +
                def nameaddition(person)
         | 
| 601 | 
            +
                  return [] unless person["suffix"]
         | 
| 602 | 
            +
             | 
| 603 | 
            +
                  [RelatonBib::LocalizedString.new(person["suffix"], "en", "Latn")]
         | 
| 604 | 
            +
                end
         | 
| 605 | 
            +
             | 
| 606 | 
            +
                #
         | 
| 607 | 
            +
                # Create a person identifier from a person hash.
         | 
| 608 | 
            +
                #
         | 
| 609 | 
            +
                # @param [Hash] person The person hash.
         | 
| 610 | 
            +
                #
         | 
| 611 | 
            +
                # @return [Array<RelatonBib::PersonIdentifier>] The person identifier.
         | 
| 612 | 
            +
                #
         | 
| 613 | 
            +
                def person_id(person)
         | 
| 614 | 
            +
                  return [] unless person["ORCID"]
         | 
| 615 | 
            +
             | 
| 616 | 
            +
                  [RelatonBib::PersonIdentifier.new("orcid", person["ORCID"])]
         | 
| 617 | 
            +
                end
         | 
| 618 | 
            +
             | 
| 619 | 
            +
                #
         | 
| 620 | 
            +
                # Parse a place from the source hash.
         | 
| 621 | 
            +
                #
         | 
| 622 | 
            +
                # @return [Array<RelatonBib::Place>] The place.
         | 
| 623 | 
            +
                #
         | 
| 624 | 
            +
                def parse_place # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
         | 
| 625 | 
            +
                  pub_location = @src["publisher-location"] || fetch_location
         | 
| 626 | 
            +
                  return [] unless pub_location
         | 
| 627 | 
            +
             | 
| 628 | 
            +
                  pls1, pls2 = pub_location.split(", ")
         | 
| 629 | 
            +
                  pls1 = str_cleanup pls1
         | 
| 630 | 
            +
                  pls2 &&= str_cleanup pls2
         | 
| 631 | 
            +
                  if COUNTRIES.include? pls2
         | 
| 632 | 
            +
                    country = RelatonBib::Place::RegionType.new(name: pls2)
         | 
| 633 | 
            +
                    [RelatonBib::Place.new(city: pls1, country: [country])]
         | 
| 634 | 
            +
                  elsif pls2 && pls2 == pls2&.upcase
         | 
| 635 | 
            +
                    region = RelatonBib::Place::RegionType.new(name: pls2)
         | 
| 636 | 
            +
                    [RelatonBib::Place.new(city: pls1, region: [region])]
         | 
| 637 | 
            +
                  elsif pls1 == pls2 || pls2.nil? || pls2.empty?
         | 
| 638 | 
            +
                    [RelatonBib::Place.new(city: pls1)]
         | 
| 639 | 
            +
                  else
         | 
| 640 | 
            +
                    [RelatonBib::Place.new(city: pls1), RelatonBib::Place.new(city: pls2)]
         | 
| 641 | 
            +
                  end
         | 
| 642 | 
            +
                end
         | 
| 643 | 
            +
             | 
| 644 | 
            +
                #
         | 
| 645 | 
            +
                # Fetch location from container.
         | 
| 646 | 
            +
                #
         | 
| 647 | 
            +
                # @return [String, nil] The location.
         | 
| 648 | 
            +
                #
         | 
| 649 | 
            +
                def fetch_location # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
         | 
| 650 | 
            +
                  title = @item[:title].first&.title&.content
         | 
| 651 | 
            +
                  qparts = [title, fetch_year, @src["publisher"]]
         | 
| 652 | 
            +
                  query = CGI.escape qparts.compact.join("+").gsub(" ", "+")
         | 
| 653 | 
            +
                  filter = "type:#{%w[book-chapter book-part book-section book-track].join(',type:')}"
         | 
| 654 | 
            +
                  resp = Faraday.get "https://api.crossref.org/works?query=#{query}&filter=#{filter}"
         | 
| 655 | 
            +
                  json = JSON.parse resp.body
         | 
| 656 | 
            +
                  json["message"]["items"].detect do |i|
         | 
| 657 | 
            +
                    i["publisher-location"] && i["container-title"].include?(title)
         | 
| 658 | 
            +
                  end&.dig("publisher-location")
         | 
| 659 | 
            +
                end
         | 
| 660 | 
            +
             | 
| 661 | 
            +
                #
         | 
| 662 | 
            +
                # Parse relations from the source hash.
         | 
| 663 | 
            +
                #
         | 
| 664 | 
            +
                # @return [Array<RelatonBib::DocumentRelation>] The relations.
         | 
| 665 | 
            +
                #
         | 
| 666 | 
            +
                def parse_relation # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
         | 
| 667 | 
            +
                  rels = included_in_relation
         | 
| 668 | 
            +
                  @src["relation"].each_with_object(rels) do |(k, v), a|
         | 
| 669 | 
            +
                    type, desc = relation_type k
         | 
| 670 | 
            +
                    RelatonBib.array(v).each do |r|
         | 
| 671 | 
            +
                      rel_item = Crossref.get_by_id r["id"]
         | 
| 672 | 
            +
                      title = rel_item["title"].map { |t| create_title t }
         | 
| 673 | 
            +
                      docid = RelatonBib::DocumentIdentifier.new(id: r["id"], type: "DOI")
         | 
| 674 | 
            +
                      bib = create_bibitem r["id"], title: title, docid: [docid]
         | 
| 675 | 
            +
                      a << RelatonBib::DocumentRelation.new(type: type, description: desc, bibitem: bib)
         | 
| 676 | 
            +
                    end
         | 
| 677 | 
            +
                  end
         | 
| 678 | 
            +
                end
         | 
| 679 | 
            +
             | 
| 680 | 
            +
                #
         | 
| 681 | 
            +
                # Transform crossref relation type to relaton relation type.
         | 
| 682 | 
            +
                #
         | 
| 683 | 
            +
                # @param [String] crtype The crossref relation type.
         | 
| 684 | 
            +
                #
         | 
| 685 | 
            +
                # @return [Array<String>] The relaton relation type and description.
         | 
| 686 | 
            +
                #
         | 
| 687 | 
            +
                def relation_type(crtype)
         | 
| 688 | 
            +
                  type = REALATION_TYPES[crtype] || begin
         | 
| 689 | 
            +
                    desc = RelatonBib::FormattedString.new(content: crtype)
         | 
| 690 | 
            +
                    "related"
         | 
| 691 | 
            +
                  end
         | 
| 692 | 
            +
                  [type, desc]
         | 
| 693 | 
            +
                end
         | 
| 694 | 
            +
             | 
| 695 | 
            +
                #
         | 
| 696 | 
            +
                # Create included in relation.
         | 
| 697 | 
            +
                #
         | 
| 698 | 
            +
                # @return [Array<RelatonBib::DocumentRelation>] The relations.
         | 
| 699 | 
            +
                #
         | 
| 700 | 
            +
                def included_in_relation
         | 
| 701 | 
            +
                  types = %w[
         | 
| 702 | 
            +
                    book book-chapter book-part book-section book-track dataset journal-issue
         | 
| 703 | 
            +
                    journal-value proceedings-article reference-entry report-component
         | 
| 704 | 
            +
                  ]
         | 
| 705 | 
            +
                  return [] unless @src["container-title"] && types.include?(@src["type"])
         | 
| 706 | 
            +
             | 
| 707 | 
            +
                  @src["container-title"].map do |ct|
         | 
| 708 | 
            +
                    contrib = included_in_editors(ct)
         | 
| 709 | 
            +
                    bib = RelatonBib::BibliographicItem.new(title: [content: ct], contributor: contrib)
         | 
| 710 | 
            +
                    RelatonBib::DocumentRelation.new(type: "includedIn", bibitem: bib)
         | 
| 711 | 
            +
                  end
         | 
| 712 | 
            +
                end
         | 
| 713 | 
            +
             | 
| 714 | 
            +
                #
         | 
| 715 | 
            +
                # Fetch included in editors.
         | 
| 716 | 
            +
                #
         | 
| 717 | 
            +
                # @param [String] title container-title
         | 
| 718 | 
            +
                #
         | 
| 719 | 
            +
                # @return [Array<RelatonBib::ContributionInfo>] The editors contribution info.
         | 
| 720 | 
            +
                #
         | 
| 721 | 
            +
                def included_in_editors(title)
         | 
| 722 | 
            +
                  item = fetch_included_in title
         | 
| 723 | 
            +
                  return [] unless item
         | 
| 724 | 
            +
             | 
| 725 | 
            +
                  item["editor"].map { |e| contributor(create_person(e), "editor") }
         | 
| 726 | 
            +
                end
         | 
| 727 | 
            +
             | 
| 728 | 
            +
                #
         | 
| 729 | 
            +
                # Fetch included in relation.
         | 
| 730 | 
            +
                #
         | 
| 731 | 
            +
                # @param [String] title container-title
         | 
| 732 | 
            +
                #
         | 
| 733 | 
            +
                # @return [Hash] The included in relation item.
         | 
| 734 | 
            +
                #
         | 
| 735 | 
            +
                def fetch_included_in(title)
         | 
| 736 | 
            +
                  query = CGI.escape [title, @src["publisher"], @src["publisher-location"], fetch_year].join(", ")
         | 
| 737 | 
            +
                  resp = Faraday.get %{http://api.crossref.org/works?query.bibliographic="#{query}"&rows=10&filter=type:book}
         | 
| 738 | 
            +
                  json = JSON.parse resp.body
         | 
| 739 | 
            +
                  json["message"]["items"].detect { |i| i["title"].include?(title) && i["editor"] }
         | 
| 740 | 
            +
                end
         | 
| 741 | 
            +
             | 
| 742 | 
            +
                #
         | 
| 743 | 
            +
                # Fetch year from the source hash.
         | 
| 744 | 
            +
                #
         | 
| 745 | 
            +
                # @return [String] The year.
         | 
| 746 | 
            +
                #
         | 
| 747 | 
            +
                def fetch_year
         | 
| 748 | 
            +
                  d = @src["published"] || @src["approved"] || @src["created"]
         | 
| 749 | 
            +
                  d["date-parts"][0][0]
         | 
| 750 | 
            +
                end
         | 
| 751 | 
            +
             | 
| 752 | 
            +
                #
         | 
| 753 | 
            +
                # Parse an extent from the source hash.
         | 
| 754 | 
            +
                #
         | 
| 755 | 
            +
                # @return [Array<RelatonBib::Locality>] The extent.
         | 
| 756 | 
            +
                #
         | 
| 757 | 
            +
                def parse_extent # rubocop:disable Metrics/AbcSize
         | 
| 758 | 
            +
                  extent = []
         | 
| 759 | 
            +
                  extent << RelatonBib::Locality.new("volume", @src["volume"]) if @src["volume"]
         | 
| 760 | 
            +
                  extent << RelatonBib::Locality.new("issue", @src["issue"]) if @src["issue"]
         | 
| 761 | 
            +
                  if @src["page"]
         | 
| 762 | 
            +
                    from, to = @src["page"].split("-")
         | 
| 763 | 
            +
                    extent << RelatonBib::Locality.new("page", from, to)
         | 
| 764 | 
            +
                  end
         | 
| 765 | 
            +
                  extent.any? ? [RelatonBib::LocalityStack.new(extent)] : []
         | 
| 766 | 
            +
                end
         | 
| 767 | 
            +
             | 
| 768 | 
            +
                #
         | 
| 769 | 
            +
                # Parse a series from the source hash.
         | 
| 770 | 
            +
                #
         | 
| 771 | 
            +
                # @return [Arrey<RelatonBib::Series>] The series.
         | 
| 772 | 
            +
                #
         | 
| 773 | 
            +
                def parse_series # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 774 | 
            +
                  types = %w[inbook incollection inproceedings]
         | 
| 775 | 
            +
                  return [] if !@src["container-title"] || types.include?(@item[:type]) || @src["type"] == "report-component"
         | 
| 776 | 
            +
             | 
| 777 | 
            +
                  con_ttl = if main_sub_titles.any? || project_titles.any?
         | 
| 778 | 
            +
                              @src["container-title"]
         | 
| 779 | 
            +
                            elsif @src["container-title"].size > 1
         | 
| 780 | 
            +
                              sct = @src["short-container-title"]&.last
         | 
| 781 | 
            +
                              abbrev = RelatonBib::LocalizedString.new sct if sct
         | 
| 782 | 
            +
                              @src["container-title"][-1..-1]
         | 
| 783 | 
            +
                            else []
         | 
| 784 | 
            +
                            end
         | 
| 785 | 
            +
                  con_ttl.map do |ct|
         | 
| 786 | 
            +
                    title = RelatonBib::TypedTitleString.new content: ct
         | 
| 787 | 
            +
                    RelatonBib::Series.new title: title, abbreviation: abbrev
         | 
| 788 | 
            +
                  end
         | 
| 789 | 
            +
                end
         | 
| 790 | 
            +
             | 
| 791 | 
            +
                #
         | 
| 792 | 
            +
                # Parse a medium from the source hash.
         | 
| 793 | 
            +
                #
         | 
| 794 | 
            +
                # @return [RelatonBib::Mediub, nil] The medium.
         | 
| 795 | 
            +
                #
         | 
| 796 | 
            +
                def parse_medium
         | 
| 797 | 
            +
                  genre = @src["degree"]&.first
         | 
| 798 | 
            +
                  return unless genre
         | 
| 799 | 
            +
             | 
| 800 | 
            +
                  RelatonBib::Medium.new genre: genre
         | 
| 801 | 
            +
                end
         | 
| 802 | 
            +
              end
         | 
| 803 | 
            +
            end
         | 
    
        data/lib/relaton_doi/version.rb
    CHANGED
    
    
    
        data/lib/relaton_doi.rb
    CHANGED
    
    | @@ -8,6 +8,7 @@ require "relaton_ietf" | |
| 8 8 | 
             
            require "relaton_ieee"
         | 
| 9 9 | 
             
            require "relaton_nist"
         | 
| 10 10 | 
             
            require_relative "relaton_doi/version"
         | 
| 11 | 
            +
            require_relative "relaton_doi/parser"
         | 
| 11 12 | 
             
            require_relative "relaton_doi/crossref"
         | 
| 12 13 |  | 
| 13 14 | 
             
            Serrano.configuration do |config|
         | 
| @@ -15,6 +16,4 @@ Serrano.configuration do |config| | |
| 15 16 | 
             
            end
         | 
| 16 17 |  | 
| 17 18 | 
             
            module RelatonDoi
         | 
| 18 | 
            -
              class Error < StandardError; end
         | 
| 19 | 
            -
              # Your code goes here...
         | 
| 20 19 | 
             
            end
         | 
    
        data/relaton-doi.gemspec
    CHANGED
    
    | @@ -41,7 +41,7 @@ Gem::Specification.new do |spec| | |
| 41 41 | 
             
              spec.add_development_dependency "vcr"
         | 
| 42 42 | 
             
              spec.add_development_dependency "webmock"
         | 
| 43 43 |  | 
| 44 | 
            -
              spec.add_dependency "relaton-bib", "~> 1.14. | 
| 44 | 
            +
              spec.add_dependency "relaton-bib", "~> 1.14.3"
         | 
| 45 45 | 
             
              spec.add_dependency "relaton-bipm", "~> 1.14.0"
         | 
| 46 46 | 
             
              spec.add_dependency "relaton-ieee", "~> 1.14.0"
         | 
| 47 47 | 
             
              spec.add_dependency "relaton-ietf", "~> 1.14.0"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: relaton-doi
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.14. | 
| 4 | 
            +
              version: 1.14.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Ribose Inc.
         | 
| 8 | 
            -
            autorequire:
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2023-01-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: equivalent-xml
         | 
| @@ -100,14 +100,14 @@ dependencies: | |
| 100 100 | 
             
                requirements:
         | 
| 101 101 | 
             
                - - "~>"
         | 
| 102 102 | 
             
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            -
                    version: 1.14. | 
| 103 | 
            +
                    version: 1.14.3
         | 
| 104 104 | 
             
              type: :runtime
         | 
| 105 105 | 
             
              prerelease: false
         | 
| 106 106 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 107 | 
             
                requirements:
         | 
| 108 108 | 
             
                - - "~>"
         | 
| 109 109 | 
             
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            -
                    version: 1.14. | 
| 110 | 
            +
                    version: 1.14.3
         | 
| 111 111 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 112 112 | 
             
              name: relaton-bipm
         | 
| 113 113 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -200,6 +200,7 @@ files: | |
| 200 200 | 
             
            - bin/setup
         | 
| 201 201 | 
             
            - lib/relaton_doi.rb
         | 
| 202 202 | 
             
            - lib/relaton_doi/crossref.rb
         | 
| 203 | 
            +
            - lib/relaton_doi/parser.rb
         | 
| 203 204 | 
             
            - lib/relaton_doi/processor.rb
         | 
| 204 205 | 
             
            - lib/relaton_doi/version.rb
         | 
| 205 206 | 
             
            - relaton-doi.gemspec
         | 
| @@ -208,7 +209,7 @@ licenses: | |
| 208 209 | 
             
            - BSD-2-Clause
         | 
| 209 210 | 
             
            metadata:
         | 
| 210 211 | 
             
              homepage_uri: https://github.com/relaton/relaton-doi
         | 
| 211 | 
            -
            post_install_message:
         | 
| 212 | 
            +
            post_install_message: 
         | 
| 212 213 | 
             
            rdoc_options: []
         | 
| 213 214 | 
             
            require_paths:
         | 
| 214 215 | 
             
            - lib
         | 
| @@ -223,8 +224,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 223 224 | 
             
                - !ruby/object:Gem::Version
         | 
| 224 225 | 
             
                  version: '0'
         | 
| 225 226 | 
             
            requirements: []
         | 
| 226 | 
            -
            rubygems_version: 3. | 
| 227 | 
            -
            signing_key:
         | 
| 227 | 
            +
            rubygems_version: 3.1.6
         | 
| 228 | 
            +
            signing_key: 
         | 
| 228 229 | 
             
            specification_version: 4
         | 
| 229 230 | 
             
            summary: 'RelatonDOI: retrieve Standards for bibliographic using DOI through Crossrefand
         | 
| 230 231 | 
             
              provide Relaton object.'
         |