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,26 @@
1
+ module CanvasCc::Canvas
2
+ class Wiki < CanvasCc::CC::Wiki
3
+ include Resource
4
+ attr_accessor :pages
5
+
6
+ def initialize(mod)
7
+ @href_template = "#{WIKI_FOLDER}/%s.html"
8
+ super
9
+
10
+ @pages.map! do |page|
11
+ page.body.gsub!(/\[(.*?)\]/) do |match|
12
+ title_slug = file_slug(@title)
13
+ slug = [title_slug, file_slug(match)].join('-')
14
+ href = File.join(CGI.escape(WIKI_TOKEN), 'wiki', slug)
15
+ %(<a href="#{href}" title="#{$1}">#{$1}</a>)
16
+ end
17
+ page
18
+ end
19
+ end
20
+
21
+ def create_module_meta_item_elements(item_node)
22
+ item_node.content_type 'WikiPage'
23
+ item_node.identifierref @identifier
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,82 @@
1
+ module CanvasCc::CanvasCC
2
+ class AssessmentWriter
3
+
4
+ def initialize(work_dir, *assessments)
5
+ @work_dir = work_dir
6
+ @assessments = assessments
7
+ end
8
+
9
+ def write
10
+ @assessments.each do |assessment|
11
+ write_assessment(assessment)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def write_assessment(assessment)
18
+ write_assessment_non_cc_qti_xml(assessment)
19
+ write_assessment_meta_xml(assessment)
20
+ end
21
+
22
+ def write_assessment_meta_xml(assessment)
23
+ xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
24
+ xml.quiz(
25
+ :identifier => assessment.identifier,
26
+ 'xsi:schemaLocation' => "http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd",
27
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
28
+ 'xmlns' => "http://canvas.instructure.com/xsd/cccv1p0"
29
+ ) do |quiz_node|
30
+ CanvasCc::CanvasCC::Models::Assessment::META_ATTRIBUTES.each do |attr|
31
+ quiz_node.send(attr, assessment.send(attr)) unless assessment.send(attr).nil?
32
+ end
33
+ CanvasCc::CanvasCC::Models::Assessment::DATETIME_ATTRIBUTES.each do |attr|
34
+ quiz_node.send(attr, CanvasCc::CC::CCHelper.ims_datetime(assessment.send(attr))) unless assessment.send(attr).nil?
35
+ end
36
+ end
37
+ end.to_xml
38
+
39
+ file_path = File.join(@work_dir, assessment.meta_file_path)
40
+ FileUtils.mkdir_p(File.dirname(file_path)) unless File.exists?(File.dirname(file_path))
41
+ File.open(file_path, 'w') { |f| f.write(xml) }
42
+ end
43
+
44
+ def write_assessment_non_cc_qti_xml(assessment)
45
+ raise "need to resolve questions references" if assessment.items.nil?
46
+
47
+ xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
48
+ xml.questestinterop("xmlns" => "http://www.imsglobal.org/xsd/ims_qtiasiv1p2",
49
+ "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
50
+ "xsi:schemaLocation"=> "http://www.imsglobal.org/xsd/ims_qtiasiv1p2 http://www.imsglobal.org/xsd/ims_qtiasiv1p2p1.xsd"
51
+ ) { |xml|
52
+ xml.assessment(:title => assessment.title, :ident => assessment.identifier) do |assessment_node|
53
+ assessment_node.qtimetadata do |qtimetadata_node|
54
+ qtimetadata_node.qtimetadatafield do |qtimetadatafield_node|
55
+ qtimetadatafield_node.fieldlabel "qmd_timelimit"
56
+ qtimetadatafield_node.fieldentry assessment.time_limit
57
+ end
58
+ qtimetadata_node.qtimetadatafield do |qtimetadatafield_node|
59
+ qtimetadatafield_node.fieldlabel "cc_maxattempts"
60
+ qtimetadatafield_node.fieldentry assessment.allowed_attempts
61
+ end
62
+ end
63
+ assessment_node.section(:ident => 'root_section') do |section_node|
64
+ assessment.items.each do |item|
65
+ case item
66
+ when CanvasCc::CanvasCC::Models::Question
67
+ CanvasCc::CanvasCC::QuestionWriter.write_question(section_node, item)
68
+ when CanvasCc::CanvasCC::Models::QuestionGroup
69
+ CanvasCc::CanvasCC::QuestionGroupWriter.write_question_group(section_node, item)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ }
75
+ end.to_xml
76
+
77
+ file_path = File.join(@work_dir, assessment.qti_file_path)
78
+ FileUtils.mkdir_p(File.dirname(file_path)) unless File.exists?(File.dirname(file_path))
79
+ File.open(file_path, 'w') { |f| f.write(xml) }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,48 @@
1
+ module CanvasCc::CanvasCC
2
+ class AssignmentGroupWriter
3
+ ASSIGNMENT_GROUP_FILE = 'assignment_groups.xml'
4
+
5
+ def initialize(work_dir, assignment_groups)
6
+ @work_dir = work_dir
7
+ @assignment_groups = assignment_groups
8
+ end
9
+
10
+ def write
11
+ xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
12
+ write_assignment_groups(xml) do |xml|
13
+ @assignment_groups.each do |ag|
14
+ xml.assignmentGroup(:identifier => ag.identifier){
15
+ xml.title ag.title
16
+ xml.position ag.position
17
+ xml.group_weight ag.group_weight
18
+ write_rules(ag, xml)
19
+ }
20
+ end
21
+ end
22
+ end.to_xml
23
+ File.open(File.join(@work_dir, CanvasCc::CanvasCC::CartridgeCreator::COURSE_SETTINGS_DIR, ASSIGNMENT_GROUP_FILE), 'w') { |f| f.write(xml) }
24
+ end
25
+
26
+ private
27
+
28
+ def write_rules(assignment_group, xml)
29
+ xml.rules{
30
+ assignment_group.rules.each do |rule|
31
+ xml.rule{
32
+ rule.each { |k, v| xml.send(k, v) }
33
+ }
34
+ end
35
+ }
36
+ end
37
+
38
+ def write_assignment_groups(xml)
39
+ xml.assignmentGroups(
40
+ 'xmlns' => 'http://canvas.instructure.com/xsd/cccv1p0',
41
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
42
+ 'xsi:schemaLocation' => 'http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd'
43
+ ) {
44
+ yield xml
45
+ }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ module CanvasCc::CanvasCC
2
+ class AssignmentWriter
3
+
4
+ def initialize(work_dir, *assignments)
5
+ @work_dir = work_dir
6
+ @assignments = assignments
7
+ end
8
+
9
+ def write
10
+ @assignments.each do |assignment|
11
+ assignment_dir = File.join(@work_dir, assignment.assignment_resource.identifier)
12
+ Dir.mkdir(assignment_dir)
13
+ write_html(assignment_dir, assignment)
14
+ write_settings(assignment_dir, assignment)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def write_settings(assignment_dir, assignment)
21
+ xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
22
+ xml.assignment('identifier' => assignment.assignment_resource.identifier,
23
+ 'xmlns' => 'http://canvas.instructure.com/xsd/cccv1p0',
24
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
25
+ 'xsi:schemaLocation' => 'http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd'
26
+ ) { |xml|
27
+ xml.title assignment.title
28
+ xml.due_at CanvasCc::CC::CCHelper.ims_datetime(assignment.due_at) if assignment.due_at
29
+ xml.lock_at CanvasCc::CC::CCHelper.ims_datetime(assignment.lock_at) if assignment.lock_at
30
+ xml.unlock_at CanvasCc::CC::CCHelper.ims_datetime(assignment.unlock_at) if assignment.unlock_at
31
+ xml.all_day_date CanvasCc::CC::CCHelper.ims_datetime(assignment.all_day_date) if assignment.all_day_date
32
+ xml.peer_reviews_due_at CanvasCc::CC::CCHelper.ims_datetime(assignment.peer_reviews_due_at) if assignment.peer_reviews_due_at
33
+ xml.assignment_group_identifierref assignment.assignment_group_identifier_ref if assignment.assignment_group_identifier_ref
34
+ xml.workflow_state assignment.workflow_state if assignment.workflow_state
35
+ xml.points_possible assignment.points_possible if assignment.points_possible
36
+ xml.grading_type assignment.grading_type if assignment.grading_type
37
+ xml.all_day assignment.all_day unless assignment.all_day.nil?
38
+ xml.submission_types assignment.submission_types.join(',') unless assignment.submission_types.empty?
39
+ xml.position assignment.position if assignment.position
40
+ xml.peer_review_count assignment.peer_review_count if assignment.peer_review_count
41
+ xml.peer_reviews_assigned assignment.peer_reviews_assigned unless assignment.peer_reviews_assigned.nil?
42
+ xml.peer_reviews assignment.peer_reviews unless assignment.peer_reviews.nil?
43
+ xml.automatic_peer_reviews assignment.automatic_peer_reviews unless assignment.automatic_peer_reviews.nil?
44
+ xml.grade_group_students_individually assignment.grade_group_students_individually unless assignment.grade_group_students_individually.nil?
45
+ xml.muted assignment.muted unless assignment.muted.nil?
46
+ }
47
+ end.to_xml
48
+ File.open(File.join(assignment_dir, CanvasCc::CanvasCC::Models::Assignment::ASSIGNMENT_SETTINGS_FILE), 'w') { |f| f.write(xml) }
49
+ end
50
+
51
+
52
+ def write_html(assignment_dir, assignment)
53
+ builder = Nokogiri::HTML::Builder.new(:encoding => 'UTF-8') do |doc|
54
+ doc.html { |html|
55
+ html.head { |head|
56
+ head.meta('http-equiv' => 'Content-Type', content: 'text/html; charset=utf-8')
57
+ head.title "Assignment: #{assignment.title}"
58
+ }
59
+ html.body { |body|
60
+ body << Nokogiri::HTML::fragment(assignment.body)
61
+ }
62
+ }
63
+ end
64
+ File.open(File.join(@work_dir, assignment.assignment_resource.href), 'w') { |f| f.write(builder.to_html) }
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,72 @@
1
+ module CanvasCc::CanvasCC
2
+ class CalculatedQuestionWriter < QuestionWriter
3
+ register_writer_type 'calculated_question'
4
+
5
+ private
6
+
7
+ def self.write_responses(presentation_node, question)
8
+ presentation_node.response_str(:rcardinality => 'Single', :ident => 'response1') do |response_node|
9
+ response_node.render_fib(:fibtype => 'Decimal') do |render_node|
10
+ render_node.response_label(:ident => 'answer1')
11
+ end
12
+ end
13
+ end
14
+
15
+ def self.write_response_conditions(processing_node, question)
16
+ processing_node.respcondition(:title => 'correct') do |condition|
17
+ condition.conditionvar do |var_node|
18
+ var_node.other
19
+ end
20
+ condition.setvar('100', :varname => 'SCORE', :action => 'Set')
21
+ end
22
+ processing_node.respcondition(:title => 'incorrect') do |condition|
23
+ condition.conditionvar do |var_node|
24
+ var_node.other
25
+ end
26
+ condition.setvar('0', :varname => 'SCORE', :action => 'Set')
27
+ end
28
+ end
29
+
30
+ def self.write_additional_nodes(item_node, question)
31
+ item_node.itemproc_extension do |extension_node|
32
+ extension_node.calculated do |calculated_node|
33
+ calculated_node.answer_tolerance question.tolerance
34
+
35
+ formula_decimal_places = question.correct_answer_format.to_i == 1 ? question.correct_answer_length : 0
36
+ calculated_node.formulas(:decimal_places => formula_decimal_places) do |formulas_node|
37
+ formulas = question.answers.map { |a| a.answer_text.gsub(/[\{\}\s]/, '') }
38
+ formulas.each do |formula|
39
+ formulas_node.formula formula
40
+ end
41
+ end
42
+ calculated_node.vars do |vars_node|
43
+ vars = question.dataset_definitions.map do |ds_def|
44
+ name = ds_def[:name]
45
+ type, min, max, scale = ds_def[:options].split(':')
46
+ {:name => name, :min => min, :max => max, :scale => scale}
47
+ end
48
+
49
+ vars.each do |var|
50
+ vars_node.var(:name => var[:name], :scale => var[:scale]) do |var_node|
51
+ var_node.min var[:min]
52
+ var_node.max var[:max]
53
+ end
54
+ end
55
+ end
56
+
57
+ calculated_node.var_sets do |var_sets_node|
58
+ question.var_sets.each do |var_set|
59
+ ident = var_set[:vars].sort.map { |k,v| v.to_s.split('.').join }.flatten.join
60
+ var_sets_node.var_set(:ident => ident) do |var_set_node|
61
+ var_set[:vars].each do |k, v|
62
+ var_set_node.var(v, :name => k)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,18 @@
1
+ module CanvasCc::CanvasCC
2
+ class CanvasExportWriter
3
+
4
+ CANVAS_EXPORT_FILE = 'canvas_export.txt'
5
+
6
+ def initialize(work_dir)
7
+ @work_dir = work_dir
8
+ end
9
+
10
+ def write
11
+ File.open(File.join(@work_dir, CanvasCc::CanvasCC::CartridgeCreator::COURSE_SETTINGS_DIR, CANVAS_EXPORT_FILE), 'w') do |f|
12
+ f.write("Q: What did the panda say when he was forced out of his natural habitat?\nA: This is un-BEAR-able")
13
+ end
14
+ end
15
+
16
+
17
+ end
18
+ end
@@ -0,0 +1,60 @@
1
+ require 'zip'
2
+
3
+ module CanvasCc::CanvasCC
4
+ class CartridgeCreator
5
+
6
+ COURSE_SETTINGS_DIR = 'course_settings'
7
+
8
+ def initialize(course)
9
+ @course = course
10
+ end
11
+
12
+ def create(out_dir)
13
+ out_file = File.join(out_dir, filename)
14
+ Dir.mktmpdir do |dir|
15
+ write_cartridge(dir)
16
+
17
+ tmp_file = File.join(dir, filename)
18
+ zip_dir(tmp_file, dir)
19
+ FileUtils.mv(tmp_file, out_file)
20
+ end
21
+ out_file
22
+ end
23
+
24
+ def filename
25
+ title = @course.title.gsub(/::/, '/').
26
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
27
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
28
+ gsub(/[\/|\.]/, '_').
29
+ tr('- ', '_').downcase.
30
+ gsub(/_{2,}/, '_')
31
+ "#{title}.imscc"
32
+ end
33
+
34
+ private
35
+
36
+ def write_cartridge(dir)
37
+ Dir.mkdir(File.join(dir, COURSE_SETTINGS_DIR))
38
+ CanvasCc::CanvasCC::CanvasExportWriter.new(dir).write
39
+ CanvasCc::CanvasCC::CourseSettingWriter.new(dir, @course).write
40
+ CanvasCc::CanvasCC::AssignmentGroupWriter.new(dir, @course.assignment_groups).write
41
+ CanvasCc::CanvasCC::ModuleMetaWriter.new(dir, *@course.canvas_modules).write
42
+ CanvasCc::CanvasCC::ImsManifestGenerator.new(dir, @course).write
43
+ CanvasCc::CanvasCC::FileMetaWriter.new(dir, *@course.files).write
44
+ CanvasCc::CanvasCC::PageWriter.new(dir, *@course.pages).write
45
+ CanvasCc::CanvasCC::DiscussionWriter.new(dir, *@course.discussions).write
46
+ CanvasCc::CanvasCC::AssignmentWriter.new(dir, *@course.assignments).write
47
+ CanvasCc::CanvasCC::QuestionBankWriter.new(dir, *@course.question_banks).write
48
+ CanvasCc::CanvasCC::AssessmentWriter.new(dir, *@course.assessments).write
49
+ end
50
+
51
+ def zip_dir(out_file, dir)
52
+ Zip::File.open(out_file, Zip::File::CREATE) do |zipfile|
53
+ Dir["#{dir}/**/*"].each do |file|
54
+ zipfile.add(file.sub(dir + '/', ''), file)
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,35 @@
1
+ module CanvasCc::CanvasCC
2
+ class CourseSettingWriter
3
+
4
+ SETTINGS_POSTFIX = '_settings'
5
+ COURSE_SETTINGS_FILE = 'course_settings.xml'
6
+
7
+ def initialize(work_dir, course)
8
+ @work_dir = work_dir
9
+ @course = course
10
+ end
11
+
12
+ def write
13
+ xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
14
+ course(xml) do |xml|
15
+ @course.settings.each { |k, v| xml.send(k, v) }
16
+ end
17
+ end.to_xml
18
+ File.open(File.join(@work_dir, CanvasCc::CanvasCC::CartridgeCreator::COURSE_SETTINGS_DIR, COURSE_SETTINGS_FILE), 'w') { |f| f.write(xml) }
19
+ end
20
+
21
+ private
22
+
23
+ def course(xml)
24
+ xml.course(
25
+ 'identifier' => @course.identifier + SETTINGS_POSTFIX,
26
+ 'xmlns' => 'http://canvas.instructure.com/xsd/cccv1p0',
27
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
28
+ 'xsi:schemaLocation' => 'http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd'
29
+ ) {
30
+ yield xml
31
+ }
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,52 @@
1
+ module CanvasCc::CanvasCC
2
+ class DiscussionWriter
3
+
4
+ def initialize(work_dir, *canvas_discussions)
5
+ @work_dir = work_dir
6
+ @discussions = canvas_discussions
7
+ end
8
+
9
+ def write
10
+ @discussions.each do |discussion|
11
+ write_discussion(discussion)
12
+ write_meta(discussion)
13
+ end
14
+ end
15
+
16
+
17
+ private
18
+
19
+ def write_discussion(discussion)
20
+ xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
21
+ xml.topic('xmlns' => 'http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1',
22
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
23
+ 'xsi:schemaLocation' => 'http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imsdt_v1p1.xsd'
24
+ ) { |xml|
25
+ xml.title discussion.title
26
+ xml.text_('texttype' => 'text/html') { xml.text discussion.text }
27
+ }
28
+ end.to_xml
29
+ File.open(File.join(@work_dir, discussion.discussion_resource.files.first), 'w') { |f| f.write(xml) }
30
+ end
31
+
32
+ def write_meta(discussion)
33
+ meta_resource = discussion.meta_resource
34
+ discussion_resource = discussion.discussion_resource
35
+ xml = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
36
+ xml.topicMeta('identifier' => meta_resource.identifier, 'xmlns' => 'http://canvas.instructure.com/xsd/cccv1p0',
37
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
38
+ 'xsi:schemaLocation' => 'http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd'
39
+ ) { |xml|
40
+ xml.topic_id discussion_resource.identifier
41
+ xml.title discussion.title
42
+ xml.position
43
+ xml.type 'topic'
44
+ xml.discussion_type discussion.discussion_type
45
+ xml.require_initial_post discussion.require_initial_post
46
+ }
47
+ end.to_xml
48
+ File.open(File.join(@work_dir, meta_resource.href), 'w') { |f| f.write(xml) }
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ module CanvasCc::CanvasCC
2
+ class EssayQuestionWriter < QuestionWriter
3
+ register_writer_type 'essay_question'
4
+
5
+ private
6
+
7
+ def self.write_responses(presentation_node, question)
8
+ ShortAnswerQuestionWriter.write_responses(presentation_node, question)
9
+ end
10
+
11
+ def self.write_response_conditions(processing_node, question)
12
+ processing_node.respcondition(:continue => 'No') do |condition|
13
+ condition.conditionvar do |var_node|
14
+ var_node.other
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ module CanvasCc::CanvasCC
2
+ class FileMetaWriter
3
+
4
+ FILE_META_FILE = 'files_meta.xml'
5
+
6
+ def initialize(work_dir, *canvas_files)
7
+ @work_dir = work_dir
8
+ @canvas_files = canvas_files
9
+ end
10
+
11
+ def write
12
+ copy_files
13
+ write_xml
14
+ end
15
+
16
+ private
17
+
18
+ def copy_files
19
+
20
+ @canvas_files.each do |canvas_file|
21
+ FileUtils.mkdir_p(File.dirname(File.join(@work_dir, canvas_file.href)))
22
+ FileUtils.cp(canvas_file.file_location, File.join(@work_dir, canvas_file.href))
23
+ end
24
+ end
25
+
26
+ def write_xml
27
+ xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
28
+ xml.fileMeta(
29
+ 'xmlns' => 'http://canvas.instructure.com/xsd/cccv1p0',
30
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
31
+ 'xsi:schemaLocation' => 'http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd'
32
+ )
33
+ end.to_xml
34
+ File.open(File.join(@work_dir, CanvasCc::CanvasCC::CartridgeCreator::COURSE_SETTINGS_DIR, FILE_META_FILE), 'w') { |f| f.write(xml) }
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,123 @@
1
+ module CanvasCc::CanvasCC
2
+ class ImsManifestGenerator
3
+
4
+ MANIFEST_FILE_NAME = 'imsmanifest.xml'
5
+ SCHEMA = 'IMS Common Cartridge'
6
+ SCHEMA_VERSION = '1.1.0'
7
+ LOMIMSCC = 'lomimscc'
8
+ SETTINGS_POSTFIX = '_settings'
9
+ TYPE_LAR = 'associatedcontent/imscc_xmlv1p1/learning-application-resource'
10
+ CANVAS_EXPORT_PATH = 'course_settings/canvas_export.txt'
11
+ ALL_MODULES_IDENTIFIER = 'LearningModules'
12
+
13
+ def initialize(work_dir, course)
14
+ @work_dir = work_dir
15
+ @course = course
16
+ end
17
+
18
+ def write
19
+ xml = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
20
+ manifest(xml) do |xml|
21
+ metadata(xml)
22
+ organizations(xml)
23
+ resources(xml, @course.all_resources)
24
+ end
25
+ end.to_xml
26
+ File.open(File.join(@work_dir, MANIFEST_FILE_NAME), 'w') { |f| f.write(xml) }
27
+ end
28
+
29
+ private
30
+
31
+ def manifest(xml)
32
+ xml.manifest(
33
+ "identifier" => @course.identifier,
34
+ "xmlns" => "http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1",
35
+ "xmlns:lom" => "http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource",
36
+ "xmlns:lomimscc" => "http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifest",
37
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
38
+ "xsi:schemaLocation" => "http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imscp_v1p2_v1p0.xsd http://ltsc.ieee.org/xsd/imsccv1p1/LOM/resource http://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lomresource_v1p0.xsd http://ltsc.ieee.org/xsd/imsccv1p1/LOM/manifest http://www.imsglobal.org/profile/cc/ccv1p1/LOM/ccv1p1_lommanifest_v1p0.xsd"
39
+ ) {
40
+ yield xml
41
+ }
42
+ end
43
+
44
+ def metadata(xml)
45
+ xml.metadata {
46
+ xml.schema SCHEMA
47
+ xml.schemaversion SCHEMA_VERSION
48
+ ns = LOMIMSCC
49
+ xml[ns].lom {
50
+ xml[ns].general {
51
+ xml[ns].title {
52
+ xml[ns].string @course.title
53
+ }
54
+ }
55
+ rights(xml)
56
+ }
57
+ }
58
+ end
59
+
60
+ def rights(xml)
61
+ ns = LOMIMSCC
62
+ has_copyright = @course.copyright && !@course.copyright.empty?
63
+ xml[ns].rights {
64
+ xml[ns].copyrightAndOtherRestrictions {
65
+ xml[ns].value has_copyright ? 'yes' : 'no'
66
+ }
67
+ if has_copyright
68
+ xml[ns].description {
69
+ xml[ns].string @course.copyright
70
+ }
71
+ end
72
+ }
73
+ end
74
+
75
+
76
+ def organizations(xml)
77
+ canvas_modules = @course.canvas_modules
78
+ xml.organizations { |xml|
79
+ if canvas_modules.count > 0
80
+ xml.organization('identifier' => 'org_1', 'structure' => 'rooted-hierarchy') { |xml|
81
+ xml.item('identifier' => ALL_MODULES_IDENTIFIER) do |xml|
82
+ canvas_modules.each do |mod|
83
+ xml.item('identifier' => mod.identifier) { |xml|
84
+ xml.title mod.title
85
+ mod.module_items.each do |module_item|
86
+ xml.item('identifier' => module_item.identifier, 'identifierref' => module_item.identifierref ) { |xml|
87
+ xml.title(module_item.title)
88
+ }
89
+ end
90
+ }
91
+ end
92
+ end
93
+ }
94
+ end
95
+ }
96
+ end
97
+
98
+ def resources(xml, resources)
99
+ xml.resources { |xml|
100
+ course_setting_resource(xml)
101
+ resources.each do |resource|
102
+ xml.resource(resource.attributes) do |xml|
103
+ resource.files.each { |file| xml.file(href: file) }
104
+ resource.dependencies.each { |dependency| xml.dependency(identifierref: dependency) }
105
+ end
106
+ end
107
+ }
108
+ end
109
+
110
+ def course_setting_resource(xml)
111
+ course_setting_dir = CanvasCc::CanvasCC::CartridgeCreator::COURSE_SETTINGS_DIR
112
+ export_file = File.join(course_setting_dir, CanvasCc::CanvasCC::CanvasExportWriter::CANVAS_EXPORT_FILE)
113
+ course_setting_file = File.join(course_setting_dir, CanvasCc::CanvasCC::CourseSettingWriter::COURSE_SETTINGS_FILE)
114
+ module_meta_file = File.join(course_setting_dir, CanvasCc::CanvasCC::ModuleMetaWriter::MODULE_META_FILE)
115
+ xml.resource('identifier' => @course.identifier + SETTINGS_POSTFIX, 'type' => TYPE_LAR, 'href' => export_file) { |xml|
116
+ xml.file('href' => course_setting_file)
117
+ xml.file('href' => module_meta_file) if @course.canvas_modules.count > 0
118
+ }
119
+ end
120
+
121
+
122
+ end
123
+ end