ps_utilities 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c62242e43417fc95e14b36ba194604f4dfa79aac9e45887793ab982abedeb814
4
- data.tar.gz: e0cf20edad5ba754ff741a99629e4554b38a831973dbb66ca51f61cdf74afc08
3
+ metadata.gz: 4abd4b17bf893e811f7e920887cc881baad21ffc7b43e112de022ac5240269e2
4
+ data.tar.gz: da43f677c4b59e491e352894061a96290406dea47ad6c6bb0f395715ebead669
5
5
  SHA512:
6
- metadata.gz: 8603dbf338731a62a39f386b6458579e4f49a3eca1eb41b2320beedafa352792f2a539cae7abc9f043934c17ccaec84085b7d1a692ba8d66b90a0116a07f70c4
7
- data.tar.gz: 2a300ec71565e9e48e081740b402b87f450ca83e1effcfaf3705a0090d46938923353204a217c53538521758ba0d35c32ae5e0d8748459158385d84a4ff6b2f7
6
+ metadata.gz: 299bbefec900eef22d92c605838e90358f6cae6a561828ba2229d813e0e5fcdccc0df95d145c13e2e40d6b7b0b03d79e54f9c03e531ddfb90c5911a6622f1382
7
+ data.tar.gz: edb76484fc23806fd56b1dd67573a8e2466513023340535f79437751c808423bb0a30025a182ba4141221eb8483efc0b2f5a22d80f1d08fed2faec0f0c4ae238
@@ -21,16 +21,20 @@ module PsUtilities
21
21
  # The PsUtilities, makes it east to work with the Powerschool API
22
22
  # @since 0.1.0
23
23
  #
24
- # @note You should use environment variables to initialize your server.
24
+
25
25
  class Connection
26
26
 
27
- attr_reader :credentials, :headers, :base_uri, :auth_path, :auth_token
27
+ attr_reader :credentials, :headers, :base_uri
28
+ attr_reader :auth_path, :auth_token
28
29
  attr_reader :version
29
30
 
30
31
  include PsUtilities::PreBuiltGet
31
32
  include PsUtilities::PreBuiltPut
32
33
  include PsUtilities::PreBuiltPost
33
34
 
35
+ # @param attributes: [Hash] - options include: { base_uri: ENV['PS_URL'], auth_endpoint: (ENV['PS_AUTH_ENDPOINT'] || '/oauth/access_token'), client_id: ENV['PS_CLIENT_ID'], client_secret: ENV['PS_CLIENT_SECRET'] }
36
+ # @param headers: [Hash] - allows to change from json to xml (only do this if you are doing direct api calls and not using pre-built calls) returns and use a different useragent: { 'User-Agent' => "PsUtilities - #{version}", 'Accept' => 'application/json', 'Content-Type' => 'application/json'}
37
+ # @note preference is to use environment variables to initialize your server.
34
38
  def initialize(attributes: {}, headers: {})
35
39
  @version = "v#{PsUtilities::Version::VERSION}"
36
40
  @credentials = attr_defaults.merge(attributes)
@@ -46,7 +50,12 @@ module PsUtilities
46
50
  credentials[:base_uri].empty?
47
51
  end
48
52
 
49
- # with no command it just checks authenticates if needed
53
+ # this runs the various options:
54
+ # @param command: [Symbol] - commands include direct api calls: :authenticate, :delete, :get, :patch, :post, :put (these require api_path: and options: params) & also pre-built commands - see included methods (they require params:)
55
+ # @param api_path: [String] - this is the api_endpoint (only needed for direct api calls)
56
+ # @param options: [Hash] - this is the data body or the query options (needed for direct api calls)
57
+ # @param params: [Hash] - this is the data needed for using pre-built commands - see the individual command for details
58
+ # @note with no command an authenticatation check is done
50
59
  def run(command: nil, api_path: "", options: {}, params: {})
51
60
  authenticate unless token_valid?
52
61
  @headers[:headers].merge!('Authorization' => 'Bearer ' + authorized_token)
@@ -72,7 +81,9 @@ module PsUtilities
72
81
  retries = 3
73
82
  ps_url = base_uri + api_path
74
83
  options = options.merge(headers)
84
+ pp "api-url"
75
85
  pp ps_url
86
+ pp "api-options"
76
87
  pp options
77
88
  begin
78
89
  HTTParty.send(verb, ps_url, options)
@@ -2,15 +2,32 @@ module PsUtilities
2
2
 
3
3
  module PreBuiltGet
4
4
 
5
+ # return all active students within the district (special case of #get_all_matching_students) - a recursive search
6
+ # @param params [Hash] - ignored - only included for the api standard
7
+ # @return - (see #get_all_matching_students)
5
8
  def get_all_active_students(params={})
6
- params[:status_code] = 0
9
+ params = {status_code: 0}
10
+ # params[:status_code] = 0
7
11
  get_all_matching_students(params)
