bright 1.3 → 2.1
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 +4 -4
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/Rakefile +0 -1
- data/bright.gemspec +12 -14
- data/lib/bright/address.rb +5 -5
- data/lib/bright/connection.rb +7 -9
- data/lib/bright/contact.rb +7 -9
- data/lib/bright/cursor_response_collection.rb +8 -10
- data/lib/bright/email_address.rb +1 -2
- data/lib/bright/enrollment.rb +2 -2
- data/lib/bright/errors.rb +0 -1
- data/lib/bright/helpers/blank_helper.rb +0 -2
- data/lib/bright/model.rb +13 -13
- data/lib/bright/phone_number.rb +1 -2
- data/lib/bright/response_collection.rb +8 -8
- data/lib/bright/school.rb +1 -4
- data/lib/bright/sis_apis/aeries.rb +18 -19
- data/lib/bright/sis_apis/base.rb +3 -5
- data/lib/bright/sis_apis/bright_sis.rb +82 -84
- data/lib/bright/sis_apis/focus.rb +78 -84
- data/lib/bright/sis_apis/one_roster/infinite_campus.rb +15 -0
- data/lib/bright/sis_apis/one_roster/skyward.rb +6 -0
- data/lib/bright/sis_apis/{infinite_campus.rb → one_roster.rb} +103 -113
- data/lib/bright/sis_apis/power_school.rb +105 -107
- data/lib/bright/sis_apis/synergy.rb +6 -8
- data/lib/bright/sis_apis/tsis.rb +54 -54
- data/lib/bright/student.rb +15 -19
- data/lib/bright/version.rb +1 -1
- data/lib/bright.rb +14 -12
- metadata +6 -8
- data/lib/bright/sis_apis/skyward.rb +0 -277
@@ -1,22 +1,21 @@
|
|
1
|
-
require
|
1
|
+
require "oauth"
|
2
2
|
|
3
3
|
module Bright
|
4
4
|
module SisApi
|
5
|
-
class
|
6
|
-
|
7
|
-
@@
|
8
|
-
@@
|
9
|
-
@@api_version = "1.1"
|
5
|
+
class OneRoster < Base
|
6
|
+
@@description = "Connects to the OneRoster API for accessing student information"
|
7
|
+
@@doc_url = "https://www.imsglobal.org/sites/default/files/spec/oneroster/v1p2/rostering-informationmodel/OneRosterv1p2RosteringService_InfoModelv1p0.html"
|
8
|
+
@@api_version = "1.2"
|
10
9
|
|
11
10
|
attr_accessor :connection_options, :schools_cache, :school_years_cache
|
12
11
|
|
13
12
|
DEMOGRAPHICS_CONVERSION = {
|
14
|
-
"americanIndianOrAlaskaNative"=>"American Indian Or Alaska Native",
|
15
|
-
"asian"=>"Asian",
|
16
|
-
"blackOrAfricanAmerican"=>"Black Or African American",
|
17
|
-
"nativeHawaiianOrOtherPacificIslander"=>"Native Hawaiian Or Other Pacific Islander",
|
18
|
-
"white"=>"White",
|
19
|
-
"hispanicOrLatinoEthnicity"=>"Hispanic Or Latino"
|
13
|
+
"americanIndianOrAlaskaNative" => "American Indian Or Alaska Native",
|
14
|
+
"asian" => "Asian",
|
15
|
+
"blackOrAfricanAmerican" => "Black Or African American",
|
16
|
+
"nativeHawaiianOrOtherPacificIslander" => "Native Hawaiian Or Other Pacific Islander",
|
17
|
+
"white" => "White",
|
18
|
+
"hispanicOrLatinoEthnicity" => "Hispanic Or Latino"
|
20
19
|
}
|
21
20
|
|
22
21
|
def initialize(options = {})
|
@@ -31,36 +30,36 @@ module Bright
|
|
31
30
|
end
|
32
31
|
|
33
32
|
def api_version
|
34
|
-
Gem::Version.new(
|
33
|
+
Gem::Version.new(connection_options.dig(:api_version) || @@api_version)
|
35
34
|
end
|
36
35
|
|
37
36
|
def get_student_by_api_id(api_id, params = {})
|
38
|
-
if api_version <= Gem::Version.new("1.1")
|
39
|
-
|
37
|
+
params = if api_version <= Gem::Version.new("1.1")
|
38
|
+
{role: "student"}.merge(params)
|
40
39
|
else
|
41
|
-
|
40
|
+
{roles: "student"}.merge(params)
|
42
41
|
end
|
43
|
-
st_hsh =
|
44
|
-
Student.new(convert_to_user_data(st_hsh["user"])) if st_hsh
|
42
|
+
st_hsh = request(:get, "users/#{api_id}", params)
|
43
|
+
Student.new(convert_to_user_data(st_hsh["user"])) if st_hsh && st_hsh["user"]
|
45
44
|
end
|
46
45
|
|
47
46
|
def get_student(params = {}, options = {})
|
48
|
-
|
47
|
+
get_students(params, options.merge(limit: 1, wrap_in_collection: false)).first
|
49
48
|
end
|
50
49
|
|
51
50
|
def get_students(params = {}, options = {})
|
52
|
-
if api_version <= Gem::Version.new("1.1")
|
53
|
-
|
51
|
+
params = if api_version <= Gem::Version.new("1.1")
|
52
|
+
{role: "student"}.merge(params)
|
54
53
|
else
|
55
|
-
|
54
|
+
{roles: "student"}.merge(params)
|
56
55
|
end
|
57
56
|
params[:limit] = params[:limit] || options[:limit] || 100
|
58
|
-
students_response_hash =
|
57
|
+
students_response_hash = request(:get, "users", map_search_params(params))
|
59
58
|
total_results = students_response_hash[:response_headers]["x-total-count"].to_i
|
60
|
-
if students_response_hash
|
59
|
+
if students_response_hash && students_response_hash["users"]
|
61
60
|
students_hash = [students_response_hash["users"]].flatten
|
62
61
|
|
63
|
-
students = students_hash.compact.collect {|st_hsh|
|
62
|
+
students = students_hash.compact.collect { |st_hsh|
|
64
63
|
Student.new(convert_to_user_data(st_hsh))
|
65
64
|
}
|
66
65
|
end
|
@@ -69,14 +68,14 @@ module Bright
|
|
69
68
|
load_more_call = proc { |page|
|
70
69
|
# pages start at one, so add a page here
|
71
70
|
params[:offset] = (params[:limit].to_i * page)
|
72
|
-
api.get_students(params, {:
|
71
|
+
api.get_students(params, {wrap_in_collection: false})
|
73
72
|
}
|
74
73
|
ResponseCollection.new({
|
75
|
-
:
|
76
|
-
:
|
77
|
-
:
|
78
|
-
:
|
79
|
-
:
|
74
|
+
seed_page: students,
|
75
|
+
total: total_results,
|
76
|
+
per_page: params[:limit],
|
77
|
+
load_more_call: load_more_call,
|
78
|
+
no_threads: options[:no_threads]
|
80
79
|
})
|
81
80
|
else
|
82
81
|
students
|
@@ -92,22 +91,22 @@ module Bright
|
|
92
91
|
end
|
93
92
|
|
94
93
|
def get_school_by_api_id(api_id, params = {})
|
95
|
-
sc_hsh =
|
96
|
-
School.new(convert_to_school_data(sc_hsh["org"])) if sc_hsh
|
94
|
+
sc_hsh = request(:get, "schools/#{api_id}", params)
|
95
|
+
School.new(convert_to_school_data(sc_hsh["org"])) if sc_hsh && sc_hsh["org"]
|
97
96
|
end
|
98
97
|
|
99
98
|
def get_school(params = {}, options = {})
|
100
|
-
|
99
|
+
get_schools(params, options.merge(limit: 1, wrap_in_collection: false)).first
|
101
100
|
end
|
102
101
|
|
103
102
|
def get_schools(params = {}, options = {})
|
104
103
|
params[:limit] = params[:limit] || options[:limit] || 100
|
105
|
-
schools_response_hash =
|
104
|
+
schools_response_hash = request(:get, "schools", map_school_search_params(params))
|
106
105
|
total_results = schools_response_hash[:response_headers]["x-total-count"].to_i
|
107
|
-
if schools_response_hash
|
106
|
+
if schools_response_hash && schools_response_hash["orgs"]
|
108
107
|
schools_hash = [schools_response_hash["orgs"]].flatten
|
109
108
|
|
110
|
-
schools = schools_hash.compact.collect {|sc_hsh|
|
109
|
+
schools = schools_hash.compact.collect { |sc_hsh|
|
111
110
|
School.new(convert_to_school_data(sc_hsh))
|
112
111
|
}
|
113
112
|
end
|
@@ -116,27 +115,27 @@ module Bright
|
|
116
115
|
load_more_call = proc { |page|
|
117
116
|
# pages start at one, so add a page here
|
118
117
|
params[:offset] = (params[:limit].to_i * page)
|
119
|
-
api.get_schools(params, {:
|
118
|
+
api.get_schools(params, {wrap_in_collection: false})
|
120
119
|
}
|
121
120
|
ResponseCollection.new({
|
122
|
-
:
|
123
|
-
:
|
124
|
-
:
|
125
|
-
:
|
126
|
-
:
|
121
|
+
seed_page: schools,
|
122
|
+
total: total_results,
|
123
|
+
per_page: params[:limit],
|
124
|
+
load_more_call: load_more_call,
|
125
|
+
no_threads: options[:no_threads]
|
127
126
|
})
|
128
127
|
else
|
129
128
|
schools
|
130
129
|
end
|
131
130
|
end
|
132
131
|
|
133
|
-
def get_contact_by_api_id(api_id, params ={})
|
134
|
-
contact_hsh =
|
135
|
-
Contact.new(convert_to_user_data(contact_hsh["user"], bright_type: "Contact")) if contact_hsh
|
132
|
+
def get_contact_by_api_id(api_id, params = {})
|
133
|
+
contact_hsh = request(:get, "users/#{api_id}", params)
|
134
|
+
Contact.new(convert_to_user_data(contact_hsh["user"], bright_type: "Contact")) if contact_hsh && contact_hsh["user"]
|
136
135
|
end
|
137
136
|
|
138
137
|
def request(method, path, params = {})
|
139
|
-
uri
|
138
|
+
uri = "#{connection_options[:uri]}/#{path}"
|
140
139
|
body = nil
|
141
140
|
if method == :get
|
142
141
|
query = URI.encode_www_form(params)
|
@@ -147,7 +146,7 @@ module Bright
|
|
147
146
|
|
148
147
|
response = connection_retry_wrapper {
|
149
148
|
connection = Bright::Connection.new(uri)
|
150
|
-
headers =
|
149
|
+
headers = headers_for_auth(uri)
|
151
150
|
connection.request(method, body, headers)
|
152
151
|
}
|
153
152
|
|
@@ -155,8 +154,8 @@ module Bright
|
|
155
154
|
response_hash = JSON.parse(response.body)
|
156
155
|
response_hash[:response_headers] = response.headers
|
157
156
|
else
|
158
|
-
puts
|
159
|
-
puts
|
157
|
+
puts response.inspect
|
158
|
+
puts response.body
|
160
159
|
end
|
161
160
|
response_hash
|
162
161
|
end
|
@@ -164,48 +163,46 @@ module Bright
|
|
164
163
|
protected
|
165
164
|
|
166
165
|
def headers_for_auth(uri)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
site = "#{site.scheme}://#{site.host}"
|
171
|
-
consumer = OAuth::Consumer.new(self.connection_options[:client_id], self.connection_options[:client_secret], { :site => site, :scheme => :header })
|
172
|
-
options = {:timestamp => Time.now.to_i, :nonce => SecureRandom.uuid}
|
173
|
-
{"Authorization" => consumer.create_signed_request(:get, uri, nil, options)["Authorization"]}
|
174
|
-
when Gem::Version.new("1.2")
|
175
|
-
if self.connection_options[:access_token].nil? or self.connection_options[:access_token_expires] < Time.now
|
176
|
-
self.retrieve_access_token
|
166
|
+
if api_version >= Gem::Version.new("1.2") || connection_options[:token_uri].present?
|
167
|
+
if connection_options[:access_token].nil? || (connection_options[:access_token_expires] < Time.now)
|
168
|
+
retrieve_access_token
|
177
169
|
end
|
178
170
|
{
|
179
|
-
"Authorization" => "Bearer #{
|
180
|
-
"Accept" => "application/json
|
181
|
-
"Content-Type" =>"application/json
|
171
|
+
"Authorization" => "Bearer #{connection_options[:access_token]}",
|
172
|
+
"Accept" => "application/json",
|
173
|
+
"Content-Type" => "application/json"
|
182
174
|
}
|
175
|
+
else
|
176
|
+
site = URI.parse(connection_options[:uri])
|
177
|
+
site = "#{site.scheme}://#{site.host}"
|
178
|
+
consumer = OAuth::Consumer.new(connection_options[:client_id], connection_options[:client_secret], {site: site, scheme: :header})
|
179
|
+
options = {timestamp: Time.now.to_i, nonce: SecureRandom.uuid}
|
180
|
+
{"Authorization" => consumer.create_signed_request(:get, uri, nil, options)["Authorization"]}
|
183
181
|
end
|
184
182
|
end
|
185
183
|
|
186
184
|
def retrieve_access_token
|
187
|
-
connection = Bright::Connection.new(
|
185
|
+
connection = Bright::Connection.new(connection_options[:token_uri])
|
188
186
|
response = connection.request(:post,
|
189
187
|
{
|
190
188
|
"grant_type" => "client_credentials",
|
191
|
-
"username" =>
|
192
|
-
"password" =>
|
189
|
+
"username" => connection_options[:client_id],
|
190
|
+
"password" => connection_options[:client_secret]
|
193
191
|
},
|
194
|
-
|
195
|
-
)
|
192
|
+
headers_for_access_token)
|
196
193
|
if !response.error?
|
197
194
|
response_hash = JSON.parse(response.body)
|
198
195
|
end
|
199
196
|
if response_hash["access_token"]
|
200
|
-
|
201
|
-
|
197
|
+
connection_options[:access_token] = response_hash["access_token"]
|
198
|
+
connection_options[:access_token_expires] = (Time.now - 10) + response_hash["expires_in"]
|
202
199
|
end
|
203
200
|
response_hash
|
204
201
|
end
|
205
202
|
|
206
203
|
def headers_for_access_token
|
207
204
|
{
|
208
|
-
"Authorization" => "Basic #{Base64.strict_encode64("#{
|
205
|
+
"Authorization" => "Basic #{Base64.strict_encode64("#{connection_options[:client_id]}:#{connection_options[:client_secret]}")}",
|
209
206
|
"Content-Type" => "application/x-www-form-urlencoded;charset=UTF-8"
|
210
207
|
}
|
211
208
|
end
|
@@ -215,7 +212,7 @@ module Bright
|
|
215
212
|
default_params = {}
|
216
213
|
|
217
214
|
filter = []
|
218
|
-
params.each do |k,v|
|
215
|
+
params.each do |k, v|
|
219
216
|
case k.to_s
|
220
217
|
when "first_name"
|
221
218
|
filter << "givenName='#{v}'"
|
@@ -236,14 +233,14 @@ module Bright
|
|
236
233
|
unless filter.empty?
|
237
234
|
params = {"filter" => filter.join(" AND ")}
|
238
235
|
end
|
239
|
-
default_params.merge(params).reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
|
236
|
+
default_params.merge(params).reject { |k, v| v.respond_to?(:empty?) ? v.empty? : v.nil? }
|
240
237
|
end
|
241
238
|
|
242
239
|
def map_school_search_params(params)
|
243
240
|
params = params.dup
|
244
241
|
default_params = {}
|
245
242
|
filter = []
|
246
|
-
params.each do |k,v|
|
243
|
+
params.each do |k, v|
|
247
244
|
case k.to_s
|
248
245
|
when "number"
|
249
246
|
filter << "identifier='#{v}'"
|
@@ -256,50 +253,46 @@ module Bright
|
|
256
253
|
unless filter.empty?
|
257
254
|
params = {"filter" => filter.join(" AND ")}
|
258
255
|
end
|
259
|
-
default_params.merge(params).reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
|
256
|
+
default_params.merge(params).reject { |k, v| v.respond_to?(:empty?) ? v.empty? : v.nil? }
|
260
257
|
end
|
261
258
|
|
262
259
|
def convert_to_school_data(school_params)
|
263
260
|
return {} if school_params.blank?
|
264
|
-
|
265
|
-
:
|
266
|
-
:
|
267
|
-
:
|
268
|
-
:
|
261
|
+
{
|
262
|
+
api_id: school_params["sourcedId"],
|
263
|
+
name: school_params["name"],
|
264
|
+
number: school_params["identifier"],
|
265
|
+
last_modified: school_params["dateLastModified"]
|
269
266
|
}
|
270
|
-
return school_data_hsh
|
271
267
|
end
|
272
268
|
|
273
269
|
def convert_to_user_data(user_params, bright_type: "Student")
|
274
270
|
return {} if user_params.blank?
|
275
271
|
user_data_hsh = {
|
276
|
-
:
|
277
|
-
:
|
278
|
-
:
|
279
|
-
:
|
280
|
-
:
|
281
|
-
}.reject{|k,v| v.blank?}
|
272
|
+
api_id: user_params["sourcedId"],
|
273
|
+
first_name: user_params["givenName"],
|
274
|
+
middle_name: user_params["middleName"],
|
275
|
+
last_name: user_params["familyName"],
|
276
|
+
last_modified: user_params["dateLastModified"]
|
277
|
+
}.reject { |k, v| v.blank? }
|
282
278
|
unless user_params["identifier"].blank?
|
283
279
|
user_data_hsh[:sis_student_id] = user_params["identifier"]
|
284
280
|
end
|
285
|
-
unless user_params["userMasterIdentifier"].blank?
|
286
|
-
user_data_hsh[:state_student_id] = user_params["userMasterIdentifier"]
|
287
|
-
end
|
288
281
|
unless user_params["userIds"].blank?
|
289
|
-
if (state_id_hsh = user_params["userIds"].detect{|user_id_hsh| user_id_hsh["type"] == "stateID"})
|
282
|
+
if (state_id_hsh = user_params["userIds"].detect { |user_id_hsh| user_id_hsh["type"] == "stateID" })
|
290
283
|
user_data_hsh[:state_student_id] = state_id_hsh["identifier"]
|
291
284
|
end
|
292
285
|
end
|
293
286
|
unless user_params["email"].blank?
|
294
287
|
user_data_hsh[:email_address] = {
|
295
|
-
:
|
288
|
+
email_address: user_params["email"]
|
296
289
|
}
|
297
290
|
end
|
298
291
|
unless user_params["orgs"].blank?
|
299
|
-
if (s = user_params["orgs"].detect{|org| org["href"] =~ /\/schools\//})
|
292
|
+
if (s = user_params["orgs"].detect { |org| org["href"] =~ /\/schools\// })
|
300
293
|
self.schools_cache ||= {}
|
301
294
|
if (attending_school = self.schools_cache[s["sourcedId"]]).nil?
|
302
|
-
attending_school =
|
295
|
+
attending_school = get_school_by_api_id(s["sourcedId"])
|
303
296
|
self.schools_cache[attending_school.api_id] = attending_school
|
304
297
|
end
|
305
298
|
end
|
@@ -308,26 +301,24 @@ module Bright
|
|
308
301
|
end
|
309
302
|
end
|
310
303
|
unless user_params["phone"].blank?
|
311
|
-
user_data_hsh[:phone_numbers] = [{:
|
304
|
+
user_data_hsh[:phone_numbers] = [{phone_number: user_params["phone"]}]
|
312
305
|
end
|
313
306
|
unless user_params["sms"].blank?
|
314
307
|
user_data_hsh[:phone_numbers] ||= []
|
315
|
-
user_data_hsh[:phone_numbers] << {:
|
308
|
+
user_data_hsh[:phone_numbers] << {phone_number: user_params["sms"]}
|
316
309
|
end
|
317
310
|
|
318
|
-
#add the demographic information
|
311
|
+
# add the demographic information
|
319
312
|
demographics_hash = get_demographic_information(user_data_hsh[:api_id])
|
320
313
|
user_data_hsh.merge!(demographics_hash) unless demographics_hash.blank?
|
321
314
|
|
322
|
-
#if you're a student, build the contacts too
|
323
|
-
if bright_type == "Student"
|
315
|
+
# if you're a student, build the contacts too
|
316
|
+
if (bright_type == "Student") && !user_params["agents"].blank?
|
324
317
|
user_data_hsh[:contacts] = user_params["agents"].collect do |agent_hsh|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
raise e
|
330
|
-
end
|
318
|
+
get_contact_by_api_id(agent_hsh["sourcedId"])
|
319
|
+
rescue Bright::ResponseError => e
|
320
|
+
if !e.message.to_s.include?("404")
|
321
|
+
raise e
|
331
322
|
end
|
332
323
|
end.compact
|
333
324
|
user_data_hsh[:grade] = (user_params["grades"] || []).first
|
@@ -336,7 +327,7 @@ module Bright
|
|
336
327
|
end
|
337
328
|
end
|
338
329
|
|
339
|
-
|
330
|
+
user_data_hsh
|
340
331
|
end
|
341
332
|
|
342
333
|
def get_demographic_information(api_id)
|
@@ -345,7 +336,7 @@ module Bright
|
|
345
336
|
begin
|
346
337
|
demographics_params = request(:get, "demographics/#{api_id}")["demographics"]
|
347
338
|
rescue Bright::ResponseError => e
|
348
|
-
if e.message.to_s.include?(
|
339
|
+
if e.message.to_s.include?("404")
|
349
340
|
return demographic_hsh
|
350
341
|
else
|
351
342
|
raise e
|
@@ -368,22 +359,21 @@ module Bright
|
|
368
359
|
end
|
369
360
|
end
|
370
361
|
end
|
371
|
-
|
362
|
+
demographic_hsh
|
372
363
|
end
|
373
364
|
|
374
365
|
def get_grade_school_year(date = Date.today)
|
375
|
-
#return the school year of a specific date
|
366
|
+
# return the school year of a specific date
|
376
367
|
self.school_years_cache ||= {}
|
377
368
|
if self.school_years_cache[date].nil?
|
378
|
-
academic_periods_params =
|
379
|
-
school_years = academic_periods_params.map{|ap| ap["schoolYear"]}.uniq
|
369
|
+
academic_periods_params = request(:get, "academicSessions", {"filter" => "startDate<='#{date}' AND endDate>='#{date}' AND status='active'"})["academicSessions"]
|
370
|
+
school_years = academic_periods_params.map { |ap| ap["schoolYear"] }.uniq
|
380
371
|
if school_years.size == 1
|
381
372
|
self.school_years_cache[date] = school_years.first
|
382
373
|
end
|
383
374
|
end
|
384
|
-
|
375
|
+
self.school_years_cache[date]
|
385
376
|
end
|
386
|
-
|
387
377
|
end
|
388
378
|
end
|
389
379
|
end
|