umlaut 3.0.0alpha9 → 3.0.0alpha10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/app/controllers/umlaut_controller.rb +5 -0
  2. data/app/models/collection.rb +90 -54
  3. data/app/models/referent.rb +30 -17
  4. data/app/models/service_wave.rb +59 -41
  5. data/lib/exlibris/primo/holding.rb +3 -9
  6. data/lib/exlibris/primo/related_link.rb +17 -0
  7. data/lib/exlibris/primo/searcher.rb +52 -31
  8. data/lib/exlibris/primo/source/distribution/nyu_aleph.rb +59 -38
  9. data/lib/exlibris/primo_ws.rb +1 -1
  10. data/lib/service.rb +1 -1
  11. data/lib/service_adaptors/primo_service.rb +38 -5
  12. data/lib/service_adaptors/primo_source.rb +2 -2
  13. data/lib/service_adaptors/sfx.rb +11 -0
  14. data/lib/term_color.rb +9 -2
  15. data/lib/umlaut/version.rb +1 -1
  16. data/lib/umlaut_configurable.rb +9 -0
  17. data/test/dummy/config/umlaut_services.yml +20 -0
  18. data/test/dummy/tmp/cache/assets/CDC/680/sprockets%2F2b68ef632d12610f3c9563168bfa7c05 +0 -0
  19. data/test/dummy/tmp/cache/assets/CF5/9B0/sprockets%2F7933bfe880731b396791f1682ce3f7fa +0 -0
  20. data/test/dummy/tmp/cache/assets/CFB/2F0/sprockets%2F62d51f0aa5cac4b1cf7091823772a604 +0 -0
  21. data/test/dummy/tmp/cache/assets/D4C/0A0/sprockets%2F7810d837eec3ac57ad78756af83a6a55 +0 -0
  22. data/test/dummy/tmp/cache/assets/D4C/E30/sprockets%2F631abf89746799b7a5b2b3b4f6294bcd +0 -0
  23. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  24. data/test/dummy/tmp/cache/assets/D6C/7D0/sprockets%2F8a05d6981ec0d38c51739bef0b3a9c2b +0 -0
  25. data/test/dummy/tmp/cache/assets/D70/080/sprockets%2F24d3ce40ae5cc827a9183b1fb837e84e +0 -0
  26. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  27. data/test/dummy/tmp/cache/assets/E5F/960/sprockets%2Fdc007b6cad5c7ef08e33ec28cfff0ef6 +0 -0
  28. data/test/unit/aleph_patron_test.rb +6 -6
  29. data/test/unit/aleph_record_benchmarks.rb +1 -1
  30. data/test/unit/aleph_record_test.rb +4 -4
  31. data/test/unit/primo_searcher_test.rb +90 -82
  32. data/test/unit/primo_service_test.rb +683 -680
  33. data/test/unit/primo_ws_test.rb +45 -32
  34. metadata +135 -155
  35. data/lib/tasks/#Untitled-1# +0 -1843
  36. data/test/dummy/out +0 -5
@@ -57,6 +57,11 @@ class UmlautController < ApplicationController
57
57
  # (see lib/referent_filters )
58
58
  # add_referent_filters!( :match => /.*/, :filter => DissertationCatch.new )
59
59
 
60
+ # Turn off permalink-generation? If you don't want it at all, or
61
+ # don't want it temporarily because you are pointing at a database
62
+ # that won't last.
63
+ # create_permalinks false
64
+
60
65
  # How many seconds between updates of the background updater for background
61
66
  # services?
62
67
  # poll_wait_seconds 4
@@ -53,6 +53,95 @@ class Collection
53
53
  # Sets all services in collection to have a 'queued' status if appropriate.
54
54
  # Then actually executes the services that are dispatchable (queued).
55
55
  def dispatch_services!
