github-to-canvas 0.0.57 → 0.1.3

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.
@@ -5,21 +5,21 @@ class CanvasDotfile
5
5
  File.file?(".canvas")
6
6
  end
7
7
 
8
- def self.update_or_create(filepath, response, course, type)
8
+ def self.update_or_create(options, response)
9
9
  if self.exists?
10
- if type == "assignment" || type == "discussion"
11
- canvas_data = self.update_assignment_data(response, course, type)
10
+ if options[:type] == "assignment" || options[:type] == "discussion"
11
+ canvas_data = self.update_assignment_data(response, options[:course_id], options[:type])
12
12
  else
13
- canvas_data = self.update_page_data(response, course, type)
13
+ canvas_data = self.update_page_data(response, options[:course_id], options[:type])
14
14
  end
15
15
  else
16
- if type == "assignment" || type == "discussion"
17
- canvas_data = self.create_assignment_data(response, course, type)
16
+ if options[:type] == "assignment" || options[:type] == "discussion"
17
+ canvas_data = self.create_assignment_data(response, options[:course_id], options[:type])
18
18
  else
19
- canvas_data = self.create_page_data(response, course, type)
19
+ canvas_data = self.create_page_data(response, options[:course_id], options[:type])
20
20
  end
21
21
  end
22
- self.create_canvas_dotfile(filepath, canvas_data)
22
+ self.create_canvas_dotfile(options[:filepath], canvas_data)
23
23
  end
24
24
 
25
25
  def self.create_canvas_dotfile(filepath, canvas_data)
@@ -64,7 +64,7 @@ class CanvasDotfile
64
64
 
65
65
  def self.update_page_data(response, course, type)
66
66
  canvas_data = YAML.load(File.read(".canvas"))
