commonmeta-ruby 3.4.5 → 3.5.1

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +8 -8
  3. data/csl-data.json +538 -0
  4. data/lib/commonmeta/author_utils.rb +103 -71
  5. data/lib/commonmeta/crossref_utils.rb +31 -25
  6. data/lib/commonmeta/metadata.rb +8 -14
  7. data/lib/commonmeta/metadata_utils.rb +4 -3
  8. data/lib/commonmeta/readers/bibtex_reader.rb +3 -3
  9. data/lib/commonmeta/readers/cff_reader.rb +7 -6
  10. data/lib/commonmeta/readers/codemeta_reader.rb +3 -3
  11. data/lib/commonmeta/readers/crossref_reader.rb +131 -124
  12. data/lib/commonmeta/readers/crossref_xml_reader.rb +7 -6
  13. data/lib/commonmeta/readers/csl_reader.rb +3 -4
  14. data/lib/commonmeta/readers/datacite_reader.rb +5 -5
  15. data/lib/commonmeta/readers/json_feed_reader.rb +8 -4
  16. data/lib/commonmeta/readers/npm_reader.rb +2 -2
  17. data/lib/commonmeta/readers/ris_reader.rb +1 -1
  18. data/lib/commonmeta/readers/schema_org_reader.rb +6 -4
  19. data/lib/commonmeta/schema_utils.rb +1 -1
  20. data/lib/commonmeta/utils.rb +4 -2
  21. data/lib/commonmeta/version.rb +1 -1
  22. data/lib/commonmeta/writers/bibtex_writer.rb +1 -1
  23. data/lib/commonmeta/writers/cff_writer.rb +5 -4
  24. data/lib/commonmeta/writers/codemeta_writer.rb +4 -2
  25. data/lib/commonmeta/writers/csv_writer.rb +4 -2
  26. data/lib/commonmeta/writers/datacite_writer.rb +1 -1
  27. data/lib/commonmeta/writers/jats_writer.rb +9 -5
  28. data/lib/commonmeta/writers/ris_writer.rb +2 -1
  29. data/lib/commonmeta/writers/schema_org_writer.rb +7 -4
  30. data/resources/{commonmeta_v0.9.3.json → commonmeta_v0.10.3.json} +138 -55
  31. data/resources/csl-citation.json +99 -0
  32. data/spec/author_utils_spec.rb +16 -16
  33. data/spec/cli_spec.rb +1 -1
  34. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_crossref_metadata/missing_contributor.yml +307 -0
  35. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_datacite_metadata/SoftwareSourceCode.yml +76 -0
  36. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item_metadata/archived_wordpress_post.yml +119 -0
  37. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/book_oup.yml +107 -0
  38. data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/journal_article_plos.yml +407 -0
  39. data/spec/metadata_spec.rb +2 -2
  40. data/spec/readers/bibtex_reader_spec.rb +5 -5
  41. data/spec/readers/cff_reader_spec.rb +127 -127
  42. data/spec/readers/codemeta_reader_spec.rb +11 -11
  43. data/spec/readers/crossref_reader_spec.rb +844 -835
  44. data/spec/readers/crossref_xml_reader_spec.rb +899 -901
  45. data/spec/readers/csl_reader_spec.rb +33 -33
  46. data/spec/readers/datacite_reader_spec.rb +106 -103
  47. data/spec/readers/json_feed_reader_spec.rb +68 -40
  48. data/spec/readers/npm_reader_spec.rb +32 -33
  49. data/spec/readers/ris_reader_spec.rb +36 -36
  50. data/spec/readers/schema_org_reader_spec.rb +289 -288
  51. data/spec/writers/codemeta_writer_spec.rb +19 -20
  52. data/spec/writers/crossref_xml_writer_spec.rb +73 -37
  53. data/spec/writers/datacite_writer_spec.rb +2 -1
  54. metadata +10 -3
@@ -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
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ # frozen_string_literal: truefiles
2
2
 
3
3
  require_relative 'metadata_utils'
4
4
 
@@ -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, :files, :references, :related_identifiers, :related_items, :style, :locale
14
14
 
15
15
  def initialize(options = {})
16
16
  options.symbolize_keys!
@@ -45,7 +45,7 @@ module Commonmeta
45
45
  'state' => options[:state],
46
46
  'provider_id' => options[:provider_id],
47
47
  'client_id' => options[:client_id],
48
- 'content_url' => options[:content_url]
48
+ 'files' => options[:files]
49
49
  }
50
50
  string = File.read(options[:input])
51
51
  @from = options[:from] || find_from_format(string: string, ext: ext)
@@ -59,8 +59,7 @@ module Commonmeta
59
59
  'state' => options[:state],
60
60
  'provider_id' => options[:provider_id],
61
61
  'client_id' => options[:client_id],
62
- 'content_url' => options[:content_url],
63
- 'creators' => options[:creators],
62
+ 'files' => options[:files],
64
63
  'contributors' => options[:contributors],
65
64
  'titles' => options[:titles],
66
65
  'publisher' => options[:publisher]
@@ -86,7 +85,7 @@ module Commonmeta
86
85
  @state = hsh.to_h['state'].presence
87
86
  @provider_id = hsh.to_h['provider_id'].presence
88
87
  @client_id = hsh.to_h['client_id'].presence
89
- @content_url = hsh.to_h['content_url'].presence
88
+ @files = hsh.to_h['files'].presence
90
89
 
91
90
  # options that come from the cli, needed
92
91
  # for crossref doi registration
@@ -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,
@@ -228,8 +226,8 @@ module Commonmeta
228
226
  @alternate_identifiers ||= meta.fetch('alternate_identifiers', nil)
229
227
  end
230
228
 
231
- def content_url
232
- @content_url ||= meta.fetch('content_url', nil)
229
+ def files
230
+ @files ||= meta.fetch('files', nil)
233
231
  end
234
232
 
235
233
  def provider
@@ -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,