56
+ queued_service_ids = prepare_for_dispatch!
57
+
58
+ dispatch_foreground!(queued_service_ids)
59
+
60
+ dispatch_background!(queued_service_ids)
61
+ end
62
+
63
+ # Call prepare_for_dispatch! first, the return value from that call
64
+ # is suitable as argument for this call: queued_service_ids, list of
65
+ # service id's already identified as suitable for running, and
66
+ # marked queued in the DispatchedService table.
67
+ #
68
+ # Will run such services in foreground priority waves. And then reload
69
+ # the UmlautRequest object in the current thread, to pick up any
70
+ # changes made in service threads.
71
+ def dispatch_foreground!(queued_service_ids)
72
+ # Foreground services
73
+ (0..9).each do | priority |
74
+ services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
75
+ next if services_to_run.empty?
76
+ ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
77
+ end
78
+
79
+ # Need to reload the request from db, so it gets changes
80
+ # made by services in threads, so future code (such as view rendering)
81
+ # will see changes.
82
+ umlaut_request.reload
83
+ end
84
+
85
+ # Call prepare_for_dispatch! first, the return value from that call
86
+ # is suitable as argument for this call: queued_service_ids, list of
87
+ # service id's already identified as suitable for running, and
88
+ # marked queued in the DispatchedService table.
89
+ #
90
+ # Will run such services in background priority waves.
91
+ def dispatch_background!(queued_service_ids)
92
+ # Now we do some crazy magic, start a Thread to run our background
93
+ # services. We are NOT going to wait for this thread to join,
94
+ # we're going to let it keep doing it's thing in the background after
95
+ # we return a response to the browser
96
+ backgroundThread = Thread.new(self, umlaut_request) do | t_collection, t_request|
97
+ # Tell our AR extension not to allow implicit checkouts
98
+ ActiveRecord::Base.forbid_implicit_checkout_for_thread! if ActiveRecord::Base.respond_to?("forbid_implicit_checkout_for_thread!")
99
+
100
+ # got to reserve an AR connection for our main 'background traffic director'
101
+ # thread, so it has a connection to use to mark services as failed, at least.
102
+ ActiveRecord::Base.connection_pool.with_connection do
103
+ begin
104
+ # Deal with ruby's brain dead thread scheduling by setting
105
+ # bg threads to a lower priority so they don't interfere with fg
106
+ # threads.
107
+ Thread.current.priority = -1
108
+
109
+ ('a'..'z').each do | priority |
110
+ services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
111
+ next if services_to_run.empty?
112
+ ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
113
+ end
114
+ rescue Exception => e
115
+ # We are divorced from any HTTP request at this point, and may not
116
+ # have access to an ActiveRecord connection. Not much
117
+ # we can do except log it.
118
+ # If we're catching an exception here, service processing was
119
+ # probably interrupted, which is bad. You should not intentionally
120
+ # raise exceptions to be caught here.
121
+ #
122
+ # Normally even unexpected exceptions were caught inside the ServiceWave,
123
+ # and logged to db as well as logfile if possible, only bugs in ServiceWave
124
+ # itself should wind up caught here.
125
+ Thread.current[:exception] = e
126
+ logger.error("Background Service execution exception: #{e}\n\n " + clean_backtrace(e).join("\n"))
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+
133
+ # Goes through all services and marks them with a DispatchedService
134
+ # record in 'queued' state.
135
+ #
136
+ # Will time out any too-old services in a running state.
137
+ #
138
+ # Will remove DispatchedService status for
139
+ # any services marked failed that are old enough to re-run, or services
140
+ # that are too old to re-use. Such services are then queuable.
141
+ #
142
+ # Returns array of Service identifiers for services that are now
143
+ # queued and execable.
144
+ def prepare_for_dispatch!
56
145
  # Go through currently dispatched services, looking for timed out
57
146
  # services -- services still in progress that have taken too long,
58
147
  # as well as service responses that are too old to be used.
@@ -117,60 +206,7 @@ class Collection
117
206
  end
118
207
  end
119
208
 
