commonmeta-ruby 3.4.4 → 3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/lib/commonmeta/author_utils.rb +103 -71
  4. data/lib/commonmeta/crossref_utils.rb +31 -25
  5. data/lib/commonmeta/metadata.rb +2 -8
  6. data/lib/commonmeta/metadata_utils.rb +4 -3
  7. data/lib/commonmeta/readers/bibtex_reader.rb +3 -3
  8. data/lib/commonmeta/readers/cff_reader.rb +7 -6
  9. data/lib/commonmeta/readers/codemeta_reader.rb +3 -3
  10. data/lib/commonmeta/readers/crossref_reader.rb +3 -5
  11. data/lib/commonmeta/readers/crossref_xml_reader.rb +7 -6
  12. data/lib/commonmeta/readers/csl_reader.rb +3 -4
  13. data/lib/commonmeta/readers/datacite_reader.rb +3 -5
  14. data/lib/commonmeta/readers/json_feed_reader.rb +3 -3
  15. data/lib/commonmeta/readers/npm_reader.rb +2 -2
  16. data/lib/commonmeta/readers/ris_reader.rb +1 -1
  17. data/lib/commonmeta/readers/schema_org_reader.rb +3 -3
  18. data/lib/commonmeta/schema_utils.rb +1 -1
  19. data/lib/commonmeta/utils.rb +4 -2
  20. data/lib/commonmeta/version.rb +1 -1
  21. data/lib/commonmeta/writers/bibtex_writer.rb +1 -1
  22. data/lib/commonmeta/writers/cff_writer.rb +5 -4
  23. data/lib/commonmeta/writers/codemeta_writer.rb +4 -2
  24. data/lib/commonmeta/writers/csv_writer.rb +4 -2
  25. data/lib/commonmeta/writers/datacite_writer.rb +1 -1
  26. data/lib/commonmeta/writers/jats_writer.rb +9 -5
  27. data/lib/commonmeta/writers/ris_writer.rb +2 -1
  28. data/lib/commonmeta/writers/schema_org_writer.rb +6 -3
  29. data/resources/{commonmeta_v0.9.3.json → commonmeta_v0.10.json} +62 -46
  30. data/spec/author_utils_spec.rb +16 -16
  31. data/spec/cli_spec.rb +1 -1
  32. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_crossref_metadata/missing_contributor.yml +307 -0
  33. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_datacite_metadata/SoftwareSourceCode.yml +76 -0
  34. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item_metadata/ghost_post_with_related_identifiers_and_funding.yml +36 -36
  35. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item_metadata/ghost_post_with_related_identifiers_and_link_to_peer-reviewed_article.yml +4911 -0
  36. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/book_oup.yml +107 -0
  37. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/journal_article_plos.yml +407 -0
  38. data/spec/metadata_spec.rb +2 -2
  39. data/spec/readers/bibtex_reader_spec.rb +5 -5
  40. data/spec/readers/cff_reader_spec.rb +127 -127
  41. data/spec/readers/codemeta_reader_spec.rb +11 -11
  42. data/spec/readers/crossref_reader_spec.rb +831 -835
  43. data/spec/readers/crossref_xml_reader_spec.rb +899 -901
  44. data/spec/readers/csl_reader_spec.rb +33 -33
  45. data/spec/readers/datacite_reader_spec.rb +106 -103
  46. data/spec/readers/json_feed_reader_spec.rb +64 -38
  47. data/spec/readers/npm_reader_spec.rb +32 -33
  48. data/spec/readers/ris_reader_spec.rb +36 -36
  49. data/spec/readers/schema_org_reader_spec.rb +284 -284
  50. data/spec/writers/codemeta_writer_spec.rb +19 -20
  51. data/spec/writers/crossref_xml_writer_spec.rb +73 -37
  52. data/spec/writers/datacite_writer_spec.rb +1 -1
  53. metadata +8 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5c580fb509592f279c823dff8e3706420d08af27e2fbf34eae997ab27138b35
4
- data.tar.gz: 44ac2df8b90492d35c45d125f4b9d4c5800827a17acde4c50453af49e5b04160
3
+ metadata.gz: eff7462ec031d3c8ad11d8f684738711c0464cee5644028d8d805ff36f09058f
4
+ data.tar.gz: 8fda70b594d569ffe432f6071c85f9009d8e83434bd3f079a6ea7d312e57f3bd
5
5
  SHA512:
