umlaut 3.0.0alpha1
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/LICENSE +7 -0
- data/README.md +49 -0
- data/Rakefile +37 -0
- data/app/assets/images/error.gif +0 -0
- data/app/assets/images/export_bg_bot.gif +0 -0
- data/app/assets/images/export_bg_mid.gif +0 -0
- data/app/assets/images/export_bg_top.gif +0 -0
- data/app/assets/images/famfamfam/book_open.png +0 -0
- data/app/assets/images/famfamfam/cross.png +0 -0
- data/app/assets/images/famfamfam/page_sound.gif +0 -0
- data/app/assets/images/famfamfam/page_text.gif +0 -0
- data/app/assets/images/famfamfam/page_up.gif +0 -0
- data/app/assets/images/famfamfam/page_white.png +0 -0
- data/app/assets/images/famfamfam/readme.html +1495 -0
- data/app/assets/images/famfamfam/tiny_cross.png +0 -0
- data/app/assets/images/frame_remove.gif +0 -0
- data/app/assets/images/ico_go.gif +0 -0
- data/app/assets/images/jhu_findit.gif +0 -0
- data/app/assets/images/list_closed.png +0 -0
- data/app/assets/images/list_open.png +0 -0
- data/app/assets/images/more_info.gif +0 -0
- data/app/assets/images/rails.png +0 -0
- data/app/assets/images/request.gif +0 -0
- data/app/assets/images/spinner.gif +0 -0
- data/app/assets/javascripts/umlaut/ajax_windows.js +35 -0
- data/app/assets/javascripts/umlaut/ensure_window_size.js.erb +34 -0
- data/app/assets/javascripts/umlaut/expand_contract_toggle.js +25 -0
- data/app/assets/javascripts/umlaut/search_autocomplete.js +46 -0
- data/app/assets/javascripts/umlaut/simple_visible_toggle.js +8 -0
- data/app/assets/javascripts/umlaut/update_html.js +152 -0
- data/app/assets/javascripts/umlaut.js +17 -0
- data/app/assets/stylesheets/umlaut.css +857 -0
- data/app/controllers/application_controller.rb +14 -0
- data/app/controllers/export_email_controller.rb +123 -0
- data/app/controllers/js_helper_controller.rb +10 -0
- data/app/controllers/link_router_controller.rb +87 -0
- data/app/controllers/open_search_controller.rb +9 -0
- data/app/controllers/resolve_controller.rb +288 -0
- data/app/controllers/resource_controller.rb +83 -0
- data/app/controllers/search_controller.rb +328 -0
- data/app/controllers/search_methods/sfx3.rb +148 -0
- data/app/controllers/search_methods/sfx4.rb +257 -0
- data/app/controllers/search_methods/sfx_api.rb +47 -0
- data/app/controllers/store_controller.rb +64 -0
- data/app/controllers/umlaut/controller_behavior.rb +20 -0
- data/app/controllers/umlaut/controller_logic.rb +96 -0
- data/app/controllers/umlaut/error_handling.rb +48 -0
- data/app/controllers/umlaut_controller.rb +112 -0
- data/app/helpers/application_helper.rb +4 -0
- data/app/helpers/emailer_helper.rb +43 -0
- data/app/helpers/export_email_helper.rb +34 -0
- data/app/helpers/open_search_helper.rb +7 -0
- data/app/helpers/resolve_helper.rb +225 -0
- data/app/helpers/search_helper.rb +50 -0
- data/app/helpers/umlaut/footer_helper.rb +64 -0
- data/app/helpers/umlaut/helper.rb +62 -0
- data/app/helpers/umlaut/html_head_helper.rb +37 -0
- data/app/helpers/umlaut/url_generation.rb +77 -0
- data/app/mailers/emailer.rb +48 -0
- data/app/models/clickthrough.rb +2 -0
- data/app/models/collection.rb +259 -0
- data/app/models/crossref_lookup.rb +2 -0
- data/app/models/dispatched_service.rb +58 -0
- data/app/models/permalink.rb +29 -0
- data/app/models/referent.rb +473 -0
- data/app/models/referent_value.rb +14 -0
- data/app/models/request.rb +449 -0
- data/app/models/service_response.rb +179 -0
- data/app/models/service_store.rb +59 -0
- data/app/models/service_type_value.rb +58 -0
- data/app/models/service_wave.rb +150 -0
- data/app/models/sfx_db/az_additional_title.rb +11 -0
- data/app/models/sfx_db/az_letter_group.rb +11 -0
- data/app/models/sfx_db/az_title.rb +38 -0
- data/app/models/sfx_db/az_title_v2.rb +34 -0
- data/app/models/sfx_db/isbn.rb +12 -0
- data/app/models/sfx_db/issn.rb +12 -0
- data/app/models/sfx_db/object.rb +35 -0
- data/app/models/sfx_db/object_portfolio.rb +6 -0
- data/app/models/sfx_db/publisher.rb +10 -0
- data/app/models/sfx_db/sfx_db_base.rb +54 -0
- data/app/models/sfx_db/target.rb +9 -0
- data/app/models/sfx_db/target_service.rb +10 -0
- data/app/models/sfx_db/title.rb +10 -0
- data/app/models/sfx_db.rb +10 -0
- data/app/models/sfx_url.rb +35 -0
- data/app/views/emailer/citation.text.erb +28 -0
- data/app/views/emailer/short_citation.text.erb +8 -0
- data/app/views/export_email/_email.html.erb +25 -0
- data/app/views/export_email/_send_email.html.erb +3 -0
- data/app/views/export_email/_send_txt.html.erb +3 -0
- data/app/views/export_email/_txt.html.erb +62 -0
- data/app/views/export_email/email.html.erb +3 -0
- data/app/views/export_email/send_email.html.erb +1 -0
- data/app/views/export_email/send_txt.html.erb +1 -0
- data/app/views/export_email/txt.html.erb +3 -0
- data/app/views/js_helper/loader.erb.js +13 -0
- data/app/views/layouts/umlaut.html.erb +52 -0
- data/app/views/open_search/index.html.erb +9 -0
- data/app/views/resolve/_api_in_progress.xml.erb +21 -0
- data/app/views/resolve/_background_progress.html.erb +51 -0
- data/app/views/resolve/_background_updater.html.erb +38 -0
- data/app/views/resolve/_citation.html.erb +87 -0
- data/app/views/resolve/_coins.html.erb +1 -0
- data/app/views/resolve/_compact_citation.html.erb +33 -0
- data/app/views/resolve/_cover_image.html.erb +35 -0
- data/app/views/resolve/_fulltext.html.erb +55 -0
- data/app/views/resolve/_help.html.erb +17 -0
- data/app/views/resolve/_holding.html.erb +91 -0
- data/app/views/resolve/_related_items.html.erb +35 -0
- data/app/views/resolve/_search_inside.html.erb +62 -0
- data/app/views/resolve/_section_display.html.erb +49 -0
- data/app/views/resolve/_service_errors.html.erb +29 -0
- data/app/views/resolve/_standard_response_item.html.erb +89 -0
- data/app/views/resolve/api.xml.builder +72 -0
- data/app/views/resolve/background_status.html.erb +26 -0
- data/app/views/resolve/index.html.erb +73 -0
- data/app/views/resolve/partial_html_sections.xml.erb +30 -0
- data/app/views/search/_a_to_z.html.erb +6 -0
- data/app/views/search/_citation.html.erb +94 -0
- data/app/views/search/_pager.html.erb +60 -0
- data/app/views/search/books.html.erb +103 -0
- data/app/views/search/journal_search.html.erb +90 -0
- data/app/views/search/journals.html.erb +167 -0
- data/app/views/search/opensearch_description.rxml +10 -0
- data/app/views/testing/index.html.erb +1 -0
- data/app/views/umlaut/README +5 -0
- data/app/views/umlaut/error.html.erb +45 -0
- data/db/migrate/01_umlaut_init.rb +113 -0
- data/db/orig_fixed_data/service_type_values.yml +120 -0
- data/db/seeds.rb +7 -0
- data/lib/CronTab.rb +192 -0
- data/lib/aws_product_sign.rb +146 -0
- data/lib/exlibris/aleph/patron.rb +64 -0
- data/lib/exlibris/aleph/record.rb +54 -0
- data/lib/exlibris/aleph/rest_api.rb +29 -0
- data/lib/exlibris/primo/holding.rb +192 -0
- data/lib/exlibris/primo/rsrc.rb +17 -0
- data/lib/exlibris/primo/searcher.rb +276 -0
- data/lib/exlibris/primo/source/aleph.rb +46 -0
- data/lib/exlibris/primo/source/distribution/nyu_aleph.rb +323 -0
- data/lib/exlibris/primo/toc.rb +17 -0
- data/lib/exlibris/primo_ws.rb +140 -0
- data/lib/generators/templates/umlaut_services.yml +237 -0
- data/lib/generators/umlaut/asset_hooks_generator.rb +44 -0
- data/lib/generators/umlaut/install_generator.rb +110 -0
- data/lib/hip3/bib.rb +291 -0
- data/lib/hip3/bib_searcher.rb +302 -0
- data/lib/hip3/custom_field_lookup.rb +44 -0
- data/lib/hip3/holding.rb +50 -0
- data/lib/hip3/item.rb +65 -0
- data/lib/hip3/receipt.rb +7 -0
- data/lib/hip3/serial_copy.rb +82 -0
- data/lib/holding.rb +32 -0
- data/lib/marc_helper.rb +254 -0
- data/lib/metadata_helper.rb +312 -0
- data/lib/opensearch_feed.rb +398 -0
- data/lib/opensearch_query.rb +98 -0
- data/lib/referent_filter.rb +16 -0
- data/lib/referent_filters/dissertation_catch.rb +45 -0
- data/lib/section_renderer.rb +503 -0
- data/lib/service.rb +336 -0
- data/lib/service_adaptors/ajax_export.rb +37 -0
- data/lib/service_adaptors/amazon.rb +412 -0
- data/lib/service_adaptors/blacklight.rb +327 -0
- data/lib/service_adaptors/book_finder.rb +40 -0
- data/lib/service_adaptors/bx.rb +51 -0
- data/lib/service_adaptors/cover_thing.rb +73 -0
- data/lib/service_adaptors/elsevier_cover.rb +57 -0
- data/lib/service_adaptors/email_export.rb +10 -0
- data/lib/service_adaptors/ezproxy.rb +171 -0
- data/lib/service_adaptors/google_book_search.rb +442 -0
- data/lib/service_adaptors/gpo.rb +124 -0
- data/lib/service_adaptors/hathi_trust.rb +308 -0
- data/lib/service_adaptors/hip3_service.rb +150 -0
- data/lib/service_adaptors/hip_holding_search.rb +237 -0
- data/lib/service_adaptors/internet_archive.rb +488 -0
- data/lib/service_adaptors/isbn_db.rb +86 -0
- data/lib/service_adaptors/isi.rb +258 -0
- data/lib/service_adaptors/jcr.rb +146 -0
- data/lib/service_adaptors/opac.rb +351 -0
- data/lib/service_adaptors/open_library.rb +316 -0
- data/lib/service_adaptors/open_library_cover.rb +73 -0
- data/lib/service_adaptors/primo_service.rb +392 -0
- data/lib/service_adaptors/primo_source.rb +78 -0
- data/lib/service_adaptors/pubmed.rb +133 -0
- data/lib/service_adaptors/request_to_fixture.rb +68 -0
- data/lib/service_adaptors/scopus.rb +295 -0
- data/lib/service_adaptors/sfx-new.rb +557 -0
- data/lib/service_adaptors/sfx.rb +566 -0
- data/lib/service_adaptors/sfx_backchannel_record.rb +69 -0
- data/lib/service_adaptors/txt_holding_export.rb +32 -0
- data/lib/service_adaptors/ulrichs_cover.rb +57 -0
- data/lib/service_adaptors/ulrichs_link.rb +47 -0
- data/lib/service_adaptors/worldcat.rb +116 -0
- data/lib/service_adaptors/worldcat_identities.rb +591 -0
- data/lib/tasks/umlaut.rake +134 -0
- data/lib/umlaut/default_configuration.rb +5 -0
- data/lib/umlaut/routes.rb +136 -0
- data/lib/umlaut/version.rb +3 -0
- data/lib/umlaut.rb +37 -0
- data/lib/umlaut_configurable.rb +343 -0
- data/lib/umlaut_http.rb +100 -0
- data/lib/xml_schema_helper.rb +109 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/umlaut_controller.rb +112 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database-jhu.yml +44 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +34 -0
- data/test/dummy/config/environments/production.rb +60 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +61 -0
- data/test/dummy/config/umlaut_services.yml +237 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20111228211210_umlaut_init.rb +113 -0
- data/test/dummy/db/schema.rb +124 -0
- data/test/dummy/log/development.log +12981 -0
- data/test/dummy/log/production.log +0 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/tmp/cache/assets/C5F/340/sprockets%2F99692920160b7a279b86a80415b79db7 +0 -0
- data/test/dummy/tmp/cache/assets/C70/4D0/sprockets%2F034ad2036e623081bd352800786dfe80 +0 -0
- data/test/dummy/tmp/cache/assets/C73/920/sprockets%2Fd371318f22900492fd180f17c5e2a504 +9268 -0
- data/test/dummy/tmp/cache/assets/C80/980/sprockets%2Fc94807409c1523d43e18d25f35d93c41 +0 -0
- data/test/dummy/tmp/cache/assets/C8F/780/sprockets%2Fe47e28558116fb5f8038754e60d1961d +11769 -0
- data/test/dummy/tmp/cache/assets/CAA/EB0/sprockets%2F1d179210e8b76f1ea63c802688a015e4 +9271 -0
- data/test/dummy/tmp/cache/assets/CBB/9C0/sprockets%2F706f28923fb754cad04b9107c89986a1 +0 -0
- data/test/dummy/tmp/cache/assets/CBF/B60/sprockets%2F08ca89671549936265dcb673bf02e36f +0 -0
- data/test/dummy/tmp/cache/assets/CC9/9F0/sprockets%2F306166316e2cafd13c15e62b51a2339d +0 -0
- data/test/dummy/tmp/cache/assets/CF6/F20/sprockets%2F5b2ffa1103079dfd555197838f87a99f +0 -0
- data/test/dummy/tmp/cache/assets/CF7/2B0/sprockets%2F25a7c73655bd3598173b39d9f98bcd46 +862 -0
- data/test/dummy/tmp/cache/assets/CFE/080/sprockets%2F37fe9f4255baddbd549a659914929398 +0 -0
- data/test/dummy/tmp/cache/assets/D22/060/sprockets%2F9aec77b768e91a802d284271c58e2f7e +21357 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D33/6D0/sprockets%2F500129c57f1146e556ec3aacd6cd38c1 +0 -0
- data/test/dummy/tmp/cache/assets/D33/FD0/sprockets%2F2ba0b4e6334a77b923e5f770381bb2bf +0 -0
- data/test/dummy/tmp/cache/assets/D42/C20/sprockets%2Fbcf14e437b1582bf93b77670acf8e090 +21353 -0
- data/test/dummy/tmp/cache/assets/D50/A30/sprockets%2F7d8b294ac433db5d056538f8cf7c66b9 +0 -0
- data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +872 -0
- data/test/dummy/tmp/cache/assets/D65/590/sprockets%2Fc1bb92fc3406a126b7dd302edc96d629 +0 -0
- data/test/dummy/tmp/cache/assets/D71/6B0/sprockets%2Fde558b71b494cf09b1bf055c8dff0353 +0 -0
- data/test/dummy/tmp/cache/assets/D72/610/sprockets%2Fa8c708eeb30ef93de34d755d4f45d023 +859 -0
- data/test/dummy/tmp/cache/assets/D76/AD0/sprockets%2Fe2158cde93188cf5ab6457bc6d6602ec +0 -0
- data/test/dummy/tmp/cache/assets/D7A/E40/sprockets%2F9622ffcc499a57627cd1bb18fe31b8e4 +11772 -0
- data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
- data/test/dummy/tmp/cache/assets/D9B/770/sprockets%2F8aacf02eb7dbb0949704b28f27b87e0b +0 -0
- data/test/dummy/tmp/cache/assets/DA6/A80/sprockets%2F92e26d8e58d5bcc8b8f6c25d1b05b9c1 +0 -0
- data/test/dummy/tmp/cache/assets/DE8/790/sprockets%2Fd1333bde2b9aafcc712d11dd09ab35d8 +0 -0
- data/test/dummy/tmp/cache/assets/DF7/F30/sprockets%2F7bc16c4109b17fabe29f8ddbbf732d1c +374 -0
- data/test/dummy/tmp/cache/assets/E03/570/sprockets%2F493bdc0ac14cd4f57fdfe4253f992bde +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E0B/4B0/sprockets%2F7988df51a61c81ce6ede4a2d4c8cce4f +377 -0
- data/test/dummy/tmp/cache/assets/E5F/960/sprockets%2Fdc007b6cad5c7ef08e33ec28cfff0ef6 +0 -0
- data/test/fixtures/dispatched_services.yml +5 -0
- data/test/fixtures/permalinks.yml +5 -0
- data/test/fixtures/referent_values.yml +1734 -0
- data/test/fixtures/referents.yml +156 -0
- data/test/fixtures/requests.yml +284 -0
- data/test/fixtures/service_responses.yml +5 -0
- data/test/fixtures/sfx_urls.yml +4 -0
- data/test/performance/browsing_test.rb +9 -0
- data/test/test_helper.rb +10 -0
- data/test/umlaut_test.rb +7 -0
- data/test/unit/aleph_patron_test.rb +39 -0
- data/test/unit/aleph_record_benchmarks.rb +28 -0
- data/test/unit/aleph_record_test.rb +30 -0
- data/test/unit/aws_product_sign_test.rb +93 -0
- data/test/unit/collection_test.rb +76 -0
- data/test/unit/google_book_search_test.rb +101 -0
- data/test/unit/primo_searcher_test.rb +403 -0
- data/test/unit/primo_service_test.rb +939 -0
- data/test/unit/primo_ws_test.rb +131 -0
- data/test/unit/service_response_test.rb +9 -0
- data/test/unit/service_test.rb +33 -0
- metadata +580 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
# Looks for cover images from OpenLibrary Cover API.
|
2
|
+
# Lookig up covers in OL can require multiple HTTP requests, one for each
|
3
|
+
# identifier, which can sometimes lead to slowness.
|
4
|
+
# OL also doesn't have great cover image coverage. So if you have access to
|
5
|
+
# Amazon or Google covers, you probably don't need this service.
|
6
|
+
class OpenLibraryCover < Service
|
7
|
+
require 'net/http'
|
8
|
+
|
9
|
+
def service_types_generated
|
10
|
+
return [ServiceTypeValue[:cover_image]]
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
@base_url = "http://covers.openlibrary.org/b/"
|
15
|
+
@size = "medium" # "small", "medium" or "large"
|
16
|
+
|
17
|
+
@credits = {
|
18
|
+
"OpenLibrary" => "http://openlibrary.org/"
|
19
|
+
}
|
20
|
+
|
21
|
+
super(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle(request)
|
25
|
+
ids = {
|
26
|
+
:isbn => request.referent.isbn,
|
27
|
+
:oclc => request.referent.oclcnum,
|
28
|
+
:lccn => request.referent.lccn
|
29
|
+
}
|
30
|
+
ids.delete_if {|k,v| v.blank?}
|
31
|
+
|
32
|
+
# Return if we don't have any identifiers
|
33
|
+
return request.dispatched(self, true) unless ids.size > 0
|
34
|
+
|
35
|
+
# What order is best for trying first?
|
36
|
+
[:isbn, :oclc, :lccn].each do |type|
|
37
|
+
next unless ids[type]
|
38
|
+
|
39
|
+
uri = cover_uri(type, ids[type] )
|
40
|
+
s_time = Time.now
|
41
|
+
response = Net::HTTP.get_response(URI.parse(uri))
|
42
|
+
Rails.logger.debug("#{@id}: #{Time.now - s_time}s to lookup #{uri}")
|
43
|
+
|
44
|
+
if response.kind_of?( Net::HTTPNotFound )
|
45
|
+
# OL has no cover
|
46
|
+
next
|
47
|
+
end
|
48
|
+
|
49
|
+
unless response.kind_of?( Net::HTTPSuccess )
|
50
|
+
# unexpected response
|
51
|
+
Rails.logger.error("#{@id}: Error in HTTP response when requesting #{uri}, #{response.inspect}")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Got this far, we've got a response.
|
55
|
+
request.add_service_response(
|
56
|
+
:service => self,
|
57
|
+
:display_text => "Cover Image",
|
58
|
+
:size => "medium",
|
59
|
+
:url => uri,
|
60
|
+
:service_type_value => :cover_image
|
61
|
+
)
|
62
|
+
break
|
63
|
+
end
|
64
|
+
|
65
|
+
return request.dispatched(self, true)
|
66
|
+
end
|
67
|
+
|
68
|
+
def cover_uri(type, id)
|
69
|
+
@base_url + type.to_s + "/" + id.to_s + "-" + @size[0,1].upcase + ".jpg?default=false"
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,392 @@
|
|
1
|
+
# == Overview
|
2
|
+
# PrimoService is a Service that makes a call to the Primo web services based on the OpenURL key value pairs.
|
3
|
+
#--
|
4
|
+
# NOT YET:
|
5
|
+
# It first looks for rft.primo *DEPRECATED*, failing that, it parses the identifier for an id.
|
6
|
+
#++
|
7
|
+
# It first looks for rft.primo, the Primo id.
|
8
|
+
# If the Primo id is present, the service gets the PNX record from the Primo web
|
9
|
+
# services.
|
10
|
+
# If no Primo id is found, the service searches Primo by (in order of precedence):
|
11
|
+
# * ISBN
|
12
|
+
# * ISSN
|
13
|
+
# * Title, Author, Genre
|
14
|
+
#
|
15
|
+
# == Available Services
|
16
|
+
# Several service types are available in the Primo service. The default service types are:
|
17
|
+
# fulltext, holding, holding_search, table_of_contents, referent_enhance, cover_image
|
18
|
+
# Available service types are listed below and can be configured using the service_types parameter
|
19
|
+
# in service.yml:
|
20
|
+
# * fulltext - parsed from links/linktorsrc elements in the PNX record
|
21
|
+
# * holding - parsed from display/availlibrary elements in the PNX record
|
22
|
+
# * holding_search - link to an exact title search in Primo if no holdings found AND the OpenURL did not come from Primo
|
23
|
+
# * primo_source - similar to holdings but used in conjuction with the PrimoSource service to map Primo records to their original sources; a PrimoSource service must be defined in service.yml for this to work
|
24
|
+
# * table_of_contents - parsed from links/linktotoc elements in the PNX record
|
25
|
+
# * referent_enhance - metadata parsed from the addata section of the PNX record when the record was found by Primo id
|
26
|
+
# * cover_image - parsed from first addata/lad02 element in the PNX record
|
27
|
+
#
|
28
|
+
# ==Available Parameters
|
29
|
+
# Several configurations parameters are available to be set in services.yml, e.g.
|
30
|
+
# Primo:
|
31
|
+
# type: PrimoService
|
32
|
+
# priority: 2 # After SFX, to get SFX metadata enhancement
|
33
|
+
# status: active
|
34
|
+
# base_url: http://bobcat.library.nyu.edu
|
35
|
+
# vid: NYU
|
36
|
+
# holding_search_institution: NYU
|
37
|
+
# holding_search_text: Search for this title in BobCat.
|
38
|
+
# suppress_holdings: [ !ruby/regexp '/\$\$LWEB/', !ruby/regexp '/\$\$1Restricted Internet Resources/' ]
|
39
|
+
# ez_proxy: !ruby/regexp '/https\:\/\/ezproxy\.library\.nyu\.edu\/login\?url=/'
|
40
|
+
# service_types:
|
41
|
+
# - holding
|
42
|
+
# - holding_search
|
43
|
+
# - fulltext
|
44
|
+
# - table_of_contents
|
45
|
+
# - referent_enhance
|
46
|
+
# - cover_image
|
47
|
+
# base_url:: _required_ host and port of Primo server; used for Primo web services, deep links and holding_search
|
48
|
+
# base_path:: *DEPRECATED* previous name of base_url
|
49
|
+
# vid:: _required_ view id for Primo deep links and holding_search.
|
50
|
+
# institution:: _required_ institution id for Primo institution; used for Primo web services
|
51
|
+
# base_view_id:: *DEPRECATED* previous name of vid
|
52
|
+
# holding_search_institution:: _required if service types include holding_search_ institution to be used for the holding_search
|
53
|
+
# holding_search_text:: _optional_ text to display for the holding_search
|
54
|
+
# default holding search text:: "Search for this title."
|
55
|
+
# link_to_search_text:: *DEPRECATED* previous name of holding_search_text
|
56
|
+
# service_types:: _optional_ array of strings that represent the service types desired.
|
57
|
+
# options are: fulltext, holding, holding_search, table_of_contents,
|
58
|
+
# referent_enhance, cover_image, primo_source
|
59
|
+
# defaults are: fulltext, holding, holding_search, table_of_contents,
|
60
|
+
# referent_enhance, cover_image
|
61
|
+
# if no options are specified, default service types will be added.
|
62
|
+
# suppress_urls:: _optional_ array of strings or regexps to NOT use from the catalog.
|
63
|
+
# Used for linktorsrc elements that may duplicate resources from in other services.
|
64
|
+
# Regexps can be put in the services.yml like this:
|
65
|
+
# [!ruby/regexp '/sagepub.com$/']
|
66
|
+
# suppress_holdings:: _optional_ array of strings or regexps to NOT use from the catalog.
|
67
|
+
# Used for availlibrary elements that may duplicate resources from in other services.
|
68
|
+
# Regexps can be put in the services.yml like this:
|
69
|
+
# [!ruby/regexp '/\$\$LWEB$/']
|
70
|
+
# suppress_tocs:: _optional_ array of strings or regexps to NOT link to for Tables of Contents.
|
71
|
+
# Used for linktotoc elements that may duplicate resources from in other services.
|
72
|
+
# Regexps can be put in the services.yml like this:
|
73
|
+
# [!ruby/regexp '/\$\$LWEB$/']
|
74
|
+
# service_types:: _optional_ array of strings that represent the service types desired.
|
75
|
+
# options are: fulltext, holding, holding_search, table_of_contents,
|
76
|
+
# referent_enhance, cover_image, primo_source
|
77
|
+
# defaults are: fulltext, holding, holding_search, table_of_contents,
|
78
|
+
# referent_enhance, cover_image
|
79
|
+
# if no options are specified, default service types will be added.
|
80
|
+
# ez_proxy:: _optional_ string or regexp of an ezproxy prefix.
|
81
|
+
# used in the case where an ezproxy prefix (on any other regexp) is hardcoded in the URL,
|
82
|
+
# and needs to be removed in order to match against SFXUrls.
|
83
|
+
# Example:
|
84
|
+
# !ruby/regexp '/https\:\/\/ezproxy\.library\.nyu\.edu\/login\?url=/'
|
85
|
+
# primo_config:: _optional_ string representing the primo yaml config file in config/umlaut_config
|
86
|
+
# default file name: primo.yml
|
87
|
+
# hash mappings from yaml config
|
88
|
+
# institutions:
|
89
|
+
# "primo_institution_code": "Primo Institution String"
|
90
|
+
# libraries:
|
91
|
+
# "primo_library_code": "Primo Library String"
|
92
|
+
# statuses:
|
93
|
+
# "status1_code": "Status One"
|
94
|
+
# sources:
|
95
|
+
# data_source1:
|
96
|
+
# base_url: "http://source1.base.url
|
97
|
+
# type: source_type
|
98
|
+
# class_name: Source1Implementation (in exlibris/primo/sources or exlibris/primo/sources/local)
|
99
|
+
# source1_config_option1: source1_config_option1
|
100
|
+
# source1_config_option2: source1_config_option2
|
101
|
+
# data_source2:
|
102
|
+
# base_url: "http://source2.base.url
|
103
|
+
# type: source_type
|
104
|
+
# class_name: Source2Implementation (in exlibris/primo/sources or exlibris/primo/sources/local)
|
105
|
+
# source2_config_option1: source2_config_option1
|
106
|
+
# source2_config_option2: source2_config_option2
|
107
|
+
# holding_attributes:: _optional_ array of Holding attribute readers to save to
|
108
|
+
# holding/primo_source service_data; can be used to save
|
109
|
+
# custom source implementation attributes for display by a custom holding partial
|
110
|
+
# ==Benchmarks
|
111
|
+
# The following benchmarks were run on SunOS 5.10 Generic_141414-08 sun4u sparc SUNW,Sun-Fire-V240.
|
112
|
+
# Rehearsal ----------------------------------------------------------------
|
113
|
+
# PrimoService Minimum Config: 3.850000 0.060000 3.910000 ( 4.163065)
|
114
|
+
# PrimoService Default Config: 3.410000 0.060000 3.470000 ( 3.958777)
|
115
|
+
# ------------------------------------------------------- total: 7.380000sec
|
116
|
+
#
|
117
|
+
# user system total real
|
118
|
+
# PrimoService Minimum Config: 3.470000 0.050000 3.520000 ( 4.567797)
|
119
|
+
# PrimoService Default Config: 3.420000 0.050000 3.470000 ( 3.990271)
|
120
|
+
|
121
|
+
class PrimoService < Service
|
122
|
+
required_config_params :base_url, :vid, :institution
|
123
|
+
|
124
|
+
# Overwrites Service#new.
|
125
|
+
def initialize(config)
|
126
|
+
# defaults
|
127
|
+
@holding_attributes = Exlibris::Primo::Holding.base_attributes
|
128
|
+
@rsrc_attributes = Exlibris::Primo::Rsrc.base_attributes
|
129
|
+
@toc_attributes = Exlibris::Primo::Toc.base_attributes
|
130
|
+
# TODO: Run these decisions by Bill M. to see if they make sense.
|
131
|
+
@referent_enhancements = {
|
132
|
+
# Prefer SFX journal titles to Primo journal titles
|
133
|
+
:jtitle => { :overwrite => false },
|
134
|
+
:btitle => { :overwrite => true }, :aulast => { :overwrite => true },
|
135
|
+
:aufirst => { :overwrite => true }, :aucorp => { :overwrite => true },
|
136
|
+
:au => { :overwrite => true }, :pub => { :overwrite => true },
|
137
|
+
:place => { :value => :cop, :overwrite => false },
|
138
|
+
# Prefer SFX journal titles to Primo journal titles
|
139
|
+
:title => { :value => :jtitle, :overwrite => false},
|
140
|
+
:title => { :value => :btitle, :overwrite => true},
|
141
|
+
# Primo lccn and oclcid are spotty in Primo, so don't overwrite
|
142
|
+
:lccn => { :overwrite => false }, :oclcnum => { :value => :oclcid, :overwrite => false}
|
143
|
+
}
|
144
|
+
@suppress_urls = []
|
145
|
+
@suppress_tocs = []
|
146
|
+
@suppress_holdings = []
|
147
|
+
@service_types = [ "fulltext", "holding", "holding_search",
|
148
|
+
"table_of_contents", "referent_enhance", "cover_image" ] if @service_types.nil?
|
149
|
+
# For backward compatibility, re-map "old" config values to new more
|
150
|
+
# Umlaut-y names and print deprecation warning in the logs.
|
151
|
+
old_to_new_mappings = {
|
152
|
+
:base_path => :base_url,
|
153
|
+
:base_view_id => :vid,
|
154
|
+
:link_to_search_text => :holding_search_text
|
155
|
+
}
|
156
|
+
old_to_new_mappings.each do |old_param, new_param|
|
157
|
+
unless config["#{old_param}"].nil?
|
158
|
+
config["#{new_param}"] = config["#{old_param}"] if config["#{new_param}"].nil?
|
159
|
+
Rails.logger.warn("Parameter '#{old_param}' is deprecated. Please use '#{new_param}' instead.")
|
160
|
+
end
|
161
|
+
end # End backward compatibility maintenance
|
162
|
+
super(config)
|
163
|
+
# For backward compatibility, handle the special case where holding_search_institution was not included.
|
164
|
+
# Set holding_search_institution to vid and print warning in the logs.
|
165
|
+
if @service_types.include?("holding_search") and @holding_search_institution.nil?
|
166
|
+
@holding_search_institution = @institution
|
167
|
+
RAILS_DEFAULT_LOGGER.warn("Required parameter 'holding_search_institution' was not set. Please set the appropriate value in services.yml. Defaulting institution to view id, #{@vid}.")
|
168
|
+
end # End backward compatibility maintenance
|
169
|
+
raise ArgumentError.new(
|
170
|
+
"Missing Service configuration parameter. Service type #{self.class} (id: #{self.id}) requires a config parameter named 'holding_search_institution'. Check your config/umlaut_config/services.yml file."
|
171
|
+
) if @service_types.include?("holding_search") and @holding_search_institution.nil?
|
172
|
+
end
|
173
|
+
|
174
|
+
# Overwrites Service#service_types_generated.
|
175
|
+
def service_types_generated
|
176
|
+
types = Array.new
|
177
|
+
@service_types.each do |type|
|
178
|
+
types.push(ServiceTypeValue[type.to_sym])
|
179
|
+
end
|
180
|
+
return types
|
181
|
+
end
|
182
|
+
|
183
|
+
# Overwrites Service#handle.
|
184
|
+
def handle(request)
|
185
|
+
@identifier = request.referrer_id
|
186
|
+
primo_id = @identifier.match(/primo-(.+)/)[1] if primo_identifier? unless @identifier.nil? or @identifier.match(/primo-(.+)/).nil?
|
187
|
+
# DEPRECATED
|
188
|
+
# Extend OpenURL standard to take Primo Doc Id
|
189
|
+
primo_id = request.referent.metadata['primo'] unless request.referent.metadata['primo'].nil?
|
190
|
+
Rails.logger.warn("Use of 'rft.primo' is deprecated. Please use the identifier instead.") unless request.referent.metadata['primo'].nil?
|
191
|
+
# End DEPRECATED
|
192
|
+
searcher_setup = {
|
193
|
+
:base_url => @base_url, :vid => @vid, :institution => @institution,
|
194
|
+
:config => primo_config
|
195
|
+
}
|
196
|
+
# don't send mal-formed issn
|
197
|
+
issn = request.referent.metadata['issn'] if request.referent.metadata['issn'] =~ /\d{4}(-)?\d{3}(\d|X)/
|
198
|
+
title = title(request)
|
199
|
+
search_params = {
|
200
|
+
:primo_id => primo_id,
|
201
|
+
:isbn => request.referent.metadata['isbn'],
|
202
|
+
:issn => issn,
|
203
|
+
:title => title,
|
204
|
+
:author => author(request),
|
205
|
+
:genre => request.referent.metadata['genre']
|
206
|
+
}
|
207
|
+
begin
|
208
|
+
primo_searcher = Exlibris::Primo::Searcher.new(searcher_setup, search_params)
|
209
|
+
rescue Exception => e
|
210
|
+
# Log error and return finished
|
211
|
+
Rails.logger.error(
|
212
|
+
"Error in Exlibris::Primo::Searcher. "+
|
213
|
+
"Returning 0 Primo services for search #{search_params.inspect}. "+
|
214
|
+
"Exlibris::Primo::Searcher raised the following exception:\n#{e}")
|
215
|
+
return request.dispatched(self, true)
|
216
|
+
end
|
217
|
+
# Enhance the referent with metadata from Primo Searcher if primo id is present
|
218
|
+
# i.e. if we did our search with the Primo system number
|
219
|
+
if primo_id and @service_types.include?("referent_enhance")
|
220
|
+
@referent_enhancements.each do |key, options|
|
221
|
+
value = (options[:value].nil?) ? key.to_sym : options[:value].to_sym
|
222
|
+
request.referent.enhance_referent(
|
223
|
+
key.to_s, primo_searcher.method(value).call,
|
224
|
+
true, false, options
|
225
|
+
) if primo_searcher.respond_to? value and not primo_searcher.method(value).call.nil?
|
226
|
+
end
|
227
|
+
end
|
228
|
+
# Get cover image only if primo_id is defined
|
229
|
+
if primo_id and @service_types.include?("referent_enhance")
|
230
|
+
cover_image = primo_searcher.cover_image
|
231
|
+
unless cover_image.nil?
|
232
|
+
request.add_service_response(
|
233
|
+
:service => self,
|
234
|
+
:display_text => 'Cover Image',
|
235
|
+
:key => 'medium',
|
236
|
+
:url => cover_image,
|
237
|
+
:size => 'medium',
|
238
|
+
:service_type_value => :cover_image)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
# Get holdings from Primo Searcher
|
242
|
+
if @service_types.include?("holding") or @service_types.include?("primo_source")
|
243
|
+
holdings = primo_searcher.holdings # Array of Exlibris::Primo::Holding
|
244
|
+
holdings.each do |holding|
|
245
|
+
next if @suppress_holdings.find {|suppress| suppress === holding.availlibrary}
|
246
|
+
service_data = {}
|
247
|
+
@holding_attributes.each do |attr|
|
248
|
+
service_data[attr] = holding.method(attr).call
|
249
|
+
end
|
250
|
+
# Only add one service type, either "primo_source" OR "holding", not both.
|
251
|
+
service_type = (@service_types.include?("primo_source")) ? "primo_source" : "holding"
|
252
|
+
# Add some other holding information for compatibility with default holding partial
|
253
|
+
service_data.merge!({
|
254
|
+
:call_number => holding.call_number, :collection => holding.collection,
|
255
|
+
:collection_str => "#{holding.library} #{holding.collection}",
|
256
|
+
:coverage_str => holding.coverage.join("<br />"),
|
257
|
+
:coverage_str_array => holding.coverage }) if service_type.eql? "holding"
|
258
|
+
request.add_service_response(
|
259
|
+
service_data.merge(
|
260
|
+
:service => self,
|
261
|
+
:service_type_value => service_type
|
262
|
+
)
|
263
|
+
)
|
264
|
+
end
|
265
|
+
# Provide title search functionality in the absence of available holdings.
|
266
|
+
if @service_types.include?("holding_search")
|
267
|
+
if holdings.empty? and
|
268
|
+
not primo_identifier? and
|
269
|
+
not title.nil?
|
270
|
+
service_data = {}
|
271
|
+
service_data[:type] = "link_to_search"
|
272
|
+
service_data[:display_text] = (@holding_search_text.nil?) ? "Search for this title." : @holding_search_text
|
273
|
+
service_data[:note] = ""
|
274
|
+
service_data[:url] = @base_url+"/primo_library/libweb/action/dlSearch.do?institution=#{@holding_search_institution}&vid=#{@vid}&onCampus=false&query=#{CGI::escape("title,exact,"+title)}&indx=1&bulkSize=10&group=GUEST"
|
275
|
+
request.add_service_response(
|
276
|
+
service_data.merge(
|
277
|
+
:service => self,
|
278
|
+
:service_type_value => 'holding_search'
|
279
|
+
)
|
280
|
+
)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
# Get fulltext
|
285
|
+
if @service_types.include?("fulltext")
|
286
|
+
# Get RSRCs from Primo Searcher (executes search)
|
287
|
+
# Let's find any URLs, and add full text responses for those.
|
288
|
+
urls_seen = [] # for de-duplicating urls from catalog.
|
289
|
+
primo_searcher.rsrcs.each do |rsrc|
|
290
|
+
# No url? Forget it.
|
291
|
+
next if rsrc.url.nil?
|
292
|
+
# Next if duplicate.
|
293
|
+
next if urls_seen.include?(rsrc.url)
|
294
|
+
# Don't add the URL if it matches our SFXUrl finder (unless fulltext is empty,
|
295
|
+
# [assuming something is better than nothing]), because
|
296
|
+
# that means we think this is an SFX controlled URL.
|
297
|
+
next if SfxUrl.sfx_controls_url?(handle_ezproxy(rsrc.url)) and
|
298
|
+
request.referent.metadata['genre'] != "book" and
|
299
|
+
!request.get_service_type("fulltext", { :refresh => true }).empty?
|
300
|
+
# We have our own list of URLs to suppress, array of strings
|
301
|
+
# or regexps.
|
302
|
+
next if @suppress_urls.find {|suppress| suppress === rsrc.url}
|
303
|
+
urls_seen.push(rsrc.url)
|
304
|
+
service_data = {}
|
305
|
+
@rsrc_attributes.each do |attr|
|
306
|
+
service_data[attr] = rsrc.method(attr).call
|
307
|
+
end
|
308
|
+
# Default display text to URL.
|
309
|
+
service_data[:display_text] = (service_data[:display].nil?) ? service_data[:url] : service_data[:display]
|
310
|
+
# Add the response
|
311
|
+
request.add_service_response(
|
312
|
+
service_data.merge(
|
313
|
+
:service => self,
|
314
|
+
:service_type_value => 'fulltext'
|
315
|
+
)
|
316
|
+
)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
# Get TOCs
|
320
|
+
if @service_types.include?("table_of_contents")
|
321
|
+
# Let's find any TOCs, and add table of contents responses for those.
|
322
|
+
tocs_seen = [] # for de-duplicating urls from catalog.
|
323
|
+
primo_searcher.tocs.each do |toc|
|
324
|
+
url = toc.url # actual url
|
325
|
+
next if tocs_seen.include?(toc.url)
|
326
|
+
# We have our own list of URLs to suppress, array of strings
|
327
|
+
# or regexps.
|
328
|
+
next if @suppress_tocs.find {|suppress| suppress === toc.url}
|
329
|
+
# No url? Forget it.
|
330
|
+
next if toc.url.nil?
|
331
|
+
tocs_seen.push(toc.url)
|
332
|
+
service_data = {}
|
333
|
+
@toc_attributes.each do |attr|
|
334
|
+
service_data[attr] = toc.method(attr).call
|
335
|
+
end
|
336
|
+
# Default display text to URL.
|
337
|
+
service_data[:display_text] = (service_data[:display].nil?) ? service_data[:url] : service_data[:display]
|
338
|
+
# Add the response
|
339
|
+
request.add_service_response(
|
340
|
+
service_data.merge(
|
341
|
+
:service => self,
|
342
|
+
:service_type_value => 'table_of_contents'
|
343
|
+
)
|
344
|
+
)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
return request.dispatched(self, true)
|
348
|
+
end
|
349
|
+
|
350
|
+
# Called by ServiceType#view_data to provide custom functionality for Primo sources.
|
351
|
+
# For more information on Primo sources see PrimoSource.
|
352
|
+
def to_primo_source(service_response)
|
353
|
+
source_parameters = { :base_url => @base_url, :vid => @vid, :config => primo_config }
|
354
|
+
@holding_attributes.each { |attr|
|
355
|
+
source_parameters[attr] = service_response.data_values[attr] }
|
356
|
+
return Exlibris::Primo::Holding.new(source_parameters).to_source
|
357
|
+
end
|
358
|
+
|
359
|
+
private
|
360
|
+
def primo_config
|
361
|
+
default_file = "#{Rails.root}/config/umlaut_config/primo.yml"
|
362
|
+
config_file = @primo_config.nil? ? default_file : "#{Rails.root}/config/umlaut_config/"+ @primo_config
|
363
|
+
Rails.logger.warn("Primo config file not found: #{config_file}.") and return {} unless File.exists?(config_file)
|
364
|
+
config_hash = YAML.load_file(config_file)
|
365
|
+
return (config_hash.nil?) ? {} : config_hash
|
366
|
+
end
|
367
|
+
|
368
|
+
# If an ezproxy prefix (on any other regexp) is hardcoded in the URL,
|
369
|
+
# strip it out for matching against SFXUrls
|
370
|
+
def handle_ezproxy(str)
|
371
|
+
return str if @ez_proxy.nil?
|
372
|
+
return (str.gsub(@ez_proxy, '').nil? ? str : str.gsub(@ez_proxy, ''))
|
373
|
+
end
|
374
|
+
|
375
|
+
def title(request)
|
376
|
+
return request.referent.metadata['jtitle'] unless request.referent.metadata['jtitle'].nil? or request.referent.metadata['jtitle'].empty?
|
377
|
+
return request.referent.metadata['btitle'] unless request.referent.metadata['btitle'].nil? or request.referent.metadata['btitle'].empty?
|
378
|
+
return request.referent.metadata['title'] unless request.referent.metadata['title'].nil? or request.referent.metadata['title'].empty?
|
379
|
+
return request.referent.metadata['atitle'] unless request.referent.metadata['atitle'].nil? or request.referent.metadata['atitle'].empty?
|
380
|
+
end
|
381
|
+
|
382
|
+
def author(request)
|
383
|
+
return request.referent.metadata['au'] unless request.referent.metadata['au'].nil? or request.referent.metadata['au'].empty?
|
384
|
+
return request.referent.metadata['aulast'] unless request.referent.metadata['aulast'].nil? or request.referent.metadata['aulast'].empty?
|
385
|
+
return request.referent.metadata['aucorp'] unless request.referent.metadata['aucorp'].nil? or request.referent.metadata['aucorp'].empty?
|
386
|
+
end
|
387
|
+
|
388
|
+
def primo_identifier?
|
389
|
+
return false if @identifier.nil?
|
390
|
+
return @identifier.start_with?('info:sid/primo.exlibrisgroup.com')
|
391
|
+
end
|
392
|
+
end
|