120
-
121
-
122
- # Now actually dispatch.
123
-
124
- # Foreground services
125
- (0..9).each do | priority |
126
- services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
127
- next if services_to_run.empty?
128
- ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
129
- end
130
-
131
- # Need to reload the request from db, so it gets changes
132
- # made by services in threads.
133
- umlaut_request.reload
134
-
135
- # Now we run background services.
136
- # Now we do some crazy magic, start a Thread to run our background
137
- # services. We are NOT going to wait for this thread to join,
138
- # we're going to let it keep doing it's thing in the background after
139
- # we return a response to the browser
140
- backgroundThread = Thread.new(self, umlaut_request) do | t_collection, t_request|
141
- # Tell our AR extension not to allow implicit checkouts
142
- ActiveRecord::Base.forbid_implicit_checkout_for_thread! if ActiveRecord::Base.respond_to?("forbid_implicit_checkout_for_thread!")
143
-
144
- # got to reserve an AR connection for our main 'background traffic director'
145
- # thread, so it has a connection to use to mark services as failed, at least.
146
- ActiveRecord::Base.connection_pool.with_connection do
147
- begin
148
- # Deal with ruby's brain dead thread scheduling by setting
149
- # bg threads to a lower priority so they don't interfere with fg
150
- # threads.
151
- Thread.current.priority = -1
152
-
153
- ('a'..'z').each do | priority |
154
- services_to_run = self.instantiate_services!(:level => priority, :ids => queued_service_ids)
155
- next if services_to_run.empty?
156
- ServiceWave.new(services_to_run , priority).handle(umlaut_request, umlaut_request.session_id)
157
- end
158
- rescue Exception => e
159
- #debugger
160
- # We are divorced from any request at this point, not much
161
- # we can do except log it. Actually, we'll also store it in the
162
- # db, and clean up after any dispatched services that need cleaning up.
163
- # If we're catching an exception here, service processing was
164
- # probably interrupted, which is bad. You should not intentionally
165
- # raise exceptions to be caught here.
166
- Thread.current[:exception] = e
167
- logger.error("Background Service execution exception1: #{e}\n\n " + clean_backtrace(e).join("\n"))
168
- end
169
- end
170
- end
171
-
172
-
173
-
209
+ return queued_service_ids
174
210
  end
175
211
 
176
212
  def completed_dispatch_expired?(ds)
@@ -85,6 +85,9 @@ class Referent < ActiveRecord::Base
85
85
  # :permalink => false if you already have a permalink and don't
86
86
  # need to create one. Caller should attach that permalink to this referent!
87
87
  def self.create_by_context_object(co, options = {})
88
+ options = { :permalink => UmlautController.umlaut_config.create_permalinks
89
+ }.merge(options)
90
+
88
91
 
89
92
  self.clean_up_context_object(co)
90
93
  rft = Referent.new
@@ -186,8 +189,10 @@ class Referent < ActiveRecord::Base
186
189
  rv.key_name = key_name
187
190
  rv.value = value
188
191
  rv.normalized_value = normalized_value
189
-
190
- unless (key_name == "identifier" || key_name == "format")
192
+
193
+ if key_name == "private_data"
194
+ rv.private_data = true
195
+ elsif key_name != "identifier" && key_name != "format"
191
196
  rv.metadata = true
192
197
  end
193
198
 
@@ -209,11 +214,19 @@ class Referent < ActiveRecord::Base
209
214
  if rft.format
210
215
  ensure_value!('format', rft.format)
211
216
  end
212
-
217
+ if rft.private_data
218
+ # this comes in as "pid" or "rft_dat", we store it in
219
+ # our database as "private_data", sorry, easiest way to
220
+ # fit this in at the moment.
221
+ ensure_value!("private_data", rft.private_data)
222
+ end
223
+
213
224
  rft.metadata.each { | key, value |
214
225
  next unless value
215
226
  ensure_value!( key, value)
216
- }
227
+ }
228
+
229
+
217
230
  end
218
231
 
219
232
  # pass in a Referent, or a ropenurl ContextObjectEntity that has a metadata
@@ -320,9 +333,8 @@ class Referent < ActiveRecord::Base
320
333
  co.referent = OpenURL::ContextObjectEntity.new_from_format( fmt_uri )
321
334
  rft = co.referent
322
335
 
323
- # Now set all the values.
336
+ # Now set all the values.
324
337
  self.referent_values.each do | val |
325
- next if val.private_data?
326
338
  if val.metadata?
327
339
  rft.set_metadata(val.key_name, val.value)
328
340
  next
@@ -338,8 +350,7 @@ class Referent < ActiveRecord::Base
338
350
  # call self.metadata once and use the array for efficiency, don't
339
351
  # keep calling it. profiling shows it DOES make a difference.
340
352
  my_metadata = self.metadata
341
-
342
-
353
+
343
354
  if my_metadata['atitle'] && ! my_metadata['atitle'].blank?
344
355
  citation[:title] = my_metadata['atitle']
345
356
  citation[:title_label], citation[:subtitle_label] =
@@ -392,23 +403,25 @@ class Referent < ActiveRecord::Base
392
403
  end
393
404
  if ! my_metadata["au"].blank?
394
405
  citation[:author] = my_metadata["au"]