8
12
  end
9
-
10
- # params = {username: "xxxxxxx"} or {local_id: "12345"}
11
- # or {enroll_status: "x"} or {status_code: "0"}
12
- # or {first_name: "John"} or {last_name: "Brown"}
13
- # api_path = "/ws/v1/district/student?expansions=school_enrollment,contact&q=enroll_status==A;name.last_name==J*"
13
+ alias_method :all_active_students, :get_all_active_students
14
+ alias_method :active_students, :get_all_active_students
15
+
16
+ # return all students within the district matching the filters are passed in -- this is a Recursive search and will collect all students
17
+ # @param params [Hash] - enter a match criteria in the following format (* are allowed for wildcard matching):
18
+ # {username: "xxxxx*"} or {local_id: "12345"}
19
+ # or {enroll_status: "x"} or {status_code: "0"}
20
+ # or {first_name: "John"} or {last_name: "Br*"}
21
+ # or multiple field match
22
+ # {status_code: "0", last_name: "Br*"}
23
+ # @return [Hash of an Array of Student Summaries] - see below format
24
+ # {students: [
25
+ # {"id"=>4916, "local_id"=>112406, "student_username"=>"cccc406", "name"=>{"first_name"=>"Ssssss", "last_name"=>"CCCCC"}},
26
+ # {"id"=>4932, "local_id"=>112520, "student_username"=>"jjjj520", "name"=>{"first_name"=>"Ppppppp", "last_name"=>"JJJJJJJJ"}},
27
+ # {"id"=>4969, "local_id"=>112766, "student_username"=>"aaaa766", "name"=>{"first_name"=>"Sssss", "middle_name"=>"Aaaaaaaa", "last_name"=>"Aaaaaaaaaa"}}
28
+ # ]
29
+ # }
30
+ # @note - the api_path sent to the api call looks like: "/ws/v1/district/student?expansions=school_enrollment,contact&q=enroll_status==A;name.last_name==J*"
14
31
  def get_all_matching_students(params)
15
32
  params[:page_size] ||= 100
16
33
  count = get_matching_students_count(params)
@@ -26,15 +43,69 @@ module PsUtilities
26
43
  # return answer
27
44
  { students: students.flatten }
28
45
  end
29
- # {students: [
30
- # {"id"=>4916, "local_id"=>112406, "student_username"=>"cccc406", "name"=>{"first_name"=>"Ssssss", "last_name"=>"CCCCC"}},
31
- # {"id"=>4932, "local_id"=>112520, "student_username"=>"jjjj520", "name"=>{"first_name"=>"Ppppppp", "last_name"=>"JJJJJJJJ"}},
32
- # {"id"=>4969, "local_id"=>112766, "student_username"=>"aaaa766", "name"=>{"first_name"=>"Sssss", "middle_name"=>"Aaaaaaaa", "last_name"=>"Aaaaaaaaaa"}}
33
- # ]
46
+ alias_method :all_matching_students, :get_all_matching_students
47
+ alias_method :matching_students, :get_all_matching_students
48
+ alias_method :find_all_students, :get_all_matching_students
49
+ alias_method :find_students, :get_all_matching_students
50
+
51
+ # retrieves all individual student's details - you must use the DCID !!!
52
+ # @param params [Hash] - use either: {dcid: "12345"} or {id: "12345"}
53
+ # @return [Hash] - in the format of:
54
+ # { :student=>
55
+ # { "@expansions"=> "demographics, addresses, alerts, phones, school_enrollment, ethnicity_race, contact, contact_info, initial_enrollment, schedule_setup, fees, lunch",
56
+ # "@extensions"=> "s_stu_crdc_x,activities,c_studentlocator,u_students_extension,u_studentsuserfields,s_stu_ncea_x,s_stu_edfi_x,studentcorefields",
57
+ # "_extension_data"=> {
58
+ # "_table_extension"=> [
59
+ # { "recordFound"=>false,
60
+ # "_field"=> [
61
+ # {"name"=>"preferredname", "type"=>"String", "value"=>"Guy"},
62
+ # {"name"=>"student_email", "type"=>"String", "value"=>"guy@las.ch"}
63
+ # ],
64
+ # "name"=>"u_students_extension"
65
+ # },
66
+ # { "recordFound"=>false,
67
+ # "_field"=> [
68
+ # {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>1858},
69
+ # {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"CH"},
70
+ # {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"Bex"},
71
+ # {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"VD"},
72
+ # {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"LAS"},
73
+ # {"name"=>"transcriptaddrline2", "type"=>"String", "value"=>"CP 108"}
74
+ # ],
75
+ # "name"=>"u_studentsuserfields"
76
+ # }
77
+ # ]
78
+ # },
79
+ # "id"=>7337,
80
+ # "local_id"=>555807,
81
+ # "student_username"=>"guy807",
82
+ # "name"=>{"first_name"=>"Mountain", "last_name"=>"BIV"},
83
+ # "demographics"=>{"gender"=>"M", "birth_date"=>"2002-08-26", "projected_graduation_year"=>2021},
84
+ # "addresses"=>"",
85
+ # "alerts"=>"",
86
+ # "phones"=>"",
87
+ # "school_enrollment"=> {
88
+ # "enroll_status"=>"A",
89
+ # "enroll_status_description"=>"Active",
90
+ # "enroll_status_code"=>0,
91
+ # "grade_level"=>9,
92
+ # "entry_date"=>"2018-06-22",
93
+ # "exit_date"=>"2019-08-06",
94
+ # "school_number"=>2,
95
+ # "school_id"=>2,
96
+ # "full_time_equivalency"=>{"fteid"=>970, "name"=>"FTE Admissions"}
97
+ # },
98
+ # "ethnicity_race"=>{"federal_ethnicity"=>"NO"},
99
+ # "contact"=>{"guardian_email"=>"guydad@orchid.ch"},
100
+ # "contact_info"=>{"email"=>"guy@las.ch"},
101
+ # "initial_enrollment"=>{"district_entry_grade_level"=>0, "school_entry_grade_level"=>0},
102
+ # "schedule_setup"=>{"next_school"=>33, "sched_next_year_grade"=>10},
103
+ # "fees"=>"",
104
+ # "lunch"=>{"balance_1"=>"0.00", "balance_2"=>"0.00", "balance_3"=>"0.00", "balance_4"=>"0.00", "lunch_id"=>0}
105
+ # }
34
106
  # }
