bright 0.1.0 → 1.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.
@@ -2,29 +2,29 @@ module Bright
2
2
  module SisApi
3
3
  class Aeries < Base
4
4
  DATE_FORMAT = '%Y-%m-%dT%H:%M:%S'
5
-
5
+
6
6
  @@description = "Connects to the Aeries API for accessing student information"
7
7
  @@doc_url = "http://www.aeries.com/downloads/docs.1234/TechnicalSpecs/Aeries_API_Documentation.pdf"
8
8
  @@api_version = ""
9
-
9
+
10
10
  attr_accessor :connection_options
11
-
11
+
12
12
  def initialize(options = {})
13
13
  self.connection_options = options[:connection] || {}
14
14
  # {
15
- # :certficate => "",
15
+ # :certficate => "",
16
16
  # :uri => ""
17
17
  # }
18
18
  end
19
-
19
+
20
20
  def get_student_by_api_id(api_id)
21
21
  get_students({:api_id => api_id, :limit => 1}).first
22
22
  end
23
-
23
+
24
24
  def get_student(params)
25
25
  get_students(params.merge(:limit => 1)).first
26
26
  end
27
-
27
+
28
28
  def get_students(params)
29
29
  if params.has_key?(:school) or params.has_key?(:school_api_id)
30
30
  school_api_id = params.delete(:school) || params.delete(:school_api_id)
@@ -32,7 +32,7 @@ module Bright
32
32
  else
33
33
  threads = []
34
34
  get_schools.each do |school|
35
- threads << Thread.new do
35
+ threads << Thread.new do
36
36
  get_students_by_school(school, params)
37
37
  end
38
38
  end
@@ -40,7 +40,7 @@ module Bright
40
40
  end
41
41
  filter_students_by_params(students, params)
42
42
  end
43
-
43
+
44
44
  def get_students_by_school(school, params = {})
45
45
  school_api_id = school.is_a?(School) ? school.api_id : school
46
46
  if params.has_key?(:api_id)
@@ -53,21 +53,21 @@ module Bright
53
53
  students_response_hash = self.request(:get, path, self.map_student_search_params(params))
54
54
  students_response_hash.collect{|shsh| Student.new(convert_to_student_data(shsh))}
55
55
  end
56
-
57
- def create_student(student, additional_params = {})
56
+
57
+ def create_student(student)
58
58
  raise NotImplementedError
59
59
  end
60
-
61
- def update_student(student, additional_params = {})
60
+
61
+ def update_student(student)
62
62
  raise NotImplementedError
63
63
  end
64
-
64
+
65
65
  def get_schools(params = {})
66
66
  schools_response_hash = self.request(:get, 'api/v2/schools', params)
67
-
67
+
68
68
  schools_response_hash.collect{|h| School.new(convert_to_school_data(h))}
69
69
  end
70
-
70
+
71
71
  def request(method, path, params = {})
72
72
  uri = "#{self.connection_options[:uri]}/#{path}"
73
73
  body = nil
@@ -78,11 +78,12 @@ module Bright
78
78
  body = JSON.dump(params)
79
79
  end
80
80
 
81
- headers = self.headers_for_auth
81
+ response = connection_retry_wrapper {
82
+ connection = Bright::Connection.new(uri)
83
+ headers = self.headers_for_auth
84
+ connection.request(method, body, headers)
85
+ }
82
86
 
83
- connection = Bright::Connection.new(uri)
84
- response = connection.request(method, body, headers)
85
-
86
87
  if !response.error?
87
88
  response_hash = JSON.parse(response.body)
88
89
  else
@@ -91,55 +92,55 @@ module Bright
91
92
  end
92
93
  response_hash
93
94
  end
94
-
95
+
95
96
  protected
96
-
97
+
97
98
  def map_student_search_params(attrs)
98
99
  attrs
99
100
  end
100
-
101
+
101
102
  def convert_to_student_data(attrs)
102
103
  cattrs = {}
103
-
104
+
104
105
  cattrs[:first_name] = attrs["FirstName"]
105
106
  cattrs[:middle_name] = attrs["MiddleName"]
106
107
  cattrs[:last_name] = attrs["LastName"]
107
-
108
+
108
109
  cattrs[:api_id] = attrs["PermanentID"]
109
110
  cattrs[:sis_student_id] = attrs["StudentNumber"]
110
111
  cattrs[:state_student_id] = attrs["StateStudentID"]
111
-
112
+
112
113
  cattrs[:gender] = attrs["Sex"]
113
114
  if attrs["Birthdate"]
114
- begin
115
+ begin
115
116
  cattrs[:birth_date] = Date.strptime(attrs["Birthdate"], DATE_FORMAT)
116
117
  rescue => e
