github-to-canvas 0.0.58 → 0.1.0

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