35
-
36
- # params = {dcid: "xxxxxxx"} or {id: "12345"}
37
- def get_student(params)
107
+ # @note the data within "u_students_extension" - is unique for each school
108
+ def get_one_student(params)
38
109
  # api_path = "/ws/v1/district/student/{dcid}?expansions=school_enrollment,contact&q=student_username==xxxxxx237"
39
110
  ps_dcid = params[:dcid] || params[:dc_id] || params[:id]
40
111
  api_path = "/ws/v1/student/#{ps_dcid.to_i}"
@@ -48,105 +119,42 @@ module PsUtilities
48
119
  answer = api(:get, api_path, options)
49
120
  { student: (answer["student"] || []) }
50
121
  end
51
-
52
- # params = {username: "xxxxxxx"} or {local_id: "12345"}
53
- def find_student(params)
54
- # api_path = "/ws/v1/district/student?expansions=school_enrollment,contact&q=student_username==xxxxxx237"
55
- api_path = "/ws/v1/district/student"
56
- options = { query:
57
- {"expansions" => "contact,contact_info,phones"}}
58
- query = []
59
- query << "student_username==#{params[:username]}" if params.has_key?(:username)
60
- query << "local_id==#{params[:local_id]}" if params.has_key?(:local_id)
61
-
62
- options[:query]["q"] = query.join(";")
63
- pp options
64
- return {"errorMessage"=>{"message"=>"A valid parameter must be entered."}} if query.empty?
65
-
66
- answer = api(:get, api_path, options)
67
- { student: (answer.dig("students","student") || []) }
68
- end
69
- alias_method :find_student_by_local_id, :find_student
70
- alias_method :find_student_by_username, :find_student
71
- # {student:
72
- # {"id"=>5023,
73
- # "local_id"=>112193,
74
- # "student_username"=>"xxxxxxx193",
75
- # "name"=>{"first_name"=>"Aaaaaaaaa", "last_name"=>"EEEEEEEEE"},
76
- # "school_enrollment"=>
77
- # {"enroll_status"=>"A",
78
- # "enroll_status_description"=>"Active",
79
- # "enroll_status_code"=>0,
80
- # "grade_level"=>12,
81
- # "entry_date"=>"2017-08-25",
82
- # "exit_date"=>"2018-06-09",
83
- # "school_number"=>33,
84
- # "school_id"=>6,
85
- # "entry_comment"=>"Promote Same School",
86
- # "full_time_equivalency"=>{"fteid"=>1070, "name"=>"FTE Value: 1"}
87
- # },
88
- # "contact"=>
89
- # {"guardian_email"=>"parent1@example.fr,parent2@example.ch", "mother"=>"EEEEEEE, Vvvvv", "father"=>"EEEEEEEE, Sssssss"},
90
- # "contact_info"=>{"email"=>"xxxxxxx193@xxxx.de"}
91
- # }
92
- # }
122
+ alias_method :get_student, :get_one_student
93
123
 
94
124
  private
95
- # given page_size = 100
96
- # 430 kids = 5 pages
97
- # 400 kids = 4 pages
125
+
126
+ # given the number of students and page size calculate pages needed to return all students
127
+ # @param count [Integer] - total number of students matching filter
128
+ # @param page_size [Integer] - total number of students to be return per page
129
+ # For example:
130
+ # given: page_size = 100
131
+ # when: 430 kids, then: 5 pages
132
+ # when: 400 kids, then: 4 pages
133
+ # @return [Integer] - number of pages needed to return all students
98
134
  def calc_pages(count, page_size)
