github-to-canvas 0.0.58 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,219 @@ 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(options)
245
+ course_info = YAML.load(File.read("#{Dir.pwd}/#{options[: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
+ puts lesson["repository"] if options[:urls_only]
282
+ end
283
+ end
284
+ sleep(1)
285
+ lesson
286
+ end
287
+ mod
102
288
  end
289
+ puts course_info.to_yaml if !options[:urls_only]
103
290
  end
104
291
 
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
- ""
292
+ def self.csv(file_to_convert)
293
+ course_info = YAML.load(File.read("#{Dir.pwd}/#{file_to_convert}"))
294
+ course_info[:modules] = course_info[:modules].map do |mod|
295
+ mod[:lessons] = mod[:lessons].map do |lesson|
296
+
297
+ url = lesson["url"]
298
+ response = RestClient.get(url, headers={
299
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
300
+ })
301
+ begin
302
+ lesson_data = JSON.parse(response)
303
+ contents = lesson_data["body"] if lesson["type"] == "Page"
304
+ contents = lesson_data["message"] if lesson["type"] == "Discussion"
305
+ contents = lesson_data["description"] if lesson["type"] == "Assignment" || lesson["type"] == "Quiz"
306
+ if contents.nil?
307
+ repo = ""
308
+ else
309
+ if contents[/data-repo=\"(.*?)"/]
310
+ repo = contents[/data-repo=\"(.*?)"/]
311
+ repo = repo.slice(11..-2)
312
+ elsif contents[/class=\"fis-git-link\" href=\"(.*?)"/]
313
+ repo = contents[/class=\"fis-git-link\" href=\"(.*?)"/]
314
+ repo = repo.slice(27..-2)
315
+ else
316
+ repo = ""
317
+ end
318
+ end
319
+ rescue
320
+ puts 'Error while mapping course info.'
321
+ abort
322
+ end
323
+
324
+ if repo != nil && repo != ""
325
+ if repo.include?('https://github.com/learn-co-curriculum/')
326
+ lesson["repository"] = repo
327
+ else
328
+ lesson["repository"] = "https://github.com/learn-co-curriculum/" + repo
329
+ end
330
+ end
331
+ sleep(1)
332
+ lesson
333
+ end
334
+ mod
119
335
  end
336
+ byebug
337
+ puts course_info.to_yaml
120
338
  end
121
339
 
122
- def self.build_payload(type, name, new_readme, only_update_content)
123
- if only_update_content
124
- if type == "assignment"
340
+
341
+
342
+
343
+ def self.build_payload(options, name, html)
344
+ if options[:only_update_content]
345
+ if options[:type] == "assignment"
125
346
  payload = {
126
- 'assignment[description]' => new_readme
347
+ 'assignment[description]' => html
127
348
  }
128
- elsif type == "discussion"
349
+ elsif options[:type] == "discussion"
129
350
  payload = {
130
- 'message' => new_readme
351
+ 'message' => html
131
352
  }
132
353
  else
133
354
  payload = {
134
- 'wiki_page[body]' => new_readme
355
+ 'wiki_page[body]' => html
135
356
  }
136
357
  end
137
358
  else
138
- if type == "assignment"
359
+ if options[:type] == "assignment"
139
360
  payload = {
140
361
  'assignment[name]' => name,
141
- 'assignment[description]' => new_readme
362
+ 'assignment[description]' => html,
363
+ 'assignment[submission_types][]' => "online_url",
364
+ 'assignment[grading_type]' => 'pass_fail',
365
+ 'assignment[points_possible]' => 1
142
366
  }
143
- elsif type == "discussion"
367
+ elsif options[:type] == "discussion"
144
368
  payload = {
145
369
  'title' => name,
146
- 'message' => new_readme
370
+ 'message' => html
147
371
  }
148
372
  else
149
373
  payload = {
150
374
  'wiki_page[title]' => name,
151
- 'wiki_page[body]' => new_readme,
375
+ 'wiki_page[body]' => html,
152
376
  'wiki_page[editing_roles]' => "teachers"
153
377
  }
154
378
  end
155
379
  end
156
380
  end
381
+
382
+ def self.read_lesson(url)
383
+ types = ["page", "assignment", "quiz", "discussion"]
384
+ type = types.find {|type| url.match(type)}
385
+ if !url.include?(ENV['CANVAS_API_PATH'])
386
+ url = url.sub(/^.*\/\/.*?\//,"#{ENV['CANVAS_API_PATH']}/")
387
+ end
388
+
389
+ response = RestClient.get(url, headers={
390
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
391
+ })
392
+ lesson_info = JSON.parse(response)
393
+ lesson_info = lesson_info.slice("title",
394
+ "name",
395
+ "description",
396
+ "body",
397
+ "message",
398
+ "shuffle_answers",
399
+ "allowed_attempts",
400
+ "question_count"
401
+ )
402
+ lesson_info["type"] = type.capitalize
403
+ if lesson_info["type"] == "Quiz"
404
+ url = url + "/questions"
405
+ response = RestClient.get(url, headers={
406
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
407
+ })
408
+ lesson_info["questions"] = JSON.parse(response)
409
+ lesson_info["questions"] = lesson_info["questions"].map do |question|
410
+ question.slice("id",
411
+ "position",
412
+ "question_name",
413
+ "question_type",
414
+ "question_text",
415
+ "points_possible",
416
+ "correct_comments_html",
417
+ "incorrect_comments_html",
418
+ "neutral_comments_html",
419
+ "answers"
420
+ )
421
+ end
422
+ end
423
+ lesson_info.to_yaml
424
+ end
425
+
426
+ def self.create_lesson_from_remote(course_id, module_id, lesson_type, raw_url, yaml_file)
427
+ url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/modules/#{module_id}/items"
428
+ if yaml_file
429
+ data = YAML.load(File.read("#{Dir.pwd}/#{yaml_file}"))
430
+ payload = {
431
+ 'module_item[type]' => data["type"],
432
+ 'module_item[title]' => data["title"]
433
+ }
434
+ else
435
+
436
+ end
437
+
438
+
439
+ end
440
+
441
+ def self.headers
442
+ {
443
+ "Authorization" => "Bearer #{ENV['CANVAS_API_KEY']}"
444
+ }
445
+ end
446
+
447
+ # def self.create_quiz_from_remote(course_id, module_id, lesson_type, raw_url)
448
+ # url = "#{ENV['CANVAS_API_PATH']}/courses/#{course_id}/quizzes"
449
+ # payload = {
450
+ # 'quiz[title]' =>
451
+ # }
452
+ # /api/v1/courses/:course_id/quizzes
453
+ # data = YAML.load(File.read("#{Dir.pwd}/#{yaml_file}"))
454
+ # payload = {
455
+ # 'module_item[type]' => data["type"],
456
+ # 'module_item[title]' => data["title"]
457
+ # }
458
+ # end
157
459
  end