umlaut 3.0.0beta2 → 3.0.0beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/app/assets/javascripts/umlaut/{expand_contract_toggle.js → expand_contract_toggle.js.erb} +4 -4
  2. data/app/controllers/store_controller.rb +2 -6
  3. data/lib/service_adaptors/primo_service.rb +39 -14
  4. data/lib/service_adaptors/sfx.rb +23 -22
  5. data/lib/service_adaptors/worldcat_identities.rb +3 -3
  6. data/lib/umlaut/version.rb +1 -1
  7. data/test/integration/permalinks_test.rb +19 -0
  8. metadata +45 -132
  9. data/lib/exlibris/aleph/patron.rb +0 -64
  10. data/lib/exlibris/aleph/record.rb +0 -54
  11. data/lib/exlibris/aleph/rest_api.rb +0 -29
  12. data/lib/exlibris/primo/holding.rb +0 -186
  13. data/lib/exlibris/primo/related_link.rb +0 -17
  14. data/lib/exlibris/primo/rsrc.rb +0 -17
  15. data/lib/exlibris/primo/searcher.rb +0 -297
  16. data/lib/exlibris/primo/source/aleph.rb +0 -46
  17. data/lib/exlibris/primo/source/distribution/nyu_aleph.rb +0 -344
  18. data/lib/exlibris/primo/toc.rb +0 -17
  19. data/lib/exlibris/primo_ws.rb +0 -140
  20. data/test/dummy/tmp/cache/assets/C2A/410/sprockets%2Fd654b74912b4773a2534616863fb6565 +0 -0
  21. data/test/dummy/tmp/cache/assets/C45/A30/sprockets%2F39494895e462697b478d3d0c79298a26 +0 -0
  22. data/test/dummy/tmp/cache/assets/C5F/340/sprockets%2F99692920160b7a279b86a80415b79db7 +0 -0
  23. data/test/dummy/tmp/cache/assets/C70/4D0/sprockets%2F034ad2036e623081bd352800786dfe80 +0 -0
  24. data/test/dummy/tmp/cache/assets/C80/980/sprockets%2Fc94807409c1523d43e18d25f35d93c41 +0 -0
  25. data/test/dummy/tmp/cache/assets/CBD/730/sprockets%2F034c1086748b981c36672d5a56e7fed6 +0 -0
  26. data/test/dummy/tmp/cache/assets/CBF/B60/sprockets%2F08ca89671549936265dcb673bf02e36f +0 -0
  27. data/test/dummy/tmp/cache/assets/CC9/9F0/sprockets%2F306166316e2cafd13c15e62b51a2339d +0 -0
  28. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  29. data/test/dummy/tmp/cache/assets/CF7/2B0/sprockets%2F25a7c73655bd3598173b39d9f98bcd46 +0 -0
  30. data/test/dummy/tmp/cache/assets/CFE/080/sprockets%2F37fe9f4255baddbd549a659914929398 +0 -0
  31. data/test/dummy/tmp/cache/assets/D16/F90/sprockets%2F5fe3c021048c6f9a6086bed7736d87b1 +0 -0
  32. data/test/dummy/tmp/cache/assets/D24/360/sprockets%2F6987b047a96dc685ba3cf39b31477f6d +0 -0
  33. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  34. data/test/dummy/tmp/cache/assets/D33/FD0/sprockets%2F2ba0b4e6334a77b923e5f770381bb2bf +0 -0
  35. data/test/dummy/tmp/cache/assets/D37/2B0/sprockets%2F40834fb07d7318c1fddd5003bd9e04f6 +0 -0
  36. data/test/dummy/tmp/cache/assets/D43/0D0/sprockets%2F682843a8d0795a5fbcfeb2f0c81727d0 +0 -0
  37. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  38. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  39. data/test/dummy/tmp/cache/assets/D6C/7D0/sprockets%2F8a05d6981ec0d38c51739bef0b3a9c2b +0 -0
  40. data/test/dummy/tmp/cache/assets/D74/4C0/sprockets%2F64fdf30f75592d6e45fcfc45a48d20a2 +0 -0
  41. data/test/dummy/tmp/cache/assets/D94/FF0/sprockets%2F3b56a1aa77de0d570c38a4a9d5f4b1d6 +0 -0
  42. data/test/dummy/tmp/cache/assets/D97/6B0/sprockets%2Fb070e8c799d1a4ad5e62e0a1ae3b83e6 +0 -0
  43. data/test/dummy/tmp/cache/assets/DA6/A80/sprockets%2F92e26d8e58d5bcc8b8f6c25d1b05b9c1 +0 -0
  44. data/test/dummy/tmp/cache/assets/DC0/D20/sprockets%2F1ccf7405cd252dcec4bf23af82e2563a +0 -0
  45. data/test/dummy/tmp/cache/assets/DD2/D80/sprockets%2Fc66d103807d0f971fbbcf9aa8b8b27ee +0 -0
  46. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  47. data/test/dummy/tmp/cache/assets/DE8/790/sprockets%2Fd1333bde2b9aafcc712d11dd09ab35d8 +0 -0
  48. data/test/dummy/tmp/cache/assets/DF7/960/sprockets%2F99ac6db10b44a64fbba4ee847b35ba8b +0 -0
  49. data/test/dummy/tmp/cache/assets/DFC/040/sprockets%2F15ea81cf915c0cb1dfc9cc04c9fef364 +0 -0
  50. data/test/dummy/tmp/cache/assets/DFD/300/sprockets%2Fabac9489cf7f1db8ef00d72a1571ee1e +0 -0
  51. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  52. data/test/dummy/tmp/cache/assets/E38/FE0/sprockets%2Fe1fc875efa817cbb94a5d8de25ea4e6b +0 -0
  53. data/test/dummy/tmp/cache/assets/E5F/960/sprockets%2Fdc007b6cad5c7ef08e33ec28cfff0ef6 +0 -0
  54. data/test/unit/aleph_patron_test.rb +0 -44
  55. data/test/unit/aleph_record_benchmarks.rb +0 -32
  56. data/test/unit/aleph_record_test.rb +0 -35
  57. data/test/unit/primo_searcher_test.rb +0 -415
  58. data/test/unit/primo_ws_test.rb +0 -147
