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,86 @@
1
+ # Talks to the ISBNDb (isbndb.com) to get pricing info and links to pricing
2
+ # info for online sellers. There are potentially other services we could
3
+ # make use of there in the future too.
4
+ #
5
+ # jrochkind talked to the operators of isbndb and got a very high traffic limit
6
+ # (instead of the default free 500 requests/day), for free. You could try the
7
+ # same.
8
+ #
9
+ # config params in services.yml:
10
+ #
11
+ # access_key: Your API access key from isbnDB.
12
+ # display_text: (Optional) name of link.
13
+ # timeout: (Optional) seconds to wait for response
14
+ # display_name: (Optional) what to call the service in display
15
+ class IsbnDb < Service
16
+ require 'nokogiri'
17
+
18
+ required_config_params :access_key
19
+
20
+ def service_types_generated
21
+ return [ServiceTypeValue['highlighted_link']]
22
+ end
23
+
24
+ def initialize(config)
25
+ @timeout = 7
26
+ @display_text = "Compare online prices"
27
+ @display_name = "ISBNdb.com"
28
+
29
+ @credits = {
30
+ "ISBNdb" => "http://isbndb.com/"
31
+ }
32
+
33
+ super(config)
34
+ end
35
+
36
+ def handle(umlaut_request)
37
+
38
+ isbn = umlaut_request.referent.metadata['isbn']
39
+
40
+ # No isbn, nothing we can do.
41
+ return umlaut_request.dispatched(self, true) if isbn.blank?
42
+
43
+ response = do_request(isbn)
44
+ xml = Nokogiri::XML( response.body )
45
+ book_xml = xml.at('ISBNdb/BookList/BookData')
46
+
47
+ # No hits?
48
+ return umlaut_request.dispatched(self, true) if book_xml.blank?
49
+
50
+ prices_link = get_prices_link( book_xml )
51
+
52
+ umlaut_request.add_service_response(
53
+ :service=>self,
54
+ :url=> prices_link,
55
+ :display_text=> @display_text,
56
+ :service_type_value => ServiceTypeValue[:highlighted_link]
57
+ )
58
+
59
+ return umlaut_request.dispatched(self, true)
60
+ end
61
+
62
+ def do_request(isbn)
63
+ host = "isbndb.com"
64
+ # including results=details will prime isbndb to refresh pricing, Andrew tells me.
65
+ path = "/api/books.xml?access_key=#{@access_key}&results=details,prices&index1=isbn&value1=#{isbn}"
66
+
67
+ http = Net::HTTP.new( host )
68
+ http.open_timeout = @timeout
69
+ http.read_timeout = @timeout
70
+
71
+ response = http.get( path )
72
+ # raise if not 200 OK response
73
+ response.value
74
+
75
+ return response
76
+ end
77
+
78
+ # Pass in nokogiri object representing the <BookData> element.
79
+ # passes back string url of isbndb prices/availability page
80
+ def get_prices_link( book_data )
81
+ book_id = book_data.attributes['book_id']
82
+
83
+ return (book_id) ? "http://isbndb.com/d/book/#{book_id}/prices.html" : nil
84
+ end
85
+
86
+ end
@@ -0,0 +1,258 @@
1
+ # encoding: UTF-8
2
+
3
+ # Uses ISI Web of Knowledge to generates links to "cited by" and "similar"
4
+ # articles.
5
+ #
6
+ # REQUIREMENTS: You must be an ISI customer if you want these links to actually
7
+ # work for your users. Off-campus users should be sent through EZProxy, see
8
+ # the EZProxy plug-in.
9
+ #
10
+ # You need to register for the the Thomson 'Links Article Match Retrieval'
11
+ # (LAMR) service api, which is used here. To register, see:
12
+ # http://isiwebofknowledge.com/products_tools/products/related/trlinks/.
13
+ #
14
+ # You register by IP address, so no API key is needed once your registration
15
+ # goes through.
16
+ #
17
+ # If you later need to change the IP addresses entitled to use this API, use
18
+ # http://scientific.thomson.com/scientific/techsupport/cpe/form.html.
19
+ # to request a change.
20
+ #
21
+ # Note, as of 13 april 09, there's a bug in ISI where journal titles including
22
+ # ampersands cause an error. We will catch those errors and output a 'warning'
23
+ # instead of an 'error', since it's a known problem.
24
+ class Isi < Service
25
+ require 'open-uri'
26
+ require 'nokogiri'
27
+ require 'net/http'
28
+ require 'builder'
29
+
30
+ include MetadataHelper
31
+
32
+
33
+ def service_types_generated
34
+ return [ServiceTypeValue[:cited_by]]
35
+ end
36
+
37
+ def initialize(config)
38
+ #defaults
39
+ @wos_app_name = "Umlaut"
40
+ @display_name = "Web of Knowledge\xc2\xae" # trademark symbol
41
+ @api_url = "https://ws.isiknowledge.com/cps/xrpc"
42
+ @include_cited_by = true
43
+ @include_similar = true
44
+
45
+ @credits = {
46
+ @display_name => "http://apps.webofknowledge.com"
47
+ }
48
+
49
+ super(config)
50
+ end
51
+
52
+ def handle(request)
53
+
54
+ unless ( sufficient_metadata?(request.referent))
55
+ return request.dispatched(self, true)
56
+ end
57
+
58
+ xml = gen_lamr_request(request)
59
+
60
+ isi_response = do_lamr_request(xml)
61
+
62
+
63
+ begin
64
+ add_responses( request, isi_response )
65
+ rescue IsiResponseException => e
66
+ # Is this the known problem with ampersands?
67
+ # if so, output a warning, but report success not exception,
68
+ # because this is a known condition.
69
+ metadata = request.referent.metadata
70
+ if ( (metadata["title"] && metadata["title"].include?('&')) ||
71
+ (metadata["jtitle"] && metadata['jtitle'].include?('&')))
72
+ Rails.logger.warn("ISI LAMR still exhibiting ampersand problems: #{e.message} ; OpenURL: ?#{request.to_context_object.kev}")
73
+ return request.dispatched(self, true)
74
+ else
75
+ # Log the error, return exception condition.
76
+ Rails.logger.error("#{e.message} ; OpenURL: ?#{request.to_context_object.kev}")
77
+ return request.dispatched(self, false, e)
78
+ end
79
+ end
80
+
81
+ return request.dispatched(self, true)
82
+ end
83
+
84
+ # A DOI is always sufficient. Otherwise, it gets complicated because the ISI
85
+ # service is kind of picky in weird ways. ISSN alone is not enough, we need
86
+ # jtitle. Once you have jtitle, Vol/issue/start page are often enough, but
87
+ # article title really helps, and jtitle+atitle+year is often enough too.
88
+ def sufficient_metadata?(referent)
89
+ metadata = referent.metadata
90
+ return get_doi(referent) || get_pmid(referent) ||
91
+ ( ( metadata['jtitle'] ||
92
+ metadata['title'] ) &&
93
+ (! (metadata['atitle'].blank? ||
94
+ metadata['date'].blank?
95
+ ) ||
96
+ ! ( metadata['volume'].blank? || metadata['issue'].blank? ||
97
+ metadata['spage'].blank?))
98
+ )
99
+ end
100
+
101
+ # produces XML to be posted to Thomson 'Links Article Match Retrieval' service api.
102
+ def gen_lamr_request(request)
103
+ output = ""
104
+
105
+ builder = Builder::XmlMarkup.new(:target => output, :indent => 2)
106
+ builder.instruct!(:xml, :encoding => "UTF-8")
107
+
108
+ builder.request(:xmlns => "http://www.isinet.com/xrpc41", :src => "app.id=Umlaut") do
109
+ builder.fn(:name => "LinksAMR.retrieve") do
110
+ builder.list do
111
+ # first map is authentication info. empty 'map' element since we are IP authenticated.
112
+ builder.map
113
+ # specify what we're requesting
114
+ builder.map do
115
+ builder.list(:name=>"WOS") do
116
+ builder.val("timesCited")
117
+ builder.val("ut")
118
+ builder.val("doi")
119
+ builder.val("sourceURL")
120
+ builder.val("citingArticlesURL")
121
+ builder.val("relatedRecordsURL")
122
+ end
123
+ end
124
+ # specify our query
125
+ builder.map do
126
+ builder.map(:name => "cite_id") do
127
+ # Here's the tricky part, depends on what we've got.
128
+ metadata = request.referent.metadata
129
+
130
+ # DOI
131
+ if ( doi = get_doi(request.referent))
132
+ builder.val(doi, :name => "doi")
133
+ end
134
+
135
+ if ( pmid = get_pmid(request.referent))
136
+ builder.val(pmid, :name => "pmid")
137
+ end
138
+
139
+ # Journal title is crucial for ISI -- ISSN alone is
140
+ # not enough, weirdly!
141
+ if ( ! metadata['jtitle'].blank? )
142
+ builder.val(metadata['jtitle'], :name => "stitle" )
143
+ else
144
+ builder.val(metadata['title'], :name => 'stitle' )
145
+ end
146
+
147
+ # ISSN, not actually used much by ISI, but can't hurt.
148
+ if ( issn = request.referent.issn )
149
+ # ISSN _needs_ a hyphen for WoS, bah!
150
+ unless issn.match( /\-/ )
151
+ issn = issn[0,4] + '-' + issn[4,7]
152
+ end
153
+ builder.val(issn, :name => "issn")
154
+ end
155
+
156
+ # article title often helpful.
157
+ unless ( metadata['atitle'].blank?)
158
+ builder.val( metadata['atitle'], :name => "atitle")
159
+ end
160
+ # year
161
+ unless ( metadata['date'].blank?)
162
+ #first four digits are year
163
+ builder.val( metadata["date"][0,4], :name => "year" )
164
+ end
165
+
166
+ # Vol/issue/page. Oddly, issue isn't used very strongly
167
+ # by ISI, but can't hurt.
168
+ unless ( metadata['volume'].blank? )
169
+ builder.val(metadata['volume'], :name => 'vol')
170
+ end
171
+ unless ( metadata['issue'].blank? )
172
+ builder.val( metadata['issue'] , :name => 'issue')
173
+ end
174
+ unless ( metadata['spage'].blank? )
175
+ builder.val(metadata['spage'], :name => 'spage ')
176
+ end
177
+
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ return output
184
+ end
185
+
186
+ def do_lamr_request(xml)
187
+ uri = URI.parse(@api_url)
188
+ http = Net::HTTP.new(uri.host, uri.port)
189
+ http.use_ssl = true if (uri.scheme == 'https')
190
+
191
+ headers = {'Content-Type' => 'application/xml'}
192
+
193
+ return http.post(uri.request_uri, xml, headers)
194
+ end
195
+
196
+ def add_responses(request, isi_response)
197
+ #raise if it's an error HTTP response
198
+ isi_response.value
199
+
200
+ response_xml = Nokogiri::XML(isi_response.body)
201
+
202
+ # Check for errors.
203
+ if (error = (response_xml.at('val[@name = "error"]') || response_xml.at("error") || response_xml.at('null[@name = "error"]')))
204
+ raise IsiResponseException.new("ISI service reported error: #{error.inner_text}")
205
+ end
206
+
207
+ results = response_xml.at('map[@name ="cite_id"] map[@name="WOS"]')
208
+ unless (results)
209
+ error_message = "#{self.id}: "
210
+ error_message << 'Unexpected ISI response. The ISI response was not reported as an error, but did not contain a <map name="WOS"> inside a <map name="cite_id"> as we expected it to:'
211
+ error_message << "\n ISI XML request:\n#{gen_lamr_request(request)}\n"
212
+ error_message << "\n ISI http response status: #{isi_response.code}\n"
213
+ error_message << "\n ISI http response body:\n#{isi_response.body}\n"
214
+ Rails.logger.error(error_message)
215
+ end
216
+
217
+
218
+ # cited by
219
+ count = results.at('val[@name="timesCited"]')
220
+ count = count ? count.inner_text.to_i : 0
221
+
222
+ cited_by_url = results.at('val[@name="citingArticlesURL"]')
223
+ cited_by_url = cited_by_url.inner_text if cited_by_url
224
+
225
+ if (@include_cited_by && count > 0 && cited_by_url )
226
+ label = ServiceTypeValue[:cited_by].display_name_pluralize.downcase.capitalize
227
+ if count && count == 1
228
+ label = ServiceTypeValue[:cited_by].display_name.downcase.capitalize
229
+ end
230
+
231
+ request.add_service_response(:service=>self,
232
+ :display_text => "#{count} #{label}",
233
+ :count=> count,
234
+ :url => cited_by_url,
235
+ :debug_info => "url: " + cited_by_url,
236
+ :service_type_value => :cited_by)
237
+ end
238
+
239
+ # similar
240
+
241
+ similar_url = results.at('val[@name ="relatedRecordsURL"]')
242
+ similar_url = similar_url.inner_text if similar_url
243
+
244
+ if (@include_similar && similar_url )
245
+ request.add_service_response( :service=>self,
246
+ :display_text => " #{ServiceTypeValue[:similar].display_name_pluralize.downcase.capitalize}",
247
+ :url => similar_url,
248
+ :debug_info => "url: " + similar_url,
249
+ :service_type_value => :similar)
250
+ end
251
+
252
+ end
253
+
254
+ end
255
+
256
+ class IsiResponseException < StandardError
257
+
258
+ end
@@ -0,0 +1,146 @@
1
+ # encoding: UTF-8
2
+
3
+ # Link to Thomson JCR impact report for journal title.
4
+ #
5
+ #
6
+ # REQUIREMENTS: You must be an ISI customer if you want these links to actually
7
+ # work for your users. Off-campus users should be sent through EZProxy, see
8
+ # the EZProxy plug-in.
9
+ #
10
+ # You need to register for the the Thomson 'Links Article Match Retrieval'
11
+ # (LAMR) service api, which is used here (and also in the Isi plugin). To
12
+ # register, see:
13
+ # http://isiwebofknowledge.com/products_tools/products/related/trlinks/.
14
+ #
15
+ # You register by IP address, so no API key is needed once your registration
16
+ # goes through.
17
+ #
18
+ # If you later need to change the IP addresses entitled to use this API, use
19
+ # http://scientific.thomson.com/scientific/techsupport/cpe/form.html.
20
+ # to request a change.
21
+ class Jcr < Service
22
+ require 'open-uri'
23
+ require 'nokogiri'
24
+ require 'net/http'
25
+ require 'builder'
26
+
27
+ include MetadataHelper
28
+
29
+
30
+ def service_types_generated
31
+ return [ServiceTypeValue[:highlighted_link]]
32
+ end
33
+
34
+ def initialize(config)
35
+ #defaults
36
+ @wos_app_name = "Umlaut"
37
+ @display_name = "Journal Citation Reports\xc2\xae" # trademark symbol, utf-8
38
+ @link_text = "Journal impact factor"
39
+ @api_url = "https://ws.isiknowledge.com/cps/xrpc"
40
+ @include_for_article_level = true
41
+
42
+ @credits = {
43
+ @display_name => "http://thomsonreuters.com/products_services/science/science_products/a-z/journal_citation_reports/"
44
+ }
45
+
46
+ super(config)
47
+ end
48
+
49
+ def handle(request)
50
+
51
+ unless ( sufficient_metadata?(request.referent))
52
+ return request.dispatched(self, true)
53
+ end
54
+
55
+ xml = gen_lamr_request(request)
56
+
57
+ isi_response = do_lamr_request(xml)
58
+
59
+ add_responses( request, isi_response )
60
+
61
+ return request.dispatched(self, true)
62
+ end
63
+
64
+ # Need an ISSN.
65
+ def sufficient_metadata?(referent)
66
+ return ! referent.issn.blank?
67
+ end
68
+
69
+ # produces XML to be posted to Thomson 'Links Article Match Retrieval' service api.
70
+ def gen_lamr_request(request)
71
+ output = ""
72
+
73
+ builder = Builder::XmlMarkup.new(:target => output, :indent => 2)
74
+ builder.instruct!(:xml, :encoding => "UTF-8")
75
+
76
+ builder.request(:xmlns => "http://www.isinet.com/xrpc41", :src => "app.id=Umlaut") do
77
+ builder.fn(:name => "LinksAMR.retrieve") do
78
+ builder.list do
79
+ # first map is authentication info. empty 'map' element since we are IP authenticated.
80
+ builder.map
81
+ # specify what we're requesting
82
+ builder.map do
83
+ builder.list(:name=>"JCR") do
84
+ builder.val("impactGraphURL")
85
+ end
86
+ end
87
+ # specify our query
88
+ builder.map do
89
+ builder.map(:name => "cite_id") do
90
+ metadata = request.referent.metadata
91
+ if (issn = request.referent.issn)
92
+ issn = issn[0,4] + '-' + issn[4,7] unless issn =~ /\-/
93
+ builder.val(issn, :name => "issn")
94
+ end
95
+ # Journal title.
96
+ if (! metadata['jtitle'].blank? )
97
+ builder.val(metadata['jtitle'], :name => "stitle" )
98
+ elsif (! metadata['title'].blank? )
99
+ builder.val(metadata['title'], :name => 'stitle' )
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ return output
107
+ end
108
+
109
+ def do_lamr_request(xml)
110
+ uri = URI.parse(@api_url)
111
+ http = Net::HTTP.new(uri.host, uri.port)
112
+ http.use_ssl = true if (uri.scheme == 'https')
113
+
114
+ headers = {'Content-Type' => 'application/xml'}
115
+
116
+ return http.post(uri.request_uri, xml, headers)
117
+ end
118
+
119
+ def add_responses(request, isi_response)
120
+ # raise if it's an HTTP error code
121
+ isi_response.value
122
+
123
+ nokogiri = Nokogiri::XML(isi_response.body)
124
+
125
+ # Check for errors.
126
+ if (error = (nokogiri.at('val[@name = "error"]') || nokogiri.at('error') || nokogiri.at('null[@name = "error"]')))
127
+ raise Exception.new("Third party service error: #{error.inner_text}")
128
+ end
129
+
130
+
131
+ results = nokogiri.at('map[@name ="cite_id"] map[@name="JCR"]')
132
+
133
+ impact_url = results.at('val[@name ="impactGraphURL"]')
134
+
135
+ if (impact_url )
136
+ request.add_service_response(:service=>self,
137
+ :display_text => @link_text,
138
+ :url => impact_url.inner_text,
139
+ :service_type_value => :highlighted_link,
140
+ :debug_info => "url: #{impact_url.inner_text}")
141
+ end
142
+
143
+
144
+ end
145
+
146
+ end