old_school 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +4 -0
- data/lib/old_school.rb +19 -0
- data/lib/old_school/api.rb +209 -0
- data/lib/old_school/api/url_factory.rb +181 -0
- data/lib/old_school/api/utils.rb +198 -0
- data/lib/old_school/core_ext.rb +6 -0
- data/lib/old_school/core_ext/object.rb +23 -0
- data/lib/old_school/csv.rb +7 -0
- data/lib/old_school/csv/content_row.rb +38 -0
- data/lib/old_school/csv/header_row.rb +34 -0
- data/lib/old_school/models/assignment.rb +26 -0
- data/lib/old_school/models/assignment_score.rb +17 -0
- data/lib/old_school/models/contact_info.rb +15 -0
- data/lib/old_school/models/course.rb +12 -0
- data/lib/old_school/models/demographics.rb +13 -0
- data/lib/old_school/models/name.rb +11 -0
- data/lib/old_school/models/school.rb +17 -0
- data/lib/old_school/models/section.rb +14 -0
- data/lib/old_school/models/section_enrollment.rb +25 -0
- data/lib/old_school/models/staff.rb +26 -0
- data/lib/old_school/models/student.rb +27 -0
- data/lib/old_school/models/term.rb +15 -0
- data/lib/old_school/version.rb +3 -0
- data/lib/old_school/virtus_ext.rb +7 -0
- data/lib/old_school/virtus_ext/class_methods.rb +18 -0
- data/lib/old_school/virtus_ext/model.rb +20 -0
- data/old_school.gemspec +27 -0
- metadata +184 -0
data/README.md
ADDED
data/lib/old_school.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
|
3
|
+
require 'old_school/virtus_ext'
|
4
|
+
require 'old_school/core_ext'
|
5
|
+
require 'old_school/csv'
|
6
|
+
|
7
|
+
require 'old_school/api'
|
8
|
+
require 'old_school/models/student'
|
9
|
+
require 'old_school/models/name'
|
10
|
+
require 'old_school/models/contact_info'
|
11
|
+
require 'old_school/models/demographics'
|
12
|
+
require 'old_school/models/school'
|
13
|
+
require 'old_school/models/section'
|
14
|
+
require 'old_school/models/course'
|
15
|
+
require 'old_school/models/term'
|
16
|
+
require 'old_school/models/assignment'
|
17
|
+
require 'old_school/models/assignment_score'
|
18
|
+
require 'old_school/models/section_enrollment'
|
19
|
+
require 'old_school/models/staff'
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require_relative 'api/utils'
|
2
|
+
require_relative 'api/url_factory'
|
3
|
+
|
4
|
+
module OldSchool
|
5
|
+
class API
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
def initialize(host, id, secret)
|
9
|
+
@url_factory = URLFactory.new(host)
|
10
|
+
self.id = id
|
11
|
+
self.secret = secret
|
12
|
+
self.token = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
#Assignment Resource
|
16
|
+
def get_assignment(assignment_id)
|
17
|
+
hash_from_response get(@url_factory.assignment_url(assignment_id)), 'assignment'
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_assignments(assignment_ids)
|
21
|
+
get_many assignment_ids, 'assignment', ->(id){ @url_factory.assignment_url(id) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_assignment(assignment_id, assignment)
|
25
|
+
put @url_factory.assignment_url(assignment_id), {assignment: assignment}.to_json
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_assignment(assignment_id)
|
29
|
+
delete @url_factory.assignment_url(assignment_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_student_assignment_score(assignment_id, student_id, assignment_score)
|
33
|
+
put @url_factory.assignment_student_score_url(assignment_id, student_id), {assignment_score: assignment_score}.to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_student_assignment_score(assignment_id, student_id)
|
37
|
+
hash_from_response get(@url_factory.assignment_student_score_url(assignment_id, student_id)), 'assignment_score'
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_student_assignment_scores(assignment_id, student_ids)
|
41
|
+
get_many student_ids, 'assignment_score', ->(id){ @url_factory.assignment_student_score_url(assignment_id, id) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_multiple_student_assignment_scores(assignment_id, assignment_scores)
|
45
|
+
put @url_factory.assignment_score_url(assignment_id), {assignment_scores: {assignment_score: assignment_scores}}.to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_multiple_student_assignment_scores(assignment_id)
|
49
|
+
hash_from_response get(@url_factory.assignment_score_url(assignment_id)), %w(assignment_scores assignment_score)
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete_assignment_score(assignment_id, student_id)
|
53
|
+
delete @url_factory.assignment_student_score_url(assignment_id, student_id)
|
54
|
+
end
|
55
|
+
|
56
|
+
#School Resource
|
57
|
+
def get_students_by_school(school_id)
|
58
|
+
num_students = get_students_count_by_school(school_id)
|
59
|
+
results = get_with_pagination_url('student', num_students) {|page|
|
60
|
+
@url_factory.school_students_url(school_id, {
|
61
|
+
page: page,
|
62
|
+
expansions: 'contact_info,demographics'
|
63
|
+
})
|
64
|
+
}
|
65
|
+
results.map { |item| OldSchool::Student.new({school_id: school_id}.merge(item)) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_students_count_by_school(school_id)
|
69
|
+
hash_from_response get(@url_factory.school_student_count_url(school_id)), %w(resource count)
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_staff_by_school(school_id)
|
73
|
+
num_staff = get_staff_count_by_school(school_id)
|
74
|
+
results = get_with_pagination_url('staff', num_staff) {|page|
|
75
|
+
@url_factory.school_staff_index_url(school_id, {page: page})
|
76
|
+
}
|
77
|
+
results.map { |item|
|
78
|
+
attributes = {school_id: school_id}.merge(item)
|
79
|
+
OldSchool::Staff.new(attributes)
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_staff_count_by_school(school_id)
|
84
|
+
hash_from_response get(@url_factory.school_staff_count_url(school_id)), %w(resource count)
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_sections_by_school(school_id, start_year = nil)
|
88
|
+
num_sections = get_section_count_by_school(school_id, start_year)
|
89
|
+
results = get_with_pagination_url('section', num_sections) { |page|
|
90
|
+
query = {page: page}
|
91
|
+
query["q"] = "term.start_year==#{ start_year }" unless start_year.nil?
|
92
|
+
@url_factory.school_sections_url(school_id, query)
|
93
|
+
}
|
94
|
+
results.map { |item| OldSchool::Section.new({school_id: school_id}.merge(item)) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_section_count_by_school(school_id, start_year = nil)
|
98
|
+
query = {}
|
99
|
+
query["q"] = "term.start_year==#{ start_year }" unless start_year.nil?
|
100
|
+
url = @url_factory.school_section_count_url(school_id, query)
|
101
|
+
hash_from_response get(url), %w(resource count)
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_terms_by_school(school_id, start_year = nil)
|
105
|
+
num_terms = get_terms_count_by_school(school_id, start_year)
|
106
|
+
results = get_with_pagination_url('term', num_terms) do |page|
|
107
|
+
query = {page: page}
|
108
|
+
query["q"] = "start_year==#{ start_year }" unless start_year.nil?
|
109
|
+
@url_factory.school_terms_url(school_id, query)
|
110
|
+
end
|
111
|
+
results.map { |item| OldSchool::Term.new({school_id: school_id}.merge(item)) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_terms_count_by_school(school_id, start_year = nil)
|
115
|
+
query = {}
|
116
|
+
query["q"] = "start_year==#{ start_year }" unless start_year.nil?
|
117
|
+
url = @url_factory.school_term_count_url(school_id, query)
|
118
|
+
hash_from_response get(url), %w(resource count)
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_courses_by_school(school_id)
|
122
|
+
num_courses = get_course_count_by_school(school_id)
|
123
|
+
results = get_with_pagination_url('course', num_courses) {|page|
|
124
|
+
@url_factory.school_courses_url(school_id, {page: page})
|
125
|
+
}
|
126
|
+
results.map { |item| OldSchool::Course.new({school_id: school_id}.merge(item)) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_course_count_by_school(school_id)
|
130
|
+
hash_from_response get(@url_factory.school_course_count_url(school_id)), %w(resource count)
|
131
|
+
end
|
132
|
+
|
133
|
+
def get_schools_in_current_district
|
134
|
+
num_schools = get_school_count_in_current_district
|
135
|
+
results = get_with_pagination_url('school', num_schools) {|page|
|
136
|
+
@url_factory.district_schools_url({
|
137
|
+
page: page,
|
138
|
+
})
|
139
|
+
}
|
140
|
+
results.map { |item| OldSchool::School.new(item) }
|
141
|
+
end
|
142
|
+
|
143
|
+
def get_school_count_in_current_district
|
144
|
+
hash_from_response get(@url_factory.district_school_count_url), %w(resource count)
|
145
|
+
end
|
146
|
+
|
147
|
+
def get_school_by_id(school_id)
|
148
|
+
hash_from_response get(@url_factory.school_url(school_id)), 'school'
|
149
|
+
end
|
150
|
+
|
151
|
+
def get_current_district
|
152
|
+
hash_from_response get(@url_factory.district_url), 'district'
|
153
|
+
end
|
154
|
+
|
155
|
+
#staff resources
|
156
|
+
def get_staff_by_id(staff_id)
|
157
|
+
hash_from_response get(@url_factory.staff_url(staff_id)), 'staff'
|
158
|
+
end
|
159
|
+
|
160
|
+
#student resource
|
161
|
+
def get_student_by_id(student_id)
|
162
|
+
hash_from_response get(@url_factory.student_url(student_id)), 'student'
|
163
|
+
end
|
164
|
+
|
165
|
+
#term resource
|
166
|
+
def get_term_by_id(term_id)
|
167
|
+
hash_from_response get(@url_factory.term_url(term_id)), 'term'
|
168
|
+
end
|
169
|
+
|
170
|
+
#Section Enrollment Resource
|
171
|
+
def get_section_enrollment_by_id(section_enrollment_id)
|
172
|
+
hash_from_response get(@url_factory.section_enrollment_url(section_enrollment_id)), 'section_enrollment'
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_section_by_id(section_id)
|
176
|
+
hash_from_response get(@url_factory.section_url(section_id)),'section'
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_section_enrollments_by_section_id(section_id)
|
180
|
+
results = collection_from_response get(@url_factory.section_section_enrollments_url(section_id)), %w(section_enrollments section_enrollment)
|
181
|
+
results.map { |item| OldSchool::SectionEnrollment.new(item) }
|
182
|
+
end
|
183
|
+
|
184
|
+
#Section Resource
|
185
|
+
def add_assignment_to_section(section_id, assignment)
|
186
|
+
url = @url_factory.section_assignments_url(section_id)
|
187
|
+
data = { assignment: hash_without_nil_values(assignment) }.to_json
|
188
|
+
response = post url, data
|
189
|
+
case response.code
|
190
|
+
when 201
|
191
|
+
assignment_id_from_response response
|
192
|
+
else
|
193
|
+
code, msg = parse_error(response)
|
194
|
+
case error["errors"]["code"]
|
195
|
+
when "MISSING_DATE_ASSIGNMENT_DUE"
|
196
|
+
raise MissingRequiredField, msg
|
197
|
+
when "ASSIGNMENT_NAME_OR_ABBREVIATION_IN_USE"
|
198
|
+
raise AlreadyExists, msg
|
199
|
+
else
|
200
|
+
raise ResponseError, msg
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def assignment_id_from_response(response)
|
206
|
+
response.headers["Location"].split(/[\s\/]/).last
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module OldSchool
|
4
|
+
class API
|
5
|
+
class URLFactory
|
6
|
+
PTG_V2_NAMESPACE = '/powerschool-ptg-api/v2'
|
7
|
+
|
8
|
+
WS_V1_NAMESPACE = '/ws/v1'
|
9
|
+
WS_V1_RESOURCES = %w{school staff student term section_enrollment section}
|
10
|
+
WS_V1_RESOURCES_NESTED_IN_DISTRICT = %w{school}
|
11
|
+
WS_V1_RESOURCES_NESTED_IN_SCHOOL = %w{student section term course}
|
12
|
+
|
13
|
+
def initialize(host_with_protocol)
|
14
|
+
@base_uri = URI.parse(host_with_protocol)
|
15
|
+
if @base_uri.scheme.nil?
|
16
|
+
@base_uri = URI.parse('https://' + host_with_protocol)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def base_url
|
21
|
+
@base_uri.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def access_token_url
|
25
|
+
merge_with_base '/oauth/access_token'
|
26
|
+
end
|
27
|
+
|
28
|
+
def assignment_url(id)
|
29
|
+
merge_with_base ptg_v2_resource_path('assignment', id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def assignment_score_url(id)
|
33
|
+
merge_with_base assignment_score_path(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def assignment_student_score_url(assignment_id, student_id)
|
37
|
+
merge_with_base assignment_student_score_path(assignment_id, student_id)
|
38
|
+
end
|
39
|
+
|
40
|
+
def district_url
|
41
|
+
merge_with_base ws_v1_resource_path('district')
|
42
|
+
end
|
43
|
+
|
44
|
+
WS_V1_RESOURCES_NESTED_IN_DISTRICT.each do |resource|
|
45
|
+
define_method "district_#{ resource }_count_url" do |query = {}|
|
46
|
+
merge_with_base district_resource_uri(resource, 'count', query)
|
47
|
+
end
|
48
|
+
|
49
|
+
define_method "district_#{ resource }s_url" do |query = {}|
|
50
|
+
merge_with_base district_resource_uri(resource, query)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
WS_V1_RESOURCES.each do |resource|
|
55
|
+
define_method "#{ resource }_url" do |id|
|
56
|
+
merge_with_base ws_v1_resource_path(resource, id)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def metadata_url
|
61
|
+
merge_with_base ws_v1_resource_path('metadata')
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge_with_base(path_or_uri)
|
65
|
+
new_uri = @base_uri.dup
|
66
|
+
if String === path_or_uri
|
67
|
+
new_uri.path += path_or_uri
|
68
|
+
new_uri.path.gsub!(/\/\//, '/')
|
69
|
+
else
|
70
|
+
new_uri.path += path_or_uri.path
|
71
|
+
new_uri.query = path_or_uri.query
|
72
|
+
end
|
73
|
+
new_uri.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def section_assignments_url(section_id)
|
77
|
+
merge_with_base section_assignments_path(section_id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def section_section_enrollments_url(section_id)
|
81
|
+
merge_with_base section_section_enrollments_path(section_id)
|
82
|
+
end
|
83
|
+
|
84
|
+
def school_url(id)
|
85
|
+
merge_with_base school_resource_uri(id)
|
86
|
+
end
|
87
|
+
|
88
|
+
WS_V1_RESOURCES_NESTED_IN_SCHOOL.each do |resource|
|
89
|
+
define_method "school_#{ resource }_count_url" do |id, query = {}|
|
90
|
+
merge_with_base school_resource_uri(id, resource, 'count', query)
|
91
|
+
end
|
92
|
+
|
93
|
+
define_method "school_#{ resource }s_url" do |id, query = {}|
|
94
|
+
merge_with_base school_resource_uri(id, resource, query)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# because staffs isn't the correct pluralization and we aren't
|
99
|
+
# going to pull in active support for this pair of methods
|
100
|
+
def school_staff_count_url(id, query = {})
|
101
|
+
merge_with_base school_resource_uri(id, 'staff', 'count', query)
|
102
|
+
end
|
103
|
+
|
104
|
+
def school_staff_index_url(id, query = {})
|
105
|
+
merge_with_base school_resource_uri(id, 'staff', query)
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def assignment_score_path(id)
|
111
|
+
tuple_to_path(ptg_v2_resource_path_tuple('assignment',id) + ['score'])
|
112
|
+
end
|
113
|
+
|
114
|
+
def assignment_student_path_tuple(assignment_id, student_id)
|
115
|
+
ptg_v2_resource_path_tuple('assignment',assignment_id) + resource_path_tuple('student', student_id)
|
116
|
+
end
|
117
|
+
|
118
|
+
def assignment_student_score_path_tuple(assignment_id, student_id)
|
119
|
+
assignment_student_path_tuple(assignment_id, student_id) + ['score']
|
120
|
+
end
|
121
|
+
|
122
|
+
def assignment_student_score_path(assignment_id, student_id)
|
123
|
+
tuple_to_path(assignment_student_score_path_tuple(assignment_id, student_id))
|
124
|
+
end
|
125
|
+
|
126
|
+
def district_resource_uri(*nested_resources_and_query)
|
127
|
+
query = nested_resources_and_query.pop if Hash === nested_resources_and_query.last
|
128
|
+
URI::Generic.build({}).tap {|uri|
|
129
|
+
uri.path = tuple_to_path(ws_v1_resource_path_tuple('district') + nested_resources_and_query)
|
130
|
+
uri.query = encode_query(query) unless query.nil? || query.empty?
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
def encode_query(enumerable)
|
135
|
+
enumerable.map{|k, v|
|
136
|
+
[k.to_s, v.to_s].join('=')
|
137
|
+
}.join('&')
|
138
|
+
end
|
139
|
+
|
140
|
+
def ptg_v2_resource_path_tuple(resource, id)
|
141
|
+
resource_path_tuple(resource, id).unshift(PTG_V2_NAMESPACE)
|
142
|
+
end
|
143
|
+
|
144
|
+
def ptg_v2_resource_path(resource, id)
|
145
|
+
tuple_to_path ptg_v2_resource_path_tuple(resource, id)
|
146
|
+
end
|
147
|
+
|
148
|
+
def resource_path_tuple(resource, id)
|
149
|
+
[resource, id].compact
|
150
|
+
end
|
151
|
+
|
152
|
+
def section_assignments_path(section_id)
|
153
|
+
tuple_to_path(ptg_v2_resource_path_tuple('section', section_id) + resource_path_tuple('assignment', nil))
|
154
|
+
end
|
155
|
+
|
156
|
+
def section_section_enrollments_path(section_id)
|
157
|
+
tuple_to_path(ws_v1_resource_path_tuple('section', section_id) + resource_path_tuple('section_enrollment', nil))
|
158
|
+
end
|
159
|
+
|
160
|
+
def school_resource_uri(id, *nested_resources_and_query)
|
161
|
+
query = nested_resources_and_query.pop if Hash === nested_resources_and_query.last
|
162
|
+
URI::Generic.build({}).tap {|uri|
|
163
|
+
uri.path = tuple_to_path(ws_v1_resource_path_tuple('school', id) + nested_resources_and_query)
|
164
|
+
uri.query = encode_query(query) unless query.nil? || query.empty?
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
def tuple_to_path(tuple)
|
169
|
+
tuple.join('/')
|
170
|
+
end
|
171
|
+
|
172
|
+
def ws_v1_resource_path_tuple(resource, id = nil)
|
173
|
+
resource_path_tuple(resource, id).unshift(WS_V1_NAMESPACE)
|
174
|
+
end
|
175
|
+
|
176
|
+
def ws_v1_resource_path(resource, id = nil)
|
177
|
+
tuple_to_path ws_v1_resource_path_tuple(resource, id)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module OldSchool
|
5
|
+
|
6
|
+
class ResponseError < StandardError; end
|
7
|
+
class MissingRequiredField < ResponseError; end
|
8
|
+
class AlreadyExists < ResponseError; end
|
9
|
+
|
10
|
+
class API
|
11
|
+
module Utils
|
12
|
+
attr_accessor :id, :secret, :url_factory
|
13
|
+
|
14
|
+
def resource_metadata
|
15
|
+
@resource_metadata ||= hash_from_response(get(@url_factory.metadata_url),'metadata')
|
16
|
+
end
|
17
|
+
|
18
|
+
def token
|
19
|
+
return @token unless @token.nil?
|
20
|
+
response = Typhoeus.post(
|
21
|
+
@url_factory.access_token_url,
|
22
|
+
userpwd: "#{@id}:#{@secret}",
|
23
|
+
params: {
|
24
|
+
grant_type: 'client_credentials'
|
25
|
+
})
|
26
|
+
|
27
|
+
@token = hash_from_response(response, 'access_token')
|
28
|
+
end
|
29
|
+
|
30
|
+
def token=(token)
|
31
|
+
@token = token
|
32
|
+
end
|
33
|
+
|
34
|
+
def collection_from_response(response, keys = nil)
|
35
|
+
as_array hash_from_response!(response, keys) { [] }
|
36
|
+
end
|
37
|
+
|
38
|
+
def hash_from_response(response, keys = nil)
|
39
|
+
hash_from_response!(response, keys) do |body, key|
|
40
|
+
raise ResponseError, "\"#{key}\" key not in response object:\n #{response.body}:\nfrom original:\n#{response.inspect.to_s}" unless body.has_key?(key)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def hash_from_response!(response, keys)
|
45
|
+
keys = as_array keys
|
46
|
+
validate_response response
|
47
|
+
body = parse_json response
|
48
|
+
|
49
|
+
while keys.size > 0
|
50
|
+
key = keys.shift
|
51
|
+
if body.has_key?(key)
|
52
|
+
body = body[key]
|
53
|
+
else
|
54
|
+
return yield(body, key) if block_given?
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
body
|
60
|
+
end
|
61
|
+
|
62
|
+
def hash_without_nil_values(hash)
|
63
|
+
hash.to_hash.delete_if{ |k,v| v.nil? }
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_json(response)
|
67
|
+
begin
|
68
|
+
body = JSON.parse(response.body)
|
69
|
+
rescue JSON::ParserError
|
70
|
+
raise ResponseError, "Could not parse body as JSON: #{response.body}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_error(response)
|
75
|
+
error = parse_json(response)["errorMessage"]
|
76
|
+
begin
|
77
|
+
msg = "#{error["message"]} ("
|
78
|
+
msg += "#{error["errors"]["resource"]}#{error["errors"]["field"] ? "." : ""}#{error["errors"]["field"]}"
|
79
|
+
msg +=": #{error["errors"]["code"]})"
|
80
|
+
return [error["errors"]["code"], msg]
|
81
|
+
rescue
|
82
|
+
return ["ERROR", response.body]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate_response(response)
|
87
|
+
if response.success?
|
88
|
+
return
|
89
|
+
elsif response.timed_out?
|
90
|
+
raise ResponseError, 'HTTP request timed out'
|
91
|
+
elsif response.code == 0
|
92
|
+
raise ResponseError, response.return_message
|
93
|
+
else
|
94
|
+
reason = extract_from_response_options(response, :response_headers)
|
95
|
+
raise ResponseError, "HTTP request failed: #{response.code}:\n#{reason}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def extract_from_response_options(response, key)
|
100
|
+
begin
|
101
|
+
return response.options[key]
|
102
|
+
rescue
|
103
|
+
return nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def default_headers
|
108
|
+
{
|
109
|
+
Authorization: "Bearer #{token}",
|
110
|
+
Accept: 'application/JSON',
|
111
|
+
'Content-Type'=>'application/JSON'
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def get(uri)
|
116
|
+
Typhoeus.get(
|
117
|
+
make_sane_uri(uri),
|
118
|
+
headers: default_headers
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_wait(uri)
|
123
|
+
Typhoeus::Request.new(
|
124
|
+
make_sane_uri(uri),
|
125
|
+
method: :get,
|
126
|
+
headers: default_headers)
|
127
|
+
end
|
128
|
+
|
129
|
+
def put(uri, body)
|
130
|
+
Typhoeus.put(
|
131
|
+
make_sane_uri(uri),
|
132
|
+
headers: default_headers,
|
133
|
+
body: body
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
def delete(uri)
|
138
|
+
Typhoeus.delete(
|
139
|
+
make_sane_uri(uri),
|
140
|
+
headers: default_headers
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
def post(uri, body)
|
145
|
+
Typhoeus.post(
|
146
|
+
make_sane_uri(uri),
|
147
|
+
headers: default_headers,
|
148
|
+
body: body
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
def make_sane_uri(uri)
|
153
|
+
if /\Ahttps?/.match(uri)
|
154
|
+
uri
|
155
|
+
else
|
156
|
+
@url_factory.merge_with_base(uri)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_with_pagination_url(resource, num_items, &uri_proc)
|
161
|
+
return [] if num_items == 0 #if paginating for zero items, just return an empty list
|
162
|
+
|
163
|
+
#calculate page size based on
|
164
|
+
page_size = resource_metadata["#{resource}_max_page_size"]
|
165
|
+
pages = (num_items - 1) / page_size + 1
|
166
|
+
|
167
|
+
results = get_many((1..pages), ["#{resource}s", resource], uri_proc)
|
168
|
+
results.values.flatten
|
169
|
+
end
|
170
|
+
|
171
|
+
def as_array(item)
|
172
|
+
return item.clone if item.is_a?(Array)
|
173
|
+
return [] if item.nil?
|
174
|
+
return [item]
|
175
|
+
end
|
176
|
+
|
177
|
+
def get_many(items, keys, uri_proc, complete_proc = nil)
|
178
|
+
hydra = Typhoeus::Hydra.hydra
|
179
|
+
requests = []
|
180
|
+
items.each do |item|
|
181
|
+
uri = uri_proc.call(item)
|
182
|
+
request = get_wait(uri)
|
183
|
+
request.on_complete {|response| complete_proc.call(response)} unless complete_proc.nil?
|
184
|
+
requests << [item, request]
|
185
|
+
hydra.queue request
|
186
|
+
end
|
187
|
+
|
188
|
+
hydra.run
|
189
|
+
|
190
|
+
responses = {}
|
191
|
+
requests.each do |request|
|
192
|
+
responses[request[0]] = hash_from_response(request[1].response, keys)
|
193
|
+
end
|
194
|
+
responses
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'virtus/model'
|
2
|
+
|
3
|
+
module OldSchool
|
4
|
+
module CoreExt
|
5
|
+
module Object
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
def virtus_model?
|
11
|
+
self.class.virtus_model?
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def virtus_model?
|
16
|
+
ancestors.include?(Virtus::Model::Core)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Object.send(:include, OldSchool::CoreExt::Object)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module OldSchool
|
2
|
+
module CSV
|
3
|
+
class ContentRow
|
4
|
+
def self.for(model)
|
5
|
+
new(model).to_csv
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(model)
|
9
|
+
@model = model
|
10
|
+
@public_attributes = model.publicly_readable_attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_csv
|
14
|
+
@public_attributes.map{|attribute|
|
15
|
+
values_for(attribute)
|
16
|
+
}.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def values_for(attribute)
|
22
|
+
primitive = attribute.primitive
|
23
|
+
value = @model.public_send(attribute.name)
|
24
|
+
raise 'TODO: figure out how to handle Array and Hash attributes in CSV conversion' if Array == primitive || Hash == primitive
|
25
|
+
|
26
|
+
primitive.virtus_model? ? values_from_nested_model(value, primitive) : value
|
27
|
+
end
|
28
|
+
|
29
|
+
def values_from_nested_model(value, primitive)
|
30
|
+
if value.nil?
|
31
|
+
Array.new(primitive.publicly_readable_attributes.length)
|
32
|
+
else
|
33
|
+
ContentRow.for(value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module OldSchool
|
2
|
+
module CSV
|
3
|
+
class HeaderRow
|
4
|
+
def self.for(klass)
|
5
|
+
new(klass).to_csv
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(klass)
|
9
|
+
@klass = klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_csv
|
13
|
+
@klass.publicly_readable_attributes.map{|attribute|
|
14
|
+
column_headers_for(attribute)
|
15
|
+
}.flatten
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def column_headers_for(attribute)
|
21
|
+
primitive = attribute.primitive
|
22
|
+
raise 'TODO: figure out how to handle Array and Hash attributes in CSV conversion' if Array == primitive || Hash == primitive
|
23
|
+
|
24
|
+
primitive.virtus_model? ? columns_for_nested_model(attribute) : attribute.name.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def columns_for_nested_model(attribute)
|
28
|
+
HeaderRow.for(attribute.primitive).map{|nested_header|
|
29
|
+
[attribute.name, nested_header].join('.')
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module OldSchool
|
2
|
+
class Assignment
|
3
|
+
|
4
|
+
include Virtus.model
|
5
|
+
|
6
|
+
attribute :id, Integer
|
7
|
+
attribute :name, String
|
8
|
+
attribute :abbreviation, String
|
9
|
+
attribute :description, String
|
10
|
+
attribute :date_assignment_due, Date #YYYY-MM-DD
|
11
|
+
attribute :category, String
|
12
|
+
attribute :extra_credit_points, Integer
|
13
|
+
attribute :include_final_grades, Boolean
|
14
|
+
attribute :points_possible, Integer
|
15
|
+
attribute :publish_scores, Boolean
|
16
|
+
attribute :publish_state, String # PUBLISH_NEVER,
|
17
|
+
# PUBLISH_IMMEDIATELY, PUBLISH_ON_DUE_DATE,
|
18
|
+
# PUBLISH_ON_DUE_DATE, PUBLISH_ON_SPECIFIC_DATE,
|
19
|
+
# PUBLISH_DAYS_BEFORE_DUE
|
20
|
+
attribute :publish_days_before_due, Integer
|
21
|
+
attribute :publish_specific_date, Date #YYYY-MM-DD
|
22
|
+
attribute :scoring_type, String #POINTS, PERCENTAGE, LETTERGRADE
|
23
|
+
attribute :weight, Float
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OldSchool
|
2
|
+
class AssignmentScore
|
3
|
+
|
4
|
+
include Virtus.model
|
5
|
+
|
6
|
+
attribute :student_id, String
|
7
|
+
attribute :score_entered, String
|
8
|
+
attribute :points_possible, Integer
|
9
|
+
attribute :comment, String
|
10
|
+
attribute :collected, Boolean
|
11
|
+
attribute :missing, Boolean
|
12
|
+
attribute :exempt, Boolean
|
13
|
+
attribute :turned_in_late, Boolean
|
14
|
+
attribute :scoring_type, String # POINTS, PERCENTAGE, LETTERGRADE
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module OldSchool
|
2
|
+
class Demographics
|
3
|
+
|
4
|
+
include Virtus.model
|
5
|
+
|
6
|
+
attribute :gender, String
|
7
|
+
attribute :birth_date, DateTime
|
8
|
+
attribute :district_entry_date, DateTime
|
9
|
+
attribute :project_graduation_year, String
|
10
|
+
attribute :ssn, String
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module OldSchool
|
3
|
+
class School
|
4
|
+
|
5
|
+
include Virtus.model
|
6
|
+
|
7
|
+
attribute :id, Integer
|
8
|
+
attribute :name, String
|
9
|
+
attribute :school_number, String
|
10
|
+
attribute :state_province_id, String
|
11
|
+
attribute :low_grade, Integer
|
12
|
+
attribute :high_grade, Integer
|
13
|
+
attribute :alternate_school_number, Integer
|
14
|
+
#attribute :assistant_principal, AssistantPrincipal
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module OldSchool
|
2
|
+
class Section
|
3
|
+
|
4
|
+
include Virtus.model
|
5
|
+
|
6
|
+
attribute :id, Integer
|
7
|
+
attribute :school_id, Integer
|
8
|
+
attribute :course_id, Integer
|
9
|
+
attribute :term_id, Integer
|
10
|
+
attribute :section_number, Integer
|
11
|
+
attribute :expression, String
|
12
|
+
attribute :staff_id, Integer
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module OldSchool
|
4
|
+
class SectionEnrollment
|
5
|
+
|
6
|
+
include Virtus.model
|
7
|
+
|
8
|
+
attribute :id, Integer
|
9
|
+
attribute :section_id, Integer
|
10
|
+
attribute :student_id, Integer
|
11
|
+
attribute :entry_date, DateTime
|
12
|
+
attribute :exit_date, DateTime
|
13
|
+
attribute :dropped, Boolean
|
14
|
+
|
15
|
+
def future?
|
16
|
+
return true if entry_date > Date.today
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
def past?
|
21
|
+
return true if exit_date > Date.today
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'name'
|
2
|
+
|
3
|
+
module OldSchool
|
4
|
+
class Staff
|
5
|
+
|
6
|
+
include Virtus.model
|
7
|
+
|
8
|
+
attribute :id, Integer
|
9
|
+
attribute :local_id, String
|
10
|
+
attribute :admin_username, String
|
11
|
+
attribute :teacher_username, String
|
12
|
+
attribute :name, Name
|
13
|
+
#TODO attribute :phones, Phones
|
14
|
+
#TODO attribute :schedule_setup, String
|
15
|
+
#TODO attribute :school_enrollment, String
|
16
|
+
|
17
|
+
attribute :school_id, Integer
|
18
|
+
def login_id
|
19
|
+
if !self.admin_username && !self.teacher_username
|
20
|
+
return nil
|
21
|
+
end
|
22
|
+
return self.teacher_username if self.teacher_username != nil
|
23
|
+
return self.admin_username
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'name'
|
2
|
+
require_relative 'contact_info'
|
3
|
+
require_relative 'demographics'
|
4
|
+
|
5
|
+
module OldSchool
|
6
|
+
class Student
|
7
|
+
|
8
|
+
include Virtus.model
|
9
|
+
|
10
|
+
attribute :id, Integer
|
11
|
+
attribute :local_id, String
|
12
|
+
attribute :state_province_id, String
|
13
|
+
attribute :student_username, String
|
14
|
+
#TODO attribute :addresses, Address
|
15
|
+
#TODO attribute :alerts, Alerts
|
16
|
+
#TODO attribute :contact, Contact
|
17
|
+
attribute :contact_info, ContactInfo # TODO figure out blank string issues
|
18
|
+
attribute :demographics, Demographics
|
19
|
+
#TODO attribute :ethnicity_race, EthnicityRace
|
20
|
+
attribute :name, Name
|
21
|
+
#TODO attribute :phones, Phones
|
22
|
+
#TODO attribute :schedule_setup, String
|
23
|
+
#TODO attribute :school_enrollment, String
|
24
|
+
|
25
|
+
attribute :school_id, Integer
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module OldSchool
|
2
|
+
class Term
|
3
|
+
|
4
|
+
include Virtus.model
|
5
|
+
|
6
|
+
attribute :id, Integer
|
7
|
+
attribute :school_id, Integer
|
8
|
+
attribute :start_year, Integer
|
9
|
+
attribute :portion, Integer
|
10
|
+
attribute :start_date, DateTime
|
11
|
+
attribute :end_date, DateTime
|
12
|
+
attribute :abbreviation, String
|
13
|
+
attribute :name, String
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
require 'old_school/csv'
|
3
|
+
|
4
|
+
module OldSchool
|
5
|
+
module VirtusExt
|
6
|
+
module ClassMethods
|
7
|
+
def publicly_readable_attributes
|
8
|
+
@attribute_set.select(&:public_reader?)
|
9
|
+
end
|
10
|
+
|
11
|
+
def csv_header
|
12
|
+
OldSchool::CSV::HeaderRow.for(self)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Virtus::ClassMethods.send(:include, OldSchool::VirtusExt::ClassMethods)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
require 'old_school/csv'
|
3
|
+
|
4
|
+
module OldSchool
|
5
|
+
module VirtusExt
|
6
|
+
module Model
|
7
|
+
module Core
|
8
|
+
def publicly_readable_attributes
|
9
|
+
self.class.publicly_readable_attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_csv
|
13
|
+
OldSchool::CSV::ContentRow.for(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Virtus::Model::Core.send(:include, OldSchool::VirtusExt::Model::Core)
|
data/old_school.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path('../lib/old_school/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'old_school'
|
5
|
+
gem.date = '2013-01-10'
|
6
|
+
gem.summary = 'Ruby Gem for a Powerful SIS'
|
7
|
+
gem.description = 'Provides an interface to work with a Powerful SIS REST API'
|
8
|
+
gem.authors = ['kaiged']
|
9
|
+
gem.email = 'kaiged@gmail.com'
|
10
|
+
gem.homepage = 'https://github.com/kaiged/old_school'
|
11
|
+
|
12
|
+
gem.version = OldSchool::VERSION
|
13
|
+
|
14
|
+
gem.files = %w[old_school.gemspec README.md]
|
15
|
+
gem.files += Dir.glob("lib/**/*")
|
16
|
+
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.add_dependency 'typhoeus', '>= 0.6.7'
|
20
|
+
gem.add_dependency 'json', '>=1.8.1'
|
21
|
+
gem.add_dependency 'virtus'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'debugger'
|
24
|
+
gem.add_development_dependency 'rspec'
|
25
|
+
gem.add_development_dependency 'guard'
|
26
|
+
gem.add_development_dependency 'guard-rspec'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: old_school
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- kaiged
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: typhoeus
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.6.7
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.6.7
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.8.1
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.8.1
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: virtus
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: debugger
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: guard
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: guard-rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Provides an interface to work with a Powerful SIS REST API
|
127
|
+
email: kaiged@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- old_school.gemspec
|
133
|
+
- README.md
|
134
|
+
- lib/old_school/api/url_factory.rb
|
135
|
+
- lib/old_school/api/utils.rb
|
136
|
+
- lib/old_school/api.rb
|
137
|
+
- lib/old_school/core_ext/object.rb
|
138
|
+
- lib/old_school/core_ext.rb
|
139
|
+
- lib/old_school/csv/content_row.rb
|
140
|
+
- lib/old_school/csv/header_row.rb
|
141
|
+
- lib/old_school/csv.rb
|
142
|
+
- lib/old_school/models/assignment.rb
|
143
|
+
- lib/old_school/models/assignment_score.rb
|
144
|
+
- lib/old_school/models/contact_info.rb
|
145
|
+
- lib/old_school/models/course.rb
|
146
|
+
- lib/old_school/models/demographics.rb
|
147
|
+
- lib/old_school/models/name.rb
|
148
|
+
- lib/old_school/models/school.rb
|
149
|
+
- lib/old_school/models/section.rb
|
150
|
+
- lib/old_school/models/section_enrollment.rb
|
151
|
+
- lib/old_school/models/staff.rb
|
152
|
+
- lib/old_school/models/student.rb
|
153
|
+
- lib/old_school/models/term.rb
|
154
|
+
- lib/old_school/version.rb
|
155
|
+
- lib/old_school/virtus_ext/class_methods.rb
|
156
|
+
- lib/old_school/virtus_ext/model.rb
|
157
|
+
- lib/old_school/virtus_ext.rb
|
158
|
+
- lib/old_school.rb
|
159
|
+
homepage: https://github.com/kaiged/old_school
|
160
|
+
licenses: []
|
161
|
+
post_install_message:
|
162
|
+
rdoc_options: []
|
163
|
+
require_paths:
|
164
|
+
- lib
|
165
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
166
|
+
none: false
|
167
|
+
requirements:
|
168
|
+
- - ! '>='
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
none: false
|
173
|
+
requirements:
|
174
|
+
- - ! '>='
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
requirements: []
|
178
|
+
rubyforge_project:
|
179
|
+
rubygems_version: 1.8.23
|
180
|
+
signing_key:
|
181
|
+
specification_version: 3
|
182
|
+
summary: Ruby Gem for a Powerful SIS
|
183
|
+
test_files: []
|
184
|
+
has_rdoc:
|