67
- if canvas_data[:lessons].none? { |lesson| lesson[:page_id] == response['page_id'] && lesson[:course_id] == course.to_i && lesson[:canvas_url] == response['html_url']}
67
+ if canvas_data[:lessons].none? { |lesson| lesson[:id] == response['page_id'] && lesson[:course_id] == course.to_i && lesson[:canvas_url] == response['html_url']}
68
68
  lesson_data = {
69
69
  id: response['page_id'],
70
70
  course_id: course.to_i,
@@ -1,7 +1,140 @@
1
+ require 'byebug'
1
2
  require 'json'
2
3
  require 'rest-client'
4
+ require 'yaml'
3
5
  class CanvasInterface
4
6
 
7
+ def self.create_course(course_info)
8
+ # POST /api/v1/accounts/:account_id/courses
9
+ url = "#{ENV['CANVAS_API_PATH']}/accounts/1/courses"
10
+ payload = {
11
+ 'course[name]' => course_info[:name],
12
+ 'course[course_code]' => course_info[:course_code]
13
+ }
14
+ response = RestClient.post(url, payload, self.headers)
15
+ JSON.parse(response)
16
+ end
17
+
18
+ def self.create_module(course_id, module_info)
19
+ # POST /api/v1/courses/:course_id/modules
20
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/modules"
21
+ payload = {
22
+ 'module[name]' => module_info[:name]
23
+ }
24
+ response = RestClient.post(url, payload, headers={
25
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
26
+ })
27
+ JSON.parse(response)
28
+ end
29
+
30
+ def self.create_lesson(options, name, html)
31
+ if options[:type] == 'discussion'
32
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{options[:course_id]}/#{options[:type]}_topics"
33
+ else
34
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{options[:course_id]}/#{options[:type]}s"
35
+ end
36
+ payload = self.build_payload(options, name, html)
37
+ begin
38
+ response = RestClient.post(url, payload, self.headers)
39
+ rescue
40
+ puts "Something went wrong while pushing lesson #{options[:id]} to course #{options[:course_id]}"
41
+ abort
42
+ end
43
+ if ![200, 201].include? response.code
44
+ puts "Canvas push failed. #{response.code} status code returned "
45
+ abort
46
+ end
47
+ JSON.parse(response.body)
48
+ end
49
+
50
+ def self.add_to_module(course_id, module_info, lesson_info)
51
+ # POST /api/v1/courses/:course_id/modules/:module_id/items
52
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/modules/#{module_info["id"]}/items"
53
+
54
+ if lesson_info["type"] == "Page"
55
+ payload = {
56
+ 'module_item[title]' => lesson_info["title"],
57
+ 'module_item[type]' => lesson_info["type"],
58
+ 'module_item[indent]' => 0,
59
+ 'module_item[page_url]' => lesson_info["id"],
60
+ 'module_item[completion_requirement][type]' => 'must_view'
61
+ }
62
+ elsif lesson_info["type"] == "Quiz"
63
+ puts "Quiz needs to be added manually - #{lesson_info['title']} - lesson_info["
64
+ else
65
+
66
+ payload = {
67
+ 'module_item[title]' => lesson_info["title"],
68
+ 'module_item[type]' => lesson_info["type"],
69
+ 'module_item[indent]' => 1,
70
+ 'module_item[content_id]' => lesson_info["id"],
71
+ 'module_item[completion_requirement][type]' => 'must_submit'
72
+ }
73
+ end
74
+ begin
75
+ response = RestClient.post(url, payload, self.headers)
76
+ rescue
77
+ puts "Something went wrong while adding lesson #{lesson_info["id"]} to module #{module_info["id"]} in course #{course_id}" if lesson_info["type"] == "Assignment"
78
+ puts "Something went wrong while adding lesson #{lesson_info["page_url"]} to module #{module_info["id"]} in course #{course_id}" if lesson_info["type"] == "Page"
79
+ abort
80
+ end
81
+ response
82
+
83
+ end
84
+
85
+ def self.update_existing_lesson(options, name, html)
86
+ if options[:type] == "discussion"
87
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{options[:course_id]}/#{options[:type]}_topics/#{options[:id]}"
88
+ else
89
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{options[:course_id]}/#{options[:type]}s/#{options[:id]}"
90
+ end
91
+ payload = self.build_payload(options, name, html)
92
+
93
+ begin
94
+ headers = self.headers
95
+ if options[:type] == 'page' || options[:type] == 'Page'
96
+ response = RestClient.get(url, headers)
97
+ lesson_info = JSON.parse(response)
98
+ url = url.sub(/[^\/]+$/, lesson_info["page_id"].to_s)
99
+ end
100
+ response = RestClient.put(url, payload, headers)
101
+ rescue
102
+ puts "Something went wrong while pushing lesson #{options[:id]} to course #{options[:course_id]}"
103
+ puts "Make sure you are working on lessons that are not locked"
104
+ abort
105
+ ""
106
+ end
107
+ JSON.parse(response.body)
108
+ end
109
+
110
+ def self.update_all_related_lessons(options, name, html)
111
+ # Read the local .canvas file if --id <ID> is not used. Otherwise, use provided ID (--course <COURSE> also required)
112
+ if !options[:id]
113
+ canvas_data = CanvasDotfile.read_canvas_data
114
+ response = nil
115
+ canvas_data[:lessons] = canvas_data[:lessons].map { |lesson|
116
+ response = self.update_existing_lesson(lesson, name, html)
117
+ options[:id] = lesson[:id]
118
+ options[:course_id] = lesson[:course_id]
119
+ options[:type] = lesson[:type]
120
+
121
+ }
122
+ RepositoryInterface.local_repo_post_submission(options, response)
123
+ puts "Canvas lesson updated. Lesson available at #{response['html_url']}"
124
+ else
125
+ # If an ID (and course) are provided, they are used instead of the .canvas file
126
+ # Gets the current lesson's type (page or assignment)
127
+
128
+ options[:type] = self.get_lesson_info(options[:course_id], options[:id])[1]
129
+
130
+ # Implements update on Canvas
131
+ response = self.update_existing_lesson(options, name, html)
132
+ RepositoryInterface.local_repo_post_submission(options, response)
133
+ puts "Canvas lesson updated. Lesson available at #{response['html_url']}"
134
+ end
135
+
136
+ end
137
+
5
138
  def self.get_lesson_info(course, id)
6
139
 
7
140
  lesson_types = ["quizzes", "assignments", "pages", "discussion_topics"]
@@ -14,6 +147,7 @@ class CanvasInterface
14
147
  info = ""
15
148
  lesson_type_urls.each do |url|
16
149
  begin
150
+
17
151
  response = RestClient.get(url, headers={
18
152
  "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
19
153
  })
@@ -33,6 +167,7 @@ class CanvasInterface
33
167
  [info, type]
34
168
  end
35
169
 
170
+
36
171
  def self.get_course_info(course, id)
37
172
  if id
38
173
  lesson_data = self.get_lesson_info(course, id)
@@ -41,34 +176,64 @@ class CanvasInterface
41
176
  return
42
177
  end
43
178
 
179
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course}"
180
+ response = RestClient.get(url, self.headers)
181
+ course_data = JSON.parse(response)
182
+
183
+ # /api/v1/courses/:course_id/modules
184
+ course_info = {
185
+ name: course_data['name'],
186
+ id: course_data['id'],
187
+ modules: []
188
+ }
189
+
44
190
  begin
