canvas_cc 0.0.1

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