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_attributes ⇒ Object



380
381
382
383
384
385
386
387
388
# File 'lib/commonmeta/crossref_utils.rb', line 380

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_citation_list(xml) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/commonmeta/crossref_utils.rb', line 173

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["cYear"]) if ref["cYear"].present?
        xml.article_title(ref["article_title"]) if ref["article_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



365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/commonmeta/crossref_utils.rb', line 365

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 || Metadata::SO_TO_DC_TRANSLATIONS[types["schemaOrg"]] || "Other") end



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/commonmeta/crossref_utils.rb', line 205

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, creator) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/commonmeta/crossref_utils.rb', line 146

def insert_crossref_anonymous(xml, creator)
  if person["affiliation"].present?
    xml.anonymous do
      xml.affiliations do
        xml.institution do
          xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
          xml.institution_id(creator.dig("affiliation", 0, "affiliationIdentifier"), "type" => creator.dig("affiliation", 0, "affiliationIdentifierScheme")) if creator.dig("affiliation", 0, "affiliationIdentifier").present?
        end
      end
    end
  else
    xml.anonymous
  end
end

#insert_crossref_creators(xml) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/commonmeta/crossref_utils.rb', line 95

def insert_crossref_creators(xml)
  xml.contributors do
    Array.wrap(creators).each_with_index do |creator, index|
      if creator["type"] == "Organization"
        xml.organization("contributor_role" => "author",
                         "sequence" => index.zero? ? "first" : "additional") do
          insert_crossref_organization(xml, creator)
        end
      elsif creator["givenName"].present? || creator["familyName"].present?
        xml.person_name("contributor_role" => "author",
                        "sequence" => index.zero? ? "first" : "additional") do
          insert_crossref_person(xml, creator)
        end
      else
        xml.unknown("contributor_role" => "author",
                    "sequence" => index.zero? ? "first" : "additional") do
          insert_crossref_anonymous(xml, creator)
        end
      end
    end
  end
end

#insert_crossref_issn(xml) ⇒ Object



355
356
357
358
359
360
361
362
363
# File 'lib/commonmeta/crossref_utils.rb', line 355

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



263
264
265
266
267
# File 'lib/commonmeta/crossref_utils.rb', line 263

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

  xml.language(language)
end

#insert_crossref_license(xml) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/commonmeta/crossref_utils.rb', line 330

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_organization(xml, creator) ⇒ Object



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

def insert_crossref_organization(xml, creator)
  xml.name(creator["name"]) if creator["name"].present?
  if creator["affiliation"].present?
    xml.affiliations do
      xml.institution do
        xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
        xml.institution_id(creator.dig("affiliation", 0, "affiliationIdentifier"), "type" => creator.dig("affiliation", 0, "affiliationIdentifierScheme")) if creator.dig("affiliation", 0, "affiliationIdentifier").present?
      end
    end
  end
end

#insert_crossref_person(xml, creator) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/commonmeta/crossref_utils.rb', line 118

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

#insert_crossref_publication_date(xml) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
# File 'lib/commonmeta/crossref_utils.rb', line 269

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_subjects(xml) ⇒ Object

xml.fundingReferences do Array.wrap(funding_references).each do |funding_reference| xml.fundingReference do xml.funderName(funding_reference) xml.funderIdentifier(funding_reference, { "funderIdentifierType" => funding_reference }.compact) if funding_reference.present? xml.awardNumber(funding_reference, { "awardURI" => funding_reference }.compact) if funding_reference.present? || funding_reference.present? xml.awardTitle(funding_reference) if funding_reference.present? end end end end



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

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



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/commonmeta/crossref_utils.rb', line 161

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



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

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

  xml.doi_data do
    xml.doi(doi_from_url(id).downcase)
    xml.resource(url)
    xml.collection("property" => "text-mining") do
      xml.item do
        xml.resource(url, "mime_type" => "text/html")
      end
    end
  end
end

#insert_group_title(xml) ⇒ Object



89
90
91
92
93
# File 'lib/commonmeta/crossref_utils.rb', line 89

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

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

#insert_institution(xml) ⇒ Object



293
294
295
296
297
298
299
# File 'lib/commonmeta/crossref_utils.rb', line 293

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

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

#insert_item_number(xml) ⇒ Object



301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/commonmeta/crossref_utils.rb', line 301

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
# 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_creators(xml)
      insert_crossref_publication_date(xml)
      insert_crossref_abstract(xml)
      insert_crossref_issn(xml)
      insert_item_number(xml)
      insert_crossref_access_indicators(xml)
      insert_doi_data(xml)
      insert_citation_list(xml)
    end
  end
end

#insert_posted_content(xml) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/commonmeta/crossref_utils.rb', line 72

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_creators(xml)
    insert_crossref_titles(xml)
    insert_posted_date(xml)
    insert_institution(xml)
    insert_item_number(xml)
    insert_crossref_abstract(xml)
    insert_crossref_access_indicators(xml)
    insert_doi_data(xml)
    insert_citation_list(xml)
  end
end

#insert_posted_date(xml) ⇒ Object



281
282
283
284
285
286
287
288
289
290
291
# File 'lib/commonmeta/crossref_utils.rb', line 281

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_xml ⇒ Object



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