commonmeta-ruby 3.2.15 → 3.3.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 +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
|