6
- metadata.gz: ece0ebc5a0e2028704029c9eb97318721d532b2f9eb580c9dbf5419784e10a34176925517790c0bd6819e7f9d5dfd807f8f97afb9ec5722f4255c0c4065724e1
7
- data.tar.gz: a12d1a02e8ce66d1f3c6ed4ca7672b9dbf54413f1e3c336fed6a7fa6f3f53a73e2bdf937f8d4d32faf2c4e40fd41357b043813adde42c5387bb528d1fb6c86fa
6
+ metadata.gz: 69b5c2c59cb28d2d4615ec21c10f760efa504aa1c33fda841327900f61367a71e1a51accce0a5c643fd2b4bd066ebaf34e67a616de01a147bc1140f3fed8f442
7
+ data.tar.gz: 005a9c7e7b1fee2e452d6683e9952cbf778817cb19c0a62fa1b83f805e22ba435e1f64bfcfb1e217aec2c1112ea83cb9321f3f1a743d478161fe1a100f65a9c8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- commonmeta-ruby (3.4.4)
4
+ commonmeta-ruby (3.5)
5
5
  activesupport (>= 4.2.5, < 8.0)
6
6
  addressable (~> 2.8.1, < 2.8.2)
7
7
  base32-url (>= 0.7.0, < 1)
@@ -202,7 +202,7 @@ GEM
202
202
  rubocop-ast (>= 0.4.0)
203
203
  rubocop-rake (0.6.0)
204
204
  rubocop (~> 1.0)
205
- rubocop-rspec (2.23.2)
205
+ rubocop-rspec (2.24.0)
206
206
  rubocop (~> 1.33)
207
207
  rubocop-capybara (~> 2.17)
208
208
  rubocop-factory_bot (~> 2.22)
@@ -1,77 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'namae'
3
+ require "namae"
4
4
 
5
5
  module Commonmeta
6
6
  module AuthorUtils
7
+ # mapping of DataCite contributorType to commonmeta contributorRoles
8
+ def datacite_contributor_roles = {
9
+ "ContactPerson" => "ContactPerson",
10
+ "DataCurator" => "DataCuration",
11
+ "DataManager" => "Other",
12
+ "Distributor" => "Other",
13
+ "Editor" => "Editor",
14
+ "HostingInstitution" => "Other",
15
+ "Other" => "Other",
16
+ "Producer" => "Other",
17
+ "ProjectLeader" => "Other",
18
+ "ProjectManager" => "Other",
19
+ "ProjectMember" => "Other",
20
+ "RegistrationAgency" => "Other",
21
+ "RegistrationAuthority" => "Other",
22
+ "RelatedPerson" => "Other",
23
+ "ResearchGroup" => "Other",
24
+ "RightsHolder" => "Other",
25
+ "Researcher" => "Other",
26
+ "Sponsor" => "Other",
27
+ "Supervisor" => "Supervision",
28
+ "WorkPackageLeader" => "Other"
29
+ }
30
+
7
31
  def get_one_author(author)
8
32
  # basic sanity checks
9
33
  return nil if author.blank?
10
34
 
11
35
  # author is a string
12
- author = { 'name' => author } if author.is_a?(String)
36
+ author = { "name" => author } if author.is_a?(String)
13
37
 
14
38
  # malformed XML
15
- return nil if author.fetch('name', nil).is_a?(Array)
39
+ return nil if author.fetch("name", nil).is_a?(Array)
16
40
 
17
41
  # parse author name attributes
18
- name = parse_attributes(author.fetch('name', nil)) ||
19
- parse_attributes(author.fetch('creatorName', nil)) ||
20
- parse_attributes(author.fetch('contributorName', nil))
42
+ name = parse_attributes(author.fetch("name", nil)) ||
43
+ parse_attributes(author.fetch("creatorName", nil)) ||
44
+ parse_attributes(author.fetch("contributorName", nil))
21
45
 
22
- given_name = parse_attributes(author.fetch('givenName', nil)) ||
23
- parse_attributes(author.fetch('given', nil))
24
- family_name = parse_attributes(author.fetch('familyName', nil)) ||
25
- parse_attributes(author.fetch('family', nil))
46
+ given_name = parse_attributes(author.fetch("givenName", nil)) ||
47
+ parse_attributes(author.fetch("given", nil))
48
+ family_name = parse_attributes(author.fetch("familyName", nil)) ||
49
+ parse_attributes(author.fetch("family", nil))
26
50
 
27
51
  name = cleanup_author(name)
28
52
 
29
53
  # parse author identifier
