commonmeta-ruby 3.4.4 → 3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/lib/commonmeta/author_utils.rb +103 -71
- data/lib/commonmeta/crossref_utils.rb +31 -25
- data/lib/commonmeta/metadata.rb +2 -8
- data/lib/commonmeta/metadata_utils.rb +4 -3
- data/lib/commonmeta/readers/bibtex_reader.rb +3 -3
- data/lib/commonmeta/readers/cff_reader.rb +7 -6
- data/lib/commonmeta/readers/codemeta_reader.rb +3 -3
- data/lib/commonmeta/readers/crossref_reader.rb +3 -5
- data/lib/commonmeta/readers/crossref_xml_reader.rb +7 -6
- data/lib/commonmeta/readers/csl_reader.rb +3 -4
- data/lib/commonmeta/readers/datacite_reader.rb +3 -5
- data/lib/commonmeta/readers/json_feed_reader.rb +3 -3
- data/lib/commonmeta/readers/npm_reader.rb +2 -2
- data/lib/commonmeta/readers/ris_reader.rb +1 -1
- data/lib/commonmeta/readers/schema_org_reader.rb +3 -3
- data/lib/commonmeta/schema_utils.rb +1 -1
- data/lib/commonmeta/utils.rb +4 -2
- data/lib/commonmeta/version.rb +1 -1
- data/lib/commonmeta/writers/bibtex_writer.rb +1 -1
- data/lib/commonmeta/writers/cff_writer.rb +5 -4
- data/lib/commonmeta/writers/codemeta_writer.rb +4 -2
- data/lib/commonmeta/writers/csv_writer.rb +4 -2
- data/lib/commonmeta/writers/datacite_writer.rb +1 -1
- data/lib/commonmeta/writers/jats_writer.rb +9 -5
- data/lib/commonmeta/writers/ris_writer.rb +2 -1
- data/lib/commonmeta/writers/schema_org_writer.rb +6 -3
- data/resources/{commonmeta_v0.9.3.json → commonmeta_v0.10.json} +62 -46
- data/spec/author_utils_spec.rb +16 -16
- data/spec/cli_spec.rb +1 -1
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_crossref_metadata/missing_contributor.yml +307 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_datacite_metadata/SoftwareSourceCode.yml +76 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item_metadata/ghost_post_with_related_identifiers_and_funding.yml +36 -36
- 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
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/book_oup.yml +107 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/journal_article_plos.yml +407 -0
- data/spec/metadata_spec.rb +2 -2
- data/spec/readers/bibtex_reader_spec.rb +5 -5
- data/spec/readers/cff_reader_spec.rb +127 -127
- data/spec/readers/codemeta_reader_spec.rb +11 -11
- data/spec/readers/crossref_reader_spec.rb +831 -835
- data/spec/readers/crossref_xml_reader_spec.rb +899 -901
- data/spec/readers/csl_reader_spec.rb +33 -33
- data/spec/readers/datacite_reader_spec.rb +106 -103
- data/spec/readers/json_feed_reader_spec.rb +64 -38
- data/spec/readers/npm_reader_spec.rb +32 -33
- data/spec/readers/ris_reader_spec.rb +36 -36
- data/spec/readers/schema_org_reader_spec.rb +284 -284
- data/spec/writers/codemeta_writer_spec.rb +19 -20
- data/spec/writers/crossref_xml_writer_spec.rb +73 -37
- data/spec/writers/datacite_writer_spec.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eff7462ec031d3c8ad11d8f684738711c0464cee5644028d8d805ff36f09058f
|
4
|
+
data.tar.gz: 8fda70b594d569ffe432f6071c85f9009d8e83434bd3f079a6ea7d312e57f3bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
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.
|
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
|
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 = {
|
36
|
+
author = { "name" => author } if author.is_a?(String)
|
13
37
|
|
14
38
|
# malformed XML
|
15
|
-
return nil if author.fetch(
|
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(
|
19
|
-
parse_attributes(author.fetch(
|
20
|
-
parse_attributes(author.fetch(
|
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(
|
23
|
-
parse_attributes(author.fetch(
|
24
|
-
family_name = parse_attributes(author.fetch(
|
25
|
-
parse_attributes(author.fetch(
|
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(
|
31
|
-
parse_attributes(author.fetch(
|
32
|
-
parse_attributes(author.fetch(
|
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[
|
36
|
-
id = Array.wrap(author.dig(
|
37
|
-
ni[
|
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[
|
40
|
-
|
41
|
-
elsif id.nil? && author[
|
42
|
-
id = author.fetch(
|
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(
|
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?(
|
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 ==
|
53
|
-
type =
|
54
|
-
author[
|
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 ==
|
57
|
-
type =
|
58
|
-
elsif type.blank? && author[
|
59
|
-
type =
|
60
|
-
elsif type.blank? && id.is_a?(String) && URI.parse(id).host ==
|
61
|
-
type =
|
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 =
|
64
|
-
elsif type.blank? && is_personal_name?(name: name) && name.to_s.exclude?(
|
65
|
-
type =
|
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 =
|
91
|
+
type = "Organization"
|
68
92
|
end
|
69
93
|
|
70
94
|
# parse author contributor role
|
71
|
-
|
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 ==
|
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
|
-
{
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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(
|
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,
|
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] ==
|
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
|
-
|
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(
|
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(
|
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[
|
163
|
-
[a[
|
164
|
-
elsif a[
|
165
|
-
a[
|
166
|
-
elsif a[
|
167
|
-
"{#{a[
|
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(
|
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[
|
181
|
-
affiliation_identifier = a[
|
182
|
-
if a[
|
183
|
-
schemeURI = a[
|
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?(
|
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[
|
219
|
+
name = (a["name"] || a["__content__"]).to_s.squish.presence
|
188
220
|
end
|
189
221
|
|
190
|
-
{
|
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
|
-
{
|
199
|
-
|
200
|
-
|
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
|
-
|
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
|
-
|
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
|
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(
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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,
|
114
|
+
insert_crossref_person(xml, contributor)
|
109
115
|
end
|
110
|
-
elsif
|
111
|
-
xml.anonymous("contributor_role" =>
|
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,
|
119
|
+
insert_crossref_anonymous(xml, contributor)
|
114
120
|
end
|
115
121
|
else
|
116
|
-
xml.anonymous("contributor_role" =>
|
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,
|
124
|
-
xml.given_name(
|
125
|
-
xml.surname(
|
126
|
-
if
|
127
|
-
xml.ORCID(
|
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
|
135
|
+
if contributor["affiliation"].present?
|
130
136
|
xml.affiliations do
|
131
137
|
xml.institution do
|
132
|
-
xml.institution_name(
|
133
|
-
xml.institution_id(
|
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,
|
145
|
+
def insert_crossref_anonymous(xml, contributor)
|
140
146
|
xml.affiliations do
|
141
147
|
xml.institution do
|
142
|
-
xml.institution_name(
|
143
|
-
xml.institution_id(
|
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
|
data/lib/commonmeta/metadata.rb
CHANGED
@@ -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, :
|
13
|
-
:titles, :publisher, :license, :date, :volume, :url, :version, :subjects, :
|
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(
|
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(
|
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
|
-
|
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
|
-
'
|
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
|
-
|
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
|
-
'
|
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
|
84
|
-
Array.wrap(
|
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
|
-
'
|
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
|
-
|
38
|
-
contributors
|
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
|
-
|
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
|
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
|
-
'
|
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
|