canvas_cc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/canvas_cc.gemspec +37 -0
  8. data/lib/canvas_cc/canvas/assessment.rb +124 -0
  9. data/lib/canvas_cc/canvas/assignment.rb +98 -0
  10. data/lib/canvas_cc/canvas/converter.rb +30 -0
  11. data/lib/canvas_cc/canvas/course.rb +143 -0
  12. data/lib/canvas_cc/canvas/discussion_topic.rb +64 -0
  13. data/lib/canvas_cc/canvas/label.rb +8 -0
  14. data/lib/canvas_cc/canvas/question.rb +492 -0
  15. data/lib/canvas_cc/canvas/question_bank.rb +57 -0
  16. data/lib/canvas_cc/canvas/question_group.rb +35 -0
  17. data/lib/canvas_cc/canvas/resource.rb +16 -0
  18. data/lib/canvas_cc/canvas/templates/assignment.html.erb +9 -0
  19. data/lib/canvas_cc/canvas/templates/syllabus.html.erb +9 -0
  20. data/lib/canvas_cc/canvas/templates/wiki_content.html.erb +10 -0
  21. data/lib/canvas_cc/canvas/web_content.rb +15 -0
  22. data/lib/canvas_cc/canvas/web_link.rb +16 -0
  23. data/lib/canvas_cc/canvas/wiki.rb +26 -0
  24. data/lib/canvas_cc/canvas_cc/assessment_writer.rb +82 -0
  25. data/lib/canvas_cc/canvas_cc/assignment_group_writer.rb +48 -0
  26. data/lib/canvas_cc/canvas_cc/assignment_writer.rb +68 -0
  27. data/lib/canvas_cc/canvas_cc/calculated_question_writer.rb +72 -0
  28. data/lib/canvas_cc/canvas_cc/canvas_export_writer.rb +18 -0
  29. data/lib/canvas_cc/canvas_cc/cartridge_creator.rb +60 -0
  30. data/lib/canvas_cc/canvas_cc/course_setting_writer.rb +35 -0
  31. data/lib/canvas_cc/canvas_cc/discussion_writer.rb +52 -0
  32. data/lib/canvas_cc/canvas_cc/essay_question_writer.rb +19 -0
  33. data/lib/canvas_cc/canvas_cc/file_meta_writer.rb +38 -0
  34. data/lib/canvas_cc/canvas_cc/ims_manifest_generator.rb +123 -0
  35. data/lib/canvas_cc/canvas_cc/matching_question_writer.rb +43 -0
  36. data/lib/canvas_cc/canvas_cc/models/answer.rb +12 -0
  37. data/lib/canvas_cc/canvas_cc/models/assessment.rb +102 -0
  38. data/lib/canvas_cc/canvas_cc/models/assignment.rb +30 -0
  39. data/lib/canvas_cc/canvas_cc/models/assignment_group.rb +9 -0
  40. data/lib/canvas_cc/canvas_cc/models/calculated_question.rb +19 -0
  41. data/lib/canvas_cc/canvas_cc/models/canvas_file.rb +22 -0
  42. data/lib/canvas_cc/canvas_cc/models/canvas_module.rb +10 -0
  43. data/lib/canvas_cc/canvas_cc/models/course.rb +53 -0
  44. data/lib/canvas_cc/canvas_cc/models/discussion.rb +36 -0
  45. data/lib/canvas_cc/canvas_cc/models/discussion_topic.rb +5 -0
  46. data/lib/canvas_cc/canvas_cc/models/matching_question.rb +7 -0
  47. data/lib/canvas_cc/canvas_cc/models/module_item.rb +16 -0
  48. data/lib/canvas_cc/canvas_cc/models/multiple_dropdowns_question.rb +7 -0
  49. data/lib/canvas_cc/canvas_cc/models/numerical_question.rb +7 -0
  50. data/lib/canvas_cc/canvas_cc/models/page.rb +41 -0
  51. data/lib/canvas_cc/canvas_cc/models/question.rb +37 -0
  52. data/lib/canvas_cc/canvas_cc/models/question_bank.rb +42 -0
  53. data/lib/canvas_cc/canvas_cc/models/question_group.rb +10 -0
  54. data/lib/canvas_cc/canvas_cc/models/resource.rb +23 -0
  55. data/lib/canvas_cc/canvas_cc/models/web_content.rb +7 -0
  56. data/lib/canvas_cc/canvas_cc/models/web_link.rb +7 -0
  57. data/lib/canvas_cc/canvas_cc/models/workflow_state.rb +6 -0
  58. data/lib/canvas_cc/canvas_cc/module_meta_writer.rb +52 -0
  59. data/lib/canvas_cc/canvas_cc/multiple_answers_question_writer.rb +41 -0
  60. data/lib/canvas_cc/canvas_cc/multiple_blanks_question_writer.rb +51 -0
  61. data/lib/canvas_cc/canvas_cc/multiple_choice_question_writer.rb +48 -0
  62. data/lib/canvas_cc/canvas_cc/multiple_dropdowns_question_writer.rb +30 -0
  63. data/lib/canvas_cc/canvas_cc/numerical_question_writer.rb +40 -0
  64. data/lib/canvas_cc/canvas_cc/page_writer.rb +38 -0
  65. data/lib/canvas_cc/canvas_cc/question_bank_writer.rb +44 -0
  66. data/lib/canvas_cc/canvas_cc/question_group_writer.rb +23 -0
  67. data/lib/canvas_cc/canvas_cc/question_writer.rb +129 -0
  68. data/lib/canvas_cc/canvas_cc/short_answer_question_writer.rb +42 -0
  69. data/lib/canvas_cc/canvas_cc/text_only_question_writer.rb +19 -0
  70. data/lib/canvas_cc/canvas_cc/true_false_question_writer.rb +38 -0
  71. data/lib/canvas_cc/cc/assessment.rb +25 -0
  72. data/lib/canvas_cc/cc/assignment.rb +61 -0
  73. data/lib/canvas_cc/cc/cc_helper.rb +285 -0
  74. data/lib/canvas_cc/cc/converter.rb +137 -0
  75. data/lib/canvas_cc/cc/course.rb +16 -0
  76. data/lib/canvas_cc/cc/discussion_topic.rb +53 -0
  77. data/lib/canvas_cc/cc/label.rb +6 -0
  78. data/lib/canvas_cc/cc/question.rb +23 -0
  79. data/lib/canvas_cc/cc/resource.rb +30 -0
  80. data/lib/canvas_cc/cc/templates/assignment.html.erb +13 -0
  81. data/lib/canvas_cc/cc/templates/syllabus.html.erb +9 -0
  82. data/lib/canvas_cc/cc/templates/wiki_content.html.erb +10 -0
  83. data/lib/canvas_cc/cc/web_content.rb +48 -0
  84. data/lib/canvas_cc/cc/web_link.rb +75 -0
  85. data/lib/canvas_cc/cc/wiki.rb +73 -0
  86. data/lib/canvas_cc/version.rb +3 -0
  87. data/lib/canvas_cc.rb +94 -0
  88. data/spec/moodle2cc/canvas_cc/assessment_writer_spec.rb +95 -0
  89. data/spec/moodle2cc/canvas_cc/assignment_group_writer_spec.rb +51 -0
  90. data/spec/moodle2cc/canvas_cc/assignment_writer_spec.rb +74 -0
  91. data/spec/moodle2cc/canvas_cc/calculated_question_writer_spec.rb +88 -0
  92. data/spec/moodle2cc/canvas_cc/canvas_export_writer_spec.rb +26 -0
  93. data/spec/moodle2cc/canvas_cc/cartridge_creator_spec.rb +50 -0
  94. data/spec/moodle2cc/canvas_cc/course_setting_writer_spec.rb +47 -0
  95. data/spec/moodle2cc/canvas_cc/discussion_writer_spec.rb +38 -0
  96. data/spec/moodle2cc/canvas_cc/essay_question_writer_spec.rb +31 -0
  97. data/spec/moodle2cc/canvas_cc/file_meta_writer_spec.rb +57 -0
  98. data/spec/moodle2cc/canvas_cc/ims_manifest_generator_spec.rb +141 -0
  99. data/spec/moodle2cc/canvas_cc/matching_question_writer_spec.rb +51 -0
  100. data/spec/moodle2cc/canvas_cc/models/assessment_spec.rb +131 -0
  101. data/spec/moodle2cc/canvas_cc/models/assignment_group_spec.rb +9 -0
  102. data/spec/moodle2cc/canvas_cc/models/assignment_spec.rb +45 -0
  103. data/spec/moodle2cc/canvas_cc/models/calculated_question_spec.rb +25 -0
  104. data/spec/moodle2cc/canvas_cc/models/canvas_file_spec.rb +23 -0
  105. data/spec/moodle2cc/canvas_cc/models/canvas_module_spec.rb +16 -0
  106. data/spec/moodle2cc/canvas_cc/models/course_spec.rb +43 -0
  107. data/spec/moodle2cc/canvas_cc/models/discussion_spec.rb +47 -0
  108. data/spec/moodle2cc/canvas_cc/models/discussion_topic_spec.rb +7 -0
  109. data/spec/moodle2cc/canvas_cc/models/module_item_spec.rb +15 -0
  110. data/spec/moodle2cc/canvas_cc/models/page_spec.rb +34 -0
  111. data/spec/moodle2cc/canvas_cc/models/question_spec.rb +8 -0
  112. data/spec/moodle2cc/canvas_cc/models/resource_spec.rb +17 -0
  113. data/spec/moodle2cc/canvas_cc/models/web_content_spec.rb +7 -0
  114. data/spec/moodle2cc/canvas_cc/models/web_link_spec.rb +9 -0
  115. data/spec/moodle2cc/canvas_cc/module_meta_writer_spec.rb +118 -0
  116. data/spec/moodle2cc/canvas_cc/multiple_answers_question_writer_spec.rb +50 -0
  117. data/spec/moodle2cc/canvas_cc/multiple_blanks_question_writer_spec.rb +77 -0
  118. data/spec/moodle2cc/canvas_cc/multiple_choice_question_writer_spec.rb +69 -0
  119. data/spec/moodle2cc/canvas_cc/multiple_dropdowns_question_writer_spec.rb +46 -0
  120. data/spec/moodle2cc/canvas_cc/numerical_question_writer_spec.rb +69 -0
  121. data/spec/moodle2cc/canvas_cc/page_writer_spec.rb +29 -0
  122. data/spec/moodle2cc/canvas_cc/question_bank_writer_spec.rb +43 -0
  123. data/spec/moodle2cc/canvas_cc/question_writer_spec.rb +129 -0
  124. data/spec/moodle2cc/canvas_cc/short_answer_question_writer_spec.rb +50 -0
  125. data/spec/moodle2cc/canvas_cc/text_only_question_writer_spec.rb +23 -0
  126. data/spec/moodle2cc/canvas_cc/true_false_question_writer_spec.rb +62 -0
  127. data/spec/spec_helper.rb +12 -0
  128. data/spec/support/shared_examples_for_models.rb +26 -0
  129. data/test/acceptance/migrator_test.rb +24 -0
  130. data/test/fixtures/cc.imscc +0 -0
  131. data/test/fixtures/common_cartridge/ims_manifest_simple.xml +45 -0
  132. data/test/fixtures/common_cartridge/schema/cccv1p0.xsd +563 -0
  133. data/test/fixtures/common_cartridge/schema/ccv1p1_imsccauth_v1p1.xsd +203 -0
  134. data/test/fixtures/common_cartridge/schema/ccv1p1_imscp_v1p2_v1p0.xsd +827 -0
  135. data/test/fixtures/common_cartridge/schema/ccv1p1_lommanifest_v1p0.xsd +822 -0
  136. data/test/fixtures/common_cartridge/schema/ccv1p1_lomresource_v1p0.xsd +273 -0
  137. data/test/fixtures/common_cartridge/schema/xml.xsd +286 -0
  138. data/test/test_helper.rb +71 -0
  139. data/test/test_question_helper.rb +122 -0
  140. data/test/test_wiki_helper.rb +21 -0
  141. data/test/unit/canvas/assessment_test.rb +261 -0
  142. data/test/unit/canvas/assignment_test.rb +220 -0
  143. data/test/unit/canvas/converter_test.rb +171 -0
  144. data/test/unit/canvas/course_test.rb +176 -0
  145. data/test/unit/canvas/discussion_topic_test.rb +129 -0
  146. data/test/unit/canvas/label_test.rb +32 -0
  147. data/test/unit/canvas/question_bank_test.rb +92 -0
  148. data/test/unit/canvas/question_group_test.rb +90 -0
  149. data/test/unit/canvas/question_test.rb +876 -0
  150. data/test/unit/canvas/web_content_test.rb +46 -0
  151. data/test/unit/canvas/web_link_test.rb +48 -0
  152. data/test/unit/canvas/wiki_test.rb +74 -0
  153. data/test/unit/cc/assessment_test.rb +48 -0
  154. data/test/unit/cc/assignment_test.rb +72 -0
  155. data/test/unit/cc/cc_helper_test.rb +19 -0
  156. data/test/unit/cc/converter_test.rb +173 -0
  157. data/test/unit/cc/course_test.rb +36 -0
  158. data/test/unit/cc/discussion_topic_test.rb +83 -0
  159. data/test/unit/cc/label_test.rb +29 -0
  160. data/test/unit/cc/question_test.rb +54 -0
  161. data/test/unit/cc/web_content_test.rb +76 -0
  162. data/test/unit/cc/web_link_test.rb +159 -0
  163. data/test/unit/cc/wiki_test.rb +139 -0
  164. data/test/unit/logger_test.rb +33 -0
  165. data/test/unit/migrator_test.rb +67 -0
  166. data/test/unit/resource_factory_test.rb +216 -0
  167. metadata +499 -0