30
- id = parse_attributes(author.fetch('id', nil), first: true) ||
31
- parse_attributes(author.fetch('identifier', nil), first: true) ||
32
- parse_attributes(author.fetch('sameAs', nil), first: true)
54
+ id = parse_attributes(author.fetch("id", nil), first: true) ||
55
+ parse_attributes(author.fetch("identifier", nil), first: true) ||
56
+ parse_attributes(author.fetch("sameAs", nil), first: true)
33
57
 
34
58
  # DataCite metadata
35
- if id.nil? && author['nameIdentifiers'].present?
36
- id = Array.wrap(author.dig('nameIdentifiers')).find do |ni|
37
- ni['nameIdentifierScheme'] == 'ORCID'
59
+ if id.nil? && author["nameIdentifiers"].present?
60
+ id = Array.wrap(author.dig("nameIdentifiers")).find do |ni|
61
+ ni["nameIdentifierScheme"] == "ORCID"
38
62
  end
39
- id = id['nameIdentifier'] if id.present?
40
- # Crossref metadata
41
- elsif id.nil? && author['ORCID'].present?
42
- id = author.fetch('ORCID')
63
+ id = id["nameIdentifier"] if id.present?
64
+ # Crossref metadata
65
+ elsif id.nil? && author["ORCID"].present?
66
+ id = author.fetch("ORCID")
43
67
  end
44
68
  id = normalize_orcid(id) || normalize_ror(id)
45
69
 
46
70
  # parse author type, i.e. "Person", "Organization" or not specified
47
- type = author.fetch('type', nil)
71
+ type = author.fetch("type", nil)
48
72
  type = type.first if type.is_a?(Array)
49
73
  # DataCite metadata
50
- type = type[0..-3] if type.is_a?(String) && type.end_with?('al')
74
+ type = type[0..-3] if type.is_a?(String) && type.end_with?("al")
51
75
 
52
- if type.blank? && name.blank? && id.is_a?(String) && URI.parse(id).host == 'ror.org'
53
- type = 'Person'
54
- author['affiliation'] = { 'affiliationIdentifier' => id }
76
+ if type.blank? && name.blank? && id.is_a?(String) && URI.parse(id).host == "ror.org"
77
+ type = "Person"
78
+ author["affiliation"] = { "affiliationIdentifier" => id }
55
79
  id = nil
56
- elsif type.blank? && id.is_a?(String) && URI.parse(id).host == 'ror.org'
57
- type = 'Organization'
58
- elsif type.blank? && author['type'] == 'Organization'
59
- type = 'Organization'
60
- elsif type.blank? && id.is_a?(String) && URI.parse(id).host == 'orcid.org'
61
- type = 'Person'
80
+ elsif type.blank? && id.is_a?(String) && URI.parse(id).host == "ror.org"
81
+ type = "Organization"
82
+ elsif type.blank? && author["type"] == "Organization"
83
+ type = "Organization"
84
+ elsif type.blank? && id.is_a?(String) && URI.parse(id).host == "orcid.org"
85
+ type = "Person"
62
86
  elsif type.blank? && (given_name.present? || family_name.present?)
63
- type = 'Person'
64
- elsif type.blank? && is_personal_name?(name: name) && name.to_s.exclude?(';')
65
- type = 'Person'
87
+ type = "Person"
88
+ elsif type.blank? && is_personal_name?(name: name) && name.to_s.exclude?(";")
89
+ type = "Person"
66
90
  elsif type.blank? && name.present? && !is_personal_name?(name: name)
67
- type = 'Organization'
91
+ type = "Organization"
68
92
  end
69
93
 
70
94
  # parse author contributor role
71
- contributor_type = parse_attributes(author.fetch('contributorType', nil))
95
+ contributor_roles = parse_attributes(author.fetch("contributorType", nil))
96
+ if contributor_roles
97
+ contributor_roles = [datacite_contributor_roles[contributor_roles]]
98
+ else
99
+ contributor_roles = ["Author"]
100
+ end
72
101
 
73
102
  # split name for type Person into given/family name if not already provided
74
- if type == 'Person' && name.present? && given_name.blank? && family_name.blank?
103
+ if type == "Person" && name.present? && given_name.blank? && family_name.blank?
75
104
  Namae.options[:include_particle_in_family] = true
76
105
  names = Namae.parse(name)
77
106
  parsed_name = names.first
@@ -87,53 +116,56 @@ module Commonmeta
87
116
 
88
117
  # return author in commonmeta format, using name vs. given/family name
89
118
  # depending on type