@@ -1,64 +0,0 @@
1
- module Exlibris::Aleph
2
- class Patron < RestAPI
3
- attr_accessor :patron_id
4
-
5
- def initialize(patron_id, uri)
6
- @patron_id = patron_id
7
- raise "Initialization error in #{self.class}. Missing patron id." if @patron_id.nil?
8
- super(uri)
9
- @uri = @uri+ "/patron/#{patron_id}"
10
- end
11
-
12
- # Place a hold on the specificed item.
13
- # Raises errors if
14
- def place_hold(adm_library, bib_library, bib_id, item_id, params)
15
- pickup_location = params[:pickup_location]
16
- raise "Error in place hold. Missing pickup location." if pickup_location.nil?
17
- last_interest_date = params.fetch(:last_interest_date, "")
18
- start_interest_date = params.fetch(:start_interest_date, "")
19
- sub_author = params.fetch(:sub_author, "")
20
- sub_title = params.fetch(:sub_title, "")
21
- pages = params.fetch(:pages, "")
22
- note_1 = params.fetch(:note_1, "")
23
- note_2 = params.fetch(:note_2, "")
24
- rush = params.fetch(:rush, "N")
25
- body_str = "<hold-request-parameters>"
26
- body_str << "<pickup-location>#{pickup_location}</pickup-location>"
27
- body_str << "<last-interest-date>#{last_interest_date}</last-interest-date>"
28
- body_str << "<start-interest-date>#{start_interest_date}</start-interest-date>"
29
- body_str << "<sub-author>#{sub_author}</sub-author>"
30
- body_str << "<sub-title>#{sub_title}</sub-title>"
31
- body_str << "<pages>#{pages}</pages>"
32
- body_str << "<note-1>#{note_1}</note-1>"
33
- body_str << "<note-2>#{note_2}</note-2>"
34
- body_str << "<rush>#{rush}</rush>"
35
- body_str << "</hold-request-parameters>"
36
- options = { :body => "post_xml=#{body_str}"}
37
- @response = self.class.put(@uri+ "/record/#{bib_library}#{bib_id}/items/#{item_id}/hold", options)
38
- raise "Error placing hold through Aleph REST APIs. #{error}" unless error.nil?
39
- return @response
40
- end
41
-
42
- def loans()
43
- @response = self.class.get(@uri+ "/circulationActions/loans?view=full")
44
- raise "Error getting loans through Aleph REST APIs. #{error}" unless error.nil?
45
- return @response
46
- end
47
-
48
- def renew_loans(item_id="")
49
- # Will renew all if specific item not specified
50
- @response = self.class.post(@uri+ "/circulationActions/loans/#{item_id}")
51
- raise "Error renewing loan(s) through Aleph REST APIs. #{error}" unless error.nil?
52
- return @response
53
- end
54
-
55
- def note
56
- return (not @response.first.last.kind_of?(Hash) or @response.first.last["create_hold"].nil?) ? "" : ": #{@response.first.last["create_hold"]["note"]}" if @response.instance_of?(Hash)
57
- end
58
-
59
- def error
60
- return nil if reply_code == "0000"
61
- return "#{reply_text}#{note}"
62
- end
63
- end
64
- end
@@ -1,54 +0,0 @@
1
- module Exlibris::Aleph
2
- class Record < RestAPI
3
- def initialize(bib_library, record_id, uri)
4
- @record_id = record_id
5
- raise "Initialization error in #{self.class}. Missing record id." if @record_id.nil?
6
- @bib_library = bib_library
7
- raise "Initialization error in #{self.class}. Missing bib library." if @bib_library.nil?
8
- super(uri)
9
- @uri = @uri+ "/record/#{bib_library}#{record_id}"
10
- # Format :xml parses response as a hash.
11
- # Eventually I'd like this to be the default since it raises exceptions for invalid XML.
12
- # self.class.format :xml
13
- # Format :html does no parsing, just passes back raw XML for parsing by client
14
- self.class.format :html
15
- end
16
-
17
- # Returns an XML string representation of a bib.
18
- # Every method call refreshes the data from the underlying API.
19
- # Raises and exception if there are errors.
20
- def bib
21
- @response = self.class.get(@uri+ "?view=full")
22
- raise "Error getting bib from Aleph REST APIs. #{error}" unless error.nil?
23
- return @response
24
- end
25
-
26
- # Returns an array of items. Each item is represented as an HTTParty hash.
27
- # Every method call refreshes the data from the underlying API.
28
- # Raises an exception if the response is not valid XML or there are errors.
29
- def items
30
- @items = []
31
- self.class.format :xml
32
- # Since we're parsing xml, this will raise an error
33
- # if the response isn't xml.
34
- @response = self.class.get(@uri+ "/items?view=full")
35
- self.class.format :html
36
- raise "Error getting items from Aleph REST APIs. #{error}" if not error.nil? or
37
- @response.nil? or @response["get_item_list"].nil? or @response["get_item_list"]["items"].nil?
38
- item_list = @response["get_item_list"]["items"]["item"]
39
- @items.push(item_list) if item_list.instance_of?(Hash)
40
- item_list.each {|item|@items.push(item)} if item_list.instance_of?(Array)
41
- Rails.logger.warn("No items returned from Aleph in #{self.class}.") if @items.empty?
42
- return @items
43
- end
44
-
45
- # Returns an XML string representation of holdings
46
- # Every method call refreshes the data from the underlying API.
47
- # Raises and exception if there are errors.
48
- def holdings
49
- @response = self.class.get(@uri+ "/holdings?view=full")
50
- raise "Error getting holdings from Aleph REST APIs. #{error}" unless error.nil?
51
- return @response
52
- end
53
- end
54
- end
@@ -1,29 +0,0 @@
1
- module Exlibris::Aleph
2
- require 'httparty'
3
- class RestAPI
4
- include HTTParty
5
- format :xml
6
- def initialize(uri)
7
- @uri = uri
8
- raise "Initialization error in #{self.class}. Missing URI." if @uri.nil?
9
- end
10
- def error
11
- return nil if reply_code == "0000"
12
- return "#{reply_text}"
13
- end
14
- def reply_code
15
- return "No response." if @response.nil?
16
- return (not @response.first.last.kind_of?(Hash) or @response.first.last["reply_code"].nil?) ? "Unexpected response hash." : @response.first.last["reply_code"] if @response.instance_of?(Hash)
17
- response_match = @response.match(/\<reply-code\>(.+)\<\/reply-code\>/) if @response.instance_of?(String)
18
- return (response_match.nil?) ? "Unexpected response string." : response_match[1] if @response.instance_of?(String)
19
- return "Unexpected response type."
20
- end
21
- def reply_text
22
- return "No response." if @response.nil?
23
- return (not @response.first.last.kind_of?(Hash) or @response.first.last["reply_text"].nil?) ? "Unexpected response hash." : @response.first.last["reply_text"] if @response.instance_of?(Hash)
24
- response_match = @response.match(/\<reply-text\>(.+)\<\/reply-text\>/) if @response.instance_of?(String)
25
- return (response_match.nil?) ? "Unexpected response string." : response_match[1] if @response.instance_of?(String)
26
- return "Unexpected response type."
27
- end
28
- end
29
- end
@@ -1,186 +0,0 @@
1
- module Exlibris::Primo
2
- # == Overview
3
- # Exlibris::Primo::Holding represents a Primo holding.
4
- # This class should be extended to create Primo source objects for
5
- # expanding holdings information, linking to Primo sources, and storing
6
- # additional metadata based on those sources.
7
- #
8
- # == Tips on Extending
9
- # When extending the class, a few basics guidelines should be observed.
10
- # 1. A Exlibris::Primo::Holding is initialized from random Hash of parameters.
11
- # Instance variables are created from these parameters for use in the class.
12
- #
13
- # 2. A Exlibris::Primo::Holding can be initialized from an input
14
- # Exlibris::Primo::Holding by specifying the reserved
15
- # parameter name :holding, i.e. :holding => input_holding.
16
- # If the input holding has instance variables that are also specified in
17
- # the random Hash, the value in the Hash takes precedence.
18
- #
19
- # 3. The following methods are available for overriding:
20
- # expand - expand holdings information based on data source. default: [self]
21
- # dedup? - does this data source contain duplicate holdings that need to be deduped? default: false
22
- #
23
- # 4. The following instance variables will be saved in the view_data and will be available
24
- # to a local holding partial:
25
- # @record_id, @source_id, @original_source_id, @source_record_id,
26
- # @availlibrary, @institution_code, @institution, @library_code, @library,
27
- # @status_code, @status, @id_one, @id_two, @origin, @display_type, @coverage, @notes,
28
- # @url, @request_url, @match_reliability, @request_link_supports_ajax_call, @source_data
29
- #
30
- # 5. Additional source data should be saved in the @source_data instance variable.
31
- # @source_data is a hash that can contain any number of string elements,
32
- # perfect for storing local source information.
33
- # @source_data will get saved in the view_data and will be available to a
34
- # local holding partial.
35
- #
36
- # == Examples
37
- # Example of Primo source implementations are:
38
- # * Exlibris::Primo::Source::Aleph
39
- class Holding
40
- @base_attributes = [ :record_id, :source_id, :original_source_id, :source_record_id,
41
- :availlibrary, :institution_code, :institution, :library_code, :library,
42
- :status_code, :status, :id_one, :id_two, :origin, :display_type, :coverage, :notes,
43
- :url, :request_url, :match_reliability, :request_link_supports_ajax_call, :source_data ]
44
- # Make sure attribute you're aliasing in in base_attributes
45
- @attribute_aliases = { :collection => :id_one, :call_number => :id_two }
46
- @required_parameters = [ :base_url, :record_id, :source_id,
47
- :original_source_id, :source_record_id, :availlibrary,
48
- :institution_code, :library_code, :id_one, :id_two, :status_code ]
49
- @parameter_default_values = { :vid => "DEFAULT", :config => {},
50
- :max_holdings => 10, :request_link_supports_ajax_call => false,
51
- :coverage => [], :source_data => {} }
52
- @decode_variables = {
53
- :institution => {},
54
- :library => { :address => "libraries" },
55
- :status => { :address => "statuses" }
56
- }
57
- class << self; attr_reader :base_attributes, :attribute_aliases, :required_parameters, :parameter_default_values, :decode_variables end
58
-
59
- def initialize(parameters={})
60
- # Set attr_readers
61
- base_attributes = (self.class.base_attributes.nil?) ?
62
- Exlibris::Primo::Holding.base_attributes : self.class.base_attributes
63
- base_attributes.each { |attribute|
64
- self.class.send(:attr_reader, attribute)
65
- }
66
- # Defensive copy the holding parameter.
67
- holding = parameters[:holding].clone unless parameters[:holding].nil?
68
- raise "Initialization error in #{self.class}. Unexpected holding parameter: #{holding.class}." unless holding.kind_of? Holding or holding.nil?
69
- # Copy the defensive copy of holding to self.
70
- holding.instance_variables.each { |name|
71
- instance_variable_set((name).to_sym, holding.instance_variable_get(name))
72
- } if holding.kind_of? Holding
73
- # Add required instance variables, raising an exception if they're missing
74
- # Params passed in overwrite instance variables copied from the holding
75
- required_parameters = (self.class.required_parameters.nil?) ?
76
- Exlibris::Primo::Holding.required_parameters : self.class.required_parameters
77
- required_parameters.each do |param|
78
- instance_variable_set(
79
- "@#{param}".to_sym,
80
- parameters.delete(param) {
81
- instance_variable_get("@#{param}") if instance_variable_defined?("@#{param}") }
82
- )
83
- raise_required_parameter_error param unless instance_variable_defined?("@#{param}")
84
- end
85
- # Set additional instance variables from passed parameters
86
- # Params passed in overwrite instance variables copied from the holding
87
- parameters.each { |param, value|
88
- instance_variable_set("@#{param}".to_sym, value)
89
- }
90
- # If appropriate, add defaults to non-required elements
91
- parameter_default_values = (self.class.parameter_default_values.nil?) ?
92
- Exlibris::Primo::Holding.parameter_default_values : self.class.parameter_default_values
93
- parameter_default_values.each { |param, default|
94
- instance_variable_set("@#{param}".to_sym, default) unless instance_variable_defined?("@#{param}")
95
- }
96
- # Set decoded fields
97
- decode_variables = (self.class.decode_variables.nil?) ?
98
- Exlibris::Primo::Holding.decode_variables : self.class.decode_variables
99
- decode_variables.each { |var, decode_params|
100
- decode var, decode_params, true
101
- }
102
- # Deep link URL to record
103
- @url = primo_url if @url.nil?
104
- # Set source parameters
105
- @source_config = @config["sources"][source_id] unless @config["sources"].nil?
106
- @source_class = @source_config["class_name"] unless @source_config.nil?
107
- @source_url = @source_config["base_url"] unless @source_config.nil?
108
- @source_type = @source_config["type"] unless @source_config.nil?
109
- @source_data = {
110
- :source_class => @source_class,
111
- :source_url => @source_url,
112
- :source_type => @source_type
113
- }
114
- # Set aliases for convenience
115
- attribute_aliases = (self.class.attribute_aliases.nil?) ?
116
- Exlibris::Primo::Holding.attribute_aliases : self.class.attribute_aliases
117
- attribute_aliases.each { |alias_name, method_name|
118
- begin
119
- self.class.send(:alias_method, alias_name.to_sym, method_name.to_sym)
120
- rescue NameError => ne
121
- raise NameError, "Error in #{self}. Make sure method, #{method_name}, is defined. You may need to add it to #{self} @base_attributes.\nRoot exception: #{ne.message}"
122
- end
123
- }
124
- end
125
-
126
- # Returns an array of self.
127
- # Should be overridden by source subclasses to map multiple holdings
128
- # to one availlibrary.
129
- def expand
130
- return [self]
131
- end
132
-
133
- # Determine if we're de-duplicating.
134
- # Should be overridden by source subclasses if appropriate.
135
- def dedup?
136
- return false
137
- end
138
-
139
- # Return this holding as a new holdings subclass instance based on source
140
- def to_source
141
- return self if @source_class.nil?
142
- begin
143
- # Get source class in Primo::Source module
144
- return Exlibris::Primo::Source.const_get(@source_class).new(:holding => self)
145
- rescue Exception => e
146
- Rails.logger.error("#{e.message}")
147
- Rails.logger.error("Class #{@source_class} can't be found in Exlibris::Primo::Source.
148
- Please check primo.yml to ensure the class_name is defined correctly.
149
- Not converting to source.")
150
- return self
151
- end
152
- end
153
-
154
- def [](key)
155
- raise "Error in #{self.class}. #{key} doesn't exist or is restricted." unless self.class.base_attributes.include?(key)
156
- method(key).call
157
- end
158
-
159
- protected
160
- def decode(var, decode_params={}, refresh=false)
161
- return instance_variable_get("@#{var}") unless (not instance_variable_defined?("@#{var}")) or refresh
162
- code_sym = (decode_params[:code].nil?) ? "#{var}_code".to_sym : decode_params[:code]
163
- code = instance_variable_get("@#{code_sym}")
164
- config_sym = (decode_params[:config].nil?) ? :config : decode_params[:config]
165
- config = instance_variable_get("@#{config_sym}")
166
- address = (decode_params[:address].nil?) ? "#{var}s" : decode_params[:address]
167
- instance_variable_set("@#{var}",
168
- (config[address].nil? or config[address][code].nil?) ?
169
- code : config[address][code]) unless code.nil?
170
- end
171
-
172
- # Returns Primo deep link URL to record
173
- def primo_url
174
- "#{@base_url}/primo_library/libweb/action/dlDisplay.do?docId=#{@record_id}&institution=#{@institution_code}&vid=#{@vid}"
175
- end
176
-
177
- private
178
- # def self.add_attr_reader(reader)
179
- # attr_reader reader.to_sym
180
- # end
181
- #
182
- def raise_required_parameter_error(parameter)
183
- raise "Initialization error in #{self.class}. Missing required parameter: #{parameter}."
184
- end
185
- end
186
- end
@@ -1,17 +0,0 @@
1
- module Exlibris::Primo
2
- # Class for handling Primo related links from links/addlink
3
- class RelatedLink
4
- @base_attributes = [ :record_id, :addlink, :url, :display, :notes ]
5
- class << self; attr_reader :base_attributes end
6
- def initialize(options={})
7
- base_attributes = (self.class.base_attributes.nil?) ?
8
- Exlibris::Primo::RelatedLink.base_attributes : self.class.base_attributes
9
- base_attributes.each { |attribute|
10
- self.class.send(:attr_reader, attribute)
11
- }
12
- options.each { |option, value|
13
- self.instance_variable_set(('@'+option.to_s).to_sym, value)
14
- }
15
- end
16
- end
17
- end
@@ -1,17 +0,0 @@
1
- module Exlibris::Primo
2
- # Class for handling Primo Rsrcs from links/linktorsrc
3
- class Rsrc
4
- @base_attributes = [ :record_id, :linktorsrc, :v, :url, :display, :institution_code, :origin, :notes ]
5
- class << self; attr_reader :base_attributes end
6
- def initialize(options={})
7
- base_attributes = (self.class.base_attributes.nil?) ?
8
- Exlibris::Primo::Rsrc.base_attributes : self.class.base_attributes
9
- base_attributes.each { |attribute|
10
- self.class.send(:attr_reader, attribute)
11
- }
12
- options.each { |option, value|
13
- self.instance_variable_set(('@'+option.to_s).to_sym, value)
14
- }
15
- end
16
- end
17
- end
@@ -1,297 +0,0 @@
1
- # == Overview
2
- # Searcher searches Primo for records.
3
- # Searcher must have sufficient metadata to make
4
- # the request. Sufficient means either:
5
- # * We have a Primo doc id
6
- # * We have either an isbn OR an issn
7
- # * We have a title AND an author AND a genre
8
- # If none of these criteria are met, Searcher.search
9
- # will raise a RuntimeException.
10
- require "iconv"
11
-
12
- module Exlibris::Primo
13
- class Searcher
14
- #@required_setup = [ :base_url ]
15
- #@setup_default_values = { :vid => "DEFAULT", :config => {} }
16
-
17
- attr_reader :response, :count
18
- attr_reader :cover_image, :titles, :author
19
- attr_reader :holdings, :rsrcs, :tocs, :related_links
20
- PNX_NS = {'pnx' => 'http://www.exlibrisgroup.com/xsd/primo/primo_nm_bib'}
21
- SEARCH_NS = {'search' => 'http://www.exlibrisgroup.com/xsd/jaguar/search'}
22
-
23
- # Instantiates the object and performs the search for based on the input search criteria.
24
- # setup parameter requires { :base_url => http://primo.server.institution.edu }
25
- # Other optional parameters are :vid => "view_id", :config => { Hash of primo config settings}
26
- # search_params are a sufficient combination of
27
- # { :primo_id => "primo_1", :isbn => "ISBN", :issn => "ISSN",
28
- # :title => "=Title", :author => "Author", :genre => "Genre" }
29
- def initialize(setup, search_params)
30
- @holdings = []
31
- @rsrcs = []
32
- @tocs = []
33
- @related_links = []
34
- @holding_attributes = Exlibris::Primo::Holding.base_attributes
35
- @base_url = setup[:base_url]
36
- raise_required_setup_parameter_error :base_url if @base_url.nil?
37
- @institution = setup[:institution]
38
- raise_required_setup_parameter_error :institution if @institution.nil?
39
- @vid = setup.fetch(:vid, "DEFAULT")
40
- raise_required_setup_parameter_error :vid if @vid.nil?
41
- @config = setup.fetch(:config, {})
42
- raise_required_setup_parameter_error :config if @config.nil?
43
- search_params.each { |param, value| self.instance_variable_set("@#{param}".to_sym, value) }
44
- # Perform the search
45
- search
46
- end
47
-
48
- private
49
- def self.add_attr_reader(reader)
50
- attr_reader reader.to_sym
51
- end
52
-
53
- # Execute search based on instance vars
54
- # Process Holdings based on display/availlibrary
55
- # Process URLs based on links/linktorsrc
56
- # Process TOCs based on links/linktotoc
57
- def search
58
- Rails.logger.warn("Insufficient search terms for #{self.class}. "+
59
- "Please refer to #{self.class}'s documentation to determine how to structure "+
60
- "a sufficient query.") and return if insufficient_query?
61
- # Call Primo Web Services
62
- unless @primo_id.nil? or @primo_id.empty?
63
- get_record = Exlibris::PrimoWS::GetRecord.new(@primo_id, @base_url, {:institution => @institution})
64
- @response = get_record.response
65
- process_record and process_search_results #since this is a search in addition to being a record call
66
- else
67
- brief_search = Exlibris::PrimoWS::SearchBrief.new(search_params, @base_url, {:institution => @institution})
68
- @response = brief_search.response
69
- process_search_results
70
- end
71
- end
72
-
73
- # Determine whether we have sufficient search criteria to search
74
- # Sufficient means either:
75
- # * We have a Primo doc id
76
- # * We have either an isbn OR an issn
77
- # * We have a title AND an author AND a genre
78
- def insufficient_query?
79
- return false unless (@primo_id.nil? or @primo_id.empty?)
80
- return false unless (@issn.nil? or @issn.empty?) and (@isbn.nil? or @isbn.empty?)
81
- return false unless (@title.nil? or @title.empty?) or (@author.nil? or @author.empty?) or (@genre.nil? or @genre.empty?)
82
- return true
83
- end
84
-
85
- # Search params are determined by input to Exlibris::PrimoWS::SearchBrief
86
- def search_params
87
- search_params = {}
88
- unless (@issn.nil? or @issn.empty?) and (@isbn.nil? or @isbn.empty?)
89
- search_params[:isbn] = @isbn unless @isbn.nil?
90
- search_params[:issn] = @issn if search_params.empty?
91
- else
92
- search_params[:title] = @title unless @title.nil?
93
- search_params[:author] = @author unless @title.nil? or @author.nil?
94
- search_params[:genre] = @genre unless @title.nil? or @author.nil? or @genre.nil?
95
- end
96
- return search_params
97
- end
98
-
99
- # Process a single record
100
- def process_record
101
- @count = response.at("//search:DOCSET", SEARCH_NS)["TOTALHITS"] unless response.nil? or @count
102
- response.at("//pnx:addata", PNX_NS).children.each do |addata_child|
103
- name = addata_child.name and value = addata_child.inner_text if addata_child.elem?
104
- next if value.nil?
105
- self.class.add_attr_reader name.to_sym unless name.nil?
106
- instance_variable_set("@#{name}".to_sym, "#{value}") unless name.nil?
107
- end
108
- @cover_image = response.at("//pnx:addata/pnx:lad02", PNX_NS).inner_text unless response.at("//pnx:addata/pnx:lad02", PNX_NS).nil?
109
- @titles = []
110
- response.search("//pnx:display/pnx:title", PNX_NS).each do |title|
111
- @titles.push(title.inner_text)
112
- end
113
- @authors = []
114
- response.search("//pnx:display/pnx:creator", PNX_NS).each do |creator|
115
- @authors.push(creator.inner_text)
116
- end
117
- end
118
-
119
- # Process search results
120
- # Process Holdings based on display/availlibrary
121
- # Process URLs based on links/linktorsrc
122
- # Process TOCs based on links/linktotoc
123
- def process_search_results
124
- @count = response.at("//search:DOCSET", SEARCH_NS)["TOTALHITS"] unless response.nil? or @count
125
- # Loop through records to set metadata for holdings, urls and tocs
126
- response.search("//pnx:record", PNX_NS).each do |record|
127
- # Default genre to article if necessary
128
- record_genre = (record.xpath("pnx:addata/pnx:genre", PNX_NS).nil?) ? "article" : record.xpath("pnx:addata/pnx:genre", PNX_NS).inner_text
129
- # Don't process if passed in genre doesn't match the record genre unless the discrepancy is only b/w journals and articles
130
- # If we're working off id numbers, we should be good to proceed
131
- next unless @primo_id or @isbn or @issn or
132
- @genre == record_genre or (@genre == "journal" and record_genre == "article")
133
- # Just take the first element for record level elements
134
- # (should only be one, except sourceid which will be handled later)
135
- record_id = record.xpath("pnx:control/pnx:recordid", PNX_NS).inner_text
136
- display_type = record.xpath("pnx:display/pnx:type", PNX_NS).inner_text
137
- original_source_id = record.xpath("pnx:control/pnx:originalsourceid", PNX_NS).inner_text unless record.xpath("pnx:control/pnx:originalsourceid", PNX_NS).nil?
138
- original_source_ids = process_control_hash(record, "pnx:control/pnx:originalsourceid", PNX_NS)
139
- source_id = record.xpath("pnx:control/pnx:sourceid", PNX_NS).inner_text
140
- source_ids = process_control_hash(record, "pnx:control/pnx:sourceid", PNX_NS)
141
- source_record_id = record.xpath("pnx:control/pnx:sourcerecordid", PNX_NS).inner_text
142
- # Process holdings
143
- source_record_ids = process_control_hash(record, "pnx:control/pnx:sourcerecordid", PNX_NS)
144
- record.xpath("pnx:display/pnx:availlibrary", PNX_NS).each do |availlibrary|
145
- availlibrary, institution_code, library_code, id_one, id_two, status_code, origin = process_availlibrary availlibrary
146
- holding_original_source_id = (origin.nil?) ? original_source_ids[record_id] : original_source_ids[origin] unless original_source_ids.empty?
147
- holding_original_source_id = original_source_id if holding_original_source_id.nil?
148
- holding_source_id = (origin.nil?) ? source_ids[record_id] : source_ids[origin] unless source_ids.empty?
149
- holding_source_id = source_id if holding_source_id.nil?
150
- holding_source_record_id = (origin.nil?) ? source_record_ids[record_id] : source_record_ids[origin] unless source_record_ids.empty?
151
- holding_source_record_id = source_record_id if holding_source_record_id.nil?
152
- holding_parameters = {
153
- :base_url => @base_url, :vid => @vid, :config => @config,
154
- :record_id => record_id, :original_source_id => holding_original_source_id,
155
- :source_id => holding_source_id, :source_record_id => holding_source_record_id,
156
- :origin => origin, :availlibrary => availlibrary, :institution_code => institution_code,
157
- :library_code => library_code, :id_one => id_one, :id_two => id_two,
158
- :status_code => status_code, :origin => origin, :display_type => display_type, :notes => "",
159
- :match_reliability =>
160
- (record.xpath("pnx:display/pnx:title", PNX_NS) and record.xpath("pnx:display/pnx:creator", PNX_NS)) ?
161
- (reliable_match?(:title => record.xpath("pnx:display/pnx:title", PNX_NS).inner_text, :author => record.xpath("pnx:display/pnx:creator", PNX_NS).inner_text)) ?
162
- ServiceResponse::MatchExact : ServiceResponse::MatchUnsure : ServiceResponse::MatchExact
163
- }
164
- holding = Exlibris::Primo::Holding.new(holding_parameters)
165
- @holdings.push(holding) unless holding.nil?
166
- end
167
- # Process urls
168
- record.xpath("pnx:links/pnx:linktorsrc", PNX_NS).each do |linktorsrc|
169
- linktorsrc, v, url, display, institution_code, origin = process_linktorsrc linktorsrc
170
- rsrc = Exlibris::Primo::Rsrc.new({
171
- :record_id => record_id, :linktorsrc => linktorsrc,
172
- :v => v, :url => url, :display => display,
173
- :institution_code => institution_code, :origin => origin,
174
- :notes => ""
175
- }) unless linktorsrc.nil?
176
- @rsrcs.push(rsrc) unless (rsrc.nil? or rsrc.url.nil?)
177
- end
178
- # Process tocs
179
- record.xpath("pnx:links/pnx:linktotoc", PNX_NS).each do |linktotoc|
180
- linktotoc, url, display = process_linktotoc linktotoc
181
- toc = Exlibris::Primo::Toc.new({
182
- :record_id => record_id, :linktotoc => linktotoc,
183
- :url => url, :display => display,
184
- :notes => ""
185
- }) unless linktotoc.nil?
186
- @tocs.push(toc) unless (toc.nil? or toc.url.nil?)
187
- end
188
- # Process addlinks
189
- record.xpath("pnx:links/pnx:addlink", PNX_NS).each do |addlink|
190
- addlink, url, display = process_addlink addlink
191
- related_link = Exlibris::Primo::RelatedLink.new({
192
- :record_id => record_id, :addlink => addlink,
193
- :url => url, :display => display,
194
- :notes => ""
195
- }) unless addlink.nil?
196
- @related_links.push(related_link) unless (related_link.nil? or related_link.url.nil?)
197
- end
198
- end
199
- end
200
-
201
- def process_control_hash(record, xpath, ns)
202
- h = {}
203
- record.xpath(xpath, ns).each do |e|
204
- str = e.inner_text unless e.nil?
205
- a = str.split(/\$(?=\$)/) unless str.nil?
206
- v = nil
207
- o = nil
208
- a.each do |s|
209
- v = s.sub!(/^\$V/, "") unless s.match(/^\$V/).nil?
210
- o = s.sub!(/^\$O/, "") unless s.match(/^\$O/).nil?
211
- end
212
- h[o] = v unless (o.nil? or v.nil?)
213
- end
214
- return h
215
- end
216
-
217
- # Determine how sure we are that this is a match.
218
- # Dynamically compares record metadata to input values
219
- # based on the values passed in.
220
- # Minimum requirement is to check title.
221
- def reliable_match?(record_metadata)
222
- return true unless (@primo_id.nil? or @primo_id.empty?)
223
- return true unless (@issn.nil? or @issn.empty?) and (@isbn.nil? or @isbn.empty?)
224
- return false if (record_metadata.nil? or record_metadata.empty? or record_metadata[:title].nil? or record_metadata[:title].empty?)
225
- # Titles must be equal
226
- return false unless record_metadata[:title].downcase.eql?(@title.downcase)
227
- # Compare record metadata with metadata that was passed in.
228
- # Only check if the record metadata value contains the input value since we can't be too strict.
229
- record_metadata.each { |type, value| return false if value.downcase.match("#{self.method(type).call}".downcase).nil?}
230
- return true
231
- end
232
-
233
- def process_availlibrary(input)
234
- availlibrary, institution_code, library_code, id_one, id_two, status_code, origin =
235
- nil, nil, nil, nil, nil, nil, nil
236
- return institution_code, library_code, id_one, id_two, status_code, origin if input.nil? or input.inner_text.nil?
237
- availlibrary = input.inner_text
238
- availlibrary.split(/\$(?=\$)/).each do |s|
239
- institution_code = s.sub!(/^\$I/, "") unless s.match(/^\$I/).nil?
240
- library_code = s.sub!(/^\$L/, "") unless s.match(/^\$L/).nil?
241
- id_one = s.sub!(/^\$1/, "") unless s.match(/^\$1/).nil?
242
- id_two = s.sub!(/^\$2/, "") unless s.match(/^\$2/).nil?
243
- # Always display "Check Availability" if this is from Primo.
244
- #@status_code = s.sub!(/^\$S/, "") unless s.match(/^\$S/).nil?
245
- status_code = "check_holdings"
246
- origin = s.sub!(/^\$O/, "") unless s.match(/^\$O/).nil?
247
- end
248
- return availlibrary, institution_code, library_code, id_one, id_two, status_code, origin
249
- end
250
-
251
- def process_linktorsrc(input)
252
- linktorsrc, v, url, display, institution_code, origin = nil, nil, nil, nil, nil, nil
253
- return linktorsrc, v, url, display, institution_code, origin if input.nil? or input.inner_text.nil?
254
- linktorsrc = input.inner_text
255
- linktorsrc.split(/\$(?=\$)/).each do |s|
256
- v = s.sub!(/^\$V/, "") unless s.match(/^\$V/).nil?
257
- url = s.sub!(/^\$U/, "") unless s.match(/^\$U/).nil?
258
- display = s.sub!(/^\$D/, "") unless s.match(/^\$D/).nil?
259
- institution_code = s.sub!(/^\$I/, "") unless s.match(/^\$I/).nil?
260
- origin = s.sub!(/^\$O/, "") unless s.match(/^\$O/).nil?
261
- end
262
- return linktorsrc, v, url, display, institution_code, origin
263
- end
264
-
265
- def process_linktotoc(input)
266
- linktotoc, url, display, = nil, nil, nil
267
- return linktotoc, url, display if input.nil? or input.inner_text.nil?
268
- linktotoc = input.inner_text
269
- linktotoc.split(/\$(?=\$)/).each do |s|
270
- url = s.sub!(/^\$U/, "") unless s.match(/^\$U/).nil?
271
- display = s.sub!(/^\$D/, "") unless s.match(/^\$D/).nil?
272
- end
273
- return linktotoc, url, display
274
- end
275
-
276
- def process_addlink(input)
277
- addlink, url, display, = nil, nil, nil
278
- return addlink, url, display if input.nil? or input.inner_text.nil?
279
- addlink = input.inner_text
280
- addlink.split(/\$(?=\$)/).each do |s|
281
- url = s.sub!(/^\$U/, "") unless s.match(/^\$U/).nil?
282
- display = s.sub!(/^\$D/, "") unless s.match(/^\$D/).nil?
283
- end
284
- return addlink, url, display
285
- end
286
-
287
- def raise_required_setup_parameter_error(parameter)
288
- raise "Initialization error in #{self.class}. Missing required setup parameter: #{parameter}."
289
- end
290
-
291
- # def convert_diacritics(string)
292
- # converter = Iconv.new('UTF-8', 'UTF-8')
293
- # # Convert value to UTF-8
294
- # return converter.iconv(string) unless string.nil?
295
- # end
296
- end
297
- end