45
- results = []
46
191
  index = 1
47
192
 
48
193
  while !!index
49
- url = "#{ENV['CANVAS_API_PATH']}/courses/#{course}/pages?order=asc&sort=title&page=#{index}&per_page=10"
194
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course}/modules?page=#{index}&per_page=20"
50
195
  index += 1
51
- response = RestClient.get(url, headers={
52
- "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
53
- })
54
- pages = JSON.parse(response.body)
55
- if ([200, 201].include? response.code) && (!pages.empty?)
56
- results = results + pages
196
+ response = RestClient.get(url, self.headers)
197
+ modules = JSON.parse(response.body)
198
+
199
+ if ([200, 201].include? response.code) && (!modules.empty?)
200
+ course_info[:modules] = course_info[:modules] + modules
57
201
  else
58
202
  index = nil
59
203
  end
60
204
  end
61
- puts ""
62
- puts ""
63
- puts "Info for Course #{course} from #{ENV['CANVAS_API_PATH']}"
64
- puts ""
65
- puts "## Pages ##"
66
- puts "Title : Page ID"
67
- puts ""
68
-
69
- results.each {|result|
70
- puts "#{result['title']} : #{result['page_id']}"
71
- }
205
+
206
+ course_info[:modules] = course_info[:modules].map do |mod|
207
+ new_mod = {
208
+ id: mod['id'],
209
+ name: mod['name'],
210
+ lessons: []
211
+ }
212
+ index = 1
213
+ while !!index
214
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course}/modules/#{mod['id']}/items?page=#{index}&per_page=20"
215
+ index += 1
216
+ response = RestClient.get(url, headers={
217
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
218
+ })
219
+ lessons = JSON.parse(response.body)
220
+ lessons = lessons.map do |lesson|
221
+ lesson = lesson.slice("id","title","name","indent","type","html_url","page_url","url","completion_requirement", "published")
222
+ lesson["repository"] = ""
223
+ lesson['id'] = lesson['url'].gsub(/^(.*[\\\/])/,'')
224
+ lesson
225
+ end
226
+ if ([200, 201].include? response.code) && (!lessons.empty?)
227
+ new_mod[:lessons] = new_mod[:lessons] + lessons
228
+ else
229
+ index = nil
230
+ end
231
+
232
+ end
233
+ new_mod
234
+ end
235
+
236
+ puts course_info.to_yaml
72
237
 
73
238
  rescue
74
239
  puts "Something went wrong while getting info about course #{course}"
@@ -76,82 +241,218 @@ class CanvasInterface
76
241
  end
77
242
  end
78
243
 
79
- def self.submit_to_canvas(course_id, type, name, readme)
80
- response = self.push_to_canvas(course_id, type, name, readme)
81
- if ![200, 201].include? response.code
82
- puts "Canvas push failed. #{response.code} status code returned "
83
- abort
84
- end
85
- JSON.parse(response.body)
86
- end
244
+ def self.map_course_info(file_to_convert)
245
+ course_info = YAML.load(File.read("#{Dir.pwd}/#{file_to_convert}"))
246
+ course_info[:modules] = course_info[:modules].map do |mod|
247
+ mod[:lessons] = mod[:lessons].map do |lesson|
87
248
 