90
- { 'id' => id,
91
- 'type' => type,
92
- 'name' => type == 'Person' ? nil : name,
93
- 'givenName' => type == 'Organization' ? nil : given_name,
94
- 'familyName' => type == 'Organization' ? nil : family_name,
95
- 'affiliation' => get_affiliations(author.fetch('affiliation', nil)),
96
- 'contributorType' => contributor_type }.compact
119
+ { "id" => id,
120
+ "type" => type,
121
+ "name" => type == "Person" ? nil : name,
122
+ "contributorRoles" => contributor_roles,
123
+ "givenName" => type == "Organization" ? nil : given_name,
124
+ "familyName" => type == "Organization" ? nil : family_name,
125
+ "affiliation" => get_affiliations(author.fetch("affiliation", nil)) }.compact
97
126
  end
98
127
 
99
128
  def cleanup_author(author)
100
129
  return nil unless author.present?
101
130
 
102
131
  # detect pattern "Smith J.", but not "Smith, John K."
103
- unless author.include?(',')
132
+ unless author.include?(",")
104
133
  author = author.gsub(/[[:space:]]([A-Z]\.)?(-?[A-Z]\.)$/, ', \1\2')
105
134
  end
106
135
 
107
136
  # strip suffixes, e.g. "John Smith, MD" as the named parser doesn't handle them
108
- author = author.split(',').first if %w[MD PhD].include? author.split(', ').last
137
+ author = author.split(",").first if %w[MD PhD].include? author.split(", ").last
109
138
 
110
139
  # remove email addresses
111
140
  email = validate_email(author)
112
- author = author.gsub(email, '') if email.present?
141
+ author = author.gsub(email, "") if email.present?
113
142
 
114
143
  # strip spaces at the beginning and end of string
115
144
  author = author.strip
116
145
 
117
146
  # remove parentheses around names
118
- author = author[1..-2] if author[0] == '(' && author[-1] == ')'
147
+ author = author[1..-2] if author[0] == "(" && author[-1] == ")"
119
148
 
120
149
  # remove spaces around hyphens
121
- author = author.gsub(' - ', '-')
150
+ author = author.gsub(" - ", "-")
122
151
 
123
152
  # remove non-standard space characters
124
- author.gsub(/[[:space:]]/, ' ')
153
+ author.gsub(/[[:space:]]/, " ")
125
154
  end
126
155
 
127
156
  # check if given name is in the database of known given names:
128
157
  # https://github.com/bmuller/gender_detector
129
158
  def is_personal_name?(name: nil)
130
- return true if name_exists?(name.to_s.split.first) || name_exists?(name.to_s.split(', ').last)
159
+ # personal names are not allowed to contain semicolons
160
+ return false if name.to_s.include?(";")
161
+
162
+ return true if name_exists?(name.to_s.split.first) || name_exists?(name.to_s.split(", ").last)
131
163
 
132
164
  # check if a name has only one word, e.g. "FamousOrganization", not including commas
133
- return false if name.to_s.split(' ').size == 1 && name.to_s.exclude?(',')
165
+ return false if name.to_s.split(" ").size == 1 && name.to_s.exclude?(",")
134
166
 
135
167
  # check for suffixes, e.g. "John Smith, MD"
136
- return true if %w[MD PhD].include? name.split(', ').last
168
+ return true if %w[MD PhD].include? name.split(", ").last
137
169
 
138
170
  # check of name can be parsed into given/family name
139
171
  Namae.options[:include_particle_in_family] = true
@@ -141,7 +173,7 @@ module Commonmeta
141
173
 
142
174
  parsed_name = names.first
143
175
  return true if parsed_name && parsed_name.given
144
-
176
+
145
177
  false
146
178
  end
147
179
 
@@ -159,14 +191,14 @@ module Commonmeta
159
191
 
160
192
  def authors_as_string(authors)
161
193
  Array.wrap(authors).map do |a|
162
- if a['familyName'].present?
163
- [a['familyName'], a['givenName']].join(', ')
164
- elsif a['type'] == 'Person'
165
- a['name']
166
- elsif a['name'].present?
167
- "{#{a['name']}}"
194
+ if a["familyName"].present?
195
+ [a["familyName"], a["givenName"]].join(", ")
196
+ elsif a["type"] == "Person"
197
+ a["name"]
198
+ elsif a["name"].present?
199
+ "{#{a["name"]}}"
168
200
  end
169
- end.join(' and ').presence
201
+ end.join(" and ").presence
170
202
  end
171
203
 
172
204
  def get_affiliations(affiliations)
