evergreen_holdings 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +5 -5
  2. data/lib/evergreen_holdings.rb +196 -198
  3. metadata +32 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: da13869aee1d6030dc730f20f662d3bbae682f1b
4
- data.tar.gz: efbf034638ed3b6b301934de474f1dc1365bcfcb
2
+ SHA256:
3
+ metadata.gz: e5800a7a145695ef115c882d45a1ba4264ea8f97888d7d305b7fdbbda4085b84
4
+ data.tar.gz: 0eaf07e5c8bb021fd79fcee3ec8584beff6b1842d097de72b7285614e2866368
5
5
  SHA512:
6
- metadata.gz: 8a874195ffa5f2c7b0a202cecf66121d487d8eca4f60cf8ba0536f258040cc9a58d50dda5d6b4f62d6dcb17ce93a46654cdc3e3b0b6b81d0f0ca16872839ef6b
7
- data.tar.gz: 9557150bed33a4e14d189e1ee2cd0e8f6048ca44d63cd289bb10e32547e315ed7b2a5c2ad8efc73d346b1179dca1d5d52686a951e06c6e66ee6c62f443b98138
6
+ metadata.gz: 14af7152bf4201748e500b1821152cc0331413e6b4e7df8ede03983b180bfbe3e32f9d368714b8bf7d5f9a015003500b32fcfce5a98e757b98c40ced3813166e
7
+ data.tar.gz: e4e82b9fe288ae589cad6089d34464421a5f0ffb698abbaa1ae2d41e55a9f1b6cbd2477ba545e0fc7bbc897828f3bce8fc3262a9e79872c16d3c6e20fa23c881
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'net/http'
2
4
  require 'json'
3
5
  require 'evergreen_holdings/errors'
@@ -7,229 +9,225 @@ require 'open-uri'
7
9
  OSRF_PATH = '/osrf-gateway-v1'
8
10
 
9
11
  module EvergreenHoldings
10
- class Connection
11
- attr_reader :org_units
12
- # Create a new object with the evergreen_domain
13
- # specified, e.g. http://libcat.linnbenton.edu
14
- #
15
- # Usage: `conn = EvergreenHoldings::Connection.new 'http://gapines.org'`
16
- def initialize evergreen_domain
17
- @evergreen_domain = evergreen_domain
18
- @gateway = URI evergreen_domain+OSRF_PATH
19
- fetch_idl_order
20
- unless fetch_statuses
21
- raise CouldNotConnectToEvergreenError
22
- end
23
- fetch_ou_tree
12
+ class Connection
13
+ attr_reader :org_units
14
+ # Create a new object with the evergreen_domain
15
+ # specified, e.g. http://libcat.linnbenton.edu
16
+ #
17
+ # Usage: `conn = EvergreenHoldings::Connection.new 'http://gapines.org'`
18
+ def initialize(evergreen_domain)
19
+ @evergreen_domain = evergreen_domain
20
+ @gateway = URI evergreen_domain + OSRF_PATH
21
+ @acpl_cache = {}
22
+ fetch_idl_order
23
+ raise CouldNotConnectToEvergreenError unless fetch_statuses
24
+
25
+ fetch_ou_tree
26
+ end
27
+
28
+ # Fetch holdings data from the Evergreen server
29
+ # Returns a Status object
30
+ #
31
+ # Usage: `stat = conn.get_holdings 23405`
32
+ # If you just want holdings at a specific org_unit: `my_connection.get_holdings 23405, org_unit: 5`
33
+ def get_holdings(tcn, options = {})
34
+ if options.key?(:org_unit)
35
+ if options[:descendants]
36
+ params = "format=json&input_format=json&service=open-ils.cat&method=open-ils.cat.asset.copy_tree.retrieve&param=auth_token_not_needed_for_this_call&param=#{tcn}"
37
+ @org_units[options[:org_unit]][:descendants]&.each do |ou|
38
+ params << + "&param=#{ou}"
39
+ end
40
+ else
41
+ params = "format=json&input_format=json&service=open-ils.cat&method=open-ils.cat.asset.copy_tree.retrieve&param=auth_token_not_needed_for_this_call&param=#{tcn}&param=#{options[:org_unit]}"
24
42
  end
