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.
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