lusi_api 0.1.11
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 +7 -0
- data/.gitignore +58 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +235 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/lusi_api.rb +5 -0
- data/lib/lusi_api/calendar.rb +234 -0
- data/lib/lusi_api/core/api.rb +190 -0
- data/lib/lusi_api/core/code.rb +101 -0
- data/lib/lusi_api/core/exceptions.rb +47 -0
- data/lib/lusi_api/core/lookup.rb +612 -0
- data/lib/lusi_api/core/util.rb +102 -0
- data/lib/lusi_api/core/xml.rb +168 -0
- data/lib/lusi_api/country.rb +111 -0
- data/lib/lusi_api/course.rb +1300 -0
- data/lib/lusi_api/enrolment.rb +247 -0
- data/lib/lusi_api/organisation.rb +291 -0
- data/lib/lusi_api/person/staff.rb +115 -0
- data/lib/lusi_api/person/student.rb +551 -0
- data/lib/lusi_api/service_account.rb +121 -0
- data/lib/lusi_api/service_method.rb +52 -0
- data/lib/lusi_api/version.rb +5 -0
- data/lib/lusi_api/vle.rb +329 -0
- data/lusi_api.gemspec +36 -0
- metadata +182 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'lusi_api/core/api'
|
2
|
+
require 'lusi_api/core/code'
|
3
|
+
require 'lusi_api/core/xml'
|
4
|
+
|
5
|
+
|
6
|
+
module LUSI
|
7
|
+
module API
|
8
|
+
module Enrolment
|
9
|
+
|
10
|
+
# Represents an enrolment role in the LUSI API
|
11
|
+
class EnrolmentRole < LUSI::API::Core::Code
|
12
|
+
|
13
|
+
# @!attribute [rw] vle_role_description
|
14
|
+
# @return [String, nil] the VLE role description a member has against this enrolment
|
15
|
+
attr_accessor :vle_role_description
|
16
|
+
|
17
|
+
# Initialises a new EnrolmentRole instance
|
18
|
+
# @param (see LUSI::API::Core::Code#initialize)
|
19
|
+
# @param vle_role_description [String, nil] the default VLE role description
|
20
|
+
# @return [void]
|
21
|
+
def initialize(xml = nil, lookup = nil, vle_role_description: nil, **kwargs)
|
22
|
+
super(xml, lookup, **kwargs)
|
23
|
+
@vle_role_description = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:VLERoleDescription',
|
24
|
+
vle_role_description)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# The abstract base class for enrolment classes
|
31
|
+
# @abstract Subclasses must define the lusi_ws_* endpoint methods and additional attributes
|
32
|
+
class EnrolmentBase
|
33
|
+
|
34
|
+
extend LUSI::API::Core::Endpoint
|
35
|
+
|
36
|
+
# @!attribute [rw] enrolment_role
|
37
|
+
# @return [LUSI::API::Enrolment::EnrolmentRole, nil] the role that members have against this enrolment
|
38
|
+
attr_accessor :enrolment_role
|
39
|
+
|
40
|
+
# @!attribute [rw] identity
|
41
|
+
# @return [LUSI::API::Enrolment::EnrolmentIdentity] the identity of the subject (person) of the enrolment
|
42
|
+
attr_accessor :identity
|
43
|
+
|
44
|
+
# @!attribute [rw] is_current_enrolment
|
45
|
+
# @return [Boolean, nil] true if the enrolment is current, false otherwise
|
46
|
+
attr_accessor :is_current_enrolment
|
47
|
+
|
48
|
+
# @!attribute [rw] is_current_identity
|
49
|
+
# @return [Boolean, nil] true if the member's identity is current, false otherwise
|
50
|
+
attr_accessor :is_current_identity
|
51
|
+
|
52
|
+
# @!attribute [rw] username
|
53
|
+
# @return [String, nil] the username of the subject (person) of the enrolment
|
54
|
+
attr_accessor :username
|
55
|
+
|
56
|
+
# Initalises a new Enrolment instance
|
57
|
+
# @param xml [Nokogiri::XML::Document, Nokogiri::XML::Node] the parsed XML root of the enrolment
|
58
|
+
# @param lookup [LUSI::API::Core::Lookup::LookupService, nil] the lookup service for object resolution
|
59
|
+
# @param enrolment_role [LUSI::API::Enrolment::EnrolmentRole, nil] the default enrolment role
|
60
|
+
# @param identity [String, nil] the default user identity code
|
61
|
+
# @param is_current_enrolment [Boolean, nil] the default current enrolment flag
|
62
|
+
# @param is_current_identity [Boolean, nil] the default current identity flag
|
63
|
+
# @param username [String, nil] the default username
|
64
|
+
# @return [void]
|
65
|
+
def initialize(xml = nil, lookup = nil, enrolment_role: nil, identity: nil, is_current_enrolment: nil,
|
66
|
+
is_current_identity: nil, username: nil)
|
67
|
+
is_current_enrolment = is_current_enrolment.nil? || is_current_enrolment ? true : false
|
68
|
+
is_current_identity = is_current_identity.nil? || is_current_identity ? true : false
|
69
|
+
@enrolment_role = EnrolmentRole.new(LUSI::API::Core::XML.xml_at(xml, 'xmlns:EnrolmentRole', enrolment_role),
|
70
|
+
lookup)
|
71
|
+
@identity = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:Identity', identity)
|
72
|
+
@is_current_enrolment = LUSI::API::Core::XML.xml_boolean_at(xml, 'xmlns:IsCurrentEnrolment',
|
73
|
+
is_current_enrolment)
|
74
|
+
@is_current_identity = LUSI::API::Core::XML.xml_boolean_at(xml, 'xmlns:IsCurrentIdentity',
|
75
|
+
is_current_identity)
|
76
|
+
@username = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:Username', username)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an array of instances matching the specified search criteria
|
80
|
+
# @param api [LUSI::API::Core::API] the LUSI API instance to use for searching
|
81
|
+
# @param lookup [LUSI::API::Core::Lookup::LookupService, nil] the lookup service for object resolution
|
82
|
+
# @param (see #get_instance_params)
|
83
|
+
# @yield [obj] Passes the instance to the block
|
84
|
+
def self.get_instance(api, lookup = nil, current_only: nil, **kwargs)
|
85
|
+
current_only = current_only.nil? || current_only ? true : false
|
86
|
+
if current_only
|
87
|
+
# Filter nodes which have current enrolments and user identities
|
88
|
+
filter = Proc.new do |node|
|
89
|
+
LUSI::API::Core::XML.xml_boolean_at(node, 'xmlns:IsCurrentEnrolment', false) &&
|
90
|
+
LUSI::API::Core::XML.xml_boolean_at(node, 'xmlns:IsCurrentIdentity', false)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
filter = nil
|
94
|
+
end
|
95
|
+
params = self.get_instance_params(**kwargs)
|
96
|
+
xml = api.call(self.lusi_ws_path, self.lusi_ws_endpoint, self.lusi_ws_method, **params)
|
97
|
+
result = LUSI::API::Core::XML.xml(xml, "xmlns:#{self.lusi_ws_xml_root}", filter: filter) do |m|
|
98
|
+
obj = self.new(m, lookup)
|
99
|
+
yield(obj) if block_given?
|
100
|
+
obj
|
101
|
+
end
|
102
|
+
# Return the array of instances
|
103
|
+
result
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the lookup indices supported by this enrolment type
|
107
|
+
# @return [Array<Symbol>] the supported lookup indices
|
108
|
+
def lookup_indices
|
109
|
+
[:identity, :role, :username]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns the value to be used as a key for the specified lookup index
|
113
|
+
# @return [Object] the key value
|
114
|
+
def lookup_key(index = nil)
|
115
|
+
case index
|
116
|
+
when :identity
|
117
|
+
self.identity
|
118
|
+
when :role
|
119
|
+
self.enrolment_role ? self.enrolment_role.identity : nil
|
120
|
+
when :username
|
121
|
+
self.username
|
122
|
+
else
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# @see (LUSI::API::Core::Endpoint#lusi_ws_path)
|
128
|
+
def self.lusi_ws_path
|
129
|
+
'UserDetails'
|
130
|
+
end
|
131
|
+
|
132
|
+
protected
|
133
|
+
|
134
|
+
# Returns a hash of parameters for the LUSI API call. Subclasses may extend or override this method.
|
135
|
+
# @param active_vle_space_only [Boolean, nil] if true, restrict results to active VLE spaces only
|
136
|
+
# @param asp_identity [String, nil] return instances matching the academically significant period (ASP) identity
|
137
|
+
# @param cohort_identity [String, nil] return instances matching the cohort identity
|
138
|
+
# @param course_identity [String, nil] return instances matching the course identity
|
139
|
+
# @param current_student_only [Boolean, nil] if true, restrict results to current students only
|
140
|
+
# @param department_identity [String, nil] return instances matching the department identity
|
141
|
+
# @param require_username [Boolean, nil] if true, include the student username, ignore students with no username
|
142
|
+
# @param year_identity (String, nil) return instances matching the year identity
|
143
|
+
# @return [Hash<String, String>] the parameters for the LUSI API call
|
144
|
+
def self.get_instance_params(**kwargs)
|
145
|
+
result = {
|
146
|
+
ActiveVLESpaceOnly: kwargs.fetch(:active_vle_space_only, true) ? 'true' : 'false',
|
147
|
+
ASPIdentity: kwargs.fetch(:asp_identity, ''),
|
148
|
+
CohortIdentity: kwargs.fetch(:cohort_identity, ''),
|
149
|
+
CourseIdentity: kwargs.fetch(:course_identity, ''),
|
150
|
+
DepartmentIdentity: kwargs.fetch(:department_identity, ''),
|
151
|
+
RequireUsername: kwargs.fetch(:require_username, true) ? 'true' : 'false',
|
152
|
+
YearIdentity: kwargs.fetch(:year_identity, '')
|
153
|
+
}
|
154
|
+
if self.lusi_ws_endpoint == 'Student.asmx'
|
155
|
+
result[:CurrentStudentOnly] = kwargs.fetch(:current_student_only, true) ? 'true' : 'false'
|
156
|
+
end
|
157
|
+
result
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
class EnrolmentLookup
|
164
|
+
|
165
|
+
# Initialises a new EnrolmentLookup instance
|
166
|
+
def initialize(enrolments = nil, *indices, &block)
|
167
|
+
@default_proc = block
|
168
|
+
@indices = {}
|
169
|
+
enrolments.each { |e| add(e, *indices) }
|
170
|
+
end
|
171
|
+
|
172
|
+
# @see (LUSI::API::Enrolment::EnrolmentLookup#fetch)
|
173
|
+
def [](key, *indices)
|
174
|
+
fetch(key, *indices)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Adds an enrolment to specified indices (default is all indices if unspecified)
|
178
|
+
# @param enrolment [LUSI::API::Enrolment::EnrolmentBase] the enrolment to add
|
179
|
+
# Reminaing positional parameters specify the indices to update
|
180
|
+
# @return [void]
|
181
|
+
def add(enrolment = nil, *indices)
|
182
|
+
indices = enrolment.lookup_indices if indices.nil? || indices.empty?
|
183
|
+
indices.each { |index| add_enrolment(enrolment, index) }
|
184
|
+
nil
|
185
|
+
end
|
186
|
+
|
187
|
+
# Searches the specified indices for the lookup key and returns the first match
|
188
|
+
# @param key [Object] the lookup key. If the key defines method #enrolment_lookup_keys, this method determines
|
189
|
+
# which keys are searched for.
|
190
|
+
# Remaining positional parameters specify the indices to search. If no indices are specified, but the key
|
191
|
+
# instance defines method #enrolment_lookup_indices, this method determines which indices are searched.
|
192
|
+
# @return [Array<LUSI::API::Enrolment::EnrolmentBase>, nil] the enrolments corresponding to key, or nil
|
193
|
+
# if match was found
|
194
|
+
def fetch(key = nil, *indices)
|
195
|
+
|
196
|
+
# If no index is specified, infer it from the key type if possible
|
197
|
+
if indices.nil? || indices.empty?
|
198
|
+
indices = key.respond_to?(:enrolment_lookup_indices) ? key.enrolment_lookup_indices : nil
|
199
|
+
end
|
200
|
+
return nil if indices.nil? || indices.empty?
|
201
|
+
|
202
|
+
# Use the lookup keys specified by the key instance if possible, otherwise use the literal key value
|
203
|
+
keys = key.respond_to?(:enrolment_lookup_keys) ? key.enrolment_lookup_keys : [key]
|
204
|
+
|
205
|
+
# Search the specified indices until a match is found, then return matches for all key values from this index
|
206
|
+
unless keys.nil? || keys.empty?
|
207
|
+
result = []
|
208
|
+
indices.each do |index|
|
209
|
+
i = @indices[index]
|
210
|
+
if i
|
211
|
+
keys.each { |key| result += i[key] || [] }
|
212
|
+
end
|
213
|
+
return result unless result.empty?
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# If we get here, the search failed in all indices - call the default_proc if available, otherwise return nil
|
218
|
+
@default_proc ? @default_proc.call(key) : nil
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
protected
|
223
|
+
|
224
|
+
# Adds an enrolment to the specified index
|
225
|
+
# @param enrolment [LUSI::API::Enrolment::EnrolmentBase] the enrolment
|
226
|
+
# @param index [Symbol] the index to be updated
|
227
|
+
def add_enrolment(enrolment, index)
|
228
|
+
# Get the specified index hash
|
229
|
+
@indices[index] = {} unless @indices.include?(index)
|
230
|
+
hash = @indices[index]
|
231
|
+
# Add the enrolment to the list
|
232
|
+
key = enrolment.lookup_key(index)
|
233
|
+
if hash.include?(key)
|
234
|
+
hash[key].push(enrolment)
|
235
|
+
else
|
236
|
+
hash[key] = [enrolment]
|
237
|
+
end
|
238
|
+
# Return the list containing the enrolment
|
239
|
+
hash[key]
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'lusi_api/core/xml'
|
2
|
+
|
3
|
+
|
4
|
+
module LUSI
|
5
|
+
module API
|
6
|
+
|
7
|
+
# Classes representation organisation structure
|
8
|
+
module Organisation
|
9
|
+
|
10
|
+
# Represents an organisation as a hierarchy of Units.
|
11
|
+
#
|
12
|
+
# The position in the hierarchy is defined by the Unit.type attribute.
|
13
|
+
#
|
14
|
+
# For each hierarchy level (Unit type) the Organisation maintains three indices, identified as follows:
|
15
|
+
# :identity allows retrieval by the Unit.identity attribute
|
16
|
+
# :mnemonic allows retrieval by the Unit.mnemonic attribute
|
17
|
+
# :title allows retrieval by the Unit.title attribute
|
18
|
+
#
|
19
|
+
# Once an instance is created, it should be populated by calling the #load method
|
20
|
+
# @example Populating an Organisation instance
|
21
|
+
# api = LUSI::API::Core::API.new(...)
|
22
|
+
# org = LUSI::API::Organisation::Organisation.new
|
23
|
+
# begin
|
24
|
+
# org.load(api, in_use_only: false)
|
25
|
+
# rescue LUSI::API::Core::APIError => e
|
26
|
+
# # API error handling
|
27
|
+
# end
|
28
|
+
class Organisation
|
29
|
+
|
30
|
+
# Defines the organisation hierarchy as a mapping from unit type to its child unit type and corresponding XPath.
|
31
|
+
# A leaf unit with no children maps to nil. The reserved unit type "_root" defines the root of the hierarchy.
|
32
|
+
HIERARCHY = {
|
33
|
+
_root: { type: :institution, path: '//xmlns:Institution' },
|
34
|
+
institution: { type: :faculty, path: 'xmlns:Faculties/xmlns:Faculty' },
|
35
|
+
faculty: { type: :department, path: 'xmlns:Departments/xmlns:Department' },
|
36
|
+
department: nil
|
37
|
+
}
|
38
|
+
|
39
|
+
# Initialises a new Organisation instance
|
40
|
+
# @param api [LUSI::API::Core::API, nil] an optional LUSI API instance used to retrieve data
|
41
|
+
# @param lookup [LUSI::API::Core::Lookup::LookupService, nil] the lookup service for object resolution
|
42
|
+
# @return [void]
|
43
|
+
def initialize(api = nil, lookup = nil)
|
44
|
+
@api = api
|
45
|
+
clear
|
46
|
+
end
|
47
|
+
|
48
|
+
# Searches the organisation top-down for the specified identity code and returns the first matching Unit
|
49
|
+
# @param identity [any] the identity code
|
50
|
+
# @return [Unit, nil] the matching Unit instance, or nil if no match is found
|
51
|
+
def [](identity = nil)
|
52
|
+
key = self.class.key(identity)
|
53
|
+
type = HIERARCHY[:_root][:type]
|
54
|
+
until type.nil? do
|
55
|
+
if @indices[type]
|
56
|
+
unit = @indices[type][:identity][key]
|
57
|
+
return unit if unit
|
58
|
+
end
|
59
|
+
type = HIERARCHY[type] ? HIERARCHY[type][:type] : nil
|
60
|
+
end
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Adds a new Unit to the Organisation
|
65
|
+
# Child units are recursively added
|
66
|
+
# @param unit [LUSI::API::Organisation::Unit] the Unit instance
|
67
|
+
# @return void
|
68
|
+
def add(unit)
|
69
|
+
|
70
|
+
# Require a Unit instance
|
71
|
+
return unless unit.is_a?(Unit)
|
72
|
+
|
73
|
+
# Initialise the indices for this Unit type if required
|
74
|
+
@indices[unit.type] = { identity: {}, mnemonic: {}, title: {} } unless @indices.include?(unit.type)
|
75
|
+
|
76
|
+
# Index the unit
|
77
|
+
hash = @indices[unit.type]
|
78
|
+
hash[:identity][self.class.key(unit.identity)] = unit
|
79
|
+
hash[:mnemonic][self.class.key(unit.mnemonic)] = unit
|
80
|
+
hash[:title][self.class.key(unit.title)] = unit
|
81
|
+
|
82
|
+
# Index the unit's child units
|
83
|
+
unit.children.each { |child| add(child) } if unit.children
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Clears the organisation structure
|
88
|
+
# @return [void]
|
89
|
+
def clear
|
90
|
+
@indices = {}
|
91
|
+
end
|
92
|
+
|
93
|
+
# Iterates over selected parts of the organisation hierarchy
|
94
|
+
# @param type [Symbol, nil] the Unit type to iterate over
|
95
|
+
# @param index [Symbol, nil] the Unit type's index to iterate over (:identity | :mnemonic | :title)
|
96
|
+
# @return [void]
|
97
|
+
# @yield Passes a Unit instance to the block
|
98
|
+
# @yieldparam unit [LUSI::API::Organisation::Unit] the current unit of the iterator
|
99
|
+
def each(type = nil, index = nil, &block)
|
100
|
+
if type
|
101
|
+
# Iterate over the selected type; ignore invalid types
|
102
|
+
hash = @indices[type]
|
103
|
+
if hash
|
104
|
+
hash_index = hash.include?(index) ? index : :identity
|
105
|
+
hash[hash_index].each_value(&block) if hash[hash_index]
|
106
|
+
end
|
107
|
+
else
|
108
|
+
# Iterate over all types
|
109
|
+
@indices.each_value do |type|
|
110
|
+
hash_index = type.include?(index) ? index : :identity
|
111
|
+
type[hash_index].each_value(&block) if type[hash_index]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Iterates over each unit type in the organisation hierarchy starting from the root
|
117
|
+
# @return [void]
|
118
|
+
# @yield [unit_type] Passes the unit type to the block
|
119
|
+
# @yieldparam unit_type [Symbol] the current unit type of the iterator
|
120
|
+
def each_unit_type
|
121
|
+
unit_type = HIERARCHY[:_root][:type]
|
122
|
+
while unit_type
|
123
|
+
yield(unit_type)
|
124
|
+
unit_type = HIERARCHY[unit_type] ? HIERARCHY[unit_type][:type] : nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Indicates whether the organisation is empty (contains units) or not
|
129
|
+
# @return [Boolean] true if the organisation contains no units, otherwise false
|
130
|
+
def empty?
|
131
|
+
# If @indices is not empty, use the #length method to determine emptiness
|
132
|
+
@indices.nil? || @indices.empty? || length == 0
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the Unit instance matching the given parameters
|
136
|
+
# Key values are case-insensitive and normalised by removing leading, trailing and redundant whitespace.
|
137
|
+
# Title matching is based on the exact title after key normalisation.
|
138
|
+
# @param key [any] the Unit attribute value to search for
|
139
|
+
# @param type [Symbol, nil] the Unit type to search for (defaults to the root type of the organisation hierarchy)
|
140
|
+
# @param index [Symbol, nil] the Unit index to search (:identity, :mnemonic, :title)
|
141
|
+
# @return [Unit, nil] the matching Unit instance, or nil if no matches are found
|
142
|
+
def get(key, type = nil, index: nil)
|
143
|
+
hash = @indices[type] || @indices[HIERARCHY[:_root][:type]]
|
144
|
+
if hash
|
145
|
+
index = :identity unless hash.include?(index)
|
146
|
+
hash[index][self.class.key(key)]
|
147
|
+
else
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the number of Units matching the given parameters
|
153
|
+
# @param type [Symbol, nil] the Unit type to count (defaults to all units)
|
154
|
+
# @param index [Symbol, nil] the Unit index to count (:identity, :mnemonic, :title)
|
155
|
+
# @return [Integer, nil] the number of Unit instances for the given parameters, or nil
|
156
|
+
def length(type = nil, index: nil)
|
157
|
+
count = 0
|
158
|
+
types = type.nil? ? @indices.keys : [type]
|
159
|
+
types.each do |unit_type|
|
160
|
+
hash = @indices[unit_type]
|
161
|
+
if hash
|
162
|
+
hash_index = hash.include?(index) ? hash[index] : hash[:identity]
|
163
|
+
count += hash_index.length if hash_index
|
164
|
+
end
|
165
|
+
end
|
166
|
+
count
|
167
|
+
end
|
168
|
+
|
169
|
+
# Populates the Organisation instance from the LUSI API
|
170
|
+
# @param api [LUSI::API::Core::API, nil] the LUSI API instance to use (defaults to the Organisation's @api)
|
171
|
+
# @param in_use_only [Boolean] if true, include only current Units; if false, additionally include historic Units
|
172
|
+
# @return [Nokogiri::XML::Node] the parsed XML <body/> content from the LUSI API call
|
173
|
+
def load(api = nil, in_use_only: true)
|
174
|
+
|
175
|
+
# Call the LUSI API
|
176
|
+
api ||= @api
|
177
|
+
xml = api.call('LUSIReference', 'General.asmx', 'GetOrganisationStructure', InUseOnly: in_use_only)
|
178
|
+
|
179
|
+
# Clear the existing organisation structure
|
180
|
+
clear
|
181
|
+
|
182
|
+
# Add each organisation unit to the structure
|
183
|
+
root = HIERARCHY[:_root]
|
184
|
+
LUSI::API::Core::XML.xml(xml, root[:path]) do |u|
|
185
|
+
add(Unit.new(u, type: root[:type], hierarchy: HIERARCHY))
|
186
|
+
end
|
187
|
+
|
188
|
+
# Return the parsed XML body content from the LUSI API call
|
189
|
+
xml
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
protected
|
194
|
+
|
195
|
+
# Returns a normalised index key
|
196
|
+
# Normalisation removes redundant whitespace and converts to uppercase for case-insensitivity
|
197
|
+
# @param key [any] the key value
|
198
|
+
# @return [String, nil] the normalised key value
|
199
|
+
def self.key(key)
|
200
|
+
return nil if key.nil?
|
201
|
+
key = key.to_s.strip
|
202
|
+
key.squeeze!(' ')
|
203
|
+
key.upcase!
|
204
|
+
key
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
# Represents a single unit (component) of an Organisation
|
211
|
+
class Unit
|
212
|
+
|
213
|
+
# @!attribute [rw] children
|
214
|
+
# @return [Array<Unit>, nil] an array of child Unit instances
|
215
|
+
attr_accessor :children
|
216
|
+
# @!attribute [rw] identity
|
217
|
+
# @return [any, nil] the identity code of the unit
|
218
|
+
attr_accessor :identity
|
219
|
+
# @!attribute [rw] in_use
|
220
|
+
# @return [Boolean, nil] true if the unit is currently in use, or false if historic
|
221
|
+
attr_accessor :in_use
|
222
|
+
# @!attribute [rw] is_academic
|
223
|
+
# @return [Boolean, nil] true if the unit is an academic unit, or false if not (e.g. administrative)
|
224
|
+
attr_accessor :is_academic
|
225
|
+
# @!attribute [rw] mnemonic
|
226
|
+
# @return [String, nil] a short mnemonic code for the unit
|
227
|
+
attr_accessor :mnemonic
|
228
|
+
# @!attribute [rw] parent
|
229
|
+
# @return [Unit, nil] the parent of this unit in the organisation hierarchy, or nil if this is a top-level unit
|
230
|
+
attr_accessor :parent
|
231
|
+
# @!attribute [rw] talis_code
|
232
|
+
# @return [String, nil] the code for this unit used by Talis Aspire reading lists
|
233
|
+
attr_accessor :talis_code
|
234
|
+
# @!attribute [rw] title
|
235
|
+
# @return [String, nil] the title (name) of the unit
|
236
|
+
attr_accessor :title
|
237
|
+
# @!attribute [rw] type
|
238
|
+
# @return [Symbol, nil] the type of the unit (e.g. :institution, :faculty, :department)
|
239
|
+
attr_accessor :type
|
240
|
+
|
241
|
+
# Initialises a new Unit instance
|
242
|
+
# If the hierarchy definition for the unit type specifies child units, the child unit instances are recursively
|
243
|
+
# created and added to the @children attribute.
|
244
|
+
# @param xml [Nokogiri::XML::Document, Nokogiri::XML::Node] the XML root of the unit from the LUSI API call
|
245
|
+
# @param lookup [LUSI::API::Core::Lookup::LookupService, nil] the lookup service for object resolution
|
246
|
+
# @param type [Symbol] the unit type
|
247
|
+
# @param hierarchy [Hash] the organisation's hierarchy definition
|
248
|
+
# @see LUSI::API::Organisation::Organisation::HIERARCHY
|
249
|
+
# @param parent [Unit, nil] the parent Unit of this unit, or nil for a top-level unit
|
250
|
+
# @param identity [any, nil] the default identity code
|
251
|
+
# @param in_use [Boolean, nil] the default in-use flag value
|
252
|
+
# @param is_academic [Boolean, nil] the default is-academic flag value
|
253
|
+
# @param mnemonic [any, nil] the default mnemonic code
|
254
|
+
# @param talis_code [any, nil] the default Talis code
|
255
|
+
# @param title [any, nil] the default unit title (name)
|
256
|
+
def initialize(xml = nil, lookup = nil, type: nil, hierarchy: nil, parent: nil, identity: nil, in_use: nil,
|
257
|
+
is_academic: nil, mnemonic: nil, talis_code: nil, title: nil)
|
258
|
+
|
259
|
+
@identity = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:Identity', identity)
|
260
|
+
@in_use = LUSI::API::Core::XML.xml_boolean_at(xml, 'xmlns:InUse', in_use)
|
261
|
+
@is_academic = LUSI::API::Core::XML.xml_boolean_at(xml, 'xmlns:IsAcademic', is_academic)
|
262
|
+
@mnemonic = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:Mnemonic', mnemonic)
|
263
|
+
@parent = parent
|
264
|
+
@talis_code = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:TalisCode', talis_code)
|
265
|
+
@title = LUSI::API::Core::XML.xml_content_at(xml, 'xmlns:Title', title)
|
266
|
+
@type = type
|
267
|
+
|
268
|
+
# Get the child units of this unit
|
269
|
+
# If a block is given, yield each child to the block
|
270
|
+
child = hierarchy ? hierarchy[type] : nil
|
271
|
+
if child
|
272
|
+
@children = LUSI::API::Core::XML.xml(xml, child[:path]) do |u|
|
273
|
+
Unit.new(u, lookup, type: child[:type], parent: self, hierarchy: hierarchy)
|
274
|
+
end
|
275
|
+
else
|
276
|
+
@children = nil
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
# Returns a string representation of the unit
|
282
|
+
def to_s
|
283
|
+
"#{@type}: #{@title}"
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
end
|