43
+ else
44
+ params = "format=json&input_format=json&service=open-ils.cat&method=open-ils.cat.asset.copy_tree.global.retrieve&param=auth_token_not_needed_for_this_call&param=#{tcn}"
45
+ end
46
+ @gateway.query = params
25
47
 
26
- # Fetch holdings data from the Evergreen server
27
- # Returns a Status object
28
- #
29
- # Usage: `stat = conn.get_holdings 23405`
30
- # If you just want holdings at a specific org_unit: `my_connection.get_holdings 23405, org_unit: 5`
31
- def get_holdings tcn, options = {}
32
- if options.key?(:org_unit)
33
- if options[:descendants]
34
- params = "format=json&input_format=json&service=open-ils.cat&method=open-ils.cat.asset.copy_tree.retrieve&param=auth_token_not_needed_for_this_call&param=#{tcn}"
35
- if @org_units[options[:org_unit]][:descendants]
36
- @org_units[options[:org_unit]][:descendants].each do |ou|
37
- params << + "&param=#{ou}"
38
- end
39
- end
40
- else
41
- params = "format=json&input_format=json&service=open-ils.cat&method=open-ils.cat.asset.copy_tree.retrieve&param=auth_token_not_needed_for_this_call&param=#{tcn}&param=#{options[:org_unit]}"
42
- end
43
- else
44
- params = "format=json&input_format=json&service=open-ils.cat&method=open-ils.cat.asset.copy_tree.global.retrieve&param=auth_token_not_needed_for_this_call&param=#{tcn}"
45
- end
46
- @gateway.query = params
48
+ res = send_query
49
+ return Status.new res.body, @idl_order, self if res
50
+ end
47
51
 
48
- res = send_query
49
- return Status.new res.body, @idl_order, self if res
50
- end
52
+ # Given an ID, returns a human-readable name
53
+ def location_name(id)
54
+ @acpl_cache.fetch(id) { |id| fetch_new_acpl(id) || id }
55
+ end
51
56
 
52
- # Given an ID, returns a human-readable name
53
- def location_name id
54
- params = "format=json&input_format=json&service=open-ils.circ&method=open-ils.circ.copy_location.retrieve&param=#{id}"
55
- @gateway.query = params
56
- res = send_query
57
- if res
58
- data = JSON.parse(res.body)['payload'][0]
59
- unless data.key? 'stacktrace'
60
- return data['__p'][@idl_order[:acpl]['name']]
61
- end
62
- end
63
- return id
64
- end
57
+ def status_name(id)
58
+ @possible_item_statuses[id]
59
+ end
65
60
 
66
- def status_name id
67
- return @possible_item_statuses[id]
61
+ def ou_name(id)
62
+ @org_units[id][:name]
68
63
  end
69
64
 
70
- def ou_name id
71
- return @org_units[id][:name]
72
- end
65
+ private
73
66
 
74
- private
75
-
76
- def add_ou_descendants id, parent
77
- (@org_units[parent][:descendants] ||= []) << id
78
- if @org_units[parent][:parent]
79
- add_ou_descendants id, @org_units[parent][:parent]
80
- end
81
- end
82
-
83
- def take_info_from_ou_tree o
84
- id = o[@idl_order[:aou]['id']]
85
- @org_units[id] = {}
86
- @org_units[id][:name] = o[@idl_order[:aou]['name']]
87
- if o[@idl_order[:aou]['parent_ou']]
88
- @org_units[id][:parent] = o[@idl_order[:aou]['parent_ou']]
89
- add_ou_descendants id, o[@idl_order[:aou]['parent_ou']]
90
- end
91
- o[@idl_order[:aou]['children']].each do |p|
92
- take_info_from_ou_tree p['__p']
93
- end
94
- end
67
+ def add_ou_descendants(id, parent)
68
+ (@org_units[parent][:descendants] ||= []) << id
69
+ add_ou_descendants id, @org_units[parent][:parent] if @org_units[parent][:parent]
70
+ end
95
71
 
72
+ def take_info_from_ou_tree(o)
73
+ id = o[@idl_order[:aou]['id']]
74
+ @org_units[id] = {}
75
+ @org_units[id][:name] = o[@idl_order[:aou]['name']]
76
+ if o[@idl_order[:aou]['parent_ou']]
77
+ @org_units[id][:parent] = o[@idl_order[:aou]['parent_ou']]
78
+ add_ou_descendants id, o[@idl_order[:aou]['parent_ou']]
79
+ end
80
+ o[@idl_order[:aou]['children']].each do |p|
81
+ take_info_from_ou_tree p['__p']
82
+ end
83
+ end
96
84
 
