Module: Commonmeta::CrossrefUtils

Included in:
MetadataUtils
Defined in:
lib/commonmeta/crossref_utils.rb

Instance Method Summary collapse

Instance Method Details

#crossref_errors(xml: nil) ⇒ Object



25
26
27
28
29
30
31
32
33
34
# File 'lib/commonmeta/crossref_utils.rb', line 25

def crossref_errors(xml: nil)
  filepath = File.expand_path("../../resources/crossref/crossref5.3.1.xsd", __dir__)
  File.open(filepath, "r") do |f|
    schema = Nokogiri::XML::Schema(f)
  end

  schema.validate(Nokogiri::XML(xml, nil, "UTF-8")).map(&:to_s).unwrap
rescue Nokogiri::XML::SyntaxError => e
  e.message
end

#crossref_root_attributesObject



415
416
417
418
419
420
421
422
423
# File 'lib/commonmeta/crossref_utils.rb', line 415

def crossref_root_attributes
  { 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
    'xsi:schemaLocation': "http://www.crossref.org/schema/5.3.1 https://www.crossref.org/schemas/crossref5.3.1.xsd",
    xmlns: "http://www.crossref.org/schema/5.3.1",
    'xmlns:jats': "http://www.ncbi.nlm.nih.gov/JATS1",
    'xmlns:fr': "http://www.crossref.org/fundref.xsd",
    'xmlns:mml': "http://www.w3.org/1998/Math/MathML",
    version: "5.3.1" }
end

#insert_archive_locations(xml) ⇒ Object



331
332
333
334
335
336
337
338
339
# File 'lib/commonmeta/crossref_utils.rb', line 331

def insert_archive_locations(xml)
  return xml if archive_locations.blank?

  xml.archive_locations do
    archive_locations.each do |archive_location|
      xml.archive("name" => archive_location)
    end
  end
end

#insert_citation_list(xml) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/commonmeta/crossref_utils.rb', line 167

def insert_citation_list(xml)
  xml.citation_list do
    Array.wrap(references).each do |ref|
      xml.citation("key" => ref["key"]) do
        xml.journal_article(ref["journal_title"]) if ref["journal_title"].present?
        xml.author(ref["author"]) if ref["author"].present?
        xml.volume(ref["volume"]) if ref["volume"].present?
        xml.first_page(ref["first_page"]) if ref["first_page"].present?
        xml.cYear(ref["publicationYear"]) if ref["publicationYear"].present?
        xml.article_title(ref["title"]) if ref["title"].present?
        xml.doi(doi_from_url(ref["doi"])) if ref["doi"].present?
        xml.unstructured_citation(ref["url"]) if ref["url"].present?
      end
    end
  end
end

#insert_crossref_abstract(xml) ⇒ Object



400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/commonmeta/crossref_utils.rb', line 400

def insert_crossref_abstract(xml)
  return xml if descriptions.blank?

  if descriptions.first.is_a?(Hash)
    d = descriptions.first
  else
    d = {}
    d["description"] = descriptions.first
  end

  xml.abstract("xmlns" => "http://www.ncbi.nlm.nih.gov/JATS1") do
    xml.p(d["description"])
  end
end

#insert_crossref_access_indicators(xml) ⇒ Object

xml.resourceType(types || types,

'resourceTypeGeneral' => types["resourceTypeGeneral"] || Metadata::SO_TO_DC_TRANSLATIONS[types["schemaOrg"]] || "Other")

end



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/commonmeta/crossref_utils.rb', line 199

def insert_crossref_access_indicators(xml)
  return xml if license.blank?

  rights_uri = license["url"]

  xml.program("xmlns" => "http://www.crossref.org/AccessIndicators.xsd",
              "name" => "AccessIndicators") do
    xml.license_ref(rights_uri, "applies_to" => "vor")
    xml.license_ref(rights_uri, "applies_to" => "tdm")
  end
end

#insert_crossref_anonymous(xml, contributor) ⇒ Object



