bright 0.1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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