ebsco-eds 0.0.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env.test +12 -0
- data/.gitignore +20 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +72 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ebsco-eds.gemspec +47 -0
- data/lib/ebsco/eds.rb +27 -0
- data/lib/ebsco/eds/error.rb +47 -0
- data/lib/ebsco/eds/http_exception.rb +49 -0
- data/lib/ebsco/eds/info.rb +193 -0
- data/lib/ebsco/eds/jsonable.rb +28 -0
- data/lib/ebsco/eds/options.rb +339 -0
- data/lib/ebsco/eds/record.rb +700 -0
- data/lib/ebsco/eds/results.rb +375 -0
- data/lib/ebsco/eds/session.rb +624 -0
- data/lib/ebsco/eds/settings.yml +35 -0
- data/lib/ebsco/eds/version.rb +5 -0
- metadata +248 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module JSONable
|
4
|
+
module ClassMethods
|
5
|
+
attr_accessor :attributes
|
6
|
+
|
7
|
+
def attr_accessor(*attrs)
|
8
|
+
self.attributes = Array attrs
|
9
|
+
super
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
def as_json
|
18
|
+
serialized = Hash.new
|
19
|
+
self.class.attributes.each do |attribute|
|
20
|
+
serialized[attribute] = self.public_send attribute
|
21
|
+
end
|
22
|
+
serialized
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json(*a)
|
26
|
+
as_json.to_json(*a)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'ebsco/eds/jsonable'
|
2
|
+
|
3
|
+
module EBSCO
|
4
|
+
|
5
|
+
module EDS
|
6
|
+
|
7
|
+
class Options
|
8
|
+
include JSONable
|
9
|
+
attr_accessor :SearchCriteria, :RetrievalCriteria, :Actions
|
10
|
+
def initialize(options = {}, info)
|
11
|
+
|
12
|
+
@SearchCriteria = EBSCO::EDS::SearchCriteria.new(options, info)
|
13
|
+
|
14
|
+
@RetrievalCriteria = EBSCO::EDS::RetrievalCriteria.new(options, info)
|
15
|
+
|
16
|
+
@Actions = []
|
17
|
+
|
18
|
+
options.each do |key, value|
|
19
|
+
|
20
|
+
case key
|
21
|
+
|
22
|
+
when :actions
|
23
|
+
add_actions(options[:actions], info)
|
24
|
+
|
25
|
+
# solr facet translation
|
26
|
+
# "f"=>{"format"=>["eBooks"]}
|
27
|
+
when 'f'
|
28
|
+
if value.has_key?('format')
|
29
|
+
format_list = value['format']
|
30
|
+
format_list.each do |item|
|
31
|
+
@Actions.push "addfacetfilter(SourceType:#{item})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
if value.has_key?('language_facet')
|
35
|
+
lang_list = value['language_facet']
|
36
|
+
lang_list.each do |item|
|
37
|
+
@Actions.push "addfacetfilter(Language:#{item})"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
if value.has_key?('subject_topic_facet')
|
41
|
+
subj_list = value['subject_topic_facet']
|
42
|
+
subj_list.each do |item|
|
43
|
+
@Actions.push "addfacetfilter(SubjectEDS:#{item})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
puts 'ACTIONS: ' + @Actions.inspect
|
48
|
+
|
49
|
+
else
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_actions(actions, info)
|
57
|
+
if actions.kind_of?(Array) && actions.count > 0
|
58
|
+
actions.each do |item|
|
59
|
+
#if is_valid_action(item, info)
|
60
|
+
@Actions.push item
|
61
|
+
#end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
#if is_valid_action(actions, info)
|
65
|
+
@Actions = [actions]
|
66
|
+
#else
|
67
|
+
#end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# def is_valid_action(action, info)
|
72
|
+
# # actions in info that require an enumerated value (e.g., addlimiter(LA99:Bulgarian))
|
73
|
+
# _available_actions = info.available_actions
|
74
|
+
# _defined_action = _available_actions.include? action
|
75
|
+
# # actions not enumerated in info (e.g., GoToPage(3))
|
76
|
+
# _available_standard_actions = ['GoToPage']
|
77
|
+
# _standard_action = _available_standard_actions.any? { |std| action.include? std }
|
78
|
+
# # actions in info that require a user supplied value (e.g., addlimiter(TI:value))
|
79
|
+
# _available_value_actions = %w{PG4 CS1 FM FT FR RV DT1 SO}
|
80
|
+
# _value_action = _available_value_actions.any? { |type| action.include? type }
|
81
|
+
#
|
82
|
+
# if _value_action || _defined_action || _standard_action
|
83
|
+
# true
|
84
|
+
# else
|
85
|
+
# false
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class SearchCriteria
|
92
|
+
include JSONable
|
93
|
+
attr_accessor :Queries, :SearchMode, :IncludeFacets, :FacetFilters, :Limiters, :Sort, :PublicationId,
|
94
|
+
:RelatedContent, :AutoSuggest, :Expanders
|
95
|
+
|
96
|
+
def initialize(options = {}, info)
|
97
|
+
|
98
|
+
# defaults
|
99
|
+
@SearchMode = info.default_search_mode
|
100
|
+
@IncludeFacets = 'y'
|
101
|
+
@Sort = 'relevance'
|
102
|
+
@AutoSuggest = info.default_auto_suggest
|
103
|
+
_has_query = false
|
104
|
+
|
105
|
+
@Expanders = info.default_expander_ids
|
106
|
+
_my_expanders = []
|
107
|
+
_available_expander_ids = info.available_expander_ids
|
108
|
+
|
109
|
+
@Limiter = nil
|
110
|
+
_my_limiters = []
|
111
|
+
|
112
|
+
@RelatedContent = info.default_related_content_types
|
113
|
+
_my_related_content = []
|
114
|
+
_available_related_content_types = info.available_related_content_types
|
115
|
+
|
116
|
+
options.each do |key, value|
|
117
|
+
|
118
|
+
case key
|
119
|
+
|
120
|
+
# ====================================================================================
|
121
|
+
# query
|
122
|
+
# ====================================================================================
|
123
|
+
when :query, 'q'
|
124
|
+
|
125
|
+
# add blacklight search_fields
|
126
|
+
_field_code = ''
|
127
|
+
if options.has_key? 'search_field'
|
128
|
+
_field = options['search_field']
|
129
|
+
case _field
|
130
|
+
when 'author'
|
131
|
+
_field_code = 'AU'
|
132
|
+
when 'subject'
|
133
|
+
_field_code = 'SU'
|
134
|
+
when 'title'
|
135
|
+
_field_code = 'TI'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
if not _field_code == ''
|
140
|
+
@Queries = [{:FieldCode => _field_code, :Term => value}]
|
141
|
+
else
|
142
|
+
@Queries = [{:Term => value}]
|
143
|
+
end
|
144
|
+
_has_query = true
|
145
|
+
|
146
|
+
# ====================================================================================
|
147
|
+
# mode
|
148
|
+
# ====================================================================================
|
149
|
+
when :mode
|
150
|
+
if info.available_search_mode_types.include? value.downcase
|
151
|
+
@SearchMode = value.downcase
|
152
|
+
else
|
153
|
+
@SearchMode = info.default_search_mode
|
154
|
+
end
|
155
|
+
|
156
|
+
# ====================================================================================
|
157
|
+
# facets
|
158
|
+
# ====================================================================================
|
159
|
+
when :include_facets
|
160
|
+
@IncludeFacets = value ? 'y' : 'n'
|
161
|
+
|
162
|
+
when :facet_filters
|
163
|
+
@FacetFilters = value
|
164
|
+
|
165
|
+
# ====================================================================================
|
166
|
+
# sort
|
167
|
+
# ====================================================================================
|
168
|
+
when :sort, 'sort'
|
169
|
+
if info.available_sorts(value.downcase).empty?
|
170
|
+
if value.downcase == 'newest' || value.downcase == 'pub_date_sort desc'
|
171
|
+
@Sort = 'date'
|
172
|
+
elsif value.downcase == 'oldest' || value.downcase == 'pub_date_sort asc'
|
173
|
+
@Sort = 'date2'
|
174
|
+
elsif value.downcase == 'score desc'
|
175
|
+
@Sort = 'relevance'
|
176
|
+
else
|
177
|
+
@Sort = 'relevance'
|
178
|
+
end
|
179
|
+
else
|
180
|
+
@Sort = value.downcase
|
181
|
+
end
|
182
|
+
|
183
|
+
# ====================================================================================
|
184
|
+
# publication id
|
185
|
+
# ====================================================================================
|
186
|
+
when :publication_id
|
187
|
+
@PublicationId = value
|
188
|
+
|
189
|
+
when :auto_suggest
|
190
|
+
@AutoSuggest = value ? 'y' : 'n'
|
191
|
+
|
192
|
+
# ====================================================================================
|
193
|
+
# expanders
|
194
|
+
# ====================================================================================
|
195
|
+
when :expanders
|
196
|
+
value.each do |item|
|
197
|
+
if _available_expander_ids.include? item.downcase
|
198
|
+
_my_expanders.push(item)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
if _my_expanders.empty?
|
202
|
+
_my_expanders = info.default_expander_ids
|
203
|
+
end
|
204
|
+
@Expanders = _my_expanders
|
205
|
+
|
206
|
+
# ====================================================================================
|
207
|
+
# limiters
|
208
|
+
# ====================================================================================
|
209
|
+
when :limiters
|
210
|
+
value.each do |item|
|
211
|
+
_key = item.split(':',2).first.upcase
|
212
|
+
_values = item.split(':',2).last
|
213
|
+
# is limiter id available?
|
214
|
+
if info.available_limiter_ids.include? _key
|
215
|
+
_limiter = info.available_limiters(_key)
|
216
|
+
# if multi-value, add the values if they're available
|
217
|
+
if _limiter['Type'] == 'multiselectvalue'
|
218
|
+
_available_values = info.available_limiter_values(_key)
|
219
|
+
_multi_values = []
|
220
|
+
_values.split(',').each do |val|
|
221
|
+
# todo: make case insensitive?
|
222
|
+
if _available_values.include? val
|
223
|
+
_multi_values.push(val)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
if _multi_values.empty?
|
227
|
+
# do nothing, none of the values are available
|
228
|
+
else
|
229
|
+
_my_limiters.push({:Id => _key, :Values => _multi_values})
|
230
|
+
end
|
231
|
+
# single value, just pass it on
|
232
|
+
else
|
233
|
+
_my_limiters.push({:Id => _key, :Values => [_values]})
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
@Limiters = _my_limiters
|
238
|
+
|
239
|
+
# ====================================================================================
|
240
|
+
# related content
|
241
|
+
# ====================================================================================
|
242
|
+
when :related_content
|
243
|
+
value.each do |item|
|
244
|
+
if _available_related_content_types.include? item.downcase
|
245
|
+
_available_related_content_types.push(item)
|
246
|
+
else
|
247
|
+
# silently ignore
|
248
|
+
end
|
249
|
+
end
|
250
|
+
if _my_related_content.empty?
|
251
|
+
_my_related_content = info.default_related_content_types
|
252
|
+
end
|
253
|
+
@RelatedContent = _my_related_content
|
254
|
+
|
255
|
+
# ====================================================================================
|
256
|
+
# unsupported parameters, ignore
|
257
|
+
# ====================================================================================
|
258
|
+
else
|
259
|
+
# ignore
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
class RetrievalCriteria
|
270
|
+
include JSONable
|
271
|
+
attr_accessor :View, :ResultsPerPage, :PageNumber, :Highlight
|
272
|
+
def initialize(options = {}, info)
|
273
|
+
|
274
|
+
# defaults
|
275
|
+
@View = info.default_result_list_view
|
276
|
+
@ResultsPerPage = info.default_results_per_page
|
277
|
+
@PageNumber = 1
|
278
|
+
|
279
|
+
options.each do |key, value|
|
280
|
+
|
281
|
+
case key
|
282
|
+
|
283
|
+
# ====================================================================================
|
284
|
+
# view
|
285
|
+
# ====================================================================================
|
286
|
+
when :view
|
287
|
+
if info.available_result_list_views.include? value.downcase
|
288
|
+
@View = value.downcase
|
289
|
+
else
|
290
|
+
@View = info.default_result_list_view
|
291
|
+
end
|
292
|
+
|
293
|
+
# ====================================================================================
|
294
|
+
# results per page
|
295
|
+
# ====================================================================================
|
296
|
+
when :results_per_page, 'rows', 'per_page'
|
297
|
+
if value.to_i > info.max_results_per_page
|
298
|
+
@ResultsPerPage = info.max_results_per_page
|
299
|
+
else
|
300
|
+
@ResultsPerPage = value.to_i
|
301
|
+
end
|
302
|
+
|
303
|
+
# ====================================================================================
|
304
|
+
# page number
|
305
|
+
# ====================================================================================
|
306
|
+
when :page_number, 'page'
|
307
|
+
@PageNumber = value.to_i
|
308
|
+
# puts 'NEW PAGE: ' + @PageNumber.inspect
|
309
|
+
# solr starts at page 0
|
310
|
+
when 'start'
|
311
|
+
@PageNumber = value.to_i + 1
|
312
|
+
|
313
|
+
# ====================================================================================
|
314
|
+
# highlight
|
315
|
+
# ====================================================================================
|
316
|
+
when :highlight
|
317
|
+
@Highlight = value ? 'y' : 'n'
|
318
|
+
# solr/blacklight version
|
319
|
+
when 'hl'
|
320
|
+
if value == 'on'
|
321
|
+
@Highlight = 'y'
|
322
|
+
else
|
323
|
+
@Highlight = 'n'
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
else
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
end
|
@@ -0,0 +1,700 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'bibtex'
|
4
|
+
|
5
|
+
module EBSCO
|
6
|
+
|
7
|
+
module EDS
|
8
|
+
|
9
|
+
# A single search result
|
10
|
+
class Record
|
11
|
+
|
12
|
+
# Raw record as returned by the \EDS API via search or retrieve
|
13
|
+
attr_reader :record
|
14
|
+
|
15
|
+
# Lookup table of databases that have their labels suppressed in the response.
|
16
|
+
DBS = YAML::load_file(File.join(__dir__, 'settings.yml'))['databases']
|
17
|
+
|
18
|
+
# Creates a search or retrieval result record
|
19
|
+
def initialize(results_record)
|
20
|
+
|
21
|
+
if results_record.key? 'Record'
|
22
|
+
@record = results_record['Record'] # single record returned by retrieve api
|
23
|
+
else
|
24
|
+
@record = results_record # set of records returned by search api
|
25
|
+
end
|
26
|
+
|
27
|
+
@items = @record.fetch('Items', {})
|
28
|
+
|
29
|
+
@bib_entity = @record.fetch('RecordInfo', {})
|
30
|
+
.fetch('BibRecord', {})
|
31
|
+
.fetch('BibEntity', {})
|
32
|
+
|
33
|
+
@bib_relationships = @record.fetch('RecordInfo', {})
|
34
|
+
.fetch('BibRecord', {})
|
35
|
+
.fetch('BibRelationships', {})
|
36
|
+
|
37
|
+
@bib_part = @record.fetch('RecordInfo', {})
|
38
|
+
.fetch('BibRecord', {})
|
39
|
+
.fetch('BibRelationships', {})
|
40
|
+
.fetch('IsPartOfRelationships', {})[0]
|
41
|
+
|
42
|
+
@bibtex = BibTeX::Entry.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# \Options hash containing accession number and database ID. This can be passed to the retrieve method.
|
46
|
+
def retrieve_options
|
47
|
+
options = {}
|
48
|
+
options['an'] = self.accession_number
|
49
|
+
options['dbid'] = self.database_id
|
50
|
+
options
|
51
|
+
end
|
52
|
+
|
53
|
+
# The accession number.
|
54
|
+
def accession_number
|
55
|
+
header_an
|
56
|
+
end
|
57
|
+
|
58
|
+
# The database ID.
|
59
|
+
def database_id
|
60
|
+
header_db_id
|
61
|
+
end
|
62
|
+
|
63
|
+
# The database name or label.
|
64
|
+
def database_name
|
65
|
+
header_db_label
|
66
|
+
end
|
67
|
+
|
68
|
+
# The access level.
|
69
|
+
def access_level
|
70
|
+
header_access_level
|
71
|
+
end
|
72
|
+
|
73
|
+
# The search relevancy score.
|
74
|
+
def relevancy_score
|
75
|
+
header_score
|
76
|
+
end
|
77
|
+
|
78
|
+
# The title.
|
79
|
+
def title
|
80
|
+
get_item_data_by_name('Title') || bib_title
|
81
|
+
end
|
82
|
+
|
83
|
+
# The source title (e.g., Journal)
|
84
|
+
def source_title
|
85
|
+
bib_source_title || get_item_data_by_name('TitleSource')
|
86
|
+
end
|
87
|
+
|
88
|
+
# Other alternative titles.
|
89
|
+
def other_titles
|
90
|
+
get_item_data_by_name('TitleAlt')
|
91
|
+
end
|
92
|
+
|
93
|
+
# The abstract
|
94
|
+
def abstract
|
95
|
+
get_item_data_by_name('Abstract')
|
96
|
+
end
|
97
|
+
|
98
|
+
# The list of authors
|
99
|
+
def authors
|
100
|
+
bib_authors || get_item_data_by_name('Author')
|
101
|
+
end
|
102
|
+
|
103
|
+
# The author affiliations
|
104
|
+
def author_affiliations
|
105
|
+
get_item_data_by_name('AffiliationAuthor')
|
106
|
+
end
|
107
|
+
|
108
|
+
# The list of subject terms.
|
109
|
+
def subjects
|
110
|
+
bib_subjects || get_item_data_by_name('Subject')
|
111
|
+
end
|
112
|
+
|
113
|
+
# The list of geographic subjects
|
114
|
+
def subjects_geographic
|
115
|
+
get_item_data_by_name('SubjectGeographic')
|
116
|
+
end
|
117
|
+
|
118
|
+
# The list of person subjects
|
119
|
+
def subjects_person
|
120
|
+
get_item_data_by_name('SubjectPerson')
|
121
|
+
end
|
122
|
+
|
123
|
+
# Author supplied keywords
|
124
|
+
def author_supplied_keywords
|
125
|
+
get_item_data_by_label('Author-Supplied Keywords')
|
126
|
+
end
|
127
|
+
|
128
|
+
# Notes
|
129
|
+
def notes
|
130
|
+
get_item_data_by_name('Note')
|
131
|
+
end
|
132
|
+
|
133
|
+
# Languages
|
134
|
+
def languages
|
135
|
+
get_item_data_by_name('Language') || bib_languages
|
136
|
+
end
|
137
|
+
|
138
|
+
# Total number of pages.
|
139
|
+
def page_count
|
140
|
+
bib_page_count
|
141
|
+
end
|
142
|
+
|
143
|
+
# Starting page number.
|
144
|
+
def page_start
|
145
|
+
bib_page_start
|
146
|
+
end
|
147
|
+
|
148
|
+
# Physical description.
|
149
|
+
def physical_description
|
150
|
+
get_item_data_by_name('PhysDesc')
|
151
|
+
end
|
152
|
+
|
153
|
+
# Publication type.
|
154
|
+
def publication_type
|
155
|
+
header_publication_type || get_item_data_by_name('TypePub')
|
156
|
+
end
|
157
|
+
|
158
|
+
# Publication type ID.
|
159
|
+
def publication_type_id
|
160
|
+
header_publication_type_id
|
161
|
+
end
|
162
|
+
|
163
|
+
# Publication date.
|
164
|
+
def publication_date
|
165
|
+
bib_publication_date || get_item_data_by_name('DatePub')
|
166
|
+
end
|
167
|
+
|
168
|
+
# Publication year.
|
169
|
+
def publication_year
|
170
|
+
bib_publication_year || get_item_data_by_name('DatePub')
|
171
|
+
end
|
172
|
+
|
173
|
+
# Publisher information.
|
174
|
+
def publisher_info
|
175
|
+
get_item_data_by_label('Publication Information')
|
176
|
+
end
|
177
|
+
|
178
|
+
# Document type.
|
179
|
+
def document_type
|
180
|
+
get_item_data_by_name('TypeDocument')
|
181
|
+
end
|
182
|
+
|
183
|
+
# DOI identifier.
|
184
|
+
def doi
|
185
|
+
get_item_data_by_name('DOI') || bib_doi
|
186
|
+
end
|
187
|
+
|
188
|
+
# OCLC identifier.
|
189
|
+
def oclc
|
190
|
+
get_item_data_by_label('OCLC')
|
191
|
+
end
|
192
|
+
|
193
|
+
# Prind ISSN
|
194
|
+
def issn_print
|
195
|
+
get_item_data_by_name('ISSN') || bib_issn_print
|
196
|
+
end
|
197
|
+
|
198
|
+
# List of ISSNs
|
199
|
+
def issns
|
200
|
+
bib_issns
|
201
|
+
end
|
202
|
+
|
203
|
+
# List of ISBNs
|
204
|
+
def isbns
|
205
|
+
bib_isbns | item_related_isbns
|
206
|
+
end
|
207
|
+
|
208
|
+
# Print ISBN
|
209
|
+
def isbn_print
|
210
|
+
bib_isbn_print
|
211
|
+
end
|
212
|
+
|
213
|
+
# Electronic ISBN
|
214
|
+
def isbn_electronic
|
215
|
+
bib_isbn_electronic
|
216
|
+
end
|
217
|
+
|
218
|
+
# Series information.
|
219
|
+
def series
|
220
|
+
get_item_data_by_name('SeriesInfo')
|
221
|
+
end
|
222
|
+
|
223
|
+
# Volume
|
224
|
+
def volume
|
225
|
+
bib_volume
|
226
|
+
end
|
227
|
+
|
228
|
+
# Issue
|
229
|
+
def issue
|
230
|
+
bib_issue
|
231
|
+
end
|
232
|
+
|
233
|
+
# Cover images
|
234
|
+
def covers
|
235
|
+
images
|
236
|
+
end
|
237
|
+
|
238
|
+
# Cover image - thumbnail size link
|
239
|
+
def cover_thumb_url
|
240
|
+
images('thumb').first[:src]
|
241
|
+
end
|
242
|
+
|
243
|
+
# Cover image - medium size link
|
244
|
+
def cover_medium_url
|
245
|
+
images('medium').first[:src]
|
246
|
+
end
|
247
|
+
|
248
|
+
# Word count for fulltext.
|
249
|
+
def fulltext_word_count
|
250
|
+
get_item_data_by_name('FullTextWordCount').to_i
|
251
|
+
end
|
252
|
+
|
253
|
+
# --
|
254
|
+
# ====================================================================================
|
255
|
+
# GENERAL: ResultId, PLink, ImageInfo, CustomLinks, FullText
|
256
|
+
# ====================================================================================
|
257
|
+
# ++
|
258
|
+
|
259
|
+
# Result ID.
|
260
|
+
def result_id
|
261
|
+
@record['ResultId']
|
262
|
+
end
|
263
|
+
|
264
|
+
# EBSCO's persistent link.
|
265
|
+
def plink
|
266
|
+
@record['PLink']
|
267
|
+
end
|
268
|
+
|
269
|
+
# Fulltext.
|
270
|
+
def html_fulltext
|
271
|
+
if @record.fetch('FullText',{}).fetch('Text',{}).fetch('Availability',0) == '1'
|
272
|
+
@record.fetch('FullText',{}).fetch('Text',{})['Value']
|
273
|
+
else
|
274
|
+
nil
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# List of cover images.
|
279
|
+
def images (size_requested = 'all')
|
280
|
+
returned_images = []
|
281
|
+
images = @record.fetch('ImageInfo', {})
|
282
|
+
if images.count > 0
|
283
|
+
images.each do |image|
|
284
|
+
if size_requested == image['Size'] || size_requested == 'all'
|
285
|
+
returned_images.push({size: image['Size'], src: image['Target']})
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
returned_images
|
290
|
+
end
|
291
|
+
|
292
|
+
# --
|
293
|
+
# ====================================================================================
|
294
|
+
# LINK HELPERS
|
295
|
+
# ====================================================================================
|
296
|
+
# ++
|
297
|
+
|
298
|
+
# A list of all available links.
|
299
|
+
def all_links
|
300
|
+
self.fulltext_links + self.non_fulltext_links
|
301
|
+
end
|
302
|
+
|
303
|
+
# The first fulltext link.
|
304
|
+
def fulltext_link
|
305
|
+
self.fulltext_links.first || {}
|
306
|
+
end
|
307
|
+
|
308
|
+
# All available fulltext links.
|
309
|
+
def fulltext_links
|
310
|
+
|
311
|
+
links = []
|
312
|
+
|
313
|
+
ebscolinks = @record.fetch('FullText',{}).fetch('Links',{})
|
314
|
+
if ebscolinks.count > 0
|
315
|
+
ebscolinks.each do |ebscolink|
|
316
|
+
if ebscolink['Type'] == 'pdflink'
|
317
|
+
link_label = 'PDF Full Text'
|
318
|
+
link_icon = 'PDF Full Text Icon'
|
319
|
+
link_url = ebscolink['Url'] || 'detail'
|
320
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'pdf'})
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
htmlfulltextcheck = @record.fetch('FullText',{}).fetch('Text',{}).fetch('Availability',{})
|
326
|
+
if htmlfulltextcheck == '1'
|
327
|
+
link_url = 'detail'
|
328
|
+
link_label = 'Full Text in Browser'
|
329
|
+
link_icon = 'Full Text in Browser Icon'
|
330
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'html'})
|
331
|
+
end
|
332
|
+
|
333
|
+
if ebscolinks.count > 0
|
334
|
+
ebscolinks.each do |ebscolink|
|
335
|
+
if ebscolink['Type'] == 'ebook-pdf'
|
336
|
+
link_label = 'PDF eBook Full Text'
|
337
|
+
link_icon = 'PDF eBook Full Text Icon'
|
338
|
+
link_url = ebscolink['Url'] || 'detail'
|
339
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'ebook-pdf'})
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
if ebscolinks.count > 0
|
345
|
+
ebscolinks.each do |ebscolink|
|
346
|
+
if ebscolink['Type'] == 'ebook-epub'
|
347
|
+
link_label = 'ePub eBook Full Text'
|
348
|
+
link_icon = 'ePub eBook Full Text Icon'
|
349
|
+
link_url = ebscolink['Url'] || 'detail'
|
350
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'ebook-epub'})
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
items = @record.fetch('Items',{})
|
356
|
+
if items.count > 0
|
357
|
+
items.each do |item|
|
358
|
+
if item['Group'] == 'Url'
|
359
|
+
if item['Data'].include? 'linkTerm="'
|
360
|
+
link_start = item['Data'].index('linkTerm="')+15
|
361
|
+
link_url = item['Data'][link_start..-1]
|
362
|
+
link_end = link_url.index('"')-1
|
363
|
+
link_url = link_url[0..link_end]
|
364
|
+
link_label_start = item['Data'].index('link>')+8
|
365
|
+
link_label = item['Data'][link_label_start..-1]
|
366
|
+
link_label = link_label.strip
|
367
|
+
else
|
368
|
+
link_url = item['Data']
|
369
|
+
link_label = item['Label']
|
370
|
+
end
|
371
|
+
link_icon = 'Catalog Link Icon'
|
372
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'cataloglink'})
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
if ebscolinks.count > 0
|
378
|
+
ebscolinks.each do |ebscolink|
|
379
|
+
if ebscolink['Type'] == 'other'
|
380
|
+
link_label = 'Linked Full Text'
|
381
|
+
link_icon = 'Linked Full Text Icon'
|
382
|
+
link_url = ebscolink['Url'] || 'detail'
|
383
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'smartlinks+'})
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
ft_customlinks = @record.fetch('FullText',{}).fetch('CustomLinks',{})
|
389
|
+
if ft_customlinks.count > 0
|
390
|
+
ft_customlinks.each do |ft_customlink|
|
391
|
+
link_url = ft_customlink['Url']
|
392
|
+
link_label = ft_customlink['Text']
|
393
|
+
link_icon = ft_customlink['Icon']
|
394
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'customlink-fulltext'})
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
links
|
399
|
+
end
|
400
|
+
|
401
|
+
# All available non-fulltext links.
|
402
|
+
def non_fulltext_links
|
403
|
+
links = []
|
404
|
+
other_customlinks = @record.fetch('CustomLinks',{})
|
405
|
+
if other_customlinks.count > 0
|
406
|
+
other_customlinks.each do |other_customlink|
|
407
|
+
link_url = other_customlink['Url']
|
408
|
+
link_label = other_customlink['Text']
|
409
|
+
link_icon = other_customlink['Icon']
|
410
|
+
links.push({url: link_url, label: link_label, icon: link_icon, type: 'customlink-other'})
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
links
|
415
|
+
end
|
416
|
+
|
417
|
+
#:nodoc: all
|
418
|
+
# No need to document methods below
|
419
|
+
|
420
|
+
# Experimental bibtex support.
|
421
|
+
def retrieve_bibtex
|
422
|
+
|
423
|
+
@bibtex.key = self.accession_number
|
424
|
+
@bibtex.title = self.title
|
425
|
+
if self.bib_authors_list.length > 0
|
426
|
+
@bibtex.author = self.bib_authors_list.join(' and ').chomp
|
427
|
+
end
|
428
|
+
@bibtex.year = self.publication_year.to_i
|
429
|
+
|
430
|
+
# bibtex type
|
431
|
+
_type = self.publication_type
|
432
|
+
case _type
|
433
|
+
when 'Academic Journal'
|
434
|
+
@bibtex.type = :article
|
435
|
+
@bibtex.journal = self.source_title
|
436
|
+
if self.issue
|
437
|
+
@bibtex.issue = self.issue
|
438
|
+
end
|
439
|
+
if self.volume
|
440
|
+
@bibtex.number = self.volume
|
441
|
+
end
|
442
|
+
if self.page_start && self.page_count
|
443
|
+
@bibtex.pages = self.page_start + '-' + (self.page_start.to_i + self.page_count.to_i-1).to_s
|
444
|
+
end
|
445
|
+
if self.bib_publication_month
|
446
|
+
@bibtex.month = self.bib_publication_month.to_i
|
447
|
+
end
|
448
|
+
when 'Conference'
|
449
|
+
@bibtex.type = :conference
|
450
|
+
@bibtex.booktitle = self.source_title
|
451
|
+
if self.issue
|
452
|
+
@bibtex.issue = self.issue
|
453
|
+
end
|
454
|
+
if self.volume
|
455
|
+
@bibtex.number = self.volume
|
456
|
+
end
|
457
|
+
if self.page_start && self.page_count
|
458
|
+
@bibtex.pages = self.page_start + '-' + (self.page_start.to_i + self.page_count.to_i-1).to_s
|
459
|
+
end
|
460
|
+
if self.bib_publication_month
|
461
|
+
@bibtex.month = self.bib_publication_month.to_i
|
462
|
+
end
|
463
|
+
if self.publisher_info
|
464
|
+
@bibtex.publisher = self.publisher_info
|
465
|
+
end
|
466
|
+
if self.series
|
467
|
+
@bibtex.series = self.series
|
468
|
+
end
|
469
|
+
when 'Book', 'eBook'
|
470
|
+
@bibtex.type = :book
|
471
|
+
if self.publisher_info
|
472
|
+
@bibtex.publisher = self.publisher_info
|
473
|
+
end
|
474
|
+
if self.series
|
475
|
+
@bibtex.series = self.series
|
476
|
+
end
|
477
|
+
if self.bib_publication_month
|
478
|
+
@bibtex.month = self.bib_publication_month.to_i
|
479
|
+
end
|
480
|
+
if self.isbns
|
481
|
+
@bibtex.isbn = self.isbns.first
|
482
|
+
end
|
483
|
+
else
|
484
|
+
@bibtex.type = :other
|
485
|
+
end
|
486
|
+
@bibtex
|
487
|
+
end
|
488
|
+
|
489
|
+
# ====================================================================================
|
490
|
+
# HEADER: DbId, DbLabel, An, PubType, PubTypeId, AccessLevel
|
491
|
+
# ====================================================================================
|
492
|
+
|
493
|
+
def header_an
|
494
|
+
@record['Header']['An'].to_s
|
495
|
+
end
|
496
|
+
|
497
|
+
def header_db_id
|
498
|
+
@record['Header']['DbId'].to_s
|
499
|
+
end
|
500
|
+
|
501
|
+
# only available from search not retrieve
|
502
|
+
def header_score
|
503
|
+
@record['Header']['RelevancyScore']
|
504
|
+
end
|
505
|
+
|
506
|
+
def header_publication_type
|
507
|
+
@record['Header']['PubType']
|
508
|
+
end
|
509
|
+
|
510
|
+
def header_publication_type_id
|
511
|
+
@record['Header']['PubTypeId']
|
512
|
+
end
|
513
|
+
|
514
|
+
def header_db_label
|
515
|
+
DBS[self.database_id.upcase] || @record['Header']['DbLabel']
|
516
|
+
end
|
517
|
+
|
518
|
+
# not sure the rules for when this appears or not - RecordInfo.AccessInfo?
|
519
|
+
def header_access_level
|
520
|
+
@record['Header']['AccessLevel']
|
521
|
+
end
|
522
|
+
|
523
|
+
# ====================================================================================
|
524
|
+
# ITEMS
|
525
|
+
# ====================================================================================
|
526
|
+
|
527
|
+
# look up by 'Name' and return 'Data'
|
528
|
+
def get_item_data_by_name(name)
|
529
|
+
_item_property = @items.find{|item| item['Name'] == name}
|
530
|
+
if _item_property.nil?
|
531
|
+
nil
|
532
|
+
else
|
533
|
+
_item_property['Data']
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
# look up by 'Label' and return 'Data'
|
538
|
+
def get_item_data_by_label(label)
|
539
|
+
_item_property = @items.find{|item| item['Label'] == label}
|
540
|
+
if _item_property.nil?
|
541
|
+
nil
|
542
|
+
else
|
543
|
+
_item_property['Data']
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
def item_related_isbns
|
548
|
+
get_item_data_by_label('Related ISBNs').split(' ').map!{|item| item.gsub(/\.$/, '')}
|
549
|
+
end
|
550
|
+
|
551
|
+
# ====================================================================================
|
552
|
+
# BIB ENTITY
|
553
|
+
# ====================================================================================
|
554
|
+
|
555
|
+
def bib_title
|
556
|
+
@bib_entity.fetch('Titles', {}).find{|item| item['Type'] == 'main'}['TitleFull']
|
557
|
+
end
|
558
|
+
|
559
|
+
def bib_authors
|
560
|
+
@bib_relationships.deep_find('NameFull').join('; ')
|
561
|
+
end
|
562
|
+
|
563
|
+
def bib_authors_list
|
564
|
+
@bib_relationships.deep_find('NameFull')
|
565
|
+
end
|
566
|
+
|
567
|
+
def bib_subjects
|
568
|
+
@bib_entity.deep_find('SubjectFull')
|
569
|
+
end
|
570
|
+
|
571
|
+
def bib_languages
|
572
|
+
@bib_entity.fetch('Languages', {}).map{|lang| lang['Text']}
|
573
|
+
end
|
574
|
+
|
575
|
+
# def bib_pages
|
576
|
+
# @bib_entity.fetch('PhysicalDescription', {})['Pagination']
|
577
|
+
# end
|
578
|
+
|
579
|
+
def bib_page_count
|
580
|
+
@bib_entity.deep_find('PageCount').first
|
581
|
+
end
|
582
|
+
|
583
|
+
def bib_page_start
|
584
|
+
@bib_entity.deep_find('StartPage').first
|
585
|
+
end
|
586
|
+
|
587
|
+
def bib_doi
|
588
|
+
@bib_entity.fetch('Identifiers',{}).find{|item| item['Type'] == 'doi'}['Value']
|
589
|
+
end
|
590
|
+
|
591
|
+
# ====================================================================================
|
592
|
+
# BIB - IS PART OF (journal, book)
|
593
|
+
# ====================================================================================
|
594
|
+
|
595
|
+
def bib_source_title
|
596
|
+
@bib_part.fetch('BibEntity',{}).fetch('Titles',{}).find{|item| item['Type'] == 'main'}['TitleFull']
|
597
|
+
end
|
598
|
+
|
599
|
+
def bib_issn_print
|
600
|
+
@bib_part.fetch('BibEntity',{}).fetch('Identifiers',{}).find{|item| item['Type'] == 'issn-print'}['Value']
|
601
|
+
end
|
602
|
+
|
603
|
+
def bib_issn_electronic
|
604
|
+
@bib_part.fetch('BibEntity',{}).fetch('Identifiers',{}).find{|item| item['Type'] == 'issn-electronic'}['Value']
|
605
|
+
end
|
606
|
+
|
607
|
+
def bib_issns
|
608
|
+
issns = []
|
609
|
+
@bib_part.fetch('BibEntity',{}).fetch('Identifiers',{}).each do |id|
|
610
|
+
if id['Type'].include?('issn') && !id['Type'].include?('locals')
|
611
|
+
issns.push(id['Value'])
|
612
|
+
end
|
613
|
+
end
|
614
|
+
issns
|
615
|
+
end
|
616
|
+
|
617
|
+
def bib_isbn_print
|
618
|
+
@bib_part.fetch('BibEntity',{}).fetch('Identifiers',{}).find{|item| item['Type'] == 'isbn-print'}['Value']
|
619
|
+
end
|
620
|
+
|
621
|
+
def bib_isbn_electronic
|
622
|
+
@bib_part.fetch('BibEntity',{}).fetch('Identifiers',{}).find{|item| item['Type'] == 'isbn-electronic'}['Value']
|
623
|
+
end
|
624
|
+
|
625
|
+
# todo: make this generic and take an optional parameter for type
|
626
|
+
def bib_isbns
|
627
|
+
isbns = []
|
628
|
+
@bib_part.fetch('BibEntity',{}).fetch('Identifiers',{}).each do |id|
|
629
|
+
if id['Type'].include?('isbn') && !id['Type'].include?('locals')
|
630
|
+
isbns.push(id['Value'])
|
631
|
+
end
|
632
|
+
end
|
633
|
+
isbns
|
634
|
+
end
|
635
|
+
|
636
|
+
def bib_publication_date
|
637
|
+
_date = @bib_part.fetch('BibEntity',{}).fetch('Dates',{}).find{|item| item['Type'] == 'published'}
|
638
|
+
if _date
|
639
|
+
if _date.has_key?('Y') && _date.has_key?('M') && _date.has_key?('D')
|
640
|
+
_date['Y'] + '-' + _date['M'] + '-' + _date['D']
|
641
|
+
else
|
642
|
+
nil
|
643
|
+
end
|
644
|
+
else
|
645
|
+
nil
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
def bib_publication_year
|
650
|
+
_date = @bib_part.fetch('BibEntity',{}).fetch('Dates',{}).find{|item| item['Type'] == 'published'}
|
651
|
+
if _date
|
652
|
+
_date.has_key?('Y') ? _date['Y'] : nil
|
653
|
+
else
|
654
|
+
nil
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
def bib_publication_month
|
659
|
+
_date = @bib_part.fetch('BibEntity',{}).fetch('Dates',{}).find{|item| item['Type'] == 'published'}
|
660
|
+
if _date
|
661
|
+
_date.has_key?('M') ? _date['M'] : nil
|
662
|
+
else
|
663
|
+
nil
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
def bib_volume
|
668
|
+
@bib_part.fetch('BibEntity',{}).fetch('Numbering',{}).find{|item| item['Type'] == 'volume'}['Value']
|
669
|
+
end
|
670
|
+
|
671
|
+
def bib_issue
|
672
|
+
@bib_part.fetch('BibEntity',{}).fetch('Numbering',{}).find{|item| item['Type'] == 'issue'}['Value']
|
673
|
+
end
|
674
|
+
|
675
|
+
def to_hash
|
676
|
+
hash = {}
|
677
|
+
hash['id'] = database_id + '-' + accession_number
|
678
|
+
hash['title_display'] = title.gsub('<highlight>', '').gsub('</highlight>', '')
|
679
|
+
hash['pub_date'] = publication_year
|
680
|
+
hash
|
681
|
+
end
|
682
|
+
|
683
|
+
end # Class Record
|
684
|
+
end # Module EDS
|
685
|
+
end # Module EBSCO
|
686
|
+
|
687
|
+
|
688
|
+
# monkey patches
|
689
|
+
class Hash
|
690
|
+
def deep_find(key, object=self, found=[])
|
691
|
+
if object.respond_to?(:key?) && object.key?(key)
|
692
|
+
found << object[key]
|
693
|
+
end
|
694
|
+
if object.is_a? Enumerable
|
695
|
+
found << object.collect { |*a| deep_find(key, a.last) }
|
696
|
+
end
|
697
|
+
found.flatten.compact
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|