umlaut 3.0.0alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (293) hide show
  1. data/LICENSE +7 -0
  2. data/README.md +49 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/images/error.gif +0 -0
  5. data/app/assets/images/export_bg_bot.gif +0 -0
  6. data/app/assets/images/export_bg_mid.gif +0 -0
  7. data/app/assets/images/export_bg_top.gif +0 -0
  8. data/app/assets/images/famfamfam/book_open.png +0 -0
  9. data/app/assets/images/famfamfam/cross.png +0 -0
  10. data/app/assets/images/famfamfam/page_sound.gif +0 -0
  11. data/app/assets/images/famfamfam/page_text.gif +0 -0
  12. data/app/assets/images/famfamfam/page_up.gif +0 -0
  13. data/app/assets/images/famfamfam/page_white.png +0 -0
  14. data/app/assets/images/famfamfam/readme.html +1495 -0
  15. data/app/assets/images/famfamfam/tiny_cross.png +0 -0
  16. data/app/assets/images/frame_remove.gif +0 -0
  17. data/app/assets/images/ico_go.gif +0 -0
  18. data/app/assets/images/jhu_findit.gif +0 -0
  19. data/app/assets/images/list_closed.png +0 -0
  20. data/app/assets/images/list_open.png +0 -0
  21. data/app/assets/images/more_info.gif +0 -0
  22. data/app/assets/images/rails.png +0 -0
  23. data/app/assets/images/request.gif +0 -0
  24. data/app/assets/images/spinner.gif +0 -0
  25. data/app/assets/javascripts/umlaut/ajax_windows.js +35 -0
  26. data/app/assets/javascripts/umlaut/ensure_window_size.js.erb +34 -0
  27. data/app/assets/javascripts/umlaut/expand_contract_toggle.js +25 -0
  28. data/app/assets/javascripts/umlaut/search_autocomplete.js +46 -0
  29. data/app/assets/javascripts/umlaut/simple_visible_toggle.js +8 -0
  30. data/app/assets/javascripts/umlaut/update_html.js +152 -0
  31. data/app/assets/javascripts/umlaut.js +17 -0
  32. data/app/assets/stylesheets/umlaut.css +857 -0
  33. data/app/controllers/application_controller.rb +14 -0
  34. data/app/controllers/export_email_controller.rb +123 -0
  35. data/app/controllers/js_helper_controller.rb +10 -0
  36. data/app/controllers/link_router_controller.rb +87 -0
  37. data/app/controllers/open_search_controller.rb +9 -0
  38. data/app/controllers/resolve_controller.rb +288 -0
  39. data/app/controllers/resource_controller.rb +83 -0
  40. data/app/controllers/search_controller.rb +328 -0
  41. data/app/controllers/search_methods/sfx3.rb +148 -0
  42. data/app/controllers/search_methods/sfx4.rb +257 -0
  43. data/app/controllers/search_methods/sfx_api.rb +47 -0
  44. data/app/controllers/store_controller.rb +64 -0
  45. data/app/controllers/umlaut/controller_behavior.rb +20 -0
  46. data/app/controllers/umlaut/controller_logic.rb +96 -0
  47. data/app/controllers/umlaut/error_handling.rb +48 -0
  48. data/app/controllers/umlaut_controller.rb +112 -0
  49. data/app/helpers/application_helper.rb +4 -0
  50. data/app/helpers/emailer_helper.rb +43 -0
  51. data/app/helpers/export_email_helper.rb +34 -0
  52. data/app/helpers/open_search_helper.rb +7 -0
  53. data/app/helpers/resolve_helper.rb +225 -0
  54. data/app/helpers/search_helper.rb +50 -0
  55. data/app/helpers/umlaut/footer_helper.rb +64 -0
  56. data/app/helpers/umlaut/helper.rb +62 -0
  57. data/app/helpers/umlaut/html_head_helper.rb +37 -0
  58. data/app/helpers/umlaut/url_generation.rb +77 -0
  59. data/app/mailers/emailer.rb +48 -0
  60. data/app/models/clickthrough.rb +2 -0
  61. data/app/models/collection.rb +259 -0
  62. data/app/models/crossref_lookup.rb +2 -0
  63. data/app/models/dispatched_service.rb +58 -0
  64. data/app/models/permalink.rb +29 -0
  65. data/app/models/referent.rb +473 -0
  66. data/app/models/referent_value.rb +14 -0
  67. data/app/models/request.rb +449 -0
  68. data/app/models/service_response.rb +179 -0
  69. data/app/models/service_store.rb +59 -0
  70. data/app/models/service_type_value.rb +58 -0
  71. data/app/models/service_wave.rb +150 -0
  72. data/app/models/sfx_db/az_additional_title.rb +11 -0
  73. data/app/models/sfx_db/az_letter_group.rb +11 -0
  74. data/app/models/sfx_db/az_title.rb +38 -0
  75. data/app/models/sfx_db/az_title_v2.rb +34 -0
  76. data/app/models/sfx_db/isbn.rb +12 -0
  77. data/app/models/sfx_db/issn.rb +12 -0
  78. data/app/models/sfx_db/object.rb +35 -0
  79. data/app/models/sfx_db/object_portfolio.rb +6 -0
  80. data/app/models/sfx_db/publisher.rb +10 -0
  81. data/app/models/sfx_db/sfx_db_base.rb +54 -0
  82. data/app/models/sfx_db/target.rb +9 -0
  83. data/app/models/sfx_db/target_service.rb +10 -0
  84. data/app/models/sfx_db/title.rb +10 -0
  85. data/app/models/sfx_db.rb +10 -0
  86. data/app/models/sfx_url.rb +35 -0
  87. data/app/views/emailer/citation.text.erb +28 -0
  88. data/app/views/emailer/short_citation.text.erb +8 -0
  89. data/app/views/export_email/_email.html.erb +25 -0
  90. data/app/views/export_email/_send_email.html.erb +3 -0
  91. data/app/views/export_email/_send_txt.html.erb +3 -0
  92. data/app/views/export_email/_txt.html.erb +62 -0
  93. data/app/views/export_email/email.html.erb +3 -0
  94. data/app/views/export_email/send_email.html.erb +1 -0
  95. data/app/views/export_email/send_txt.html.erb +1 -0
  96. data/app/views/export_email/txt.html.erb +3 -0
  97. data/app/views/js_helper/loader.erb.js +13 -0
  98. data/app/views/layouts/umlaut.html.erb +52 -0
  99. data/app/views/open_search/index.html.erb +9 -0
  100. data/app/views/resolve/_api_in_progress.xml.erb +21 -0
  101. data/app/views/resolve/_background_progress.html.erb +51 -0
  102. data/app/views/resolve/_background_updater.html.erb +38 -0
  103. data/app/views/resolve/_citation.html.erb +87 -0
  104. data/app/views/resolve/_coins.html.erb +1 -0
  105. data/app/views/resolve/_compact_citation.html.erb +33 -0
  106. data/app/views/resolve/_cover_image.html.erb +35 -0
  107. data/app/views/resolve/_fulltext.html.erb +55 -0
  108. data/app/views/resolve/_help.html.erb +17 -0
  109. data/app/views/resolve/_holding.html.erb +91 -0
  110. data/app/views/resolve/_related_items.html.erb +35 -0
  111. data/app/views/resolve/_search_inside.html.erb +62 -0
  112. data/app/views/resolve/_section_display.html.erb +49 -0
  113. data/app/views/resolve/_service_errors.html.erb +29 -0
  114. data/app/views/resolve/_standard_response_item.html.erb +89 -0
  115. data/app/views/resolve/api.xml.builder +72 -0
  116. data/app/views/resolve/background_status.html.erb +26 -0
  117. data/app/views/resolve/index.html.erb +73 -0
  118. data/app/views/resolve/partial_html_sections.xml.erb +30 -0
  119. data/app/views/search/_a_to_z.html.erb +6 -0
  120. data/app/views/search/_citation.html.erb +94 -0
  121. data/app/views/search/_pager.html.erb +60 -0
  122. data/app/views/search/books.html.erb +103 -0
  123. data/app/views/search/journal_search.html.erb +90 -0
  124. data/app/views/search/journals.html.erb +167 -0
  125. data/app/views/search/opensearch_description.rxml +10 -0
  126. data/app/views/testing/index.html.erb +1 -0
  127. data/app/views/umlaut/README +5 -0
  128. data/app/views/umlaut/error.html.erb +45 -0
  129. data/db/migrate/01_umlaut_init.rb +113 -0
  130. data/db/orig_fixed_data/service_type_values.yml +120 -0
  131. data/db/seeds.rb +7 -0
  132. data/lib/CronTab.rb +192 -0
  133. data/lib/aws_product_sign.rb +146 -0
  134. data/lib/exlibris/aleph/patron.rb +64 -0
  135. data/lib/exlibris/aleph/record.rb +54 -0
  136. data/lib/exlibris/aleph/rest_api.rb +29 -0
  137. data/lib/exlibris/primo/holding.rb +192 -0
  138. data/lib/exlibris/primo/rsrc.rb +17 -0
  139. data/lib/exlibris/primo/searcher.rb +276 -0
  140. data/lib/exlibris/primo/source/aleph.rb +46 -0
  141. data/lib/exlibris/primo/source/distribution/nyu_aleph.rb +323 -0
  142. data/lib/exlibris/primo/toc.rb +17 -0
  143. data/lib/exlibris/primo_ws.rb +140 -0
  144. data/lib/generators/templates/umlaut_services.yml +237 -0
  145. data/lib/generators/umlaut/asset_hooks_generator.rb +44 -0
  146. data/lib/generators/umlaut/install_generator.rb +110 -0
  147. data/lib/hip3/bib.rb +291 -0
  148. data/lib/hip3/bib_searcher.rb +302 -0
  149. data/lib/hip3/custom_field_lookup.rb +44 -0
  150. data/lib/hip3/holding.rb +50 -0
  151. data/lib/hip3/item.rb +65 -0
  152. data/lib/hip3/receipt.rb +7 -0
  153. data/lib/hip3/serial_copy.rb +82 -0
  154. data/lib/holding.rb +32 -0
  155. data/lib/marc_helper.rb +254 -0
  156. data/lib/metadata_helper.rb +312 -0
  157. data/lib/opensearch_feed.rb +398 -0
  158. data/lib/opensearch_query.rb +98 -0
  159. data/lib/referent_filter.rb +16 -0
  160. data/lib/referent_filters/dissertation_catch.rb +45 -0
  161. data/lib/section_renderer.rb +503 -0
  162. data/lib/service.rb +336 -0
  163. data/lib/service_adaptors/ajax_export.rb +37 -0
  164. data/lib/service_adaptors/amazon.rb +412 -0
  165. data/lib/service_adaptors/blacklight.rb +327 -0
  166. data/lib/service_adaptors/book_finder.rb +40 -0
  167. data/lib/service_adaptors/bx.rb +51 -0
  168. data/lib/service_adaptors/cover_thing.rb +73 -0
  169. data/lib/service_adaptors/elsevier_cover.rb +57 -0
  170. data/lib/service_adaptors/email_export.rb +10 -0
  171. data/lib/service_adaptors/ezproxy.rb +171 -0
  172. data/lib/service_adaptors/google_book_search.rb +442 -0
  173. data/lib/service_adaptors/gpo.rb +124 -0
  174. data/lib/service_adaptors/hathi_trust.rb +308 -0
  175. data/lib/service_adaptors/hip3_service.rb +150 -0
  176. data/lib/service_adaptors/hip_holding_search.rb +237 -0
  177. data/lib/service_adaptors/internet_archive.rb +488 -0
  178. data/lib/service_adaptors/isbn_db.rb +86 -0
  179. data/lib/service_adaptors/isi.rb +258 -0
  180. data/lib/service_adaptors/jcr.rb +146 -0
  181. data/lib/service_adaptors/opac.rb +351 -0
  182. data/lib/service_adaptors/open_library.rb +316 -0
  183. data/lib/service_adaptors/open_library_cover.rb +73 -0
  184. data/lib/service_adaptors/primo_service.rb +392 -0
  185. data/lib/service_adaptors/primo_source.rb +78 -0
  186. data/lib/service_adaptors/pubmed.rb +133 -0
  187. data/lib/service_adaptors/request_to_fixture.rb +68 -0
  188. data/lib/service_adaptors/scopus.rb +295 -0
  189. data/lib/service_adaptors/sfx-new.rb +557 -0
  190. data/lib/service_adaptors/sfx.rb +566 -0
  191. data/lib/service_adaptors/sfx_backchannel_record.rb +69 -0
  192. data/lib/service_adaptors/txt_holding_export.rb +32 -0
  193. data/lib/service_adaptors/ulrichs_cover.rb +57 -0
  194. data/lib/service_adaptors/ulrichs_link.rb +47 -0
  195. data/lib/service_adaptors/worldcat.rb +116 -0
  196. data/lib/service_adaptors/worldcat_identities.rb +591 -0
  197. data/lib/tasks/umlaut.rake +134 -0
  198. data/lib/umlaut/default_configuration.rb +5 -0
  199. data/lib/umlaut/routes.rb +136 -0
  200. data/lib/umlaut/version.rb +3 -0
  201. data/lib/umlaut.rb +37 -0
  202. data/lib/umlaut_configurable.rb +343 -0
  203. data/lib/umlaut_http.rb +100 -0
  204. data/lib/xml_schema_helper.rb +109 -0
  205. data/test/dummy/Rakefile +7 -0
  206. data/test/dummy/app/assets/javascripts/application.js +13 -0
  207. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  208. data/test/dummy/app/controllers/application_controller.rb +3 -0
  209. data/test/dummy/app/controllers/umlaut_controller.rb +112 -0
  210. data/test/dummy/app/helpers/application_helper.rb +2 -0
  211. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  212. data/test/dummy/config/application.rb +45 -0
  213. data/test/dummy/config/boot.rb +10 -0
  214. data/test/dummy/config/database-jhu.yml +44 -0
  215. data/test/dummy/config/database.yml +25 -0
  216. data/test/dummy/config/environment.rb +5 -0
  217. data/test/dummy/config/environments/development.rb +34 -0
  218. data/test/dummy/config/environments/production.rb +60 -0
  219. data/test/dummy/config/environments/test.rb +39 -0
  220. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  221. data/test/dummy/config/initializers/inflections.rb +10 -0
  222. data/test/dummy/config/initializers/mime_types.rb +5 -0
  223. data/test/dummy/config/initializers/secret_token.rb +7 -0
  224. data/test/dummy/config/initializers/session_store.rb +8 -0
  225. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  226. data/test/dummy/config/locales/en.yml +5 -0
  227. data/test/dummy/config/routes.rb +61 -0
  228. data/test/dummy/config/umlaut_services.yml +237 -0
  229. data/test/dummy/config.ru +4 -0
  230. data/test/dummy/db/migrate/20111228211210_umlaut_init.rb +113 -0
  231. data/test/dummy/db/schema.rb +124 -0
  232. data/test/dummy/log/development.log +12981 -0
  233. data/test/dummy/log/production.log +0 -0
  234. data/test/dummy/public/404.html +26 -0
  235. data/test/dummy/public/422.html +26 -0
  236. data/test/dummy/public/500.html +26 -0
  237. data/test/dummy/public/favicon.ico +0 -0
  238. data/test/dummy/script/rails +6 -0
  239. data/test/dummy/tmp/cache/assets/C5F/340/sprockets%2F99692920160b7a279b86a80415b79db7 +0 -0
  240. data/test/dummy/tmp/cache/assets/C70/4D0/sprockets%2F034ad2036e623081bd352800786dfe80 +0 -0
  241. data/test/dummy/tmp/cache/assets/C73/920/sprockets%2Fd371318f22900492fd180f17c5e2a504 +9268 -0
  242. data/test/dummy/tmp/cache/assets/C80/980/sprockets%2Fc94807409c1523d43e18d25f35d93c41 +0 -0
  243. data/test/dummy/tmp/cache/assets/C8F/780/sprockets%2Fe47e28558116fb5f8038754e60d1961d +11769 -0
  244. data/test/dummy/tmp/cache/assets/CAA/EB0/sprockets%2F1d179210e8b76f1ea63c802688a015e4 +9271 -0
  245. data/test/dummy/tmp/cache/assets/CBB/9C0/sprockets%2F706f28923fb754cad04b9107c89986a1 +0 -0
  246. data/test/dummy/tmp/cache/assets/CBF/B60/sprockets%2F08ca89671549936265dcb673bf02e36f +0 -0
  247. data/test/dummy/tmp/cache/assets/CC9/9F0/sprockets%2F306166316e2cafd13c15e62b51a2339d +0 -0
  248. data/test/dummy/tmp/cache/assets/CF6/F20/sprockets%2F5b2ffa1103079dfd555197838f87a99f +0 -0
  249. data/test/dummy/tmp/cache/assets/CF7/2B0/sprockets%2F25a7c73655bd3598173b39d9f98bcd46 +862 -0
  250. data/test/dummy/tmp/cache/assets/CFE/080/sprockets%2F37fe9f4255baddbd549a659914929398 +0 -0
  251. data/test/dummy/tmp/cache/assets/D22/060/sprockets%2F9aec77b768e91a802d284271c58e2f7e +21357 -0
  252. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  253. data/test/dummy/tmp/cache/assets/D33/6D0/sprockets%2F500129c57f1146e556ec3aacd6cd38c1 +0 -0
  254. data/test/dummy/tmp/cache/assets/D33/FD0/sprockets%2F2ba0b4e6334a77b923e5f770381bb2bf +0 -0
  255. data/test/dummy/tmp/cache/assets/D42/C20/sprockets%2Fbcf14e437b1582bf93b77670acf8e090 +21353 -0
  256. data/test/dummy/tmp/cache/assets/D50/A30/sprockets%2F7d8b294ac433db5d056538f8cf7c66b9 +0 -0
  257. data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +872 -0
  258. data/test/dummy/tmp/cache/assets/D65/590/sprockets%2Fc1bb92fc3406a126b7dd302edc96d629 +0 -0
  259. data/test/dummy/tmp/cache/assets/D71/6B0/sprockets%2Fde558b71b494cf09b1bf055c8dff0353 +0 -0
  260. data/test/dummy/tmp/cache/assets/D72/610/sprockets%2Fa8c708eeb30ef93de34d755d4f45d023 +859 -0
  261. data/test/dummy/tmp/cache/assets/D76/AD0/sprockets%2Fe2158cde93188cf5ab6457bc6d6602ec +0 -0
  262. data/test/dummy/tmp/cache/assets/D7A/E40/sprockets%2F9622ffcc499a57627cd1bb18fe31b8e4 +11772 -0
  263. data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
  264. data/test/dummy/tmp/cache/assets/D9B/770/sprockets%2F8aacf02eb7dbb0949704b28f27b87e0b +0 -0
  265. data/test/dummy/tmp/cache/assets/DA6/A80/sprockets%2F92e26d8e58d5bcc8b8f6c25d1b05b9c1 +0 -0
  266. data/test/dummy/tmp/cache/assets/DE8/790/sprockets%2Fd1333bde2b9aafcc712d11dd09ab35d8 +0 -0
  267. data/test/dummy/tmp/cache/assets/DF7/F30/sprockets%2F7bc16c4109b17fabe29f8ddbbf732d1c +374 -0
  268. data/test/dummy/tmp/cache/assets/E03/570/sprockets%2F493bdc0ac14cd4f57fdfe4253f992bde +0 -0
  269. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  270. data/test/dummy/tmp/cache/assets/E0B/4B0/sprockets%2F7988df51a61c81ce6ede4a2d4c8cce4f +377 -0
  271. data/test/dummy/tmp/cache/assets/E5F/960/sprockets%2Fdc007b6cad5c7ef08e33ec28cfff0ef6 +0 -0
  272. data/test/fixtures/dispatched_services.yml +5 -0
  273. data/test/fixtures/permalinks.yml +5 -0
  274. data/test/fixtures/referent_values.yml +1734 -0
  275. data/test/fixtures/referents.yml +156 -0
  276. data/test/fixtures/requests.yml +284 -0
  277. data/test/fixtures/service_responses.yml +5 -0
  278. data/test/fixtures/sfx_urls.yml +4 -0
  279. data/test/performance/browsing_test.rb +9 -0
  280. data/test/test_helper.rb +10 -0
  281. data/test/umlaut_test.rb +7 -0
  282. data/test/unit/aleph_patron_test.rb +39 -0
  283. data/test/unit/aleph_record_benchmarks.rb +28 -0
  284. data/test/unit/aleph_record_test.rb +30 -0
  285. data/test/unit/aws_product_sign_test.rb +93 -0
  286. data/test/unit/collection_test.rb +76 -0
  287. data/test/unit/google_book_search_test.rb +101 -0
  288. data/test/unit/primo_searcher_test.rb +403 -0
  289. data/test/unit/primo_service_test.rb +939 -0
  290. data/test/unit/primo_ws_test.rb +131 -0
  291. data/test/unit/service_response_test.rb +9 -0
  292. data/test/unit/service_test.rb +33 -0
  293. metadata +580 -0
