umlaut 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/app/assets/stylesheets/umlaut/_resolve.scss +1 -1
- data/app/controllers/feedback_controller.rb +46 -0
- data/app/controllers/resource_controller.rb +19 -1
- data/app/controllers/search_methods/sfx4.rb +15 -0
- data/app/controllers/umlaut_controller.rb +6 -0
- data/app/helpers/emailer_helper.rb +17 -3
- data/app/mailers/feedback_mailer.rb +25 -0
- data/app/mixin_logic/metadata_helper.rb +37 -0
- data/app/models/referent.rb +22 -19
- data/app/models/request.rb +3 -0
- data/app/referent_filters/dissertation_catch.rb +1 -1
- data/app/service_adaptors/all_books_dot_com.rb +17 -0
- data/app/service_adaptors/illiad.rb +161 -0
- data/app/service_adaptors/isbn_db.rb +6 -1
- data/app/service_adaptors/isbn_link.rb +57 -0
- data/app/service_adaptors/scopus.rb +4 -0
- data/app/service_adaptors/scopus2.rb +330 -0
- data/app/views/feedback/_resolve_section.html.erb +16 -0
- data/app/views/feedback/new.html.erb +33 -0
- data/app/views/feedback_mailer/feedback.text.erb +23 -0
- data/app/views/layouts/umlaut.html.erb +2 -0
- data/app/views/umlaut/_alerts.html.erb +14 -0
- data/lib/generators/templates/umlaut_services.yml +8 -8
- data/lib/umlaut/routes.rb +8 -1
- data/lib/umlaut/version.rb +1 -1
- data/test/dummy/tmp/cache/assets/BFF/760/sprockets%2Fe00969069e468419c393709f042b4527 +0 -0
- data/test/dummy/tmp/cache/assets/C9D/060/sprockets%2F5c8956a1666824a1d214531abd22e2a2 +0 -0
- data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/D15/FC0/sprockets%2F8cbf3a8b7acb7fc27a42168846226385 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/D98/990/sprockets%2F710ede3e7f5ebab14e9772fa88c00c02 +0 -0
- data/test/dummy/tmp/cache/assets/DAD/BA0/sprockets%2F193f81f7e4eae26eaaa7d909c0c8e956 +0 -0
- data/test/dummy/tmp/cache/assets/DCB/620/sprockets%2F2332a294ceeab3ab9b5ee643989dc0eb +0 -0
- data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/functional/feedback_controller_test.rb +59 -0
- data/test/test_helper.rb +13 -0
- data/test/unit/feedback_mailer_test.rb +57 -0
- data/test/unit/illiad_test.rb +146 -0
- data/test/unit/metadata_helper_test.rb +49 -0
- data/test/unit/referent_to_citation_test.rb +45 -0
- data/test/unit/scopus2_test.rb +147 -0
- data/test/vcr_cassettes/scopus/live_test_with_no_hits.yml +52 -0
- data/test/vcr_cassettes/scopus/live_test_with_result.yml +62 -0
- data/test/vcr_cassettes/scopus/live_trigger_scopus_error.yml +50 -0
- metadata +34 -120
- data/test/dummy/tmp/cache/sass/b43409235ed55124ccf6a0235156711682d0fe98/umlaut.css.scssc +0 -0
- data/test/dummy/tmp/cache/sass/d2b87393a9fcb33d01e765e1c09b76db7f14f647/bootstrap-responsive.scssc +0 -0
- data/test/dummy/tmp/cache/sass/d2b87393a9fcb33d01e765e1c09b76db7f14f647/bootstrap.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_accordion.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_alerts.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_breadcrumbs.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_button-groups.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_buttons.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_carousel.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_close.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_code.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_component-animations.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_dropdowns.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_forms.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_grid.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_hero-unit.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_labels-badges.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_layouts.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_media.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_mixins.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_modals.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_navbar.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_navs.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_pager.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_pagination.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_popovers.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_progress-bars.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_reset.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_responsive-1200px-min.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_responsive-767px-max.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_responsive-768px-979px.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_responsive-navbar.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_responsive-utilities.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_scaffolding.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_sprites.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_tables.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_thumbnails.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_tooltip.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_type.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_utilities.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_variables.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/_wells.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/bootstrap.scssc +0 -0
- data/test/dummy/tmp/cache/sass/db21ae7b0d8da2224e8e1d588985ee0507dc926a/responsive.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_admin.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_az.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_forms.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_icons.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_layout.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_misc.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_mixins.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_modal.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_resolve.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_results.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_search.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_spinner.scssc +0 -0
- data/test/dummy/tmp/cache/sass/fc140da5c32e19b5c3f84a23bb2383ebc83fcc20/_variables.scssc +0 -0
data/README.md
CHANGED
@@ -24,7 +24,7 @@ advertise/recommend to it's users.
|
|
24
24
|
|
25
25
|
Umlaut strives to supply links that take the user in as few clicks as possible to the service listed, without ever listing 'blind links' that you first have to click on to find out whether they are available. Umlaut pre-checks things when neccesary to only list services, with any needed contextual info, such that the user knows what they get when they click on it. Save the time of the user.
|
26
26
|
|
27
|
-
[What do you mean by all this?](https://github.com/team-umlaut/umlaut/wiki/What-is-Umlaut-anyway)
|
27
|
+
[What do you mean by all this?](https://github.com/team-umlaut/umlaut/wiki/What-is-Umlaut-anyway%3F)
|
28
28
|
|
29
29
|
Umlaut is distributed as a ruby Rails engine gem. It's a very heavyweight engine,
|
30
30
|
the point of distro'ing as a gem is to make it easy to keep local
|
@@ -141,7 +141,7 @@
|
|
141
141
|
@extend %indented-subitem;
|
142
142
|
}
|
143
143
|
|
144
|
-
.
|
144
|
+
.edition_warning, .response_source, .response_coverage_statement, .response_notes {
|
145
145
|
@extend %indented-subitem;
|
146
146
|
@extend .muted;
|
147
147
|
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class FeedbackController < UmlautController
|
2
|
+
def new
|
3
|
+
contact_email_lookup(params[:contact_id])
|
4
|
+
# default render
|
5
|
+
end
|
6
|
+
|
7
|
+
def create
|
8
|
+
contact_config = contact_email_lookup(params[:contact_id])
|
9
|
+
to_address = contact_config[:email_address]
|
10
|
+
|
11
|
+
options = params.slice(:name, :email, :feedback)
|
12
|
+
if params[:request_id] && umlaut_request = Request.find_by_id(params[:request_id])
|
13
|
+
options = options.merge(
|
14
|
+
:umlaut_request => umlaut_request
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
FeedbackMailer.feedback(request.host_with_port, to_address, options).deliver
|
19
|
+
|
20
|
+
flash[:alert_success] = "Thanks, your message has been sent."
|
21
|
+
|
22
|
+
if umlaut_request
|
23
|
+
redirect_to :controller => "resolve", :action => :index, "umlaut.request_id" => umlaut_request.id
|
24
|
+
else
|
25
|
+
redirect_to root_url
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def contact_email_lookup(contact_id)
|
31
|
+
unless contact_id
|
32
|
+
raise NoFeedbackEmailFoundException.new("Missing a contact_id, needed to look up feedback destination email.")
|
33
|
+
end
|
34
|
+
contact_config = umlaut_config.feedback && umlaut_config.feedback.contacts && umlaut_config.feedback.contacts[contact_id]
|
35
|
+
|
36
|
+
unless contact_config && contact_config[:email_address]
|
37
|
+
raise NoFeedbackEmailFoundException.new("Could not find feedback destination email for contact_id: `#{contact_id}`")
|
38
|
+
end
|
39
|
+
|
40
|
+
return contact_config
|
41
|
+
end
|
42
|
+
|
43
|
+
class NoFeedbackEmailFoundException < ArgumentError
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -7,6 +7,11 @@
|
|
7
7
|
# an open proxy with the security problems that would cause.
|
8
8
|
class ResourceController < UmlautController
|
9
9
|
require 'open-uri'
|
10
|
+
require 'timeout'
|
11
|
+
|
12
|
+
# seconds to wait for thing were proxying. Yeah, fairly big num seems neccesary
|
13
|
+
# for Amazon at least.
|
14
|
+
HttpTimeout = 4
|
10
15
|
|
11
16
|
# We really ought to _stream_ the remote response to our client, but I
|
12
17
|
# couldn't get that to work how I wanted in Rails2. Even using
|
@@ -25,7 +30,11 @@ class ResourceController < UmlautController
|
|
25
30
|
end
|
26
31
|
|
27
32
|
proxied_headers = proxy_headers( request, uri.host )
|
28
|
-
|
33
|
+
|
34
|
+
# open-uri :read_timeout is not behaving reliably, resort to Timeout.timeout
|
35
|
+
# should we just be using raw Net::Http and give up on open-uri? remember
|
36
|
+
# to update timeout exceptions in rescue below if you change.
|
37
|
+
remote_response = Timeout.timeout(HttpTimeout) { open(uri, 'rb', proxied_headers) }
|
29
38
|
|
30
39
|
# copy certain headers to our proxied response
|
31
40
|
["Content-Type", "Cache-Control", "Expires", "Content-Length", "Last-Modified", "Etag", "Date"].each do |key|
|
@@ -33,10 +42,19 @@ class ResourceController < UmlautController
|
|
33
42
|
# rack doens't like it if we set a nil value here.
|
34
43
|
response.headers[key] = value unless value.blank?
|
35
44
|
end
|
45
|
+
|
36
46
|
response.headers["X-Original-Url"] = url_str
|
37
47
|
|
38
48
|
# And send the actual result out
|
39
49
|
render(:text => remote_response.read)
|
50
|
+
rescue Timeout::Error, Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
|
51
|
+
msg = "#{e.inspect}: waiting for image proxy from `#{url_str}`; timeout is currently set to #{HttpTimeout}s; returning broken image"
|
52
|
+
relevant_backtrace = e.backtrace.find_all {|line| line =~ /umlaut/}
|
53
|
+
|
54
|
+
logger.warn("#{msg}\n #{relevant_backtrace.join("\n ")}")
|
55
|
+
|
56
|
+
response.headers['X-Original-Url'] = url_str
|
57
|
+
render :text => msg, :status => 504
|
40
58
|
end
|
41
59
|
|
42
60
|
protected
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'nokogiri'
|
2
3
|
module SearchMethods
|
3
4
|
module Sfx4
|
@@ -56,6 +57,7 @@ module SearchMethods
|
|
56
57
|
# than 2 causes false negatives. Otherwise we use it to be consistent
|
57
58
|
# with SFX. This reverse-engineering is full of pitfalls.
|
58
59
|
query = terms.collect do |term|
|
60
|
+
term = replace_problem_tokens(term)
|
59
61
|
"+" + connection.quote_string(term) + (term.length > 2 ? "*" : "")
|
60
62
|
end.join(" ")
|
61
63
|
"MATCH (TS.TITLE_SEARCH) AGAINST ('#{query}' IN BOOLEAN MODE)"
|
@@ -132,6 +134,19 @@ module SearchMethods
|
|
132
134
|
return [context_objects, total_hits]
|
133
135
|
end
|
134
136
|
|
137
|
+
# Query sanitising - remove bad characters from query
|
138
|
+
# (mimics SFX default behaviour - SFX stores them like this
|
139
|
+
# for sorting purposes)
|
140
|
+
# Note - this method is not complete, more substitutions may
|
141
|
+
# be necessary see the relevant Diacritics file in your SFX config
|
142
|
+
# directory for more details
|
143
|
+
def replace_problem_tokens(term)
|
144
|
+
term.gsub!('æ', 'a1')
|
145
|
+
term.gsub!('å', 'a2')
|
146
|
+
|
147
|
+
term
|
148
|
+
end
|
149
|
+
|
135
150
|
# Used for clicks on A, B, C, 0-9, etc.
|
136
151
|
def find_by_group
|
137
152
|
connection = sfx4_db_connection
|
@@ -111,6 +111,12 @@ class UmlautController < ApplicationController
|
|
111
111
|
|
112
112
|
end
|
113
113
|
|
114
|
+
# You can use Umlaut's built-in feedback form if you want
|
115
|
+
# See https://github.com/team-umlaut/umlaut/wiki/Umlaut's-Feedback-Form
|
116
|
+
# feedback do
|
117
|
+
# main_library {:email_address => "dummy@example.org", :label => "Main Library"}
|
118
|
+
# end
|
119
|
+
|
114
120
|
# Advanced topic, you can declaratively configure
|
115
121
|
# what sections of the resolve page are output where
|
116
122
|
# and how using resolve_sections and add_resolve_sections!
|
@@ -8,8 +8,9 @@ module EmailerHelper
|
|
8
8
|
options[:include_labels] ||= false
|
9
9
|
rv =""
|
10
10
|
cite = request.referent.to_citation
|
11
|
-
title = truncate(cite[:title].strip, :length => 70, :
|
12
|
-
|
11
|
+
title = truncate(cite[:title].strip, :length => 70, :separator => ' ')
|
12
|
+
|
13
|
+
rv << (cite[:title_label].strip + ": ")if options[:include_labels] && cite[:title_label]
|
13
14
|
rv << title
|
14
15
|
rv << "\n"
|
15
16
|
if cite[:author]
|
@@ -18,7 +19,7 @@ module EmailerHelper
|
|
18
19
|
rv << "\n"
|
19
20
|
end
|
20
21
|
if cite[:subtitle]
|
21
|
-
rv << (cite[:subtitle_label].strip + ": ") if options[:include_labels]
|
22
|
+
rv << (cite[:subtitle_label].strip + ": ") if options[:include_labels] && cite[:subtitle_label]
|
22
23
|
rv << cite[:subtitle].strip
|
23
24
|
rv << "\n"
|
24
25
|
end
|
@@ -33,4 +34,17 @@ module EmailerHelper
|
|
33
34
|
end
|
34
35
|
return rv
|
35
36
|
end
|
37
|
+
|
38
|
+
def citation_identifiers(request, options = {})
|
39
|
+
citation = request.referent.to_citation
|
40
|
+
str = ""
|
41
|
+
|
42
|
+
str << "ISSN: #{citation[:issn]}\n" if citation[:issn]
|
43
|
+
str << "ISBN: #{citation[:isbn]}\n" if citation[:isbn]
|
44
|
+
citation[:identifiers].each do |identifier|
|
45
|
+
str << "#{identifier}\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
return str
|
49
|
+
end
|
36
50
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class FeedbackMailer < ActionMailer::Base
|
2
|
+
add_template_helper(EmailerHelper)
|
3
|
+
|
4
|
+
default from: UmlautController.umlaut_config.from_email_addr
|
5
|
+
|
6
|
+
# feedback("findit.library.school.edu", "librarian@university.edu",:name => "Joe", :email => "joe@gmail.com", :feedback => "Whatever", :umlaut_request => urequest)
|
7
|
+
# * umlaut_request is optional
|
8
|
+
def feedback(host, to_address, options = {})
|
9
|
+
@host = host
|
10
|
+
@umlaut_request = options[:umlaut_request]
|
11
|
+
@name = options[:name]
|
12
|
+
@email = options[:email]
|
13
|
+
@feedback = options[:feedback]
|
14
|
+
|
15
|
+
# Force permalink creation if we don't have one already
|
16
|
+
if @umlaut_request && @umlaut_request.referent.permalinks.empty?
|
17
|
+
permalink = Permalink.new_with_values!(@umlaut_request.referent, @umlaut_request.referrer_id)
|
18
|
+
@umlaut_request.referent.permalinks << permalink
|
19
|
+
@umlaut_request.save!
|
20
|
+
end
|
21
|
+
|
22
|
+
mail(:to => to_address, :subject => "#{UmlautController.umlaut_config.app_name} Feedback: #{options[:name]}", :reply_to => @email)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -301,6 +301,43 @@ module MetadataHelper
|
|
301
301
|
return nil
|
302
302
|
end
|
303
303
|
|
304
|
+
def get_month(rft)
|
305
|
+
if rft.metadata['date'] =~ /\d\d\d\d\-(\d\d?)/
|
306
|
+
return $1
|
307
|
+
elsif rft.metadata['month']
|
308
|
+
# some link generators use an illegal 'month' parameter
|
309
|
+
return rft.metadata['month']
|
310
|
+
else
|
311
|
+
return nil
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# uses `spage` or tries to parse `pages`
|
316
|
+
def get_spage(rft)
|
317
|
+
if rft.metadata['spage'].present?
|
318
|
+
return rft.metadata['spage']
|
319
|
+
elsif rft.metadata['pages'] =~ /\A *(.*?) *\-.*\Z/
|
320
|
+
return $1
|
321
|
+
elsif rft.metadata['pages'].present?
|
322
|
+
return rft.metadata['pages']
|
323
|
+
else
|
324
|
+
return nil
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# uses `epage` or tries to parse `pages`
|
329
|
+
def get_epage(rft)
|
330
|
+
if rft.metadata['epage'].present?
|
331
|
+
return rft.metadata['epage']
|
332
|
+
elsif rft.metadata['pages'] =~ /\A.*\- *(.*) *\Z/
|
333
|
+
return $1
|
334
|
+
elsif rft.metadata['pages'].present?
|
335
|
+
return rft.metadata['pages']
|
336
|
+
else
|
337
|
+
return nil
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
304
341
|
# Look at weird bad OpenURLs, use heuristics to see if the 'title' probably
|
305
342
|
# represents a journal rather than a book. A guess at best, based on the bad
|
306
343
|
# data we've seen, sigh.
|
data/app/models/referent.rb
CHANGED
@@ -280,6 +280,8 @@ class Referent < ActiveRecord::Base
|
|
280
280
|
end
|
281
281
|
|
282
282
|
# Creates a hash for use in View code to display a citation
|
283
|
+
#
|
284
|
+
# Crazy if/else tree logic, should be refactored, needs more tests first prob.
|
283
285
|
def to_citation
|
284
286
|
citation = {}
|
285
287
|
# call self.metadata once and use the array for efficiency, don't
|
@@ -290,7 +292,7 @@ class Referent < ActiveRecord::Base
|
|
290
292
|
citation[:title] = my_metadata['atitle']
|
291
293
|
citation[:title_label], citation[:container_label] =
|
292
294
|
case my_metadata['genre']
|
293
|
-
when /article|journal|issue/ then ['Article Title', '
|
295
|
+
when /article|journal|issue/ then ['Article Title', 'Journal']
|
294
296
|
when /bookitem|book/ then ['Chapter/Part Title', 'book']
|
295
297
|
when /proceeding|conference/ then ['Proceeding Title', 'conference']
|
296
298
|
when 'report' then ['Report Title','report']
|
@@ -298,7 +300,7 @@ class Referent < ActiveRecord::Base
|
|
298
300
|
if self.format == 'book'
|
299
301
|
['Chapter/Part Title', 'book']
|
300
302
|
elsif self.format == 'journal'
|
301
|
-
['Article Title', '
|
303
|
+
['Article Title', 'Journal']
|
302
304
|
else # default fall through, use much what SFX uses.
|
303
305
|
['Title', '']
|
304
306
|
end
|
@@ -312,11 +314,11 @@ class Referent < ActiveRecord::Base
|
|
312
314
|
end
|
313
315
|
else
|
314
316
|
citation[:title_label] = case my_metadata["genre"]
|
315
|
-
when /article|journal|issue/ then '
|
316
|
-
when /bookitem|book/ then '
|
317
|
-
when /proceeding|conference/ then '
|
318
|
-
when 'report' then '
|
319
|
-
else
|
317
|
+
when /article|journal|issue/i then 'Journal'
|
318
|
+
when /bookitem|book/i then 'Book'
|
319
|
+
when /proceeding|conference/i then 'Conference'
|
320
|
+
when 'report' then 'Report'
|
321
|
+
else nil
|
320
322
|
end
|
321
323
|
['title','btitle','jtitle'].each do | t_type |
|
322
324
|
if ! my_metadata[t_type].blank?
|
@@ -326,7 +328,7 @@ class Referent < ActiveRecord::Base
|
|
326
328
|
end
|
327
329
|
end
|
328
330
|
# add publisher for books
|
329
|
-
if (my_metadata['genre']
|
331
|
+
if (my_metadata['genre'] =~ /book/i)
|
330
332
|
citation[:pub] = my_metadata['pub'] unless my_metadata['pub'].blank?
|
331
333
|
end
|
332
334
|
|
@@ -336,21 +338,22 @@ class Referent < ActiveRecord::Base
|
|
336
338
|
['volume','issue','date'].each do | key |
|
337
339
|
citation[key.to_sym] = my_metadata[key]
|
338
340
|
end
|
339
|
-
|
340
|
-
|
341
|
+
|
342
|
+
if my_metadata["au"].present?
|
343
|
+
citation[:author] = my_metadata["au"].strip
|
341
344
|
elsif my_metadata["aulast"]
|
342
|
-
citation[:author] = my_metadata["aulast"]
|
343
|
-
if
|
344
|
-
citation[:author] += ',
|
345
|
+
citation[:author] = my_metadata["aulast"].strip
|
346
|
+
if my_metadata["aufirst"].present?
|
347
|
+
citation[:author] += ', '+my_metadata["aufirst"].strip
|
345
348
|
else
|
346
|
-
if
|
347
|
-
citation[:author] += ',
|
349
|
+
if my_metadata["auinit"].present?
|
350
|
+
citation[:author] += ', '+my_metadata["auinit"].strip
|
348
351
|
else
|
349
|
-
if
|
350
|
-
citation[:author] += ',
|
352
|
+
if my_metadata["auinit1"].present?
|
353
|
+
citation[:author] += ', '+my_metadata["auinit1"].strip
|
351
354
|
end
|
352
|
-
if
|
353
|
-
citation[:author] += my_metadata["auinitm"]
|
355
|
+
if my_metadata["auinitm"].present?
|
356
|
+
citation[:author] += my_metadata["auinitm"].strip
|
354
357
|
end
|
355
358
|
end
|
356
359
|
end
|
data/app/models/request.rb
CHANGED
@@ -16,6 +16,8 @@ class Request < ActiveRecord::Base
|
|
16
16
|
# responses show up first
|
17
17
|
has_many :service_responses, :order => 'id ASC'
|
18
18
|
|
19
|
+
has_many :clickthroughs
|
20
|
+
|
19
21
|
belongs_to :referent, :include => :referent_values
|
20
22
|
# holds a hash representing submitted http params
|
21
23
|
serialize :http_env
|
@@ -35,6 +37,7 @@ class Request < ActiveRecord::Base
|
|
35
37
|
|
36
38
|
# Create a context object from our http params
|
37
39
|
context_object = OpenURL::ContextObject.new_from_form_vars( co_params )
|
40
|
+
|
38
41
|
# Sometimes umlaut puts in a 'umlaut.request_id' parameter.
|
39
42
|
# first look by that, if we have it, for an existing request.
|
40
43
|
request_id = params['umlaut.request_id']
|
@@ -33,7 +33,6 @@ class DissertationCatch < ReferentFilter
|
|
33
33
|
issn = get_identifier(:urn, "issn", referent)
|
34
34
|
return unless issn
|
35
35
|
|
36
|
-
|
37
36
|
# normalize removing hyphen
|
38
37
|
issn.gsub!('-', '')
|
39
38
|
|
@@ -66,6 +65,7 @@ class DissertationCatch < ReferentFilter
|
|
66
65
|
referent.remove_value("issue_start")
|
67
66
|
referent.remove_value("spage")
|
68
67
|
referent.remove_value("epage")
|
68
|
+
referent.remove_value("pages")
|
69
69
|
end
|
70
70
|
|
71
71
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'isbn_link'
|
2
|
+
|
3
|
+
# Blind (not pre-checked for hits) link to compare prices
|
4
|
+
# at AllBookstores.com
|
5
|
+
# -- because that site seemed good, includes shipping prices, has decent UX,
|
6
|
+
# and includes independent bookstores like Powell's and The Strand.
|
7
|
+
#
|
8
|
+
# subclasses IsbnLink
|
9
|
+
class AllBooksDotCom < IsbnLink
|
10
|
+
def initialize(config)
|
11
|
+
super(config)
|
12
|
+
|
13
|
+
@display_text ||= "Compare online prices"
|
14
|
+
@display_name ||= "AllBookstores.com"
|
15
|
+
@link_template ||= "http://www.allbookstores.com/book/compare/%s"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'openurl'
|
3
|
+
|
4
|
+
# Just creates an OpenURL link out, corresponding to the current OpenURL.
|
5
|
+
# But tweaked in ways to try and work out for sending to ILLiad.
|
6
|
+
#
|
7
|
+
# If you use SFX, you may want to just use SFX's built-in ILLiad targets, which
|
8
|
+
# will generally be picked up by the SFX Umlaut service.
|
9
|
+
#
|
10
|
+
# But if you don't or if you are unhappy with what SFX is doing, you could turn
|
11
|
+
# off ILLiad target(s) in SFX (or configure Umlaut SFX adapter to ignore them),
|
12
|
+
# and use this instead.
|
13
|
+
#
|
14
|
+
# # Pre-empting
|
15
|
+
#
|
16
|
+
# You may want to show ILLiad links only if there is no fulltext, or only if there
|
17
|
+
# is no fulltext from a certain service. You can use Umlaut's standard service
|
18
|
+
# pre-emption configuration for that.
|
19
|
+
#
|
20
|
+
# Do not produce ILLiad links if there are ANY fulltext links already produced
|
21
|
+
# in the request. In umlaut_services.yml:
|
22
|
+
#
|
23
|
+
# illiad:
|
24
|
+
# type: Illiad
|
25
|
+
# priority: 4
|
26
|
+
# preempted_by:
|
27
|
+
# existing_type: fulltext
|
28
|
+
#
|
29
|
+
# Or, preempt ILLiad links only if there are fulltext links created by SFX
|
30
|
+
# specifically (assume "SFX" is the id of your sfx service in umlaut_services.yml)
|
31
|
+
#
|
32
|
+
# illiad:
|
33
|
+
# type: Illiad
|
34
|
+
# priority: 4
|
35
|
+
# preempted_by:
|
36
|
+
# existing_service: SFX
|
37
|
+
# existing_type: fulltext
|
38
|
+
#
|
39
|
+
# Pre-emption can only take account of services already generated before ILLiad
|
40
|
+
# service is triggered, so you'd want to make sure to give ILLiad a priority greater
|
41
|
+
# than the services you want to potentially preempt it.
|
42
|
+
#
|
43
|
+
# # Config parameters
|
44
|
+
# ## Required
|
45
|
+
#
|
46
|
+
# * base_url: Illiad base url, such as `http://ill.university.edu/site/illiad.dll/OpenURL`. It should probably end in '/illiad.dll/OpenURL'
|
47
|
+
#
|
48
|
+
# ## Optional
|
49
|
+
#
|
50
|
+
# * display_name: Default "Place ILL Request"
|
51
|
+
# * sid_suffix: Default " (via Umlaut)", appended to existing sid before sending to ILLiad.
|
52
|
+
# * notes: Some additional notes to display under the link.
|
53
|
+
class Illiad < Service
|
54
|
+
include MetadataHelper
|
55
|
+
|
56
|
+
required_config_params :base_url
|
57
|
+
|
58
|
+
def initialize(config)
|
59
|
+
@service_type = "document_delivery"
|
60
|
+
@display_name = "Place ILL Request"
|
61
|
+
@sid_suffix = " (via Umlaut)"
|
62
|
+
|
63
|
+
super(config)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def service_types_generated
|
68
|
+
[ServiceTypeValue[@service_type.to_sym]]
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle(request)
|
72
|
+
target_url = @base_url + "?" + illiad_query_parameters(request).to_query
|
73
|
+
|
74
|
+
request.add_service_response(
|
75
|
+
:service =>self,
|
76
|
+
:display_text => @display_name,
|
77
|
+
:url => target_url,
|
78
|
+
:notes => @notes,
|
79
|
+
:service_type_value => @service_type.to_sym
|
80
|
+
)
|
81
|
+
|
82
|
+
return request.dispatched(self, true)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def illiad_query_parameters(request)
|
87
|
+
metadata = request.referent.metadata
|
88
|
+
|
89
|
+
qp = {}
|
90
|
+
|
91
|
+
qp['genre'] = metadata['genre']
|
92
|
+
|
93
|
+
if metadata['aulast']
|
94
|
+
qp["aulast"] = metadata['aulast']
|
95
|
+
qp["aufirst"] = [metadata['aufirst'], metadata["auinit"]].find {|a| a.present?}
|
96
|
+
else
|
97
|
+
qp["au"] = metadata["au"]
|
98
|
+
end
|
99
|
+
|
100
|
+
qp['volume'] = metadata['volume']
|
101
|
+
qp['issue'] = metadata['issue']
|
102
|
+
|
103
|
+
qp['spage'] = get_spage(request.referent)
|
104
|
+
qp['epage'] = get_epage(request.referent)
|
105
|
+
|
106
|
+
qp['issn'] = get_issn(request.referent)
|
107
|
+
qp['isbn'] = get_isbn(request.referent)
|
108
|
+
qp['pmid'] = get_pmid(request.referent)
|
109
|
+
|
110
|
+
qp['stitle'] = metadata['stitle']
|
111
|
+
|
112
|
+
qp['sid'] = sid_for_illiad(request)
|
113
|
+
|
114
|
+
qp['year'] = get_year(request.referent)
|
115
|
+
qp['month'] = get_month(request.referent)
|
116
|
+
|
117
|
+
qp['atitle'] = metadata['atitle']
|
118
|
+
|
119
|
+
# ILLiad always wants 'title', not the various title keys that exist in OpenURL
|
120
|
+
qp['title'] = [metadata['jtitle'], metadata['btitle'], metadata['title']].find {|a| a.present?}
|
121
|
+
|
122
|
+
# For some reason these go to ILLiad prefixed with rft.
|
123
|
+
qp['rft.pub'] = metadata['pub']
|
124
|
+
qp['rft.place'] = metadata['place']
|
125
|
+
qp['rft.edition'] = metadata['edition']
|
126
|
+
|
127
|
+
# ILLiad likes OCLCnum in `rfe_dat`
|
128
|
+
qp['rfe_dat'] = get_oclcnum(request.referent)
|
129
|
+
|
130
|
+
|
131
|
+
# Genre normalization. ILLiad pays a lot of attention to `&genre`, but
|
132
|
+
# doesn't use actual OpenURL rft_val_fmt
|
133
|
+
if request.referent.format == "dissertation"
|
134
|
+
qp['genre'] = 'dissertation'
|
135
|
+
elsif qp['isbn'].present? && qp['genre'] == 'book' && qp['atitle'] && (! qp['issn'].present?)
|
136
|
+
# actually a book chapter, not a book, fix it.
|
137
|
+
qp['genre'] = 'bookitem'
|
138
|
+
elsif qp['issn'].present? && qp['atitle'].present?
|
139
|
+
# Otherwise, if there is an ISSN, we force genre to 'article', seems
|
140
|
+
# to work best.
|
141
|
+
qp['genre'] = 'article'
|
142
|
+
elsif qp['genre'] == 'unknown' && qp['atitle'].blank?
|
143
|
+
# WorldCat likes to send these, ILLiad is happier considering them 'book'
|
144
|
+
qp['genre'] = "book"
|
145
|
+
end
|
146
|
+
|
147
|
+
# trim empty ones please
|
148
|
+
qp.delete_if {|k, v| v.blank?}
|
149
|
+
|
150
|
+
return qp
|
151
|
+
end
|
152
|
+
|
153
|
+
# Grab a source label out of `sid` or `rfr_id`, add on our suffix.
|
154
|
+
def sid_for_illiad(request)
|
155
|
+
sid = request.referrer_id || ""
|
156
|
+
|
157
|
+
sid = sid.gsub(%r{\Ainfo\:sid/}, '')
|
158
|
+
|
159
|
+
return "#{sid}#{@sid_suffix}"
|
160
|
+
end
|
161
|
+
end
|
@@ -2,6 +2,11 @@
|
|
2
2
|
# info for online sellers. There are potentially other services we could
|
3
3
|
# make use of there in the future too.
|
4
4
|
#
|
5
|
+
# By default makes an API request in advance to check if book is available, and thus requires
|
6
|
+
# an API key. However, ISBNdb seems no longer as reliable as it once was there.
|
7
|
+
# You may not want to use ISBNdb at all, see AllBooksDotCom as an alternative
|
8
|
+
# (although with no pre-check for hits.)
|
9
|
+
#
|
5
10
|
# jrochkind talked to the operators of isbndb and got a very high traffic limit
|
6
11
|
# (instead of the default free 500 requests/day), for free. You could try the
|
7
12
|
# same.
|
@@ -22,7 +27,7 @@ class IsbnDb < Service
|
|
22
27
|
end
|
23
28
|
|
24
29
|
def initialize(config)
|
25
|
-
@timeout =
|
30
|
+
@timeout = 3
|
26
31
|
@display_text = "Compare online prices"
|
27
32
|
@display_name = "ISBNdb.com"
|
28
33
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'isbn'
|
2
|
+
|
3
|
+
# A simple service to generate a blind link (NOT pre-checked for hits, just
|
4
|
+
# blindly created from a template) out to a service based on ISBN.
|
5
|
+
#
|
6
|
+
# May likely be sub-classed for specific services (see AllBooks.com),
|
7
|
+
# which set default values.
|
8
|
+
#
|
9
|
+
# * :link_template. => String where "%s" will be replaced with ISBN
|
10
|
+
# * :display_name
|
11
|
+
# * :dispaly_text. Such as "Compare online prices
|
12
|
+
# * :isbn_normalize. Default nil, set to :ten or :thirteen if you need to normalize
|
13
|
+
# ISBN before substituting in :link_template.
|
14
|
+
class IsbnLink < Service
|
15
|
+
include MetadataHelper
|
16
|
+
|
17
|
+
def service_types_generated
|
18
|
+
return [ServiceTypeValue['highlighted_link']]
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(config)
|
22
|
+
@display_text = "Compare online prices"
|
23
|
+
@isbn_normalize = nil
|
24
|
+
|
25
|
+
super(config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def handle(umlaut_request)
|
29
|
+
|
30
|
+
isbn = get_isbn(umlaut_request.referent)
|
31
|
+
|
32
|
+
# No isbn, nothing we can do.
|
33
|
+
return umlaut_request.dispatched(self, true) if isbn.blank?
|
34
|
+
|
35
|
+
# invalid isbn? forget it.
|
36
|
+
return umlaut_request.dispatched(self, true) unless ISBN.valid?(isbn)
|
37
|
+
|
38
|
+
if @isbn_normalize == :ten
|
39
|
+
isbn = ISBN.ten(isbn)
|
40
|
+
elsif @isbn_normalize == :thirteen
|
41
|
+
isbn = ISBN.thirteen(isbn)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add the link
|
45
|
+
link = @link_template.gsub("%s", isbn)
|
46
|
+
|
47
|
+
umlaut_request.add_service_response(
|
48
|
+
:service=>self,
|
49
|
+
:url=> link,
|
50
|
+
:display_text=> @display_text,
|
51
|
+
:service_type_value => ServiceTypeValue[:highlighted_link]
|
52
|
+
)
|
53
|
+
|
54
|
+
return umlaut_request.dispatched(self, true)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
# Service adapter plug-in.
|
2
|
+
#
|
3
|
+
# NOTE: This is based on deprecated Scopus API's, Scopus will take them
|
4
|
+
# away 31 December 2014. Please see scopus2.rb instead which uses
|
5
|
+
# new Scopus API's.
|
2
6
|
#
|
3
7
|
# PURPOSE: Includes "cited by", "similar articles" and "more by these authors"
|
4
8
|
# links from scopus. Also will throw in an abstract from Scopus if found.
|