umlaut 3.0.0beta5 → 3.0.0beta6
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.
- data/app/views/admin/service_errors/_dispatched_service.html.erb +4 -2
- data/app/views/admin/service_errors/index.html.erb +6 -1
- data/lib/service_adaptors/amazon.rb +9 -5
- data/lib/service_adaptors/google_book_search.rb +2 -2
- data/lib/service_adaptors/hathi_trust.rb +2 -2
- data/lib/service_adaptors/internet_archive.rb +9 -18
- data/lib/service_adaptors/primo_service.rb +4 -2
- data/lib/service_adaptors/primo_source.rb +4 -0
- data/lib/service_adaptors/scopus.rb +1 -1
- data/lib/umlaut/routes.rb +1 -1
- data/lib/umlaut/version.rb +1 -1
- metadata +31 -35
- data/app/assets/javascripts/umlaut/#simple_visible_toggle.js# +0 -8
- data/app/assets/javascripts/umlaut/#update_html.js# +0 -181
- data/lib/service_adaptors/#blacklight.rb# +0 -327
- data/lib/umlaut/#routes.rb# +0 -164
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
<td><%= dispatched_service.updated_at.localtime.to_s(:short) %></td>
|
|
3
3
|
<td><%= dispatched_service.service_id %></td>
|
|
4
4
|
<td><%= dispatched_service.status %></td>
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
<% if dispatched_service.request %>
|
|
6
|
+
<td><%= link_to "[Re-request live]", url_for_with_co({:controller => "/resolve", :action => :index, "umlaut.force_new_request" => "true"}, dispatched_service.request.to_context_object) %></td>
|
|
7
|
+
<td><%= link_to "[View cached request]", url_for(:controller => "/resolve", :action => :index, "umlaut.request_id" => dispatched_service.request_id ) %></td>
|
|
8
|
+
<% end %>
|
|
7
9
|
</tr>
|
|
8
10
|
<tr>
|
|
9
11
|
<% if dispatched_service.exception_info %>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<% if params[:service_id] %>
|
|
15
15
|
<h2>Service: <%= params[:service_id] %></h2>
|
|
16
|
-
<%= link_to "[View All]", params.merge(:service_id => nil) %>
|
|
16
|
+
<%= link_to "[View All]", params.merge(:service_id => nil, :offset => nil) %>
|
|
17
17
|
<% else %>
|
|
18
18
|
<h2>By service</h2>
|
|
19
19
|
|
|
@@ -37,9 +37,14 @@
|
|
|
37
37
|
|
|
38
38
|
<p>
|
|
39
39
|
<%= @offset + 1 %> - <%= [@offset + @limit, @dispatched_services_count].min %> of <%= @dispatched_services_count %>. <%= link_to "[Next]", params.merge("offset" => @offset + @limit ) %>
|
|
40
|
+
</p>
|
|
40
41
|
|
|
41
42
|
<table>
|
|
42
43
|
<%= render :partial => "dispatched_service", :collection => @dispatched_services %>
|
|
43
44
|
</table>
|
|
45
|
+
|
|
46
|
+
<p>
|
|
47
|
+
<%= @offset + 1 %> - <%= [@offset + @limit, @dispatched_services_count].min %> of <%= @dispatched_services_count %>. <%= link_to "[Next]", params.merge("offset" => @offset + @limit ) %>
|
|
48
|
+
</p>
|
|
44
49
|
|
|
45
50
|
</div>
|
|
@@ -94,8 +94,15 @@ class Amazon < Service
|
|
|
94
94
|
isbn = request.referent.metadata['isbn']
|
|
95
95
|
isbn = isbn.gsub(/[^0-9X]/,'') if isbn
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
# does it look like a good ISBN?
|
|
98
|
+
return request.dispatched(self, true) if isbn.blank? || ! [10,13].include?(isbn.length)
|
|
98
99
|
|
|
100
|
+
# Make sure it's REALLY a good ISBN, and
|
|
101
|
+
# Convert 13 to 10 if neccesary, cause we're using it as an ASIN.
|
|
102
|
+
# An ISBN-13 is never an ASIN.
|
|
103
|
+
isbn = ISBN.ten( isbn )
|
|
104
|
+
|
|
105
|
+
|
|
99
106
|
begin
|
|
100
107
|
|
|
101
108
|
selected_aws_vals = {}
|
|
@@ -137,10 +144,7 @@ class Amazon < Service
|
|
|
137
144
|
# We're assuming the ISBN is the ASIN Amazon ID. Not neccesarily valid
|
|
138
145
|
# assumption, but works enough of the time and there's no easy
|
|
139
146
|
# alternative.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# got to try converting to 10. An ISBN-13 is never an ASIN.
|
|
143
|
-
isbn = ISBN.ten( isbn )
|
|
147
|
+
|
|
144
148
|
|
|
145
149
|
|
|
146
150
|
query_params = {
|
|
@@ -217,7 +217,7 @@ class GoogleBookSearch < Service
|
|
|
217
217
|
|
|
218
218
|
Rails.logger.debug("GoogleBookSearch requesting: #{link}")
|
|
219
219
|
response = http_fetch(link, :headers => headers, :raise_on_http_error_code => false)
|
|
220
|
-
data = MultiJson.
|
|
220
|
+
data = MultiJson.load(response.body)
|
|
221
221
|
|
|
222
222
|
# If Google gives us an error cause it says it can't geo-locate,
|
|
223
223
|
# remove the IP, log warning, and try again.
|
|
@@ -227,7 +227,7 @@ class GoogleBookSearch < Service
|
|
|
227
227
|
Rails.logger.warn("GoogleBookSearch: geo-locate error, retrying without X-Forwarded-For: '#{link}' headers: #{headers.inspect} #{response.inspect}\n #{data.inspect}")
|
|
228
228
|
|
|
229
229
|
response = http_fetch(link, :raise_on_http_error_code => false)
|
|
230
|
-
data = MultiJson.
|
|
230
|
+
data = MultiJson.load(response.body)
|
|
231
231
|
|
|
232
232
|
end
|
|
233
233
|
|
|
@@ -133,9 +133,9 @@ class HathiTrust < Service
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
# conducts query and parses the JSON
|
|
136
|
-
def do_query(params)
|
|
136
|
+
def do_query(params)
|
|
137
137
|
link = @api_url + "/brief/json/" + params
|
|
138
|
-
return MultiJson.
|
|
138
|
+
return MultiJson.load( open(link).read )
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
|
|
@@ -104,25 +104,16 @@ class InternetArchive < Service
|
|
|
104
104
|
|
|
105
105
|
# using open() conveniently follows the redirect for us. Alas, it
|
|
106
106
|
# doesn't give us access to the IA http status code response though.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
rescue Exception => e
|
|
113
|
-
# Log more info for exception, and then just forward exception on,
|
|
114
|
-
# we don't have any way to handle it.
|
|
115
|
-
Rails.logger.error("InternetArchive exception, for url[[#{link}]] , Exception #{e.class}")
|
|
116
|
-
raise e
|
|
117
|
-
end
|
|
118
|
-
|
|
107
|
+
response = nil
|
|
108
|
+
timeout(@http_timeout.to_i) {
|
|
109
|
+
response = open(link).read
|
|
110
|
+
}
|
|
119
111
|
if response.blank?
|
|
120
|
-
|
|
121
|
-
return nil
|
|
112
|
+
raise Exception.new("InternetArchive returned empty response for #{link}")
|
|
122
113
|
end
|
|
123
114
|
|
|
124
115
|
|
|
125
|
-
doc = MultiJson.
|
|
116
|
+
doc = MultiJson.load(response)
|
|
126
117
|
results = doc['response']['docs']
|
|
127
118
|
|
|
128
119
|
@mediatypes.each do |type|
|
|
@@ -258,14 +249,14 @@ class InternetArchive < Service
|
|
|
258
249
|
# commands even within quotes.
|
|
259
250
|
output = string.downcase
|
|
260
251
|
|
|
261
|
-
# Remove parens, semi-colons,
|
|
252
|
+
# Remove parens, semi-colons, brackets, hyphens -- they all mess
|
|
262
253
|
# up IA, which thinks they are special chars. Remove double quote,
|
|
263
254
|
# special char, which sometimes we want to use ourselves. Replace
|
|
264
255
|
# all with spaces to avoid accidentally conjoining words.
|
|
265
256
|
# (could be
|
|
266
257
|
# escaping instead? Not worth it, we don't want to search
|
|
267
258
|
# on these anyway. Remove ALL punctuation? Not sure.)
|
|
268
|
-
output.gsub!(/[)(\]\[;"
|
|
259
|
+
output.gsub!(/[)(\]\[;"\=\-]/, ' ')
|
|
269
260
|
|
|
270
261
|
return output
|
|
271
262
|
end
|
|
@@ -485,4 +476,4 @@ end
|
|
|
485
476
|
# The Snow-Image: http://www.worldcat.org/oclc/5020610
|
|
486
477
|
# Les Canadiens-Français: http://www.worldcat.org/oclc/186641188
|
|
487
478
|
# FIXME should match 1 record and doesn't. character encoding problems?
|
|
488
|
-
# John L. Stoddard's Lectures: http://www.worldcat.org/oclc/2181690
|
|
479
|
+
# John L. Stoddard's Lectures: http://www.worldcat.org/oclc/2181690
|
|
@@ -261,7 +261,9 @@ class PrimoService < Service
|
|
|
261
261
|
service_data[:match_reliability] =
|
|
262
262
|
(reliable_match?(:title => holding.title, :author => holding.author)) ?
|
|
263
263
|
ServiceResponse::MatchExact : ServiceResponse::MatchUnsure
|
|
264
|
-
service_data[:request_link_supports_ajax_call] =
|
|
264
|
+
service_data[:request_link_supports_ajax_call] =
|
|
265
|
+
(holding.respond_to?(:request_link_supports_ajax_call)) ?
|
|
266
|
+
holding.request_link_supports_ajax_call : false
|
|
265
267
|
# Only add one service type, either "primo_source" OR "holding", not both.
|
|
266
268
|
service_type = (@service_types.include?("primo_source")) ? "primo_source" : "holding"
|
|
267
269
|
# Add some other holding information for compatibility with default holding partial
|
|
@@ -447,4 +449,4 @@ class PrimoService < Service
|
|
|
447
449
|
return false if @identifier.nil?
|
|
448
450
|
return @identifier.start_with?('info:sid/primo.exlibrisgroup.com')
|
|
449
451
|
end
|
|
450
|
-
end
|
|
452
|
+
end
|
|
@@ -30,6 +30,7 @@ class PrimoSource < PrimoService
|
|
|
30
30
|
# Overwrites PrimoService#new.
|
|
31
31
|
def initialize(config)
|
|
32
32
|
@service_types = ["holding"]
|
|
33
|
+
@source_attributes = []
|
|
33
34
|
super(config)
|
|
34
35
|
end
|
|
35
36
|
|
|
@@ -52,6 +53,9 @@ class PrimoSource < PrimoService
|
|
|
52
53
|
@holding_attributes.each do |attr|
|
|
53
54
|
service_data[attr] = holding.method(attr).call
|
|
54
55
|
end
|
|
56
|
+
@source_attributes.each do |attr|
|
|
57
|
+
service_data[attr.to_sym] = holding.method(attr.to_sym).call
|
|
58
|
+
end
|
|
55
59
|
service_data.merge!({
|
|
56
60
|
:call_number => holding.call_number, :collection => holding.collection,
|
|
57
61
|
:collection_str => "#{holding.library} #{holding.collection}",
|
|
@@ -110,7 +110,7 @@ class Scopus < Service
|
|
|
110
110
|
|
|
111
111
|
# Take the first hit from scopus's results, hope they relevancy ranked it
|
|
112
112
|
# well. For DOI/pmid search, there should ordinarly be only one hit!
|
|
113
|
-
results = MultiJson.
|
|
113
|
+
results = MultiJson.load(response)
|
|
114
114
|
|
|
115
115
|
if ( results["ERROR"])
|
|
116
116
|
Rails.logger.error("Error from Scopus API: #{results["ERROR"].inspect} openurl: ?#{request.referent.to_context_object.kev}")
|
data/lib/umlaut/routes.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Umlaut
|
|
|
53
53
|
def a_z
|
|
54
54
|
add_routes do |options|
|
|
55
55
|
# Special one for alpha list
|
|
56
|
-
match 'journal_list/:id/:page' => 'search#journal_list', :defaults => { :page => 1, :id => 'A' }
|
|
56
|
+
match 'journal_list(/:id(/:page))' => 'search#journal_list', :defaults => { :page => '1', :id => 'A' }
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
# Catch redirected from SFX A-Z and citation linker urls
|
data/lib/umlaut/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: umlaut
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.0beta6
|
|
5
5
|
prerelease: 5
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,11 +9,11 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-04-
|
|
12
|
+
date: 2012-04-16 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rails
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &102344040 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ~>
|
|
@@ -21,10 +21,10 @@ dependencies:
|
|
|
21
21
|
version: 3.2.2
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *102344040
|
|
25
25
|
- !ruby/object:Gem::Dependency
|
|
26
26
|
name: jquery-rails
|
|
27
|
-
requirement: &
|
|
27
|
+
requirement: &102355740 !ruby/object:Gem::Requirement
|
|
28
28
|
none: false
|
|
29
29
|
requirements:
|
|
30
30
|
- - ! '>='
|
|
@@ -32,10 +32,10 @@ dependencies:
|
|
|
32
32
|
version: '0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *102355740
|
|
36
36
|
- !ruby/object:Gem::Dependency
|
|
37
37
|
name: nokogiri
|
|
38
|
-
requirement: &
|
|
38
|
+
requirement: &102351520 !ruby/object:Gem::Requirement
|
|
39
39
|
none: false
|
|
40
40
|
requirements:
|
|
41
41
|
- - =
|
|
@@ -43,10 +43,10 @@ dependencies:
|
|
|
43
43
|
version: 1.5.0
|
|
44
44
|
type: :runtime
|
|
45
45
|
prerelease: false
|
|
46
|
-
version_requirements: *
|
|
46
|
+
version_requirements: *102351520
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: openurl
|
|
49
|
-
requirement: &
|
|
49
|
+
requirement: &102374560 !ruby/object:Gem::Requirement
|
|
50
50
|
none: false
|
|
51
51
|
requirements:
|
|
52
52
|
- - ! '>='
|
|
@@ -54,10 +54,10 @@ dependencies:
|
|
|
54
54
|
version: 0.3.0
|
|
55
55
|
type: :runtime
|
|
56
56
|
prerelease: false
|
|
57
|
-
version_requirements: *
|
|
57
|
+
version_requirements: *102374560
|
|
58
58
|
- !ruby/object:Gem::Dependency
|
|
59
59
|
name: marc
|
|
60
|
-
requirement: &
|
|
60
|
+
requirement: &102378660 !ruby/object:Gem::Requirement
|
|
61
61
|
none: false
|
|
62
62
|
requirements:
|
|
63
63
|
- - ~>
|
|
@@ -65,10 +65,10 @@ dependencies:
|
|
|
65
65
|
version: 0.4.3
|
|
66
66
|
type: :runtime
|
|
67
67
|
prerelease: false
|
|
68
|
-
version_requirements: *
|
|
68
|
+
version_requirements: *102378660
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: isbn
|
|
71
|
-
requirement: &
|
|
71
|
+
requirement: &102376760 !ruby/object:Gem::Requirement
|
|
72
72
|
none: false
|
|
73
73
|
requirements:
|
|
74
74
|
- - ! '>='
|
|
@@ -76,10 +76,10 @@ dependencies:
|
|
|
76
76
|
version: '0'
|
|
77
77
|
type: :runtime
|
|
78
78
|
prerelease: false
|
|
79
|
-
version_requirements: *
|
|
79
|
+
version_requirements: *102376760
|
|
80
80
|
- !ruby/object:Gem::Dependency
|
|
81
81
|
name: htmlentities
|
|
82
|
-
requirement: &
|
|
82
|
+
requirement: &102385880 !ruby/object:Gem::Requirement
|
|
83
83
|
none: false
|
|
84
84
|
requirements:
|
|
85
85
|
- - ! '>='
|
|
@@ -87,10 +87,10 @@ dependencies:
|
|
|
87
87
|
version: '0'
|
|
88
88
|
type: :runtime
|
|
89
89
|
prerelease: false
|
|
90
|
-
version_requirements: *
|
|
90
|
+
version_requirements: *102385880
|
|
91
91
|
- !ruby/object:Gem::Dependency
|
|
92
92
|
name: multi_json
|
|
93
|
-
requirement: &
|
|
93
|
+
requirement: &102404440 !ruby/object:Gem::Requirement
|
|
94
94
|
none: false
|
|
95
95
|
requirements:
|
|
96
96
|
- - ! '>='
|
|
@@ -98,10 +98,10 @@ dependencies:
|
|
|
98
98
|
version: '0'
|
|
99
99
|
type: :runtime
|
|
100
100
|
prerelease: false
|
|
101
|
-
version_requirements: *
|
|
101
|
+
version_requirements: *102404440
|
|
102
102
|
- !ruby/object:Gem::Dependency
|
|
103
103
|
name: confstruct
|
|
104
|
-
requirement: &
|
|
104
|
+
requirement: &102501700 !ruby/object:Gem::Requirement
|
|
105
105
|
none: false
|
|
106
106
|
requirements:
|
|
107
107
|
- - ~>
|
|
@@ -109,10 +109,10 @@ dependencies:
|
|
|
109
109
|
version: '0.2'
|
|
110
110
|
type: :runtime
|
|
111
111
|
prerelease: false
|
|
112
|
-
version_requirements: *
|
|
112
|
+
version_requirements: *102501700
|
|
113
113
|
- !ruby/object:Gem::Dependency
|
|
114
114
|
name: exlibris-primo
|
|
115
|
-
requirement: &
|
|
115
|
+
requirement: &102514240 !ruby/object:Gem::Requirement
|
|
116
116
|
none: false
|
|
117
117
|
requirements:
|
|
118
118
|
- - ~>
|
|
@@ -120,10 +120,10 @@ dependencies:
|
|
|
120
120
|
version: 0.1.0
|
|
121
121
|
type: :runtime
|
|
122
122
|
prerelease: false
|
|
123
|
-
version_requirements: *
|
|
123
|
+
version_requirements: *102514240
|
|
124
124
|
- !ruby/object:Gem::Dependency
|
|
125
125
|
name: single_test
|
|
126
|
-
requirement: &
|
|
126
|
+
requirement: &102512120 !ruby/object:Gem::Requirement
|
|
127
127
|
none: false
|
|
128
128
|
requirements:
|
|
129
129
|
- - ~>
|
|
@@ -131,10 +131,10 @@ dependencies:
|
|
|
131
131
|
version: 0.5.1
|
|
132
132
|
type: :development
|
|
133
133
|
prerelease: false
|
|
134
|
-
version_requirements: *
|
|
134
|
+
version_requirements: *102512120
|
|
135
135
|
- !ruby/object:Gem::Dependency
|
|
136
136
|
name: uglifier
|
|
137
|
-
requirement: &
|
|
137
|
+
requirement: &102509700 !ruby/object:Gem::Requirement
|
|
138
138
|
none: false
|
|
139
139
|
requirements:
|
|
140
140
|
- - ! '>='
|
|
@@ -142,10 +142,10 @@ dependencies:
|
|
|
142
142
|
version: '0'
|
|
143
143
|
type: :development
|
|
144
144
|
prerelease: false
|
|
145
|
-
version_requirements: *
|
|
145
|
+
version_requirements: *102509700
|
|
146
146
|
- !ruby/object:Gem::Dependency
|
|
147
147
|
name: therubyracer
|
|
148
|
-
requirement: &
|
|
148
|
+
requirement: &102507660 !ruby/object:Gem::Requirement
|
|
149
149
|
none: false
|
|
150
150
|
requirements:
|
|
151
151
|
- - ! '>='
|
|
@@ -153,10 +153,10 @@ dependencies:
|
|
|
153
153
|
version: '0'
|
|
154
154
|
type: :development
|
|
155
155
|
prerelease: false
|
|
156
|
-
version_requirements: *
|
|
156
|
+
version_requirements: *102507660
|
|
157
157
|
- !ruby/object:Gem::Dependency
|
|
158
158
|
name: ruby-prof
|
|
159
|
-
requirement: &
|
|
159
|
+
requirement: &102522180 !ruby/object:Gem::Requirement
|
|
160
160
|
none: false
|
|
161
161
|
requirements:
|
|
162
162
|
- - ! '>='
|
|
@@ -164,7 +164,7 @@ dependencies:
|
|
|
164
164
|
version: '0'
|
|
165
165
|
type: :development
|
|
166
166
|
prerelease: false
|
|
167
|
-
version_requirements: *
|
|
167
|
+
version_requirements: *102522180
|
|
168
168
|
description:
|
|
169
169
|
email:
|
|
170
170
|
- umlaut-general@rubyforge.org
|
|
@@ -213,13 +213,11 @@ files:
|
|
|
213
213
|
- app/assets/javascripts/umlaut_ui.js
|
|
214
214
|
- app/assets/javascripts/umlaut.js
|
|
215
215
|
- app/assets/javascripts/umlaut/update_html.js
|
|
216
|
-
- app/assets/javascripts/umlaut/#simple_visible_toggle.js#
|
|
217
216
|
- app/assets/javascripts/umlaut/ajax_windows.js
|
|
218
217
|
- app/assets/javascripts/umlaut/simple_visible_toggle.js
|
|
219
218
|
- app/assets/javascripts/umlaut/ensure_window_size.js.erb
|
|
220
219
|
- app/assets/javascripts/umlaut/expand_contract_toggle.js
|
|
221
220
|
- app/assets/javascripts/umlaut/search_autocomplete.js
|
|
222
|
-
- app/assets/javascripts/umlaut/#update_html.js#
|
|
223
221
|
- app/assets/stylesheets/umlaut.css
|
|
224
222
|
- app/models/sfx_url.rb
|
|
225
223
|
- app/models/crossref_lookup.rb
|
|
@@ -323,7 +321,6 @@ files:
|
|
|
323
321
|
- lib/opensearch_feed.rb
|
|
324
322
|
- lib/holding.rb
|
|
325
323
|
- lib/umlaut_configurable.rb
|
|
326
|
-
- lib/umlaut/#routes.rb#
|
|
327
324
|
- lib/umlaut/routes.rb
|
|
328
325
|
- lib/umlaut/version.rb
|
|
329
326
|
- lib/umlaut/default_configuration.rb
|
|
@@ -336,7 +333,6 @@ files:
|
|
|
336
333
|
- lib/service_adaptors/primo_service.rb
|
|
337
334
|
- lib/service_adaptors/open_library.rb
|
|
338
335
|
- lib/service_adaptors/elsevier_cover.rb
|
|
339
|
-
- lib/service_adaptors/#blacklight.rb#
|
|
340
336
|
- lib/service_adaptors/ulrichs_link.rb
|
|
341
337
|
- lib/service_adaptors/ulrichs_cover.rb
|
|
342
338
|
- lib/service_adaptors/scopus.rb
|
|
@@ -485,7 +481,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
485
481
|
version: '0'
|
|
486
482
|
segments:
|
|
487
483
|
- 0
|
|
488
|
-
hash:
|
|
484
|
+
hash: 367390304893023156
|
|
489
485
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
490
486
|
none: false
|
|
491
487
|
requirements:
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/* simple_visible_toggle.js. Used for toggling visibility of error information. Can possibly be combined with more powerful expand_contract_toggle.js */
|
|
2
|
-
jQuery(document).ready(function($) {
|
|
3
|
-
|
|
4
|
-
$("a.simple_visible_toggle").live("click", function() {
|
|
5
|
-
$(this).next().toggle();
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
});
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
/* update_html.js: Provide functions to update content on page with background responses from Umlaut. Used by Umlaut itself, as well as by third party callers.*/
|
|
2
|
-
(function($) {
|
|
3
|
-
|
|
4
|
-
function SectionTarget(config) {
|
|
5
|
-
//Add properties from config to ourself
|
|
6
|
-
$.extend(this, config);
|
|
7
|
-
|
|
8
|
-
//Defaults
|
|
9
|
-
if (this.selector == undefined)
|
|
10
|
-
this.selector = "#" + this.umlaut_section_id;
|
|
11
|
-
if (this.position == undefined)
|
|
12
|
-
this.position = "html";
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
//Callback default to no-op function please.
|
|
16
|
-
var noop = function() {};
|
|
17
|
-
SectionTarget.prototype.before_update = noop;
|
|
18
|
-
SectionTarget.prototype.after_update = noop;
|
|
19
|
-
SectionTarget.prototype.complete = noop;
|
|
20
|
-
|
|
21
|
-
SectionTarget.prototype.ensure_placement_destination = function() {
|
|
22
|
-
if ( this.selector == undefined) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
//Already have it cached?
|
|
27
|
-
if ( this.host_div_element ) {
|
|
28
|
-
return this.host_div_element;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
var new_div = $('<div class="umlaut" style="display:none"></div>');
|
|
32
|
-
// Find the first thing matched by selector, and call the
|
|
33
|
-
// method specified in "action" string on it, giving it our
|
|
34
|
-
// HTML to replace. This works because our actions are
|
|
35
|
-
// all arguments that will take one method: html, before, after, append,
|
|
36
|
-
// prepend.
|
|
37
|
-
$(this.selector).eq(0)[ this.position ]( new_div );
|
|
38
|
-
|
|
39
|
-
//Cache for later
|
|
40
|
-
this.host_div_element = new_div;
|
|
41
|
-
return this.host_div_element;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Define an object constructor on the global window object
|
|
46
|
-
// For our UmlautHtmlUpdater object.
|
|
47
|
-
function HtmlUpdater(umlaut_base, context_object) {
|
|
48
|
-
if (context_object == undefined)
|
|
49
|
-
context_object = "";
|
|
50
|
-
|
|
51
|
-
umlaut_base = umlaut_base.replace(/\/$/,'');
|
|
52
|
-
this.umlaut_uri = umlaut_base + '/resolve/partial_html_sections?umlaut.response_format=json&' + context_object;
|
|
53
|
-
|
|
54
|
-
this.section_targets = [];
|
|
55
|
-
|
|
56
|
-
this.add_section_target = function(config) {
|
|
57
|
-
this.section_targets.push( new SectionTarget(config) );
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
//default no-op call-backs
|
|
61
|
-
this.complete = noop;
|
|
62
|
-
this.before_update = noop;
|
|
63
|
-
this.after_update = noop;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
//Code for seeing if a URI is same origin or not borrowed from jQuery
|
|
67
|
-
this.is_remote_url = function(url) {
|
|
68
|
-
var regexp = /^(\w+:)?\/\/([^\/?#]+)/;
|
|
69
|
-
var parts = regexp.exec( url );
|
|
70
|
-
return (parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
this.update = function() {
|
|
74
|
-
// Need to capture because we won't have 'this' inside the ajax
|
|
75
|
-
// success handler.
|
|
76
|
-
var myself = this;
|
|
77
|
-
var dataType = this.is_remote_url( this.umlaut_uri ) ? "jsonp" : "json";
|
|
78
|
-
$.ajax({
|
|
79
|
-
url: myself.umlaut_uri,
|
|
80
|
-
dataType: dataType,
|
|
81
|
-
jsonp: "umlaut.jsonp",
|
|
82
|
-
error: function() {
|
|
83
|
-
$.error("Problem loading background elements.");
|
|
84
|
-
},
|
|
85
|
-
success: function(umlaut_response) {
|
|
86
|
-
for (var i = 0; i < myself.section_targets.length; i++) {
|
|
87
|
-
var section_target = myself.section_targets[i];
|
|
88
|
-
|
|
89
|
-
var umlaut_html_section = myself.find_umlaut_response_section(umlaut_response, section_target.umlaut_section_id);
|
|
90
|
-
|
|
91
|
-
if (umlaut_html_section == undefined) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
var count = null;
|
|
95
|
-
if (typeof umlaut_html_section.response_count != "undefined") {
|
|
96
|
-
count = parseInt(umlaut_html_section.response_count.value);
|
|
97
|
-
}
|
|
98
|
-
var existing_element = section_target.ensure_placement_destination();
|
|
99
|
-
var new_element = $('<div class="umlaut" style="display:none" class="' + section_target.umlaut_section_id +'"></div>');
|
|
100
|
-
new_element.html(umlaut_html_section.html_content);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
var should_continue = section_target.before_update(new_element, count, section_target);
|
|
104
|
-
if (should_continue != false) {
|
|
105
|
-
should_continue = myself.before_update(new_element, count, section_target);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (should_continue != false) {
|
|
109
|
-
existing_element.replaceWith(new_element);
|
|
110
|
-
|
|
111
|
-
section_target.host_div_element = new_element;
|
|
112
|
-
|
|
113
|
-
new_element.show();
|
|
114
|
-
|
|
115
|
-
section_target.after_update(new_element, count, section_target);
|
|
116
|
-
myself.after_update(new_element, count, section_target);
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
//Do we need to update again?
|
|
122
|
-
if (umlaut_response.partial_html_sections.in_progress) {
|
|
123
|
-
//Fix our update URI to be the one umlaut suggests
|
|
124
|
-
//Except strip out the umlaut.jsonp parameter, jquery is
|
|
125
|
-
//going to add that back in as desired.
|
|
126
|
-
myself.umlaut_uri =
|
|
127
|
-
umlaut_response.partial_html_sections.in_progress.refresh_url.replace(/[?;&]umlaut\.jsonp=[^;&]+/, '');
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
var refresh_seconds =
|
|
131
|
-
umlaut_response.partial_html_sections.in_progress.requested_wait_seconds;
|
|
132
|
-
window.setTimeout(function() { myself.update(); }, refresh_seconds * 1000);
|
|
133
|
-
|
|
134
|
-
} else {
|
|
135
|
-
myself.complete();
|
|
136
|
-
for (var i = 0; i < myself.section_targets.length; i++) {
|
|
137
|
-
var section_target = myself.section_targets[i];
|
|
138
|
-
section_target.complete(section_target);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
};
|
|
145
|
-
this.find_umlaut_response_section = function(response, id) {
|
|
146
|
-
return $.grep(response.partial_html_sections.html_section, function(section) {
|
|
147
|
-
return section.id == id;
|
|
148
|
-
})[0];
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
//Put it in a global object, leave space for other things in "Umlaut" later.
|
|
154
|
-
if (window.Umlaut == undefined)
|
|
155
|
-
window.Umlaut = new Object();
|
|
156
|
-
window.Umlaut.HtmlUpdater = HtmlUpdater;
|
|
157
|
-
|
|
158
|
-
/* LEGACY Loader was recommended for loading Umlaut JS behaviors
|
|
159
|
-
in an external page, for JQuery Content Utility.
|
|
160
|
-
|
|
161
|
-
var loader = new Umlaut.Loader();
|
|
162
|
-
loader.load();
|
|
163
|
-
|
|
164
|
-
We will provide just enough code to keep that from
|
|
165
|
-
error'ing (and halting js execution), although at present it does not
|
|
166
|
-
actually load the JS behaviors using new style, app wont' have
|
|
167
|
-
JS behaviors. */
|
|
168
|
-
|
|
169
|
-
window.Umlaut.Loader = function() {
|
|
170
|
-
this.load = function(option_list) {
|
|
171
|
-
// log problem in browsers that support it.
|
|
172
|
-
if (typeof console != "undefined" && typeof console.log != "undefined") {
|
|
173
|
-
console.log("WARN: Umlaut.Loader no longer supported in Umlaut 3.x, you may have not loaded Umlaut JS Behaviors as desired. See Umlaut documentation for new way.");
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
})(jQuery);
|
|
181
|
-
|
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
require 'nokogiri'
|
|
2
|
-
require 'open-uri'
|
|
3
|
-
require 'base64'
|
|
4
|
-
require 'marc'
|
|
5
|
-
|
|
6
|
-
# Searches a Blacklight with the cql extension installed.
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# Params include:
|
|
10
|
-
# [base_url]
|
|
11
|
-
# required. Complete URL to catalog.atom action. Eg "https://blacklight.mse.jhu.edu/catalog.atom"
|
|
12
|
-
# [bl_fields]
|
|
13
|
-
# required with at least some entries if you want this to do anything. Describe the names of given semantic fields in your BL instance.
|
|
14
|
-
# * issn
|
|
15
|
-
# * isbn
|
|
16
|
-
# * lccn
|
|
17
|
-
# * oclcnum
|
|
18
|
-
# * id (defaults to 'id')
|
|
19
|
-
# * title
|
|
20
|
-
# * author
|
|
21
|
-
# * serials_limit_clause => not an index name, full URL clause for a limit to apply to known serials searches, for instance "f[format][]=Serial"
|
|
22
|
-
# [identifier_search]
|
|
23
|
-
# Do catalog search on issn/isbn/oclcnum/lccn/bibId. Default true.
|
|
24
|
-
# [keyword_search]
|
|
25
|
-
# Do catalog search on title/author keywords where applicable. Generally only used when identifier_search finds no hits, if identifier_search is on. Default true.
|
|
26
|
-
# [keyword_per_page]
|
|
27
|
-
# How many records to fetch from blacklight when doing keyword searches.
|
|
28
|
-
# [exclude_holdings]
|
|
29
|
-
# Can be used to exclude certain 'dummy' holdings that have certain collection, location, or other values. Eg:
|
|
30
|
-
# exclude_holdings:
|
|
31
|
-
# collection_str:
|
|
32
|
-
# - World Wide Web
|
|
33
|
-
# - Internet
|
|
34
|
-
# [rft_id_bibnum_prefixes]
|
|
35
|
-
# Array of URI prefixes in an rft_id that indicate that the actual solr id comes next. For instance, if your blacklight will send "http://blacklight.com/catalog/some_id" in an rft_id, then include "http://blacklight.com/catalog/". Optional.
|
|
36
|
-
class Blacklight < Service
|
|
37
|
-
required_config_params :base_url, :display_name
|
|
38
|
-
attr_reader :base_url, :cql_search_field
|
|
39
|
-
attr_reader :bl_fields, :issn
|
|
40
|
-
|
|
41
|
-
include UmlautHttp
|
|
42
|
-
include MetadataHelper
|
|
43
|
-
include MarcHelper
|
|
44
|
-
include XmlSchemaHelper
|
|
45
|
-
|
|
46
|
-
def initialize(config)
|
|
47
|
-
# defaults
|
|
48
|
-
# If you are sending an OpenURL from a library service, you may
|
|
49
|
-
# have the HIP bibnum, and include it in the OpenURL as, eg.
|
|
50
|
-
# rft_id=http://catalog.library.jhu.edu/bib/343434 (except URL-encoded)
|
|
51
|
-
# Then you'd set rft_id_bibnum_prefix to http://catalog.library.jhu.edu/bib/
|
|
52
|
-
@rft_id_bibnum_prefixes = []
|
|
53
|
-
@cql_search_field = "cql"
|
|
54
|
-
@keyword_per_page = 10
|
|
55
|
-
@identifier_search = true
|
|
56
|
-
@keyword_search = true
|
|
57
|
-
@link_to_search = true
|
|
58
|
-
super(config)
|
|
59
|
-
@bl_fields = { "id" => "id "}.merge(@bl_fields)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Standard method, used by background service updater. See Service docs.
|
|
63
|
-
def service_types_generated
|
|
64
|
-
types = [ ServiceTypeValue[:fulltext], ServiceTypeValue[:holding], ServiceTypeValue[:table_of_contents], ServiceTypeValue[:relevant_link] ]
|
|
65
|
-
|
|
66
|
-
return types
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def handle(request)
|
|
71
|
-
ids_processed = []
|
|
72
|
-
holdings_added = 0
|
|
73
|
-
|
|
74
|
-
if (@identifier_search && url = blacklight_precise_search_url(request) )
|
|
75
|
-
doc = Nokogiri::XML( http_fetch(url).body )
|
|
76
|
-
|
|
77
|
-
ids_processed.concat( bib_ids_from_atom_entries( doc.xpath("atom:feed/atom:entry", xml_ns) ) )
|
|
78
|
-
|
|
79
|
-
# namespaces make xpath harder than it should be, but css
|
|
80
|
-
# selector still easy, thanks nokogiri! Grab the marc from our
|
|
81
|
-
# results.
|
|
82
|
-
marc_matches = doc.xpath("atom:feed/atom:entry/atom:content[@type='application/marc']", xml_ns).collect do |encoded_marc21|
|
|
83
|
-
MARC::Reader.decode( Base64.decode64(encoded_marc21.text) )
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
add_856_links(request, marc_matches )
|
|
87
|
-
|
|
88
|
-
# Got to make a second fetch for dlf_expanded info, cause BL doens't
|
|
89
|
-
# (yet) let us ask for more than one at once
|
|
90
|
-
holdings_url = blacklight_precise_search_url( request, "dlf_expanded" )
|
|
91
|
-
holdings_added += add_holdings( holdings_url ) if holdings_url
|
|
92
|
-
end
|
|
93
|
-
#keyword search.
|
|
94
|
-
if (@keyword_search &&
|
|
95
|
-
url = blacklight_keyword_search_url(request))
|
|
96
|
-
|
|
97
|
-
doc = Nokogiri::XML( http_fetch(url).body )
|
|
98
|
-
# filter out matches whose titles don't really match at all, or
|
|
99
|
-
# which have already been seen in identifier search.
|
|
100
|
-
entries = filter_keyword_entries( doc.xpath("atom:feed/atom:entry", xml_ns) , :exclude_ids => ids_processed, :remove_subtitle => (! title_is_serial?(request.referent)) )
|
|
101
|
-
|
|
102
|
-
marc_by_atom_id = {}
|
|
103
|
-
|
|
104
|
-
# Grab the marc from our entries. Important not to do a // xpath
|
|
105
|
-
# search, or we'll wind up matching parent elements not actually
|
|
106
|
-
# included in our 'entries' list.
|
|
107
|
-
marc_matches = entries.xpath("atom:content[@type='application/marc']", xml_ns).collect do |encoded_marc21|
|
|
108
|
-
marc = MARC::Reader.decode( Base64.decode64(encoded_marc21.text) )
|
|
109
|
-
|
|
110
|
-
marc_by_atom_id[ encoded_marc21.at_xpath("ancestor::atom:entry/atom:id/text()", xml_ns).to_s ] = marc
|
|
111
|
-
|
|
112
|
-
marc
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# We've filtered out those we consider just plain bad
|
|
116
|
-
# matches, everything else we're going to call
|
|
117
|
-
# an approximate match. Sort so that those with
|
|
118
|
-
# a date close to our request date are first.
|
|
119
|
-
if ( year = get_year(request.referent))
|
|
120
|
-
marc_matches = marc_matches.partition {|marc| get_years(marc).include?( year )}.flatten
|
|
121
|
-
end
|
|
122
|
-
# And add in the 856's
|
|
123
|
-
add_856_links(request, marc_matches, :match_reliability => ServiceResponse::MatchUnsure)
|
|
124
|
-
|
|
125
|
-
# Fetch and add in the holdings
|
|
126
|
-
url = blacklight_url_for_ids(bib_ids_from_atom_entries(entries))
|
|
127
|
-
|
|
128
|
-
holdings_added += add_holdings( url, :match_reliability => ServiceResponse::MatchUnsure, :marc_data => marc_by_atom_id ) if url
|
|
129
|
-
|
|
130
|
-
if (@link_to_search && holdings_added ==0)
|
|
131
|
-
hit_count = doc.at_xpath("atom:feed/opensearch:totalResults/text()", xml_ns).to_s.to_i
|
|
132
|
-
html_result_url = doc.at_xpath("atom:feed/atom:link[@rel='alternate'][@type='text/html']/attribute::href", xml_ns).to_s
|
|
133
|
-
|
|
134
|
-
if hit_count > 0
|
|
135
|
-
request.add_service_response(
|
|
136
|
-
:service => self,
|
|
137
|
-
:source_name => @display_name,
|
|
138
|
-
:count => hit_count,
|
|
139
|
-
:display_text => "#{hit_count} possible #{case; when hit_count > 1 ; 'matches' ; else; 'match' ; end} in #{@display_name}",
|
|
140
|
-
:url => html_result_url,
|
|
141
|
-
:service_type_value => :holding_search )
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return request.dispatched(self, true)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# Send a CQL request for any identifiers present.
|
|
155
|
-
# Ask for for an atom response with embedded marc21 back.
|
|
156
|
-
def blacklight_precise_search_url(request, format = "marc")
|
|
157
|
-
# Add search clauses for our identifiers, if we have them and have a configured search field for them.
|
|
158
|
-
clauses = []
|
|
159
|
-
added = []
|
|
160
|
-
["lccn", "isbn", "oclcnum"].each do |key|
|
|
161
|
-
if bl_fields[key] && request.referent.send(key)
|
|
162
|
-
clauses.push( "#{bl_fields[key]} = \"#{request.referent.send(key)}\"")
|
|
163
|
-
added << key
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
# Only add ISSN if we don't have an ISBN, reduces false matches
|
|
167
|
-
if ( !added.include?("isbn") &&
|
|
168
|
-
bl_fields["issn"] &&
|
|
169
|
-
request.referent.issn)
|
|
170
|
-
clauses.push("#{bl_fields["issn"]} = \"#{request.referent.issn}\"")
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
# Add Solr document identifier if we can get one from the URL
|
|
175
|
-
|
|
176
|
-
if (id = get_solr_id(request.referent))
|
|
177
|
-
clauses.push("#{bl_fields['id']} = \"#{id}\"")
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# if we have nothing, we can do no search.
|
|
181
|
-
return nil if clauses.length == 0
|
|
182
|
-
|
|
183
|
-
cql = clauses.join(" OR ")
|
|
184
|
-
|
|
185
|
-
return base_url + "?search_field=#{@cql_search_field}&content_format=#{format}&q=#{CGI.escape(cql)}"
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
# Construct a CQL search against blacklight for author and title,
|
|
189
|
-
# possibly with serial limit. Ask for Atom with embedded MARC back.
|
|
190
|
-
def blacklight_keyword_search_url(request, options = {})
|
|
191
|
-
options[:format] ||= "atom"
|
|
192
|
-
options[:content_format] ||= "marc"
|
|
193
|
-
|
|
194
|
-
clauses = []
|
|
195
|
-
|
|
196
|
-
# We need both title and author to search keyword style, or
|
|
197
|
-
# we get too many false positives. Except serials we'll do
|
|
198
|
-
# title only. sigh, logic tree.
|
|
199
|
-
title = get_search_title(request.referent)
|
|
200
|
-
author = get_top_level_creator(request.referent)
|
|
201
|
-
return nil unless title && (author || (@bl_fields["serials_limit_clause"] && title_is_serial?(request.referent)))
|
|
202
|
-
# phrase search for title, just raw dismax for author
|
|
203
|
-
# Embed quotes inside the quoted value, need to backslash-quote for CQL,
|
|
204
|
-
# and backslash the backslashes for ruby literal.
|
|
205
|
-
clauses.push("#{@bl_fields["title"]} = \"\\\"#{title}\\\"\"")
|
|
206
|
-
clauses.push("#{@bl_fields["author"]} = \"#{author}\"") if author
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
url = base_url + "?search_field=#{@cql_search_field}&content_format=#{options[:content_format]}&q=#{CGI.escape(clauses.join(" AND "))}"
|
|
211
|
-
|
|
212
|
-
if (@bl_fields["serials_limit_clause"] &&
|
|
213
|
-
title_is_serial?(request.referent))
|
|
214
|
-
url += "&" + @bl_fields["serials_limit_clause"]
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
return url
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Takes a url that will return atom response of dlf_expanded content.
|
|
221
|
-
# Adds Umlaut "holding" ServiceResponses for dlf_expanded, as appropriate.
|
|
222
|
-
# Returns number of holdings added.
|
|
223
|
-
def add_holdings(holdings_url, options = {})
|
|
224
|
-
options[:match_reliability] ||= ServiceResponse::MatchExact
|
|
225
|
-
options[:marc_data] ||= {}
|
|
226
|
-
|
|
227
|
-
atom = Nokogiri::XML( http_fetch(holdings_url).body )
|
|
228
|
-
content_entries = atom.search("/atom:feed/atom:entry/atom:content", xml_ns)
|
|
229
|
-
|
|
230
|
-
# For each atom entry, find the dlf_expanded record. For each dlf_expanded
|
|
231
|
-
# record, take all of it's holdingsrec's if it has them, or all of it's
|
|
232
|
-
# items if it doesn't, and add them to list. We wind up with a list
|
|
233
|
-
# of mixed holdingsrec's and items.
|
|
234
|
-
holdings_xml = content_entries.collect do |dlf_expanded|
|
|
235
|
-
copies = dlf_expanded.xpath("dlf:record/dlf:holdings/dlf:holdingset/dlf:holdingsrec", xml_ns)
|
|
236
|
-
copies.length > 0 ? copies : dlf_expanded.xpath("dlf:record/dlf:items/dlf:item", xml_ns)
|
|
237
|
-
end.flatten
|
|
238
|
-
|
|
239
|
-
service_data = holdings_xml.collect do | xml_metadata |
|
|
240
|
-
atom_entry = xml_metadata.at_xpath("ancestor::atom:entry", xml_ns)
|
|
241
|
-
atom_id = atom_entry.at_xpath("atom:id/text()", xml_ns).to_s
|
|
242
|
-
|
|
243
|
-
edition_str = edition_statement(options[:marc_data][atom_id])
|
|
244
|
-
url = atom_entry.at_xpath("atom:link[@rel='alternate'][@type='text/html']/attribute::href", xml_ns).to_s
|
|
245
|
-
|
|
246
|
-
xml_to_holdings( xml_metadata ).merge(
|
|
247
|
-
:service => self,
|
|
248
|
-
:match_reliability => options[:match_reliability],
|
|
249
|
-
:edition_str => edition_str,
|
|
250
|
-
:url => url
|
|
251
|
-
)
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# strip out holdings that aren't really holdings
|
|
255
|
-
service_data.delete_if do |data|
|
|
256
|
-
@exclude_holdings.collect do |key, values|
|
|
257
|
-
values.include?(data[key.to_sym])
|
|
258
|
-
end.include?(true)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
# Sort by "collection"
|
|
262
|
-
service_data.sort do |a, b|
|
|
263
|
-
a[:collection_str] <=> b[:collection_str]
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
service_data.each do |data|
|
|
267
|
-
request.add_service_response(data.merge(:service => self, :service_type_value =>"holding"))
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
return service_data.length
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
def filter_keyword_entries(atom_entries, options = {})
|
|
274
|
-
options[:exclude_ids] ||= []
|
|
275
|
-
options[:remove_subtitle] ||= true
|
|
276
|
-
request_title_forms = [
|
|
277
|
-
raw_search_title(request.referent).downcase,
|
|
278
|
-
normalize_title( raw_search_title(request.referent) )
|
|
279
|
-
]
|
|
280
|
-
request_title_forms << normalize_title( raw_search_title(request.referent), :remove_subtitle => true) if options[:remove_subtitle]
|
|
281
|
-
request_title_forms.compact
|
|
282
|
-
|
|
283
|
-
# Only keep entries with title match, and that aren't in the
|
|
284
|
-
# exclude_ids list.
|
|
285
|
-
good_entries = atom_entries.find_all do |atom_entry|
|
|
286
|
-
title = atom_entry.xpath("atom:title/text()", xml_ns).to_s
|
|
287
|
-
|
|
288
|
-
entry_title_forms = [
|
|
289
|
-
title.downcase,
|
|
290
|
-
normalize_title(title)
|
|
291
|
-
]
|
|
292
|
-
entry_title_forms << normalize_title(title, :remove_subtitle=>true) if options[:remove_subtitle]
|
|
293
|
-
entry_title_forms.compact
|
|
294
|
-
|
|
295
|
-
((entry_title_forms & request_title_forms).length > 0 &&
|
|
296
|
-
(bib_ids_from_atom_entries(atom_entry) & options[:exclude_ids]).length == 0)
|
|
297
|
-
end
|
|
298
|
-
return Nokogiri::XML::NodeSet.new( atom_entries.document, good_entries)
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
def bib_ids_from_atom_entries(entries)
|
|
302
|
-
entries.xpath("atom:id/text()", xml_ns).to_a.collect do |atom_id|
|
|
303
|
-
atom_id.to_s =~ /([^\/]+)$/
|
|
304
|
-
$1
|
|
305
|
-
end.compact
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def blacklight_url_for_ids(ids, format="dlf_expanded")
|
|
309
|
-
return nil unless ids.length > 0
|
|
310
|
-
|
|
311
|
-
return base_url + "?search_field=#{@cql_search_field}&content_format=#{format}&q=" + CGI.escape("#{@bl_fields["id"]} any \"#{ids.join(" ")}\"")
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
def get_solr_id(rft)
|
|
316
|
-
rft.identifiers.each do |id|
|
|
317
|
-
@rft_id_bibnum_prefixes.each do |prefix|
|
|
318
|
-
if id[0, prefix.length] == prefix
|
|
319
|
-
return id[prefix.length, id.length]
|
|
320
|
-
end
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
return nil
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
end
|
data/lib/umlaut/#routes.rb#
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
|
2
|
-
module Umlaut
|
|
3
|
-
# Class to inject Umlaut routes, design copied from Blacklight project.
|
|
4
|
-
# you would do a:
|
|
5
|
-
# Umlaut::Routes.new(self, optional_args).draw
|
|
6
|
-
# in local
|
|
7
|
-
# app routes.rb, that line is generated into local app by Umlaut generator.
|
|
8
|
-
# options include :only and :except to limit what route groups are generated.
|
|
9
|
-
class Routes
|
|
10
|
-
|
|
11
|
-
def initialize(router, options ={})
|
|
12
|
-
@router = router
|
|
13
|
-
@options = options
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def draw
|
|
17
|
-
route_sets.each do |r|
|
|
18
|
-
self.send(r)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
protected
|
|
23
|
-
|
|
24
|
-
def add_routes &blk
|
|
25
|
-
@router.instance_exec(@options, &blk)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def route_sets
|
|
29
|
-
# :admin is not included by default, needs to be turned on.
|
|
30
|
-
(@options[:only] || default_route_sets) - (@options[:except] || []) + (@options[:admin] == true ? [:admin] : [])
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def default_route_sets
|
|
34
|
-
[:root, :permalinks, :a_z, :resolve, :open_search, :link_router, :export_email, :resources, :search, :javascript]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
module RouteSets
|
|
38
|
-
# for now include root generation in Umlaut auto-generation
|
|
39
|
-
def root
|
|
40
|
-
add_routes do |options|
|
|
41
|
-
root :to => "search#index"
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def permalinks
|
|
46
|
-
add_routes do |options|
|
|
47
|
-
match 'go/:id' => 'store#index'
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# some special direct links to A-Z type searches, including
|
|
52
|
-
# legacy redirects for SFX-style urls, to catch any bookmarks.
|
|
53
|
-
def a_z
|
|
54
|
-
add_routes do |options|
|
|
55
|
-
# Special one for alpha list
|
|
56
|
-
match 'journal_list/:id/:page' => 'search#journal_list', :defaults => { :page => 1, :id => 'A' }
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Catch redirected from SFX A-Z and citation linker urls
|
|
60
|
-
# v2 A-Z links redirected to umlaut, point to journal_list
|
|
61
|
-
# code in journal_list filter picks out SFX URL vars for
|
|
62
|
-
# letter.
|
|
63
|
-
match '/resolve/azlist/default' => 'search#journal_list', :page => 1, :id => 'A'
|
|
64
|
-
|
|
65
|
-
# SFX v3 A-Z list url format
|
|
66
|
-
match 'resolve/az' => 'search#journal_list', :page => 1, :id => 'A'
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# This is a legacy wild controller route that's not recommended for RESTful applications.
|
|
71
|
-
# Note: This route will make all actions in every controller accessible via GET requests.
|
|
72
|
-
# match ':controller(/:action(/:id(.:format)))'
|
|
73
|
-
|
|
74
|
-
def resolve
|
|
75
|
-
add_routes do |options|
|
|
76
|
-
# ResolveController still uses rails 2.0 style 'wildcard' routes,
|
|
77
|
-
# TODO tighten this up to only match what oughta be matched.
|
|
78
|
-
# Note: This route will make all actions in this controller accessible via GET requests.
|
|
79
|
-
|
|
80
|
-
match 'resolve(/:action(/:id(.:format)))' => "resolve"
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def open_search
|
|
85
|
-
add_routes do |options|
|
|
86
|
-
# OpenSearchController still uses rails 2.0 style 'wildcard' routes,
|
|
87
|
-
# TODO tighten this up to only match what oughta be matched.
|
|
88
|
-
# Note: This route will make all actions in this controller accessible via GET requests.
|
|
89
|
-
|
|
90
|
-
match 'open_search(/:action(/:id(.:format)))' => "open_search"
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def link_router
|
|
95
|
-
add_routes do |options|
|
|
96
|
-
# LinkRouterController still uses rails 2.0 style 'wildcard' routes,
|
|
97
|
-
# TODO tighten this up to only match what oughta be matched.
|
|
98
|
-
# Note: This route will make all actions in this controller accessible via GET requests.
|
|
99
|
-
|
|
100
|
-
match 'link_router(/:action(/:id(.:format)))' => "link_router"
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def export_email
|
|
105
|
-
add_routes do |options|
|
|
106
|
-
# ExportEmailController still uses rails 2.0 style 'wildcard' routes,
|
|
107
|
-
# TODO tighten this up to only match what oughta be matched.
|
|
108
|
-
# Note: This route will make all actions in this controller accessible via GET requests.
|
|
109
|
-
|
|
110
|
-
match 'export_email(/:action(/:id(.:format)))' => "export_email"
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def resources
|
|
115
|
-
add_routes do |options|
|
|
116
|
-
# ResourceController still uses rails 2.0 style 'wildcard' routes,
|
|
117
|
-
# TODO tighten this up to only match what oughta be matched.
|
|
118
|
-
# Note: This route will make all actions in this controller accessible via GET requests.
|
|
119
|
-
|
|
120
|
-
match 'resource(/:action(/:id(.:format)))' => "resource"
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def search
|
|
125
|
-
add_routes do |options|
|
|
126
|
-
# SearchController still uses rails 2.0 style 'wildcard' routes,
|
|
127
|
-
# TODO tighten this up to only match what oughta be matched.
|
|
128
|
-
# Note: This route will make all actions in this controller accessible via GET requests.
|
|
129
|
-
|
|
130
|
-
match 'search(/:action(/:id(.:format)))' => "search"
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def javascript
|
|
135
|
-
add_routes do |options|
|
|
136
|
-
# Legacy location for update_html.js used by JQuery Content Utility
|
|
137
|
-
# to embed JS on external sites. Redirect to new location.
|
|
138
|
-
# Intentionally non-fingerprinted, most efficient thing
|
|
139
|
-
# we can do in this case is let the web server take care
|
|
140
|
-
# of Last-modified-by etc headers.
|
|
141
|
-
match 'javascripts/jquery/umlaut/update_html.js' => redirect("/assets/umlaut/update_html.js", :status => 301)
|
|
142
|
-
|
|
143
|
-
# The loader doens't work _exactly_ like the new umlaut-ui.js, but
|
|
144
|
-
# it's close enough that it'll work better redirecting than just
|
|
145
|
-
# 404'ing.
|
|
146
|
-
match 'js_helper/loader' => redirect("/assets/umlaut_ui.js")
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
match 'images/spinner.gif' => redirect("/assets/spinner.gif")
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def admin
|
|
154
|
-
add_routes do |options|
|
|
155
|
-
namespace "admin" do
|
|
156
|
-
match 'service_errors(/:service_id)' => "service_errors#index"
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
end
|
|
162
|
-
include RouteSets
|
|
163
|
-
end
|
|
164
|
-
end
|