canvas_cc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/canvas_cc.gemspec +37 -0
- data/lib/canvas_cc/canvas/assessment.rb +124 -0
- data/lib/canvas_cc/canvas/assignment.rb +98 -0
- data/lib/canvas_cc/canvas/converter.rb +30 -0
- data/lib/canvas_cc/canvas/course.rb +143 -0
- data/lib/canvas_cc/canvas/discussion_topic.rb +64 -0
- data/lib/canvas_cc/canvas/label.rb +8 -0
- data/lib/canvas_cc/canvas/question.rb +492 -0
- data/lib/canvas_cc/canvas/question_bank.rb +57 -0
- data/lib/canvas_cc/canvas/question_group.rb +35 -0
- data/lib/canvas_cc/canvas/resource.rb +16 -0
- data/lib/canvas_cc/canvas/templates/assignment.html.erb +9 -0
- data/lib/canvas_cc/canvas/templates/syllabus.html.erb +9 -0
- data/lib/canvas_cc/canvas/templates/wiki_content.html.erb +10 -0
- data/lib/canvas_cc/canvas/web_content.rb +15 -0
- data/lib/canvas_cc/canvas/web_link.rb +16 -0
- data/lib/canvas_cc/canvas/wiki.rb +26 -0
- data/lib/canvas_cc/canvas_cc/assessment_writer.rb +82 -0
- data/lib/canvas_cc/canvas_cc/assignment_group_writer.rb +48 -0
- data/lib/canvas_cc/canvas_cc/assignment_writer.rb +68 -0
- data/lib/canvas_cc/canvas_cc/calculated_question_writer.rb +72 -0
- data/lib/canvas_cc/canvas_cc/canvas_export_writer.rb +18 -0
- data/lib/canvas_cc/canvas_cc/cartridge_creator.rb +60 -0
- data/lib/canvas_cc/canvas_cc/course_setting_writer.rb +35 -0
- data/lib/canvas_cc/canvas_cc/discussion_writer.rb +52 -0
- data/lib/canvas_cc/canvas_cc/essay_question_writer.rb +19 -0
- data/lib/canvas_cc/canvas_cc/file_meta_writer.rb +38 -0
- data/lib/canvas_cc/canvas_cc/ims_manifest_generator.rb +123 -0
- data/lib/canvas_cc/canvas_cc/matching_question_writer.rb +43 -0
- data/lib/canvas_cc/canvas_cc/models/answer.rb +12 -0
- data/lib/canvas_cc/canvas_cc/models/assessment.rb +102 -0
- data/lib/canvas_cc/canvas_cc/models/assignment.rb +30 -0
- data/lib/canvas_cc/canvas_cc/models/assignment_group.rb +9 -0
- data/lib/canvas_cc/canvas_cc/models/calculated_question.rb +19 -0
- data/lib/canvas_cc/canvas_cc/models/canvas_file.rb +22 -0
- data/lib/canvas_cc/canvas_cc/models/canvas_module.rb +10 -0
- data/lib/canvas_cc/canvas_cc/models/course.rb +53 -0
- data/lib/canvas_cc/canvas_cc/models/discussion.rb +36 -0
- data/lib/canvas_cc/canvas_cc/models/discussion_topic.rb +5 -0
- data/lib/canvas_cc/canvas_cc/models/matching_question.rb +7 -0
- data/lib/canvas_cc/canvas_cc/models/module_item.rb +16 -0
- data/lib/canvas_cc/canvas_cc/models/multiple_dropdowns_question.rb +7 -0
- data/lib/canvas_cc/canvas_cc/models/numerical_question.rb +7 -0
- data/lib/canvas_cc/canvas_cc/models/page.rb +41 -0
- data/lib/canvas_cc/canvas_cc/models/question.rb +37 -0
- data/lib/canvas_cc/canvas_cc/models/question_bank.rb +42 -0
- data/lib/canvas_cc/canvas_cc/models/question_group.rb +10 -0
- data/lib/canvas_cc/canvas_cc/models/resource.rb +23 -0
- data/lib/canvas_cc/canvas_cc/models/web_content.rb +7 -0
- data/lib/canvas_cc/canvas_cc/models/web_link.rb +7 -0
- data/lib/canvas_cc/canvas_cc/models/workflow_state.rb +6 -0
- data/lib/canvas_cc/canvas_cc/module_meta_writer.rb +52 -0
- data/lib/canvas_cc/canvas_cc/multiple_answers_question_writer.rb +41 -0
- data/lib/canvas_cc/canvas_cc/multiple_blanks_question_writer.rb +51 -0
- data/lib/canvas_cc/canvas_cc/multiple_choice_question_writer.rb +48 -0
- data/lib/canvas_cc/canvas_cc/multiple_dropdowns_question_writer.rb +30 -0
- data/lib/canvas_cc/canvas_cc/numerical_question_writer.rb +40 -0
- data/lib/canvas_cc/canvas_cc/page_writer.rb +38 -0
- data/lib/canvas_cc/canvas_cc/question_bank_writer.rb +44 -0
- data/lib/canvas_cc/canvas_cc/question_group_writer.rb +23 -0
- data/lib/canvas_cc/canvas_cc/question_writer.rb +129 -0
- data/lib/canvas_cc/canvas_cc/short_answer_question_writer.rb +42 -0
- data/lib/canvas_cc/canvas_cc/text_only_question_writer.rb +19 -0
- data/lib/canvas_cc/canvas_cc/true_false_question_writer.rb +38 -0
- data/lib/canvas_cc/cc/assessment.rb +25 -0
- data/lib/canvas_cc/cc/assignment.rb +61 -0
- data/lib/canvas_cc/cc/cc_helper.rb +285 -0
- data/lib/canvas_cc/cc/converter.rb +137 -0
- data/lib/canvas_cc/cc/course.rb +16 -0
- data/lib/canvas_cc/cc/discussion_topic.rb +53 -0
- data/lib/canvas_cc/cc/label.rb +6 -0
- data/lib/canvas_cc/cc/question.rb +23 -0
- data/lib/canvas_cc/cc/resource.rb +30 -0
- data/lib/canvas_cc/cc/templates/assignment.html.erb +13 -0
- data/lib/canvas_cc/cc/templates/syllabus.html.erb +9 -0
- data/lib/canvas_cc/cc/templates/wiki_content.html.erb +10 -0
- data/lib/canvas_cc/cc/web_content.rb +48 -0
- data/lib/canvas_cc/cc/web_link.rb +75 -0
- data/lib/canvas_cc/cc/wiki.rb +73 -0
- data/lib/canvas_cc/version.rb +3 -0
- data/lib/canvas_cc.rb +94 -0
- data/spec/moodle2cc/canvas_cc/assessment_writer_spec.rb +95 -0
- data/spec/moodle2cc/canvas_cc/assignment_group_writer_spec.rb +51 -0
- data/spec/moodle2cc/canvas_cc/assignment_writer_spec.rb +74 -0
- data/spec/moodle2cc/canvas_cc/calculated_question_writer_spec.rb +88 -0
- data/spec/moodle2cc/canvas_cc/canvas_export_writer_spec.rb +26 -0
- data/spec/moodle2cc/canvas_cc/cartridge_creator_spec.rb +50 -0
- data/spec/moodle2cc/canvas_cc/course_setting_writer_spec.rb +47 -0
- data/spec/moodle2cc/canvas_cc/discussion_writer_spec.rb +38 -0
- data/spec/moodle2cc/canvas_cc/essay_question_writer_spec.rb +31 -0
- data/spec/moodle2cc/canvas_cc/file_meta_writer_spec.rb +57 -0
- data/spec/moodle2cc/canvas_cc/ims_manifest_generator_spec.rb +141 -0
- data/spec/moodle2cc/canvas_cc/matching_question_writer_spec.rb +51 -0
- data/spec/moodle2cc/canvas_cc/models/assessment_spec.rb +131 -0
- data/spec/moodle2cc/canvas_cc/models/assignment_group_spec.rb +9 -0
- data/spec/moodle2cc/canvas_cc/models/assignment_spec.rb +45 -0
- data/spec/moodle2cc/canvas_cc/models/calculated_question_spec.rb +25 -0
- data/spec/moodle2cc/canvas_cc/models/canvas_file_spec.rb +23 -0
- data/spec/moodle2cc/canvas_cc/models/canvas_module_spec.rb +16 -0
- data/spec/moodle2cc/canvas_cc/models/course_spec.rb +43 -0
- data/spec/moodle2cc/canvas_cc/models/discussion_spec.rb +47 -0
- data/spec/moodle2cc/canvas_cc/models/discussion_topic_spec.rb +7 -0
- data/spec/moodle2cc/canvas_cc/models/module_item_spec.rb +15 -0
- data/spec/moodle2cc/canvas_cc/models/page_spec.rb +34 -0
- data/spec/moodle2cc/canvas_cc/models/question_spec.rb +8 -0
- data/spec/moodle2cc/canvas_cc/models/resource_spec.rb +17 -0
- data/spec/moodle2cc/canvas_cc/models/web_content_spec.rb +7 -0
- data/spec/moodle2cc/canvas_cc/models/web_link_spec.rb +9 -0
- data/spec/moodle2cc/canvas_cc/module_meta_writer_spec.rb +118 -0
- data/spec/moodle2cc/canvas_cc/multiple_answers_question_writer_spec.rb +50 -0
- data/spec/moodle2cc/canvas_cc/multiple_blanks_question_writer_spec.rb +77 -0
- data/spec/moodle2cc/canvas_cc/multiple_choice_question_writer_spec.rb +69 -0
- data/spec/moodle2cc/canvas_cc/multiple_dropdowns_question_writer_spec.rb +46 -0
- data/spec/moodle2cc/canvas_cc/numerical_question_writer_spec.rb +69 -0
- data/spec/moodle2cc/canvas_cc/page_writer_spec.rb +29 -0
- data/spec/moodle2cc/canvas_cc/question_bank_writer_spec.rb +43 -0
- data/spec/moodle2cc/canvas_cc/question_writer_spec.rb +129 -0
- data/spec/moodle2cc/canvas_cc/short_answer_question_writer_spec.rb +50 -0
- data/spec/moodle2cc/canvas_cc/text_only_question_writer_spec.rb +23 -0
- data/spec/moodle2cc/canvas_cc/true_false_question_writer_spec.rb +62 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/shared_examples_for_models.rb +26 -0
- data/test/acceptance/migrator_test.rb +24 -0
- data/test/fixtures/cc.imscc +0 -0
- data/test/fixtures/common_cartridge/ims_manifest_simple.xml +45 -0
- data/test/fixtures/common_cartridge/schema/cccv1p0.xsd +563 -0
- data/test/fixtures/common_cartridge/schema/ccv1p1_imsccauth_v1p1.xsd +203 -0
- data/test/fixtures/common_cartridge/schema/ccv1p1_imscp_v1p2_v1p0.xsd +827 -0
- data/test/fixtures/common_cartridge/schema/ccv1p1_lommanifest_v1p0.xsd +822 -0
- data/test/fixtures/common_cartridge/schema/ccv1p1_lomresource_v1p0.xsd +273 -0
- data/test/fixtures/common_cartridge/schema/xml.xsd +286 -0
- data/test/test_helper.rb +71 -0
- data/test/test_question_helper.rb +122 -0
- data/test/test_wiki_helper.rb +21 -0
- data/test/unit/canvas/assessment_test.rb +261 -0
- data/test/unit/canvas/assignment_test.rb +220 -0
- data/test/unit/canvas/converter_test.rb +171 -0
- data/test/unit/canvas/course_test.rb +176 -0
- data/test/unit/canvas/discussion_topic_test.rb +129 -0
- data/test/unit/canvas/label_test.rb +32 -0
- data/test/unit/canvas/question_bank_test.rb +92 -0
- data/test/unit/canvas/question_group_test.rb +90 -0
- data/test/unit/canvas/question_test.rb +876 -0
- data/test/unit/canvas/web_content_test.rb +46 -0
- data/test/unit/canvas/web_link_test.rb +48 -0
- data/test/unit/canvas/wiki_test.rb +74 -0
- data/test/unit/cc/assessment_test.rb +48 -0
- data/test/unit/cc/assignment_test.rb +72 -0
- data/test/unit/cc/cc_helper_test.rb +19 -0
- data/test/unit/cc/converter_test.rb +173 -0
- data/test/unit/cc/course_test.rb +36 -0
- data/test/unit/cc/discussion_topic_test.rb +83 -0
- data/test/unit/cc/label_test.rb +29 -0
- data/test/unit/cc/question_test.rb +54 -0
- data/test/unit/cc/web_content_test.rb +76 -0
- data/test/unit/cc/web_link_test.rb +159 -0
- data/test/unit/cc/wiki_test.rb +139 -0
- data/test/unit/logger_test.rb +33 -0
- data/test/unit/migrator_test.rb +67 -0
- data/test/unit/resource_factory_test.rb +216 -0
- 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
|