bright 0.1.0
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 +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/bright.gemspec +26 -0
- data/lib/bright.rb +20 -0
- data/lib/bright/address.rb +17 -0
- data/lib/bright/connection.rb +119 -0
- data/lib/bright/enrollment.rb +7 -0
- data/lib/bright/errors.rb +24 -0
- data/lib/bright/model.rb +47 -0
- data/lib/bright/response_collection.rb +55 -0
- data/lib/bright/school.rb +17 -0
- data/lib/bright/sis_apis/aeries.rb +145 -0
- data/lib/bright/sis_apis/base.rb +26 -0
- data/lib/bright/sis_apis/infinite_campus.rb +31 -0
- data/lib/bright/sis_apis/power_school.rb +386 -0
- data/lib/bright/sis_apis/synergy.rb +31 -0
- data/lib/bright/sis_apis/tsis.rb +248 -0
- data/lib/bright/student.rb +53 -0
- data/lib/bright/version.rb +3 -0
- metadata +126 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Bright
|
5
|
+
module SisApi
|
6
|
+
class TSIS < Base
|
7
|
+
DATE_FORMAT = '%m/%d/%Y'
|
8
|
+
|
9
|
+
@@description = "Connects to the TIES API for accessing TIES TSIS student information"
|
10
|
+
@@doc_url = "#unkown"
|
11
|
+
|
12
|
+
attr_accessor :connection_options
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
self.connection_options = options[:connection] || {}
|
16
|
+
# {
|
17
|
+
# :key => "",
|
18
|
+
# :secret => "",
|
19
|
+
# :district_id => "",
|
20
|
+
# :uri => ""
|
21
|
+
# }
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_student_by_api_id(api_id, params = {})
|
25
|
+
self.get_student(params.merge({:sis_student_id => api_id}))
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_student(params = {}, options = {})
|
29
|
+
params = self.apply_options(params, options.merge({:per_page => 1}))
|
30
|
+
|
31
|
+
# Students only gets you students that are enrolled in a school for a given school year.
|
32
|
+
students_response_hash = self.request(:get, 'Students/', self.map_search_params(params))
|
33
|
+
found_student = nil
|
34
|
+
if students_response_hash["Return"] and students_response_hash["Return"].first
|
35
|
+
found_student = Student.new(convert_to_student_data(students_response_hash["Return"].first))
|
36
|
+
end
|
37
|
+
if found_student.nil?
|
38
|
+
# Students/Family can get you students that are not enrolled in a school for a given school year.
|
39
|
+
family_response_hash = self.request(:get, 'Students/Family/', self.map_search_params(params))
|
40
|
+
if family_response_hash["Return"] and family_response_hash["Return"].first
|
41
|
+
found_student = Student.new(convert_to_student_data(family_response_hash["Return"].first))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
found_student
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_students(params = {}, options = {})
|
48
|
+
params = self.apply_options(params, options)
|
49
|
+
|
50
|
+
if params[:schoolyear]
|
51
|
+
# Students only gets you students that are enrolled in a school for a given school year.
|
52
|
+
response_hash = self.request(:get, self.apply_page_to_url('Students/', options[:page]), self.map_search_params(params))
|
53
|
+
else
|
54
|
+
# Students/Family can get you students that are not enrolled in a school for a given school year.
|
55
|
+
response_hash = self.request(:get, self.apply_page_to_url('Students/Family/', options[:page]), self.map_search_params(params))
|
56
|
+
end
|
57
|
+
|
58
|
+
students = response_hash["Return"].collect{|hsh| Student.new(convert_to_student_data(hsh))}
|
59
|
+
total_results = response_hash["TotalCount"].to_i
|
60
|
+
|
61
|
+
if options[:wrap_in_collection] != false
|
62
|
+
api = self
|
63
|
+
load_more_call = proc { |page|
|
64
|
+
# pages start at one, so add a page here
|
65
|
+
api.get_students(params, {:wrap_in_collection => false, :page => (page + 1)})
|
66
|
+
}
|
67
|
+
|
68
|
+
ResponseCollection.new({
|
69
|
+
:seed_page => students,
|
70
|
+
:total => total_results,
|
71
|
+
:per_page => options[:per_page],
|
72
|
+
:load_more_call => load_more_call
|
73
|
+
})
|
74
|
+
else
|
75
|
+
students
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_student(student, additional_params = {})
|
80
|
+
raise NotImplementedError, "TSIS does not support creating students"
|
81
|
+
end
|
82
|
+
|
83
|
+
def update_student(student, additional_params = {})
|
84
|
+
raise NotImplementedError, "TSIS does not support updating students"
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_schools(params = {}, options = {})
|
88
|
+
params = self.apply_options(params, options)
|
89
|
+
|
90
|
+
# schools api end point takes page # in the url itself for some reason
|
91
|
+
schools_response_hash = self.request(:get, self.apply_page_to_url('Schools/', options[:page]), params)
|
92
|
+
|
93
|
+
schools = schools_response_hash["Return"].collect{|hsh| School.new(convert_to_school_data(hsh))}
|
94
|
+
total_results = schools_response_hash["TotalCount"].to_i
|
95
|
+
|
96
|
+
if options[:wrap_in_collection] != false
|
97
|
+
api = self
|
98
|
+
load_more_call = proc { |page|
|
99
|
+
# pages start at one, so add a page here
|
100
|
+
api.get_schools(params, {:wrap_in_collection => false, :page => (page + 1)})
|
101
|
+
}
|
102
|
+
|
103
|
+
ResponseCollection.new({
|
104
|
+
:seed_page => schools,
|
105
|
+
:total => total_results,
|
106
|
+
:per_page => options[:per_page],
|
107
|
+
:load_more_call => load_more_call
|
108
|
+
})
|
109
|
+
else
|
110
|
+
schools
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def request(method, path, params = {})
|
115
|
+
uri = "#{self.connection_options[:uri]}/#{path}"
|
116
|
+
body = nil
|
117
|
+
query = URI.encode(params.map{|k,v| "#{k}=#{v}"}.join("&"))
|
118
|
+
if method == :get
|
119
|
+
uri += "?#{query}"
|
120
|
+
else
|
121
|
+
body = query
|
122
|
+
end
|
123
|
+
|
124
|
+
headers = self.headers_for_auth(uri)
|
125
|
+
|
126
|
+
connection = Bright::Connection.new(uri)
|
127
|
+
response = connection.request(method, body, headers)
|
128
|
+
if !response.error?
|
129
|
+
response_hash = JSON.parse(response.body)
|
130
|
+
end
|
131
|
+
response_hash
|
132
|
+
end
|
133
|
+
|
134
|
+
protected
|
135
|
+
|
136
|
+
def map_search_params(params)
|
137
|
+
params = params.dup
|
138
|
+
|
139
|
+
params["studentname"] = params.delete(:name)
|
140
|
+
params["studentname"] ||= "#{params.delete(:last_name)}, #{params.delete(:first_name)} #{params.delete(:middle_name)}".strip
|
141
|
+
params["studentids"] = params.delete(:sis_student_id)
|
142
|
+
|
143
|
+
params = Hash[params.collect do |k,v|
|
144
|
+
if v.is_a?(Array)
|
145
|
+
v = v.join(",")
|
146
|
+
end
|
147
|
+
k = k.to_s.gsub(/[^A-Za-z]/, "").downcase
|
148
|
+
[k,v]
|
149
|
+
end]
|
150
|
+
|
151
|
+
params.reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
|
152
|
+
end
|
153
|
+
|
154
|
+
def convert_to_student_data(attrs)
|
155
|
+
catt = {}
|
156
|
+
if attrs["StudentName"]
|
157
|
+
split_name = attrs["StudentName"].strip.split(",")
|
158
|
+
if split_name[1]
|
159
|
+
split_first_name = split_name[1].to_s.strip.split(" ")
|
160
|
+
if split_first_name.size > 1
|
161
|
+
catt[:first_name] = split_first_name[0...-1].join(" ").strip
|
162
|
+
catt[:middle_name] = split_first_name[-1].strip
|
163
|
+
else
|
164
|
+
catt[:first_name] = split_first_name.first.strip
|
165
|
+
end
|
166
|
+
end
|
167
|
+
catt[:last_name] = split_name[0].to_s.strip
|
168
|
+
else
|
169
|
+
catt[:first_name] = attrs["FirstName"].strip
|
170
|
+
catt[:middle_name] = attrs["MiddleName"].strip
|
171
|
+
catt[:last_name] = (attrs["LastName"] || attrs["SurName"]).strip
|
172
|
+
end
|
173
|
+
|
174
|
+
catt[:state_student_id] = (attrs["StateId"] || attrs["StudentStateId"]).to_s
|
175
|
+
catt[:sis_student_id] = attrs["StudentId"].to_s
|
176
|
+
catt[:api_id] = attrs["StudentId"].to_s
|
177
|
+
catt[:homeless_code] = attrs["HomelessCode"]
|
178
|
+
|
179
|
+
# "Economic\":{\"Description\":\"\",\"EconomicIndicatorId\":\"0\"}
|
180
|
+
|
181
|
+
bd = attrs["BirthDate"] || attrs["StudentBirthDate"]
|
182
|
+
if !(bd.nil? or bd.empty?)
|
183
|
+
begin
|
184
|
+
catt[:birth_date] = Date.strptime(bd, DATE_FORMAT)
|
185
|
+
rescue => e
|
186
|
+
puts "#{e.inspect} #{bd}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
catt[:addresses] = [self.convert_to_address_data(attrs["Address"])] if attrs["Address"]
|
191
|
+
|
192
|
+
catt.reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
|
193
|
+
end
|
194
|
+
|
195
|
+
def convert_to_address_data(attrs)
|
196
|
+
cattrs = {}
|
197
|
+
|
198
|
+
if attrs["Line1"]
|
199
|
+
cattrs[:street] = "#{attrs["Line1"]}\n#{attrs["Line2"]}\n#{attrs["Line3"]}".strip
|
200
|
+
elsif attrs["Address1"]
|
201
|
+
cattrs[:street] = "#{attrs["Address1"]}\n#{attrs["Address2"]}".strip
|
202
|
+
end
|
203
|
+
cattrs[:city] = attrs["City"]
|
204
|
+
cattrs[:state] = attrs["State"]
|
205
|
+
cattrs[:postal_code] = attrs["Zip"]
|
206
|
+
cattrs[:type] = attrs["Type"].to_s.downcase
|
207
|
+
|
208
|
+
cattrs.reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
|
209
|
+
end
|
210
|
+
|
211
|
+
def convert_to_school_data(attrs)
|
212
|
+
cattrs = {}
|
213
|
+
|
214
|
+
cattrs[:name] = attrs["Name"]
|
215
|
+
cattrs[:api_id] = cattrs[:number] = attrs["SchoolId"]
|
216
|
+
cattrs[:address] = convert_to_address_data(attrs)
|
217
|
+
|
218
|
+
cattrs.reject{|k,v| v.respond_to?(:empty?) ? v.empty? : v.nil?}
|
219
|
+
end
|
220
|
+
|
221
|
+
def apply_options(params, options)
|
222
|
+
options[:per_page] = params[:rpp] ||= params.delete(:per_page) || options[:per_page] || 100
|
223
|
+
options[:page] = params.delete(:page) || options[:page]
|
224
|
+
params[:schoolyear] ||= params.delete(:school_year) || options[:school_year]
|
225
|
+
params
|
226
|
+
end
|
227
|
+
|
228
|
+
def apply_page_to_url(url, page)
|
229
|
+
"#{url}#{url[-1] == "/" ? "" : "/"}#{page}"
|
230
|
+
end
|
231
|
+
|
232
|
+
def headers_for_auth(uri)
|
233
|
+
t = Time.now.utc.httpdate
|
234
|
+
string_to_sign = "GET\n#{t}\n#{uri}"
|
235
|
+
|
236
|
+
signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), self.connection_options[:secret], string_to_sign)).strip
|
237
|
+
authorization = "TIES" + " " + self.connection_options[:key] + ":" + signature
|
238
|
+
|
239
|
+
{
|
240
|
+
'Authorization' => authorization,
|
241
|
+
'DistrictNumber' => self.connection_options[:district_id],
|
242
|
+
'ties-date' => t,
|
243
|
+
'Content-Type' => "application/json"
|
244
|
+
}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Bright
|
4
|
+
class Student < Model
|
5
|
+
@attribute_names = [:client_id, :api_id, :first_name, :middle_name, :last_name, :nick_name,
|
6
|
+
:birth_date, :grade, :projected_graduation_year, :gender,
|
7
|
+
:hispanic_ethnicity, :race, :image, :primary_language, :secondary_language,
|
8
|
+
:homeless_code, :frl_status, :sis_student_id,
|
9
|
+
:state_student_id, :last_modified]
|
10
|
+
attr_accessor *@attribute_names
|
11
|
+
|
12
|
+
def self.attribute_names
|
13
|
+
@attribute_names
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO:: map contact info (addresses, email, phone, etc)
|
17
|
+
attr_accessor :enrollment, :addresses
|
18
|
+
|
19
|
+
def initialize(*args)
|
20
|
+
super
|
21
|
+
self.client_id ||= SecureRandom.uuid
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
"#{self.first_name} #{self.middle_name} #{self.last_name}".gsub(/\s+/, " ").strip
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(other)
|
30
|
+
(self.sis_student_id and self.sis_student_id == other.sis_student_id) or
|
31
|
+
(self.state_student_id and self.state_student_id == other.state_student_id) or
|
32
|
+
(self.first_name == other.first_name and self.middle_name == other.middle_name and self.last_name == other.last_name and self.birth_date == other.birth_date)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias id client_id
|
36
|
+
|
37
|
+
def addresses=(array)
|
38
|
+
if array.size <= 0 or array.first.is_a?(Address)
|
39
|
+
@addresses = array
|
40
|
+
@addresses.each{|a| a.student = self}
|
41
|
+
elsif array.first.is_a?(Hash)
|
42
|
+
@addresses = array.collect{|a| Address.new(a)}
|
43
|
+
end
|
44
|
+
@addresses ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
def addresses
|
48
|
+
@addresses ||= []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bright
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arux Software
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httpi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
description: Bright is a simple Student Information System API abstraction library
|
70
|
+
used in and sponsored by FeePay. It is written by Stephen Heuer, Steven Novotny,
|
71
|
+
and contributors. The aim of the project is to abstract as many parts as possible
|
72
|
+
away from the user to offer a consistent interface across all supported Student
|
73
|
+
Information System APIs.
|
74
|
+
email:
|
75
|
+
- sheuer@aruxsoftware.com
|
76
|
+
executables: []
|
77
|
+
extensions: []
|
78
|
+
extra_rdoc_files: []
|
79
|
+
files:
|
80
|
+
- ".gitignore"
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- bright.gemspec
|
86
|
+
- lib/bright.rb
|
87
|
+
- lib/bright/address.rb
|
88
|
+
- lib/bright/connection.rb
|
89
|
+
- lib/bright/enrollment.rb
|
90
|
+
- lib/bright/errors.rb
|
91
|
+
- lib/bright/model.rb
|
92
|
+
- lib/bright/response_collection.rb
|
93
|
+
- lib/bright/school.rb
|
94
|
+
- lib/bright/sis_apis/aeries.rb
|
95
|
+
- lib/bright/sis_apis/base.rb
|
96
|
+
- lib/bright/sis_apis/infinite_campus.rb
|
97
|
+
- lib/bright/sis_apis/power_school.rb
|
98
|
+
- lib/bright/sis_apis/synergy.rb
|
99
|
+
- lib/bright/sis_apis/tsis.rb
|
100
|
+
- lib/bright/student.rb
|
101
|
+
- lib/bright/version.rb
|
102
|
+
homepage: ''
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.2.2
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: Framework and tools for dealing with Student Information Systems
|
126
|
+
test_files: []
|