97
- def send_query
98
- begin
99
- res = Net::HTTP.get_response(@gateway)
100
- rescue Errno::ECONNREFUSED, Net::ReadTimeout
101
- return nil
102
- end
103
- return res if res.is_a?(Net::HTTPSuccess)
104
- return nil
105
- end
85
+ # Given the ID of a shelving location, this method
86
+ # finds the name of the location, caches it, and
87
+ # returns it
88
+ def fetch_new_acpl(id)
89
+ params = "format=json&input_format=json&service=open-ils.circ&method=open-ils.circ.copy_location.retrieve&param=#{id}"
90
+ @gateway.query = params
91
+ res = send_query
92
+ if res
93
+ data = JSON.parse(res.body)['payload'][0]
94
+ name = data['__p'][@idl_order[:acpl]['name']] unless data.key? 'stacktrace'
95
+ @acpl_cache[id] = name
96
+ return name if name
97
+ end
98
+ false
99
+ end
106
100
 
107
- def fetch_idl_order
108
- @idl_order = {}
101
+ def send_query
102
+ begin
103
+ res = Net::HTTP.get_response(@gateway)
104
+ rescue Errno::ECONNREFUSED, Net::ReadTimeout
105
+ return nil
106
+ end
107
+ return res if res.is_a?(Net::HTTPSuccess)
109
108
 
110
- idl = Nokogiri::XML(open(@evergreen_domain + '/reports/fm_IDL.xml'))
109
+ nil
110
+ end
111
111
 
112
- [:acn, :acp, :acpl, :aou, :ccs, :circ].each do |idl_class|
113
- i = 0
114
- @idl_order[idl_class] = {}
115
- fields = idl.xpath("//idl:class[@id='#{idl_class}']/idl:fields/idl:field", 'idl' => 'http://opensrf.org/spec/IDL/base/v1')
116
- fields.each do |field|
117
- @idl_order[idl_class][field['name']] = i
118
- i = i + 1
119
- end
120
- end
121
- end
112
+ def fetch_idl_order
113
+ @idl_order = {}
122
114
 
123
- def fetch_statuses
124
- @possible_item_statuses = []
125
- params = 'format=json&input_format=json&service=open-ils.search&method=open-ils.search.config.copy_status.retrieve.all'
126
- @gateway.query = params
127
- res = send_query
128
- if res
129
- stats = JSON.parse(res.body)['payload'][0]
130
- stats.each do |stat|
131
- @possible_item_statuses[stat['__p'][@idl_order[:ccs]['id']]] = stat['__p'][@idl_order[:ccs]['name']]
132
- end
133
- return true if stats.size > 0
134
- end
135
- return false
136
- end
115
+ idl = Nokogiri::XML(open(@evergreen_domain + '/reports/fm_IDL.xml'))
137
116
 
138
- def fetch_ou_tree
139
- @org_units = {}
140
- params = 'format=json&input_format=json&service=open-ils.actor&method=open-ils.actor.org_tree.retrieve'
141
- @gateway.query = params
142
- res = send_query
143
- if res
144
- raw_orgs = JSON.parse(res.body)['payload'][0]['__p']
145
- take_info_from_ou_tree raw_orgs
146
- return true if @org_units.size > 0
147
- end
148
- return false
117
+ %i[acn acp acpl aou ccs circ].each do |idl_class|
118
+ i = 0
119
+ @idl_order[idl_class] = {}
120
+ fields = idl.xpath("//idl:class[@id='#{idl_class}']/idl:fields/idl:field", 'idl' => 'http://opensrf.org/spec/IDL/base/v1')
121
+ fields.each do |field|
122
+ @idl_order[idl_class][field['name']] = i
123
+ i += 1
149
124
  end
150
-
125
+ end
151
126
  end
152
127
 