99
- max_page = ( (count.to_i-1) / page_size.to_i ).to_i + 1
135
+ ( (count.to_i-1) / page_size.to_i ).to_i + 1
100
136
  end
101
- # 5
102
137
 
103
138
  # api_path = "/ws/v1/district/student/count?q=school_enrollment.enroll_status_code==0"
104
139
  # returns: {"resource"=>{"count"=>423}}
140
+ # @return [Integer] - the number of students matching the filter
105
141
  def get_matching_students_count(params={})
106
142
  api_path = "/ws/v1/district/student/count"
107
143
 
108
- query = []
109
- query << "student_username==#{params[:username]}" if params.has_key?(:username)
110
- query << "local_id==#{params[:local_id]}" if params.has_key?(:local_id)
111
- query << "name.last_name==#{params[:last_name]}" if params.has_key?(:last_name)
112
- query << "name.first_name==#{params[:first_name]}" if params.has_key?(:first_name)
113
- query << "school_enrollment.enroll_status==#{params[:enroll_status]}" if params.has_key?(:enroll_status)
114
- query << "school_enrollment.enroll_status_code==#{params[:status_code]}" if params.has_key?(:status_code)
115
-
116
- options = {query: {"q" => query.join(";")} }
117
- pp options
144
+ query = build_query(params)
145
+ options = {query: { "q" => query } } unless query.empty?
118
146
  return {"errorMessage"=>{"message"=>"A valid parameter must be entered."}} if query.empty?
119
147
 
120
- answer = api(:get, api_path, options) # {"resource"=>{"count"=>423}}
148
+ answer = api(:get, api_path, options) #returns: {"resource"=>{"count"=>423}}
121
149
  answer.dig("resource", "count").to_i
122
150
  end
123
- # 423
124
151
 
152
+ # NOT RECURSIVE - simple call to get one page of student summaries
125
153
  # params = {username: "xxxxxxx"} or {local_id: "12345"}
126
154
  # or {enroll_status: "x"} or {status_code: "0"}
127
155
  # or {first_name: "John"} or {last_name: "Brown"}
128
156
  # api_path = "/ws/v1/district/student?expansions=school_enrollment,contact&q=student_username==xxxxxx237"
129
- def get_matching_students_page(params)
130
- api_path = "/ws/v1/district/student"
131
- params[:page_size] ||= 100
132
- params[:page_number] ||= 1
133
- pp params
134
- options = { query:
135
- { "pagesize" => "#{params[:page_size]}",
136
- "page" => "#{params[:page_number]}"} }
137
- query = []
138
- query << "student_username==#{params[:username]}" if params.has_key?(:username)
139
- query << "local_id==#{params[:local_id]}" if params.has_key?(:local_id)
140
- query << "name.last_name==#{params[:last_name]}" if params.has_key?(:last_name)
141
- query << "name.first_name==#{params[:first_name]}" if params.has_key?(:first_name)
142
- query << "school_enrollment.enroll_status==#{params[:enroll_status]}" if params.has_key?(:enroll_status)
143
- query << "school_enrollment.enroll_status_code==#{params[:status_code]}" if params.has_key?(:status_code)
144
-
145
- options[:query]["q"] = query.join(";")
146
- return {"errorMessage"=>{"message"=>"A valid parameter must be entered."}} if query.empty?
147
- pp options
148
- api(:get, api_path, options)
149
- end
157
+ # @return [Hash] - returns one page of students
150
158
  # {"students"=>
151
159
  # {"@expansions"=>
152
160
  # "demographics, addresses, alerts, phones, school_enrollment, ethnicity_race, contact, contact_info, initial_enrollment, schedule_setup, fees, lunch",
@@ -165,7 +173,38 @@ module PsUtilities
165
173
  # ]
166
174
  # }
167
175
  # }
176
+ def get_matching_students_page(params)
177
+ api_path = "/ws/v1/district/student"
178
+ params[:page_size] ||= 100
179
+ params[:page_number] ||= 1
180
+ # pp params
181
+ options = { query:
182
+ { "pagesize" => "#{params[:page_size]}",
183
+ "page" => "#{params[:page_number]}"} }
184
+ query = build_query(params)
185
+ options[:query]["q"] = query unless query.empty?
186
+ return {"errorMessage"=>{"message"=>"A valid parameter must be entered."}} if query.empty?
187
+ # pp options
188
+ api(:get, api_path, options)
189
+ end
168
190
 
