diff --git a/lib/cc/assignment_groups.rb b/lib/cc/assignment_groups.rb index 3885582f910..8b0d251dc82 100644 --- a/lib/cc/assignment_groups.rb +++ b/lib/cc/assignment_groups.rb @@ -36,7 +36,12 @@ module CC "xsi:schemaLocation"=> "#{CCHelper::CANVAS_NAMESPACE} #{CCHelper::XSD_URI}" ) do |groups_node| @course.assignment_groups.active.each do |group| - next unless export_object?(group) || group.assignments.any?{|a| export_object?(a)} + add_item_to_export(group) if for_course_copy && !export_object?(group) && group.assignments.any? do |a| + export_object?(a) || + a.quiz && export_object?(a.quiz) || + a.discussion_topic && export_object?(a.discussion_topic) + end + next unless export_object?(group) migration_id = CCHelper.create_key(group) groups_node.assignmentGroup(:identifier=>migration_id) do |group_node| group_node.title group.name diff --git a/lib/cc/assignment_resources.rb b/lib/cc/assignment_resources.rb index 907381f5407..4aed8afc2c0 100644 --- a/lib/cc/assignment_resources.rb +++ b/lib/cc/assignment_resources.rb @@ -73,7 +73,7 @@ module CC "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation"=> "#{CCHelper::ASSIGNMENT_NAMESPACE} #{CCHelper::ASSIGNMENT_XSD_URI}" ) do |a| - AssignmentResources.create_cc_assignment(a, assignment, migration_id) + AssignmentResources.create_cc_assignment(a, assignment, migration_id, @manifest) end end @@ -108,7 +108,7 @@ module CC "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation"=> "#{CCHelper::CANVAS_NAMESPACE} #{CCHelper::XSD_URI}" ) do |a| - AssignmentResources.create_canvas_assignment(a, assignment) + AssignmentResources.create_canvas_assignment(a, assignment, @manifest) end assignment_file.close @@ -128,7 +128,7 @@ module CC "online_upload" => "file" }.freeze - def self.create_cc_assignment(node, assignment, migration_id) + def self.create_cc_assignment(node, assignment, migration_id, manifest = nil) node.title(assignment.title) node.text(assignment.description, texttype: 'text/html') if assignment.points_possible @@ -149,20 +149,20 @@ module CC "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation"=> "#{CCHelper::CANVAS_NAMESPACE} #{CCHelper::XSD_URI}" ) do |a| - AssignmentResources.create_canvas_assignment(a, assignment) + AssignmentResources.create_canvas_assignment(a, assignment, manifest) end end end - def self.create_canvas_assignment(node, assignment) + def self.create_canvas_assignment(node, assignment, manifest = nil) node.title assignment.title node.due_at CCHelper::ims_datetime(assignment.due_at) if assignment.due_at node.lock_at CCHelper::ims_datetime(assignment.lock_at) if assignment.lock_at node.unlock_at CCHelper::ims_datetime(assignment.unlock_at) if assignment.unlock_at node.all_day_date CCHelper::ims_date(assignment.all_day_date) if assignment.all_day_date node.peer_reviews_due_at CCHelper::ims_datetime(assignment.peer_reviews_due_at) if assignment.peer_reviews_due_at - node.assignment_group_identifierref CCHelper.create_key(assignment.assignment_group) - node.grading_standard_identifierref CCHelper.create_key(assignment.grading_standard) if assignment.grading_standard + node.assignment_group_identifierref CCHelper.create_key(assignment.assignment_group) if assignment.assignment_group && (!manifest || manifest.export_object?(assignment.assignment_group)) + node.grading_standard_identifierref CCHelper.create_key(assignment.grading_standard) if assignment.grading_standard && (!manifest || manifest.export_object?(assignment.grading_standard)) node.workflow_state assignment.workflow_state if assignment.rubric assoc = assignment.rubric_association diff --git a/lib/cc/grading_standards.rb b/lib/cc/grading_standards.rb index fcede6d6c4e..a9bc0f69513 100644 --- a/lib/cc/grading_standards.rb +++ b/lib/cc/grading_standards.rb @@ -19,14 +19,16 @@ module CC module GradingStandards def add_referenced_grading_standards @course.assignments.active.where('grading_standard_id IS NOT NULL').each do |assignment| - next unless export_object?(assignment) + next unless export_object?(assignment) || + assignment.quiz && export_object?(assignment.quiz) || + assignment.discussion_topic && export_object?(assignment.discussion_topic) gs = assignment.grading_standard add_item_to_export(gs) if gs && gs.context_type == 'Course' && gs.context_id == @course.id end end def create_grading_standards(document=nil) - add_referenced_grading_standards + add_referenced_grading_standards if for_course_copy standards_to_copy = (@course.grading_standards.to_a + [@course.grading_standard]).compact.uniq(&:id).select{|s| export_object?(s)} return nil unless standards_to_copy.size > 0 diff --git a/lib/cc/qti/qti_generator.rb b/lib/cc/qti/qti_generator.rb index d764d064e07..079a4ad668e 100644 --- a/lib/cc/qti/qti_generator.rb +++ b/lib/cc/qti/qti_generator.rb @@ -196,7 +196,7 @@ module CC if quiz.assignment && !quiz.assignment.deleted? assignment_migration_id = CCHelper.create_key(quiz.assignment) doc.assignment(:identifier=>assignment_migration_id) do |a| - AssignmentResources.create_canvas_assignment(a, quiz.assignment) + AssignmentResources.create_canvas_assignment(a, quiz.assignment, @manifest) end end if quiz.assignment_group_id diff --git a/lib/cc/topic_resources.rb b/lib/cc/topic_resources.rb index d1c2de7f60b..34752045349 100644 --- a/lib/cc/topic_resources.rb +++ b/lib/cc/topic_resources.rb @@ -123,7 +123,7 @@ module CC if topic.assignment && !topic.assignment.deleted? assignment_migration_id = CCHelper.create_key(topic.assignment) doc.assignment(:identifier=>assignment_migration_id) do |a| - AssignmentResources.create_canvas_assignment(a, topic.assignment) + AssignmentResources.create_canvas_assignment(a, topic.assignment, @manifest) end end end diff --git a/spec/models/content_migration/course_copy_assignments_spec.rb b/spec/models/content_migration/course_copy_assignments_spec.rb index ef6d218f5e5..665d7b8c5fa 100644 --- a/spec/models/content_migration/course_copy_assignments_spec.rb +++ b/spec/models/content_migration/course_copy_assignments_spec.rb @@ -54,6 +54,29 @@ describe ContentMigration do to_assign.assignment_group.should == @copy_to.assignment_groups.find_by_migration_id(mig_id(g)) end + it "should link assignments to assignment groups on complete export" do + g = @copy_from.assignment_groups.create!(:name => "group") + from_assign = @copy_from.assignments.create!(:title => "some assignment", :assignment_group_id => g.id) + run_export_and_import + to_assign = @copy_to.assignments.find_by_migration_id(mig_id(from_assign)) + to_assign.assignment_group.should == @copy_to.assignment_groups.find_by_migration_id(mig_id(g)) + end + + it "should not link assignments to assignment groups on selective export" do + g = @copy_from.assignment_groups.create!(:name => "group") + from_assign = @copy_from.assignments.create!(:title => "some assignment", :assignment_group_id => g.id) + # test that we neither export nor reference the assignment group + unrelated_group = @copy_to.assignment_groups.create! name: 'unrelated group with coincidentally matching migration id' + unrelated_group.update_attribute :migration_id, mig_id(g) + run_export_and_import do |export| + export.selected_content = { 'assignments' => { mig_id(from_assign) => "1" } } + end + to_assign = @copy_to.assignments.find_by_migration_id(mig_id(from_assign)) + to_assign.assignment_group.should_not == unrelated_group + unrelated_group.reload.name.should_not eql g.name + end + + it "should copy assignment attributes" do assignment_model(:course => @copy_from, :points_possible => 40, :submission_types => 'file_upload', :grading_type => 'points') @assignment.turnitin_enabled = true @@ -313,6 +336,31 @@ describe ContentMigration do @copy_to.grading_standards.map(&:title).should eql %w(Two) @copy_to.assignments.first.grading_standard.title.should eql 'Two' end + + it "should copy referenced grading standards in complete export" do + gs = make_grading_standard(@copy_from, title: 'GS') + assign = @copy_from.assignments.build + assign.grading_standard = gs + assign.save! + run_export_and_import + @copy_to.assignments.first.grading_standard.title.should eql gs.title + end + + it "should not copy referenced grading standards in selective export" do + gs = make_grading_standard(@copy_from, title: 'One') + assign = @copy_from.assignments.build + assign.grading_standard = gs + assign.save! + # test that we neither export nor reference the grading standard + unrelated_grading_standard = make_grading_standard(@copy_to, title: 'unrelated grading standard with coincidentally matching migration id') + unrelated_grading_standard.update_attribute :migration_id, mig_id(gs) + run_export_and_import do |export| + export.selected_content = { 'assignments' => { mig_id(assign) => "1" } } + end + @copy_to.assignments.count.should eql 1 + @copy_to.assignments.first.grading_standard.should be_nil + unrelated_grading_standard.reload.title.should_not eql gs.title + end end end end diff --git a/spec/models/content_migration/course_copy_discussions_spec.rb b/spec/models/content_migration/course_copy_discussions_spec.rb index eded2579c3d..fe15f2d5a13 100644 --- a/spec/models/content_migration/course_copy_discussions_spec.rb +++ b/spec/models/content_migration/course_copy_discussions_spec.rb @@ -4,6 +4,15 @@ describe ContentMigration do context "course copy discussions" do include_examples "course copy" + def graded_discussion_topic + @topic = @copy_from.discussion_topics.build(:title => "topic") + @assignment = @copy_from.assignments.build(:submission_types => 'discussion_topic', :title => @topic.title) + @assignment.infer_times + @assignment.saved_by = :discussion_topic + @topic.assignment = @assignment + @topic.save + end + it "should copy discussion topic attributes" do topic = @copy_from.discussion_topics.create!(:title => "topic", :message => "