146
147
148
149
150
151
152
153
# File 'lib/commonmeta/crossref_utils.rb', line 146

def insert_crossref_anonymous(xml, contributor)
  xml.affiliations do
    xml.institution do
      xml.institution_name(contributor.dig("affiliation", 0, "name")) if contributor.dig("affiliation", 0, "name").present?
      xml.institution_id(contributor.dig("affiliation", 0, "id"), "type" => "ror") if contributor.dig("affiliation", 0, "id").present?
    end
  end
end

#insert_crossref_contributors(xml) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/commonmeta/crossref_utils.rb', line 100

def insert_crossref_contributors(xml)
  return xml if contributors.blank?

  con = Array.wrap(contributors).select { |c| c["contributorRoles"] == ["Author"] || c["contributorRoles"] == ["Editor"] }

  xml.contributors do
    Array.wrap(con).each_with_index do |contributor, index|
      contributor_role = contributor["contributorRoles"] == ["Author"] ? "author" : "editor"

      if contributor["type"] == "Organization" && contributor["name"].present?
        xml.organization(contributor["name"], "contributor_role" => contributor_role,
                                              "sequence" => index.zero? ? "first" : "additional")
      elsif contributor["givenName"].present? || contributor["familyName"].present?
        xml.person_name("contributor_role" => contributor_role,
                        "sequence" => index.zero? ? "first" : "additional") do
          insert_crossref_person(xml, contributor)
        end
      elsif contributor["affiliation"].present?
        xml.anonymous("contributor_role" => contributor_role,
                      "sequence" => index.zero? ? "first" : "additional") do
          insert_crossref_anonymous(xml, contributor)
        end
      else
        xml.anonymous("contributor_role" => contributor_role,
                      "sequence" => index.zero? ? "first" : "additional")
      end
    end
  end
end

#insert_crossref_issn(xml) ⇒ Object



390
391
392
393
394
395
396
397
398
# File 'lib/commonmeta/crossref_utils.rb', line 390

def insert_crossref_issn(xml)
  issn = if container.to_h.fetch("identifierType", nil) == "ISSN"
      container.to_h.fetch("identifier", nil)
    end

  return xml if issn.blank?

  xml.issn(issn)
end

#insert_crossref_language(xml) ⇒ Object

xml.version(version_info) end



278
279
280
281
282
# File 'lib/commonmeta/crossref_utils.rb', line 278

def insert_crossref_language(xml)
  return xml unless language.present?

  xml.language(language)
end

#insert_crossref_license(xml) ⇒ Object



365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/commonmeta/crossref_utils.rb', line 365

def insert_crossref_license(xml)
  return xml unless license.present?

  xml.license do
    rights = license
    if rights.is_a?(Hash)
      r = rights
    else
      r = {}
      r["rights"] = rights
      r["rightsUri"] = normalize_id(rights)
    end

    attributes = {
      "rightsURI" => r["rightsUri"],
      "rightsIdentifier" => r["rightsIdentifier"],
      "rightsIdentifierScheme" => r["rightsIdentifierScheme"],
      "schemeURI" => r["schemeUri"],
      "xml:lang" => r["lang"],
    }.compact

    xml.rights(r["rights"], attributes)
  end
end

#insert_crossref_person(xml, contributor) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/commonmeta/crossref_utils.rb', line 130

def insert_crossref_person(xml, contributor)
  xml.given_name(contributor["givenName"]) if contributor["givenName"].present?
  xml.surname(contributor["familyName"]) if contributor["familyName"].present?
  if contributor.dig("id") && URI.parse(contributor.dig("id")).host == "orcid.org"
    xml.ORCID(contributor.dig("id"))
  end
  if contributor["affiliation"].present?
    xml.affiliations do
      xml.institution do
        xml.institution_name(contributor.dig("affiliation", 0, "name")) if contributor.dig("affiliation", 0, "name").present?
        xml.institution_id(contributor.dig("affiliation", 0, "id"), "type" => "ror") if contributor.dig("affiliation", 0, "id").present?
      end
    end
  end