191
+ # build the api query - you can use splats to match any character
192
+ # @param params [Hash] - valid keys include: :status_code (or :enroll_status), :username, :last_name, :first_name, :student_id (or :local_id), :id (or :dcid)
193
+ # @return [String] - "id==345;name.last_name==BA*"
194
+ def build_query(params)
195
+ query = []
196
+ query << "school_enrollment.enroll_status_code==#{params[:status_code]}" if params.has_key?(:status_code)
197
+ query << "school_enrollment.enroll_status==#{params[:enroll_status]}" if params.has_key?(:enroll_status)
198
+ query << "student_username==#{params[:username]}" if params.has_key?(:username)
199
+ query << "name.last_name==#{params[:last_name]}" if params.has_key?(:last_name)
200
+ query << "name.first_name==#{params[:first_name]}" if params.has_key?(:first_name)
201
+ query << "local_id==#{params[:local_id]}" if params.has_key?(:local_id)
202
+ query << "local_id==#{params[:student_id]}" if params.has_key?(:student_id)
203
+ query << "id==#{params[:dcid]}" if params.has_key?(:dcid)
204
+ query << "id==#{params[:id]}" if params.has_key?(:id)
205
+ answer = query.join(";")
206
+ answer
207
+ end
169
208
 
170
209
  end
171
210
 
@@ -2,18 +2,20 @@ module PsUtilities
2
2
 
3
3
  module PreBuiltPost
4
4
 
5
- # params[:students] (Array of Hashes) - kids with their attributes
6
- # params = {dcid: 7531, student_id: 23456}
7
- # or
8
- # params = { [ {dcid: 7531, student_id: 23456},
9
- # {dcid: 9753, student_id: 65432} ] }
10
- def create_students(params)
11
- action = "INSERT"
12
- kids_api_array = build_kids_api_array(action, params)
13
- options = { body: { students: { student: kids_api_array } }.to_json }
14
- answer = api(:post, "/ws/v1/student", options)
15
- end
16
- alias_method :create_student, :create_students
5
+ # this method CREATES or INSERTS a new student into PowerSchool
6
+ # @param params (Array of Hashes) - kids with their attributes
7
+ # example student entry
8
+ # @param params: { student: {dcid: 7531, student_id: 23456, email: "kid@las.ch"} }
9
+ # or multiple students (with ps and your school's database extensions)
10
+ # @param params: { students:
11
+ # [ { dcid: 9753, student_id: 65432 },
12
+ # { dcid: 7531, student_id: 23456, email: "kid@las.ch",
13
+ # u_studentsuserfields: {transcriptaddrcity: "Bex"},
14
+ # u_students_extension: {preferredname: "Joe"}
15
+ # }
16
+ # ]
17
+ # }
18
+ # @return [Hash]
17
19
  # { "results": {
18
20
  # "update_count": 2
19
21
  # "result": [
@@ -28,43 +30,152 @@ module PsUtilities
28
30
  # ]
29
31
  # }
30
32
  # }
33
+ def create_students(params)
34
+ action = "INSERT"
35
+ kids_api_array = build_kids_api_array(action, params)
36
+ options = { body: { students: { student: kids_api_array } }.to_json }
37
+ answer = api(:post, "/ws/v1/student", options)
38
+ end
39
+ alias_method :create_student, :create_students
31
40
 
32
- # params[:students] (Array of Hashes) - kids with their attributes
41
+ # this updates and existing student record within PowerSchool
42
+ # (see #create_students)
33
43
  def update_students(params)
44
+ pp "update students"
45
+ pp params
34
46
  action = "UPDATE"
35
47
  kids_api_array = build_kids_api_array(action, params)
48
+ pp kids_api_array
36
49
  options = { body: { students: { student: kids_api_array } }.to_json }
50
+ pp options
37
51
  answer = api(:post, "/ws/v1/student", options)
38
52
  end
39
53
  alias_method :update_student, :update_students
40
- # { "results": {
41
- # "update_count": 2
42
- # "result": [
43
- # { "client_uid": 124,
44
- # "status": "SUCCESS",
45
- # "action": "INSERT",
46
- # "success_message" :{
47
- # "id": 442,
48
- # "ref": "https://server/ws/v1/student/442" }
49
- # },
50
- # { ... }
54
+
55
+ # @param action [String] - either "INSERT" or "UPDATE"
56
+ # @param params [Array of Hashes] - in this format -- students: [{kid_1_info}, {kid_2_info}]
57
+ # @return [Array of Hashes] - with data like below:
58
+ #[ {:action=>"UPDATE",
59
+ # :id=>7337,
60
+ # :client_uid=>"555807",
61
+ # :contact_info=>{:email=>"bassjoe@las.ch"},
62
+ # "_extension_data"=> {
63
+ # "_table_extension"=> [
64
+ # { "recordFound"=>false,
65
+ # "name"=>"u_students_extension",
66
+ # "_field"=> [
67
+ # {"name"=>"preferredname", "type"=>"String", "value"=>"Joe"},
68
+ # {"name"=>"student_email", "type"=>"String", "value"=>"bassjoe@las.ch"}
69
+ # ]
70
+ # },
71
+ # { "recordFound"=>false,
72
+ # "name"=>"u_studentsuserfields",
73
+ # "_field"=> [
74
+ # {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"LAS"},
75
+ # {"name"=>"transcriptaddrline2", "type"=>"String", "value"=>"CP 108"},
76
+ # {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"Leysin"},
77
+ # {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>"1854"},
78
+ # {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"Vaud"},
79
+ # {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"CH"}
80
+ # ]
81
+ # }
51
82
  # ]