bloop

", :pinned => true, :discussion_type => "threaded", @@ -25,25 +34,20 @@ describe ContentMigration do end it "should copy a discussion topic when assignment is selected" do - topic = @copy_from.discussion_topics.build(:title => "topic") - assignment = @copy_from.assignments.build(:submission_types => 'discussion_topic', :title => topic.title) - assignment.infer_times - assignment.saved_by = :discussion_topic - topic.assignment = assignment - topic.save + graded_discussion_topic # Should not fail if the destination has a group @copy_to.groups.create!(:name => 'some random group of people') @cm.copy_options = { - :assignments => {mig_id(assignment) => "1"}, - :discussion_topics => {mig_id(topic) => "0"}, + :assignments => {mig_id(@assignment) => "1"}, + :discussion_topics => {mig_id(@topic) => "0"}, } @cm.save! run_course_copy - @copy_to.discussion_topics.find_by_migration_id(mig_id(topic)).should_not be_nil + @copy_to.discussion_topics.find_by_migration_id(mig_id(@topic)).should_not be_nil end it "should properly copy selected delayed announcements" do @@ -74,22 +78,56 @@ describe ContentMigration do end it "should not copy deleted assignment attached to topic" do - topic = @copy_from.discussion_topics.build(:title => "topic") - assignment = @copy_from.assignments.build(:submission_types => 'discussion_topic', :title => topic.title) - assignment.infer_times - assignment.saved_by = :discussion_topic - topic.assignment = assignment - topic.save! - assignment.workflow_state = 'deleted' - assignment.save! + graded_discussion_topic + @assignment.workflow_state = 'deleted' + @assignment.save! - topic.reload - topic.active?.should == true + @topic.reload + @topic.active?.should == true run_course_copy - @copy_to.discussion_topics.find_by_migration_id(mig_id(topic)).should_not be_nil - @copy_to.assignments.find_by_migration_id(mig_id(assignment)).should be_nil + @copy_to.discussion_topics.find_by_migration_id(mig_id(@topic)).should_not be_nil + @copy_to.assignments.find_by_migration_id(mig_id(@assignment)).should be_nil end + + it "should copy the assignment group and grading standard in selective copy" do + graded_discussion_topic + gs = make_grading_standard(@copy_from, title: 'One') + group = @copy_from.assignment_groups.create!(:name => "new group") + @assignment.assignment_group = group + @assignment.grading_standard = gs + @assignment.save! + @cm.copy_options = { 'everything' => '0', 'discussion_topics' => { mig_id(@topic) => "1" } } + run_course_copy + new_topic = @copy_to.discussion_topics.find_by_migration_id(mig_id(@topic)) + new_topic.assignment.should be_present + new_topic.assignment.assignment_group.migration_id.should eql mig_id(group) + new_topic.assignment.grading_standard.migration_id.should eql mig_id(gs) + end + + it "should not copy the assignment group and grading standard in selective export" do + graded_discussion_topic + gs = make_grading_standard(@copy_from, title: 'One') + group = @copy_from.assignment_groups.create!(:name => "new group") + @assignment.assignment_group = group + @assignment.grading_standard = gs + @assignment.save! + # test that we neither export nor reference the grading standard and assignment group + decoy_gs = make_grading_standard(@copy_to, title: 'decoy') + decoy_gs.update_attribute :migration_id, mig_id(gs) + decoy_ag = @copy_to.assignment_groups.create! name: 'decoy' + decoy_ag.update_attribute :migration_id, mig_id(group) + run_export_and_import do |export| + export.selected_content = { 'discussion_topics' => { mig_id(@topic) => "1" } } + end + new_topic = @copy_to.discussion_topics.find_by_migration_id(mig_id(@topic)) + new_topic.assignment.should be_present + new_topic.assignment.grading_standard.should be_nil + new_topic.assignment.assignment_group.migration_id.should_not eql mig_id(@group) + decoy_gs.reload.title.should_not eql gs.title + decoy_ag.reload.name.should_not eql group.name + end + end end diff --git a/spec/models/content_migration/course_copy_helper.rb b/spec/models/content_migration/course_copy_helper.rb index 11d9a948e96..45fa5a4ede5 100644 --- a/spec/models/content_migration/course_copy_helper.rb +++ b/spec/models/content_migration/course_copy_helper.rb @@ -33,6 +33,26 @@ shared_examples_for "course copy" do @copy_to.reload end + def run_export_and_import + export = @copy_from.content_exports.build + export.export_type = ContentExport::COMMON_CARTRIDGE + export.user = @teacher + yield(export) if block_given? + export.save + export.export_course + export.workflow_state.should == 'exported' + export.attachment_id.should_not be_nil + + @cm.set_default_settings + @cm.migration_type = 'canvas_cartridge_importer' + worker = Canvas::Migration::Worker::CCWorker.new + @cm.attachment_id = export.attachment_id + @cm.skip_job_progress = true + worker.perform(@cm) + @cm.workflow_state.should == 'imported' + @copy_to.reload + end + def make_grading_standard(context, opts = {}) gs = context.grading_standards.new gs.title = opts[:title] || "Standard eh" diff --git a/spec/models/content_migration/course_copy_quizzes_spec.rb b/spec/models/content_migration/course_copy_quizzes_spec.rb index 5529b19c252..ada176ac307 100644 --- a/spec/models/content_migration/course_copy_quizzes_spec.rb +++ b/spec/models/content_migration/course_copy_quizzes_spec.rb @@ -580,5 +580,34 @@ equation: "new group") + quiz = @copy_from.quizzes.create(:title => "asmnt", :quiz_type => "assignment", :assignment_group_id => group.id) + quiz.publish! + @cm.copy_options = { 'everything' => '0', 'quizzes' => { mig_id(quiz) => "1" } } + run_course_copy + dest_quiz = @copy_to.quizzes.find_by_migration_id mig_id(quiz) + dest_quiz.assignment_group.migration_id.should eql mig_id(group) + end + + it "should not copy the assignment group in selective export" do + pending unless Qti.qti_enabled? + + group = @copy_from.assignment_groups.create!(:name => "new group") + quiz = @copy_from.quizzes.create(:title => "asmnt", :quiz_type => "assignment", :assignment_group_id => group.id) + quiz.publish! + # test that we neither export nor reference the assignment group + decoy_assignment_group = @copy_to.assignment_groups.create!(:name => "decoy") + decoy_assignment_group.update_attribute(:migration_id, mig_id(group)) + run_export_and_import do |export| + export.selected_content = { 'quizzes' => { mig_id(quiz) => "1" } } + end + dest_quiz = @copy_to.quizzes.find_by_migration_id mig_id(quiz) + dest_quiz.assignment_group.migration_id.should_not eql decoy_assignment_group + decoy_assignment_group.reload.name.should_not eql group.name + end end end