d2l_sdk 0.1.10 → 0.1.11
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 +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
|