52
83
  # }
53
- # }
54
-
84
+ # { ...another_student_data... },
85
+ #]
86
+ # @note this is then sent to the API call with a body tag
55
87
  def build_kids_api_array(action, params)
56
- unless params[:students].is_a? Array
88
+ pp "build_kids_api_array"
89
+ pp params
90
+ students = []
91
+ api_array = []
92
+ students << params[:student] if params[:student]
93
+ students = params[:students] if params[:students]
94
+ unless students.is_a? Array
57
95
  return {"errorMessage"=>{"message"=>"Student Data (in Hash format) must be in an Array."}}
58
96
  end
59
- kids_api_array = []
60
- params[:students].each do |kid|
61
- kid[:las_extensions] = true if params[:las_extensions]
62
- kids_api_array << build_kid_attributes(action, kid)
97
+ students.each do |kid|
98
+ # kid[:las_extensions] = true if params[:las_extensions]
99
+ api_array << build_kid_attributes(action, kid)
100
+ end
101
+ return api_array
102
+ end
103
+
104
+ # prepare data to update student database extensions
105
+ # @param data [Hash] - with the format: {u_students_extension: {field1: data1, field2: data2}}
106
+ # @return [Hash] - with data like below:
107
+ # { "name"=>"u_students_extension",
108
+ # "recordFound"=>false,
109
+ # "_field"=> [
110
+ # {"name"=>"preferredname", "type"=>"String", "value"=>"Joe"},
111
+ # {"name"=>"student_email", "type"=>"String", "value"=>"joe@las.ch"},
112
+ # ]
113
+ # }
114
+ def u_students_extension(data)
115
+ db_extensions = { "name"=>"u_students_extension", "recordFound"=>false,
116
+ "_field"=> [] }
117
+ data.each do |key, value|
118
+ db_extensions["_field"] << {"name"=>"#{key}", "type"=>"String", "value"=>"#{value}"}
119
+ end
120
+ db_extensions
121
+ end
122
+
123
+ # prepare data to built-in database extensions
124
+ # @param data [Hash] - with the format: {u_studentsuserfields: {field1: data1, field2: data2}}
125
+ # @return [Hash] - with data like below:
126
+ # { "name"=>"u_students_extension",
127
+ # "recordFound"=>false,
128
+ # "_field"=> [
129
+ # {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>75230},
130
+ # {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"United States"},
131
+ # {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"dallas"},
132
+ # {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"Texas"},
133
+ # {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"6138 meadow rd"}
134
+ # ]
135
+ # }
136
+ def u_studentsuserfields(data)
137
+ db_extensions = { "name"=>"u_studentsuserfields", "recordFound"=>false,
138
+ "_field"=> [] }
139
+ data.each do |key, value|
140
+ db_extensions["_field"] << {"name"=>"#{key}", "type"=>"String", "value"=>"#{value}"}
63
141
  end
64
- return kids_api_array
142
+ db_extensions
65
143
  end
66
144
 
145
+ # prepare an indivdual's attributes to be sent to PowerSchool
146
+ # @param action [String, "UPDATE" or "INSERT"] - handles what powerschool should do
147
+ # @param kid [Hash] - one kid's attributes within a hash
148
+ # @return [Hash] - returns data in the below format:
149
+ # Data structure to send to API (with built-in PS extensions)
150
+ # { :action=>"UPDATE",
151
+ # :id=>7337,
152
+ # :client_uid=>"555807",
153
+ # :contact_info=>{:email=>"bassjoe@las.ch"},
154
+ # "_extension_data"=> {
155
+ # "_table_extension"=> [
156
+ # { "recordFound"=>false,
157
+ # "name"=>"u_students_extension",
158
+ # "_field"=> [
159
+ # {"name"=>"preferredname", "type"=>"String", "value"=>"Joe"},
160
+ # {"name"=>"student_email", "type"=>"String", "value"=>"bassjoe@las.ch"}
161
+ # ]
162
+ # },
163
+ # { "recordFound"=>false,
164
+ # "name"=>"u_studentsuserfields",
165
+ # "_field"=> [
166
+ # {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"LAS"},
167
+ # {"name"=>"transcriptaddrline2", "type"=>"String", "value"=>"CP 108"},
168
+ # {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"Leysin"},
169
+ # {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>"1854"},
170
+ # {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"Vaud"},
171
+ # {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"CH"}
172
+ # ]
173
+ # }
174
+ # ]
175
+ # }
67
176
  def build_kid_attributes(action, kid)
