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.
- checksums.yaml +4 -4
- data/Gemfile.lock +8 -8
- data/csl-data.json +538 -0
- data/lib/commonmeta/author_utils.rb +103 -71
- data/lib/commonmeta/crossref_utils.rb +31 -25
- data/lib/commonmeta/metadata.rb +8 -14
- 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 +131 -124
- 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 +5 -5
- data/lib/commonmeta/readers/json_feed_reader.rb +8 -4
- 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 +6 -4
- 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 +7 -4
- data/resources/{commonmeta_v0.9.3.json → commonmeta_v0.10.3.json} +138 -55
- data/resources/csl-citation.json +99 -0
- 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/archived_wordpress_post.yml +119 -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 +844 -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 +68 -40
- 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 +289 -288
- 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 +2 -1
- metadata +10 -3
@@ -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
@@ -1,4 +1,4 @@
|
|
1
|
-
# frozen_string_literal:
|
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, :
|
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, :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
|
-
'
|
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
|
-
'
|
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
|
-
@
|
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
|
232
|
-
@
|
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(
|
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,
|