uc3-citation 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +54 -0
- data/lib/uc3-citation/version.rb +5 -0
- data/lib/uc3-citation.rb +131 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9a8f1ca6a2e8720d0137706a9788c6d55759a04106e53dd5d8f21dae4c43911b
|
4
|
+
data.tar.gz: '09ac870471ad1a5602a7acc6794bc3197bacedad41477626a0fe92b4f2bddbf8'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a196e0002d906a33f9c5177e78717c994f6a635bf81a68894b611bf09cc3399a661e47c16c559050c015b3dfab316cadd70287c887232ec268c922ef1476c618
|
7
|
+
data.tar.gz: 1aa7b8cca0823887eaaa80ed9690d2056c46ee2eefbf7395e83d30815cf8538e451e624f033c2c2b79374e27c2653d50f748a723ea351a3af87154c909de7aed
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
uc3-citation
|
2
|
+
===============
|
3
|
+
|
4
|
+
A library that retrieves an citation for the specified DOI following the Chicago Author-Date standard.
|
5
|
+
|
6
|
+
This service makes a request to the DOI registrar and asks for the BibTeX version of the DOI's metadata.
|
7
|
+
|
8
|
+
If BibTeX metadata is received the service will use the CiteProc gem to build a citation that follows the Chicago Author-Date format (CiteProc uses [Citation Style Language (CSL)](https://citationstyles.org/) to build the citation). The citation is in HTML format.
|
9
|
+
|
10
|
+
You can override the default Chicago style by specifying one from the list of CSLs located in the following repository: https://github.com/citation-style-language/styles-distribution/tree/f8524f9b9df60e94e98f824f242a1fb27cc9fc59
|
11
|
+
For example `Uc3::Citation.fetch(doi: '10.1234/article.ef34', work_type: 'article', style: 'apa')`
|
12
|
+
|
13
|
+
## Basic Usage -
|
14
|
+
|
15
|
+
Add the following to your Gemfile and then run bundle install:
|
16
|
+
`gem 'uc3-citation', git: 'https://github.com/CDLUC3/uc3-citation', branch: 'main'`
|
17
|
+
|
18
|
+
Once installed you can then use the service like this:
|
19
|
+
```ruby
|
20
|
+
class YourClass
|
21
|
+
include Uc3Citation
|
22
|
+
|
23
|
+
article = fetch_citation(
|
24
|
+
doi: 'https://doi.org/10.3897/BDJ.9.e67426',
|
25
|
+
work_type: 'dataset'
|
26
|
+
)
|
27
|
+
|
28
|
+
p article
|
29
|
+
# Will display:
|
30
|
+
#
|
31
|
+
# Reboleira, Ana Sofia, and Rita Eus\'ebio. 2021. “Cave-Adapted Beetles from Continental
|
32
|
+
# Portugal.” [Dataset]. Biodiversity Data Journal 9 (August).
|
33
|
+
# https://doi.org/10.3897/BDJ.9.e67426.
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
The `fetch_citation` method accepts the following arguments:
|
38
|
+
- **doi**: The fully qualified URl or the DOI as a string. (e.g. 'https://doi.org/10.3897/BDJ.9.e67426', 'doi:10.3897/BDJ.9.e67426', or '10.3897/BDJ.9.e67426'). In the case where you are only passing the DOI, it will prepend 'https://doi.org/' when trying to acquire the citation. If the DOI is not resolvable from that domain then you will need to send the full URL
|
39
|
+
- **work_type**: Default is nil. If present, the value you specify will be added to the citation after the title to provide context as to what type of work the DOI represents. See example above.
|
40
|
+
- **style**: Default is 'chicago-author-date'. You can specify [any of the CSL defined in this list](https://github.com/citation-style-language/styles-distribution/tree/f8524f9b9df60e94e98f824f242a1fb27cc9fc59)
|
41
|
+
- **debug**: Default is false. If true it will log the results of the request for the BibTeX metadata from the DOI registrar and the result of the citation generation from the CitProc library
|
42
|
+
|
43
|
+
Although unlikely, since citations are acquired from external sources, it is a good idea to wrap the citation in a sanitization method when displaying on a web page to prevent any malicious HTML. For example in a Rails view you can use: `sanitize(article)`
|
44
|
+
|
45
|
+
You should consider the fact that this gem calls out to external systems when incorporating it into your projects. For example adding to a Model or Controller method in Rails can potentially cause slow response times. You may want to use ActiveJob or at the very least an `after_save` callback to prevent it from slowing down the Rails thread that is handling the Request-Response cycle.
|
46
|
+
|
47
|
+
## Troubleshooting
|
48
|
+
|
49
|
+
If you are not receiving a citation for a specific DOI and you believe you should, you should:
|
50
|
+
|
51
|
+
1. Verify that the DOI is able to produce the BibTeX format. For exmaple: `curl -vLH 'Accept: application/x-bibtex' https://doi.org/10.3897/BDJ.9.e67426`
|
52
|
+
2. Specify `debug: true` when calling `fetch_citation` which will output the BibTex and CiteProc responses to your log file.
|
53
|
+
|
54
|
+
NOTE that errors encountered by the gem are always written to the logs regardless of the `:debug` flag specification
|
data/lib/uc3-citation.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bibtex'
|
4
|
+
require 'citeproc'
|
5
|
+
require 'csl/styles'
|
6
|
+
require 'httparty'
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
# This service provides an interface to Datacite API.
|
10
|
+
module Uc3Citation
|
11
|
+
|
12
|
+
DEFAULT_DOI_URL = 'https://doi.org'.freeze
|
13
|
+
|
14
|
+
# Create a new DOI
|
15
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
16
|
+
def fetch_citation(doi:, work_type: '', style: 'chicago-author-date', debug: false)
|
17
|
+
return nil unless doi.is_a?(String) && doi.strip != ''
|
18
|
+
|
19
|
+
# Set the logger
|
20
|
+
logger = Object.const_defined?("Rails") ? Rails.logger : Logger.new(STDOUT)
|
21
|
+
|
22
|
+
uri = doi_to_uri(doi: doi.strip)
|
23
|
+
logger.debug("Uc3Citation - Fetching BibTeX from: '#{uri}'") if debug
|
24
|
+
resp = fetch_bibtex(uri: uri)
|
25
|
+
return nil if resp.code != 200
|
26
|
+
|
27
|
+
bibtex = BibTeX.parse(resp.body)
|
28
|
+
logger.debug('Uc3Citation - Received BibTeX') if debug
|
29
|
+
logger.debug(bibtex.data.inspect) if debug
|
30
|
+
|
31
|
+
work_type = determine_work_type(bibtex: bibtex) if work_type.nil? || work_type.split == ''
|
32
|
+
|
33
|
+
citation = bibtex_to_citation(
|
34
|
+
uri: uri,
|
35
|
+
work_type: work_type,
|
36
|
+
bibtex: bibtex,
|
37
|
+
style: style
|
38
|
+
)
|
39
|
+
logger.debug('Uc3Citation - Citation accquired') if debug
|
40
|
+
logger.debug(citation) if debug
|
41
|
+
|
42
|
+
citation
|
43
|
+
rescue URI::InvalidURIError => e
|
44
|
+
logger.error("Uc3Citation - URI: '#{uri}' - InvalidURIError: #{e.message}")
|
45
|
+
nil
|
46
|
+
rescue HTTParty::Error => e
|
47
|
+
logger.error("Uc3Citation - URI: '#{uri}' - HTTPartyError: #{e.message}")
|
48
|
+
logger.error(e&.backtrace)
|
49
|
+
nil
|
50
|
+
rescue SocketError => e
|
51
|
+
logger.error("Uc3Citation - URI: '#{uri}' - CiteProc SocketError: #{e.message}")
|
52
|
+
logger.error(bibtex&.inspect)
|
53
|
+
logger.error(e&.backtrace)
|
54
|
+
nil
|
55
|
+
rescue StandardError => e
|
56
|
+
logger.error("Uc3Citation - error - #{e.class.name}: #{e.message}")
|
57
|
+
logger.error(e&.backtrace)
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Will convert 'doi:10.1234/abcdefg' to 'http://doi.org/10.1234/abcdefg'
|
65
|
+
def doi_to_uri(doi:)
|
66
|
+
return nil unless doi.is_a?(String) && doi.strip != ''
|
67
|
+
|
68
|
+
doi.start_with?('http') ? doi : "#{DEFAULT_DOI_URL}/#{doi.gsub('doi:', '')}"
|
69
|
+
end
|
70
|
+
|
71
|
+
# If no :work_type was specified we can try to derive it from the BibTeX metadata
|
72
|
+
def determine_work_type(bibtex:)
|
73
|
+
return '' if bibtex.nil? || bibtex.data.nil? || bibtex.data.first.nil?
|
74
|
+
|
75
|
+
return 'article' unless bibtex.data.first.journal.nil?
|
76
|
+
|
77
|
+
''
|
78
|
+
end
|
79
|
+
|
80
|
+
# Recursively call the URI for application/x-bibtex
|
81
|
+
def fetch_bibtex(uri:)
|
82
|
+
return nil unless uri.is_a?(String) && uri.strip != ''
|
83
|
+
|
84
|
+
# Cannot use underlying Base Service here because the Accept seems to
|
85
|
+
# be lost after multiple redirects
|
86
|
+
resp = HTTParty.get(uri, headers: { 'Accept': 'application/x-bibtex' },
|
87
|
+
follow_redirects: false)
|
88
|
+
return resp if resp.headers['location'].nil?
|
89
|
+
|
90
|
+
fetch_bibtex(uri: resp.headers['location'])
|
91
|
+
end
|
92
|
+
|
93
|
+
# Convert the BibTeX item to a citation
|
94
|
+
def bibtex_to_citation(uri:, work_type:, bibtex:, style:)
|
95
|
+
return nil unless uri.is_a?(String) && uri.strip != ''
|
96
|
+
return nil if bibtex.nil? || bibtex.data.nil? || bibtex.data.first.nil?
|
97
|
+
|
98
|
+
cp = CiteProc::Processor.new(style: style, format: 'html')
|
99
|
+
cp.import(bibtex.to_citeproc)
|
100
|
+
citation = cp.render(:bibliography, id: bibtex.data.first.id)
|
101
|
+
return nil unless citation.is_a?(Array) && citation.any?
|
102
|
+
|
103
|
+
# The CiteProc renderer has trouble with some things so fix them here
|
104
|
+
#
|
105
|
+
# - It has a '{textendash}' sometimes because it cannot render the correct char
|
106
|
+
# - For some reason words in all caps in the title get wrapped in curl brackets
|
107
|
+
# - We want to add the work type after the title. e.g. `[Dataset].`
|
108
|
+
#
|
109
|
+
citation = citation.first.gsub(/{\\Textendash}/i, '-')
|
110
|
+
.gsub('{', '').gsub('}', '')
|
111
|
+
|
112
|
+
unless work_type.nil? || work_type.strip == ''
|
113
|
+
# This supports the :apa and :chicago-author-date styles
|
114
|
+
citation = citation.gsub(/\.”\s+/, "\.” [#{work_type.gsub('_', ' ').capitalize}]. ")
|
115
|
+
.gsub(/<\/i>\.\s+/, "<\/i>\. [#{work_type.gsub('_', ' ').capitalize}]. ")
|
116
|
+
end
|
117
|
+
|
118
|
+
# Convert the URL into a link. Ensure that the trailing period is not a part of
|
119
|
+
# the link!
|
120
|
+
citation.gsub(URI.regexp) do |url|
|
121
|
+
if url.start_with?('http')
|
122
|
+
'<a href="%{url}" target="_blank">%{url}</a>.' % {
|
123
|
+
url: url.end_with?('.') ? uri : "#{uri}."
|
124
|
+
}
|
125
|
+
else
|
126
|
+
url
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uc3-citation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Riley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-09-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bibtex-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: citeproc-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: csl-styles
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: httparty
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.19'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.19'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: logger
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.4'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '11.1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '11.1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.10'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.10'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.21'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.21'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.14'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.14'
|
139
|
+
description: Send this service a DOI and receive back a citation
|
140
|
+
email:
|
141
|
+
- brian.riley@ucop.edu
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- README.md
|
147
|
+
- lib/uc3-citation.rb
|
148
|
+
- lib/uc3-citation/version.rb
|
149
|
+
homepage: https://github.com/CDLUC3/uc3-citation
|
150
|
+
licenses:
|
151
|
+
- MIT
|
152
|
+
metadata: {}
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '2.4'
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubygems_version: 3.0.3
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: UC3 - Citation Service
|
172
|
+
test_files: []
|