153
- # Status objects represent all the holdings attached to a specific tcn
154
- class Status
155
- attr_reader :copies, :libraries
156
- def initialize json_data, idl_order, connection = nil
157
- @idl_order = idl_order
158
- @connection = connection
159
- @raw_data = JSON.parse(json_data)['payload'][0]
160
- extract_copies
161
- substitute_values_for_ids unless @connection.nil?
162
- @available_copies = []
163
- @next_copy_available = 'a date'
128
+ def fetch_statuses
129
+ @possible_item_statuses = []
130
+ params = 'format=json&input_format=json&service=open-ils.search&method=open-ils.search.config.copy_status.retrieve.all'
131
+ @gateway.query = params
132
+ res = send_query
133
+ if res
134
+ stats = JSON.parse(res.body)['payload'][0]
135
+ stats.each do |stat|
136
+ @possible_item_statuses[stat['__p'][@idl_order[:ccs]['id']]] = stat['__p'][@idl_order[:ccs]['name']]
164
137
  end
138
+ return true unless stats.empty?
139
+ end
140
+ false
141
+ end
165
142
 
166
- # Determines if any copies are available for your patrons
167
- def any_copies_available?
168
- @copies.each do |copy|
169
- return true if 0 == copy.status
170
- return true if 'Available' == copy.status
171
- end
172
- return false
173
- end
143
+ def fetch_ou_tree
144
+ @org_units = {}
145
+ params = 'format=json&input_format=json&service=open-ils.actor&method=open-ils.actor.org_tree.retrieve'
146
+ @gateway.query = params
147
+ res = send_query
148
+ if res
149
+ raw_orgs = JSON.parse(res.body)['payload'][0]['__p']
150
+ take_info_from_ou_tree raw_orgs
151
+ return true unless @org_units.empty?
152
+ end
153
+ false
154
+ end
155
+ end
156
+
157
+ # Status objects represent all the holdings attached to a specific tcn
158
+ class Status
159
+ attr_reader :copies, :libraries
160
+ def initialize(json_data, idl_order, connection = nil)
161
+ @idl_order = idl_order
162
+ @connection = connection
163
+ @raw_data = JSON.parse(json_data)['payload'][0]
164
+ extract_copies
165
+ substitute_values_for_ids unless @connection.nil?
166
+ @available_copies = []
167
+ @next_copy_available = 'a date'
168
+ end
174
169
 
175
- private
176
-
177
- # Look through @raw_data and find the copies
178
- def extract_copies
179
- @copies = Array.new
180
- @raw_data.each do |vol|
181
- if vol['__p'][0].size > 0
182
- vol['__p'][0].each do |item|
183
- i = 0
184
- item_info = {
185
- barcode: item['__p'][@idl_order[:acp]['barcode']],
186
- call_number: vol['__p'][@idl_order[:acn]['label']],
187
- location: item['__p'][@idl_order[:acp]['location']],
188
- status: item['__p'][@idl_order[:acp]['status']],
189
- owning_lib: item['__p'][@idl_order[:acp]['circ_lib']],
190
- }
191
- unless item['__p'][@idl_order[:acp]['circulations']].is_a? Array
192
- @copies.push Item.new item_info
193
- else
194
- begin
195
- item_info[:due_date] = item['__p'][@idl_order[:acp]['circulations']][0]['__p'][@idl_order[:circ]['due_date']]
196
- rescue
197
- end
198
- @copies.push Item.new item_info
199
- end
200
- end
201
- end
202
- end
203
- end
170
+ # Determines if any copies are available for your patrons
171
+ def any_copies_available?
172
+ @copies.each do |copy|
173
+ return true if copy.status.zero?
174
+ return true if copy.status == 'Available'
175
+ end
176
+ false
177
+ end
204
178
 
