uc3-citation 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uc3Citation
4
+ VERSION = '0.0.5'
5
+ end
@@ -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: []