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