d2l_sdk 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +204 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/d2l_sdk.gemspec +42 -0
- data/example_scripts/adjusted_courses.txt +44446 -0
- data/example_scripts/fix_201708_courses.rb +13 -0
- data/example_scripts/update_enddates.rb +88 -0
- data/lib/d2l_sdk.rb +16 -0
- data/lib/d2l_sdk/auth.rb +95 -0
- data/lib/d2l_sdk/config.rb +56 -0
- data/lib/d2l_sdk/config_variables.rb +81 -0
- data/lib/d2l_sdk/course.rb +311 -0
- data/lib/d2l_sdk/course_content.rb +362 -0
- data/lib/d2l_sdk/course_template.rb +179 -0
- data/lib/d2l_sdk/datahub.rb +213 -0
- data/lib/d2l_sdk/demographics.rb +89 -0
- data/lib/d2l_sdk/enroll.rb +133 -0
- data/lib/d2l_sdk/group.rb +232 -0
- data/lib/d2l_sdk/logging.rb +57 -0
- data/lib/d2l_sdk/news.rb +20 -0
- data/lib/d2l_sdk/org_unit.rb +383 -0
- data/lib/d2l_sdk/requests.rb +241 -0
- data/lib/d2l_sdk/section.rb +207 -0
- data/lib/d2l_sdk/semester.rb +159 -0
- data/lib/d2l_sdk/user.rb +420 -0
- data/lib/d2l_sdk/version.rb +3 -0
- metadata +228 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
|
|
2
|
+
###################
|
|
3
|
+
### CONTENT ACTIONS
|
|
4
|
+
###################
|
|
5
|
+
|
|
6
|
+
# Delete a specific module from an org unit.
|
|
7
|
+
def delete_module(org_unit_id, module_id) # DELETE
|
|
8
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}"
|
|
9
|
+
_delete(query_string)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Delete a specific topic from an org unit.
|
|
13
|
+
def delete_topic(org_unit_id, topic_id) # DELETE
|
|
14
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/topics/#{topic_id}"
|
|
15
|
+
_delete(query_string)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Retrieve a specific module for an org unit.
|
|
19
|
+
# Returns ContentObject JSON data block of type Module
|
|
20
|
+
def get_module(org_unit_id, module_id) # GET
|
|
21
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}"
|
|
22
|
+
_get(query_string)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Retrieve the structure for a specific module in an org unit.
|
|
26
|
+
# Returns JSON array of ContentObject data blocks (either Module or Topic)
|
|
27
|
+
def get_module_structure(org_unit_id, module_id) # GET
|
|
28
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}/structure/"
|
|
29
|
+
_get(query_string)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Retrieve the root module(s) for an org unit.
|
|
33
|
+
# Returns JSON array of ContentObject data blocks of type Module
|
|
34
|
+
def get_root_modules(org_unit_id) # GET
|
|
35
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/root/"
|
|
36
|
+
_get(query_string)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Retrieve a specific topic for an org unit.
|
|
40
|
+
# Returns a ContentObject JSON data block of type Topic
|
|
41
|
+
def get_topic(org_unit_id, topic_id) # GET
|
|
42
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/topics/#{topic_id}"
|
|
43
|
+
_get(query_string)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Retrieve the content topic file for a content topic.
|
|
47
|
+
# Returns underlying file for a file content topic
|
|
48
|
+
def get_topic_file(org_unit_id, topic_id, stream = false) # GET
|
|
49
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/topics/#{topic_id}/file"
|
|
50
|
+
query_string += "?stream=true" if stream == true
|
|
51
|
+
_get(query_string)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Add a child +module+ or +topic+ to a specific module’s structure.
|
|
55
|
+
# Can be used in multiple ways. D2L categorizes it into 3 different ways:
|
|
56
|
+
# --Module: add child module to parent module
|
|
57
|
+
# --Link Topic: add child topic to parent module structure consisting of a LINK
|
|
58
|
+
# type topic.
|
|
59
|
+
# --File Topic: add child topic to parent module structure consisting of a FILE
|
|
60
|
+
# type topic.
|
|
61
|
+
# INPUT: (depends upon if its module, link topic, or file topic)
|
|
62
|
+
# --Module: POST body containing a +ContentObjectData+ JSON data block of type Module
|
|
63
|
+
# --Link Topic: POST body containing a +ContentObjectData+ JSON data block of type Topic
|
|
64
|
+
# URL property in it points to resource you want to the link to point to.
|
|
65
|
+
# --File Topic: Multipart/mixed post body w/ 2 parts
|
|
66
|
+
# 1. +ContentObjectData+ JSON data block of type Topic
|
|
67
|
+
# 2. File attachment data itself you want to store in OU content area
|
|
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) # POST
|
|
70
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}/structure/"
|
|
71
|
+
# TODO
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def check_content_module_validity(content_module)
|
|
75
|
+
schema = {
|
|
76
|
+
'type' => 'object',
|
|
77
|
+
'required' => %w(Title ShortTitle Type ModuleStartDate
|
|
78
|
+
ModuleEndDate ModuleDueDate IsHidden
|
|
79
|
+
IsLocked Description Duration),
|
|
80
|
+
'properties' => {
|
|
81
|
+
'Title' => { 'type' => 'string' },
|
|
82
|
+
'ShortTitle' => { 'type' => 'string' },
|
|
83
|
+
'Type' => { 'type' => 'integer' },
|
|
84
|
+
'ModuleStartDate' => { 'type' => %w(string null) },
|
|
85
|
+
'ModuleEndDate' => { 'type' => %w(string null) },
|
|
86
|
+
'ModuleDueDate' => { 'type' => %w(string null) },
|
|
87
|
+
'IsHidden' => { 'type' => %w(string null) },
|
|
88
|
+
'IsLocked' => { 'type' => 'boolean' },
|
|
89
|
+
'Description' => {
|
|
90
|
+
'type' => 'object',
|
|
91
|
+
'properties'=>
|
|
92
|
+
{
|
|
93
|
+
"Content" => "string",
|
|
94
|
+
"Type" => "string" #"Text|HTML"
|
|
95
|
+
}
|
|
96
|
+
}, # Added with LE v1.10 API
|
|
97
|
+
'Duration' => { 'type' => %w(integer null) } #Added in LE's unstable contract as of LE v10.6.8
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
JSON::Validator.validate!(schema, content_module, validate_schema: true)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def check_content_topic_validity(content_topic)
|
|
104
|
+
schema = {
|
|
105
|
+
'type' => 'object',
|
|
106
|
+
'required' => %w(Title ShortTitle Type TopicType Url StartDate
|
|
107
|
+
EndDate DueDate IsHidden IsLocked OpenAsExternalResource
|
|
108
|
+
Description MajoyUpdate MajorUpdateText ResetCompletionTracking),
|
|
109
|
+
'properties' => {
|
|
110
|
+
'Title' => { 'type' => 'string' },
|
|
111
|
+
'ShortTitle' => { 'type' => 'string' },
|
|
112
|
+
'Type' => { 'type' => 'integer' },
|
|
113
|
+
'TopicType' => { 'type' => 'integer' },
|
|
114
|
+
'StartDate' => { 'type' => %w(string null) },
|
|
115
|
+
'EndDate' => { 'type' => %w(string null) },
|
|
116
|
+
'DueDate' => { 'type' => %w(string null) },
|
|
117
|
+
'IsHidden' => { 'type' => %w(string null) },
|
|
118
|
+
'IsLocked' => { 'type' => 'boolean' },
|
|
119
|
+
'OpenAsExternalResource' => { 'type' => %w(boolean null) }, # Added with LE v1.6 API
|
|
120
|
+
'Description' => { # Added with LE v1.10 API
|
|
121
|
+
'type' => 'object',
|
|
122
|
+
'properties'=>
|
|
123
|
+
{
|
|
124
|
+
"Content" => "string",
|
|
125
|
+
"Type" => "string" #"Text|HTML"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
'MajorUpdate' => { 'type' => %w(boolean null) }, # Added with LE v1.12 API
|
|
129
|
+
'MajorUpdateText' => { 'type' => 'string' }, #Added with LE v1.12 API
|
|
130
|
+
'ResetCompletionTracking' => { 'type' => %w(boolean null) } #Added with LE v1.12 API
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
JSON::Validator.validate!(schema, content_topic, validate_schema: true)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Create a new root module for an org unit.
|
|
137
|
+
# INPUT: ContentObjectData (of type Module) – New root module property data.
|
|
138
|
+
# Returns JSON array of ContentObject data blocks of type Module
|
|
139
|
+
def create_root_module(org_unit_id, content_module) # GET
|
|
140
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/root/"
|
|
141
|
+
payload = {
|
|
142
|
+
"Title" => "",
|
|
143
|
+
"ShortTitle" => "",
|
|
144
|
+
"Type" => 0,
|
|
145
|
+
"ModuleStartDate" => nil, #<string:UTCDateTime>|null
|
|
146
|
+
"ModuleEndDate" => nil, #<string:UTCDateTime>|null
|
|
147
|
+
"ModuleDueDate" => nil, #<string:UTCDateTime>|null
|
|
148
|
+
"IsHidden" => false,
|
|
149
|
+
"IsLocked" => false,
|
|
150
|
+
"Description" => nil, #{ <composite:RichTextInput> }|null --Added with LE v1.10 API
|
|
151
|
+
"Duration" => nil # <number>|null --Added in LE's +unstable+ contract as of LE v10.6.8
|
|
152
|
+
}.merge!(content_module)
|
|
153
|
+
check_content_module_validity(payload)
|
|
154
|
+
_post(query_string, payload)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Update a particular module for an org unit.
|
|
158
|
+
# INPUT: ContentObjectData of type Module
|
|
159
|
+
# NOTE: Cannot use this action to affect a module’s existing Structure property.
|
|
160
|
+
def update_module(org_unit_id, module_id) # PUT
|
|
161
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/modules/#{module_id}"
|
|
162
|
+
payload = {
|
|
163
|
+
"Title" => "",
|
|
164
|
+
"ShortTitle" => "",
|
|
165
|
+
"Type" => 0,
|
|
166
|
+
"ModuleStartDate" => nil, #<string:UTCDateTime>|null
|
|
167
|
+
"ModuleEndDate" => nil, #<string:UTCDateTime>|null
|
|
168
|
+
"ModuleDueDate" => nil, #<string:UTCDateTime>|null
|
|
169
|
+
"IsHidden" => false,
|
|
170
|
+
"IsLocked" => false,
|
|
171
|
+
"Description" => nil, #{ <composite:RichTextInput> }|null --Added with LE v1.10 API
|
|
172
|
+
"Duration" => nil # <number>|null --Added in LE's +unstable+ contract as of LE v10.6.8
|
|
173
|
+
}.merge!(content_module)
|
|
174
|
+
check_content_module_validity(payload)
|
|
175
|
+
_put(query_string, payload)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Update a particular topic for an org unit.
|
|
179
|
+
# INPUT: ContentObjectData of type Topic
|
|
180
|
+
# Returns underlying file for a file content topic
|
|
181
|
+
def update_topic(org_unit_id, topic_id, content_topic) # GET
|
|
182
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/topics/#{topic_id}"
|
|
183
|
+
payload = {
|
|
184
|
+
'Title' => "",
|
|
185
|
+
'ShortTitle' => "",
|
|
186
|
+
'Type' => 0,
|
|
187
|
+
'TopicType' => 0,
|
|
188
|
+
'StartDate' => nil,
|
|
189
|
+
'EndDate' => nil,
|
|
190
|
+
'DueDate' => nil,
|
|
191
|
+
'IsHidden' => nil,
|
|
192
|
+
'IsLocked' => false,
|
|
193
|
+
'OpenAsExternalResource' => nil, # Added with LE v1.6 API
|
|
194
|
+
'Description' => nil,
|
|
195
|
+
'MajorUpdate' => nil, # Added with LE v1.12 API
|
|
196
|
+
'MajorUpdateText' => "", #Added with LE v1.12 API
|
|
197
|
+
'ResetCompletionTracking' => nil #Added with LE v1.12 API
|
|
198
|
+
}.merge!(content_topic)
|
|
199
|
+
check_content_topic_validity(content_topic)
|
|
200
|
+
_put(query_string, payload)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
####################
|
|
204
|
+
### CONTENT OVERVIEW
|
|
205
|
+
####################
|
|
206
|
+
|
|
207
|
+
# Retrieve the overview for a course offering.
|
|
208
|
+
def get_course_overview(org_unit_id) # GET
|
|
209
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/overview"
|
|
210
|
+
_get(query_string)
|
|
211
|
+
# Returns: a Overview JSON data block containing
|
|
212
|
+
# the course offering overview’s details.
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Retrieve the overview file attachment for a course offering.
|
|
216
|
+
def get_course_overview_file_attachment(org_unit_id) # GET
|
|
217
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/overview/attachment"
|
|
218
|
+
_get(query_string)
|
|
219
|
+
# Returns: a file stream containing the course offering’s overview attachment.
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
########
|
|
223
|
+
### ISBN
|
|
224
|
+
########
|
|
225
|
+
|
|
226
|
+
# Remove the association between an ISBN and org unit.
|
|
227
|
+
def delete_isbn_association(org_unit_id, isbn) # DELETE
|
|
228
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/isbn/#{isbn}"
|
|
229
|
+
_delete(query_string)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Retrieve all the org units associated with an ISBN.
|
|
233
|
+
def get_org_units_of_isbn(isbn) # GET
|
|
234
|
+
query_string = "/d2l/api/le/#{$le_ver}/content/isbn/#{isbn}"
|
|
235
|
+
_get(query_string)
|
|
236
|
+
# Returns: JSON array of IsbnAssociation data blocks specifying
|
|
237
|
+
# all the org units associated with the provided ISBN.
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Retrieve all ISBNs associated with an org unit.
|
|
241
|
+
def get_isbns_of_org_unit(org_unit_id) # GET
|
|
242
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}content/isbn/"
|
|
243
|
+
_get(query_string)
|
|
244
|
+
# Returns: JSON array of IsbnAssociation data blocks specifying
|
|
245
|
+
# all the org units associated with the provided ISBN.
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Retrieve the association between a ISBN and org unit.
|
|
249
|
+
def get_isbn_org_unit_association(org_unit_id, isbn) # GET
|
|
250
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}content/isbn/#{isbn}"
|
|
251
|
+
_get(query_string)
|
|
252
|
+
# Returns: a IsbnAssociation JSON data block specifying
|
|
253
|
+
# the association between an org unit and an ISBN.
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def validate_isbn_association_data(isbn_association_data)
|
|
257
|
+
schema = {
|
|
258
|
+
'type' => 'object',
|
|
259
|
+
'required' => %w(OrgUnitId Isbn),
|
|
260
|
+
'properties' => {
|
|
261
|
+
'OrgUnitId' => { 'type' => 'integer' },
|
|
262
|
+
'Isbn' => { 'type' => 'string' },
|
|
263
|
+
'IsRequired' => { 'type' => 'boolean' },
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
JSON::Validator.validate!(schema, isbn_association_data, validate_schema: true)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Create a new association between an ISBN and an org unit.
|
|
270
|
+
def create_isbn_org_unit_association(org_unit_id, isbn_association_data) # GET
|
|
271
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}content/isbn/"
|
|
272
|
+
payload = {
|
|
273
|
+
"OrgUnitId" => 0,
|
|
274
|
+
"Isbn" => ""
|
|
275
|
+
#"IsRequired" => false ## optional
|
|
276
|
+
}.merge!(isbn_association_data)
|
|
277
|
+
_post(query_string, payload)
|
|
278
|
+
# Returns: a IsbnAssociation JSON data block specifying
|
|
279
|
+
# the association between an org unit and an ISBN.
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
####################
|
|
283
|
+
### SCHEDULED ITEMS
|
|
284
|
+
####################
|
|
285
|
+
|
|
286
|
+
# Retrieve the overdue items for a particular user in a particular org unit.
|
|
287
|
+
# +org_unit_ids_CSV+ is a CSV of D2LIDs or rather Org unit IDs (optional)
|
|
288
|
+
# Viewing user overdue items depends upon the current calling user's permissions.
|
|
289
|
+
# Returns: An ObjectListPage JSON block containing a list of OverdueItem.
|
|
290
|
+
def get_user_overdue_items(user_id, org_unit_ids_CSV = nil) # GET
|
|
291
|
+
query_string = "/d2l/api/le/#{$le_ver}/overdueItems/"
|
|
292
|
+
query_string += "?userId=#{user_id}"
|
|
293
|
+
query_string += "&orgUnitIdsCSV=#{org_unit_ids_CSV}" unless org_unit_ids_CSV.nil?
|
|
294
|
+
_get(query_string)
|
|
295
|
+
# Returns: An ObjectListPage JSON block containing a list of OverdueItem.
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Retrieves the calling user’s overdue items, within a number of org units.
|
|
299
|
+
# org_unit_ids_CSV is a CSV of D2LIDs or rather Org unit IDs (optional)
|
|
300
|
+
# Returns: An ObjectListPage JSON block containing a list of OverdueItem.
|
|
301
|
+
# NOTE: If using a support account, try not to use this without defining a csv...
|
|
302
|
+
# It will consider all courses designated as somehow linked to the acc.
|
|
303
|
+
# and return ALL overdue items EVER for the support account.
|
|
304
|
+
def get_current_user_overdue_items(org_unit_ids_CSV = nil) # GET
|
|
305
|
+
query_string = "/d2l/api/le/#{$le_ver}/overdueItems/myItems"
|
|
306
|
+
query_string += "?orgUnitIdsCSV=#{org_unit_ids_CSV}" unless org_unit_ids_CSV.nil?
|
|
307
|
+
_get(query_string)
|
|
308
|
+
# Returns: An ObjectListPage JSON block containing a list of OverdueItem.
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
#####################
|
|
312
|
+
### TABLE OF CONTENTS
|
|
313
|
+
#####################
|
|
314
|
+
|
|
315
|
+
# Retrieve a list of topics that have been bookmarked.
|
|
316
|
+
def get_bookmarked_topics(org_unit_id) # GET
|
|
317
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/bookmarks"
|
|
318
|
+
_get(query_string)
|
|
319
|
+
# Returns: a JSON array of Topic ToC entries.
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Retrieve a list of the most recently visited topics.
|
|
323
|
+
def get_most_recently_visited_topics(org_unit_id) # GET
|
|
324
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/recent"
|
|
325
|
+
_get(query_string)
|
|
326
|
+
# Returns: a JSON array of Topic ToC entries.
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Retrieve the table of course content for an org unit.
|
|
330
|
+
def get_org_unit_toc(org_unit_id, ignore_module_data_restrictions = false) # GET
|
|
331
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/toc"
|
|
332
|
+
query_string += "?ignoreModuleDateRestrictions=true" if ignore_module_data_restrictions
|
|
333
|
+
_get(query_string)
|
|
334
|
+
# Returns: a TableOfContents JSON block.
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
#################
|
|
338
|
+
### USER PROGRESS
|
|
339
|
+
#################
|
|
340
|
+
|
|
341
|
+
# Retrieves the aggregate count of completed and required content topics in an org unit for the calling user.
|
|
342
|
+
# levels: 1=OrgUnit, 2=RootModule, 3=Topic
|
|
343
|
+
def get_current_user_progress(org_unit_id, level) # GET
|
|
344
|
+
query_string = "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/completions/mycount/"
|
|
345
|
+
query_string += "?level=#{level}"
|
|
346
|
+
_get(query_string)
|
|
347
|
+
# Returns: ObjectListPage JSON block containing
|
|
348
|
+
# a list of ContentAggregateCompletion items.
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Retrieve the user progress items in an org unit, for specific users or content topics.
|
|
352
|
+
# NOTE: UNSTABLE
|
|
353
|
+
# _get "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/userprogress/"
|
|
354
|
+
|
|
355
|
+
# Retrieve one user’s progress within an org unit for a particular content topic.
|
|
356
|
+
# NOTE: UNSTABLE
|
|
357
|
+
# _get "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/userprogress/#{topic_id}"
|
|
358
|
+
|
|
359
|
+
# Update a user progress item.
|
|
360
|
+
# NOTE: UNSTABLE
|
|
361
|
+
# _post "/d2l/api/le/#{$le_ver}/#{org_unit_id}/content/userprogress/"
|
|
362
|
+
# payload: UserProgressData
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require_relative 'requests'
|
|
2
|
+
require 'json-schema'
|
|
3
|
+
########################
|
|
4
|
+
# COURSE TEMPLATES:#####
|
|
5
|
+
########################
|
|
6
|
+
|
|
7
|
+
# Checks if the created course template data conforms to the valence api for the
|
|
8
|
+
# course template JSON object. If it does conform, then nothing happens and it
|
|
9
|
+
# simply returns true. If it does not conform, then the JSON validator raises
|
|
10
|
+
# an exception.
|
|
11
|
+
def check_course_template_data_validity(course_template_data)
|
|
12
|
+
schema = {
|
|
13
|
+
'type' => 'object',
|
|
14
|
+
'required' => %w(Name Code Path ParentOrgUnitIds),
|
|
15
|
+
'properties' => {
|
|
16
|
+
'Name' => { 'type' => 'string' },
|
|
17
|
+
'Code' => { 'type' => 'string' },
|
|
18
|
+
'Path' => { 'type' => 'string' },
|
|
19
|
+
'ParentOrgUnitIds' => { 'type' => 'array',
|
|
20
|
+
'items' => { 'type' => 'integer', 'minItems' => 1 }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
JSON::Validator.validate!(schema, course_template_data, validate_schema: true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# This method creates a course template using a merged payload between a
|
|
28
|
+
# pre-formatted payload and the argument "course_template_data". Upon this merge
|
|
29
|
+
# the path is defined for the POST http method that is then executed to create
|
|
30
|
+
# the course_template object.
|
|
31
|
+
# Required: "Name", "Code"
|
|
32
|
+
# /d2l/api/lp/(version)/coursetemplates/ [POST]
|
|
33
|
+
def create_course_template(course_template_data)
|
|
34
|
+
# Path- root path to use for this course offering’s course content
|
|
35
|
+
# if your back-end service has path enforcement set on for
|
|
36
|
+
# new org units, leave this property as an empty string
|
|
37
|
+
# Define a valid, empty payload and merge! with the user_data. Print it.
|
|
38
|
+
payload = { 'Name' => '', # String
|
|
39
|
+
'Code' => 'off_SEMESTERCODE_STARNUM', # String
|
|
40
|
+
'Path' => '', # String
|
|
41
|
+
'ParentOrgUnitIds' => [99_989], # number: D2L_ID
|
|
42
|
+
}.merge!(course_template_data)
|
|
43
|
+
check_course_template_data_validity(payload)
|
|
44
|
+
puts 'Creating Course Template:'
|
|
45
|
+
ap payload
|
|
46
|
+
# Define a path referencing the courses path
|
|
47
|
+
# requires: CreateCourseTemplate JSON block
|
|
48
|
+
path = "/d2l/api/lp/#{$lp_ver}/coursetemplates/"
|
|
49
|
+
_post(path, payload)
|
|
50
|
+
puts '[+] Course template creation completed successfully'.green
|
|
51
|
+
# returns: CourseTemplate JSON block containing the new data.
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Retrieves a course template based upon an explicitly defined course template
|
|
55
|
+
# org_unit_id or Identifier. This is done by using the identifier as a component
|
|
56
|
+
# of the path, and then performing a GET http method that is then returned.
|
|
57
|
+
#
|
|
58
|
+
# returns: JSON course template data
|
|
59
|
+
# /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [GET]
|
|
60
|
+
def get_course_template(org_unit_id)
|
|
61
|
+
path = "/d2l/api/lp/#{$lp_ver}/coursetemplates/#{org_unit_id}"
|
|
62
|
+
_get(path)
|
|
63
|
+
# return: JSON course template data
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Instead of explicitly retrieving a single course template, this method uses
|
|
67
|
+
# the routing table to retrieve all of the organizations descendants with the
|
|
68
|
+
# outTypeId of 2. What this means is that it is literally retrieving any and all
|
|
69
|
+
# course templates that have an ancestor of the organization...which should be
|
|
70
|
+
# all of them.
|
|
71
|
+
#
|
|
72
|
+
# returns: JSON array of course template data objects
|
|
73
|
+
def get_all_course_templates
|
|
74
|
+
path = "/d2l/api/lp/#{$lp_ver}/orgstructure/6606/descendants/?ouTypeId=2"
|
|
75
|
+
_get(path)
|
|
76
|
+
# return: JSON array of course template data objects
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# This method retrieves all course templates that have a specific string, as
|
|
80
|
+
# specified by org_unit_name, within their names. This is done by first defining
|
|
81
|
+
# that none are found yet and initializing an empty array. Then, by searching
|
|
82
|
+
# through all course templates for ones that do have a particular string within
|
|
83
|
+
# their name, the matches are pushed into the previously empty array of matches.
|
|
84
|
+
# This array is subsequently returned; if none were found, a message is returned
|
|
85
|
+
#
|
|
86
|
+
# returns: JSON array of matching course template data objects
|
|
87
|
+
def get_course_template_by_name(org_unit_name)
|
|
88
|
+
course_template_not_found = true
|
|
89
|
+
course_template_results = []
|
|
90
|
+
puts "[+] Searching for templates using search string: \'#{org_unit_name}\'".yellow
|
|
91
|
+
results = get_all_course_templates
|
|
92
|
+
results.each do |x|
|
|
93
|
+
if x['Name'].downcase.include? org_unit_name.downcase
|
|
94
|
+
course_template_not_found = false
|
|
95
|
+
course_template_results.push(x)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
if course_template_not_found
|
|
99
|
+
puts '[-] No templates could be found based upon the search string.'.yellow
|
|
100
|
+
end
|
|
101
|
+
course_template_results
|
|
102
|
+
# return: JSON array of matching course template data objects
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Moreso a helper method, but this really just returns the schema of the
|
|
106
|
+
# course templates. This is predefined in the routing table, and retrieved via
|
|
107
|
+
# a GET http method.
|
|
108
|
+
#
|
|
109
|
+
# returns: JSON of course templates schema
|
|
110
|
+
# /d2l/api/lp/(version)/coursetemplates/schema [GET]
|
|
111
|
+
def get_course_templates_schema
|
|
112
|
+
path = "/d2l/api/lp/#{$lp_ver}/coursetemplates/schema"
|
|
113
|
+
_get(path)
|
|
114
|
+
# This action returns a JSON array of SchemaElement blocks.
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Checks if the updated course template data conforms to the valence api for the
|
|
118
|
+
# course template JSON object. If it does conform, then nothing happens and it
|
|
119
|
+
# simply returns true. If it does not conform, then the JSON validator raises
|
|
120
|
+
# an exception.
|
|
121
|
+
def check_course_template_updated_data_validity(course_template_data)
|
|
122
|
+
schema = {
|
|
123
|
+
'type' => 'object',
|
|
124
|
+
'required' => %w(Name Code),
|
|
125
|
+
'properties' => {
|
|
126
|
+
'Name' => { 'type' => 'string' },
|
|
127
|
+
'Code' => { 'type' => 'string' }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
JSON::Validator.validate!(schema, course_template_data, validate_schema: true)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# This is the primary method utilized to update course templates. As only the
|
|
134
|
+
# Name and the Code can be changed in an update, they are pre-defined to
|
|
135
|
+
# conform to the required update data. The update is then performed via a
|
|
136
|
+
# PUT http method that is executed using a path referencing the course template.
|
|
137
|
+
# /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [PUT]
|
|
138
|
+
def update_course_template(org_unit_id, new_data)
|
|
139
|
+
# Define a valid, empty payload and merge! with the new data.
|
|
140
|
+
payload = { 'Name' => '', # String
|
|
141
|
+
'Code' => 'off_SEMESTERCODE_STARNUM', # String
|
|
142
|
+
}.merge!(new_data)
|
|
143
|
+
puts "Updating course template #{org_unit_id}"
|
|
144
|
+
check_course_template_updated_data_validity(payload)
|
|
145
|
+
# ap payload
|
|
146
|
+
# requires: CourseTemplateInfo JSON block
|
|
147
|
+
# Define a path referencing the courses path
|
|
148
|
+
path = "/d2l/api/lp/#{$lp_ver}/coursetemplates/" + org_unit_id.to_s
|
|
149
|
+
_put(path, payload)
|
|
150
|
+
puts '[+] Course template update completed successfully'.green
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Simply, a course template can be deleted by refencing it using its Identifier
|
|
154
|
+
# as an argument for this method. The argument is then used to refernce the obj
|
|
155
|
+
# by a path and then the path is passed in for a delete http method.
|
|
156
|
+
# /d2l/api/lp/(version)/coursetemplates/(orgUnitId) [DELETE]
|
|
157
|
+
def delete_course_template(org_unit_id)
|
|
158
|
+
path = "/d2l/api/lp/#{$lp_ver}/coursetemplates/#{org_unit_id}"
|
|
159
|
+
_delete(path)
|
|
160
|
+
puts '[+] Course template data deleted successfully'.green
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# As a more streamlined approach to deleting many course templates conforming to
|
|
164
|
+
# a particular naming style, this function performs deletions based on a string.
|
|
165
|
+
# Using the name argument, +get_course_template_by_name+ is called in order to
|
|
166
|
+
# retrieve all matching templates. They are then deleted by referencing each
|
|
167
|
+
# of their Identifiers as arguments for +delete_course_template+.
|
|
168
|
+
def delete_all_course_templates_with_name(name)
|
|
169
|
+
puts "[!] Deleting all course templates with the name: #{name}"
|
|
170
|
+
get_course_template_by_name(name).each do |course_template|
|
|
171
|
+
puts '[!] Deleting the following course:'.red
|
|
172
|
+
ap course_template
|
|
173
|
+
delete_course_template(course_template['Identifier'])
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# TO DO:
|
|
178
|
+
def delete_course_templates_by_regex(regex)
|
|
179
|
+
end
|