end

#insert_crossref_publication_date(xml) ⇒ Object



284
285
286
287
288
289
290
291
292
293
294
# File 'lib/commonmeta/crossref_utils.rb', line 284

def insert_crossref_publication_date(xml)
  return xml if date["registered"].blank?

  date_ = get_datetime_from_iso8601(date["registered"])

  xml.publication_date("media_type" => "online") do
    xml.month(date_.month) if date_.month.present?
    xml.day(date_.day) if date_.day.present?
    xml.year(date_.year) if date_.year.present?
  end
end

#insert_crossref_relations(xml) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/commonmeta/crossref_utils.rb', line 211

def insert_crossref_relations(xml)
  return xml if related_identifiers.blank?

  xml.program("xmlns" => "http://www.crossref.org/relations.xsd",
              "name" => "relations") do
    related_identifiers.each do |related_identifier|
      xml.related_item do
        identifier_type = validate_doi(related_identifier["id"]) ? "doi" : "uri"
        id = identifier_type == "doi" ? doi_from_url(related_identifier["id"]) : related_identifier["id"]
        attributes = {
          "relationship-type" => related_identifier["type"].camelize(:lower),
          "identifier-type" => identifier_type,
        }.compact

        xml.intra_work_relation(id, attributes)
      end
    end
  end
end

#insert_crossref_subjects(xml) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/commonmeta/crossref_utils.rb', line 258

def insert_crossref_subjects(xml)
  return xml unless subjects.present?

  xml.subjects do
    subjects.each do |subject|
      if subject.is_a?(Hash)
        xml.subject(subject["subject"])
      else
        xml.subject(subject)
      end
    end
  end
end

#insert_crossref_titles(xml) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
# File 'lib/commonmeta/crossref_utils.rb', line 155

def insert_crossref_titles(xml)
  xml.titles do
    Array.wrap(titles).each do |title|
      if title.is_a?(Hash)
        xml.title(title["title"])
      else
        xml.title(title)
      end
    end
  end
end

#insert_crossref_work(xml) ⇒ Object



36
37
38
39
40
41
42
43
44
45
# File 'lib/commonmeta/crossref_utils.rb', line 36

def insert_crossref_work(xml)
  return xml if doi_from_url(id).blank?

  case type
  when "JournalArticle"
    insert_journal(xml)
  when "Article"
    insert_posted_content(xml)
  end
end

#insert_doi_data(xml) ⇒ Object



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/commonmeta/crossref_utils.rb', line 341

def insert_doi_data(xml)
  return xml if doi_from_url(id).blank? || url.blank?

  xml.doi_data do
    doi = doi_from_url(id).downcase
    xml.doi(doi)
    xml.resource(url)
    xml.collection("property" => "text-mining") do
      xml.item do
        xml.resource(url, "mime_type" => "text/html")
      end
      if is_rogue_scholar_doi?(doi)
        Array.wrap(files).each do |file|
          xml.item do
            # Crossref schema currently doesn't support text/markdown
            file["mimeType"] = "text/plain" if file["mimeType"] == "text/markdown"
            xml.resource(file["url"], "mime_type" => file["mimeType"])
          end
        end
      end
    end
  end
end

#insert_funding_references(xml) ⇒ Object

xml.dates do

  Array.wrap(dates).each do |date|
    attributes = { 'dateType' => date["dateType"] || "Issued", 'dateInformation' => date["dateInformation"] }.compact
    xml.date(date["date"], attributes)
  end
end

end



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/commonmeta/crossref_utils.rb', line 242

def insert_funding_references(xml)
  return xml if funding_references.blank?

  xml.program("xmlns" => "http://www.crossref.org/fundref.xsd",
              "name" => "fundref") do
    funding_references.each do |funding_reference|
      xml.assertion("name" => "fundgroup") do
        xml.assertion(funding_reference["funderName"], "name" => "funder_name") do
          xml.assertion(funding_reference["funderIdentifier"], "name" => "funder_identifier")
        end
        xml.assertion(funding_reference["awardNumber"], "name" => "award_number")
      end
    end
  end