88
- def self.push_to_canvas(course_id, type, name, new_readme)
89
- if type == 'discussion'
90
- url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/#{type}_topics"
91
- else
92
- url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/#{type}s"
93
- end
94
- payload = self.build_payload(type, name, new_readme, false)
95
- begin
96
- RestClient.post(url, payload, headers={
97
- "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
98
- })
99
- rescue
100
- puts "Something went wrong while pushing lesson #{id} to course #{course_id}"
101
- abort
249
+ url = lesson["url"]
250
+ response = RestClient.get(url, headers={
251
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
252
+ })
253
+ begin
254
+ lesson_data = JSON.parse(response)
255
+ contents = lesson_data["body"] if lesson["type"] == "Page"
256
+ contents = lesson_data["message"] if lesson["type"] == "Discussion"
257
+ contents = lesson_data["description"] if lesson["type"] == "Assignment" || lesson["type"] == "Quiz"
258
+ if contents.nil?
259
+ repo = ""
260
+ else
261
+ if contents[/data-repo=\"(.*?)"/]
262
+ repo = contents[/data-repo=\"(.*?)"/]
263
+ repo = repo.slice(11..-2)
264
+ elsif contents[/class=\"fis-git-link\" href=\"(.*?)"/]
265
+ repo = contents[/class=\"fis-git-link\" href=\"(.*?)"/]
266
+ repo = repo.slice(27..-2)
267
+ else
268
+ repo = ""
269
+ end
270
+ end
271
+ rescue
272
+ puts 'Error while mapping course info.'
273
+ abort
274
+ end
275
+
276
+ if repo != nil && repo != ""
277
+ if repo.include?('https://github.com/learn-co-curriculum/')
278
+ lesson["repository"] = repo
279
+ else
280
+ lesson["repository"] = "https://github.com/learn-co-curriculum/" + repo
281
+ end
282
+ end
283
+ sleep(1)
284
+ lesson
285
+ end
286
+ mod
102
287
  end
288
+ puts course_info.to_yaml
103
289
  end
104
290
 
105
- def self.update_existing_lesson(course_id, id, type, name, new_readme, only_update_content)
106
- if type == "discussion"
107
- url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/#{type}_topics/#{id}"
108
- else
109
- url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/#{type}s/#{id}"
110
- end
111
- payload = self.build_payload(type, name, new_readme, only_update_content)
112
- begin
113
- RestClient.put(url, payload, headers={
114
- "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
115
- })
116
- rescue
117
- puts "Something went wrong while pushing lesson #{id} to course #{course_id}"
118
- nil
291
+ def self.csv(file_to_convert)
292
+ course_info = YAML.load(File.read("#{Dir.pwd}/#{file_to_convert}"))
293
+ course_info[:modules] = course_info[:modules].map do |mod|
294
+ mod[:lessons] = mod[:lessons].map do |lesson|
295
+
296
+ url = lesson["url"]
297
+ response = RestClient.get(url, headers={
298
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
299
+ })
300
+ begin
301
+ lesson_data = JSON.parse(response)
302
+ contents = lesson_data["body"] if lesson["type"] == "Page"
303
+ contents = lesson_data["message"] if lesson["type"] == "Discussion"
304
+ contents = lesson_data["description"] if lesson["type"] == "Assignment" || lesson["type"] == "Quiz"
305
+ if contents.nil?
306
+ repo = ""
307
+ else
308
+ if contents[/data-repo=\"(.*?)"/]
309
+ repo = contents[/data-repo=\"(.*?)"/]
310
+ repo = repo.slice(11..-2)
311
+ elsif contents[/class=\"fis-git-link\" href=\"(.*?)"/]
312
+ repo = contents[/class=\"fis-git-link\" href=\"(.*?)"/]
313
+ repo = repo.slice(27..-2)
314
+ else
315
+ repo = ""
316
+ end
317
+ end
318
+ rescue
319
+ puts 'Error while mapping course info.'
320
+ abort
321
+ end
322
+
323
+ if repo != nil && repo != ""
324
+ if repo.include?('https://github.com/learn-co-curriculum/')
325
+ lesson["repository"] = repo
326
+ else
327
+ lesson["repository"] = "https://github.com/learn-co-curriculum/" + repo
328
+ end
329
+ end
330
+ sleep(1)
331
+ lesson
332
+ end
333
+ mod
119
334
  end
335
+ byebug
336
+ puts course_info.to_yaml
120
337
  end
121
338
 
