github-to-canvas-quiz 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +48 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/NOTES.md +53 -0
- data/README.md +89 -0
- data/Rakefile +8 -0
- data/bin/console +29 -0
- data/bin/github-to-canvas-quiz +15 -0
- data/bin/setup +8 -0
- data/github-to-canvas-quiz.gemspec +50 -0
- data/lib/github_to_canvas_quiz/builder/quiz.rb +110 -0
- data/lib/github_to_canvas_quiz/canvas_api/client.rb +80 -0
- data/lib/github_to_canvas_quiz/canvas_api/endpoints/quiz_questions.rb +29 -0
- data/lib/github_to_canvas_quiz/canvas_api/endpoints/quizzes.rb +25 -0
- data/lib/github_to_canvas_quiz/canvas_api/endpoints.rb +10 -0
- data/lib/github_to_canvas_quiz/cli.rb +31 -0
- data/lib/github_to_canvas_quiz/markdown_builder.rb +90 -0
- data/lib/github_to_canvas_quiz/markdown_converter.rb +44 -0
- data/lib/github_to_canvas_quiz/model/answer/fill_in_multiple_blanks.rb +34 -0
- data/lib/github_to_canvas_quiz/model/answer/matching.rb +35 -0
- data/lib/github_to_canvas_quiz/model/answer/multiple_answers.rb +33 -0
- data/lib/github_to_canvas_quiz/model/answer/multiple_choice.rb +33 -0
- data/lib/github_to_canvas_quiz/model/answer/multiple_dropdowns.rb +34 -0
- data/lib/github_to_canvas_quiz/model/answer/short_answer.rb +33 -0
- data/lib/github_to_canvas_quiz/model/answer/true_false.rb +33 -0
- data/lib/github_to_canvas_quiz/model/question.rb +61 -0
- data/lib/github_to_canvas_quiz/model/quiz.rb +62 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/base.rb +19 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/fill_in_multiple_blanks.rb +30 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/matching.rb +31 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/multiple_answers.rb +33 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/multiple_choice.rb +33 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/multiple_dropdowns.rb +30 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/short_answer.rb +29 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer/true_false.rb +29 -0
- data/lib/github_to_canvas_quiz/parser/canvas/answer.rb +34 -0
- data/lib/github_to_canvas_quiz/parser/canvas/helpers.rb +25 -0
- data/lib/github_to_canvas_quiz/parser/canvas/question.rb +55 -0
- data/lib/github_to_canvas_quiz/parser/canvas/quiz.rb +44 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/base.rb +22 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/fill_in_multiple_blanks.rb +21 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/matching.rb +23 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/multiple_answers.rb +21 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/multiple_choice.rb +19 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/multiple_dropdowns.rb +21 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/short_answer.rb +21 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer/true_false.rb +21 -0
- data/lib/github_to_canvas_quiz/parser/markdown/answer.rb +34 -0
- data/lib/github_to_canvas_quiz/parser/markdown/base.rb +22 -0
- data/lib/github_to_canvas_quiz/parser/markdown/helpers/node_parser.rb +21 -0
- data/lib/github_to_canvas_quiz/parser/markdown/helpers/node_scanner.rb +208 -0
- data/lib/github_to_canvas_quiz/parser/markdown/question.rb +76 -0
- data/lib/github_to_canvas_quiz/parser/markdown/quiz.rb +42 -0
- data/lib/github_to_canvas_quiz/repository_interface.rb +42 -0
- data/lib/github_to_canvas_quiz/reverse_markdown/converters/p.rb +20 -0
- data/lib/github_to_canvas_quiz/reverse_markdown/converters/pre.rb +51 -0
- data/lib/github_to_canvas_quiz/reverse_markdown/register.rb +9 -0
- data/lib/github_to_canvas_quiz/synchronizer/quiz.rb +140 -0
- data/lib/github_to_canvas_quiz/version.rb +5 -0
- data/lib/github_to_canvas_quiz.rb +62 -0
- metadata +348 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
class CLI < Thor
|
5
|
+
option :course, type: :numeric, required: true, desc: 'Canvas Course ID'
|
6
|
+
option :quiz, type: :numeric, required: true, desc: 'Canvas Quiz ID'
|
7
|
+
option :directory, default: '.',
|
8
|
+
desc: '(optional) Directory to output markdown files. Defaults to current directory'
|
9
|
+
desc 'backup', 'Backup a Canvas quiz and its questions as markdown'
|
10
|
+
def backup
|
11
|
+
puts '⬇️ Converting quiz...'
|
12
|
+
Builder::Quiz.new(client, options[:course], options[:quiz], options[:directory]).build
|
13
|
+
puts '✅ Done'
|
14
|
+
end
|
15
|
+
|
16
|
+
option :directory, default: '.',
|
17
|
+
desc: '(optional) Directory with markdown files to align. Defaults to current directory'
|
18
|
+
desc 'align', 'Updates a Canvas quiz from Markdown files.'
|
19
|
+
def align
|
20
|
+
puts '⬆️ Aligning quiz...'
|
21
|
+
Synchronizer::Quiz.new(client, options[:directory]).sync
|
22
|
+
puts '✅ Done'
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def client
|
28
|
+
@client ||= CanvasAPI::Client.new(api_key: ENV['CANVAS_API_KEY'], host: ENV['CANVAS_API_PATH'])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
# Custom DSL for building a Markdown string
|
5
|
+
class MarkdownBuilder
|
6
|
+
# Provides a custom DSL for building a Markdown string
|
7
|
+
# Usage:
|
8
|
+
#
|
9
|
+
# MarkdownBuilder.build do |md|
|
10
|
+
# md.h1('Hello')
|
11
|
+
# md.ul('item 1', 'item 2')
|
12
|
+
# md.blockquote('comment')
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # => "# Hello\n\n- item 1\n- item 2\n\n> comment\n"
|
16
|
+
def self.build
|
17
|
+
raise ArgumentError, 'no block given' unless block_given?
|
18
|
+
|
19
|
+
builder = new
|
20
|
+
yield(builder)
|
21
|
+
builder.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :blocks
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@blocks = []
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String] markdown produced by joining all blocks
|
31
|
+
def to_s
|
32
|
+
"#{blocks.join("\n\n")}\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds a frontmatter block
|
36
|
+
# @param hash [Hash] the data to covert to YAML for the frontmatter
|
37
|
+
def frontmatter(hash)
|
38
|
+
blocks << "#{hash.to_yaml.strip}\n---"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Adds a H1 heading
|
42
|
+
# @param text [String] heading text
|
43
|
+
def h1(text)
|
44
|
+
blocks << "# #{text}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds a H2 heading
|
48
|
+
# @param text [String] heading text
|
49
|
+
def h2(text)
|
50
|
+
blocks << "## #{text}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Adds a paragraph
|
54
|
+
# @param text [String] paragraph text
|
55
|
+
def p(text)
|
56
|
+
blocks << escape(text)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Adds a blockquote
|
60
|
+
# @param text [String] blockquote text
|
61
|
+
def blockquote(text)
|
62
|
+
text = text.gsub("\n", "\n> ")
|
63
|
+
blocks << "> #{text}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Adds multiple list items
|
67
|
+
# @param texts* [Strings] a list of text to convert to list items
|
68
|
+
def ul(*texts)
|
69
|
+
blocks << texts.map { |text| "- #{escape(text)}" }.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Adds a block of arbitrary markdown
|
73
|
+
# @param markdown [String] markdown string
|
74
|
+
def md(markdown)
|
75
|
+
blocks << markdown.strip
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: This doesn't belong here...
|
79
|
+
def html_to_markdown(html)
|
80
|
+
ReverseMarkdown.convert(html, github_flavored: true).strip
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# TODO: What needs escaping??
|
86
|
+
def escape(text)
|
87
|
+
text.gsub('_', '\_')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
# Custom HTML renderer for Redcarpet, using Rouge for syntax highlighting
|
5
|
+
class HTMLRenderer < Redcarpet::Render::HTML
|
6
|
+
include Rouge::Plugins::Redcarpet
|
7
|
+
end
|
8
|
+
|
9
|
+
# Convert a string of Markdown to HTML using Redcarpet and Rouge.
|
10
|
+
#
|
11
|
+
# Useage:
|
12
|
+
#
|
13
|
+
# MarkdownConverter.new("# Hello\n\nWorld\n").to_html
|
14
|
+
# # => "<h1>Hello</h1>\n\n<p>World</p>\n"
|
15
|
+
class MarkdownConverter
|
16
|
+
attr_reader :options, :markdown
|
17
|
+
|
18
|
+
OPTIONS = {
|
19
|
+
tables: true,
|
20
|
+
autolink: true,
|
21
|
+
fenced_code_blocks: true,
|
22
|
+
disable_indented_code_blocks: true,
|
23
|
+
no_intra_emphasis: true
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
# @param [String] markdown The markdown to be converted
|
27
|
+
# @param [Hash] options Overrides the defaults for the [Redcarpet](https://github.com/vmg/redcarpet) gem
|
28
|
+
def initialize(markdown, options = {})
|
29
|
+
@options = OPTIONS.merge(options)
|
30
|
+
@markdown = markdown
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String] the markdown converted to HTML
|
34
|
+
def to_html
|
35
|
+
Redcarpet::Markdown.new(renderer, options).render(markdown)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def renderer
|
41
|
+
HTMLRenderer.new(escape_html: true)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class FillInMultipleBlanks
|
7
|
+
attr_accessor :title, :text, :comments, :blank_id
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.ul(text, blank_id)
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_text' => text,
|
26
|
+
'answer_weight' => title == 'Correct' ? 100 : 0,
|
27
|
+
'answer_comment_html' => comments,
|
28
|
+
'blank_id' => blank_id
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class Matching
|
7
|
+
attr_accessor :title, :text, :comments, :left, :right
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.ul(left, right)
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_text' => left,
|
26
|
+
'answer_weight' => 100,
|
27
|
+
'answer_comment_html' => comments,
|
28
|
+
'answer_match_left' => left,
|
29
|
+
'answer_match_right' => right
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class MultipleAnswers
|
7
|
+
attr_accessor :title, :text, :comments
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.md(md.html_to_markdown(text))
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_html' => text,
|
26
|
+
'answer_weight' => title == 'Correct' ? 100 : 0,
|
27
|
+
'answer_comment_html' => comments
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class MultipleChoice
|
7
|
+
attr_accessor :title, :text, :comments
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.md(md.html_to_markdown(text))
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_html' => text,
|
26
|
+
'answer_weight' => title == 'Correct' ? 100 : 0,
|
27
|
+
'answer_comment_html' => comments
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class MultipleDropdowns
|
7
|
+
attr_accessor :title, :text, :comments, :blank_id
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.ul(text, blank_id)
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_text' => text,
|
26
|
+
'answer_weight' => title == 'Correct' ? 100 : 0,
|
27
|
+
'answer_comment_html' => comments,
|
28
|
+
'blank_id' => blank_id
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class ShortAnswer
|
7
|
+
attr_accessor :title, :text, :comments
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.p(text)
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_text' => text,
|
26
|
+
'answer_weight' => title == 'Correct' ? 100 : 0,
|
27
|
+
'answer_comment_html' => comments
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
module Answer
|
6
|
+
class TrueFalse
|
7
|
+
attr_accessor :title, :text, :comments
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
options.each do |key, value|
|
11
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_markdown
|
16
|
+
MarkdownBuilder.build do |md|
|
17
|
+
md.h2(title)
|
18
|
+
md.p(text)
|
19
|
+
md.blockquote(md.html_to_markdown(comments)) unless comments.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'answer_text' => text,
|
26
|
+
'answer_weight' => title == 'Correct' ? 100 : 0,
|
27
|
+
'answer_comment_html' => comments
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
class Question
|
6
|
+
attr_accessor :course_id, :quiz_id, :id, :type, :sources, :name, :description, :answers, :distractors
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
options.each do |key, value|
|
10
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_markdown
|
15
|
+
MarkdownBuilder.build do |md|
|
16
|
+
md.frontmatter(frontmatter_hash)
|
17
|
+
md.h1(name)
|
18
|
+
md.md(md.html_to_markdown(description))
|
19
|
+
answers.each do |answer|
|
20
|
+
md.md(answer.to_markdown)
|
21
|
+
end
|
22
|
+
unless distractors.empty?
|
23
|
+
md.h2('Incorrect')
|
24
|
+
md.ul(*distractors)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h
|
30
|
+
{
|
31
|
+
'question_name' => name,
|
32
|
+
'question_text' => description,
|
33
|
+
'question_type' => type,
|
34
|
+
'points_possible' => 1,
|
35
|
+
'neutral_comments_html' => sources.nil? || sources.empty? ? '' : sources_to_html,
|
36
|
+
'answers' => answers.map(&:to_h),
|
37
|
+
'matching_answer_incorrect_matches' => distractors.join("\n")
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def frontmatter_hash
|
42
|
+
{
|
43
|
+
'course_id' => course_id,
|
44
|
+
'quiz_id' => quiz_id,
|
45
|
+
'id' => id,
|
46
|
+
'type' => type,
|
47
|
+
'sources' => sources
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def sources_to_html
|
54
|
+
comments = sources.map do |source|
|
55
|
+
"<a href=\"#{source['url']}\">#{source['name']}</a>"
|
56
|
+
end.join
|
57
|
+
"<p><strong>Source/s:</strong> #{comments}</p>"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Model
|
5
|
+
class Quiz
|
6
|
+
attr_accessor :course_id, :id, :repo, :title, :description
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
options.each do |key, value|
|
10
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_markdown
|
15
|
+
MarkdownBuilder.build do |md|
|
16
|
+
md.frontmatter(frontmatter_hash)
|
17
|
+
md.h1(title)
|
18
|
+
md.md(md.html_to_markdown(description))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_h
|
23
|
+
{
|
24
|
+
'title' => title,
|
25
|
+
'description' => description_with_header,
|
26
|
+
'quiz_type' => 'assignment',
|
27
|
+
'shuffle_answers' => true,
|
28
|
+
'hide_results' => 'until_after_last_attempt',
|
29
|
+
'show_correct_answers_last_attempt' => true,
|
30
|
+
'allowed_attempts' => 3,
|
31
|
+
'scoring_policy' => 'keep_highest',
|
32
|
+
'one_question_at_a_time' => true
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def frontmatter_hash
|
37
|
+
{
|
38
|
+
'id' => id,
|
39
|
+
'course_id' => course_id,
|
40
|
+
'repo' => repo
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def description_with_header
|
47
|
+
[git_links_header, description].reject(&:nil?).join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def git_links_header
|
51
|
+
return unless repo
|
52
|
+
|
53
|
+
<<~HTML
|
54
|
+
<div id='git-data-element' data-org='learn-co-curriculum' data-repo='#{repo}'></div>
|
55
|
+
<header class='fis-header'>
|
56
|
+
<a class='fis-git-link' href='https://github.com/learn-co-curriculum/#{repo}/issues/new' target='_blank' rel='noopener'><img id='issue-img' title='Create New Issue' alt='Create New Issue' /></a>
|
57
|
+
</header>
|
58
|
+
HTML
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Parser
|
5
|
+
module Canvas
|
6
|
+
module Answer
|
7
|
+
class Base
|
8
|
+
include Helpers
|
9
|
+
|
10
|
+
attr_reader :data
|
11
|
+
|
12
|
+
def initialize(data)
|
13
|
+
@data = data
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Parser
|
5
|
+
module Canvas
|
6
|
+
module Answer
|
7
|
+
class FillInMultipleBlanks < Base
|
8
|
+
def parse
|
9
|
+
Model::Answer::FillInMultipleBlanks.new(
|
10
|
+
title: title,
|
11
|
+
text: data['text'],
|
12
|
+
comments: comments,
|
13
|
+
blank_id: data['blank_id']
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def title
|
20
|
+
data.fetch('weight', 0).positive? ? 'Correct' : 'Incorrect'
|
21
|
+
end
|
22
|
+
|
23
|
+
def comments
|
24
|
+
choose_text(data.fetch('comments', ''), data.fetch('comments_html', ''))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Parser
|
5
|
+
module Canvas
|
6
|
+
module Answer
|
7
|
+
class Matching < Base
|
8
|
+
def parse
|
9
|
+
Model::Answer::Matching.new(
|
10
|
+
title: title,
|
11
|
+
text: data['text'],
|
12
|
+
comments: comments,
|
13
|
+
left: data['left'],
|
14
|
+
right: data['right']
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def title
|
21
|
+
data['left'].empty? ? 'Incorrect' : 'Correct'
|
22
|
+
end
|
23
|
+
|
24
|
+
def comments
|
25
|
+
choose_text(data.fetch('comments', ''), data.fetch('comments_html', ''))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Parser
|
5
|
+
module Canvas
|
6
|
+
module Answer
|
7
|
+
class MultipleAnswers < Base
|
8
|
+
def parse
|
9
|
+
Model::Answer::MultipleAnswers.new(
|
10
|
+
title: title,
|
11
|
+
text: text,
|
12
|
+
comments: comments
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def title
|
19
|
+
data.fetch('weight', 0).positive? ? 'Correct' : 'Incorrect'
|
20
|
+
end
|
21
|
+
|
22
|
+
def text
|
23
|
+
choose_text(data.fetch('text', ''), data.fetch('html', ''))
|
24
|
+
end
|
25
|
+
|
26
|
+
def comments
|
27
|
+
choose_text(data.fetch('comments', ''), data.fetch('comments_html', ''))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GithubToCanvasQuiz
|
4
|
+
module Parser
|
5
|
+
module Canvas
|
6
|
+
module Answer
|
7
|
+
class MultipleChoice < Base
|
8
|
+
def parse
|
9
|
+
Model::Answer::MultipleChoice.new(
|
10
|
+
title: title,
|
11
|
+
text: text,
|
12
|
+
comments: comments
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def title
|
19
|
+
data.fetch('weight', 0).positive? ? 'Correct' : 'Incorrect'
|
20
|
+
end
|
21
|
+
|
22
|
+
def text
|
23
|
+
choose_text(data.fetch('text', ''), data.fetch('html', ''))
|
24
|
+
end
|
25
|
+
|
26
|
+
def comments
|
27
|
+
choose_text(data.fetch('comments', ''), data.fetch('comments_html', ''))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|