old_school 0.0.5
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.
- 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:
|