117
118
  puts "#{e.inspect} #{bd}"
118
119
  end
119
120
  end
120
-
121
+
121
122
  #SchoolCode
122
-
123
+
123
124
  cattrs.reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
124
125
  end
125
-
126
+
126
127
  def convert_to_school_data(attrs)
127
128
  cattrs = {}
128
-
129
+
129
130
  cattrs[:api_id] = attrs["SchoolCode"]
130
131
  cattrs[:name] = attrs["Name"]
131
132
  cattrs[:number] = attrs["SchoolCode"]
132
-
133
+
133
134
  cattrs.reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
134
135
  end
135
-
136
+
136
137
  def headers_for_auth
137
138
  {
138
139
  'AERIES-CERT' => self.connection_options[:certificate],
139
140
  'Content-Type' => "application/json"
140
141
  }
141
142
  end
142
-
143
+
143
144
  end
144
145
  end
145
- end
146
+ end
@@ -1,17 +1,17 @@
1
1
  module Bright
2
2
  module SisApi
3
3
  class Base
4
-
4
+
5
5
  def filter_students_by_params(students, params)
6
6
  total = params[:limit]
7
7
  count = 0
8
8
  found = []
9
-
9
+
10
10
  keys = (Student.attribute_names & params.keys.collect(&:to_sym))
11
11
  puts "filtering on #{keys.join(",")}"
12
12
  students.each do |student|
13
13
  break if total and count >= total
14
-
14
+
15
15
  should = (keys).all? do |m|
16
16
  student.send(m) =~ Regexp.new(Regexp.escape(params[m]), Regexp::IGNORECASE)
17
17
  end
@@ -20,7 +20,33 @@ module Bright
20
20
  end
21
21
  found
22
22
  end
23
-
23
+
24
+ def connection_retry_wrapper(&block)
25
+ retry_attempts = connection_options[:retry_attempts] || 2
26
+ retries = 0
27
+ begin
28
+ yield
29
+ rescue Bright::ResponseError => e
30
+ retries += 1
31
+ if e.server_error? && retries <= retry_attempts.to_i
32
+ puts "retrying #{retries}: #{e.class.to_s} - #{e.to_s}"
33
+ sleep(retries * 3)
34
+ retry
35
+ else
36
+ raise
37
+ end
38
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Net::ReadTimeout, Net::OpenTimeout, EOFError => e
39
+ retries += 1
40
+ if retries <= retry_attempts.to_i
41
+ puts "retrying #{retries}: #{e.class.to_s} - #{e.to_s}"
42
+ sleep(retries * 3)
43
+ retry
44
+ else
45
+ raise
46
+ end
47
+ end
48
+ end
49
+
24
50
  end
25
51
  end
