alma 0.2.4 → 0.3.2
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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +54 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.github/dependabot.yml +7 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +134 -0
- data/.ruby-version +1 -1
- data/Gemfile +5 -1
- data/Guardfile +75 -0
- data/README.md +146 -57
- data/Rakefile +3 -1
- data/alma.gemspec +21 -12
- data/lib/alma.rb +34 -54
- data/lib/alma/alma_record.rb +3 -3
- data/lib/alma/api_defaults.rb +39 -0
- data/lib/alma/availability_response.rb +69 -31
- data/lib/alma/bib.rb +54 -29
- data/lib/alma/bib_holding.rb +25 -0
- data/lib/alma/bib_item.rb +164 -0
- data/lib/alma/bib_item_set.rb +93 -0
- data/lib/alma/bib_set.rb +5 -10
- data/lib/alma/config.rb +10 -4
- data/lib/alma/course.rb +47 -0
- data/lib/alma/course_set.rb +17 -0
- data/lib/alma/electronic.rb +167 -0
- data/lib/alma/electronic/README.md +20 -0
- data/lib/alma/electronic/batch_utils.rb +224 -0
- data/lib/alma/electronic/business.rb +29 -0
- data/lib/alma/error.rb +16 -4
- data/lib/alma/fine.rb +16 -0
- data/lib/alma/fine_set.rb +41 -8
- data/lib/alma/item_request_options.rb +23 -0
- data/lib/alma/library.rb +29 -0
- data/lib/alma/library_set.rb +21 -0
- data/lib/alma/loan.rb +31 -2
- data/lib/alma/loan_set.rb +62 -4
- data/lib/alma/location.rb +29 -0
- data/lib/alma/location_set.rb +21 -0
- data/lib/alma/renewal_response.rb +25 -14
- data/lib/alma/request.rb +167 -0
- data/lib/alma/request_options.rb +66 -0
- data/lib/alma/request_set.rb +69 -5
- data/lib/alma/response.rb +45 -0
- data/lib/alma/result_set.rb +27 -35
- data/lib/alma/user.rb +142 -86
- data/lib/alma/user_request.rb +19 -0
- data/lib/alma/user_set.rb +5 -6
- data/lib/alma/version.rb +3 -1
- data/log/.gitignore +4 -0
- metadata +149 -10
- data/.travis.yml +0 -5
- data/lib/alma/api.rb +0 -33
data/lib/alma/loan.rb
CHANGED
@@ -1,9 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alma
|
2
4
|
class Loan < AlmaRecord
|
5
|
+
extend Alma::ApiDefaults
|
6
|
+
|
7
|
+
|
8
|
+
def renewable?
|
9
|
+
!!renewable
|
10
|
+
end
|
11
|
+
|
12
|
+
def renewable
|
13
|
+
response.fetch("renewable", false)
|
14
|
+
end
|
15
|
+
|
16
|
+
def overdue?
|
17
|
+
loan_status == "Overdue"
|
18
|
+
end
|
3
19
|
|
4
20
|
def renew
|
5
|
-
Alma::User.renew_loan({user_id: user_id, loan_id: loan_id})
|
21
|
+
Alma::User.renew_loan({ user_id: user_id, loan_id: loan_id })
|
6
22
|
end
|
7
23
|
|
24
|
+
def self.where_user(user_id, args = {})
|
25
|
+
# Always expand renewable unless you really don't want to
|
26
|
+
args[:expand] ||= "renewable"
|
27
|
+
# Default to upper limit
|
28
|
+
args[:limit] ||= 100
|
29
|
+
response = HTTParty.get(
|
30
|
+
"#{users_base_path}/#{user_id}/loans",
|
31
|
+
query: args,
|
32
|
+
headers: headers,
|
33
|
+
timeout: timeout
|
34
|
+
)
|
35
|
+
Alma::LoanSet.new(response, args)
|
36
|
+
end
|
8
37
|
end
|
9
|
-
end
|
38
|
+
end
|
data/lib/alma/loan_set.rb
CHANGED
@@ -1,18 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alma
|
2
4
|
class LoanSet < ResultSet
|
5
|
+
class ResponseError < Alma::StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
alias :total_records :total_record_count
|
9
|
+
|
10
|
+
|
11
|
+
attr_reader :results, :raw_response
|
12
|
+
def_delegators :results, :empty?
|
13
|
+
|
14
|
+
def initialize(raw_response, search_args = {})
|
15
|
+
@raw_response = raw_response
|
16
|
+
@response = raw_response.parsed_response
|
17
|
+
@search_args = search_args
|
18
|
+
validate(raw_response)
|
19
|
+
@results = @response.fetch(key, [])
|
20
|
+
.map { |item| single_record_class.new(item) }
|
21
|
+
# args passed to the search that returned this set
|
22
|
+
# such as limit, expand, order_by, etc
|
23
|
+
end
|
3
24
|
|
25
|
+
def loggable
|
26
|
+
{ search_args: @search_args,
|
27
|
+
uri: @raw_response&.request&.uri.to_s
|
28
|
+
}.select { |k, v| !(v.nil? || v.empty?) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(response)
|
32
|
+
if response.code != 200
|
33
|
+
error = "Could not find loans info."
|
34
|
+
log = loggable.merge(response.parsed_response)
|
35
|
+
raise ResponseError.new(error, log)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def all
|
40
|
+
Enumerator.new do |yielder|
|
41
|
+
offset = 0
|
42
|
+
loop do
|
43
|
+
extra_args = @search_args.merge({ limit: 100, offset: offset })
|
44
|
+
r = (offset == 0) ? self : single_record_class.where_user(user_id, extra_args)
|
45
|
+
unless r.empty?
|
46
|
+
r.map { |item| yielder << item }
|
47
|
+
offset += 100
|
48
|
+
else
|
49
|
+
raise StopIteration
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def each(&block)
|
56
|
+
@results.each(&block)
|
57
|
+
end
|
4
58
|
|
5
|
-
def
|
6
|
-
|
59
|
+
def success?
|
60
|
+
raw_response.response.code.to_s == "200"
|
7
61
|
end
|
8
62
|
|
9
|
-
def
|
10
|
-
|
63
|
+
def key
|
64
|
+
"item_loan"
|
11
65
|
end
|
12
66
|
|
13
67
|
def single_record_class
|
14
68
|
Alma::Loan
|
15
69
|
end
|
16
70
|
|
71
|
+
private
|
72
|
+
def user_id
|
73
|
+
@user_id ||= results.first.user_id
|
74
|
+
end
|
17
75
|
end
|
18
76
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alma
|
4
|
+
class Location < AlmaRecord
|
5
|
+
extend Alma::ApiDefaults
|
6
|
+
|
7
|
+
def self.all(library_code:, args: {})
|
8
|
+
response = HTTParty.get("#{configuration_base_path}/libraries/#{library_code}/locations", query: args, headers: headers, timeout: timeout)
|
9
|
+
if response.code == 200
|
10
|
+
LocationSet.new(response)
|
11
|
+
else
|
12
|
+
raise StandardError, get_body_from(response)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find(library_code:, location_code:, args: {})
|
17
|
+
response = HTTParty.get("#{configuration_base_path}/libraries/#{library_code}/locations/#{location_code}", query: args, headers: headers, timeout: timeout)
|
18
|
+
if response.code == 200
|
19
|
+
AlmaRecord.new(response)
|
20
|
+
else
|
21
|
+
raise StandardError, get_body_from(response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.get_body_from(response)
|
26
|
+
JSON.parse(response.body)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alma
|
4
|
+
class LocationSet < ResultSet
|
5
|
+
def_delegators :results, :[], :empty?
|
6
|
+
|
7
|
+
def each(&block)
|
8
|
+
results.each(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def results
|
12
|
+
@results ||= @response.fetch(key, [])
|
13
|
+
.map { |item| single_record_class.new(item) }
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
def key
|
18
|
+
"location"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,42 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Alma
|
2
4
|
class RenewalResponse
|
3
|
-
|
4
|
-
include Alma::Error
|
5
|
-
|
6
5
|
def initialize(response)
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
6
|
+
@raw_response = response
|
7
|
+
@response = response.parsed_response
|
8
|
+
@success = response.has_key?("loan_id")
|
9
|
+
end
|
10
|
+
|
11
|
+
def loggable
|
12
|
+
{ uri: @raw_response&.request&.uri.to_s
|
13
|
+
}.select { |k, v| !(v.nil? || v.empty?) }
|
10
14
|
end
|
11
15
|
|
12
16
|
def renewed?
|
13
|
-
@
|
17
|
+
@success
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_error?
|
21
|
+
!renewed?
|
14
22
|
end
|
15
23
|
|
16
24
|
def due_date
|
17
|
-
@
|
25
|
+
@response.fetch("due_date", "")
|
18
26
|
end
|
19
27
|
|
20
28
|
|
21
29
|
def due_date_pretty
|
22
|
-
Time.parse(due_date).strftime(
|
30
|
+
Time.parse(due_date).strftime("%m-%e-%y %H:%M")
|
23
31
|
end
|
24
32
|
|
25
33
|
def item_title
|
26
|
-
if
|
27
|
-
@
|
34
|
+
if renewed?
|
35
|
+
@response["title"]
|
28
36
|
else
|
29
|
-
|
37
|
+
"This Item"
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
41
|
def message
|
34
|
-
if
|
42
|
+
if renewed?
|
35
43
|
"#{item_title} is now due #{due_date}"
|
36
44
|
else
|
37
45
|
"#{item_title} could not be renewed."
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
49
|
+
def error_message
|
50
|
+
@response unless renewed?
|
51
|
+
end
|
41
52
|
end
|
42
|
-
end
|
53
|
+
end
|
data/lib/alma/request.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alma
|
4
|
+
class BibRequest
|
5
|
+
class ItemAlreadyExists < Alma::StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
extend Alma::ApiDefaults
|
9
|
+
|
10
|
+
REQUEST_TYPES = %w[HOLD DIGITIZATION BOOKING]
|
11
|
+
|
12
|
+
def self.submit(args)
|
13
|
+
request = new(args)
|
14
|
+
response = HTTParty.post(
|
15
|
+
"#{bibs_base_path}/#{request.mms_id}/requests",
|
16
|
+
query: { user_id: request.user_id },
|
17
|
+
headers: headers,
|
18
|
+
body: request.body.to_json
|
19
|
+
)
|
20
|
+
Alma::Response.new(response)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :mms_id, :user_id, :body, :request_type
|
24
|
+
def initialize(args)
|
25
|
+
@mms_id = args.delete(:mms_id) { raise ArgumentError.new(":mms_id option must be specified to create request") }
|
26
|
+
@user_id = args.delete(:user_id) { raise ArgumentError.new(":user_id option must be specified to create request") }
|
27
|
+
@request_type = args.fetch(:request_type, "NOT_SPECIFIED")
|
28
|
+
validate!(args)
|
29
|
+
normalize!(args)
|
30
|
+
@body = args
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def normalize!(args)
|
35
|
+
request_type_normalization!(args)
|
36
|
+
additional_normalization!(args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def request_type_normalization!(args)
|
40
|
+
method = "#{@request_type.downcase}_normalization".to_sym
|
41
|
+
send(method, args) if respond_to? method
|
42
|
+
end
|
43
|
+
|
44
|
+
# Intended to be overridden by subclasses, allowing extra normalization logic to be provided
|
45
|
+
def additional_normalization!(args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate!(args)
|
49
|
+
unless REQUEST_TYPES.include?(request_type)
|
50
|
+
raise ArgumentError.new(":request_type option must be specified and one of #{REQUEST_TYPES.join(", ")} to submit a request")
|
51
|
+
end
|
52
|
+
request_type_validation!(args)
|
53
|
+
additional_validation!(args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def request_type_validation!(args)
|
57
|
+
method = "#{@request_type.downcase}_validation".to_sym
|
58
|
+
send(method, args) if respond_to? method
|
59
|
+
end
|
60
|
+
|
61
|
+
# Intended to be overridden by subclasses, allowing extra validation logic to be provided
|
62
|
+
def additional_validation!(args)
|
63
|
+
end
|
64
|
+
|
65
|
+
def digitization_normalization(args)
|
66
|
+
if args[:target_destination].is_a? String
|
67
|
+
args[:target_destination] = { value: args[:target_destination] }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def digitization_validation(args)
|
72
|
+
args.fetch(:target_destination) do
|
73
|
+
raise ArgumentError.new(
|
74
|
+
":target_destination option must be specified when request_type is DIGITIZATION"
|
75
|
+
)
|
76
|
+
end
|
77
|
+
pd = args.fetch(:partial_digitization) do
|
78
|
+
raise ArgumentError.new(
|
79
|
+
":partial_digitization option must be specified when request_type is DIGITIZATION"
|
80
|
+
)
|
81
|
+
end
|
82
|
+
if pd == true
|
83
|
+
args.fetch(:comment) do
|
84
|
+
raise ArgumentError.new(
|
85
|
+
":comment option must be specified when :request_type is DIGITIZATION and :partial_digitization is true"
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def booking_normalization(args)
|
92
|
+
if args[:material_type].is_a? String
|
93
|
+
args[:material_type] = { value: args[:material_type] }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def booking_validation(args)
|
98
|
+
args.fetch(:booking_start_date) do
|
99
|
+
raise ArgumentError.new(
|
100
|
+
":booking_start_date option must be specified when request_type is BOOKING"
|
101
|
+
)
|
102
|
+
end
|
103
|
+
args.fetch(:booking_end_date) do
|
104
|
+
raise ArgumentError.new(
|
105
|
+
":booking_end_date option must be specified when request_type is BOOKING"
|
106
|
+
)
|
107
|
+
end
|
108
|
+
args.fetch(:pickup_location_type) do
|
109
|
+
raise ArgumentError.new(
|
110
|
+
":pickup_location_type option must be specified when request_type is BOOKING"
|
111
|
+
)
|
112
|
+
end
|
113
|
+
args.fetch(:pickup_location_library) do
|
114
|
+
raise ArgumentError.new(
|
115
|
+
":pickup_location_library option must be specified when request_type is BOOKING"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def hold_normalization(args)
|
121
|
+
# if args[:material_type].is_a? String
|
122
|
+
# args[:material_type] = { value: args[:material_type] }
|
123
|
+
# end
|
124
|
+
end
|
125
|
+
|
126
|
+
def hold_validation(args)
|
127
|
+
args.fetch(:pickup_location_type) do
|
128
|
+
raise ArgumentError.new(
|
129
|
+
":pickup_location_type option must be specified when request_type is HOLD"
|
130
|
+
)
|
131
|
+
end
|
132
|
+
args.fetch(:pickup_location_library) do
|
133
|
+
raise ArgumentError.new(
|
134
|
+
":pickup_location_library option must be specified when request_type is HOLD"
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class ItemRequest < BibRequest
|
141
|
+
def self.submit(args)
|
142
|
+
request = new(args)
|
143
|
+
response = HTTParty.post(
|
144
|
+
"#{bibs_base_path}/#{request.mms_id}/holdings/#{request.holding_id}/items/#{request.item_pid}/requests",
|
145
|
+
query: { user_id: request.user_id },
|
146
|
+
headers: headers,
|
147
|
+
body: request.body.to_json
|
148
|
+
)
|
149
|
+
Alma::Response.new(response)
|
150
|
+
end
|
151
|
+
|
152
|
+
attr_reader :holding_id, :item_pid
|
153
|
+
def initialize(args)
|
154
|
+
super(args)
|
155
|
+
@holding_id = args.delete(:holding_id) { raise ArgumentError.new(":holding_id option must be specified to create request") }
|
156
|
+
@item_pid = args.delete(:item_pid) { raise ArgumentError.new(":item_pid option must be specified to create request") }
|
157
|
+
end
|
158
|
+
|
159
|
+
def additional_validation!(args)
|
160
|
+
args.fetch(:description) do
|
161
|
+
raise ArgumentError.new(
|
162
|
+
":description option must be specified when request_type is DIGITIZATION"
|
163
|
+
)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alma
|
4
|
+
class RequestOptions
|
5
|
+
class ResponseError < Alma::StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
extend Alma::ApiDefaults
|
10
|
+
|
11
|
+
attr_accessor :request_options, :raw_response
|
12
|
+
def_delegators :raw_response, :response, :request
|
13
|
+
|
14
|
+
REQUEST_OPTIONS_PERMITTED_ARGS = [:user_id]
|
15
|
+
|
16
|
+
def initialize(response)
|
17
|
+
@raw_response = response
|
18
|
+
validate(response)
|
19
|
+
@request_options = response.parsed_response["request_option"]
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def self.get(mms_id, options = {})
|
24
|
+
url = "#{bibs_base_path}/#{mms_id}/request-options"
|
25
|
+
options.select! { |k, _| REQUEST_OPTIONS_PERMITTED_ARGS.include? k }
|
26
|
+
response = HTTParty.get(url, headers: headers, query: options, timeout: timeout)
|
27
|
+
new(response)
|
28
|
+
end
|
29
|
+
|
30
|
+
def loggable
|
31
|
+
{ uri: @raw_response&.request&.uri.to_s
|
32
|
+
}.select { |k, v| !(v.nil? || v.empty?) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate(response)
|
36
|
+
if response.code != 200
|
37
|
+
raise ResponseError.new("Could not get request options.", loggable.merge(response.parsed_response))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def hold_allowed?
|
42
|
+
!request_options.nil? &&
|
43
|
+
!request_options.select { |option| option["type"]["value"] == "HOLD" }.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def digitization_allowed?
|
47
|
+
!request_options.nil? &&
|
48
|
+
!request_options.select { |option| option["type"]["value"] == "DIGITIZATION" }.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def booking_allowed?
|
52
|
+
!request_options.nil? &&
|
53
|
+
!request_options.select { |option| option["type"]["value"] == "BOOKING" }.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
def resource_sharing_broker_allowed?
|
57
|
+
!request_options.nil? &&
|
58
|
+
!request_options.select { |option| option["type"]["value"] == "RS_BROKER" }.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def ez_borrow_link
|
62
|
+
broker = request_options.select { |option| option["type"]["value"] == "RS_BROKER" }
|
63
|
+
broker.collect { |opt| opt["request_url"] }.first
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|