395
- elsif my_metadata["aulast"]
406
+ elsif my_metadata["aulast"]
396
407
  citation[:author] = my_metadata["aulast"]
397
408
  if ! my_metadata["aufirst"].blank?
398
- citation[:author] += ', '+my_metadata["aufirst"]
409
+ citation[:author] += ', '+my_metadata["aufirst"]
399
410
  else
400
411
  if ! my_metadata["auinit"].blank?
401
412
  citation[:author] += ', '+my_metadata["auinit"]
402
413
  else
403
- if ! my_metadata["auinit1"].blank?
414
+ if ! my_metadata["auinit1"].blank?
404
415
  citation[:author] += ', '+my_metadata["auinit1"]
405
- end
406
- if ! my_metadata["auinitm"].blank?
416
+ end
417
+ if ! my_metadata["auinitm"].blank?
407
418
  citation[:author] += my_metadata["auinitm"]
408
- end
409
- end
410
- end
411
- end
419
+ end
420
+ end
421
+ end
422
+ elsif my_metadata["aucorp"]
423
+ citation[:author] = my_metadata["aucorp"]
424
+ end
412
425
  if my_metadata['spage']
413
426
  citation[:page] = my_metadata['spage']
414
427
  citation[:page] += ' - ' + my_metadata['epage'] if ! my_metadata['epage'].blank?
@@ -5,12 +5,13 @@
5
5
  class ServiceWave
6
6
  attr_accessor :services
7
7
  attr_accessor :priority_level
8
+ attr_reader :config
8
9
 
9
10
  # Priority level is purely information, used for debug output.
10
11
  def initialize(service_objects, priority_level = nil, config = UmlautController.umlaut_config)
11
12
  @services = service_objects
12
13
  @priority_level = priority_level
13
-
14
+ @config = config
14
15
  @log_timing = config.lookup!("log_service_timing", true)
15
16
 
16
17
  # Don't forward exceptions, that'll interrupt other service processing.
@@ -44,58 +45,75 @@ class ServiceWave
44
45
  Rails.logger.info(TermColor.color("Umlaut: Launching service wave #{@priority_level}", :yellow) + ", request #{request.id}") if @log_timing
45
46
 
46
47
 
48
+
47
49
  threads = []
48
50
  some_service_executed = false
49
51
  @services.each do | service |
50
52
  some_service_executed = true
51
53
  local_request = nil
52
-
53
- threads << Thread.new(request.id, service.clone) do | request_id, local_service |
54
- # Deal with ruby's brain dead thread scheduling by setting
55
- # bg threads to a lower priority so they don't interfere with fg
56
- # threads.
57
- Thread.current.priority = -1
54
+
55
+ service_start = Time.now
58
56
 
59
- # Save some things in thread local hash useful for debugging
60
- Thread.current[:debug_name] = local_service.class.name
61
- Thread.current[:service] = service
57
+ if config.lookup!("threaded_service_wave", true)
62
58
 
