ps_utilities 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/lib/ps_utilities/api_endpoints.rb +166 -0
- data/lib/ps_utilities/connection.rb +175 -0
- data/lib/ps_utilities/pre_built_get.rb +36 -0
- data/lib/ps_utilities/pre_built_post.rb +7 -0
- data/lib/ps_utilities/pre_built_put.rb +7 -0
- data/lib/ps_utilities/version.rb +5 -0
- data/lib/ps_utilities.rb +6 -0
- metadata +110 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6b53435e5d2f6da0cd21f54f5780806986b87e7ff36b963702e4cd1009862e7d
|
|
4
|
+
data.tar.gz: 0f3133a755d4f2f67e8bde12bbb3769969afe60015f609b672166582b06d2898
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0b39f2d31153c8fc135d6fc39a415c55f8a347f4c97f0de18b91bec030f99d182e9baa655de000a29996d01997b2e44023f6d55d4f0b02e6187f254fd3f555b3
|
|
7
|
+
data.tar.gz: 87407650b7062b75f4ae4158bd48103a2acc302472923ad4d0456fbb900dc4a07191588df67621f028bcdf6e8d18aadf908dcfe8c666467335ecb872f68a6ea1
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module PsUtilities
|
|
2
|
+
|
|
3
|
+
module ApiEndpoints
|
|
4
|
+
|
|
5
|
+
API_PATHS = {
|
|
6
|
+
ws: '/ws/v1',
|
|
7
|
+
ptg: '/powerschool-ptg-api/v2/',
|
|
8
|
+
xte: '/ws/xte'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def initialize(api_credentials, options = {})
|
|
12
|
+
self.client = Class.new(Powerschool::Client) do |klass|
|
|
13
|
+
uri = api_credentials['base_uri'] || Powerschool::Client::BASE_URI
|
|
14
|
+
klass.base_uri(uri)
|
|
15
|
+
|
|
16
|
+
# options like `verify: false` (to disable ssl verification)
|
|
17
|
+
options.each do |k, v|
|
|
18
|
+
default_options.update({k => v})
|
|
19
|
+
end
|
|
20
|
+
end.new(api_credentials)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
[:get, :post, :put, :delete].each do |command|
|
|
25
|
+
define_method(command.to_s) do |method, api, path = nil|
|
|
26
|
+
if path.nil?
|
|
27
|
+
path, api = api, nil
|
|
28
|
+
end
|
|
29
|
+
define_method(method) do |options = {}|
|
|
30
|
+
return self.client.class.send(command, prepare_path(path.dup, api, options), self.client.options.merge(options))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def prepare_path(path, api, options)
|
|
37
|
+
options = options.dup
|
|
38
|
+
options.each_pair do |key, value|
|
|
39
|
+
regexp_path_option = /(:#{key}$|:#{key}([:&\/-_]))/
|
|
40
|
+
if path.match(regexp_path_option)
|
|
41
|
+
if value.blank?
|
|
42
|
+
raise "Blank value for parameter '%s' in '%s'" % [key, path]
|
|
43
|
+
end
|
|
44
|
+
path.gsub!(regexp_path_option, "#{value}\\2")
|
|
45
|
+
options.delete(key)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
if parameter = path.match(/:(\w*)/)
|
|
49
|
+
raise "Missing parameter '%s' in '%s'. Parameters: %s" % [parameter[1], path, options]
|
|
50
|
+
end
|
|
51
|
+
if api
|
|
52
|
+
path = (API_PATHS[api] + path).gsub('//', '/')
|
|
53
|
+
end
|
|
54
|
+
path
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# retreive max_page_size from metadata. Defaults to 100
|
|
58
|
+
def get_page_size(resource)
|
|
59
|
+
@metadata ||= self.metadata()
|
|
60
|
+
@metadata['%s_max_page_size' % resource.split('/').last.singularize] rescue 100
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Process every object for a resource.
|
|
64
|
+
def all(resource, options = {}, &block)
|
|
65
|
+
page_size = (options[:query][:pagesize] rescue nil) || get_page_size(resource)
|
|
66
|
+
_options = options.dup
|
|
67
|
+
_options[:query] ||= {}
|
|
68
|
+
_options[:query][:pagesize] ||= page_size
|
|
69
|
+
|
|
70
|
+
page = 1
|
|
71
|
+
results = []
|
|
72
|
+
begin
|
|
73
|
+
_options[:query][:page] = page
|
|
74
|
+
response = self.send(resource, _options)
|
|
75
|
+
results = response.parsed_response || {}
|
|
76
|
+
if !response.parsed_response
|
|
77
|
+
if response.headers['www-authenticate'].match(/Bearer error/)
|
|
78
|
+
raise response.headers['www-authenticate'].to_s
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if results.is_a?(Hash)
|
|
83
|
+
plural = results.keys.first
|
|
84
|
+
results = results[plural][plural.singularize] || []
|
|
85
|
+
end
|
|
86
|
+
if results.is_a?(Hash)
|
|
87
|
+
# a rare(?) case has been observed where (in this case section_enrollment) did return a single
|
|
88
|
+
# data object as a hash rather than as a hash inside an array
|
|
89
|
+
results = [results]
|
|
90
|
+
end
|
|
91
|
+
results.each do |result|
|
|
92
|
+
block.call(result, response)
|
|
93
|
+
end
|
|
94
|
+
page += 1
|
|
95
|
+
end while results.any? && results.size == page_size
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# client is set up per district so it returns only one district
|
|
99
|
+
# for urls with parameters
|
|
100
|
+
get :district, :ws, '/district'
|
|
101
|
+
get :schools, :ws, '/district/school'
|
|
102
|
+
get :teachers, :ws, '/staff'
|
|
103
|
+
get :student, :ws, '/student/:student_id'
|
|
104
|
+
get :students, :ws, '/student'
|
|
105
|
+
get :school_teachers, :ws, '/school/:school_id/staff'
|
|
106
|
+
get :school_students, :ws, '/school/:school_id/student'
|
|
107
|
+
get :school_sections, :ws, '/school/:school_id/section'
|
|
108
|
+
get :school_courses, :ws, '/school/:school_id/course'
|
|
109
|
+
get :school_terms, :ws, '/school/:school_id/term'
|
|
110
|
+
get :section_enrollment, :ws, '/section/:section_id/section_enrollment'
|
|
111
|
+
|
|
112
|
+
# PowerTeacher Gradebook (pre Powerschool 10)
|
|
113
|
+
get :assignment, :ptg, 'assignment/:id'
|
|
114
|
+
post :post_section_assignment, :ptg, '/section/:section_id/assignment'
|
|
115
|
+
put :put_assignment_scores, :ptg, '/assignment/:assignment_id/score'
|
|
116
|
+
put :put_assignment_score, :ptg, '/assignment/:assignment_id/student/:student_id/score'
|
|
117
|
+
|
|
118
|
+
# PowerTeacher Pro
|
|
119
|
+
post :xte_post_section_assignment, :xte, '/section/assignment?users_dcid=:teacher_id'
|
|
120
|
+
put :xte_put_assignment_scores, :xte, '/score'
|
|
121
|
+
get :xte_section_assignments, :xte, '/section/assignment?users_dcid=:teacher_id§ion_ids=:section_id'
|
|
122
|
+
get :xte_section_assignment, :xte, '/section/assignment/:assignment_id?users_dcid=:teacher_id'
|
|
123
|
+
get :xte_teacher_category, :xte, '/teacher_category'
|
|
124
|
+
|
|
125
|
+
get :metadata, :ws, '/metadata'
|
|
126
|
+
get :areas, '/ws/schema/area'
|
|
127
|
+
get :tables, '/ws/schema/table'
|
|
128
|
+
get :table_records, '/ws/schema/table/:table?projection=:projection'
|
|
129
|
+
get :table_metadata, '/ws/schema/table/:table/metadata'
|
|
130
|
+
get :area_table, '/ws/schema/area/:area/table'
|
|
131
|
+
|
|
132
|
+
get :queries, '/ws/schema/query/api'
|
|
133
|
+
|
|
134
|
+
def start_year
|
|
135
|
+
offset = Date.today.month <= 6 ? -1 : 0
|
|
136
|
+
year = self.client.api_credentials['start_year'] || (Date.today.year + offset)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Special method to filter terms and find the current ones
|
|
140
|
+
def current_terms(options, today = nil)
|
|
141
|
+
terms = []
|
|
142
|
+
today ||= Date.today.to_s(:db)
|
|
143
|
+
self.all(:school_terms, options) do |term, response|
|
|
144
|
+
if term['end_date'] >= today
|
|
145
|
+
terms << term
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
if terms.empty?
|
|
149
|
+
options[:query] = {q: 'start_year==%s' % start_year}
|
|
150
|
+
self.all(:school_terms, options) do |term, response|
|
|
151
|
+
if term['end_date'] >= today
|
|
152
|
+
terms << term
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
# now filter again for the start date and if there isn't one matching we have to return the most recent one
|
|
157
|
+
in_two_weeks = (Date.parse(today) + 2.weeks).to_s(:db)
|
|
158
|
+
active_terms = terms.select{|term| term['start_date'] <= in_two_weeks }
|
|
159
|
+
if active_terms.any?
|
|
160
|
+
return active_terms
|
|
161
|
+
end
|
|
162
|
+
return terms
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'base64'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'httparty'
|
|
5
|
+
require 'ps_utilities/pre_built_get'
|
|
6
|
+
require 'ps_utilities/pre_built_put'
|
|
7
|
+
require 'ps_utilities/pre_built_post'
|
|
8
|
+
|
|
9
|
+
# http://blog.honeybadger.io/ruby-custom-exceptions/
|
|
10
|
+
class AuthError < RuntimeError
|
|
11
|
+
attr_reader :url, :credentials
|
|
12
|
+
def initialize(msg="", url="", credentials={})
|
|
13
|
+
@url = url
|
|
14
|
+
@credentials = credentials
|
|
15
|
+
super(msg)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module PsUtilities
|
|
20
|
+
|
|
21
|
+
# The PsUtilities, makes it east to work with the Powerschool API
|
|
22
|
+
# @since 0.1.0
|
|
23
|
+
#
|
|
24
|
+
# @note You should use environment variables to initialize your server.
|
|
25
|
+
class Connection
|
|
26
|
+
|
|
27
|
+
attr_reader :credentials, :headers
|
|
28
|
+
|
|
29
|
+
include PsUtilities::PreBuiltGet
|
|
30
|
+
include PsUtilities::PreBuiltPut
|
|
31
|
+
include PsUtilities::PreBuiltPost
|
|
32
|
+
|
|
33
|
+
def initialize(attributes: {}, headers: {})
|
|
34
|
+
@credentials = attr_defaults.merge(attributes)
|
|
35
|
+
@headers = header_defaults.merge(headers)
|
|
36
|
+
|
|
37
|
+
raise ArgumentError, "missing client_secret" if credentials[:client_secret].nil? or
|
|
38
|
+
credentials[:client_secret].empty?
|
|
39
|
+
raise ArgumentError, "missing client_id" if credentials[:client_id].nil? or
|
|
40
|
+
credentials[:client_id].empty?
|
|
41
|
+
raise ArgumentError, "missing base_uri" if credentials[:base_uri].nil? or
|
|
42
|
+
credentials[:base_uri].empty?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# with no command it just authenticates
|
|
46
|
+
def run(command: nil, params: {}, url: nil, options: {})
|
|
47
|
+
authenticate unless token_valid?
|
|
48
|
+
case command
|
|
49
|
+
when nil, :authenticate
|
|
50
|
+
# authenticate unless token_valid?
|
|
51
|
+
when :get, :put, :post
|
|
52
|
+
send(command, url, options) unless url.empty?
|
|
53
|
+
else
|
|
54
|
+
send(command, params)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# options = {query: {}}
|
|
61
|
+
def get(url, options={})
|
|
62
|
+
max_retries = 3
|
|
63
|
+
times_retried = 0
|
|
64
|
+
options = options.merge(headers)
|
|
65
|
+
ps_url = credentials[:base_uri] + url
|
|
66
|
+
begin
|
|
67
|
+
HTTParty.get(ps_url, options)
|
|
68
|
+
# self.class.get(url, query: options[:query], headers: options[:headers])
|
|
69
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
70
|
+
if times_retried < max_retries
|
|
71
|
+
times_retried += 1
|
|
72
|
+
retry
|
|
73
|
+
else
|
|
74
|
+
{ error: "no response (timeout) from URL: #{url}" }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# options = {body: {}}
|
|
80
|
+
def put(url, options={})
|
|
81
|
+
max_retries = 3
|
|
82
|
+
times_retried = 0
|
|
83
|
+
options = options.merge(headers)
|
|
84
|
+
ps_url = credentials[:base_uri] + url
|
|
85
|
+
begin
|
|
86
|
+
HTTParty.put(ps_url, options )
|
|
87
|
+
# self.class.get(url, body: options[:body], headers: options[:headers])
|
|
88
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
89
|
+
if times_retried < max_retries
|
|
90
|
+
times_retried += 1
|
|
91
|
+
retry
|
|
92
|
+
else
|
|
93
|
+
{ error: "no response (timeout) from URL: #{url}" }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# options = {body: {}}
|
|
99
|
+
def post(url, options={})
|
|
100
|
+
max_retries = 3
|
|
101
|
+
times_retried = 0
|
|
102
|
+
options = options.merge(headers)
|
|
103
|
+
ps_url = credentials[:base_uri] + url
|
|
104
|
+
begin
|
|
105
|
+
HTTParty.post(ps_url, options )
|
|
106
|
+
# self.class.get(url, body: options[:body], headers: options[:headers])
|
|
107
|
+
rescue Net::ReadTimeout, Net::OpenTimeout
|
|
108
|
+
if times_retried < max_retries
|
|
109
|
+
times_retried += 1
|
|
110
|
+
retry
|
|
111
|
+
else
|
|
112
|
+
{ error: "no response (timeout) from URL: #{url}" }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# In PowerSchool go to System>System Settings>Plugin Management Configuration>your plugin>Data Provider Configuration to manually check plugin expiration date
|
|
118
|
+
def authenticate
|
|
119
|
+
@headers[:headers] ||= {}
|
|
120
|
+
ps_url = credentials[:base_uri] + credentials[:auth_endpoint]
|
|
121
|
+
response = HTTParty.post(ps_url, {headers: auth_headers,
|
|
122
|
+
body: 'grant_type=client_credentials'})
|
|
123
|
+
|
|
124
|
+
@credentials[:token_expires] = Time.now + response.parsed_response['expires_in'].to_i
|
|
125
|
+
@credentials[:access_token] = response.parsed_response['access_token'].to_s
|
|
126
|
+
@headers[:headers].merge!('Authorization' => 'Bearer ' + credentials[:access_token])
|
|
127
|
+
|
|
128
|
+
# throw error if no token returned -- nothing else will work
|
|
129
|
+
raise AuthError.new("No Auth Token Returned",
|
|
130
|
+
ps_url, credentials
|
|
131
|
+
) if credentials[:access_token].empty?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def token_valid?(tokens = credentials)
|
|
135
|
+
return false if tokens[:access_token].nil?
|
|
136
|
+
return false if tokens[:access_token].empty?
|
|
137
|
+
return false if tokens[:token_expires].nil?
|
|
138
|
+
return false if tokens[:token_expires] <= Time.now
|
|
139
|
+
return true
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def auth_headers
|
|
143
|
+
{ 'ContentType' => 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
144
|
+
'Accept' => 'application/json',
|
|
145
|
+
'Authorization' => 'Basic ' + encode_credentials
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def encode_credentials
|
|
150
|
+
ps_auth_text = [ credentials[:client_id],
|
|
151
|
+
credentials[:client_secret]
|
|
152
|
+
].join(':')
|
|
153
|
+
Base64.encode64(ps_auth_text).gsub(/\n/, '')
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def attr_defaults
|
|
157
|
+
{ base_uri: ENV['PS_URL'],
|
|
158
|
+
auth_endpoint: ENV['PS_AUTH_ENDPOINT'] || '/oauth/access_token',
|
|
159
|
+
client_id: ENV['PS_CLIENT_ID'],
|
|
160
|
+
client_secret: ENV['PS_CLIENT_SECRET'],
|
|
161
|
+
# not recommended here - it changes (ok as a parameter though)
|
|
162
|
+
# access_token: ENV['PS_ACCESS_TOKEN'] || nil,
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def header_defaults
|
|
167
|
+
{ headers:
|
|
168
|
+
{ 'User-Agent' => "PsUtilitiesGem - v#{PsUtilities::Version::VERSION}",
|
|
169
|
+
'Accept' => 'application/json',
|
|
170
|
+
'Content-Type' => 'application/json'}
|
|
171
|
+
}
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module PsUtilities
|
|
2
|
+
|
|
3
|
+
module PreBuiltGet
|
|
4
|
+
|
|
5
|
+
def get_active_students_count(params={})
|
|
6
|
+
# url = "/ws/v1/district/student/count?q=school_enrollment.enroll_status_code==0"
|
|
7
|
+
url = "/ws/v1/district/student/count"
|
|
8
|
+
options = { query: {"q" => "school_enrollment.enroll_status_code==0"} }
|
|
9
|
+
get(url, options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def get_active_students_info(params={})
|
|
13
|
+
# url = "/ws/v1/district/student?q=school_enrollment.enroll_status==a&pagesize=500"
|
|
14
|
+
url = "/ws/v1/district/student"
|
|
15
|
+
options = { query:
|
|
16
|
+
{"q" => "school_enrollment.enroll_status_code==0",
|
|
17
|
+
"pagesize" => "500"}
|
|
18
|
+
}
|
|
19
|
+
get(url, options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# las-test.powerschool.com/ws/v1/district/student?expansions=school_enrollment&q=student_username==xxxxx237
|
|
23
|
+
# params = {username: "xxxxxxx"}
|
|
24
|
+
def get_one_student_record(params)
|
|
25
|
+
# url = "/ws/v1/district/student?expansions=school_enrollment,contact&q=student_username==xxxxxx237"
|
|
26
|
+
url = "/ws/v1/district/student"
|
|
27
|
+
options = { query:
|
|
28
|
+
{"q" => "student_username==#{params[:username]}",
|
|
29
|
+
"expansions" => "school_enrollment,contact,contact_info"}
|
|
30
|
+
}
|
|
31
|
+
get(url, options)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
data/lib/ps_utilities.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ps_utilities
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Lee Weisbecker
|
|
8
|
+
- Bill Tihen
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: exe
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2018-06-21 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: httparty
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '0.16'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '0.16'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: bundler
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '1.16'
|
|
35
|
+
type: :development
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '1.16'
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: rake
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - "~>"
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '10.5'
|
|
49
|
+
type: :development
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - "~>"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '10.5'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: rspec
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - "~>"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '3.7'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '3.7'
|
|
70
|
+
description: 'Uses oauth2 to connection to the Powerschool API. Heavily refactored
|
|
71
|
+
code (not dependent on Rails) starting with: https://github.com/TomK32/powerschool'
|
|
72
|
+
email:
|
|
73
|
+
- leeweisbecker@gmail.com
|
|
74
|
+
- btihen@gmail.com
|
|
75
|
+
executables: []
|
|
76
|
+
extensions: []
|
|
77
|
+
extra_rdoc_files: []
|
|
78
|
+
files:
|
|
79
|
+
- lib/ps_utilities.rb
|
|
80
|
+
- lib/ps_utilities/api_endpoints.rb
|
|
81
|
+
- lib/ps_utilities/connection.rb
|
|
82
|
+
- lib/ps_utilities/pre_built_get.rb
|
|
83
|
+
- lib/ps_utilities/pre_built_post.rb
|
|
84
|
+
- lib/ps_utilities/pre_built_put.rb
|
|
85
|
+
- lib/ps_utilities/version.rb
|
|
86
|
+
homepage: https://github.com/LAS-IT/ps_utilities
|
|
87
|
+
licenses:
|
|
88
|
+
- MIT
|
|
89
|
+
metadata: {}
|
|
90
|
+
post_install_message:
|
|
91
|
+
rdoc_options: []
|
|
92
|
+
require_paths:
|
|
93
|
+
- lib
|
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
requirements: []
|
|
105
|
+
rubyforge_project:
|
|
106
|
+
rubygems_version: 2.7.6
|
|
107
|
+
signing_key:
|
|
108
|
+
specification_version: 4
|
|
109
|
+
summary: Simple ruby wrapper for Powerschool API interaction.
|
|
110
|
+
test_files: []
|