relaton-doi 1.14.2 → 1.14.4
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/.github/workflows/rake.yml +1 -0
- data/.github/workflows/release.yml +3 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/Gemfile +7 -2
- data/lib/relaton_doi/crossref.rb +11 -424
- data/lib/relaton_doi/parser.rb +773 -0
- data/lib/relaton_doi/version.rb +1 -1
- data/lib/relaton_doi.rb +1 -2
- data/relaton-doi.gemspec +6 -13
- metadata +12 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4184d182bd51c7b61a50ae43d52bd95ddd73024defb5ca8ee96641ca7ae4b13b
|
4
|
+
data.tar.gz: 519e7864a1ddea5cc3a74569848e08ba0951d61877b0e9c9262c76b69afb46c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b544c06393cd74c073cc1c815e12646ef8d06b3f6fcbee464547d21416617d707ea33ffd370bb69c0aba94d6feda816bc78fa0cb36bc7ac5960ed90e5bc4304f
|
7
|
+
data.tar.gz: bd11ec0a75b525c6d34a0b922a1a2b64784a2944f3fd8e49f5756493160b25a7da7078c70ac2b853f7bf8a19ec3e941eb3597327293ccdca6a74429140bb2f55
|
data/.github/workflows/rake.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
@@ -5,8 +5,13 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in relaton-doi.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem "debug"
|
9
|
+
gem "equivalent-xml", "~> 0.6"
|
8
10
|
gem "rake", "~> 13.0"
|
9
|
-
|
10
11
|
gem "rspec", "~> 3.0"
|
11
|
-
|
12
12
|
gem "rubocop", "~> 1.7"
|
13
|
+
gem "rubocop-performance"
|
14
|
+
gem "rubocop-rails"
|
15
|
+
gem "simplecov"
|
16
|
+
gem "vcr"
|
17
|
+
gem "webmock"
|
data/lib/relaton_doi/crossref.rb
CHANGED
@@ -1,53 +1,6 @@
|
|
1
1
|
module RelatonDoi
|
2
|
-
|
3
|
-
|
4
|
-
"book-chapter" => "inbook",
|
5
|
-
"book-part" => "inbook",
|
6
|
-
"book-section" => "inbook",
|
7
|
-
"book-series" => "book",
|
8
|
-
"book-set" => "book",
|
9
|
-
"book-track" => "inbook",
|
10
|
-
"component" => "misc",
|
11
|
-
"database" => "dataset",
|
12
|
-
"dissertation" => "thesis",
|
13
|
-
"edited-book" => "book",
|
14
|
-
"grant" => "misc",
|
15
|
-
"journal-article" => "article",
|
16
|
-
"journal-issue" => "journal",
|
17
|
-
"journal-volume" => "journal",
|
18
|
-
"monograph" => "book",
|
19
|
-
"other" => "misc",
|
20
|
-
"peer-review" => "article",
|
21
|
-
"posted-content" => "social_media",
|
22
|
-
"proceedings-article" => "inproceedings",
|
23
|
-
"proceedings-series" => "proceedings",
|
24
|
-
"reference-book" => "book",
|
25
|
-
"reference-entry" => "inbook",
|
26
|
-
"report-component" => "techreport",
|
27
|
-
"report-series" => "techreport",
|
28
|
-
"report" => "techreport",
|
29
|
-
}.freeze
|
30
|
-
|
31
|
-
REALATION_TYPES = {
|
32
|
-
"is-preprint-of" => "reprintOf",
|
33
|
-
"is-review-of" => "reviewOf",
|
34
|
-
"has-review" => "hasReview",
|
35
|
-
"is-identical-to" => "identicalTo", # ?
|
36
|
-
"is-supplement-to" => "complements",
|
37
|
-
}.freeze
|
38
|
-
|
39
|
-
#
|
40
|
-
# Get a document by DOI from the CrossRef API.
|
41
|
-
#
|
42
|
-
# @param [String] doi The DOI.
|
43
|
-
#
|
44
|
-
# @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
|
45
|
-
# RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
|
46
|
-
# RelatonNist::NistBibliographicItem] The bibitem.
|
47
|
-
#
|
48
|
-
def self.get(doi)
|
49
|
-
new.get doi
|
50
|
-
end
|
2
|
+
module Crossref
|
3
|
+
extend self
|
51
4
|
|
52
5
|
#
|
53
6
|
# Get a document by DOI from the CrossRef API.
|
@@ -61,387 +14,21 @@ module RelatonDoi
|
|
61
14
|
def get(doi)
|
62
15
|
warn "[relaton-doi] [\"#{doi}\"] fetching..."
|
63
16
|
id = doi.sub(%r{^doi:}, "")
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
create_bibitem @message["DOI"], bibitem_hash
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
# Create a bibitem from the bibitem hash.
|
72
|
-
#
|
73
|
-
# @param [String] doi The DOI.
|
74
|
-
# @param [Hash] bibitem The bibitem hash.
|
75
|
-
#
|
76
|
-
# @return [RelatonBib::BibliographicItem, RelatonIetf::IetfBibliographicItem,
|
77
|
-
# RelatonBipm::BipmBibliographicItem, RelatonIeee::IeeeBibliographicItem,
|
78
|
-
# RelatonNist::NistBibliographicItem] The bibitem.
|
79
|
-
#
|
80
|
-
# @raise [RelatonDoi::Error] if the document type is not supported.
|
81
|
-
#
|
82
|
-
def create_bibitem(doi, bibitem) # rubocop:disable Metrics/CyclomaticComplexity
|
83
|
-
# case @message["institution"]&.first&.fetch("acronym")&.first
|
84
|
-
case doi
|
85
|
-
when /\/nist/ then RelatonNist::NistBibliographicItem.new(**bibitem)
|
86
|
-
when /\/rfc\d+/ then RelatonIetf::IetfBibliographicItem.new(**bibitem)
|
87
|
-
when /\/0026-1394\// then RelatonBipm::BipmBibliographicItem.new(**bibitem)
|
88
|
-
# when "ISO" then RelatonIso::IsoBibliographicItem.new(**bibitem)
|
89
|
-
# when "W3C" then RelatonW3c::W3cBibliographicItem.new(**bibitem)
|
90
|
-
when /\/ieee/ then RelatonIeee::IeeeBibliographicItem.new(**bibitem)
|
91
|
-
else RelatonBib::BibliographicItem.new(**bibitem)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
#
|
96
|
-
# Create a bibitem hash from the message hash.
|
97
|
-
#
|
98
|
-
# @return [Hash] The bibitem hash.
|
99
|
-
#
|
100
|
-
def bibitem_hash # rubocop:disable Metrics/MethodLength
|
101
|
-
{
|
102
|
-
type: parse_type,
|
103
|
-
fetched: Date.today.to_s,
|
104
|
-
title: create_title,
|
105
|
-
docid: create_docid,
|
106
|
-
date: create_date,
|
107
|
-
link: create_link,
|
108
|
-
abstract: create_abstract,
|
109
|
-
contributor: create_contributors,
|
110
|
-
doctype: @message["type"],
|
111
|
-
place: create_place,
|
112
|
-
relation: create_relation,
|
113
|
-
extent: create_extent,
|
114
|
-
series: create_series,
|
115
|
-
}
|
116
|
-
end
|
117
|
-
|
118
|
-
#
|
119
|
-
# Parse the document type.
|
120
|
-
#
|
121
|
-
# @return [String] The document type.
|
122
|
-
#
|
123
|
-
def parse_type
|
124
|
-
TYPES[@message["type"]] || @message["type"]
|
125
|
-
end
|
126
|
-
|
127
|
-
#
|
128
|
-
# Create a title and a subtitle from the message hash.
|
129
|
-
#
|
130
|
-
# @return [Array<RelatonBib::TypedTitleString>] The title and subtitle.
|
131
|
-
#
|
132
|
-
def create_title
|
133
|
-
@message["title"].map do |t|
|
134
|
-
RelatonBib::TypedTitleString.new(
|
135
|
-
type: "main", content: t, language: "en", script: "Latn",
|
136
|
-
)
|
137
|
-
end + @message["subtitle"].map do |t|
|
138
|
-
RelatonBib::TypedTitleString.new(
|
139
|
-
type: "subtitle", content: t, language: "en", script: "Latn",
|
140
|
-
)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
#
|
145
|
-
# Create a docid from the message hash.
|
146
|
-
#
|
147
|
-
# @return [Array<RelatonBib::DocumentIdentifier>] The docid.
|
148
|
-
#
|
149
|
-
def create_docid
|
150
|
-
%w[DOI ISBN].each_with_object([]) do |type, obj|
|
151
|
-
id = @message[type].is_a?(Array) ? @message[type].first : @message[type]
|
152
|
-
next unless id
|
153
|
-
|
154
|
-
primary = type == "DOI"
|
155
|
-
obj << RelatonBib::DocumentIdentifier.new(type: type, id: id, primary: primary)
|
156
|
-
end
|
17
|
+
message = get_by_id id
|
18
|
+
warn "[relaton-doi] [\"#{doi}\"] found #{message['DOI']}"
|
19
|
+
Parser.parse message
|
157
20
|
end
|
158
21
|
|
159
22
|
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
# @return [Array<RelatonBib::BibliographicDate>] The dates.
|
163
|
-
#
|
164
|
-
def create_date
|
165
|
-
%w[created issued published approved].each_with_object([]) do |type, obj|
|
166
|
-
next unless @message[type]
|
167
|
-
|
168
|
-
on = @message[type]["date-parts"][0].map { |d| d.to_s.rjust(2, "0") }.join "-"
|
169
|
-
obj << RelatonBib::BibliographicDate.new(type: type, on: on)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
#
|
174
|
-
# Create a link from the message hash.
|
175
|
-
#
|
176
|
-
# @return [Array<RelatonBib::TypedUri>] The link.
|
177
|
-
#
|
178
|
-
def create_link
|
179
|
-
links = []
|
180
|
-
if @message["URL"]
|
181
|
-
links << RelatonBib::TypedUri.new(type: "DOI", content: @message["URL"])
|
182
|
-
end
|
183
|
-
return links unless @message["link"]&.any?
|
184
|
-
|
185
|
-
link = @message["link"].first
|
186
|
-
if link["URL"].match?(/\.pdf$/)
|
187
|
-
links << RelatonBib::TypedUri.new(type: "pdf", content: link["URL"])
|
188
|
-
end
|
189
|
-
links
|
190
|
-
end
|
191
|
-
|
192
|
-
#
|
193
|
-
# Create an abstract from the message hash.
|
194
|
-
#
|
195
|
-
# @return [Array<RelatonBib::FormattedString>] The abstract.
|
196
|
-
#
|
197
|
-
def create_abstract
|
198
|
-
return [] unless @message["abstract"]
|
199
|
-
|
200
|
-
content = @message["abstract"]
|
201
|
-
abstract = RelatonBib::FormattedString.new(
|
202
|
-
content: content, language: "en", script: "Latn", format: "text/html",
|
203
|
-
)
|
204
|
-
[abstract]
|
205
|
-
end
|
206
|
-
|
207
|
-
#
|
208
|
-
# Create contributors from the message hash.
|
209
|
-
#
|
210
|
-
# @return [Array<RelatonBib::ContributionInfo>] The contributors.
|
211
|
-
#
|
212
|
-
def create_contributors # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
213
|
-
contribs = %w[author editor translator].each_with_object([]) do |type, obj|
|
214
|
-
@message[type]&.each do |contrib|
|
215
|
-
obj << contributor(person(contrib), type)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
contribs << contributor(org_publisher, "publisher")
|
219
|
-
end
|
220
|
-
|
221
|
-
#
|
222
|
-
# Cerate an organization publisher from the message hash.
|
223
|
-
#
|
224
|
-
# @return [RelatonBib::Organization] The organization.
|
225
|
-
#
|
226
|
-
def org_publisher
|
227
|
-
pbr = @message["institution"]&.detect do |i|
|
228
|
-
@message["publisher"].include?(i["name"]) ||
|
229
|
-
i["name"].include?(@message["publisher"])
|
230
|
-
end
|
231
|
-
a = pbr["acronym"]&.first if pbr
|
232
|
-
RelatonBib::Organization.new name: @message["publisher"], abbreviation: a
|
233
|
-
end
|
234
|
-
|
235
|
-
#
|
236
|
-
# Create contributor from an entity and a role type.
|
237
|
-
#
|
238
|
-
# @param [RelatonBib::Person, RelatonBib::Organization] entity The entity.
|
239
|
-
# @param [String] type The role type.
|
240
|
-
#
|
241
|
-
# @return [RelatonBib::ContributionInfo] The contributor.
|
242
|
-
#
|
243
|
-
def contributor(entity, type)
|
244
|
-
RelatonBib::ContributionInfo.new(entity: entity, role: [type: type])
|
245
|
-
end
|
246
|
-
|
247
|
-
#
|
248
|
-
# Create a person from a person hash.
|
249
|
-
#
|
250
|
-
# @param [Hash] person The person hash.
|
251
|
-
#
|
252
|
-
# @return [RelatonBib::Person] The person.
|
253
|
-
#
|
254
|
-
def person(person)
|
255
|
-
RelatonBib::Person.new(
|
256
|
-
name: person_name(person), affiliation: affiliation(person),
|
257
|
-
identifier: person_id(person)
|
258
|
-
)
|
259
|
-
end
|
260
|
-
|
261
|
-
#
|
262
|
-
# Create person affiliations from a person hash.
|
263
|
-
#
|
264
|
-
# @param [Hash] person The person hash.
|
265
|
-
#
|
266
|
-
# @return [Array<RelatonBib::Affiliation>] The affiliations.
|
267
|
-
#
|
268
|
-
def affiliation(person)
|
269
|
-
(person["affiliation"] || []).map do |a|
|
270
|
-
org = RelatonBib::Organization.new(name: a["name"])
|
271
|
-
RelatonBib::Affiliation.new organization: org
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
#
|
276
|
-
# Create a person full name from a person hash.
|
277
|
-
#
|
278
|
-
# @param [Hash] person The person hash.
|
279
|
-
#
|
280
|
-
# @return [RelatonBib::FullName] The full name.
|
281
|
-
#
|
282
|
-
def person_name(person)
|
283
|
-
sn = RelatonBib::LocalizedString.new(person["family"], "en", "Latn")
|
284
|
-
RelatonBib::FullName.new(
|
285
|
-
surname: sn, forename: forename(person), addition: nameaddition(person),
|
286
|
-
completename: completename(person), prefix: nameprefix(person)
|
287
|
-
)
|
288
|
-
end
|
289
|
-
|
290
|
-
#
|
291
|
-
# Create a person name prefix from a person hash.
|
292
|
-
#
|
293
|
-
# @param [Hash] person The person hash.
|
294
|
-
#
|
295
|
-
# @return [Array<RelatonBib::LocalizedString>] The name prefix.
|
296
|
-
#
|
297
|
-
def nameprefix(person)
|
298
|
-
return [] unless person["prefix"]
|
299
|
-
|
300
|
-
[RelatonBib::LocalizedString.new(person["prefix"], "en", "Latn")]
|
301
|
-
end
|
302
|
-
|
303
|
-
#
|
304
|
-
# Create a complete name from a person hash.
|
305
|
-
#
|
306
|
-
# @param [Hash] person The person hash.
|
307
|
-
#
|
308
|
-
# @return [RelatonBib::LocalizedString] The complete name.
|
309
|
-
#
|
310
|
-
def completename(person)
|
311
|
-
return unless person["name"]
|
312
|
-
|
313
|
-
RelatonBib::LocalizedString.new(person["name"], "en", "Latn")
|
314
|
-
end
|
315
|
-
|
316
|
-
#
|
317
|
-
# Create a forename from a person hash.
|
318
|
-
#
|
319
|
-
# @param [Hash] person The person hash.
|
320
|
-
#
|
321
|
-
# @return [Array<RelatonBib::LocalizedString>] The forename.
|
322
|
-
#
|
323
|
-
def forename(person)
|
324
|
-
return [] unless person["given"]
|
325
|
-
|
326
|
-
[RelatonBib::Forename.new(content: person["given"], language: "en", script: "Latn")]
|
327
|
-
end
|
328
|
-
|
329
|
-
#
|
330
|
-
# Create an addition from a person hash.
|
331
|
-
#
|
332
|
-
# @param [Hash] person The person hash.
|
333
|
-
#
|
334
|
-
# @return [Array<RelatonBib::LocalizedString>] The addition.
|
335
|
-
#
|
336
|
-
def nameaddition(person)
|
337
|
-
return [] unless person["suffix"]
|
338
|
-
|
339
|
-
[RelatonBib::LocalizedString.new(person["suffix"], "en", "Latn")]
|
340
|
-
end
|
341
|
-
|
342
|
-
#
|
343
|
-
# Create a person identifier from a person hash.
|
344
|
-
#
|
345
|
-
# @param [Hash] person The person hash.
|
346
|
-
#
|
347
|
-
# @return [Array<RelatonBib::PersonIdentifier>] The person identifier.
|
348
|
-
#
|
349
|
-
def person_id(person)
|
350
|
-
return [] unless person["ORCID"]
|
351
|
-
|
352
|
-
[RelatonBib::PersonIdentifier.new("orcid", person["ORCID"])]
|
353
|
-
end
|
354
|
-
|
355
|
-
#
|
356
|
-
# Create a place from the message hash.
|
357
|
-
#
|
358
|
-
# @return [Array<RelatonBib::Place>] The place.
|
359
|
-
#
|
360
|
-
def create_place
|
361
|
-
return [] unless @message["publisher-location"]
|
362
|
-
|
363
|
-
city, rg = @message["publisher-location"].split(", ")
|
364
|
-
region = RelatonBib::Place::RegionType.new(name: rg)
|
365
|
-
[RelatonBib::Place.new(city: city, region: [region])]
|
366
|
-
end
|
367
|
-
|
368
|
-
#
|
369
|
-
# Crerate relations from the message hash.
|
370
|
-
#
|
371
|
-
# @return [Array<RelatonBib::DocumentRelation>] The relations.
|
372
|
-
#
|
373
|
-
def create_relation # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
374
|
-
rels = []
|
375
|
-
@message["container-title"]&.each do |ct|
|
376
|
-
contrib = included_in_editors(ct)
|
377
|
-
bib = RelatonBib::BibliographicItem.new(title: [content: ct], contributor: contrib)
|
378
|
-
rels << RelatonBib::DocumentRelation.new(type: "includedIn", bibitem: bib)
|
379
|
-
end
|
380
|
-
@message["relation"].each_with_object(rels) do |(k, v), a|
|
381
|
-
fref = RelatonBib::FormattedRef.new(content: v["id"])
|
382
|
-
bib = create_bibitem v["id"], formattedref: fref
|
383
|
-
type = REALATION_TYPES[k] || k
|
384
|
-
a << RelatonBib::DocumentRelation.new(type: type, bibitem: bib)
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
#
|
389
|
-
# Fetch included in editors.
|
390
|
-
#
|
391
|
-
# @param [String] title container-title
|
392
|
-
#
|
393
|
-
# @return [Array<RelatonBib::ContributionInfo>] The editors contribution info.
|
394
|
-
#
|
395
|
-
def included_in_editors(title)
|
396
|
-
item = fetch_included_in title
|
397
|
-
return [] unless item
|
398
|
-
|
399
|
-
item["editor"].map { |e| contributor(person(e), "editor") }
|
400
|
-
end
|
401
|
-
|
402
|
-
#
|
403
|
-
# Fetch included in relation.
|
404
|
-
#
|
405
|
-
# @param [String] title container-title
|
406
|
-
#
|
407
|
-
# @return [Hash] The included in relation item.
|
408
|
-
#
|
409
|
-
def fetch_included_in(title) # rubocop:disable Metrics/AbcSize
|
410
|
-
year = (@message["published"] || @message["approved"])["date-parts"][0][0]
|
411
|
-
query = "#{title}, #{@message['publisher']}, #{@message['publisher-location']}, #{year}"
|
412
|
-
resp = Faraday.get %{http://api.crossref.org/works?query.bibliographic="#{query}"&rows=5&filter=type:book}
|
413
|
-
json = JSON.parse resp.body
|
414
|
-
json["message"]["items"].detect { |i| i["title"].include?(title) && i["editor"] }
|
415
|
-
end
|
416
|
-
|
417
|
-
#
|
418
|
-
# Create an extent from the message hash.
|
419
|
-
#
|
420
|
-
# @return [Array<RelatonBib::Locality>] The extent.
|
421
|
-
#
|
422
|
-
def create_extent # rubocop:disable Metrics/AbcSize
|
423
|
-
extent = []
|
424
|
-
extent << RelatonBib::Locality.new("volume", @message["volume"]) if @message["volume"]
|
425
|
-
extent << RelatonBib::Locality.new("issue", @message["issue"]) if @message["issue"]
|
426
|
-
if @message["page"]
|
427
|
-
from, to = @message["page"].split("-")
|
428
|
-
extent << RelatonBib::Locality.new("page", from, to)
|
429
|
-
end
|
430
|
-
extent.any? ? [RelatonBib::LocalityStack.new(extent)] : []
|
431
|
-
end
|
432
|
-
|
23
|
+
# Get a document by DOI from the CrossRef API.
|
433
24
|
#
|
434
|
-
#
|
25
|
+
# @param [String] id The DOI.
|
435
26
|
#
|
436
|
-
# @return [
|
27
|
+
# @return [Hash] The document.
|
437
28
|
#
|
438
|
-
def
|
439
|
-
|
440
|
-
|
441
|
-
@message["container-title"].map do |ct|
|
442
|
-
title = RelatonBib::TypedTitleString.new content: ct
|
443
|
-
RelatonBib::Series.new title: title
|
444
|
-
end
|
29
|
+
def get_by_id(id)
|
30
|
+
resp = Serrano.works ids: id
|
31
|
+
resp[0]["message"]
|
445
32
|
end
|
446
33
|
end
|
447
34
|
end
|