ps_utilities 0.3.1 → 0.3.2

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 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