63
- # Tell our AR extension not to allow implicit checkouts
64
- ActiveRecord::Base.forbid_implicit_checkout_for_thread! if ActiveRecord::Base.respond_to?("forbid_implicit_checkout_for_thread!")
65
- begin
66
- service_start = Time.now
67
- local_request = ActiveRecord::Base.connection_pool.with_connection do
68
- # pre-load all relationships so no ActiveRecord activity will be
69
- # needed later to see em.
70
- Request.includes(:referent, :service_responses, :dispatched_services).find(request_id)
59
+
60
+ threads << Thread.new(request.id, service.clone) do | request_id, local_service |
61
+ # Deal with ruby's brain dead thread scheduling by setting
62
+ # bg threads to a lower priority so they don't interfere with fg
63
+ # threads.
64
+ Thread.current.priority = -1
65
+
66
+ # Save some things in thread local hash useful for debugging
67
+ Thread.current[:debug_name] = local_service.class.name
68
+ Thread.current[:service] = service
69
+
70
+ # Tell our AR extension not to allow implicit checkouts
71
+ ActiveRecord::Base.forbid_implicit_checkout_for_thread! if ActiveRecord::Base.respond_to?("forbid_implicit_checkout_for_thread!")
72
+ begin
73
+ local_request = ActiveRecord::Base.connection_pool.with_connection do
74
+ # pre-load all relationships so no ActiveRecord activity will be
75
+ # needed later to see em.
76
+ Request.includes(:referent, :service_responses, :dispatched_services).find(request_id)
77
+ end
78
+
79
+
80
+ if prepare_dispatch!(local_request, local_service, session_id)
81
+ local_service.handle_wrapper(local_request)
82
+ else
83
+ Rails.logger.info("NOT launching service #{local_service.service_id}, level #{@priority_level}, request #{local_request.id}: not in runnable state") if @log_timing
84
+ end
85
+
86
+
87
+ rescue Exception => e
88
+ # We may not be able to access ActiveRecord because it may
89
+ # have been an AR connection error, perhaps out of connections
90
+ # in the pool. So log and record in non-AR ways.
91
+ # the code waiting on our thread will see exception
92
+ # reported in Thread local var, and log it AR if possible.
93
+
94
+
95
+ # Log it too, although experience shows it may never make it to the
96
+ # log for mysterious reasons.
97
+ Rails.logger.error(TermColor.color("Umlaut: Threaded service raised exception.", :red, true) + "Service: #{service.service_id}, #{e}\n #{clean_backtrace(e).join("\n ")}")
98
+
99
+ # And stick it in a thread variable too
100
+ Thread.current[:exception] = e
101
+ ensure
102
+ Rails.logger.info(TermColor.color("Umlaut: Completed service #{local_service.service_id}", :yellow)+ ", level #{@priority_level}, request #{local_request && local_request.id}: in #{Time.now - service_start}.") if @log_timing
71
103
  end
72
-
73
-
74
- if prepare_dispatch!(local_request, local_service, session_id)
75
- local_service.handle_wrapper(local_request)
104
+ end
105
+ else # not threaded
106
+ begin
107
+ if prepare_dispatch!(request, service, session_id)
108
+ service.handle_wrapper(request)
76
109
  else
77
- Rails.logger.info("NOT launching service #{local_service.service_id}, level #{@priority_level}, request #{local_request.id}: not in runnable state") if @log_timing
110
+ Rails.logger.info("NOT launching service #{service.service_id}, level #{@priority_level}, request #{request.id}: not in runnable state") if @log_timing
78
111
  end
79
-
80
-
81
- rescue Exception => e
82
- # We may not be able to access ActiveRecord because it may
83
- # have been an AR connection error, perhaps out of connections
84
- # in the pool. So log and record in non-AR ways.
85
- # the code waiting on our thread will see exception
86
- # reported in Thread local var, and log it AR if possible.
87
-
88
-
89
- # Log it too, although experience shows it may never make it to the
90
- # log for mysterious reasons.
91
- Rails.logger.error(TermColor.color("Umlaut: Threaded service raised exception.", :red, true) + "Service: #{service.service_id}, #{e}\n #{clean_backtrace(e).join("\n ")}")
92
-
93
- # And stick it in a thread variable too
94
- Thread.current[:exception] = e
95
112
  ensure
96
- Rails.logger.info(TermColor.color("Umlaut: Completed service #{local_service.service_id}", :yellow)+ ", level #{@priority_level}, request #{local_request && local_request.id}: in #{Time.now - service_start}.") if @log_timing
113
+ Rails.logger.info(TermColor.color("Umlaut: Completed service #{service.service_id}", :yellow)+ ", level #{@priority_level}, request #{request && request.id}: in #{Time.now - service_start}.") if @log_timing
97
114
  end
98
- end
115
+ end
116
+
99
117
  end
100
118
 
101
119
  # Wait for all the threads to complete, if any.
@@ -36,7 +36,6 @@ module Exlibris::Primo
36
36
  # == Examples
37
37
  # Example of Primo source implementations are:
38
38
  # * Exlibris::Primo::Source::Aleph
39
- # * Exlibris::Primo::Source::Local::NYUAleph
40
39
  class Holding