205
- def substitute_values_for_ids
206
- @libraries = @connection.org_units.clone
207
- @libraries.each { |key, lib| lib[:copies] = Array.new }
208
- @copies.each do |copy|
209
- if copy.location.is_a? Numeric
210
- copy.location = @connection.location_name copy.location
211
- end
212
- if copy.status.is_a? Numeric
213
- copy.status = @connection.status_name copy.status
214
- end
215
- if copy.owning_lib.is_a? Numeric
216
- ou_id = copy.owning_lib
217
- copy.owning_lib = @connection.ou_name copy.owning_lib
218
- @libraries[ou_id][:copies].push copy
219
- end
179
+ private
180
+
181
+ # Look through @raw_data and find the copies
182
+ def extract_copies
183
+ @copies = []
184
+ @raw_data.each do |vol|
185
+ next if vol['__p'][0].empty?
186
+
187
+ vol['__p'][0].each do |item|
188
+ item_info = {
189
+ barcode: item['__p'][@idl_order[:acp]['barcode']],
190
+ call_number: vol['__p'][@idl_order[:acn]['label']],
191
+ location: item['__p'][@idl_order[:acp]['location']],
192
+ status: item['__p'][@idl_order[:acp]['status']],
193
+ owning_lib: item['__p'][@idl_order[:acp]['circ_lib']]
194
+ }
195
+ if item['__p'][@idl_order[:acp]['circulations']].is_a? Array
196
+ begin
197
+ item_info[:due_date] = item['__p'][@idl_order[:acp]['circulations']][0]['__p'][@idl_order[:circ]['due_date']]
198
+ rescue StandardError
199
+ end
200
+ @copies.push Item.new item_info
201
+ else
202
+ @copies.push Item.new item_info
220
203
  end
221
204
  end
222
-
205
+ end
223
206
  end
224
207
 
225
- # A physical copy of an item
226
- class Item
227
- attr_accessor :location, :status, :owning_lib
228
- attr_reader :barcode, :call_number, :due_date
229
- def initialize data = {}
230
- data.each do |k,v|
231
- instance_variable_set("@#{k}", v) unless v.nil?
232
- end
233
- end
208
+ def substitute_values_for_ids
209
+ @libraries = @connection.org_units.clone
210
+ @libraries.each { |_key, lib| lib[:copies] = [] }
211
+ @copies.each do |copy|
212
+ copy.location = @connection.location_name copy.location if copy.location.is_a? Numeric
213
+ copy.status = @connection.status_name copy.status if copy.status.is_a? Numeric
214
+ next unless copy.owning_lib.is_a? Numeric
215
+
216
+ ou_id = copy.owning_lib
217
+ copy.owning_lib = @connection.ou_name copy.owning_lib
218
+ @libraries[ou_id][:copies].push copy
219
+ end
220
+ end
221
+ end
222
+
223
+ # A physical copy of an item
224
+ class Item
225
+ attr_accessor :location, :status, :owning_lib
226
+ attr_reader :barcode, :call_number, :due_date
227
+ def initialize(data = {})
228
+ data.each do |k, v|
229
+ instance_variable_set("@#{k}", v) unless v.nil?
230
+ end
234
231
  end
232
+ end
235
233
  end
metadata CHANGED
@@ -1,22 +1,22 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evergreen_holdings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jane Sandberg
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2019-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: nokogiri
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
18
  version: '1.11'
19
+ name: nokogiri
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -25,33 +25,53 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.11'
27
27
  - !ruby/object:Gem::Dependency
28
- name: minitest
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: 5.0.0
32
+ version: 0.7.0
33
+ name: coveralls
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 5.0.0
40
+ version: 0.7.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: coveralls
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: 0.7.0
46
+ version: 5.0.0
47
+ name: minitest
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.7.0
54
+ version: 5.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">"
59
+ - !ruby/object:Gem::Version
60
+ version: 1.0.0
61
+ - - "<"
62
+ - !ruby/object:Gem::Version
63
+ version: '2'
64
+ name: rubocop
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">"
70
+ - !ruby/object:Gem::Version
71
+ version: 1.0.0
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '2'
55
75
  description: Access holdings information from Evergreen ILS
56
76
  email: sandbej@linnbenton.edu
57
77
  executables: []
@@ -65,7 +85,7 @@ homepage: https://github.com/sandbergja/evergreen_holdings_gem
65
85
  licenses:
66
86
  - MIT
67
87
  metadata: {}
68
- post_install_message:
88
+ post_install_message:
69
89
  rdoc_options: []
70
90
  require_paths:
71
91
  - lib
@@ -80,9 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
100
  - !ruby/object:Gem::Version
81
101
  version: '0'
82
102
  requirements: []
83
- rubyforge_project:
84
- rubygems_version: 2.6.13
85
- signing_key:
103
+ rubygems_version: 3.0.6
104
+ signing_key:
86
105
  specification_version: 4
87
106
  summary: A ruby gem for getting information about copy availability from Evergreen
88
107
  ILS