26
- end
52
+ end
@@ -0,0 +1,305 @@
1
+ module Bright
2
+ module SisApi
3
+ class BrightSis < Base
4
+
5
+ @@description = "Connects to the Bright SIS Data Store"
6
+ @@doc_url = ""
7
+ @@api_version = "v1"
8
+
9
+ attr_accessor :connection_options
10
+
11
+ DEMOGRAPHICS_CONVERSION = {
12
+ "I"=>"American Indian Or Alaska Native",
13
+ "A"=>"Asian",
14
+ "B"=>"Black Or African American",
15
+ "P"=>"Native Hawaiian Or Other Pacific Islander",
16
+ "W"=>"White",
17
+ "M"=>"Other"
18
+ }
19
+
20
+ def initialize(options = {})
21
+ self.connection_options = options[:connection] || {}
22
+ # {
23
+ # :access_token => ""
24
+ # :uri => ""
25
+ # }
26
+ end
27
+
28
+ def get_student_by_api_id(api_id, options = {})
29
+ self.get_students({"uuid" => api_id}, options.merge(:limit => 1, :wrap_in_collection => false)).first
30
+ end
31
+
32
+ def get_student(params = {}, options = {})
33
+ self.get_students(params, options.merge(:limit => 1, :wrap_in_collection => false)).first
34
+ end
35
+
36
+ def get_students(params = {}, options = {})
37
+ params[:limit] = params[:limit] || options[:limit] || 100
38
+ students_response_hash = self.request(:get, 'students', self.map_student_search_params(params))
39
+ total_results = students_response_hash[:response_headers]["total"].to_i
40
+ if students_response_hash and students_response_hash["students"]
41
+ students_hash = [students_response_hash["students"]].flatten
42
+
43
+ students = students_hash.compact.collect {|st_hsh|
44
+ Student.new(convert_to_student_data(st_hsh))
45
+ }
46
+ end
47
+ if options[:wrap_in_collection] != false
48
+ api = self
49
+ load_more_call = proc { |page|
50
+ # pages start at one, so add a page here
51
+ params[:offset] = (params[:limit].to_i * page)
52
+ api.get_students(params, {:wrap_in_collection => false})
53
+ }
54
+ ResponseCollection.new({
55
+ :seed_page => students,
56
+ :total => total_results,
57
+ :per_page => params[:limit],
58
+ :load_more_call => load_more_call,
59
+ :no_threads => options[:no_threads]
60
+ })
61
+ else
62
+ students
63
+ end
64
+ end
65
+
66
+ def create_student(student)
67
+ raise NotImplementedError
68
+ end
69
+
70
+ def update_student(student)
71
+ raise NotImplementedError
72
+ end
73
+
74
+ def get_schools(params = {}, options = {})
75
+ params[:limit] = params[:limit] || options[:limit] || 100
76
+ schools_response_hash = self.request(:get, 'schools', self.map_school_search_params(params))
77
+ total_results = schools_response_hash[:response_headers]["total"].to_i
78
+ if schools_response_hash and schools_response_hash["schools"]
79
+ schools_hash = [schools_response_hash["schools"]].flatten
80
+
81
+ schools = schools_hash.compact.collect {|st_hsh|
82
+ School.new(convert_to_school_data(st_hsh))
83
+ }
84
+ end
85
+ if options[:wrap_in_collection] != false
86
+ api = self
87
+ load_more_call = proc { |page|
88
+ # pages start at one, so add a page here
89
+ params[:offset] = (params[:limit].to_i * page)
90
+ api.get_schools(params, {:wrap_in_collection => false})
91
+ }
92
+ ResponseCollection.new({
93
+ :seed_page => schools,
94
+ :total => total_results,
95
+ :per_page => params[:limit],
96
+ :load_more_call => load_more_call,
97
+ :no_threads => options[:no_threads]
98
+ })
99
+ else
100
+ schools
101
+ end
102
+ end
103
+
104
+ def request(method, path, params = {})
105
+ uri = "#{self.connection_options[:uri]}/#{path}"
106
+ body = nil
107
+ if method == :get
108
+ query = URI.encode_www_form(params)
109
+ uri += "?#{query}" unless query.strip == ""
110
+ else
111
+ body = JSON.dump(params)
112
+ end
113
+
114
+ response = connection_retry_wrapper {
115
+ connection = Bright::Connection.new(uri)
116
+ headers = self.headers_for_auth
117
+ connection.request(method, body, headers)
118
+ }
119
+
120
+ if !response.error?
121
+ response_hash = JSON.parse(response.body)
122
+ response_hash[:response_headers] = response.headers
123
+ else
124
+ puts "#{response.inspect}"
125
+ puts "#{response.body}"
126
+ end
127
+ response_hash
128
+ end
129
+
130
+ protected
131
+
132
+ def headers_for_auth(uri)
133
+ {"Authorization" => "Token token=#{self.connection_options[:access_token]}"}
134
+ end
135
+
136
+ def map_student_search_params(attrs)
137
+ filter_params = {}
138
+ attrs.each do |k,v|
139
+ case k.to_s
140
+ when "api_id"
141
+ filter_params["uuid"] = v
142
+ when "sis_student_id"
143
+ filter_params["student_number"] = v
144
+ when "state_student_id"
145
+ filter_params["state_id"] = v
146
+ else
147
+ filter_params[k] = v
148
+ end
149
+ end
150
+ return filter_params
151
+ end
152
+
153
+ def convert_to_student_data(student_params)
154
+ return {} if student_params.nil?
155
+ student_data_hsh = {
156
+ :api_id => student_params["uuid"],
157
+ :first_name => student_params["first_name"],
158
+ :middle_name => student_params["middle_name"],
159
+ :last_name => student_params["last_name"],
160
+ :sis_student_id => student_params["student_number"],
161
+ :state_student_id => student_params["state_id"],
162
+ :grade => student_params["grade"],
163
+ :grade_school_year => student_params["grade_school_year"],
164
+ :projected_graduation_year => student_params["graduation_year"],
165
+ :gender => student_params["gender"],
166
+ :frl_status => student_params["frl_status"],
167
+ :image => student_params["picture"],
168
+ :hispanic_ethnicity => student_params["hispanic_latino"],
169
+ :last_modified => student_params["updated_at"]
170
+ }.reject{|k,v| v.blank?}
171
+ unless student_params["birthdate"].blank?
172
+ student_data_hsh[:birth_date] = Date.parse(student_params["birthdate"]).to_s
173
+ end
174
+
175
+ DEMOGRAPHICS_CONVERSION.each do |demographics_key, demographics_value|
176
+ if student_params["race"].to_s.upcase.include?(demographics_key)
177
+ student_data_hsh[:race] ||= []
178
+ student_data_hsh[:race] << demographics_value
179
+ end
180
+ end
181
+
182
+ unless student_params["addresses"].blank?
183
+ student_data_hsh[:addresses] = student_params["addresses"].collect do |address_params|
184
+ convert_to_address_data(address_params)
185
+ end
186
+ end
187
+
188
+ unless student_params["phone_numbers"].blank?
189
+ student_data_hsh[:phone_numbers] = student_params["phone_numbers"].collect do |phone_params|
190
+ convert_to_phone_number_data(phone_params)
191
+ end
192
+ end
193
+
194
+ unless student_params["email_addresses"].blank?
195
+ student_data_hsh[:email_address] = {
196
+ :email_address => student_params["email_addresses"].first["email_address"]
197
+ }
198
+ end
199
+
200
+ unless student_params["school"].blank?
201
+ student_data_hsh[:school] = convert_to_school_data(student_params["school"])
202
+ end
203
+
204
+ unless student_params["contacts"].blank?
205
+ student_data_hsh[:contacts] = student_params["contacts"].collect do |contact_params|
206
+ contact_data_hsh = {
207
+ :api_id => contact_params["uuid"],
208
+ :first_name => contact_params["first_name"],
209
+ :middle_name => contact_params["middle_name"],
210
+ :last_name => contact_params["last_name"],
211
+ :relationship_type => contact_params["relationship"],
212
+ :sis_student_id => contact_params["sis_id"],
213
+ :last_modified => contact_params["updated_at"]
214
+ }
215
+ unless contact_params["addresses"].blank?
216
+ contact_data_hsh[:addresses] = contact_params["addresses"].collect do |address_params|
217
+ convert_to_address_data(address_params)
218
+ end
219
+ end
220
+ unless contact_params["phone_numbers"].blank?
221
+ contact_data_hsh[:phone_numbers] = contact_params["phone_numbers"].collect do |phone_params|
222
+ convert_to_phone_number_data(phone_params)
223
+ end
224
+ end
225
+ unless contact_params["email_addresses"].blank?
226
+ contact_data_hsh[:email_address] = {
227
+ :email_address => contact_params["email_addresses"].first["email_address"]
228
+ }
229
+ end
230
+ contact_data_hsh.reject{|k,v| v.blank?}
231
+ end
232
+ end
233
+
234
+ return student_data_hsh
235
+ end
236
+
237
+ def map_school_search_params(attrs)
238
+ filter_params = {}
239
+ attrs.each do |k,v|
240
+ case k.to_s
241
+ when "api_id"
242
+ filter_params["id"] = v
243
+ else
244
+ filter_params[k] = v
245
+ end
246
+ end
247
+ return filter_params
248
+ end
249
+
250
+ def convert_to_phone_number_data(phone_number_params)
251
+ return {} if phone_number_params.nil?
252
+ {
253
+ :phone_number => phone_number_params["phone_number"],
254
+ :type => phone_number_params["phone_type"]
255
+ }.reject{|k,v| v.blank?}
256
+ end
257
+
258
+ def convert_to_address_data(address_params)
259
+ return {} if address_params.nil?
260
+ {
261
+ :street => address_params["street"],
262
+ :apt => address_params["street_line_2"],
263
+ :city => address_params["city"],
264
+ :state => address_params["state"],
265
+ :postal_code => address_params["zip"],
266
+ :lattitude => address_params["latitude"],
267
+ :longitude => address_params["longitude"],
268
+ :type => address_params["address_type"]
269
+ }.reject{|k,v| v.blank?}
270
+ end
271
+
272
+ def convert_to_school_data(school_params)
273
+ return {} if school_params.nil?
274
+
275
+ school_data_hsh = {
276
+ :api_id => school_params["id"],
277
+ :name => school_params["name"],
278
+ :number => school_params["number"],
279
+ :state_id => school_params["state_id"],
280
+ :low_grade => school_params["low_grade"],
281
+ :high_grade => school_params["high_grade"],
282
+ :last_modified => school_params["updated_at"]
283
+ }
284
+
285
+ unless school_params["school_address"].blank?
286
+ school_data_hsh[:address] = {
287
+ :street => school_params["school_address"],
288
+ :city => school_params["school_city"],
289
+ :state => school_params["school_state"],
290
+ :postal_code => school_params["school_zip"]
291
+ }
292
+ end
293
+
294
+ unless school_params["school_phone"].blank?
295
+ school_data_hsh[:phone_number] = {
296
+ :phone_number => school_params["school_phone"]
297
+ }
298
+ end
299
+
300
+ return school_data_hsh
301
+ end
302
+
303
+ end
304
+ end
305
+ end