commonmeta-ruby 3.2.15 → 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/bin/commonmeta +1 -1
- data/lib/commonmeta/author_utils.rb +1 -1
- data/lib/commonmeta/cli.rb +17 -0
- data/lib/commonmeta/crossref_utils.rb +56 -14
- data/lib/commonmeta/readers/json_feed_reader.rb +25 -1
- data/lib/commonmeta/utils.rb +37 -0
- data/lib/commonmeta/version.rb +1 -1
- data/spec/cli_spec.rb +27 -3
- data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/doi_prefix/doi_prefix_by_blog.yml +997 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/doi_prefix/doi_prefix_by_uuid.yml +256 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/encode/by_blog.yml +997 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/encode/by_blog_unknown_blog_id.yml +49 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/encode/by_uuid.yml +256 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_CLI/encode/by_uuid_unknown_uuid.yml +49 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_doi_prefix_for_blog/by_blog_id.yml +997 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_doi_prefix_for_blog/by_blog_post_uuid.yml +389 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_doi_prefix_for_blog/by_blog_post_uuid_specific_prefix.yml +389 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item/by_uuid.yml +136 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item_metadata/blog_post_with_non-url_id.yml +136 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/get_json_feed_item_metadata/ghost_post_with_organizational_author.yml +91 -0
- data/spec/fixtures/vcr_cassettes/Commonmeta_Metadata/write_metadata_as_crossref/json_feed_item_from_rogue_scholar_with_organizational_author.yml +91 -0
- data/spec/readers/json_feed_reader_spec.rb +68 -0
- data/spec/utils_spec.rb +8 -0
- data/spec/writers/crossref_xml_writer_spec.rb +28 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a5ad35cfa39e4b1aadd232585375001d87f0a14c5f5638b6574c6982bb130a6
|
4
|
+
data.tar.gz: d7104f032539d68ee797c35e6f86d69fd8514691babec286ab3024fa49bac0cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86c9391a579504409de79cd3a2d449a7725a0b46014cde46e5ef5506b51e0595f5aa4a2404ac49fca20c8d8673b8b895234127e42ddf9314dd52214a7bdb9973
|
7
|
+
data.tar.gz: 26d1153f74db838c65c99b5229f2031c0ddb90b907fc3f7f51210bd177cf78e8417814437284378e38a86fb829bf36ccb1c6d5b3a7ac6d35678d870fac918e93
|
data/Gemfile.lock
CHANGED
data/bin/commonmeta
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require File.expand_path("../../lib/commonmeta", __FILE__)
|
4
4
|
|
5
|
-
if (ARGV & %w(--version -v help --help encode decode encode_id decode_id json_feed_not_indexed json_feed_unregistered json_feed_by_blog)).empty?
|
5
|
+
if (ARGV & %w(--version -v help --help encode decode encode_id decode_id encode_by_blog encode_by_uuid json_feed_not_indexed json_feed_unregistered json_feed_by_blog)).empty?
|
6
6
|
Commonmeta::CLI.start(ARGV.dup.unshift("convert"))
|
7
7
|
else
|
8
8
|
Commonmeta::CLI.start
|
@@ -41,7 +41,7 @@ module Commonmeta
|
|
41
41
|
elsif id.nil? && author['ORCID'].present?
|
42
42
|
id = author.fetch('ORCID')
|
43
43
|
end
|
44
|
-
id = normalize_orcid(id)
|
44
|
+
id = normalize_orcid(id) || normalize_ror(id)
|
45
45
|
|
46
46
|
# parse author type, i.e. "Person", "Organization" or not specified
|
47
47
|
type = author.fetch('type', nil)
|
data/lib/commonmeta/cli.rb
CHANGED
@@ -59,6 +59,7 @@ module Commonmeta
|
|
59
59
|
desc "", "encode"
|
60
60
|
|
61
61
|
def encode(prefix)
|
62
|
+
return nil unless prefix.present?
|
62
63
|
puts encode_doi(prefix)
|
63
64
|
end
|
64
65
|
|
@@ -68,6 +69,22 @@ module Commonmeta
|
|
68
69
|
puts encode_container_id
|
69
70
|
end
|
70
71
|
|
72
|
+
desc "", "encode_by_blog"
|
73
|
+
|
74
|
+
def encode_by_blog(blog_id)
|
75
|
+
prefix = get_doi_prefix_by_blog_id(blog_id)
|
76
|
+
return nil unless prefix.present?
|
77
|
+
puts encode_doi(prefix)
|
78
|
+
end
|
79
|
+
|
80
|
+
desc "", "encode_by_uuid"
|
81
|
+
|
82
|
+
def encode_by_uuid(uuid)
|
83
|
+
prefix = get_doi_prefix_by_json_feed_item_uuid(uuid)
|
84
|
+
return nil unless prefix.present?
|
85
|
+
puts encode_doi(prefix)
|
86
|
+
end
|
87
|
+
|
71
88
|
desc "", "decode"
|
72
89
|
|
73
90
|
def decode(doi)
|
@@ -94,25 +94,67 @@ module Commonmeta
|
|
94
94
|
|
95
95
|
def insert_crossref_creators(xml)
|
96
96
|
xml.contributors do
|
97
|
-
Array.wrap(creators).each_with_index do |
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
Array.wrap(creators).each_with_index do |creator, index|
|
98
|
+
if creator["type"] == "Organization"
|
99
|
+
xml.organization("contributor_role" => "author",
|
100
|
+
"sequence" => index.zero? ? "first" : "additional") do
|
101
|
+
insert_crossref_organization(xml, creator)
|
102
|
+
end
|
103
|
+
elsif creator["givenName"].present? || creator["familyName"].present?
|
104
|
+
xml.person_name("contributor_role" => "author",
|
105
|
+
"sequence" => index.zero? ? "first" : "additional") do
|
106
|
+
insert_crossref_person(xml, creator)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
xml.unknown("contributor_role" => "author",
|
110
|
+
"sequence" => index.zero? ? "first" : "additional") do
|
111
|
+
insert_crossref_anonymous(xml, creator)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def insert_crossref_person(xml, creator)
|
119
|
+
xml.given_name(creator["givenName"]) if creator["givenName"].present?
|
120
|
+
xml.surname(creator["familyName"]) if creator["familyName"].present?
|
121
|
+
if creator.dig("id") && URI.parse(creator.dig("id")).host == "orcid.org"
|
122
|
+
xml.ORCID(creator.dig("id"))
|
123
|
+
end
|
124
|
+
if creator["affiliation"].present?
|
125
|
+
xml.affiliations do
|
126
|
+
xml.institution do
|
127
|
+
xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
|
128
|
+
xml.institution_id(creator.dig("affiliation", 0, "affiliationIdentifier"), "type" => creator.dig("affiliation", 0, "affiliationIdentifierScheme")) if creator.dig("affiliation", 0, "affiliationIdentifier").present?
|
101
129
|
end
|
102
130
|
end
|
103
131
|
end
|
104
132
|
end
|
105
133
|
|
106
|
-
def
|
107
|
-
xml.
|
108
|
-
|
109
|
-
|
110
|
-
|
134
|
+
def insert_crossref_organization(xml, creator)
|
135
|
+
xml.name(creator["name"]) if creator["name"].present?
|
136
|
+
if creator["affiliation"].present?
|
137
|
+
xml.affiliations do
|
138
|
+
xml.institution do
|
139
|
+
xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
|
140
|
+
xml.institution_id(creator.dig("affiliation", 0, "affiliationIdentifier"), "type" => creator.dig("affiliation", 0, "affiliationIdentifierScheme")) if creator.dig("affiliation", 0, "affiliationIdentifier").present?
|
141
|
+
end
|
142
|
+
end
|
111
143
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
144
|
+
end
|
145
|
+
|
146
|
+
def insert_crossref_anonymous(xml, creator)
|
147
|
+
if person["affiliation"].present?
|
148
|
+
xml.anonymous do
|
149
|
+
xml.affiliations do
|
150
|
+
xml.institution do
|
151
|
+
xml.institution_name(creator.dig("affiliation", 0, "name")) if creator.dig("affiliation", 0, "name").present?
|
152
|
+
xml.institution_id(creator.dig("affiliation", 0, "affiliationIdentifier"), "type" => creator.dig("affiliation", 0, "affiliationIdentifierScheme")) if creator.dig("affiliation", 0, "affiliationIdentifier").present?
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
else
|
157
|
+
xml.anonymous
|
116
158
|
end
|
117
159
|
end
|
118
160
|
|
@@ -265,7 +307,7 @@ module Commonmeta
|
|
265
307
|
}.compact
|
266
308
|
|
267
309
|
# strip hyphen from UUIDs, as item_number can only be 32 characters long (UUIDv4 is 36 characters long)
|
268
|
-
alternate_identifier["alternateIdentifier"] = alternate_identifier["alternateIdentifier"].gsub(
|
310
|
+
alternate_identifier["alternateIdentifier"] = alternate_identifier["alternateIdentifier"].gsub("-", "") if alternate_identifier["alternateIdentifierType"] == "UUID"
|
269
311
|
|
270
312
|
xml.item_number(alternate_identifier["alternateIdentifier"], attributes)
|
271
313
|
end
|
@@ -20,8 +20,10 @@ module Commonmeta
|
|
20
20
|
|
21
21
|
meta = string.present? ? JSON.parse(string) : {}
|
22
22
|
|
23
|
-
id = options[:doi] ? normalize_doi(options[:doi]) : normalize_id(meta.fetch("id", nil))
|
24
23
|
url = normalize_url(meta.fetch("url", nil))
|
24
|
+
id = options[:doi] ? normalize_doi(options[:doi]) : normalize_id(meta.fetch("id", nil))
|
25
|
+
id = url if id.blank? && url.present?
|
26
|
+
|
25
27
|
type = "Article"
|
26
28
|
creators = if meta.fetch("authors", nil).present?
|
27
29
|
get_authors(from_json_feed(Array.wrap(meta.fetch("authors"))))
|
@@ -124,6 +126,28 @@ module Commonmeta
|
|
124
126
|
blog = JSON.parse(response.body.to_s)
|
125
127
|
blog["items"].map { |item| item["uuid"] }.first
|
126
128
|
end
|
129
|
+
|
130
|
+
def get_doi_prefix_by_blog_id(blog_id)
|
131
|
+
# for generating a random DOI.
|
132
|
+
|
133
|
+
url = json_feed_by_blog_url(blog_id)
|
134
|
+
response = HTTP.get(url)
|
135
|
+
return nil unless response.status.success?
|
136
|
+
|
137
|
+
post = JSON.parse(response.body.to_s)
|
138
|
+
post.to_h.dig('prefix')
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_doi_prefix_by_json_feed_item_uuid(uuid)
|
142
|
+
# for generating a random DOI. Prefix is based on the blog id.
|
143
|
+
|
144
|
+
url = json_feed_item_by_uuid_url(uuid)
|
145
|
+
response = HTTP.get(url)
|
146
|
+
return nil unless response.status.success?
|
147
|
+
|
148
|
+
post = JSON.parse(response.body.to_s)
|
149
|
+
post.to_h.dig('blog', 'prefix')
|
150
|
+
end
|
127
151
|
end
|
128
152
|
end
|
129
153
|
end
|
data/lib/commonmeta/utils.rb
CHANGED
@@ -543,6 +543,11 @@ module Commonmeta
|
|
543
543
|
orcid.gsub(/[[:space:]]/, "-") if orcid.present?
|
544
544
|
end
|
545
545
|
|
546
|
+
def validate_ror(ror)
|
547
|
+
ror = Array(%r{\A(?:(?:http|https)://ror\.org/)?([0-9a-z]{7}\d{2})\z}.match(ror)).last
|
548
|
+
ror.gsub(/[[:space:]]/, "-") if ror.present?
|
549
|
+
end
|
550
|
+
|
546
551
|
def validate_orcid_scheme(orcid_scheme)
|
547
552
|
Array(%r{\A(http|https)://(www\.)?(orcid\.org)}.match(orcid_scheme)).last
|
548
553
|
end
|
@@ -634,6 +639,14 @@ module Commonmeta
|
|
634
639
|
"https://orcid.org/" + Addressable::URI.encode(orcid)
|
635
640
|
end
|
636
641
|
|
642
|
+
def normalize_ror(ror)
|
643
|
+
ror = validate_ror(ror)
|
644
|
+
return nil unless ror.present?
|
645
|
+
|
646
|
+
# turn ROR ID into URL
|
647
|
+
"https://ror.org/" + Addressable::URI.encode(ror)
|
648
|
+
end
|
649
|
+
|
637
650
|
# pick electronic issn if there are multiple
|
638
651
|
# format issn as xxxx-xxxx
|
639
652
|
def normalize_issn(input, options = {})
|
@@ -1371,6 +1384,26 @@ module Commonmeta
|
|
1371
1384
|
end
|
1372
1385
|
|
1373
1386
|
def encode_doi(prefix, options = {})
|
1387
|
+
return nil unless prefix.present?
|
1388
|
+
|
1389
|
+
# DOI suffix is a generated from a random number, encoded in base32
|
1390
|
+
# suffix has 8 digits plus two checksum digits. With base32 there are
|
1391
|
+
# 32 possible digits, so 8 digits gives 32^8 possible combinations
|
1392
|
+
if options[:uuid]
|
1393
|
+
str = Base32::URL.encode_uuid(options[:uuid], split: 7, checksum: true)
|
1394
|
+
return nil unless str.present?
|
1395
|
+
else
|
1396
|
+
random_int = SecureRandom.random_number(32 ** 7..(32 ** 8) - 1)
|
1397
|
+
suffix = Base32::URL.encode(random_int, checksum: true)
|
1398
|
+
str = "#{suffix[0, 5]}-#{suffix[5, 10]}"
|
1399
|
+
end
|
1400
|
+
"https://doi.org/#{prefix}/#{str}"
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def encode_doi_for_uuid(uuid, options = {})
|
1404
|
+
# look up prefix for rogue scholar blog associated with uuid
|
1405
|
+
# returns nil if unknown uuid or doi registration is not enabled for blog
|
1406
|
+
json_feed_by_uuid(uuid)
|
1374
1407
|
# DOI suffix is a generated from a random number, encoded in base32
|
1375
1408
|
# suffix has 8 digits plus two checksum digits. With base32 there are
|
1376
1409
|
# 32 possible digits, so 8 digits gives 32^8 possible combinations
|
@@ -1415,5 +1448,9 @@ module Commonmeta
|
|
1415
1448
|
def json_feed_by_blog_url(blog_id)
|
1416
1449
|
"https://rogue-scholar.org/api/blogs/#{blog_id}"
|
1417
1450
|
end
|
1451
|
+
|
1452
|
+
def json_feed_item_by_uuid_url(uuid)
|
1453
|
+
"https://rogue-scholar.org/api/posts/#{uuid}"
|
1454
|
+
end
|
1418
1455
|
end
|
1419
1456
|
end
|
data/lib/commonmeta/version.rb
CHANGED
data/spec/cli_spec.rb
CHANGED
@@ -311,12 +311,36 @@ describe Commonmeta::CLI do
|
|
311
311
|
# end
|
312
312
|
end
|
313
313
|
|
314
|
-
describe "encode" do
|
315
|
-
let(:input) { "10.53731" }
|
316
|
-
|
314
|
+
describe "encode", vcr: true do
|
317
315
|
it "blog prefix" do
|
316
|
+
input = "10.53731"
|
318
317
|
expect { subject.encode input }.to output(/https:\/\/doi.org\/10.53731/).to_stdout
|
319
318
|
end
|
319
|
+
|
320
|
+
it "blog prefix missing" do
|
321
|
+
input = ""
|
322
|
+
expect { subject.encode input }.to output("").to_stdout
|
323
|
+
end
|
324
|
+
|
325
|
+
it "by_blog" do
|
326
|
+
input = "tyfqw20"
|
327
|
+
expect { subject.encode_by_blog input }.to output(/https:\/\/doi.org\/10.59350/).to_stdout
|
328
|
+
end
|
329
|
+
|
330
|
+
it "by_blog unknown blog_id" do
|
331
|
+
input = "tyfqw"
|
332
|
+
expect { subject.encode_by_blog input }.to output("").to_stdout
|
333
|
+
end
|
334
|
+
|
335
|
+
it "by_uuid" do
|
336
|
+
input = "2b22bbba-bcba-4072-94cc-3f88442fff88"
|
337
|
+
expect { subject.encode_by_uuid input }.to output(/https:\/\/doi.org\/10.54900/).to_stdout
|
338
|
+
end
|
339
|
+
|
340
|
+
it "by_uuid unknown uuid" do
|
341
|
+
input = "2b22bbba-bcba-4072-94cc-3f88442"
|
342
|
+
expect { subject.encode_by_uuid input }.to output("").to_stdout
|
343
|
+
end
|
320
344
|
end
|
321
345
|
|
322
346
|
describe "decode" do
|