@@ -177,17 +209,17 @@ module Commonmeta
177
209
  if a.is_a?(String)
178
210
  name = a.squish
179
211
  elsif a.is_a?(Hash)
180
- if a['affiliationIdentifier'].present?
181
- affiliation_identifier = a['affiliationIdentifier']
182
- if a['schemeURI'].present?
183
- schemeURI = a['schemeURI'].end_with?('/') ? a['schemeURI'] : "#{a['schemeURI']}/"
212
+ if a["affiliationIdentifier"].present?
213
+ affiliation_identifier = a["affiliationIdentifier"]
214
+ if a["schemeURI"].present?
215
+ schemeURI = a["schemeURI"].end_with?("/") ? a["schemeURI"] : "#{a["schemeURI"]}/"
184
216
  end
185
- affiliation_identifier = !affiliation_identifier.to_s.start_with?('https://') && schemeURI.present? ? normalize_id(schemeURI + affiliation_identifier) : normalize_id(affiliation_identifier)
217
+ affiliation_identifier = !affiliation_identifier.to_s.start_with?("https://") && schemeURI.present? ? normalize_id(schemeURI + affiliation_identifier) : normalize_id(affiliation_identifier)
186
218
  end
187
- name = (a['name'] || a['__content__']).to_s.squish.presence
219
+ name = (a["name"] || a["__content__"]).to_s.squish.presence
188
220
  end
189
221
 
190
- { 'id' => affiliation_identifier, 'name' => name }.compact.presence
222
+ { "id" => affiliation_identifier, "name" => name }.compact.presence
191
223
  end.compact.presence
192
224
  end
193
225
 
@@ -195,9 +227,9 @@ module Commonmeta
195
227
  return nil unless id.present?
196
228
 
197
229
  Array.wrap(id).map do |i|
198
- { 'nameIdentifier' => i,
199
- 'nameIdentifierScheme' => 'ORCID',
200
- 'schemeUri' => 'https://orcid.org' }.compact
230
+ { "nameIdentifier" => i,
231
+ "nameIdentifierScheme" => "ORCID",
232
+ "schemeUri" => "https://orcid.org" }.compact
201
233
  end.compact.presence
202
234
  end
203
235
  end
@@ -57,7 +57,7 @@ module Commonmeta
57
57
  end
58
58
  xml.journal_article("publication_type" => "full_text") do
59
59
  insert_crossref_titles(xml)
60
- insert_crossref_creators(xml)
60
+ insert_crossref_contributors(xml)
61
61
  insert_crossref_publication_date(xml)
62
62
  insert_crossref_abstract(xml)
63
63
  insert_crossref_issn(xml)
@@ -76,7 +76,7 @@ module Commonmeta
76
76
 
77
77
  xml.posted_content(posted_content) do
78
78
  insert_group_title(xml)
79
- insert_crossref_creators(xml)
79
+ insert_crossref_contributors(xml)
80
80
  insert_crossref_titles(xml)
81
81
  insert_posted_date(xml)
82
82
  insert_institution(xml)
@@ -96,51 +96,57 @@ module Commonmeta
96
96
  xml.group_title(subjects.first["subject"])
97
97
  end
98
98
 
99
- def insert_crossref_creators(xml)
99
+ def insert_crossref_contributors(xml)
100
+ return xml if contributors.blank?
101
+
102
+ con = Array.wrap(contributors).select { |c| c["contributorRoles"] == ["Author"] || c["contributorRoles"] == ["Editor"] }
103
+
100
104
  xml.contributors do