122
- def self.build_payload(type, name, new_readme, only_update_content)
123
- if only_update_content
124
- if type == "assignment"
339
+
340
+
341
+
342
+ def self.build_payload(options, name, html)
343
+ if options[:only_update_content]
344
+ if options[:type] == "assignment"
125
345
  payload = {
126
- 'assignment[description]' => new_readme
346
+ 'assignment[description]' => html
127
347
  }
128
- elsif type == "discussion"
348
+ elsif options[:type] == "discussion"
129
349
  payload = {
130
- 'message' => new_readme
350
+ 'message' => html
131
351
  }
132
352
  else
133
353
  payload = {
134
- 'wiki_page[body]' => new_readme
354
+ 'wiki_page[body]' => html
135
355
  }
136
356
  end
137
357
  else
138
- if type == "assignment"
358
+ if options[:type] == "assignment"
139
359
  payload = {
140
360
  'assignment[name]' => name,
141
- 'assignment[description]' => new_readme
361
+ 'assignment[description]' => html,
362
+ 'assignment[submission_types][]' => "online_url",
363
+ 'assignment[grading_type]' => 'pass_fail',
364
+ 'assignment[points_possible]' => 1
142
365
  }
143
- elsif type == "discussion"
366
+ elsif options[:type] == "discussion"
144
367
  payload = {
145
368
  'title' => name,
146
- 'message' => new_readme
369
+ 'message' => html
147
370
  }
148
371
  else
149
372
  payload = {
150
373
  'wiki_page[title]' => name,
151
- 'wiki_page[body]' => new_readme,
374
+ 'wiki_page[body]' => html,
152
375
  'wiki_page[editing_roles]' => "teachers"
153
376
  }
154
377
  end
155
378
  end
156
379
  end
380
+
381
+ def self.read_lesson(url)
382
+ types = ["page", "assignment", "quiz", "discussion"]
383
+ type = types.find {|type| url.match(type)}
384
+ if !url.include?(ENV['CANVAS_API_PATH'])
385
+ url = url.sub(/^.*\/\/.*?\//,"#{ENV['CANVAS_API_PATH']}/")
386
+ end
387
+
388
+ response = RestClient.get(url, headers={
389
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
390
+ })
391
+ lesson_info = JSON.parse(response)
392
+ lesson_info = lesson_info.slice("title",
393
+ "name",
394
+ "description",
395
+ "body",
396
+ "message",
397
+ "shuffle_answers",
398
+ "allowed_attempts",
399
+ "question_count"
400
+ )
401
+ lesson_info["type"] = type.capitalize
402
+ if lesson_info["type"] == "Quiz"
403
+ url = url + "/questions"
404
+ response = RestClient.get(url, headers={
405
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
406
+ })
407
+ lesson_info["questions"] = JSON.parse(response)
408
+ lesson_info["questions"] = lesson_info["questions"].map do |question|
409
+ question.slice("id",
410
+ "position",
411
+ "question_name",
412
+ "question_type",
413
+ "question_text",
414
+ "points_possible",
415
+ "correct_comments_html",
416
+ "incorrect_comments_html",
417
+ "neutral_comments_html",
418
+ "answers"
419
+ )
420
+ end
421
+ end
422
+ lesson_info.to_yaml
423
+ end
424
+
425
+ def self.create_lesson_from_remote(course_id, module_id, lesson_type, raw_url, yaml_file)
426
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/modules/#{module_id}/items"
427
+ if yaml_file
428
+ data = YAML.load(File.read("#{Dir.pwd}/#{yaml_file}"))
429
+ payload = {
430
+ 'module_item[type]' => data["type"],
431
+ 'module_item[title]' => data["title"]
432
+ }
433
+ else
434
+
435
+ end
436
+
437
+
438
+ end
439
+
440
+ def self.headers
441
+ {
442
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
443
+ }
444
+ end
445
+
446
+ # def self.create_quiz_from_remote(course_id, module_id, lesson_type, raw_url)
447
+ # url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/quizzes"
448
+ # payload = {
449
+ # 'quiz[title]' =>
450
+ # }
451
+ # /api/v1/courses/:course_id/quizzes
452
+ # data = YAML.load(File.read("#{Dir.pwd}/#{yaml_file}"))
453
+ # payload = {
454
+ # 'module_item[type]' => data["type"],
455
+ # 'module_item[title]' => data["title"]
456
+ # }
457
+ # end
157
458
  end