voyager_api 0.1.2 → 0.2.0
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.
- data/Gemfile +2 -1
- data/Gemfile.lock +6 -0
- data/VERSION +1 -1
- data/config/item_status_codes.yml +172 -0
- data/config/order_status_codes.yml +25 -0
- data/lib/holdings/collection.rb +229 -3
- data/lib/holdings/item.rb +89 -145
- data/lib/holdings/order.rb +15 -17
- data/lib/holdings/record.rb +100 -1
- data/lib/voyager_api.rb +3 -0
- data/lib/voyager_config.rb +4 -0
- data/test/fixtures/holdings_1717646.xml +2 -0
- data/test/fixtures/holdings_2606957.xml +2 -0
- data/test/fixtures/holdings_6249927.xml +2 -0
- data/test/fixtures/holdings_8430339.xml +2 -0
- data/test/holdings/test_collection_output.rb +65 -2
- data/test/holdings/test_item.rb +16 -14
- data/test/holdings/test_item_status_codes.rb +10 -0
- data/test/holdings/test_order.rb +5 -4
- data/test/holdings/test_record.rb +48 -0
- data/voyager_api.gemspec +7 -6
- metadata +53 -23
data/lib/holdings/item.rb
CHANGED
|
@@ -77,7 +77,11 @@ module Voyager
|
|
|
77
77
|
|
|
78
78
|
# no items = no status available
|
|
79
79
|
if item_count == "0"
|
|
80
|
-
return {:status => 'none',
|
|
80
|
+
return {:status => 'none',
|
|
81
|
+
:messages => [ {:status_code => '0',
|
|
82
|
+
:short_message => 'Status unknown',
|
|
83
|
+
:long_message => 'No item status available'} ]
|
|
84
|
+
}
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
# item:itemRecord nodes
|
|
@@ -140,112 +144,110 @@ module Voyager
|
|
|
140
144
|
def generate_messages(records)
|
|
141
145
|
|
|
142
146
|
messages = []
|
|
143
|
-
|
|
144
147
|
records.each do |record|
|
|
145
|
-
|
|
146
|
-
if record[:requestCount] == '0'
|
|
147
|
-
messages << generate_message_no_requests(record)
|
|
148
|
-
else
|
|
149
|
-
messages << generate_message_requests(record)
|
|
150
|
-
end
|
|
151
|
-
|
|
148
|
+
messages << generate_message(record)
|
|
152
149
|
end
|
|
153
|
-
|
|
154
150
|
messages
|
|
155
151
|
|
|
156
152
|
end
|
|
157
153
|
|
|
158
154
|
# Generate message from a record (item:itemRecord node)
|
|
159
|
-
# No request count
|
|
160
155
|
#
|
|
161
156
|
# * *Args* :
|
|
162
157
|
# - +record+ -> Hash of data from an item:itemRecord node
|
|
163
158
|
# * *Returns* :
|
|
164
|
-
# - Formatted message
|
|
159
|
+
# - Formatted message in a hash
|
|
160
|
+
# :status_code => code of status message
|
|
161
|
+
# :short_message => short version of message
|
|
162
|
+
# :long_message => long version of message
|
|
165
163
|
#
|
|
166
|
-
def
|
|
167
|
-
|
|
168
|
-
# label is descriptive information for items; optional
|
|
169
|
-
labels = []
|
|
170
|
-
[:enumeration, :chronology, :year, :caption, :text].each do |type|
|
|
171
|
-
labels << record[type] unless record[type].empty?
|
|
172
|
-
end
|
|
173
|
-
labels.empty? ? label = '' : label = labels.join(' ') + ' '
|
|
164
|
+
def generate_message(record)
|
|
174
165
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
# status patron message otherwise
|
|
166
|
+
short_message = ''
|
|
167
|
+
long_message = ''
|
|
168
|
+
code = ''
|
|
169
|
+
|
|
170
|
+
# status patron message otherwise regular message
|
|
180
171
|
if record[:patronGroupCode].strip.match(/^(IND|MIS|ACO)/)
|
|
181
172
|
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
code = 'sp'
|
|
174
|
+
long_message = record[:lastName].strip + ' ' + record[:firstName].strip
|
|
175
|
+
# done in two steps in case ending puctuation is missing
|
|
176
|
+
short_message = long_message.gsub(/(Try|Place).+/, '').strip
|
|
177
|
+
short_message = short_message.gsub(/\W$/, '')
|
|
178
|
+
|
|
184
179
|
else
|
|
185
180
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
message = "Requested"
|
|
225
|
-
when '25'
|
|
226
|
-
message = "Requested"
|
|
181
|
+
code = record[:statusCode]
|
|
182
|
+
# append suffix to indicate whether there are requests - n = no requests, r = requests
|
|
183
|
+
record[:requestCount] == '0' ? code += 'n' : code += 'r'
|
|
184
|
+
|
|
185
|
+
# get parms for the message being processed
|
|
186
|
+
parms = ITEM_STATUS_CODES[code]
|
|
187
|
+
|
|
188
|
+
raise "Status code not found in config/item_status_codes.yml" unless parms
|
|
189
|
+
|
|
190
|
+
short_message = make_substitutions(parms['short_message'],record)
|
|
191
|
+
long_message = make_substitutions(parms['long_message'],record)
|
|
192
|
+
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# add labels
|
|
196
|
+
short_message = add_label(short_message,record)
|
|
197
|
+
long_message = add_label(long_message,record)
|
|
198
|
+
|
|
199
|
+
return { :status_code => code,
|
|
200
|
+
:short_message => short_message,
|
|
201
|
+
:long_message => long_message }
|
|
202
|
+
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def format_datetime(record)
|
|
206
|
+
|
|
207
|
+
# format date / time
|
|
208
|
+
datetime = ''
|
|
209
|
+
unless record[:statusDate].empty?
|
|
210
|
+
# use date in record so we can use stored test features
|
|
211
|
+
todays_date = DateTime.parse(record[:todaysDate])
|
|
212
|
+
# todays_date = DateTime.now
|
|
213
|
+
status_date = DateTime.parse(record[:statusDate])
|
|
214
|
+
diff = status_date - todays_date
|
|
215
|
+
# we have to accommodate dates in the past and the future relative to today
|
|
216
|
+
# remove times from past dates over 1 day old and future dates more than 2 days away
|
|
217
|
+
if diff.to_i > 2 || diff.to_i < 0
|
|
218
|
+
datetime = record[:statusDate].gsub(/\s.+/, '')
|
|
227
219
|
else
|
|
228
|
-
|
|
220
|
+
datetime = record[:statusDate]
|
|
229
221
|
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
datetime
|
|
230
225
|
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def make_substitutions(message,record)
|
|
229
|
+
|
|
230
|
+
datetime = format_datetime(record)
|
|
231
|
+
|
|
232
|
+
# substitute values for tokens in message strings
|
|
233
|
+
# date
|
|
234
|
+
unless datetime.empty?
|
|
235
|
+
message = message.gsub('%DATE', datetime)
|
|
236
|
+
end
|
|
237
|
+
# number of requests
|
|
238
|
+
unless record[:requestCount] == '0'
|
|
239
|
+
message = message.gsub('%REQS', record[:requestCount])
|
|
231
240
|
end
|
|
241
|
+
# hold location
|
|
242
|
+
unless record[:holdLocation].empty?
|
|
243
|
+
message = message.gsub('%LOC', record[:holdLocation])
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
message
|
|
232
247
|
|
|
233
|
-
# add label
|
|
234
|
-
message.insert(0, label) unless label.empty?
|
|
235
|
-
|
|
236
|
-
return message
|
|
237
|
-
|
|
238
248
|
end
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# Request count
|
|
242
|
-
#
|
|
243
|
-
# * *Args* :
|
|
244
|
-
# - +record+ -> Hash of data from an item:itemRecord node
|
|
245
|
-
# * *Returns* :
|
|
246
|
-
# - Formatted message
|
|
247
|
-
#
|
|
248
|
-
def generate_message_requests(record)
|
|
249
|
+
|
|
250
|
+
def add_label(message,record)
|
|
249
251
|
|
|
250
252
|
# label is descriptive information for items; optional
|
|
251
253
|
labels = []
|
|
@@ -254,69 +256,11 @@ module Voyager
|
|
|
254
256
|
end
|
|
255
257
|
labels.empty? ? label = '' : label = labels.join(' ') + ' '
|
|
256
258
|
|
|
257
|
-
datetime = record[:statusDate].split(' ') # split into date/time
|
|
258
|
-
|
|
259
|
-
message = ''
|
|
260
|
-
|
|
261
|
-
# status patron message otherwise regular message
|
|
262
|
-
if record[:patronGroupCode].strip.match(/^(IND|MIS|ACO)/)
|
|
263
|
-
|
|
264
|
-
message = record[:lastName].strip + ' ' + record[:firstName].strip
|
|
265
|
-
|
|
266
|
-
else
|
|
267
|
-
|
|
268
|
-
case record[:statusCode]
|
|
269
|
-
when '1'
|
|
270
|
-
message = "Not checked out (Requests: #{record[:requestCount]})"
|
|
271
|
-
when '2'
|
|
272
|
-
message = "Checked out, due #{datetime.join(' ')} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
273
|
-
when '3'
|
|
274
|
-
message = "Checked out, due #{datetime.join(' ')} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
275
|
-
when '4'
|
|
276
|
-
message = "Overdue as of #{datetime.join(' ')} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
277
|
-
when '5'
|
|
278
|
-
message = "Checked out, due #{datetime.join(' ')} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
279
|
-
when '6'
|
|
280
|
-
message = "Checked out, due #{datetime.join(' ')} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
281
|
-
when '7'
|
|
282
|
-
message = "On hold at #{record[:holdLocation]} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
283
|
-
when '8'
|
|
284
|
-
message = "In transit #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct"
|
|
285
|
-
when '9'
|
|
286
|
-
message = "In transit to #{record[:holdLocation]} #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct."
|
|
287
|
-
when '10'
|
|
288
|
-
message = "In transit to #{record[:holdLocation]} #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct"
|
|
289
|
-
when '11'
|
|
290
|
-
message = "Returned #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
291
|
-
when '12'
|
|
292
|
-
message = "Missing #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
293
|
-
when '13'
|
|
294
|
-
message = "Unavailable #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
295
|
-
when '14'
|
|
296
|
-
message = "Unavailable #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
297
|
-
when '18'
|
|
298
|
-
message = "Sent to bindery for 1 month on #{datetime[0]} (Requests: #{record[:requestCount]}). Try Borrow Direct or ILL."
|
|
299
|
-
when '21'
|
|
300
|
-
message = "Scheduled (Requests: #{record[:requestCount]})"
|
|
301
|
-
when '22'
|
|
302
|
-
message = "In Process #{datetime[0]} (Requests: #{record[:requestCount]}). Place an In Process item request."
|
|
303
|
-
when '23'
|
|
304
|
-
message = "Requests: #{record[:requestCount]}"
|
|
305
|
-
when '24'
|
|
306
|
-
message = "Requests: #{record[:requestCount]}"
|
|
307
|
-
when '25'
|
|
308
|
-
message = "Requests: #{record[:requestCount]}"
|
|
309
|
-
else
|
|
310
|
-
message = "Unknown"
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
end
|
|
314
|
-
|
|
315
259
|
# add label
|
|
316
260
|
message.insert(0, label) unless label.empty?
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
261
|
+
|
|
262
|
+
message
|
|
263
|
+
|
|
320
264
|
end
|
|
321
265
|
|
|
322
266
|
end
|
data/lib/holdings/order.rb
CHANGED
|
@@ -53,7 +53,10 @@ module Voyager
|
|
|
53
53
|
# * *Args* :
|
|
54
54
|
# - +poLineItems+ -> mfhd:poLineItems node
|
|
55
55
|
# * *Returns* :
|
|
56
|
-
# - Array of
|
|
56
|
+
# - Array of message hashes for orders
|
|
57
|
+
# :status_code => status code
|
|
58
|
+
# :short_message => short version of the message
|
|
59
|
+
# :long_message => long version of the message
|
|
57
60
|
# - An empty array if there is no mfhd:poLineItems node
|
|
58
61
|
#
|
|
59
62
|
def parse_order(poLineItems)
|
|
@@ -76,22 +79,17 @@ module Voyager
|
|
|
76
79
|
|
|
77
80
|
status = lineItemStatus.at_css("mfhd|status").content
|
|
78
81
|
date = lineItemStatus.at_css("mfhd|date").content
|
|
79
|
-
|
|
80
|
-
message
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
message = "Copy On Order #{date}. Try Borrow Direct or ILL."
|
|
91
|
-
else
|
|
92
|
-
message = "Unknown"
|
|
93
|
-
end
|
|
94
|
-
|
|
82
|
+
|
|
83
|
+
# get parms for the message being processed
|
|
84
|
+
parms = ORDER_STATUS_CODES[status]
|
|
85
|
+
|
|
86
|
+
raise "Status code not found in config/order_status_codes.yml" unless parms
|
|
87
|
+
|
|
88
|
+
short_message = parms['short_message'].gsub('%DATE',date)
|
|
89
|
+
long_message = parms['long_message'].gsub('%DATE',date)
|
|
90
|
+
|
|
91
|
+
{ :status_code => status, :short_message => short_message, :long_message => long_message }
|
|
92
|
+
|
|
95
93
|
end
|
|
96
94
|
|
|
97
95
|
end
|
data/lib/holdings/record.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Voyager
|
|
|
3
3
|
class Record
|
|
4
4
|
attr_reader :holding_id, :location_name, :call_number, :summary_holdings, :notes_852, :notes_866, :notes,
|
|
5
5
|
:shelving_title, :supplements, :indexes, :reproduction_note, :urls, :item_count, :temp_locations,
|
|
6
|
-
:item_status, :orders, :current_issues
|
|
6
|
+
:item_status, :orders, :current_issues, :services, :bibid
|
|
7
7
|
|
|
8
8
|
# Record class initializing method
|
|
9
9
|
# Populates instance variables from the mfhd:marcRecord node of the mfhd:mfhdRecord node.
|
|
@@ -13,6 +13,7 @@ module Voyager
|
|
|
13
13
|
# - +xml_node+ -> mfhd:mfhdRecord node
|
|
14
14
|
#
|
|
15
15
|
def initialize(xml_node)
|
|
16
|
+
@bibid = xml_node.attributes["bibId"].value
|
|
16
17
|
@holding_id = xml_node.attributes["mfhdId"].value
|
|
17
18
|
@location_name = xml_node.at_css("mfhd|mfhdData[@name='locationDisplayName']").content
|
|
18
19
|
# marc record node
|
|
@@ -64,6 +65,9 @@ module Voyager
|
|
|
64
65
|
@current_issues = order.current_issues
|
|
65
66
|
@orders = order.orders
|
|
66
67
|
|
|
68
|
+
# add available services
|
|
69
|
+
@services = determine_services(@location_name,@call_number,@item_status,@orders,@bibid)
|
|
70
|
+
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
# Collect data from all variables into a hash
|
|
@@ -73,6 +77,7 @@ module Voyager
|
|
|
73
77
|
#
|
|
74
78
|
def to_hash
|
|
75
79
|
{
|
|
80
|
+
:bibid => @bibid,
|
|
76
81
|
:holding_id => @holding_id,
|
|
77
82
|
:location_name => @location_name,
|
|
78
83
|
:call_number => @call_number,
|
|
@@ -86,6 +91,7 @@ module Voyager
|
|
|
86
91
|
:item_count => @item_count,
|
|
87
92
|
:temp_locations => @temp_locations,
|
|
88
93
|
:item_status => @item_status,
|
|
94
|
+
:services => @services,
|
|
89
95
|
:current_issues => @current_issues,
|
|
90
96
|
:orders => @orders
|
|
91
97
|
}
|
|
@@ -296,6 +302,99 @@ module Voyager
|
|
|
296
302
|
|
|
297
303
|
end
|
|
298
304
|
|
|
305
|
+
def determine_services(location_name,call_number,item_status,orders,bibid)
|
|
306
|
+
|
|
307
|
+
services = []
|
|
308
|
+
|
|
309
|
+
unless orders.empty?
|
|
310
|
+
orders.each do |order|
|
|
311
|
+
parms = ORDER_STATUS_CODES[order[:status_code]]
|
|
312
|
+
raise "Status code not found in config/order_status_codes.yml" unless parms
|
|
313
|
+
services << parms['services'] unless parms['services'].nil?
|
|
314
|
+
end
|
|
315
|
+
return services.flatten.uniq
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
services << scan_message(location_name)
|
|
319
|
+
|
|
320
|
+
status = item_status[:status]
|
|
321
|
+
messages = item_status[:messages]
|
|
322
|
+
|
|
323
|
+
case status
|
|
324
|
+
when 'online'
|
|
325
|
+
when 'none'
|
|
326
|
+
services << 'in_process' if call_number.match(/in process/i)
|
|
327
|
+
when 'available'
|
|
328
|
+
services << process_available(location_name,bibid)
|
|
329
|
+
when 'some_available'
|
|
330
|
+
services << process_some_available(location_name,bibid,messages)
|
|
331
|
+
when 'not_available'
|
|
332
|
+
services << scan_messages(messages)
|
|
333
|
+
else
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
services.flatten.uniq
|
|
337
|
+
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def process_available(location_name,bibid)
|
|
341
|
+
# offsite
|
|
342
|
+
if location_name.match(/^Offsite/) &&
|
|
343
|
+
HTTPClient.new.get_content("http://www.columbia.edu/cgi-bin/cul/lookupNBX?" + bibid) == "1"
|
|
344
|
+
return ['offsite']
|
|
345
|
+
end
|
|
346
|
+
# precat
|
|
347
|
+
if location_name.match(/^Precat/)
|
|
348
|
+
return ['precat']
|
|
349
|
+
end
|
|
350
|
+
return []
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def process_some_available(location_name,bibid,messages)
|
|
354
|
+
|
|
355
|
+
services = []
|
|
356
|
+
# offsite
|
|
357
|
+
if location_name.match(/^Offsite/) &&
|
|
358
|
+
HTTPClient.new.get_content("http://www.columbia.edu/cgi-bin/cul/lookupNBX?" + bibid) == "1"
|
|
359
|
+
services << 'offsite'
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
services << scan_messages(messages)
|
|
363
|
+
|
|
364
|
+
services
|
|
365
|
+
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def scan_messages(messages)
|
|
369
|
+
services = []
|
|
370
|
+
messages.each do |message|
|
|
371
|
+
# status patrons
|
|
372
|
+
if message[:status_code] == 'sp'
|
|
373
|
+
services << scan_message(message[:long_message])
|
|
374
|
+
else
|
|
375
|
+
parms = ITEM_STATUS_CODES[message[:status_code]]
|
|
376
|
+
raise "Status code not found in config/order_status_codes.yml" unless parms
|
|
377
|
+
services << parms['services'] unless parms['services'].nil?
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
services
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def scan_message(message)
|
|
385
|
+
|
|
386
|
+
out = []
|
|
387
|
+
out << 'recall_hold' if message =~ /Recall/i
|
|
388
|
+
out << 'recall_hold' if message =~ /hold /
|
|
389
|
+
out << 'borrow_direct' if message =~ /Borrow/
|
|
390
|
+
out << 'ill' if message =~ /ILL/
|
|
391
|
+
out << 'in_process' if message =~ /In Process/
|
|
392
|
+
|
|
393
|
+
out
|
|
394
|
+
|
|
395
|
+
end
|
|
396
|
+
|
|
299
397
|
end
|
|
398
|
+
|
|
300
399
|
end
|
|
301
400
|
end
|