@@ -0,0 +1,237 @@
1
+
2
+
3
+ class HipHoldingSearch < Hip3Service
4
+ required_config_params :base_path, :display_name
5
+ attr_reader :base_path
6
+
7
+ include MarcHelper
8
+
9
+ def initialize(config)
10
+ # Default preemption by any holding
11
+ @bib_limit = 4
12
+ @preempted_by = { "existing_type" => "holding" }
13
+ @keyword_exact_match = true
14
+ # If you are sending an OpenURL from a library service, you may
15
+ # have the HIP bibnum, and include it in the OpenURL as, eg.
16
+ # rft_id=http://catalog.library.jhu.edu/bib/343434 (except URL-encoded)
17
+ # Then you'd set rft_id_bibnum_prefix to http://catalog.library.jhu.edu/bib/
18
+ @rft_id_bibnum_prefix = nil
19
+ @profile = "general"
20
+ super(config)
21
+ # Trim question-mark from base_url, if given
22
+ @base_path.chop! if (@base_path.rindex('?') == @base_path.length)
23
+ end
24
+
25
+ def service_types_generated
26
+ # Add one more to whatever the Hip3Service does.
27
+ return super.push(ServiceTypeValue['holding_search'])
28
+ end
29
+
30
+
31
+ def handle(request)
32
+
33
+ # Only do anything if we have no holdings results from someone else.
34
+ holdings = request.service_types.find(:all, :conditions=>["service_type_value_name = ?", "holding"])
35
+
36
+ if (holdings.length > 0)
37
+ return request.dispatched(self, true)
38
+ end
39
+
40
+ ref_metadata = request.referent.metadata
41
+
42
+ bib_searcher = Hip3::BibSearcher.new(@base_path)
43
+
44
+ search_hash = {}
45
+
46
+ if ( request.referent.format != "book" &&
47
+ (! ref_metadata['jtitle'].blank?) &&
48
+ ref_metadata['bititle'].blank? )
49
+ hip_title_index = Hip3::BibSearcher::SERIAL_TITLE_KW_INDEX
50
+ else
51
+ hip_title_index = Hip3::BibSearcher::TITLE_KW_INDEX
52
+ end
53
+
54
+ title = ref_metadata['jtitle']
55
+ title = ref_metadata['btitle'] if title.blank?
56
+ title = ref_metadata['title'] if title.blank?
57
+
58
+ #title_terms = search_terms_for_title_tokenized(title)
59
+ # tokenized was too much recall, not enough precision. Try phrase
60
+ # search.
61
+ title_terms = search_terms_for_title_phrase(title)
62
+ unless ( title_terms )
63
+ Rails.logger.debug("#{self.service_id} is missing title, can not search.")
64
+ return request.dispatched(self, true)
65
+ end
66
+
67
+
68
+ search_hash[hip_title_index] = title_terms
69
+
70
+ # Do we have the bibnum?
71
+ bibnum = get_bibnum(request.referent)
72
+ bib_searcher.bibnum = bibnum if bibnum
73
+
74
+ # If it's a non-journal thing, add the author if we have an aulast (preferred) or au.
75
+ # But wait--if it's a book _part_, don't include the author name, since
76
+ # it _might_ just be the author of the part, not of the book.
77
+ unless (request.referent.format == "journal" ||
78
+ ( request.referent.format == "book" && ! ref_metadata['atitle'].blank?))
79
+ # prefer aulast
80
+ if (! ref_metadata['aulast'].blank?)
81
+ search_hash[ Hip3::BibSearcher::AUTHOR_KW_INDEX ] = [ref_metadata['aulast']]
82
+ elsif (! ref_metadata['au'].blank?)
83
+ search_hash[ Hip3::BibSearcher::AUTHOR_KW_INDEX ] = [ref_metadata['au']]
84
+ end
85
+
86
+ end
87
+
88
+ bib_searcher.search_hash = search_hash
89
+ unless bib_searcher.insufficient_query
90
+ timing_debug("start search")
91
+
92
+ bibs = bib_searcher.search
93
+
94
+ timing_debug("bib searching")
95
+
96
+ # Ssee if any our matches are exact title matches. 'exact' after normalizing a bit, including removing subtitles.
97
+ matches = [];
98
+
99
+ # Various variant normalized forms of the title from the OpenURL
100
+ # request. #compact removes nil values.
101
+ request_titles = [title,
102
+ normalize_title( title ),
103
+ normalize_title( title, :remove_subtitle => true) ].compact
104
+
105
+
106
+
107
+ if ( @keyword_exact_match )
108
+ bibs.each do |bib|
109
+ # various variant normalized forms of the title from the bib
110
+ # #compact removes nil values.
111
+ bib_titles = [ bib.title,
112
+ normalize_title(bib.title, :remove_subtitle => true),
113
+ normalize_title(bib.title) ].compact
114
+
115
+ # Do any of the various forms match? Set intersection on our
116
+ # two sets.
117
+ if ( bib_titles & request_titles ).length > 0
118
+ matches.push( bib )
119
+ end
120
+ end
121
+ end
122
+
123
+ responses_added = Hash.new
124
+
125
+ timing_debug("Finding matches")
126
+
127
+ if (matches.length > 0 )
128
+
129
+ # process as exact matches with method from Hip3Service
130
+ # Add copies
131
+ # Add 856 urls.
132
+ responses_added = {}
133
+
134
+ unless preempted_by(request, "fulltext")
135
+
136
+ # Let's do some analysis of our results. If it's got a matching
137
+ # bibnum, then include it as an EXACT match.
138
+ req_bibnum = get_bibnum(request.referent)
139
+ if ( req_bibnum )
140
+ matches.each do |bib|
141
+ if (req_bibnum == bib.bibNum)
142
+ responses_added.merge!( add_856_links(request, [bib.marc_xml]) )
143
+ responses_added.merge!( add_copies( request, [bib] ))
144
+ matches.delete(bib)
145
+ end
146
+ end
147
+ end
148
+
149
+ timing_debug("Identified matches")
150
+
151
+ # Otherwise, sort records with matching dates FIRST.
152
+ # Some link generators use an illegal 'year' parameter, bah.
153
+ if ( date = (request.referent['date'] || request.referent['year']))
154
+ req_year = date[0,4]
155
+ matches = matches.partition {|bib| get_years(bib.marc_xml).include?( req_year )}.flatten
156
+ end
157
+
158
+ timing_debug("Date sorted")
159
+
160
+ responses_added.merge!( add_856_links(request, matches.collect{|b| b.marc_xml}, :match_reliability => ServiceResponse::MatchUnsure ) )
161
+
162
+ timing_debug("added 856's")
163
+ end
164
+
165
+ responses_added.merge!( add_copies(request, matches, :match_reliability => ServiceResponse::MatchUnsure ) )
166
+
167
+ timing_debug("added copies")
168
+
169
+ end
170
+
171
+ if (bibs.length > 0 && (! responses_added['holding']))
172
+ # process as holdings_search
173
+ request.add_service_response(
174
+ :service => self,
175
+ :source_name => @display_name,
176
+ :count => bibs.length,
177
+ :display_text => "#{bibs.length} possible #{case; when bibs.length > 1 ; 'matches' ; else; 'match' ; end} in #{display_name}",
178
+ :url => bib_searcher.search_url,
179
+ :service_type_value => :holding_search)
180
+ end
181
+ end
182
+ return request.dispatched(self, true)
183
+ end
184
+
185
+ # One algorithm for turning a title into HIP search terms.
186
+ # Tokenizes the title into individual words, eliminates stop-words,
187
+ # and combines each word with 'AND'. We started with this for maximum
188
+ # recall, but after some experimentation seems to have too low precision
189
+ # without sufficient enough increase in recall.
190
+ # Returns an array of keywords.
191
+ def search_terms_for_title_tokenized(title)
192
+ title_cleaned = normalize_title(title)
193
+
194
+ if title_cleaned.blank?
195
+ # Not enough metadata to search.
196
+ return nil
197
+ end
198
+
199
+ # plus remove some obvious stop words, cause HIP is going to choke on em
200
+ title_cleaned.gsub!(/\bthe\b|\band\b|\bor\b|\bof\b|\ba\b/i,'')
201
+
202
+ title_kws = title_cleaned.split
203
+ # limit to 12 keywords
204
+ title_kws = title_kws.slice( (0..11) )
205
+
206
+ return title_kws
207
+ end
208
+
209
+ # Another algorithm for turning a title into HIP search terms.
210
+ # This one doesn't tokenize, but keeps the whole title as a phrase
211
+ # search. Does eliminate punctuation. Does not remove things that
212
+ # look like a sub-title.
213
+ # Returns an array with one item.
214
+ def search_terms_for_title_phrase(title)
215
+ title_cleaned = normalize_title(title)
216
+
217
+ if title_cleaned.blank?
218
+ # Not enough metadata to search.
219
+ return nil
220
+ end
221
+
222
+ return [title_cleaned]
223
+ end
224
+
225
+ def timing_debug(waypoint = "Waypoint")
226
+ @last_timed ||= Time.now
227
+
228
+ before = @last_timed
229
+ @last_timed = Time.now
230
+
231
+ interval = @last_timed - before
232
+
233
+ Rails.logger.debug("#{service_id}: #{waypoint}: #{interval}")
234
+
235
+ end
236
+
237
+ end
@@ -0,0 +1,488 @@
1
+ # This service searches the Internet Archive (archive.org) by title
2
+ # and, if present, creator. Results are broken down by mediatypes. Which
3
+ # mediatypes are searched can be configured via umlaut_config/services.yml.
4
+ # Also an optional link to a full search in the native interface can be
5
+ # presented to the user.
6
+
7
+ # Property settings can be set in services.yml
8
+ # url:
9
+ # num_results: a number. This is the number of results returned for each
10
+ # mediatype within the main section of the view
11
+ # mediatypes: an array of the mediatypes searched. insure there is an
12
+ # appropriate mediatype as defined by IA. Searching by mediatype searches
13
+ # across collections.
14
+ # The following link will (currently) show the possible mediatypes:
15
+ # http://homeserver7.us.archive.org:8983/solr/select?q=[*+TO+*]&fl=identifier&wt=json&rows=0&indent=yes&facet=true&facet.field=mediatype
16
+ # show_web_link: boolean. If set to true, if there are more results than
17
+ # num_results a link to those further results will display
18
+ # with highlighted_links
19
+ # display_name: defaults to "Internet Archive"
20
+
21
+
22
+ class InternetArchive < Service
23
+ require 'open-uri' #
24
+ require 'cgi'
25
+ require 'multi_json' #we ask IA for json
26
+ require 'timeout' # used to timeout our requests
27
+ include MetadataHelper
28
+
29
+ # No parameters are required, we have working defaults for them all.
30
+
31
+ attr_reader :url, :num_results, :mediatypes
32
+
33
+ # maps the IA mediatype to Umlaut service type
34
+ SERVICE_TYPE_MAP = {
35
+ "texts" => :fulltext,
36
+ "audio" => :audio
37
+ }
38
+
39
+ def service_types_generated
40
+ types = [
41
+ ServiceTypeValue[:fulltext],
42
+ ServiceTypeValue[:audio],
43
+ ServiceTypeValue[:'highlighted_link']
44
+ ]
45
+ types << ServiceTypeValue[:search_inside] if @include_search_inside
46
+ return types
47
+ end
48
+
49
+ def initialize(config)
50
+ # Default base URL for IA advanced search. We use this base link rather than
51
+ # the this rather than the IA Solr index directly because IA suggests that
52
+ # the Solr home may change over time.
53
+ @url = 'http://www.archive.org/advancedsearch.php?'
54
+ # default number of results to return
55
+ @num_results = 1
56
+ # default IA mediatypes to search
57
+ @mediatypes = ["texts", "audio"]
58
+ # Should the web link to further results be shown? default to true
59
+ @show_web_link = true
60
+ @display_name = "the Internet Archive"
61
+ @http_timeout = 5.seconds
62
+ @include_search_inside = false
63
+
64
+ @credits = {
65
+ "The Internet Archive" => "http://archive.org/"
66
+ }
67
+
68
+ super(config)
69
+ @num_results_for_types ||= {}
70
+ @mediatypes.each do |type|
71
+ @num_results_for_types[type] ||= @num_results
72
+ end
73
+ end
74
+
75
+ def handle(request)
76
+ begin
77
+ do_query(request)
78
+ rescue Timeout::Error => e
79
+ return request.dispatched(self, false, e)
80
+ end
81
+ return request.dispatched(self, true)
82
+ end
83
+
84
+ def do_query(request)
85
+ # get the search terms for use in both fulltext search and highlighted_link
86
+ # IA does index apostrophes, although not generally other puncutation. Need to keep em.
87
+ search_terms = {:title => get_search_title(request.referent ,:keep_apostrophes=>true),
88
+ :creator => get_search_creator(request.referent)}
89
+
90
+
91
+
92
+ # We need both title and author to continue
93
+ return nil if (search_terms[:title].blank? || search_terms[:creator].blank?)
94
+
95
+ # Return if this is an journal article link, an IA search can do nothing
96
+ # for us except waste CPU cycles for us and IA.
97
+ metadata = request.referent.metadata
98
+ return nil unless metadata["atitle"].blank? &&
99
+ metadata["issue"].blank? &&
100
+ metadata["volume"].blank?
101
+
102
+ # create one link that searches all configured mediatypes
103
+ link = @url + ia_params(search_terms)
104
+
105
+ # using open() conveniently follows the redirect for us. Alas, it
106
+ # doesn't give us access to the IA http status code response though.
107
+ begin
108
+ response = nil
109
+ timeout(@http_timeout.to_i) {
110
+ response = open(link).read
111
+ }
112
+ rescue Exception => e
113
+ # Log more info for exception, and then just forward exception on,
114
+ # we don't have any way to handle it.
115
+ Rails.logger.error("InternetArchive exception, for url[[#{link}]] , Exception #{e.class}")
116
+ raise e
117
+ end
118
+
119
+ if response.blank?
120
+ Rails.logger.warn("InternetArchive returned empty response for #{link}")
121
+ return nil
122
+ end
123
+
124
+
125
+ doc = MultiJson.decode(response)
126
+ results = doc['response']['docs']
127
+
128
+ @mediatypes.each do |type|
129
+ type_results = get_results_by_type(results, type)
130
+
131
+
132
+
133
+
134
+ # if we have more results than we want to show in the main view
135
+ # we can ceate a link (highlighted_link) to the search in the sidebar
136
+ num_found = type_results.length #doc['response']['numFound']
137
+ if (@show_web_link and not type_results.empty? and @num_results_for_types[type] < num_found )
138
+ do_web_link(request, search_terms, type, num_found)
139
+ end
140
+
141
+ # Check for search inside only for first result of type 'text'
142
+ if (@include_search_inside &&
143
+ type == 'texts' &&
144
+ (first_hit = type_results[0]) &&
145
+ (identifier = first_hit["identifier"])
146
+ )
147
+ direct_url = URI.parse("http://www.archive.org/stream/" + identifier)
148
+
149
+ # Head request, if we get a 200, we think it means we have page
150
+ # turner with search.
151
+ req = Net::HTTP.new(direct_url.host, direct_url.port)
152
+ response = req.request_head(direct_url.path)
153
+ if response.code == "200"
154
+ # search inside!
155
+ request.add_service_response(
156
+ :service => self,
157
+ :display_text=> @display_name,
158
+ :url => direct_url.to_s,
159
+ :service_type_value => :search_inside
160
+ )
161
+ end
162
+ end
163
+
164
+ # add a service response for each result for this mediatype
165
+ type_results.each_with_index do |result, index|
166
+ break if index == @num_results_for_types[type]
167
+ display_name = @display_name
168
+
169
+ if ( result["collection"] && COLLECTION_LABELS[result["collection"][0]])
170
+ display_name += ": " + COLLECTION_LABELS[result["collection"][0]]
171
+ elsif ( result["collection"])
172
+ display_name += ": " + result["collection"][0].titlecase
173
+ end
174
+
175
+ #note = result['title']
176
+ #note << " by " << result['creator'].join(', ') if result['creator']
177
+
178
+ service_type = SERVICE_TYPE_MAP[type]
179
+ request.add_service_response(
180
+ :service=>self,
181
+ :display_text=>display_name,
182
+ :url=>create_result_url(result),
183
+ :match_reliability => ServiceResponse::MatchUnsure,
184
+ :edition_str => edition_str(result),
185
+ :service_type_value => service_type )
186
+ end
187
+ end
188
+ end
189
+
190
+ # Here we create params in the format that the IA advanced search needs.
191
+ # These are solr-like params.
192
+ def ia_params(search_terms)
193
+ return nil if search_terms[:title].nil?
194
+ params = 'fl%5B%5D=*&fmt=json&xmlsearch=Search' #&indent=yes
195
+ params << "&rows=999&q=" #is 999 too many or even too few?
196
+ params << create_query_params(search_terms)
197
+ end
198
+
199
+ def create_result_url(result)
200
+ 'http://archive.org/details/' + result['identifier']
201
+ end
202
+
203
+ # displaying the num_found relies on the number of results from ia_params being
204
+ # enough to capture all results for a mediatype. If there are more potential
205
+ # results then num_found will not be accurate. But good enough.
206
+ def do_web_link(request, search_terms, type, num_found)
207
+ display_text = "#{num_found} digital #{type.singularize} " + (num_found > 1 ? "files" : "file")
208
+
209
+
210
+ url = create_web_link_url(search_terms, type)
211
+ request.add_service_response(
212
+ :service=>self,
213
+ :url=>url,
214
+ :display_text=>display_text,
215
+ :service_type_value => :highlighted_link
216
+ )
217
+ end
218
+
219
+ def create_web_link_url(search_terms, type)
220
+ 'http://www.archive.org/search.php?query=' << create_query_params(search_terms, type)
221
+ #url << CGI.escape('mediatype:' << type << ' AND ')
222
+
223
+ end
224
+
225
+ # if given a type it will only search for one mediatype. otherwise it
226
+ # does an OR search for all configured mediatypes
227
+ def create_query_params(search_terms, type=nil)
228
+ # Downcase params to avoid weird misconfiguration in IA's SOLR
229
+ # installation, where it's interpreting uppercase words as
230
+ # commands even within quotes. Also take out any parens in input.
231
+ # Also IA does not semi-colons in input?!?
232
+ title = safe_argument(search_terms[:title])
233
+
234
+
235
+ params = 'title:' << CGI.escape('"' << title << '"')
236
+ if (! search_terms[:creator].blank?)
237
+ creator = safe_argument(search_terms[:creator])
238
+ params << '+AND+creator:' << CGI.escape('(' << creator << ')')
239
+ end
240
+ mt = []
241
+ params << '+AND+('
242
+ if type
243
+ params << 'mediatype:' << type
244
+ else
245
+ @mediatypes.each do |t|
246
+ mt << ('mediatype:' << t)
247
+ end
248
+ params << mt.join('+OR+')
249
+ end
250
+ params << ')' #closing the mediatypes with a paren
251
+ end
252
+
253
+ # used on what will be values stuck into a URL as search terms,
254
+ # does NOT cgi escape, but does safe-ify them in other ways for IA.
255
+ def safe_argument(string)
256
+ # Downcase params to avoid weird misconfiguration in IA's SOLR
257
+ # installation, where it's interpreting uppercase words as
258
+ # commands even within quotes.
259
+ output = string.downcase
260
+
261
+ # Remove parens, semi-colons, and brackets -- they all mess
262
+ # up IA, which thinks they are special chars. Remove double quote,
263
+ # special char, which sometimes we want to use ourselves. Replace
264
+ # all with spaces to avoid accidentally conjoining words.
265
+ # (could be
266
+ # escaping instead? Not worth it, we don't want to search
267
+ # on these anyway. Remove ALL punctuation? Not sure.)
268
+ output.gsub!(/[)(\]\[;"\=]/, ' ')
269
+
270
+ return output
271
+ end
272
+
273
+
274
+ def get_results_by_type(results, type)
275
+ results.map{|doc| doc if doc["mediatype"] == type}.compact
276
+ end
277
+
278
+ def edition_str(result)
279
+ parts = []
280
+
281
+ parts.push( result['title']) unless result['title'].blank?
282
+ parts.push( result['publisher'] ) unless result['publisher'].blank?
283
+ parts.push( result['year']) unless result['year'].blank?
284
+
285
+ edition_str = parts.join(', ')
286
+ edition_str = nil if edition_str.blank?
287
+
288
+ return edition_str
289
+ end
290
+
291
+ # catch and redirect response_url fo rsearch_inside
292
+ def response_url(service_type, submitted_params)
293
+ if ( ! (service_type.service_type_value.name == "search_inside" ))
294
+ return super(service_type, submitted_params)
295
+ else
296
+ base = service_type.service_response[:url]
297
+ query = CGI.escape(submitted_params["query"] || "")
298
+ url = base + "#search/#{query}"
299
+ return url
300
+ end
301
+ end
302
+
303
+ ## collection labels
304
+ # list of collection labels can be found here:
305
+ # http://www.archive.org/advancedsearch.php?q=mediatype%3Acollection&fl[]=collection&fl[]=identifier&fl[]=title&sort[]=&sort[]=&sort[]=&rows=9999&indent=yes&fmt=json&xmlsearch=Search
306
+ # FIXME either get these dynamically at intervals or add a fuller set below.
307
+ # Currently there are over 4300 collections.
308
+ # If we're going to do this as a static hash then it should be a class
309
+ # constant. Currently this hash contains a small selection of collections
310
+ # which include the 'audio' mediatype and all that contain the 'texts' mediatype.
311
+ COLLECTION_LABELS = {
312
+ "CaliforniaFishandGame"=>"California Fish and Game",
313
+ "ol_data"=>"Open Library Data",
314
+ "worldhealthorganization"=>"World Health Organization",
315
+ "opensource_movies"=>"Open Source Movies",
316
+ "clairetcarneylibrary"=>
317
+ "Claire T. Carney Library, University of Massachusetts Dartmouth",
318
+ "university_of_illinois_urbana-champaign"=>
319
+ "University of Illinois Urbana-Champaign",
320
+ "smithsonian_books"=>"Smithsonian",
321
+ "nhml_london"=>"Natural History Museum Library, London",
322
+ "animationandcartoons"=>"Animation & Cartoons",
323
+ "university_of_toronto_regis"=>"Regis College Library",
324
+ "vlogs"=>"Vlogs",
325
+ "opensource"=>"Open Source Books",
326
+ "USGovernmentDocuments"=>"US Government Documents",
327
+ "danceman"=>"Dance Manuals",
328
+ "additional_collections"=>"Additional Collections",
329
+ "internet_archive_books"=>"Internet Archive Books",
330
+ "sloan"=>"Sloan Foundation",
331
+ "iacl"=>"Children's Library",
332
+ "audio_religion"=>"Spirituality & Religion",
333
+ "microfilm"=>"Books from Microfilm",
334
+ "toronto"=>"Canadian Libraries",
335
+ "prelinger"=>"Prelinger Archives",
336
+ "bostonpubliclibrary"=>"Boston Public Library",
337
+ "sports"=>"Sports Videos",
338
+ "universallibrary"=>"Universal Library",
339
+ "sfpl"=>"The San Francisco Public Library",
340
+ "university_of_toronto_knox"=>"Caven Library, Knox College",
341
+ "memorial_university"=>"Memorial University of Newfoundland & Labrador",
342
+ "MBLWHOI"=>"MBLWHOI Library",
343
+ "oreilly_books"=>"O'Reilly",
344
+ "burstein"=>"The Burstein Alice in Wonderland Collection",
345
+ "ucroho"=>"Regional Oral History Office",
346
+ "Brandeis_University"=>"Brandeis University Libraries",
347
+ "birney_anti_slavery_collection"=>"Birney Anti-Slavery Collection",
348
+ "Johns_Hopkins_University"=>"The Johns Hopkins University Sheridan Libraries",
349
+ "culturalandacademicfilms"=>"Cultural & Academic Films",
350
+ "Harvard_University"=>"Harvard University",
351
+ "montana_state_publications"=>"Montana State Government Publications",
352
+ "national_institute_for_newman_studies"=>
353
+ "National Institute for Newman Studies",
354
+ "buddha"=>"Buddha Books",
355
+ "university_of_toronto_fisher"=>"Thomas Fisher Rare Book Library",
356
+ "ryerson_university"=>"Ryerson University",
357
+ "university_of_toronto_emmanuel"=>
358
+ "Emmanuel College Library, Victoria University",
359
+ "unica"=>"Unica: Rare Books from UIUC",
360
+ "mugar"=>"The Mugar Memorial Library, Boston University",
361
+ "havergal"=>"Havergal College",
362
+ "university_of_toronto_gerstein"=>
363
+ "University of Toronto - Gerstein Science Information Centre",
364
+ "NY_Botanical_Garden"=>"The New York Botanical Garden",
365
+ "calacademy"=>"California Academy of Sciences",
366
+ "chm_fiche"=>"Computer History Museum",
367
+ "university_of_toronto_crrs"=>
368
+ "Centre for Reformation and Renaissance Studies Library",
369
+ "djo"=>"Dickens Journals Online",
370
+ "unclibraries"=>"University of North Carolina at Chapel Hill",
371
+ "university_of_toronto_oise"=>"OISE/UT Library",
372
+ "newsandpublicaffairs"=>"News & Public Affairs",
373
+ "biodiversity"=>"Biodiversity Heritage Library",
374
+ "university_of_ottawa"=>"University of Ottawa",
375
+ "Wellesley_College_Library"=>"Wellesley College Library",
376
+ "audio_foreign"=>"Non-English Audio",
377
+ "national_library_of_australia"=>"National Library of Australia",
378
+ "datadumps"=>"Open Library Data",
379
+ "microfilmreel"=>"Reels of Microfilm",
380
+ "saint_marys_college"=>"Saint Mary's College of California",
381
+ "university_of_toronto_pratt"=>"E.J. Pratt Library",
382
+ "Boston_College_Library"=>"Boston College Library",
383
+ "uchicago"=>"University of Chicago",
384
+ "audio_podcast"=>"Podcasts",
385
+ "tufts"=>"Tufts University",
386
+ "opensource_audio"=>"Open Source Audio",
387
+ "university_of_toronto_trinity"=>"John W. Graham Library, Trinity College",
388
+ "audio_tech"=>"Computers & Technology",
389
+ "moviesandfilms"=>"Movies",
390
+ "etree"=>"Live Music Archive",
391
+ "marcuslucero"=>"the Marucs Lucero",
392
+ "opencontentalliance"=>"Open Content Alliance",
393
+ "radioprograms"=>"Radio Programs",
394
+ "university_of_toronto_pims"=>"PIMS - University of Toronto",
395
+ "newspapers"=>"Newspapers",
396
+ "university_of_california_libraries"=>"University of California Libraries",
397
+ "millionbooks"=>"Million Book Project",
398
+ "university_of_toronto_robarts"=>"University of Toronto - Robarts Library",
399
+ "university_of_toronto"=>"University of Toronto",
400
+ "montana_state_library"=>"Montana State Library",
401
+ "bancroft_library"=>"The Bancroft Library",
402
+ "prelinger_library"=>"Prelinger Library",
403
+ "libraryofcongress"=>"The Library of Congress",
404
+ "richtest"=>"Test books from California",
405
+ "mobot"=>"Missouri Botanical Garden",
406
+ "gamevideos"=>"Video Games",
407
+ "blc"=>"The Boston Library Consortium",
408
+ "cdl"=>"California Digital Library",
409
+ "Princeton"=>"Princeton Theological Seminary",
410
+ "mcmaster_university"=>"McMaster University",
411
+ "sanfranciscopubliclibrary"=>"San Francisco Public Library",
412
+ "spanish_texts"=>"The Spanish Language Library",
413
+ "boston_college_libraries"=>"The Boston College Libraries",
414
+ "gutenberg"=>"Project Gutenberg",
415
+ "Music_UniversityofToronto"=>"Music - University of Toronto",
416
+ "msn_books"=>"Microsoft",
417
+ "youth_media"=>"Youth Media",
418
+ "independent"=>"independent texts",
419
+ "carletonlibrary"=>"Carleton University Library",
420
+ "arpanet"=>"Arpanet",
421
+ "yahoo_books"=>"Yahoo!",
422
+ "johnadamsBPL"=>"The John Adams Library at the Boston Public Library",
423
+ "library_of_congress"=>"The Library of Congress",
424
+ "ColumbiaUniversityLibraries"=>"Columbia University Libraries",
425
+ "university_of_guelph"=>"University of Guelph",
426
+ "GratefulDead"=>"Grateful Dead",
427
+ "audio_bookspoetry"=>"Audio Books & Poetry",
428
+ "ncsulibraries"=>"North Carolina State University Libraries",
429
+ "brown_university_library"=>"Brown University Library",
430
+ "Allen_County_Public_Library"=>"Allen County Public Library",
431
+ "yrlsc"=>"The Charles E. Young Research Library Special Collections",
432
+ "torontotest"=>"Test books from Canada",
433
+ "americana"=>"American Libraries",
434
+ "librivoxaudio"=>"LibriVox",
435
+ "audio_music"=>"Music & Arts",
436
+ "toronto_public_library"=>"Toronto Public Library",
437
+ "getty"=>"Research Library, Getty Research Institute",
438
+ "ontla"=>"The Legislative Assembly of Ontario Collection",
439
+ "TheChristianRadical"=>"The Christian Radical",
440
+ "netlabels"=>"Netlabels",
441
+ "newyorkpubliclibrary"=>"New York Public Library",
442
+ "University_of_New_Hampshire_Library"=>"University of New Hampshire Library",
443
+ "cbk"=>"Cook Books and Home Economics",
444
+ "audio_news"=>"News & Public Affairs",
445
+ "ant_texts"=>"Ant Texts",
446
+ "computersandtechvideos"=>"Computers & Technology",
447
+ "the_beat_within"=>"The Beat Within Magazine",
448
+ "university_of_toronto_kelly"=>"University of Toronto - John M Kelly Library",
449
+ "library_and_archives_canada"=>"Library and Archives Canada",
450
+ "ephemera"=>"Ephemeral Films",
451
+ "OXFAM"=>"Oxfam",
452
+ "foreignlanguagevideos"=>"Non-English Videos",
453
+ "MontanaStateLibrary"=>"Montana State Library",
454
+ "EarthSciences_UniversityofToronto"=>"Earth Sciences University of Toronto",
455
+ "octavo"=>"Octavo",
456
+ "artsandmusicvideos"=>"Arts & Music"
457
+ }
458
+
459
+
460
+ end
461
+
462
+ # Test URLs using defaults
463
+ # Shows texts and audio under fulltext, but only a see also for texts
464
+ # http://localhost:3000/resolve?&rft.title=Fairy+Tales&rft.aulast=Andersen&ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook
465
+ #
466
+ # Shows texts and audio, but only see also for audio
467
+ # http://localhost:3000/resolve?&rft.title=Frankenstein&rft.aulast=Shelley&ctx_ver=Z39.88-2004&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Abook
468
+ #
469
+
470
+ # WorldCat links
471
+ # If you have OpenURL Referrer or another Firefox add-on configured to
472
+ # turn COiNS into an OpenURL to localhost:3000, these links have hits in IA.
473
+ # Frankenstein: http://www.worldcat.org/oclc/33045872
474
+ # Alice in Wonderland: http://www.worldcat.org/oclc/221499
475
+ # Fairy Tales by Andersen: http://www.worldcat.org/oclc/68711386
476
+ # Adventures of Huckleberry Finn: http://www.worldcat.org/oclc/2985768
477
+ # Gift of the Magi: http://www.worldcat.org/oclc/9065223
478
+ # Heart of the West: http://www.worldcat.org/oclc/49293242
479
+ # Little Women; or, Meg, Jo, Beth, and Amy: http://www.worldcat.org/oclc/1157
480
+ # FIXME should we remove everything after ; as well?
481
+ # Letters from a Cat: http://www.worldcat.org/oclc/13529549
482
+ # Uncle Tom's Cabin: http://www.worldcat.org/oclc/7945691
483
+ # needed apostrophe to succeed
484
+ # Goody Two-Shoes: http://www.worldcat.org/oclc/32678428
485
+ # The Snow-Image: http://www.worldcat.org/oclc/5020610
486
+ # Les Canadiens-Français: http://www.worldcat.org/oclc/186641188
487
+ # FIXME should match 1 record and doesn't. character encoding problems?
488
+ # John L. Stoddard's Lectures: http://www.worldcat.org/oclc/2181690