diff --git a/app/models/assessment_question.rb b/app/models/assessment_question.rb index 13b8f6776da..ebcfa87f420 100644 --- a/app/models/assessment_question.rb +++ b/app/models/assessment_question.rb @@ -391,7 +391,7 @@ class AssessmentQuestion < ActiveRecord::Base end def self.process_migration(data, migration) - question_data = {} + question_data = {:aq_data=>{}, :qq_data=>{}} questions = data['assessment_questions'] ? data['assessment_questions']['assessment_questions'] : [] questions ||= [] to_import = migration.to_import 'quizzes' @@ -425,6 +425,10 @@ class AssessmentQuestion < ActiveRecord::Base banks = {} questions.each do |question| + if question[:assessment_question_migration_id] + question_data[:qq_data][question['migration_id']] = question + next + end question[:question_bank_name] = nil if question[:question_bank_name] == '' question[:question_bank_name] ||= bank_map[question[:migration_id]] question[:question_bank_name] ||= migration.question_bank_name @@ -441,7 +445,7 @@ class AssessmentQuestion < ActiveRecord::Base end question = AssessmentQuestion.import_from_migration(question, migration.context, banks[hash_id]) - question_data[question['migration_id']] = question + question_data[:aq_data][question['migration_id']] = question end end @@ -461,8 +465,7 @@ class AssessmentQuestion < ActiveRecord::Base end end context.imported_migration_items << bank if context.imported_migration_items && !context.imported_migration_items.include?(bank) - hash[:question_text] = ImportedHtmlConverter.convert(hash[:question_text], context, true) if hash[:question_text] - hash[:answers].each{ |answer| answer[:html] = ImportedHtmlConverter.convert(answer[:html], context, true) unless answer[:html].blank? } if hash[:answers] + prep_for_import(hash, context) question_data = ActiveRecord::Base.connection.quote hash.to_yaml question_name = ActiveRecord::Base.connection.quote hash[:question_name] query = "INSERT INTO assessment_questions (name, question_data, context_id, context_type, workflow_state, created_at, updated_at, assessment_question_bank_id, migration_id)" @@ -472,6 +475,11 @@ class AssessmentQuestion < ActiveRecord::Base hash end + def self.prep_for_import(hash, context) + hash[:question_text] = ImportedHtmlConverter.convert(hash[:question_text], context, true) if hash[:question_text] + hash[:answers].each{ |answer| answer[:html] = ImportedHtmlConverter.convert(answer[:html], context, true) unless answer[:html].blank? } if hash[:answers] + end + named_scope :active, lambda { {:conditions => ['assessment_questions.workflow_state != ?', 'deleted'] } } diff --git a/app/models/quiz.rb b/app/models/quiz.rb index 68a4df13c96..46ecbc8525a 100644 --- a/app/models/quiz.rb +++ b/app/models/quiz.rb @@ -1129,11 +1129,20 @@ class Quiz < ActiveRecord::Base hash[:questions].each do |question| case question[:question_type] when "question_reference" - if aq = question_data[question[:migration_id]] + if qq = question_data[:qq_data][question[:migration_id]] + if qq[:assessment_question_migration_id] + if aq = question_data[:aq_data][qq[:assessment_question_migration_id]] + qq['assessment_question_id'] = aq['assessment_question_id'] + AssessmentQuestion.prep_for_import(qq, context) + QuizQuestion.import_from_migration(qq, context, item) + else + AssessmentQuestion.import_from_migration(qq, context) + QuizQuestion.import_from_migration(qq, context, item) + end + end + elsif aq = question_data[:aq_data][question[:migration_id]] aq[:points_possible] = question[:points_possible] if question[:points_possible] QuizQuestion.import_from_migration(aq, context, item) - else - #TODO: no assessment question was imported for this question... end when "question_group" QuizGroup.import_from_migration(question, context, item, question_data) diff --git a/app/models/quiz_group.rb b/app/models/quiz_group.rb index e08b43abe4c..ee43c67eb06 100644 --- a/app/models/quiz_group.rb +++ b/app/models/quiz_group.rb @@ -83,10 +83,20 @@ class QuizGroup < ActiveRecord::Base end item.save! hash[:questions].each do |question| - if aq = question_data[question[:migration_id]] + if qq = question_data[:qq_data][question[:migration_id]] + if qq[:assessment_question_migration_id] + if aq = question_data[:aq_data][qq[:assessment_question_migration_id]] + qq['assessment_question_id'] = aq['assessment_question_id'] + AssessmentQuestion.prep_for_import(qq, context) + QuizQuestion.import_from_migration(qq, context, quiz, item) + else + AssessmentQuestion.import_from_migration(qq, context) + QuizQuestion.import_from_migration(qq, context, quiz, item) + end + end + elsif aq = question_data[:aq_data][question[:migration_id]] + aq[:points_possible] = question[:points_possible] if question[:points_possible] QuizQuestion.import_from_migration(aq, context, quiz, item) - else - #TODO: no assessment question was imported for this question... end end diff --git a/app/models/quiz_question.rb b/app/models/quiz_question.rb index 24d9939d3a4..1fe0bc82c6e 100644 --- a/app/models/quiz_question.rb +++ b/app/models/quiz_question.rb @@ -127,7 +127,10 @@ class QuizQuestion < ActiveRecord::Base def self.import_from_migration(hash, context, quiz=nil, quiz_group=nil) question_data = ActiveRecord::Base.connection.quote hash.to_yaml query = "INSERT INTO quiz_questions (quiz_id, quiz_group_id, assessment_question_id, question_data, created_at, updated_at, migration_id)" - query += " VALUES (#{quiz ? quiz.id : 'NULL'}, #{quiz_group ? quiz_group.id : 'NULL'}, #{hash['assessment_question_id']},#{question_data},'#{Time.now.to_s(:db)}', '#{Time.now.to_s(:db)}', '#{hash[:migration_id]}')" + aq_id = hash['assessment_question_id'] ? hash['assessment_question_id'] : 'NULL' + g_id = quiz_group ? quiz_group.id : 'NULL' + q_id = quiz ? quiz.id : 'NULL' + query += " VALUES (#{q_id}, #{g_id}, #{aq_id},#{question_data},'#{Time.now.to_s(:db)}', '#{Time.now.to_s(:db)}', '#{hash[:migration_id]}')" id = ActiveRecord::Base.connection.insert(query) hash[:quiz_question_id] = id hash diff --git a/lib/cc/qti/qti_generator.rb b/lib/cc/qti/qti_generator.rb index 08691e143a8..ddef44cc259 100644 --- a/lib/cc/qti/qti_generator.rb +++ b/lib/cc/qti/qti_generator.rb @@ -41,6 +41,17 @@ module CC non_cc_folder = File.join(@export_dir, ASSESSMENT_NON_CC_FOLDER) FileUtils::mkdir_p non_cc_folder + @course.assessment_question_banks.active.each do |bank| + bank_mig_id = create_key(bank) + + rel_path = File.join(ASSESSMENT_NON_CC_FOLDER, bank_mig_id + ".xml") + full_path = File.join(@export_dir, rel_path) + File.open(full_path, 'w') do |file| + doc = Builder::XmlMarkup.new(:target=>file, :indent=>2) + generate_bank(doc, bank, bank_mig_id) + end + end + @course.quizzes.active.each do |quiz| cc_qti_migration_id = create_key(quiz) resource_dir = File.join(@export_dir, cc_qti_migration_id) @@ -160,7 +171,7 @@ module CC if for_cc add_cc_question(section_node, item) else - add_question(section_node, item) + add_quiz_question(section_node, item) end elsif item[:questions] # It's a QuizGroup if for_cc @@ -175,6 +186,28 @@ module CC end # qti node end + def generate_bank(doc, bank, migration_id) + doc.instruct! + doc.questestinterop("xmlns" => "http://www.imsglobal.org/xsd/ims_qtiasiv1p2", + "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", + "xsi:schemaLocation"=> "http://www.imsglobal.org/xsd/ims_qtiasiv1p2 http://www.imsglobal.org/xsd/ims_qtiasiv1p2p1.xsd" + ) do |qti_node| + qti_node.objectbank( + :ident => migration_id + ) do |bank_node| + + bank_node.qtimetadata do |meta_node| + meta_field(meta_node, 'bank_title', bank.title) + end # meta_node + + bank.assessment_questions.each do |aq| + add_question(bank_node, aq.data.with_indifferent_access) + end + + end # bank_node + end # qti node + end + def meta_field(node, label, entry) node.qtimetadatafield do |meta_node| meta_node.fieldlabel label @@ -228,7 +261,7 @@ module CC unless group[:assessment_question_bank_id] group[:questions].each do |question| - add_question(section_node, question) + add_quiz_question(section_node, question) end end end # section node diff --git a/lib/cc/qti/qti_items.rb b/lib/cc/qti/qti_items.rb index 61b967e033f..9c5e358796e 100644 --- a/lib/cc/qti/qti_items.rb +++ b/lib/cc/qti/qti_items.rb @@ -38,6 +38,28 @@ module CC MULTI_ANSWER_TYPES = ['matching_question', 'multiple_dropdowns_question', 'fill_in_multiple_blanks_question'] + + def add_ref_or_question(node, question) + aq = nil + unless question[:assessment_question_id].blank? + if aq = AssessmentQuestion.find_by_id(question[:assessment_question_id]) + if aq.deleted? || + !aq.assessment_question_bank || + aq.assessment_question_bank.deleted? || + aq.assessment_question_bank.context_id != @course.id || + aq.assessment_question_bank.context_type != @course.class.to_s + aq = nil + end + end + end + + if aq + ref = CC::CCHelper::create_key(aq) + node.itemref(:linkrefid => ref) + else + add_question(node, question) + end + end # if the question is a supported CC type it will be added # it it's not supported it's just skipped @@ -48,8 +70,16 @@ module CC true end + def add_quiz_question(node, question) + question[:is_quiz_question] = true + add_question(node, question) + end + def add_question(node, question, for_cc=false) - question['migration_id'] = create_key("assessment_question_#{question['assessment_question_id']}") + aq_mig_id = create_key("assessment_question_#{question['assessment_question_id']}") + qq_mig_id = create_key("assessment_question_#{question['id']}") + question['migration_id'] = question[:is_quiz_question] ? qq_mig_id : aq_mig_id + if question['question_type'] == 'missing_word_question' change_missing_word(question) end @@ -67,6 +97,9 @@ module CC else meta_field(qm_node, 'question_type', question['question_type']) meta_field(qm_node, 'points_possible', question['points_possible']) + if question[:is_quiz_question] + meta_field(qm_node, 'assessment_question_identifierref', aq_mig_id) + end end end end # meta data diff --git a/spec/lib/importer/import_helper.rb b/spec/lib/importer/import_helper.rb index a9f4ec82051..281d86975b9 100644 --- a/spec/lib/importer/import_helper.rb +++ b/spec/lib/importer/import_helper.rb @@ -50,12 +50,12 @@ def get_import_data(sub_folder, hash_name) end def import_example_questions(context) - question_data = {} + question_data = {:aq_data=>{}, :qq_data=>{}} QUESTIONS.each do |question| if import_data_exists?(['vista', 'quiz'], question[0]) q = get_import_data ['vista', 'quiz'], question[0] q = AssessmentQuestion.import_from_migration(q, context) - question_data[q['migration_id']] = q + question_data[:aq_data][q['migration_id']] = q end end question_data diff --git a/vendor/plugins/qti_exporter/lib/qti_exporter/assessment_item_converter.rb b/vendor/plugins/qti_exporter/lib/qti_exporter/assessment_item_converter.rb index 8c7848ee589..408d8849efb 100644 --- a/vendor/plugins/qti_exporter/lib/qti_exporter/assessment_item_converter.rb +++ b/vendor/plugins/qti_exporter/lib/qti_exporter/assessment_item_converter.rb @@ -85,42 +85,47 @@ class AssessmentItemConverter end def parse_instructure_metadata - if bank = get_node_att(@doc, 'instructureMetadata instructureField[name=question_bank]', 'value') - @question[:question_bank_name] = bank - end - if bank = get_node_att(@doc, 'instructureMetadata instructureField[name=question_bank_iden]', 'value') - @question[:question_bank_id] = bank - end - if score = get_node_att(@doc, 'instructureMetadata instructureField[name=max_score]', 'value') - @question[:points_possible] = score.to_f - end - if score = get_node_att(@doc, 'instructureMetadata instructureField[name=points_possible]', 'value') - @question[:points_possible] = score.to_f - end - if type = get_node_att(@doc, 'instructureMetadata instructureField[name=bb_question_type]', 'value') - @migration_type = type - case @migration_type - when 'True/False' - @question[:question_type] = 'true_false_question' - when 'Short Response' - @question[:question_type] = 'essay_question' - when 'Fill in the Blank Plus' - @question[:question_type] = 'fill_in_multiple_blanks_question' - when 'WCT_FillInTheBlank' - @question[:question_type] = 'fill_in_multiple_blanks_question' - @question[:is_vista_fib] = true - when 'Jumbled Sentence' - @question[:question_type] = 'multiple_dropdowns_question' - when 'Essay' - @question[:question_type] = 'essay_question' + if meta = @doc.at_css('instructureMetadata') + if bank = get_node_att(meta, 'instructureField[name=question_bank]', 'value') + @question[:question_bank_name] = bank end - end - if type = get_node_att(@doc, 'instructureMetadata instructureField[name=question_type]', 'value') - @migration_type = type - if AssessmentQuestion::ALL_QUESTION_TYPES.member?(@migration_type) - @question[:question_type] = @migration_type - elsif @migration_type =~ /matching/i - @question[:question_type] = 'matching_question' + if bank = get_node_att(meta, 'instructureField[name=question_bank_iden]', 'value') + @question[:question_bank_id] = bank + end + if score = get_node_att(meta, 'instructureField[name=max_score]', 'value') + @question[:points_possible] = score.to_f + end + if score = get_node_att(meta, 'instructureField[name=points_possible]', 'value') + @question[:points_possible] = score.to_f + end + if ref = get_node_att(meta, 'instructureField[name=assessment_question_identifierref]', 'value') + @question[:assessment_question_migration_id] = ref + end + if type = get_node_att(meta, 'instructureField[name=bb_question_type]', 'value') + @migration_type = type + case @migration_type + when 'True/False' + @question[:question_type] = 'true_false_question' + when 'Short Response' + @question[:question_type] = 'essay_question' + when 'Fill in the Blank Plus' + @question[:question_type] = 'fill_in_multiple_blanks_question' + when 'WCT_FillInTheBlank' + @question[:question_type] = 'fill_in_multiple_blanks_question' + @question[:is_vista_fib] = true + when 'Jumbled Sentence' + @question[:question_type] = 'multiple_dropdowns_question' + when 'Essay' + @question[:question_type] = 'essay_question' + end + end + if type = get_node_att(meta, 'instructureField[name=question_type]', 'value') + @migration_type = type + if AssessmentQuestion::ALL_QUESTION_TYPES.member?(@migration_type) + @question[:question_type] = @migration_type + elsif @migration_type =~ /matching/i + @question[:question_type] = 'matching_question' + end end end end diff --git a/vendor/plugins/qti_exporter/spec/fixtures/canvas/matching.xml b/vendor/plugins/qti_exporter/spec/fixtures/canvas/matching.xml index 720e73b71eb..78586e8582d 100644 --- a/vendor/plugins/qti_exporter/spec/fixtures/canvas/matching.xml +++ b/vendor/plugins/qti_exporter/spec/fixtures/canvas/matching.xml @@ -19,13 +19,14 @@ Unknown text type: ignored mattext with texttype="text" treated as text/plain + diff --git a/vendor/plugins/qti_exporter/spec/lib/qti_converter/canvas_questions_spec.rb b/vendor/plugins/qti_exporter/spec/lib/qti_converter/canvas_questions_spec.rb index 374f10ac577..271f3f2ced0 100644 --- a/vendor/plugins/qti_exporter/spec/lib/qti_converter/canvas_questions_spec.rb +++ b/vendor/plugins/qti_exporter/spec/lib/qti_converter/canvas_questions_spec.rb @@ -208,7 +208,8 @@ module CanvasExpected :match_id=>2840, :text=>"4"}], :question_text=>"Make it stop! Please!", - :migration_id=>"i7ee7c77592c6cd4ac58509c3e41dace8", + :migration_id=>"i27a2844e09afc2eb6e4a6bf0599bf010", + :assessment_question_migration_id=>"i7ee7c77592c6cd4ac58509c3e41dace8", :question_type=>"matching_question", :incorrect_comments=>"How could you get this wrong?"}