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,302 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
require 'nokogiri'
|
6
|
+
|
7
|
+
# Hip3 Module has been written for JHU's HIP3 installation. It may not work
|
8
|
+
# quite right with other installations, I'm almost certain it needs to be
|
9
|
+
# abstracted and parameterized better to be more generic.
|
10
|
+
module Hip3
|
11
|
+
|
12
|
+
|
13
|
+
# If multiple search criteria are supplied, will 'or' them all to find
|
14
|
+
# bibs matching ANY criteria.
|
15
|
+
# keywords should be an array, and will be 'and'ed
|
16
|
+
# Searches using the HIP3 xml 'interface', which means it may be sensitive to
|
17
|
+
# HIP display changes that change XML.
|
18
|
+
# It finds BibNums, and creates Hip3Bib
|
19
|
+
# objects based on that bibNum. Doesn't take any other info but Bib num from
|
20
|
+
# the actual search response, but it could, and pre-load the bib object.
|
21
|
+
class BibSearcher
|
22
|
+
ISSN_KW_INDEX = '.IS'
|
23
|
+
ISBN_KW_INDEX = '.IB'
|
24
|
+
|
25
|
+
GEN_KW_INDEX = '.GW'
|
26
|
+
TITLE_KW_INDEX = '.TW'
|
27
|
+
SERIAL_TITLE_KW_INDEX = '.ST'
|
28
|
+
AUTHOR_KW_INDEX = '.AW'
|
29
|
+
|
30
|
+
BIBNUM_INDEX = 'BIB'
|
31
|
+
|
32
|
+
SUDOC_KW_INDEX = '.SD'
|
33
|
+
|
34
|
+
attr_accessor :httpSession
|
35
|
+
attr_accessor :hip_base_url_str, :hip_base_url
|
36
|
+
attr_reader :issn, :isbn # writers provided concretely
|
37
|
+
attr_accessor :sudoc, :bibnum
|
38
|
+
attr_reader :keywords
|
39
|
+
|
40
|
+
# You can pass in a Net::HTTP, if you'd for instance like to keep
|
41
|
+
# open a persistent connection. You are advised to use our special
|
42
|
+
# Hip3::HTTPSession, for it's error handling. Or better yet, just
|
43
|
+
# leave second argument empty, and we'll create one for you.
|
44
|
+
def initialize(arg_base_path, arg_http_session=nil)
|
45
|
+
self.hip_base_url_str = arg_base_path
|
46
|
+
self.hip_base_url = URI::parse(self.hip_base_url_str);
|
47
|
+
|
48
|
+
|
49
|
+
self.httpSession = arg_http_session
|
50
|
+
if self.httpSession.nil?
|
51
|
+
self.httpSession = Hip3::HTTPSession.create(self.hip_base_url.host() )
|
52
|
+
end
|
53
|
+
|
54
|
+
self.keywords = []
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
# Method checks for basic well-formedness (doesn't actually check
|
61
|
+
# checksum), and adds hyphen if neccesary, because our HIP needs
|
62
|
+
# it to search. Bah!
|
63
|
+
def issn=(argIssn)
|
64
|
+
if (argIssn.nil? || argIssn.empty?)
|
65
|
+
@issn = nil
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
# first remove hyphen to normalize
|
70
|
+
argIssn.gsub!('-', '')
|
71
|
+
# now check for basic well-formedness
|
72
|
+
unless argIssn =~ /\d{7}(\d|X)/
|
73
|
+
raise ArgumentError.new("Malformed issn: #{argIssn}")
|
74
|
+
end
|
75
|
+
#now put the hyphen back, sadly
|
76
|
+
@issn = argIssn.slice(0..3) + '-' + argIssn.slice(4..7)
|
77
|
+
end
|
78
|
+
|
79
|
+
def isbn=(arg_isbn)
|
80
|
+
if ( arg_isbn.nil? || arg_isbn.empty? )
|
81
|
+
@isbn = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
@isbn = arg_isbn
|
85
|
+
end
|
86
|
+
|
87
|
+
# Yet another way to specify search criteria.
|
88
|
+
# Hash, where the key is the name of a HIP keyword Index (use
|
89
|
+
# constants in this class if possible), and the value is an array of
|
90
|
+
# keywords. Everything is "anded" together.
|
91
|
+
def search_hash=(hash)
|
92
|
+
@search_hash = hash
|
93
|
+
end
|
94
|
+
|
95
|
+
def keywords=(arg_kw)
|
96
|
+
set_keywords(arg_kw)
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_keywords(arg_kw, args={})
|
100
|
+
|
101
|
+
arg_kw = [] if arg_kw.nil?
|
102
|
+
args[:index] = :general unless args[:index]
|
103
|
+
|
104
|
+
@keywords = arg_kw
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
if (args[:index] == :title)
|
109
|
+
@keyword_index = TITLE_KW_INDEX
|
110
|
+
elsif (args[:index] == :serial_title)
|
111
|
+
@keyword_index = SERIAL_TITLE_KW_INDEX
|
112
|
+
else
|
113
|
+
@keyword_index = GEN_KW_INDEX
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the URL starting from / that specifies the search criteria to
|
118
|
+
# HIP.
|
119
|
+
def searchPath(args = {})
|
120
|
+
args[:xml] = true if args[:xml].nil?
|
121
|
+
|
122
|
+
path = self.hip_base_url.path() + '?' "menu=search&aspect=power&npp=30&ipp=20&spp=20&profile=general&ri=2"
|
123
|
+
|
124
|
+
criteria = Array.new
|
125
|
+
|
126
|
+
# Need to do search_hash first, to make sure bibnum and isbn search
|
127
|
+
# come LAST, for HIP.
|
128
|
+
unless ( @search_hash.blank?)
|
129
|
+
manual_criteria = []
|
130
|
+
@search_hash.each_pair do |index, kws|
|
131
|
+
manual_criteria << kws.collect do |kw|
|
132
|
+
kw = '"' + kw + '"' unless [BIBNUM_INDEX, ISSN_KW_INDEX, ISBN_KW_INDEX, AUTHOR_KW_INDEX].include?(index)
|
133
|
+
"&index=#{index}&term=#{URI.escape(kw)}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
path << manual_criteria.join("&oper=and") << "&oper=or"
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
criteria<< "&index=#{SUDOC_KW_INDEX}&term=#{URI.escape('"' + self.sudoc + '"' )}" unless sudoc.nil?
|
141
|
+
criteria << "&index=#{ISSN_KW_INDEX}&term=#{URI.escape(self.issn)}" unless issn.nil?
|
142
|
+
# For some reason ISBN must be LAST in order, and bibnum must be right before, or HIP doesn't like it.
|
143
|
+
criteria << "&index=#{BIBNUM_INDEX}&term=#{URI.escape(self.bibnum)}" unless bibnum.blank?
|
144
|
+
# Go figure. I hate you, HIP.
|
145
|
+
criteria << "&index=#{ISBN_KW_INDEX}&term=#{URI.escape(self.isbn)}" unless isbn.nil?
|
146
|
+
|
147
|
+
criteria << keyword_url_args
|
148
|
+
path << criteria.join("&oper=or")
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
path << "&x=0&y=0&aspect=power"
|
154
|
+
path << "&GetXML=1" if args[:xml]
|
155
|
+
|
156
|
+
return path
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
def keyword_url_args
|
161
|
+
args =
|
162
|
+
self.keywords.collect { |k| "&index=#{@keyword_index}&term=#{CGI.escape('"' + k + '"')}" }
|
163
|
+
|
164
|
+
return args.join("&oper=and") || ""
|
165
|
+
end
|
166
|
+
|
167
|
+
# returns the numbef of hits--does not cache anything, calling
|
168
|
+
# this method will cause a trip to the db, and calling search
|
169
|
+
# will cause another one.
|
170
|
+
def count
|
171
|
+
return [] if insufficient_query
|
172
|
+
|
173
|
+
httpResp = httpSession.get( searchPath, nil )
|
174
|
+
reDoc = Nokogiri::XML( httpResp.body )
|
175
|
+
|
176
|
+
# Confusingly, sometimes
|
177
|
+
# this gives us a search results page, and sometimes it gives us
|
178
|
+
# a single bib
|
179
|
+
|
180
|
+
# single bib?
|
181
|
+
if reDoc.at('searchresponse/fullnonmarc/searchresults/results/row/key')
|
182
|
+
return 1
|
183
|
+
end
|
184
|
+
|
185
|
+
# Multiple, get the count
|
186
|
+
hits = reDoc.at('searchresponse/yoursearch/hits')
|
187
|
+
return hits ? hits.inner_text.to_s.to_i : 0
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns an array of bib objects.
|
191
|
+
def search
|
192
|
+
return [] if insufficient_query
|
193
|
+
httpResp = httpSession.get(searchPath(), nil )
|
194
|
+
|
195
|
+
|
196
|
+
bib_xml = Nokogiri::XML( httpResp.body )
|
197
|
+
# Confusingly, sometimes
|
198
|
+
# this gives us a search results page, and sometimes it gives us
|
199
|
+
# a single bib
|
200
|
+
|
201
|
+
# single bib?
|
202
|
+
if ( bibNum = bib_xml.at('searchresponse/fullnonmarc/searchresults/results/row/key'))
|
203
|
+
# Single bib
|
204
|
+
#return [Hip3::Bib.new( httpSession, bibNum.text, reDoc)]
|
205
|
+
return [Hip3::Bib.new( bibNum.inner_text, self.hip_base_url,
|
206
|
+
:http_session => httpSession,
|
207
|
+
:bib_xml_doc => bib_xml
|
208
|
+
)]
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
# Multi-response
|
213
|
+
# Get Bib #s and titles for each result.
|
214
|
+
|
215
|
+
bib_summaries = bib_xml.search('searchresponse/summary/searchresults/results/row');
|
216
|
+
|
217
|
+
return bib_summaries.collect do |bib_xml|
|
218
|
+
next unless bib_xml.at('key')
|
219
|
+
|
220
|
+
# Find a title from the summary xml
|
221
|
+
title_el = bib_xml.at('TITLE/data/text')
|
222
|
+
title = title_el ? title_el.inner_text : nil
|
223
|
+
# remove possible author on there, after a '/' char. That's how HIP rolls.
|
224
|
+
title.sub!(/\/.*$/, '')
|
225
|
+
|
226
|
+
Hip3::Bib.new(bib_xml.at('key').inner_text, self.hip_base_url, :http_session => httpSession, :title => title )
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def insufficient_query
|
231
|
+
# Have to have some search criteria to search
|
232
|
+
return (self.issn.nil? && self.isbn.nil? && self.sudoc.blank? && self.bibnum.blank? && self.keywords.blank? && @search_hash.blank?)
|
233
|
+
end
|
234
|
+
|
235
|
+
def search_url
|
236
|
+
return self.hip_base_url_str + '?' + self.searchPath(:xml => false )
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
class HTTPSession < Net::HTTP
|
243
|
+
@@timeout = 5
|
244
|
+
|
245
|
+
def HTTPSession.create(a_host, a_port = 80)
|
246
|
+
http = HTTPSession.new(a_host, a_port)
|
247
|
+
http.read_timeout = @@timeout
|
248
|
+
http.open_timeout = @@timeout
|
249
|
+
return http
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
def get(path, headers=nil, &block)
|
254
|
+
limit = 6
|
255
|
+
tries = 0
|
256
|
+
response = nil
|
257
|
+
|
258
|
+
while (response == nil || response.kind_of?(Net::HTTPRedirection) && tries < limit)
|
259
|
+
# follow redirects
|
260
|
+
if response.kind_of?( Net::HTTPRedirection )
|
261
|
+
response = Net::HTTP.get_response(URI.parse(response['location']))
|
262
|
+
else
|
263
|
+
response = super(path, headers, block)
|
264
|
+
end
|
265
|
+
tries = tries + 1
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
#This method raises if not 2xx response status.
|
270
|
+
#No idea why such a method is called 'value'
|
271
|
+
response.value
|
272
|
+
|
273
|
+
return response
|
274
|
+
end
|
275
|
+
|
276
|
+
# Does a get whether or not the connection is already open,
|
277
|
+
# if it wasn't already open, will make sure to leave it closed again.
|
278
|
+
def self.safe_get(httpObj, path, headers=nil)
|
279
|
+
if httpObj.started?
|
280
|
+
return httpObj.get(path, headers)
|
281
|
+
else
|
282
|
+
# With a block, will close the connection when we're done.
|
283
|
+
return httpObj.start { |h| h.get(path, headers) }
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
|
295
|
+
|
296
|
+
|
297
|
+
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hip3
|
2
|
+
|
3
|
+
# Certain fields we add to the Item/Copy/Bib display are in the XML, but they
|
4
|
+
# are only findable by name of the field header as configured in HIP
|
5
|
+
# admin, and then only by seeing what index in a list that header is,
|
6
|
+
# and then finding the corresponding indexed value! This object
|
7
|
+
# does that work for us, and caches it's calcuations while it's at it.
|
8
|
+
# One of these objects has it's own rexml doc representing a particular
|
9
|
+
# bib with item info, because the answer may be different for different bibs!
|
10
|
+
class CustomFieldLookup
|
11
|
+
attr_accessor :header_list
|
12
|
+
|
13
|
+
def initialize(a_header_list)
|
14
|
+
self.header_list = a_header_list
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def index_for(label)
|
20
|
+
return header_list.index(label)
|
21
|
+
end
|
22
|
+
|
23
|
+
# list can be either an array of strings, or a rexml element
|
24
|
+
# representing a <row> element for this item. In either case,
|
25
|
+
# we lookup the index i of label in our original header list,
|
26
|
+
# and then return the text value of element i in the list arg.
|
27
|
+
def text_value_for(list, label )
|
28
|
+
i = index_for(label)
|
29
|
+
return nil if i.nil?
|
30
|
+
|
31
|
+
if ( list.kind_of?(Hpricot::Node) )
|
32
|
+
# Assume they passed in a HIP 'row' element, turn it
|
33
|
+
# into a nice array of strings. Can't figure out how
|
34
|
+
# to test if it really is a 'row' element!
|
35
|
+
list = list.search('/cell/data/text').collect {|e| e.inner_text}
|
36
|
+
end
|
37
|
+
|
38
|
+
return list.at( i )
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/hip3/holding.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Hip3
|
6
|
+
# abstract superclass for copies and items (both serial and mono). Both a Copy
|
7
|
+
# and an Item are Holdings.
|
8
|
+
# coverage_str only applies to copies and serial items, not mono items, but
|
9
|
+
# we put it here anyway, it'll just be nil for mono items.
|
10
|
+
class Holding
|
11
|
+
attr_accessor :id, :location_str, :collection_str, :call_no, :copy_str, :status_str, :coverage_str, :notes
|
12
|
+
# Holdings sometimes use the bib to lazy load more stuff.
|
13
|
+
attr_accessor :bib
|
14
|
+
|
15
|
+
# If input is nil, returns nil, else returns input.inner_text
|
16
|
+
def textValue(el)
|
17
|
+
return ( el == nil ? nil : el.inner_text)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return an array of holding strings, possibly empty, possibly single-valued.
|
21
|
+
# over-ridden by SerialCopy to give you an array, since SerialCopies have
|
22
|
+
# multiple holdings strings.
|
23
|
+
def coverage_str_to_a
|
24
|
+
return coverage_str.nil? ? [] : [coverage_str]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Some items are dummy/placeholder items which don't really represent
|
28
|
+
# an item, and shouldn't be shown. Having trouble figuring out what
|
29
|
+
# our 'business rules' for that are, so this is a messy guess.
|
30
|
+
def dummy?
|
31
|
+
#Mostly trying to rule out the weird internet holdings
|
32
|
+
#that tell us nothing--url is already in the bib.
|
33
|
+
return ( collection_str == "Internet" ||
|
34
|
+
collection_str == "Internet Resource" ||
|
35
|
+
collection_str == "Welch Online Journals" ||
|
36
|
+
collection_str == "Welch Online Journal" ||
|
37
|
+
collection_str == "Gibson-Electronic Journals & Indexes" ||
|
38
|
+
collection_str == "Gibson - Electronic Journals")
|
39
|
+
|
40
|
+
#return (( (call_no == "World Wide Web" || call_no.blank?) &&
|
41
|
+
# ( collection_str == "Internet" || collection_str == "Welch Online Journals" || collection_str == "Welch Online Journal")) ||
|
42
|
+
# (collection_str == "Gibson-Electronic Journals & Indexes" && call_no="Online journal"))
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
end
|
data/lib/hip3/item.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Hip3
|
2
|
+
# We're going to try using this for serial items and mono items. Only difference is
|
3
|
+
# that serial items have a relationship to a SerialCopy, stored in :copy attribute.
|
4
|
+
# Item objects need to be initialized with reXML object representing
|
5
|
+
# the item element.
|
6
|
+
class Item < Holding
|
7
|
+
# Labels set up in HIP admin item display to expose these data of interest.
|
8
|
+
@@Field_labels = { :rmst => 'hide_rmst', :barcode => 'hide_barcode', :copy_id => 'hide_CopyNum'}
|
9
|
+
|
10
|
+
# Item-specific attributes
|
11
|
+
attr_accessor :avail_date_str, :rmst_str, :barcode, :copy_id
|
12
|
+
attr_accessor :copy
|
13
|
+
|
14
|
+
# Item is not lazily loadable--you need to give it
|
15
|
+
# all three arguments to create it, you need to have the
|
16
|
+
# XML in hand. We could certainly provide a different
|
17
|
+
# way than XML to init values, but we haven't.
|
18
|
+
def initialize(item_row_element, arg_bib)
|
19
|
+
@bib = arg_bib
|
20
|
+
loadFromItemRowElement(item_row_element)
|
21
|
+
end
|
22
|
+
|
23
|
+
def loadFromItemRowElement( el )
|
24
|
+
|
25
|
+
@id = textValue(el.at('/key'));
|
26
|
+
|
27
|
+
# Pull out the values built into HIP automatically. They have weird
|
28
|
+
# XML elements, but I think I've appropriately identified them,
|
29
|
+
# even though it doesn't look like it.Actually, no, those aren't
|
30
|
+
# reliable, I don't understand what they are. We'll just pull
|
31
|
+
# everything from the HIP display.
|
32
|
+
|
33
|
+
#@copy_str = textValue(el.elements['MIDSPINE/data/text'])
|
34
|
+
@copy_str = @bib.item_field_lookup.text_value_for(el, "Copy")
|
35
|
+
@collection_str = @bib.item_field_lookup.text_value_for(el, "Collection")
|
36
|
+
|
37
|
+
#Maybe. Not sure where else to get this.
|
38
|
+
@location_str = textValue(el.at('/LOCALLOCATION/data/text'))
|
39
|
+
|
40
|
+
#@call_no = textValue(el.elements['COPYNUMBER/data/text'])
|
41
|
+
@call_no = @bib.item_field_lookup.text_value_for(el, "Call No.")
|
42
|
+
|
43
|
+
@status_str = @bib.item_field_lookup.text_value_for(el, "Status")
|
44
|
+
|
45
|
+
# Not sure about this one.
|
46
|
+
@avail_date_str = textValue(el.at('/AVAILABILITYDATE/data/text'))
|
47
|
+
|
48
|
+
# Pull out the values we had to configure in Copy display
|
49
|
+
@rmst_str = @bib.item_field_lookup.text_value_for(el, @@Field_labels[:rmst])
|
50
|
+
@barcode = @bib.item_field_lookup.text_value_for(el, @@Field_labels[:barcode])
|
51
|
+
@copy_id = @bib.item_field_lookup.text_value_for(el, @@Field_labels[:copy_id])
|
52
|
+
|
53
|
+
# Attach this thing to the proper Copy object, in both directions.
|
54
|
+
# We have the Bib do it for us, since the Bib has a list of Copies.
|
55
|
+
# Since we've set our @copy_id, the Bib can find out what it needs to.
|
56
|
+
@bib.register_item( self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def hasSerialCopy
|
60
|
+
return ! self.copy.nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|