end

#insert_group_title(xml) ⇒ Object



94
95
96
97
98
# File 'lib/commonmeta/crossref_utils.rb', line 94

def insert_group_title(xml)
  return xml if subjects.blank?

  xml.group_title(subjects.first["subject"])
end

#insert_institution(xml) ⇒ Object



308
309
310
311
312
313
314
# File 'lib/commonmeta/crossref_utils.rb', line 308

def insert_institution(xml)
  return xml if publisher.blank?

  xml.institution do
    xml.institution_name(publisher["name"])
  end
end

#insert_item_number(xml) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/commonmeta/crossref_utils.rb', line 316

def insert_item_number(xml)
  return xml if alternate_identifiers.blank?

  alternate_identifiers.each do |alternate_identifier|
    attributes = {
      "item_number_type" => alternate_identifier["alternateIdentifierType"] ? alternate_identifier["alternateIdentifierType"].downcase : nil,
    }.compact

    # strip hyphen from UUIDs, as item_number can only be 32 characters long (UUIDv4 is 36 characters long)
    alternate_identifier["alternateIdentifier"] = alternate_identifier["alternateIdentifier"].gsub("-", "") if alternate_identifier["alternateIdentifierType"] == "UUID"

    xml.item_number(alternate_identifier["alternateIdentifier"], attributes)
  end
end

#insert_journal(xml) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/commonmeta/crossref_utils.rb', line 47

def insert_journal(xml)
  xml.journal do
    if language.present?
      xml.("language" => language[0..1]) do
        xml.full_title(container["title"])
      end
    else
      xml. do
        xml.full_title(container["title"])
      end
    end
    xml.journal_article("publication_type" => "full_text") do
      insert_crossref_titles(xml)
      insert_crossref_contributors(xml)
      insert_crossref_publication_date(xml)
      insert_crossref_abstract(xml)
      insert_crossref_issn(xml)
      insert_item_number(xml)
      insert_funding_references(xml)
      insert_crossref_access_indicators(xml)
      insert_crossref_relations(xml)
      insert_archive_locations(xml)
      insert_doi_data(xml)
      insert_citation_list(xml)
    end
  end
end

#insert_posted_content(xml) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/commonmeta/crossref_utils.rb', line 75

def insert_posted_content(xml)
  posted_content = { "type" => "other", "language" => language ? language[0..1] : nil }.compact

  xml.posted_content(posted_content) do
    insert_group_title(xml)
    insert_crossref_contributors(xml)
    insert_crossref_titles(xml)
    insert_posted_date(xml)
    insert_institution(xml)
    insert_item_number(xml)
    insert_crossref_abstract(xml)
    insert_funding_references(xml)
    insert_crossref_access_indicators(xml)
    insert_crossref_relations(xml)
    insert_doi_data(xml)
    insert_citation_list(xml)
  end
end

#insert_posted_date(xml) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
# File 'lib/commonmeta/crossref_utils.rb', line 296

def insert_posted_date(xml)
  return xml if date["published"].blank?

  date_ = get_datetime_from_iso8601(date["published"])

  xml.posted_date do
    xml.month(date_.month) if date_.month.present?
    xml.day(date_.day) if date_.day.present?
    xml.year(date_.year) if date_.year.present?
  end
end

#write_crossref_xmlObject



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/commonmeta/crossref_utils.rb', line 5

def write_crossref_xml
  @crossref_xml ||= Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
    xml.doi_batch(crossref_root_attributes) do
      xml.head do
        # we use a uuid as batch_id
        xml.doi_batch_id(SecureRandom.uuid)
        xml.timestamp(Time.now.utc.strftime("%Y%m%d%H%M%S"))
        xml.depositor do
          xml.depositor_name(depositor)
          xml.email_address(email)
        end
        xml.registrant(registrant)
      end
      xml.body do
        insert_crossref_work(xml)
      end
    end
  end.to_xml
end