github-to-canvas 0.0.58 → 0.1.0

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.
@@ -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