177
+ pp "build_kid_attributes"
178
+ pp kid
68
179
  # ALWAYS NEEDED INFO
69
180
  attribs = {action: action}
70
181
  attribs[:id] = kid[:id] || kid[:dcid]
@@ -100,7 +211,26 @@ module PsUtilities
100
211
  attribs[:name][:middle_name] = kid[:middle_name] if kid[:middle_name]
101
212
  end
102
213
 
103
- # OPTIONAL
214
+ # OPTIONAL FIELDS
215
+ attribs[:address] = {}
216
+ if kid[:physical_street] or kid[:physical_city] or kid[:physical_state_province] or
217
+ kid[:physical_postal_code] or kid[:physical_grid_location]
218
+ attribs[:address][:physical] = {}
219
+ attribs[:address][:physical][:street] = kid[:physical_street] if kid[:physical_street]
220
+ attribs[:address][:physical][:city] = kid[:physical_city] if kid[:physical_city]
221
+ attribs[:address][:physical][:state_province] = kid[:physical_state] if kid[:physical_state]
222
+ attribs[:address][:physical][:postal_code] = kid[:physical_postal_code] if kid[:physical_postal_code]
223
+ attribs[:address][:physical][:grid_location] = kid[:physical_grid_location] if kid[:physical_grid_location]
224
+ end
225
+ if kid[:mailing_street] or kid[:mailing_city] or kid[:mailing_state_province] or
226
+ kid[:mailing_postal_code] or kid[:mailing_grid_location]
227
+ attribs[:address][:mailing] = {}
228
+ attribs[:address][:mailing][:street] = kid[:mailing_street] if kid[:mailing_street]
229
+ attribs[:address][:mailing][:city] = kid[:mailing_city] if kid[:mailing_city]
230
+ attribs[:address][:mailing][:state_province] = kid[:mailing_state] if kid[:mailing_state]
231
+ attribs[:address][:mailing][:postal_code] = kid[:mailing_postal_code] if kid[:mailing_postal_code]
232
+ attribs[:address][:mailing][:grid_location] = kid[:mailing_grid_location] if kid[:mailing_grid_location]
233
+ end
104
234
  attribs[:contact] = {}
105
235
  if kid[:emergency_phone1] && kid[:emergency_contact_name1]
106
236
  attribs[:contact][:emergency_phone1] = kid[:emergency_phone1]
@@ -142,126 +272,24 @@ module PsUtilities
142
272
  attribs[:school_enrollment][:exit_date] = kid[:exit_date] if kid[:exit_date]
143
273
  attribs[:school_enrollment][:school_number] = kid[:school_number] if kid[:school_number]
144
274
  #
145
- attribs[:contact_info] = {email: kid[:email]} if kid[:email]
146
- attribs[:phone] = {main: {number: kid[:mobile]}} if kid[:mobile]
275
+ attribs[:contact_info] = {email: kid[:email]} if kid[:email]
276
+ attribs[:phone] = {main: {number: kid[:mobile]}} if kid[:mobile]
147
277
 
148
278
  # Update LAS Database Extensions as needed
149
- attribs["_extension_data"] = calc_las_extensions(kid) if kid[:las_extensions].to_s.eql?("true")
279
+ attribs["_extension_data"] = { "_table_extension" => [] }
280
+ # built-in extensions by PowerSchool
281
+ attribs["_extension_data"]["_table_extension"] << u_studentsuserfields(kid[:u_studentsuserfields])
282
+ # school defined database extensions
283
+ attribs["_extension_data"]["_table_extension"] << u_students_extension(kid[:u_students_extension])
284
+ # if no extension data present make it empty
285
+ attribs["_extension_data"] = {} if attribs["_extension_data"]["_table_extension"].empty?
150
286
 
151
- # return attributes - first remove, nils, empty strings and empty hashes
287
+ # remove, nils, empty strings and empty hashes
152
288
  answer = attribs.reject { |k,v| v.nil? || v.to_s.empty? || v.to_s.eql?("{}")}
153
- pp answer
289
+ # pp "kid-attributes"
290
+ # pp answer
154
291
  return answer
155
292
  end