@@ -0,0 +1,43 @@
1
+ module CanvasCc::CanvasCC
2
+ class MatchingQuestionWriter < QuestionWriter
3
+ register_writer_type 'matching_question'
4
+
5
+ private
6
+
7
+ def self.write_responses(presentation_node, question)
8
+ question.matches.each do |match|
9
+ presentation_node.response_lid(:ident => "response_#{match[:id]}") do |response_node|
10
+ next unless match[:question_text].length > 0
11
+ response_node.material do |material_node|
12
+ material_node.mattext(match[:question_text], :texttype => 'text/plain')
13
+ end
14
+ response_node.render_choice do |choice_node|
15
+ question.matches.each do |possible_match|
16
+ choice_node.response_label(:ident => possible_match[:id]) do |label_node|
17
+ label_node.material do |material_node|
18
+ material_node.mattext(possible_match[:answer_text], :texttype => 'text/plain')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def self.write_response_conditions(processing_node, question)
28
+ real_matches = question.matches.select{|m| m[:question_text].length > 0}
29
+ score = 100.0 / real_matches.length.to_i
30
+ real_matches.each do |match|
31
+ processing_node.respcondition do |condition_node|
32
+ condition_node.conditionvar do |var_node|
33
+ var_node.varequal match[:id], :respident => "response_#{match[:id]}"
34
+ end
35
+ condition_node.setvar "%.2f" % score, :varname => 'SCORE', :action => 'Add'
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.write_additional_nodes(item_node, question)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,12 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Answer
3
+ attr_accessor :id, :answer_text, :answer_format, :fraction, :feedback, :feedback_format, :resp_ident
4
+
5
+ def initialize(answer=nil)
6
+ return unless answer
7
+ answer.instance_variables.each do |var|
8
+ self.instance_variable_set(var, answer.instance_variable_get(var))
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,102 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Assessment
3
+ META_ATTRIBUTES = [:title, :description, :allowed_attempts,
4
+ :scoring_policy, :access_code, :ip_filter, :shuffle_answers, :time_limit, :quiz_type]
5
+ DATETIME_ATTRIBUTES = [:lock_at, :unlock_at]
6
+
7
+ ASSESSMENT_TYPE = 'imsqti_xmlv1p2/imscc_xmlv1p1/assessment'
8
+ LAR_TYPE = 'associatedcontent/imscc_xmlv1p1/learning-application-resource'
9
+ ASSESSMENT_NON_CC_FOLDER = 'non_cc_assessments'
10
+
11
+ attr_accessor :identifier, :workflow_state, :question_references, :items, *META_ATTRIBUTES, *DATETIME_ATTRIBUTES
12
+
13
+ def initialize
14
+ @question_references = []
15
+ end
16
+
17
+ def resources
18
+ [cc_assessment_resource, canvas_assessment_resource]
19
+ end
20
+
21
+ def cc_assessment_resource
22
+ resource = CanvasCc::CanvasCC::Models::Resource.new
23
+ resource.identifier = @identifier
24
+ resource.type = ASSESSMENT_TYPE
25
+ resource.dependencies = ["#{@identifier}_meta"]
26
+ resource.files = [] # TODO: export cc qti file
27
+ resource
28
+ end
29
+
30
+ def canvas_assessment_resource
31
+ resource = CanvasCc::CanvasCC::Models::Resource.new
32
+ resource.identifier = "#{@identifier}_meta"
33
+ resource.href = meta_file_path
34
+ resource.type = LAR_TYPE
35
+ resource.files = [meta_file_path, qti_file_path]
36
+ resource
37
+ end
38
+
39
+ def meta_file_path
40
+ "#{@identifier}/assessment_meta.xml"
41
+ end
42
+
43
+ def qti_file_path
44
+ "#{ASSESSMENT_NON_CC_FOLDER}/#{@identifier}.xml.qti"
45
+ end
46
+
47
+ def resolve_question_references!(question_banks)
48
+ @items ||= []
49
+
50
+ # keep track of each time a bank is referenced through a random question reference
51
+ random_bank_counts = {}
52
+
53
+ @question_references.each do |ref|
54
+ question = nil
55
+ group = nil
56
+ question_banks.each do |bank|
57
+ break if (question = bank.questions.detect{|q| q.original_identifier.to_s == ref[:question]}) ||
58
+ (group = bank.question_groups.detect{|g| g.identifier.to_s == ref[:question]})
59
+
60
+ if bank.random_question_references.any?{|r| r.to_s == ref[:question]}
61
+ random_bank_counts[bank] ||= 0
62
+ random_bank_counts[bank] += 1
63
+ break
64
+ end
65
+ end
66
+
67
+ if question
68
+ question.assessment_question_identifierref ||= "question_#{question.identifier}"
69
+ copy = question.dup
70
+ copy.points_possible = ref[:grade] if ref[:grade]
71
+ @items << copy
72
+ elsif group
73
+ copy = group.dup
74
+ if ref[:grade] && copy.selection_number.to_i != 0
75
+ copy.points_per_item = ref[:grade].to_f / copy.selection_number.to_i
76
+ end
77
+ @items << copy
78
+ end
79
+ end
80
+
81
+ random_bank_counts.each do |bank, count|
82
+ new_group = CanvasCc::CanvasCC::Models::QuestionGroup.new
83
+ new_group.identifier = "#{@identifier}#{bank.identifier}_random_group"
84
+ new_group.selection_number = count
85
+
86
+ new_group.questions = bank.questions.map(&:dup)
87
+ child_banks = bank.find_children_banks(question_banks)
88
+ child_banks.each do |child|
89
+ new_group.questions += child.questions.map(&:dup)
90
+ end
91
+
92
+ @items << new_group
93
+ end
94
+
95
+ @items.select{|i| i.is_a?(CanvasCc::CanvasCC::Models::QuestionGroup)}.each do |group|
96
+ group.questions.each do |q|
97
+ @items.delete_if{|i| i.respond_to?(:original_identifier) && i.original_identifier == q.original_identifier}
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,30 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Assignment
3
+ attr_accessor :identifier, :title, :body, :due_at, :lock_at, :unlock_at, :all_day_date, :peer_reviews_due_at,
4
+ :assignment_group_identifier_ref, :workflow_state, :points_possible, :grading_type, :all_day,
5
+ :submission_types, :position, :peer_review_count, :peer_reviews_assigned, :peer_reviews,
6
+ :automatic_peer_reviews, :grade_group_students_individually, :muted
7
+
8
+ LAR_TYPE = 'associatedcontent/imscc_xmlv1p1/learning-application-resource'
9
+ ASSIGNMENT_SETTINGS_FILE = 'assignment_settings.xml'
10
+
11
+ def initialize
12
+ @submission_types = []
13
+ end
14
+
15
+ def resources
16
+ [assignment_resource]
17
+ end
18
+
19
+ def assignment_resource
20
+ resource = CanvasCc::CanvasCC::Models::Resource.new
21
+ resource.identifier = @identifier
22
+ resource.href = "#{resource.identifier}/assignment-#{CGI::escape(title.downcase.gsub(/\s/, '-'))}.html"
23
+ resource.type = LAR_TYPE
24
+ resource.files = [resource.href, "#{resource.identifier}/#{ASSIGNMENT_SETTINGS_FILE}"]
25
+
26
+ resource
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class AssignmentGroup
3
+ attr_accessor :identifier, :title, :position, :group_weight, :rules
4
+
5
+ def initialize
6
+ @rules = []
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class CalculatedQuestion < Question
3
+ register_question_type 'calculated_question'
4
+
5
+ attr_accessor :correct_answer_format, :correct_answer_length, :dataset_definitions, :tolerance, :var_sets
6
+
7
+ def post_process!
8
+ @material.gsub!(/\{([\w\s]*?)\}/, '[\1]') if @material
9
+
10
+ return unless @answers
11
+ @answers.each do |answer|
12
+ if answer.answer_text
13
+ answer.answer_text.gsub!(/\{([\w\s]*?)\}/, '\1')
14
+ answer.answer_text = answer.answer_text.sub('=', '') if answer.answer_text.start_with?('=')
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class CanvasFile < CanvasCc::CanvasCC::Models::Resource
3
+
4
+ WEB_RESOURCES = 'web_resources'
5
+
6
+ attr_reader :file_path
7
+ attr_accessor :file_location
8
+
9
+ def initialize
10
+ super
11
+ @type = WEB_CONTENT_TYPE
12
+ end
13
+
14
+ def file_path=(file_path)
15
+ @href = File.join(WEB_RESOURCES, file_path)
16
+ @files << self.href
17
+ @file_path = file_path
18
+ end
19
+
20
+
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class CanvasModule
3
+
4
+ attr_accessor :identifier, :title, :workflow_state, :module_items
5
+
6
+ def initialize
7
+ @module_items = []
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,53 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Course
3
+
4
+ attr_accessor :format, :identifier, :copyright, :settings, :resources, :canvas_modules, :files, :pages, :discussions,
5
+ :assignments, :assessments, :question_banks, :assignment_groups
6
+
7
+ def initialize
8
+ @settings = {}
9
+ @resources = []
10
+ @canvas_modules = []
11
+ @files = []
12
+ @pages = []
13
+ @discussions = []
14
+ @assignments = []
15
+ @assessments = []
16
+ @question_banks = []
17
+ @assignment_groups = []
18
+ end
19
+
20
+ def start_at
21
+ CanvasCc::CC::CCHelper.ims_datetime(@settings[:start_at]) if @settings[:start_at]
22
+ end
23
+
24
+
25
+ def conclude_at
26
+ CanvasCc::CC::CCHelper.ims_datetime(@settings[:conclude_at]) if @settings[:conclude_at]
27
+ end
28
+
29
+ def all_resources
30
+ @resources + @files + @pages + @discussions.map(&:resources).flatten + @assignments.map(&:resources).flatten + @assessments.map(&:resources).flatten
31
+ end
32
+
33
+ def method_missing(m, *args, &block)
34
+ method = m.to_s
35
+ if method[-1, 1] == '='
36
+ method.chomp!('=')
37
+ @settings[method.to_sym] = args.first
38
+ end
39
+ @settings[method.to_sym]
40
+ end
41
+
42
+ def resolve_question_references!
43
+ return unless @assessments && @question_banks
44
+ @assessments.each{|a| a.resolve_question_references!(@question_banks)}
45
+ end
46
+
47
+ def mute_assignments!
48
+ @assignments.each do |assignment|
49
+ assignment.muted = true
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,36 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Discussion
3
+ attr_accessor :identifier, :title, :text, :discussion_type, :workflow_state, :require_initial_post
4
+ DISCUSSION_ID_POSTFIX = '_DISCUSSION'
5
+ DISCUSSION_META_POSTFIX = '_meta'
6
+ IMSDT_TYPE = 'imsdt_xmlv1p1'
7
+ LAR_TYPE = 'associatedcontent/imscc_xmlv1p1/learning-application-resource'
8
+
9
+ def resources
10
+ #generate_meta_resource
11
+ [discussion_resource, meta_resource]
12
+ end
13
+
14
+ def discussion_resource
15
+ resource = CanvasCc::CanvasCC::Models::Resource.new
16
+ resource.identifier = @identifier
17
+ resource.dependencies << resource.identifier + DISCUSSION_META_POSTFIX
18
+ resource.type = IMSDT_TYPE
19
+ resource.files << resource.identifier + '.xml'
20
+
21
+ resource
22
+ end
23
+
24
+ def meta_resource
25
+ resource = CanvasCc::CanvasCC::Models::Resource.new
26
+ resource.identifier = @identifier + DISCUSSION_META_POSTFIX
27
+ resource.type = LAR_TYPE
28
+ file_name = resource.identifier + '.xml'
29
+ resource.href = file_name
30
+ resource.files << file_name
31
+
32
+ resource
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class DiscussionTopic
3
+ attr_accessor :text
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class MatchingQuestion < Question
3
+ register_question_type 'matching_question'
4
+
5
+ attr_accessor :matches
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class ModuleItem
3
+ attr_accessor :identifier, :content_type, :workflow_state, :title,
4
+ :new_tab, :indent, :resource, :identifierref, :url
5
+
6
+ CONTENT_TYPE_WIKI_PAGE = 'WikiPage'
7
+ CONTENT_TYPE_ATTACHMENT = 'Attachment'
8
+ CONTENT_TYPE_CONTEXT_MODULE_SUB_HEADER = 'ContextModuleSubHeader'
9
+ CONTENT_TYPE_DISCUSSION_TOPIC = 'DiscussionTopic'
10
+ CONTENT_TYPE_ASSIGNMENT = 'Assignment'
11
+ CONTENT_TYPE_CONTEXT_EXTERNAL_TOOL = 'ContextExternalTool'
12
+ CONTENT_TYPE_QUIZ = 'Quizzes::Quiz'
13
+ CONTENT_TYPE_EXTERNAL_URL = 'ExternalUrl'
14
+
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class MultipleDropdownsQuestion < Question
3
+ register_question_type 'multiple_dropdowns_question'
4
+
5
+ attr_accessor :responses
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class NumericalQuestion < Question
3
+ register_question_type 'numerical_question'
4
+
5
+ attr_accessor :tolerances
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Page < CanvasCc::CanvasCC::Models::Resource
3
+
4
+ PAGE_ID_POSTFIX = '_PAGE'
5
+ WIKI_CONTENT = 'wiki_content'
6
+ BOOK_PATH = WIKI_CONTENT + '/books'
7
+
8
+ EDITING_ROLE_TEACHER = 'teachers'
9
+
10
+ MAX_URL_LENGTH = 80
11
+
12
+ attr_accessor :workflow_state, :editing_roles, :body, :title
13
+
14
+ def initialize
15
+ super
16
+ @type = WEB_CONTENT_TYPE
17
+ end
18
+
19
+ def identifier=(identifier)
20
+ @identifier = identifier
21
+ end
22
+
23
+ def identifier
24
+ @identifier
25
+ end
26
+
27
+ def page_name= name
28
+ @title = name
29
+ @href = "#{WIKI_CONTENT}/#{self.class.convert_name_to_url(name)}.html"
30
+ end
31
+
32
+ def self.convert_name_to_url(name)
33
+ url = CGI::escape(name.downcase.gsub(/\s/, '-').gsub('.', 'dot'))
34
+ if url.length > MAX_URL_LENGTH
35
+ url = url[0,MAX_URL_LENGTH][/.{0,#{MAX_URL_LENGTH}}/mu]
36
+ end
37
+ url
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Question
3
+ QTI_META_ATTRIBUTES = [:question_type, :points_possible, :assessment_question_identifierref]
4
+ STANDARD_QUESTION_TYPES = ['essay_question', 'fill_in_multiple_blanks_question',
5
+ 'multiple_choice_question', 'multiple_answers_question',
6
+ 'short_answer_question', 'text_only_question', 'true_false_question']
7
+
8
+ attr_accessor :identifier, :original_identifier, :title, :material, :answers, :general_feedback, *QTI_META_ATTRIBUTES
9
+
10
+ @@subclasses = {}
11
+
12
+ def self.create(type)
13
+ q = if STANDARD_QUESTION_TYPES.include?(type)
14
+ self.new
15
+ elsif c = @@subclasses[type]
16
+ c.new
17
+ else
18
+ raise "Unknown question type: #{type}"
19
+ end
20
+ q.question_type = type
21
+ q
22
+ end
23
+
24
+ def self.register_question_type(name)
25
+ @@subclasses[name] = self
26
+ end
27
+
28
+ def initialize
29
+ @answers = []
30
+ end
31
+ end
32
+ end
33
+
34
+ require_relative 'calculated_question'
35
+ require_relative 'matching_question'
36
+ require_relative 'multiple_dropdowns_question'
37
+ require_relative 'numerical_question'
@@ -0,0 +1,42 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class QuestionBank
3
+
4
+ attr_accessor :identifier, :title, :questions, :question_groups, :random_question_references, :original_id, :parent_id
5
+
6
+ def initialize
7
+ @questions = []
8
+ @question_groups = []
9
+ @random_question_references = []
10
+ end
11
+
12
+ LAR_TYPE = 'associatedcontent/imscc_xmlv1p1/learning-application-resource'
13
+ ASSESSMENT_NON_CC_FOLDER = 'non_cc_assessments'
14
+
15
+ def resources
16
+ [question_bank_resource]
17
+ end
18
+
19
+ def question_bank_resource
20
+ resource = CanvasCc::CanvasCC::Models::Resource.new
21
+ resource.identifier = @identifier
22
+ resource.href = "#{ASSESSMENT_NON_CC_FOLDER}/#{resource.identifier}.xml.qti"
23
+ resource.type = LAR_TYPE
24
+ resource.files = [resource.href]
25
+ resource
26
+ end
27
+
28
+ # recursively find all banks that belong to this one
29
+ def find_children_banks(all_banks, visited_banks=[])
30
+ visited_banks << self
31
+ children = []
32
+ sub_children = []
33
+ all_banks.each do |bank|
34
+ children << bank if bank.parent_id && bank.parent_id == self.original_id && !visited_banks.include?(bank)
35
+ end
36
+ children.each do |child|
37
+ sub_children += child.find_children_banks(all_banks, visited_banks)
38
+ end
39
+ return children + sub_children
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class QuestionGroup
3
+
4
+ attr_accessor :identifier, :title, :questions, :group_type, :selection_number, :points_per_item
5
+
6
+ def initialize
7
+ @questions = []
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class Resource
3
+
4
+ WEB_CONTENT_TYPE = 'webcontent'
5
+
6
+ attr_accessor :files, :href, :type, :dependencies, :identifier
7
+
8
+ def initialize
9
+ @files = []
10
+ @dependencies = []
11
+ @ident_postfix = ''
12
+ end
13
+
14
+ def attributes
15
+ {
16
+ href: href,
17
+ type: type,
18
+ identifier: identifier
19
+ }.delete_if { |_, v| v.nil? }
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class WebContent
3
+
4
+ attr_accessor :body
5
+
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ class WebLink
3
+
4
+ attr_accessor :url, :external_link, :href
5
+
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module CanvasCc::CanvasCC::Models
2
+ module WorkflowState
3
+ ACTIVE = 'active'
4
+ UNPUBLISHED = 'unpublished'
5
+ end
6
+ end
@@ -0,0 +1,52 @@
1
+ module CanvasCc::CanvasCC
2
+ class ModuleMetaWriter
3
+ MODULE_META_FILE = 'module_meta.xml'
4
+
5
+ def initialize(work_dir, *canvas_modules)
6
+ @work_dir = work_dir
7
+ @canvas_modules = canvas_modules
8
+ end
9
+
10
+ def write
11
+ xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
12
+ xml.modules(
13
+ 'xmlns' => 'http://canvas.instructure.com/xsd/cccv1p0',
14
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
15
+ 'xsi:schemaLocation' => 'http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd'
16
+ ) { |xml|
17
+ @canvas_modules.each_with_index { |mod, position| canvas_module(mod, xml, position) }
18
+ }
19
+ end.to_xml
20
+ File.open(File.join(@work_dir, CanvasCc::CanvasCC::CartridgeCreator::COURSE_SETTINGS_DIR, MODULE_META_FILE), 'w') { |f| f.write(xml) }
21
+ end
22
+
23
+ private
24
+
25
+ def canvas_module(mod, xml, position)
26
+ xml.module('identifier' => mod.identifier) {
27
+ xml.title mod.title
28
+ xml.workflow_state mod.workflow_state
29
+ xml.position(position)
30
+ xml.items { |xml|
31
+ add_module_items_to_xml(mod.module_items, xml)
32
+ }
33
+ }
34
+ end
35
+
36
+ def add_module_items_to_xml(module_items, xml)
37
+ module_items.each_with_index do |item, position|
38
+ xml.item('identifier' => item.identifier) { |xml|
39
+ xml.content_type(item.content_type)
40
+ xml.workflow_state(item.workflow_state)
41
+ xml.title(item.title)
42
+ xml.position(position)
43
+ xml.new_tab(item.new_tab) if item.new_tab
44
+ xml.indent(item.indent)
45
+ xml.identifierref(item.identifierref) if item.identifierref
46
+ xml.url(item.url) if item.url
47
+ }
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ module CanvasCc::CanvasCC
2
+ class MultipleAnswersQuestionWriter < QuestionWriter
3
+ register_writer_type 'multiple_answers_question'
4
+
5
+ private
6
+
7
+ def self.write_responses(presentation_node, question)
8
+ presentation_node.response_lid(:ident => 'response1', :rcardinality => 'Multiple') do |response_node|
9
+ response_node.render_choice do |choice_node|
10
+ question.answers.each do |answer|
11
+ choice_node.response_label(:ident => answer.id) do |label_node|
12
+ label_node.material do |material_node|
13
+ material_node.mattext answer.answer_text, :texttype => 'text/html'
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.write_response_conditions(processing_node, question)
22
+ return unless question.answers.count > 0
23
+ processing_node.respcondition(:continue => 'No') do |condition_node|
24
+ condition_node.conditionvar do |var_node|
25
+ var_node.and do |and_node|
26
+ question.answers.each do |answer|
27
+ if answer.fraction.to_f > 0
28
+ and_node.varequal answer.id, :respident => 'response1'
29
+ else
30
+ and_node.not do |not_node|
31
+ not_node.varequal answer.id, :respident => 'response1'
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ condition_node.setvar('100', :varname => 'SCORE', :action => 'Set')
38
+ end
39
+ end
40
+ end
41
+ end