41
40
  @base_attributes = [ :record_id, :source_id, :original_source_id, :source_record_id,
42
41
  :availlibrary, :institution_code, :institution, :library_code, :library,
@@ -141,16 +140,11 @@ module Exlibris::Primo
141
140
  def to_source
142
141
  return self if @source_class.nil?
143
142
  begin
144
- # Check source class in source module, if not found, see if there is a local class
145
- return (Exlibris::Primo::Source.const_defined?(@source_class)) ?
146
- Exlibris::Primo::Source.const_get(@source_class).new(:holding => self) :
147
- Exlibris::Primo::Source::Local.const_get(@source_class).new(:holding => self)
143
+ # Get source class in Primo::Source module
144
+ return Exlibris::Primo::Source.const_get(@source_class).new(:holding => self)
148
145
  rescue Exception => e
149
- # !!!!!!!!!!REMOVE NEXT LINE (raise e) WHEN GOING TO PRODUCTION!!!!!!!!!
150
- raise e
151
146
  Rails.logger.error("#{e.message}")
152
- Rails.logger.error("Class #{@source_class} can't be found in either
153
- Exlibris::Primo::Source or Exlibris::Primo::Source::Local.
147
+ Rails.logger.error("Class #{@source_class} can't be found in Exlibris::Primo::Source.
154
148
  Please check primo.yml to ensure the class_name is defined correctly.
155
149
  Not converting to source.")
156
150
  return self
@@ -0,0 +1,17 @@
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
@@ -16,7 +16,9 @@ module Exlibris::Primo
16
16
 
17
17
  attr_reader :response, :count
18
18
  attr_reader :cover_image, :titles, :author
19
- attr_reader :holdings, :rsrcs, :tocs
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'}
20
22
 
21
23
  # Instantiates the object and performs the search for based on the input search criteria.
22
24
  # setup parameter requires { :base_url => http://primo.server.institution.edu }
@@ -28,6 +30,7 @@ module Exlibris::Primo
28
30
  @holdings = []
29
31
  @rsrcs = []
30
32
  @tocs = []
33
+ @related_links = []
31
34
  @holding_attributes = Exlibris::Primo::Holding.base_attributes
32
35
  @base_url = setup[:base_url]
33
36
  raise_required_setup_parameter_error :base_url if @base_url.nil?
@@ -95,22 +98,21 @@ module Exlibris::Primo
95
98
 
96
99
  # Process a single record
97
100
  def process_record
98
- @count = response.at("//DOCSET")["TOTALHITS"] unless response.nil? or @count
99
- response.at("//addata").each_child do |addata_child|
100
- name = addata_child.pathname and value = addata_child.inner_text.chars.to_s if addata_child.elem?
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?
101
104
  next if value.nil?
102
105
  self.class.add_attr_reader name.to_sym unless name.nil?
103
- # instance_variable_set("@#{name}".to_sym, "#{convert_diacritics(value)}") unless name.nil?
104
106
  instance_variable_set("@#{name}".to_sym, "#{value}") unless name.nil?
105
107
  end
106
- @cover_image = response.at("//addata/lad02").inner_text unless response.at("//addata/lad02").nil?
108
+ @cover_image = response.at("//pnx:addata/pnx:lad02", PNX_NS).inner_text unless response.at("//pnx:addata/pnx:lad02", PNX_NS).nil?
107
109
  @titles = []
108
- response.search("//display/title") do |title|
109
- @titles.push(title.inner_text.chars.to_s)
110
+ response.search("//pnx:display/pnx:title", PNX_NS).each do |title|
111
+ @titles.push(title.inner_text)
110
112
  end
111
113
  @authors = []
112
- response.search("//display/creator") do |e|
113
- @authors.push(e.inner_text.chars.to_s)
114
+ response.search("//pnx:display/pnx:creator", PNX_NS).each do |creator|
115
+ @authors.push(creator.inner_text)
114
116
  end
115
117
  end
116
118
 
@@ -119,30 +121,27 @@ module Exlibris::Primo
119
121
  # Process URLs based on links/linktorsrc
120
122
  # Process TOCs based on links/linktotoc
121
123
  def process_search_results
122
- @count = (response.at("//DOCSET").nil?) ?
123
- (response.at("//sear:DOCSET")["TOTALHITS"].nil?) ? 0 :
124
- response.at("//sear:DOCSET")["TOTALHITS"] :
125
- response.at("//DOCSET")["TOTALHITS"] unless response.nil? or @count
124
+ @count = response.at("//search:DOCSET", SEARCH_NS)["TOTALHITS"] unless response.nil? or @count
126
125
  # Loop through records to set metadata for holdings, urls and tocs
127
- response.search("//record") do |record|
126
+ response.search("//pnx:record", PNX_NS).each do |record|
128
127
  # Default genre to article if necessary
129
- record_genre = (record.at("addata/genre").nil?) ? "article" : record.at("addata/genre").inner_text
128
+ record_genre = (record.xpath("pnx:addata/pnx:genre", PNX_NS).nil?) ? "article" : record.xpath("pnx:addata/pnx:genre", PNX_NS).inner_text
130
129
  # Don't process if passed in genre doesn't match the record genre unless the discrepancy is only b/w journals and articles
131
130
  # If we're working off id numbers, we should be good to proceed
132
131
  next unless @primo_id or @isbn or @issn or
133
132
  @genre == record_genre or (@genre == "journal" and record_genre == "article")
134
133
  # Just take the first element for record level elements
135
134
  # (should only be one, except sourceid which will be handled later)
136
- record_id = record.at("control/recordid").inner_text
137
- display_type = record.at("display/type").inner_text
138
- original_source_id = record.at("control/originalsourceid").inner_text unless record.at("control/originalsourceid").nil?
139
- original_source_ids = process_control_hash(record, "control/originalsourceid")
140
- source_id = record.at("control/sourceid").inner_text
141
- source_ids = process_control_hash(record, "control/sourceid")
142
- source_record_id = record.at("control/sourcerecordid").inner_text
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
143
142
  # Process holdings
144
- source_record_ids = process_control_hash(record, "control/sourcerecordid")
145
- record.search("display/availlibrary") do |availlibrary|
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|
146
145
  availlibrary, institution_code, library_code, id_one, id_two, status_code, origin = process_availlibrary availlibrary
147
146
  holding_original_source_id = (origin.nil?) ? original_source_ids[record_id] : original_source_ids[origin] unless original_source_ids.empty?
148
147
  holding_original_source_id = original_source_id if holding_original_source_id.nil?
@@ -158,14 +157,15 @@ module Exlibris::Primo
158
157
  :library_code => library_code, :id_one => id_one, :id_two => id_two,
159
158
  :status_code => status_code, :origin => origin, :display_type => display_type, :notes => "",
160
159
  :match_reliability =>
161
- (reliable_match?(:title => record.at("display/title").inner_text, :author => record.at("display/creator").inner_text)) ?
162
- ServiceResponse::MatchExact : ServiceResponse::MatchUnsure
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
163
  }
164
164
  holding = Exlibris::Primo::Holding.new(holding_parameters)
165
165
  @holdings.push(holding) unless holding.nil?
166
166
  end
167
167
  # Process urls
168
- record.search("links/linktorsrc") do |linktorsrc|
168
+ record.xpath("pnx:links/pnx:linktorsrc", PNX_NS).each do |linktorsrc|
169
169
  linktorsrc, v, url, display, institution_code, origin = process_linktorsrc linktorsrc
170
170
  rsrc = Exlibris::Primo::Rsrc.new({
171
171
  :record_id => record_id, :linktorsrc => linktorsrc,
@@ -176,7 +176,7 @@ module Exlibris::Primo
176
176
  @rsrcs.push(rsrc) unless (rsrc.nil? or rsrc.url.nil?)
177
177
  end
178
178
  # Process tocs
179
- record.search("links/linktotoc") do |linktotoc|
179
+ record.xpath("pnx:links/pnx:linktotoc", PNX_NS).each do |linktotoc|
180
180
  linktotoc, url, display = process_linktotoc linktotoc
181
181
  toc = Exlibris::Primo::Toc.new({
182
182
  :record_id => record_id, :linktotoc => linktotoc,
@@ -185,12 +185,22 @@ module Exlibris::Primo
185
185
  }) unless linktotoc.nil?
186
186
  @tocs.push(toc) unless (toc.nil? or toc.url.nil?)
187
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
188
198
  end
189
199
  end
190
200
 
191
- def process_control_hash(record, xpath)
201
+ def process_control_hash(record, xpath, ns)
192
202
  h = {}
193
- record.search(xpath) do |e|
203
+ record.xpath(xpath, ns).each do |e|
194
204
  str = e.inner_text unless e.nil?
195
205
  a = str.split(/\$(?=\$)/) unless str.nil?
196
206
  v = nil
@@ -263,6 +273,17 @@ module Exlibris::Primo
263
273
  return linktotoc, url, display
264
274
  end
265
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
+
266
287
  def raise_required_setup_parameter_error(parameter)
267
288
  raise "Initialization error in #{self.class}. Missing required setup parameter: #{parameter}."
268
289
  end