156
- # Data structure to send to API
157
- # options = {body: {
158
- # "students":{
159
- # "student":[
160
- # {
161
- # "client_uid":"124",
162
- # "action":"UPDATE",
163
- # "id":"442",
164
- # "name":{
165
- # "first_name":"Aaronia",
166
- # "last_name":"Stephaniia"
167
- # },
168
- # },
169
- # { ... }
170
- # ]
171
- # }
172
- # }
173
- # }
174
-
175
- def calc_las_extensions(kid)
176
- # attribs = {}
177
- # attribs["_table_extension"] = []
178
- attribs = { "_table_extension" => [] }
179
- if kid[:email] or kid[:preferred_name]
180
- db_extensions =
181
- { "recordFound"=>false,
182
- "name"=>"u_students_extension",
183
- "_field"=> [ ]
184
- }
185
- if kid[:email]
186
- db_extensions["_field"] << {"name"=>"student_email", "type"=>"String", "value"=>"#{kid[:email]}"}
187
- end
188
- if kid[:preferredname]
189
- db_extensions["_field"] << {"name"=>"preferredname", "type"=>"String", "value"=>"#{kid[:preferredname]}"}
190
- end
191
- if kid[:preferred_name]
192
- db_extensions["_field"] << {"name"=>"preferredname", "type"=>"String", "value"=>"#{kid[:preferred_name]}"}
193
- end
194
- attribs["_table_extension"] << db_extensions
195
- # { "recordFound"=>false,
196
- # "name"=>"u_students_extension",
197
- # "_field"=> [
198
- # {"name"=>"preferredname", "type"=>"String", "value"=>"Niko"},
199
- # {"name"=>"student_email", "type"=>"String", "value"=>"#{kid[:email]}"}
200
- # ]
201
- # }
202
- end
203
- if kid[:transcriptaddrline1] or kid[:transcriptaddrline2] or
204
- kid[:transcriptaddrcity] or kid[:transcriptaddrstate] or
205
- kid[:transcriptaddrzip] or kid[:transcriptaddrcountry]
206
- db_extensions =
207
- { "recordFound"=>false,
208
- "name"=>"u_studentsuserfields",
209
- "_field"=> [ ]
210
- }
211
- if kid[:transcriptaddrline1]
212
- db_extensions["_field"] << {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"#{kid[:transcriptaddrline1]}"}
213
- end
214
- if kid[:transcriptaddrline2]
215
- db_extensions["_field"] << {"name"=>"transcriptaddrline2", "type"=>"String", "value"=>"#{kid[:transcriptaddrline2]}"}
216
- end
217
- if kid[:transcriptaddrcity]
218
- db_extensions["_field"] << {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"#{kid[:transcriptaddrcity]}"}
219
- end
220
- if kid[:transcriptaddrzip]
221
- db_extensions["_field"] << {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>"#{kid[:transcriptaddrzip]}"}
222
- end
223
- if kid[:transcriptaddrstate]
224
- db_extensions["_field"] << {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"#{kid[:transcriptaddrstate]}"}
225
- end
226
- if kid[:transcriptaddrcountry]
227
- db_extensions["_field"] << {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"#{kid[:transcriptaddrcountry]}"}
228
- end
229
- attribs["_table_extension"] << db_extensions
230
- # { "recordFound"=>false,
231
- # "name"=>"u_studentsuserfields",
232
- # "_field"=> [
233
- # {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>75230},
234
- # {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"United States"},
235
- # {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"dallas"},
236
- # {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"Texas"},
237
- # {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"6138 meadow rd"}
238
- # ]
239
- # }
240
- end
241
- pp attribs
242
- attribs
243
- end
244
- # to inject into _extension_data
245
- # { "_table_extension"=> [
246
- # { "recordFound"=>false,
247
- # "name"=>"u_students_extension",
248
- # "_field"=> [
249
- # {"name"=>"preferredname", "type"=>"String", "value"=>"Jimmy"},
250
- # {"name"=>"student_email", "type"=>"String", "value"=>"bbaaccdd@las.ch"}
251
- # ]
252
- # },
253
- # { "recordFound"=>false,
254
- # "name"=>"u_studentsuserfields",
255
- # "_field"=> [
256
- # {"name"=>"transcriptaddrzip", "type"=>"String", "value"=>8154},
257
- # {"name"=>"transcriptaddrcountry", "type"=>"String", "value"=>"Switzerland"},
258
- # {"name"=>"transcriptaddrcity", "type"=>"String", "value"=>"Leysin"},
259
- # {"name"=>"transcriptaddrstate", "type"=>"String", "value"=>"Vaud"},
260
- # {"name"=>"transcriptaddrline1", "type"=>"String", "value"=>"6789 linden st"}
261
- # ]
262
- # }
263
- # ]
264
- # }
265
293
 
266
294
  end
267
295
 
@@ -1,5 +1,5 @@
1
1
  module PsUtilities
2
2
  module Version
3
- VERSION = "0.3.1"
3
+ VERSION = "0.3.2"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ps_utilities
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Weisbecker
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-06-26 00:00:00.000000000 Z
12
+ date: 2018-06-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
@@ -77,7 +77,6 @@ extensions: []
77
77
  extra_rdoc_files: []
78
78
  files:
79
79
  - lib/ps_utilities.rb
80
- - lib/ps_utilities/api_endpoints.rb
81
80
  - lib/ps_utilities/connection.rb
82
81
  - lib/ps_utilities/pre_built_get.rb
83
82
  - lib/ps_utilities/pre_built_post.rb
@@ -1,166 +0,0 @@
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&section_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