d2l_sdk 0.1.10 → 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +28 -0
- data/.fasterer.yml +28 -0
- data/.rubocop.yml +67 -0
- data/.vscode/settings.json +9 -0
- data/Gemfile +3 -2
- data/README.md +341 -63
- data/d2l_sdk.gemspec +1 -0
- data/lib/d2l_sdk/auth.rb +1 -1
- data/lib/d2l_sdk/calendar.rb +11 -12
- data/lib/d2l_sdk/config.rb +29 -29
- data/lib/d2l_sdk/config_variables.rb +27 -39
- data/lib/d2l_sdk/course.rb +53 -47
- data/lib/d2l_sdk/course_content.rb +60 -54
- data/lib/d2l_sdk/course_template.rb +19 -14
- data/lib/d2l_sdk/datahub.rb +28 -28
- data/lib/d2l_sdk/demographics.rb +23 -8
- data/lib/d2l_sdk/discussions.rb +361 -56
- data/lib/d2l_sdk/dropbox.rb +208 -16
- data/lib/d2l_sdk/enroll.rb +9 -9
- data/lib/d2l_sdk/grades.rb +7 -7
- data/lib/d2l_sdk/group.rb +42 -42
- data/lib/d2l_sdk/logging.rb +2 -2
- data/lib/d2l_sdk/lti.rb +243 -11
- data/lib/d2l_sdk/news.rb +2 -2
- data/lib/d2l_sdk/org_unit.rb +20 -18
- data/lib/d2l_sdk/requests.rb +105 -69
- data/lib/d2l_sdk/section.rb +13 -12
- data/lib/d2l_sdk/semester.rb +6 -5
- data/lib/d2l_sdk/setup_versions.rb +7 -7
- data/lib/d2l_sdk/user.rb +48 -58
- data/lib/d2l_sdk/version.rb +1 -1
- metadata +6 -5
- data/change_lastname.rb +0 -65
- data/lol.txt +0 -1
- data/test.rb +0 -19
@@ -51,7 +51,7 @@ def get_topic_file(org_unit_id, topic_id, stream = false) # GET
|
|
51
51
|
_get(query_string)
|
52
52
|
end
|
53
53
|
|
54
|
-
# TODO Add a child +module+ or +topic+ to a specific module’s structure.
|
54
|
+
# TODO: Add a child +module+ or +topic+ to a specific module’s structure.
|
55
55
|
# Can be used in multiple ways. D2L categorizes it into 3 different ways:
|
56
56
|
# --Module: add child module to parent module
|
57
57
|
# --Link Topic: add child topic to parent module structure consisting of a LINK
|
@@ -66,10 +66,11 @@ end
|
|
66
66
|
# 1. +ContentObjectData+ JSON data block of type Topic
|
67
67
|
# 2. File attachment data itself you want to store in OU content area
|
68
68
|
# Returns (if successful) a JSON data block containing properties of the newly created object
|
69
|
-
def add_child_to_module(org_unit_id, module_id, child ={}) # POST
|
69
|
+
def add_child_to_module(org_unit_id, module_id, child = {}) # POST
|
70
70
|
path = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}/structure/"
|
71
71
|
payload = {}
|
72
|
-
|
72
|
+
# TODO: Add child module to a module
|
73
|
+
if child.key?("module")
|
73
74
|
payload = {
|
74
75
|
"Title" => "title_string", # String
|
75
76
|
"ShortTitle" => "title_short_string", # String
|
@@ -83,22 +84,23 @@ def add_child_to_module(org_unit_id, module_id, child ={}) # POST
|
|
83
84
|
"Text" => "blank",
|
84
85
|
"HTML" => ""
|
85
86
|
},
|
86
|
-
"Duration" => nil, #nil, number
|
87
|
+
"Duration" => nil, # nil, number
|
87
88
|
}.merge!(child["module"])
|
88
89
|
_post(path, payload)
|
89
|
-
|
90
|
+
# TODO: Add child link-type topics to a module
|
91
|
+
elsif child.key?("link")
|
90
92
|
payload = {
|
91
93
|
"Title" => "title_string", # String
|
92
94
|
"ShortTitle" => "title_short_string", # String
|
93
95
|
"Type" => 1,
|
94
|
-
"TopicType" => 3,
|
96
|
+
"TopicType" => 3, # <number:TOPIC_T>
|
95
97
|
"Url" => "URL", # the URL you want to fetch when the user opens the link-type topic.
|
96
98
|
"StartDate" => nil, # nil || string:UTCDateTime
|
97
99
|
"EndDate" => nil, # nil || string:UTCDateTime
|
98
100
|
"DueDate" => nil, # nil || string:UTCDateTime
|
99
101
|
"IsHidden" => false,
|
100
102
|
"IsLocked" => false,
|
101
|
-
"OpenAsExternalResource" => nil, #or boolean
|
103
|
+
"OpenAsExternalResource" => nil, # or boolean
|
102
104
|
"description" => {
|
103
105
|
"Text" => "",
|
104
106
|
"HTML" => nil # -or- HTML formatted string
|
@@ -106,10 +108,11 @@ def add_child_to_module(org_unit_id, module_id, child ={}) # POST
|
|
106
108
|
"MajorUpdate" => nil, # or bool
|
107
109
|
"MajorUpdateText" => "MajorUpdateText",
|
108
110
|
"ResetCompletionTracking" => nil, # or bool
|
109
|
-
"Duration" => nil, #nil, number
|
111
|
+
"Duration" => nil, # nil, number
|
110
112
|
}.merge!(child["module"])
|
111
113
|
_post(path, payload)
|
112
|
-
|
114
|
+
# TODO: Add child file-type topics to a module
|
115
|
+
elsif child.key?("file")
|
113
116
|
_course_content_upload(query_string, payload, file, "POST")
|
114
117
|
end
|
115
118
|
end
|
@@ -131,13 +134,13 @@ def check_content_module_validity(content_module)
|
|
131
134
|
'IsLocked' => { 'type' => 'boolean' },
|
132
135
|
'Description' => {
|
133
136
|
'type' => 'object',
|
134
|
-
'properties'=>
|
137
|
+
'properties' =>
|
135
138
|
{
|
136
139
|
"Content" => "string",
|
137
|
-
"Type" => "string" #"Text|HTML"
|
140
|
+
"Type" => "string" # "Text|HTML"
|
138
141
|
}
|
139
142
|
}, # Added with LE v1.10 API
|
140
|
-
'Duration' => { 'type' => %w(integer null) } #Added in LE's unstable contract as of LE v10.6.8
|
143
|
+
'Duration' => { 'type' => %w(integer null) } # Added in LE's unstable contract as of LE v10.6.8
|
141
144
|
}
|
142
145
|
}
|
143
146
|
JSON::Validator.validate!(schema, content_module, validate_schema: true)
|
@@ -146,7 +149,7 @@ end
|
|
146
149
|
def check_content_topic_validity(content_topic)
|
147
150
|
schema = {
|
148
151
|
'type' => 'object',
|
149
|
-
'required' => %w(Title ShortTitle Type
|
152
|
+
'required' => %w(Title ShortTitle Type TopicType Url StartDate
|
150
153
|
EndDate DueDate IsHidden IsLocked OpenAsExternalResource
|
151
154
|
Description MajoyUpdate MajorUpdateText ResetCompletionTracking),
|
152
155
|
'properties' => {
|
@@ -162,15 +165,15 @@ def check_content_topic_validity(content_topic)
|
|
162
165
|
'OpenAsExternalResource' => { 'type' => %w(boolean null) }, # Added with LE v1.6 API
|
163
166
|
'Description' => { # Added with LE v1.10 API
|
164
167
|
'type' => 'object',
|
165
|
-
'properties'=>
|
168
|
+
'properties' =>
|
166
169
|
{
|
167
170
|
"Content" => "string",
|
168
|
-
"Type" => "string" #"Text|HTML"
|
171
|
+
"Type" => "string" # "Text|HTML"
|
169
172
|
}
|
170
173
|
},
|
171
174
|
'MajorUpdate' => { 'type' => %w(boolean null) }, # Added with LE v1.12 API
|
172
|
-
'MajorUpdateText' => { 'type' => 'string' }, #Added with LE v1.12 API
|
173
|
-
'ResetCompletionTracking' => { 'type' => %w(boolean null) } #Added with LE v1.12 API
|
175
|
+
'MajorUpdateText' => { 'type' => 'string' }, # Added with LE v1.12 API
|
176
|
+
'ResetCompletionTracking' => { 'type' => %w(boolean null) } # Added with LE v1.12 API
|
174
177
|
}
|
175
178
|
}
|
176
179
|
JSON::Validator.validate!(schema, content_topic, validate_schema: true)
|
@@ -185,12 +188,12 @@ def create_root_module(org_unit_id, content_module) # GET
|
|
185
188
|
"Title" => "",
|
186
189
|
"ShortTitle" => "",
|
187
190
|
"Type" => 0,
|
188
|
-
"ModuleStartDate" => nil,
|
189
|
-
"ModuleEndDate" => nil,
|
190
|
-
"ModuleDueDate" => nil,
|
191
|
+
"ModuleStartDate" => nil, # <string:UTCDateTime>|null
|
192
|
+
"ModuleEndDate" => nil, # <string:UTCDateTime>|null
|
193
|
+
"ModuleDueDate" => nil, # <string:UTCDateTime>|null
|
191
194
|
"IsHidden" => false,
|
192
195
|
"IsLocked" => false,
|
193
|
-
"Description" => nil, #{ <composite:RichTextInput> }|null --Added with LE v1.10 API
|
196
|
+
"Description" => nil, # { <composite:RichTextInput> }|null --Added with LE v1.10 API
|
194
197
|
"Duration" => nil # <number>|null --Added in LE's +unstable+ contract as of LE v10.6.8
|
195
198
|
}.merge!(content_module)
|
196
199
|
check_content_module_validity(payload)
|
@@ -200,18 +203,18 @@ end
|
|
200
203
|
# Update a particular module for an org unit.
|
201
204
|
# INPUT: ContentObjectData of type Module
|
202
205
|
# NOTE: Cannot use this action to affect a module’s existing Structure property.
|
203
|
-
def update_module(org_unit_id, module_id) # PUT
|
206
|
+
def update_module(org_unit_id, module_id, content_module) # PUT
|
204
207
|
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}"
|
205
208
|
payload = {
|
206
209
|
"Title" => "",
|
207
210
|
"ShortTitle" => "",
|
208
211
|
"Type" => 0,
|
209
|
-
"ModuleStartDate" => nil,
|
210
|
-
"ModuleEndDate" => nil,
|
211
|
-
"ModuleDueDate" => nil,
|
212
|
+
"ModuleStartDate" => nil, # <string:UTCDateTime>|null
|
213
|
+
"ModuleEndDate" => nil, # <string:UTCDateTime>|null
|
214
|
+
"ModuleDueDate" => nil, # <string:UTCDateTime>|null
|
212
215
|
"IsHidden" => false,
|
213
216
|
"IsLocked" => false,
|
214
|
-
"Description" => nil, #{ <composite:RichTextInput> }|null --Added with LE v1.10 API
|
217
|
+
"Description" => nil, # { <composite:RichTextInput> }|null --Added with LE v1.10 API
|
215
218
|
"Duration" => nil # <number>|null --Added in LE's +unstable+ contract as of LE v10.6.8
|
216
219
|
}.merge!(content_module)
|
217
220
|
check_content_module_validity(payload)
|
@@ -236,8 +239,8 @@ def update_topic(org_unit_id, topic_id, content_topic) # GET
|
|
236
239
|
'OpenAsExternalResource' => nil, # Added with LE v1.6 API
|
237
240
|
'Description' => nil,
|
238
241
|
'MajorUpdate' => nil, # Added with LE v1.12 API
|
239
|
-
'MajorUpdateText' => "", #Added with LE v1.12 API
|
240
|
-
'ResetCompletionTracking' => nil #Added with LE v1.12 API
|
242
|
+
'MajorUpdateText' => "", # Added with LE v1.12 API
|
243
|
+
'ResetCompletionTracking' => nil # Added with LE v1.12 API
|
241
244
|
}.merge!(content_topic)
|
242
245
|
check_content_topic_validity(content_topic)
|
243
246
|
_put(query_string, payload)
|
@@ -304,7 +307,7 @@ def validate_isbn_association_data(isbn_association_data)
|
|
304
307
|
'properties' => {
|
305
308
|
'OrgUnitId' => { 'type' => 'integer' },
|
306
309
|
'Isbn' => { 'type' => 'string' },
|
307
|
-
'IsRequired' => { 'type' => 'boolean' }
|
310
|
+
'IsRequired' => { 'type' => 'boolean' }
|
308
311
|
}
|
309
312
|
}
|
310
313
|
JSON::Validator.validate!(schema, isbn_association_data, validate_schema: true)
|
@@ -316,7 +319,7 @@ def create_isbn_org_unit_association(org_unit_id, isbn_association_data) # GET
|
|
316
319
|
payload = {
|
317
320
|
"OrgUnitId" => 0,
|
318
321
|
"Isbn" => ""
|
319
|
-
#"IsRequired" => false ## optional
|
322
|
+
# "IsRequired" => false ## optional
|
320
323
|
}.merge!(isbn_association_data)
|
321
324
|
_post(query_string, payload)
|
322
325
|
# Returns: a IsbnAssociation JSON data block specifying
|
@@ -327,9 +330,9 @@ end
|
|
327
330
|
### SCHEDULED ITEMS######
|
328
331
|
#########################
|
329
332
|
|
330
|
-
# REVIEW: Retrieve the calling user
|
331
|
-
def
|
332
|
-
|
333
|
+
# REVIEW: Retrieve the calling user scheduled items.
|
334
|
+
def get_calling_user_scheduled_items(org_unit_ids_CSV, completion = nil,
|
335
|
+
start_date_time = '', end_date_time = '') # GET
|
333
336
|
query_string = "/d2l/api/le/#{$le_ver}/content/myItems/?"
|
334
337
|
query_string += "orgUnitIdsCSV=#{org_unit_ids_CSV}&"
|
335
338
|
query_string += "completion=#{completion}&" unless completion.nil?
|
@@ -339,9 +342,9 @@ def get_user_overdue_items(org_unit_ids_CSV, completion = nil,
|
|
339
342
|
# Returns: An ObjectListPage JSON block containing a list of ScheduledItem blocks
|
340
343
|
end
|
341
344
|
|
342
|
-
# REVIEW: Retrieve the calling user
|
343
|
-
def
|
344
|
-
|
345
|
+
# REVIEW: Retrieve the calling user scheduled items still due.
|
346
|
+
def get_current_user_scheduled_items_still_due(org_unit_ids_CSV, completion = nil,
|
347
|
+
start_date_time = '', end_date_time = '')
|
345
348
|
query_string = "/d2l/api/le/#{$le_ver}/content/myItems/due/?"
|
346
349
|
query_string += "orgUnitIdsCSV=#{org_unit_ids_CSV}&"
|
347
350
|
query_string += "completion=#{completion}&" unless completion.nil?
|
@@ -351,7 +354,7 @@ def get_current_user_still_due_items(org_unit_ids_CSV, completion = nil,
|
|
351
354
|
# Returns: An ObjectListPage JSON block containing a list of ScheduledItem blocks
|
352
355
|
end
|
353
356
|
|
354
|
-
# REVIEW: Retrieve the quantities of the calling user
|
357
|
+
# REVIEW: Retrieve the quantities of the calling user scheduled items, organized by org unit.
|
355
358
|
# GET /d2l/api/le/(version)/content/myItems/itemCounts/
|
356
359
|
def get_current_user_organized_scheduled_items(org_unit_ids_CSV,
|
357
360
|
completion = nil,
|
@@ -379,7 +382,7 @@ def get_current_user_scheduled_item_count(org_unit_ids_CSV, completion = nil,
|
|
379
382
|
# Returns: An ObjectListPage JSON block containing a list of ScheduledItem blocks
|
380
383
|
end
|
381
384
|
|
382
|
-
# REVIEW: Retrieve the calling user
|
385
|
+
# REVIEW: Retrieve the calling user completed scheduled items.
|
383
386
|
# GET /d2l/api/le/(version)/content/myItems/completions/
|
384
387
|
def get_current_user_completed_scheduled_items(org_unit_ids_CSV,
|
385
388
|
completion_from_date_time = '',
|
@@ -405,8 +408,8 @@ def get_current_user_completed_scheduled_items_with_due_date(org_unit_ids_CSV,
|
|
405
408
|
# Returns: An ObjectListPage JSON block containing a list of ScheduledItem blocks
|
406
409
|
end
|
407
410
|
|
408
|
-
# REVIEW: Retrieve the calling user
|
409
|
-
# GET /d2l/api/le/(version)/
|
411
|
+
# REVIEW: Retrieve the calling user scheduled items for a particular org unit.
|
412
|
+
# GET /d2l/api/le/(version)/#{org_unit_id}/content/myItems/
|
410
413
|
def get_current_user_scheduled_items_by_org_unit(org_unit_id, completion = nil,
|
411
414
|
start_date_time = '',
|
412
415
|
end_date_time = '')
|
@@ -419,10 +422,10 @@ def get_current_user_scheduled_items_by_org_unit(org_unit_id, completion = nil,
|
|
419
422
|
end
|
420
423
|
|
421
424
|
# REVIEW: Retrieve the calling user’s scheduled items still due for a particular org unit.
|
422
|
-
# GET /d2l/api/le/(version)/
|
423
|
-
def
|
424
|
-
|
425
|
-
|
425
|
+
# GET /d2l/api/le/(version)/#{org_unit_id}/content/myItems/due/
|
426
|
+
def get_current_user_org_unit_scheduled_items(org_unit_id, completion = nil,
|
427
|
+
start_date_time = '',
|
428
|
+
end_date_time = '') # GET
|
426
429
|
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/myItems/due/?"
|
427
430
|
query_string += "completion=#{completion}&" unless completion.nil?
|
428
431
|
query_string += "startDateTime=#{start_date_time}&" unless start_date_time == ''
|
@@ -431,10 +434,10 @@ def get_current_user_org_unit_scheduled_item_count(org_unit_id, completion = nil
|
|
431
434
|
# Returns: An ObjectListPage JSON block containing a list of ScheduledItem blocks
|
432
435
|
end
|
433
436
|
|
434
|
-
# REVIEW: Retrieve the quantity of the calling user
|
435
|
-
# GET /d2l/api/le/(version)/
|
436
|
-
def
|
437
|
-
|
437
|
+
# REVIEW: Retrieve the quantity of the calling user scheduled items for provided org unit.
|
438
|
+
# GET /d2l/api/le/(version)/#{org_unit_id}/content/myItems/itemCount
|
439
|
+
def get_calling_user_overdue_items_count(org_unit_id, completion = nil, start_date_time = '',
|
440
|
+
end_date_time = '') # GET
|
438
441
|
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/myItems/itemCount?"
|
439
442
|
query_string += "completion=#{completion}&" unless completion.nil?
|
440
443
|
query_string += "startDateTime=#{start_date_time}&" unless start_date_time == ''
|
@@ -444,9 +447,9 @@ def get_user_overdue_items(org_unit_id, completion = nil, start_date_time = '',
|
|
444
447
|
end
|
445
448
|
|
446
449
|
# REVIEW: Retrieve quantity of the calling user’s scheduled items still due for a particular org unit.
|
447
|
-
# GET /d2l/api/le/(version)/
|
448
|
-
def
|
449
|
-
|
450
|
+
# GET /d2l/api/le/(version)/#{org_unit_id}/content/myItems/due/itemCount
|
451
|
+
def get_calling_user_due_items_count(org_unit_id, completion = nil, start_date_time = '',
|
452
|
+
end_date_time = '') # GET
|
450
453
|
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/myItems/due/itemCount?"
|
451
454
|
query_string += "completion=#{completion}&" unless completion.nil?
|
452
455
|
query_string += "startDateTime=#{start_date_time}&" unless start_date_time == ''
|
@@ -501,7 +504,7 @@ end
|
|
501
504
|
# Retrieve the table of course content for an org unit.
|
502
505
|
def get_org_unit_toc(org_unit_id, ignore_module_data_restrictions = false) # GET
|
503
506
|
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/toc"
|
504
|
-
query_string +=
|
507
|
+
query_string += '?ignoreModuleDateRestrictions=true' if ignore_module_data_restrictions
|
505
508
|
_get(query_string)
|
506
509
|
# Returns: a TableOfContents JSON block.
|
507
510
|
end
|
@@ -510,7 +513,8 @@ end
|
|
510
513
|
### USER PROGRESS
|
511
514
|
#################
|
512
515
|
|
513
|
-
# Retrieves the aggregate count of completed and required content topics
|
516
|
+
# Retrieves the aggregate count of completed and required content topics
|
517
|
+
# in an org unit for the calling user.
|
514
518
|
# levels: 1=OrgUnit, 2=RootModule, 3=Topic
|
515
519
|
def get_current_user_progress(org_unit_id, level) # GET
|
516
520
|
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/completions/mycount/"
|
@@ -520,13 +524,15 @@ def get_current_user_progress(org_unit_id, level) # GET
|
|
520
524
|
# a list of ContentAggregateCompletion items.
|
521
525
|
end
|
522
526
|
|
523
|
-
|
524
527
|
# TODO: --UNSTABLE-- Retrieve the user progress items in an org unit, for specific users or content topics.
|
525
528
|
# _get "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/userprogress/"
|
529
|
+
def get_progress_of_users; end
|
526
530
|
|
527
531
|
# TODO: --UNSTABLE-- Retrieve one user’s progress within an org unit for a particular content topic.
|
528
532
|
# _get "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/userprogress/#{topic_id}"
|
533
|
+
def get_user_progress; end
|
529
534
|
|
530
535
|
# TODO: --UNSTABLE-- Update a user progress item.
|
531
536
|
# _post "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/userprogress/"
|
532
537
|
# payload: UserProgressData
|
538
|
+
def update_user_progress; end
|
@@ -50,9 +50,10 @@ def check_course_template_data_validity(course_template_data)
|
|
50
50
|
'Name' => { 'type' => 'string' },
|
51
51
|
'Code' => { 'type' => 'string' },
|
52
52
|
'Path' => { 'type' => 'string' },
|
53
|
-
'ParentOrgUnitIds' => {
|
54
|
-
|
55
|
-
|
53
|
+
'ParentOrgUnitIds' => {
|
54
|
+
'type' => 'array',
|
55
|
+
'items' => { 'type' => 'integer', 'minItems' => 1 }
|
56
|
+
}
|
56
57
|
}
|
57
58
|
}
|
58
59
|
JSON::Validator.validate!(schema, course_template_data, validate_schema: true)
|
@@ -65,15 +66,18 @@ end
|
|
65
66
|
# Required: "Name", "Code"
|
66
67
|
# /d2l/api/lp/(version)/coursetemplates/ [POST]
|
67
68
|
def create_course_template(course_template_data)
|
69
|
+
# TODO: make a bridge function that allows this to be done with 4 arguments, rather than
|
70
|
+
# just a JSON.
|
68
71
|
# Path- root path to use for this course offering’s course content
|
69
72
|
# if your back-end service has path enforcement set on for
|
70
73
|
# new org units, leave this property as an empty string
|
71
74
|
# Define a valid, empty payload and merge! with the user_data. Print it.
|
72
|
-
payload = {
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
75
|
+
payload = {
|
76
|
+
'Name' => '', # String
|
77
|
+
'Code' => 'off_SEMESTERCODE_STARNUM', # String
|
78
|
+
'Path' => '', # String
|
79
|
+
'ParentOrgUnitIds' => [99_989], # number: D2L_ID
|
80
|
+
}.merge!(course_template_data)
|
77
81
|
check_course_template_data_validity(payload)
|
78
82
|
puts 'Creating Course Template:'
|
79
83
|
ap payload
|
@@ -89,6 +93,7 @@ end
|
|
89
93
|
# course template JSON object. If it does conform, then nothing happens and it
|
90
94
|
# simply returns true. If it does not conform, then the JSON validator raises
|
91
95
|
# an exception.
|
96
|
+
# TODO: get rid of this, and make the template update only require 2 more args.
|
92
97
|
def check_course_template_updated_data_validity(course_template_data)
|
93
98
|
schema = {
|
94
99
|
'type' => 'object',
|
@@ -108,9 +113,10 @@ end
|
|
108
113
|
# /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [PUT]
|
109
114
|
def update_course_template(org_unit_id, new_data)
|
110
115
|
# Define a valid, empty payload and merge! with the new data.
|
111
|
-
payload = {
|
112
|
-
|
113
|
-
|
116
|
+
payload = {
|
117
|
+
'Name' => '', # String
|
118
|
+
'Code' => 'off_SEMESTERCODE_STARNUM', # String
|
119
|
+
}.merge!(new_data)
|
114
120
|
puts "Updating course template #{org_unit_id}"
|
115
121
|
check_course_template_updated_data_validity(payload)
|
116
122
|
# ap payload
|
@@ -183,6 +189,5 @@ def delete_all_course_templates_with_name(name)
|
|
183
189
|
end
|
184
190
|
end
|
185
191
|
|
186
|
-
# TODO: Delete course templates by using regular expressions to filter them.
|
187
|
-
def delete_course_templates_by_regex(regex)
|
188
|
-
end
|
192
|
+
# TODO: (HOMEBREW) Delete course templates by using regular expressions to filter them.
|
193
|
+
def delete_course_templates_by_regex(regex); end
|
data/lib/d2l_sdk/datahub.rb
CHANGED
@@ -52,14 +52,14 @@ def validate_create_export_job_data(create_export_job_data)
|
|
52
52
|
{
|
53
53
|
'type' => "object",
|
54
54
|
"properties" => {
|
55
|
-
"Name" => {'type'=>"string"},
|
56
|
-
"Value" => {'type'=>"string"}
|
55
|
+
"Name" => { 'type' => "string" },
|
56
|
+
"Value" => { 'type' => "string" }
|
57
57
|
}
|
58
58
|
}
|
59
59
|
}
|
60
60
|
}
|
61
61
|
}
|
62
|
-
#ap schema
|
62
|
+
# ap schema
|
63
63
|
JSON::Validator.validate!(schema, create_export_job_data, validate_schema: true)
|
64
64
|
# returns true if the CreateExportJobData JSON block is valid
|
65
65
|
end
|
@@ -93,7 +93,7 @@ def get_data_export_job(export_job_id)
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def get_job_status_code(export_job_id)
|
96
|
-
get_data_export_job(export_job_id)["Status"] #if 2 is OKAY/COMPLETED
|
96
|
+
get_data_export_job(export_job_id)["Status"] # if 2 is OKAY/COMPLETED
|
97
97
|
end
|
98
98
|
|
99
99
|
# Downloads the identified job and stores the zip within the working directory
|
@@ -107,7 +107,7 @@ def download_job_csv(export_job_id)
|
|
107
107
|
when 2 # If the status was okay, break loop + return download of job
|
108
108
|
zip_fname = 'export1.zip'
|
109
109
|
puts "Job complete, writing to zip: #{zip_fname}"
|
110
|
-
File.write(zip_fname,_get_raw("/d2l/api/lp/#{$lp_ver}/dataExport/download/#{export_job_id}"))
|
110
|
+
File.write(zip_fname, _get_raw("/d2l/api/lp/#{$lp_ver}/dataExport/download/#{export_job_id}"))
|
111
111
|
unzip(zip_fname, /sec_|off_/) # unzip file; filter if Enrollments + CSV
|
112
112
|
puts "file downloaded and unzipped"
|
113
113
|
break
|
@@ -121,14 +121,14 @@ def download_job_csv(export_job_id)
|
|
121
121
|
break
|
122
122
|
else # else, print out the status and wait 10 seconds before next attempt
|
123
123
|
puts "The job is not currently ready to download\n Status Code: #{status}"
|
124
|
-
if status
|
124
|
+
if status.zero?
|
125
125
|
puts "Status description: Queued - Export job has been received for processing."
|
126
126
|
else
|
127
127
|
puts "Status description: Processing - Currently in process of exporting data set."
|
128
128
|
end
|
129
129
|
puts "Sleeping for 10 seconds.."
|
130
130
|
sleep 10
|
131
|
-
attempt
|
131
|
+
attempt += 1
|
132
132
|
end
|
133
133
|
# returns: ZIP file containing a CSV file of data from the export job
|
134
134
|
end
|
@@ -139,22 +139,22 @@ end
|
|
139
139
|
def unzip(file_path, csv_filter = //)
|
140
140
|
puts "Unzipping file: #{file_path}"
|
141
141
|
# Unzip the file
|
142
|
-
Zip::File.open(file_path)
|
142
|
+
Zip::File.open(file_path) do |zip_file|
|
143
143
|
# for each file in the zip file
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
144
|
+
zip_file.each do |f|
|
145
|
+
# create file path of export_jobs/#{f.name}
|
146
|
+
f_path = File.join("export_jobs", f.name)
|
147
|
+
# make the directory if not already made
|
148
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
149
|
+
# extract the file unless the file already exists
|
150
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
151
|
+
# if the file is CSV and Enrollments, apply filters and proper
|
152
|
+
# CSV formatting to the file, writing it as base f.name + filtered.csv
|
153
|
+
if (f.name.include? ".csv") && (f.name.include? "Enrollments")
|
154
|
+
filter_formatted_enrollments("export_jobs/#{f.name}", csv_filter, "export_jobs/instr.csv")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
158
|
end
|
159
159
|
|
160
160
|
# Get all 'current' courses, assuming all instr courses are current
|
@@ -163,7 +163,7 @@ end
|
|
163
163
|
def get_current_courses(csv_fname)
|
164
164
|
puts "Retrieving current courses from #{csv_fname}"
|
165
165
|
instr_courses = Set.new
|
166
|
-
CSV.foreach(csv_fname, :
|
166
|
+
CSV.foreach(csv_fname, headers: true) do |row|
|
167
167
|
star_number = row[0]
|
168
168
|
course_term = row[10]
|
169
169
|
instr_courses.add("#{course_term}_#{star_number}")
|
@@ -174,9 +174,9 @@ end
|
|
174
174
|
# Filter all enrollments and withdrawals in a csv file, excluding data
|
175
175
|
# that is not properly formatted (based on ou code), not a current or
|
176
176
|
# future course, or nil for their ou code.
|
177
|
-
def filter_formatted_enrollments(csv_fname, regex_filter =
|
177
|
+
def filter_formatted_enrollments(csv_fname, instr_fname, regex_filter = //)
|
178
178
|
# Create csv with 'filtered' pre-appended to '.csv' substring
|
179
|
-
filtered_csv = csv_fname.gsub(/\.csv/,"filtered.csv")
|
179
|
+
filtered_csv = csv_fname.gsub(/\.csv/, "filtered.csv")
|
180
180
|
File.open(filtered_csv, 'w') do |file|
|
181
181
|
# set row num to 0 to keep track of headers
|
182
182
|
row_num = 0
|
@@ -190,7 +190,7 @@ def filter_formatted_enrollments(csv_fname, regex_filter = //, instr_fname)
|
|
190
190
|
# or skip in-filter OU_code,
|
191
191
|
# or skip if the header
|
192
192
|
# or skip if not within the INSTR SET of current/future courses
|
193
|
-
if row[3]
|
193
|
+
if row[3].nil? || row_num > 0 && (row[3] !~ regex_filter) || (!current.include? row[3][4..-1])
|
194
194
|
row_num += 1
|
195
195
|
next
|
196
196
|
end
|
@@ -200,8 +200,8 @@ def filter_formatted_enrollments(csv_fname, regex_filter = //, instr_fname)
|
|
200
200
|
# If it a UTC date time value, then parse as Time.
|
201
201
|
if value =~ /\b[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]*Z\b/ # if the value is UTC formatted
|
202
202
|
line << "\"#{Time.parse(value)}\""
|
203
|
-
elsif value == row[-1]# if its the last value in the row
|
204
|
-
line <<"\"#{value}\"" # then dont put a comma at the end.
|
203
|
+
elsif value == row[-1] # if its the last value in the row
|
204
|
+
line << "\"#{value}\"" # then dont put a comma at the end.
|
205
205
|
else # not the last value in the row,
|
206
206
|
line << "\"#{value}\"," # throw a comma after the value
|
207
207
|
end
|