101
- Array.wrap(creators).each_with_index do |creator, index|
102
- if creator["type"] == "Organization" && creator["name"].present?
103
- xml.organization(creator["name"], "contributor_role" => "author",
104
- "sequence" => index.zero? ? "first" : "additional")
105
- elsif creator["givenName"].present? || creator["familyName"].present?
106
- xml.person_name("contributor_role" => "author",
105
+ Array.wrap(con).each_with_index do |contributor, index|
106
+ contributor_role = contributor["contributorRoles"] == ["Author"] ? "author" : "editor"
107
+
108
+ if contributor["type"] == "Organization" && contributor["name"].present?
109
+ xml.organization(contributor["name"], "contributor_role" => contributor_role,
110
+ "sequence" => index.zero? ? "first" : "additional")
111
+ elsif contributor["givenName"].present? || contributor["familyName"].present?
112
+ xml.person_name("contributor_role" => contributor_role,
107
113
  "sequence" => index.zero? ? "first" : "additional") do
108
- insert_crossref_person(xml, creator)
114
+ insert_crossref_person(xml, contributor)
109
115
  end
110
- elsif creator["affiliation"].present?
111
- xml.anonymous("contributor_role" => "author",
116
+ elsif contributor["affiliation"].present?
117
+ xml.anonymous("contributor_role" => contributor_role,
112
118
  "sequence" => index.zero? ? "first" : "additional") do
113
- insert_crossref_anonymous(xml, creator)
119
+ insert_crossref_anonymous(xml, contributor)
114
120
  end
115
121
  else
116
- xml.anonymous("contributor_role" => "author",
122
+ xml.anonymous("contributor_role" => contributor_role,
117
123
  "sequence" => index.zero? ? "first" : "additional")
118
124
  end
119
125
  end
120
126
  end
121
127
  end
122
128
 
123
- def insert_crossref_person(xml, creator)
124
- xml.given_name(creator["givenName"]) if creator["givenName"].present?
125
- xml.surname(creator["familyName"]) if creator["familyName"].present?
126
- if creator.dig("id") && URI.parse(creator.dig("id")).host == "orcid.org"
127
- xml.ORCID(creator.dig("id"))
129
+ def insert_crossref_person(xml, contributor)
130
+ xml.given_name(contributor["givenName"]) if contributor["givenName"].present?
131
+ xml.surname(contributor["familyName"]) if contributor["familyName"].present?
132
+ if contributor.dig("id") && URI.parse(contributor.dig("id")).host == "orcid.org"
133
+ xml.ORCID(contributor.dig("id"))
128
134
  end
129
- if creator["affiliation"].present?
135
+ if contributor["affiliation"].present?
130
136
  xml.affiliations do
131
137
  xml.institution do
132
- xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
133
- xml.institution_id(creator.dig("affiliation", 0, "id"), "type" => "ror") if creator.dig("affiliation", 0, "id").present?
138
+ xml.institution_name(contributor.dig("affiliation", 0, "name")) if contributor.dig("affiliation", 0, "name").present?
139
+ xml.institution_id(contributor.dig("affiliation", 0, "id"), "type" => "ror") if contributor.dig("affiliation", 0, "id").present?
134
140
  end
135
141
  end
136
142
  end
137
143
  end
138
144
 
139
- def insert_crossref_anonymous(xml, creator)
145
+ def insert_crossref_anonymous(xml, contributor)
140
146
  xml.affiliations do
141
147
  xml.institution do
142
- xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
143
- xml.institution_id(creator.dig("affiliation", 0, "id"), "type" => "ror") if creator.dig("affiliation", 0, "id").present?
148
+ xml.institution_name(contributor.dig("affiliation", 0, "name")) if contributor.dig("affiliation", 0, "name").present?
149
+ xml.institution_id(contributor.dig("affiliation", 0, "id"), "type" => "ror") if contributor.dig("affiliation", 0, "id").present?
144
150
  end
145
151
  end
146
152
  end
@@ -9,8 +9,8 @@ module Commonmeta
9
9
  attr_accessor :string, :from, :sandbox, :meta, :regenerate, :issue, :show_errors, :depositor,
10
10
  :email, :registrant
11
11
  attr_reader :doc, :page_start, :page_end
12
- attr_writer :id, :provider_id, :client_id, :doi, :alternate_identifiers, :creators, :contributors,
13
- :titles, :publisher, :license, :date, :volume, :url, :version, :subjects, :contributor, :descriptions, :language, :sizes, :formats, :schema_version, :meta, :container, :provider, :format, :funding_references, :state, :geo_locations, :type, :additional_type, :content_url, :references, :related_identifiers, :related_items, :style, :locale
12
+ attr_writer :id, :provider_id, :client_id, :doi, :alternate_identifiers, :contributors,
13
+ :titles, :publisher, :license, :date, :volume, :url, :version, :subjects, :descriptions, :language, :sizes, :formats, :schema_version, :meta, :container, :provider, :format, :funding_references, :state, :geo_locations, :type, :additional_type, :content_url, :references, :related_identifiers, :related_items, :style, :locale
14
14
 
15
15
  def initialize(options = {})
16
16
  options.symbolize_keys!
@@ -60,7 +60,6 @@ module Commonmeta
60
60
  'provider_id' => options[:provider_id],
61
61
  'client_id' => options[:client_id],
62
62
  'content_url' => options[:content_url],
63
- 'creators' => options[:creators],
64
63
  'contributors' => options[:contributors],
65
64
  'titles' => options[:titles],
66
65
  'publisher' => options[:publisher]
@@ -96,7 +95,6 @@ module Commonmeta
96
95
 
97
96
  # set attributes directly
98
97
  read_options = options.slice(
99
- :creators,
100
98
  :contributors,
101
99
  :titles,
102
100
  :type,
@@ -252,10 +250,6 @@ module Commonmeta
252
250
  @titles ||= meta.fetch('titles', nil)
253
251
  end
254
252
 
255
- def creators
256
- @creators ||= meta.fetch('creators', nil)
257
- end
258
-
259
253
  def contributors
260
254
  @contributors ||= meta.fetch('contributors', nil)
261
255
  end
@@ -132,14 +132,16 @@ module Commonmeta
132
132
  end
133
133
 
134
134
  def csl_hsh
135
+ authors = Array.wrap(contributors).select { |c| c['contributorRoles'] == ['Author'] }
136
+
135
137
  page = if container.to_h['firstPage'].present?
136
138
  [container['firstPage'], container['lastPage']].compact.join('-')
137
139
  end
138
- author = if Array.wrap(creators).size == 1 && Array.wrap(creators).first.fetch('name',
140
+ author = if Array.wrap(authors).size == 1 && Array.wrap(authors).first.fetch('name',
139
141
  nil) == ':(unav)'
140
142
  nil
141
143
  else
142
- to_csl(creators)
144
+ to_csl(authors)
143
145
  end
144
146
 
145
147
  type_ = if type == 'Software' && version.present?
@@ -158,7 +160,6 @@ module Commonmeta
158
160
  'categories' => categories,
159
161
  'language' => language,
160
162
  'author' => author,
161
- 'contributor' => to_csl(contributors),
162
163
  'issued' => get_date_parts(date['published']),
163
164
  'submitted' => date['submitted'] ? get_date_parts(date['submitted']) : nil,
164
165
  'abstract' => parse_attributes(descriptions, content: 'description', first: true),
@@ -14,8 +14,8 @@ module Commonmeta
14
14
 
15
15
  doi = meta.try(:doi).to_s.presence || options[:doi]
16
16
 
17
- creators = Array(meta.try(:author)).map do |a|
18
- { 'type' => 'Person', 'givenName' => a.first, 'familyName' => a.last }.compact
17
+ contributors = Array(meta.try(:author)).map do |a|
18
+ { 'type' => 'Person', 'contributorRoles' => ['Author'], 'givenName' => a.first, 'familyName' => a.last }.compact
19
19
  end
20
20
 
21
21
  container = if meta.try(:journal).present?
@@ -43,7 +43,7 @@ module Commonmeta
43
43
  'type' => type,
44
44
  'url' => meta.try(:url).to_s.presence,
45
45
  'titles' => meta.try(:title).present? ? [{ 'title' => meta.try(:title).to_s }] : [],
46
- 'creators' => creators,
46
+ 'contributors' => contributors,
47
47
  'container' => container,
48
48
  'publisher' => meta.try(:publisher).to_s ? { 'name' => meta.publisher.to_s } : nil,
49
49
  'date' => date,
@@ -34,7 +34,7 @@ module Commonmeta
34
34
  i['type'] == 'doi'
35
35
  end.to_h.fetch('value', nil))
36
36
  url = normalize_id(meta.fetch('repository-code', nil))
37
- creators = cff_creators(Array.wrap(meta.fetch('authors', nil)))
37
+ contributors = cff_contributors(Array.wrap(meta.fetch('authors', nil)))
38
38
 
39
39
  date = {}
40
40
  if meta.fetch('date-released', nil).present?
@@ -66,7 +66,7 @@ module Commonmeta
66
66
  'identifiers' => identifiers,
67
67
  'url' => url,
68
68
  'titles' => titles,
69
- 'creators' => creators,
69
+ 'contributors' => contributors,
70
70
  'publisher' => publisher,
71
71
  'references' => references,
72
72
  'date' => date,
@@ -80,8 +80,8 @@ module Commonmeta
80
80
  'state' => state }.compact.merge(read_options)
81
81
  end
82
82
 
83
- def cff_creators(creators)
84
- Array.wrap(creators).map do |a|
83
+ def cff_contributors(contributors)
84
+ Array.wrap(contributors).map do |a|
85
85
  id = normalize_orcid(parse_attributes(a['orcid']))
86
86
  if a['given-names'].present? || a['family-names'].present? || id.present?
87
87
  given_name = parse_attributes(a['given-names'])
@@ -101,12 +101,14 @@ module Commonmeta
101
101
  end.compact
102
102
 
103
103
  { 'type' => 'Person',
104
+ 'contributorRoles' => ['Author'],
104
105
  'id' => id,
105
106
  'givenName' => given_name,
106
107
  'familyName' => family_name,
107
108
  'affiliation' => affiliation.presence }.compact
108
109
  else
109
110
  { 'type' => 'Organization',
111
+ 'contributorRoles' => ['Author'],
110
112
  'name' => a['name'] || a['__content__'] }
111
113
  end
112
114
  end
@@ -127,7 +129,7 @@ module Commonmeta
127
129
  'key' => doi || url,
128
130
  'doi' => doi,
129
131
  'url' => url,
130
- 'creator' => reference.dig('author'),
132
+ 'contributor' => reference.dig('author'),
131
133
  'title' => reference.dig('article-title'),
132
134
  'publisher' => reference.dig('publisher'),
133
135
  'publicationYear' => date['published'] ? date['published'][0..3] : nil,
@@ -137,7 +139,6 @@ module Commonmeta
137
139
  'lastPage' => reference.dig('last-page'),
138
140
  'containerTitle' => reference.dig('journal-title'),
139
141
  'edition' => nil,
140
- 'contributor' => nil,
141
142
  'unstructured' => doi.nil? ? reference.dig('unstructured') : nil
142
143
  }.compact
143
144
  end
@@ -34,8 +34,9 @@ module Commonmeta
34
34
 
35
35
  has_agents = meta.fetch('agents', nil)
36
36
  authors = has_agents.nil? ? meta.fetch('authors', nil) : has_agents
37
- creators = get_authors(from_schema_org(Array.wrap(authors)))
38
- contributors = get_authors(from_schema_org(Array.wrap(meta.fetch('editor', nil))))
37
+ contributors = get_authors(from_schema_org(Array.wrap(authors)))
38
+ contributors += get_authors(from_schema_org(Array.wrap(meta.fetch('editor', nil))))
39
+
39
40
  # strip milliseconds from iso8601, as edtf library doesn't handle them
40
41
  date = {}
41
42
  if Date.edtf(strip_milliseconds(meta.fetch('datePublished', nil))).present?
@@ -71,7 +72,6 @@ module Commonmeta
71
72
  'type' => type,
72
73
  'url' => normalize_id(meta.fetch('codeRepository', nil)),
73
74
  'titles' => titles,
74
- 'creators' => creators,
75
75
  'contributors' => contributors,
76
76
  'publisher' => publisher,
77
77
  'date' => date,
@@ -38,13 +38,13 @@ module Commonmeta
38
38
  meta.fetch('publisher', nil)
39
39
  end
40
40
 
41
- creators = if meta.fetch('author', nil).present?
41
+ contributors = if meta.fetch('author', nil).present?
42
42
  get_authors(from_csl(Array.wrap(meta.fetch('author', nil))))
43
43
  else
44
44
  []
45
45
  end
46
46
  editors = Array.wrap(meta.fetch('editor', nil)).each { |e| e['contributorType'] = 'Editor' }
47
- contributors = get_authors(from_csl(editors))
47
+ contributors += get_authors(from_csl(editors))
48
48
 
49
49
  date = {}
50
50
  date['submitted'] = nil
@@ -138,7 +138,6 @@ module Commonmeta
138
138
  'type' => type,
139
139
  'url' => normalize_id(meta.dig('resource', 'primary', 'URL')),
140
140
  'titles' => [{ 'title' => title }],
141
- 'creators' => creators,
142
141
  'contributors' => contributors,
143
142
  'container' => container,
144
143
  'publisher' => publisher,
@@ -167,7 +166,7 @@ module Commonmeta
167
166
  {
168
167
  'key' => reference.dig('key'),
169
168
  'doi' => doi ? normalize_doi(doi) : nil,
170
- 'creator' => reference.dig('author'),
169
+ 'contributor' => reference.dig('author'),
171
170
  'title' => reference.dig('article-title'),
172
171
  'publisher' => reference.dig('publisher'),
173
172
  'publicationYear' => reference.dig('year'),
@@ -177,7 +176,6 @@ module Commonmeta
177
176
  'lastPage' => reference.dig('last-page'),
178
177
  'containerTitle' => reference.dig('journal-title'),
179
178
  'edition' => nil,
180
- 'contributor' => nil,
181
179
  'unstructured' => doi.nil? ? reference.dig('unstructured') : nil
182
180
  }.compact
183
181
  end