add assessment importing to common cartridge importer

refs #4153

Change-Id: I60a4e8d88f8d121f8b928564f257819b3bca1d26
Reviewed-on: https://gerrit.instructure.com/5726
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
This commit is contained in:
Bracken Mosbacker 2011-09-20 09:45:09 -06:00
parent 169e54d87b
commit 349eacb763
12 changed files with 177 additions and 48 deletions

View File

@ -25,10 +25,7 @@ module ContentImportsHelper
end
def qti_enabled?
if plugin = Canvas::Plugin.find(:qti_exporter)
return plugin.settings[:enabled].to_s == 'true'
end
false
Qti.qti_enabled?
end
def exports_enabled?

View File

@ -67,6 +67,12 @@ module MigratorHelper
error
end
def add_warning(user_message, exception_or_info='')
if @settings[:content_migration].respond_to?(:add_warning)
@settings[:content_migration].add_warning(user_message, exception_or_info)
end
end
def logger
Rails.logger
end
@ -137,30 +143,39 @@ module MigratorHelper
@settings[:id_prepender]
end
def prepend_id(id)
id_prepender ? "#{id_prepender}_#{id}" : id
def prepend_id(id, prepend_value=nil)
prepend_value ||= id_prepender
prepend_value ? "#{prepend_value}_#{id}" : id
end
def add_assessment_id_prepend
if id_prepender
if @course[:assessment_questions]
@course[:assessment_questions][:assessment_questions].each do |q|
q[:migration_id] = prepend_id(q[:migration_id])
q[:question_bank_id] = prepend_id(q[:question_bank_id]) if q[:question_bank_id].present?
end
if @course[:assessment_questions] && @course[:assessment_questions][:assessment_questions]
prepend_id_to_questions(@course[:assessment_questions][:assessment_questions])
end
if @course[:assessments]
@course[:assessments][:assessments].each do |a|
a[:migration_id] = prepend_id(a[:migration_id])
a[:questions].each do |q|
if q[:question_type] == "question_reference"
q[:migration_id] = prepend_id(q[:migration_id])
elsif q[:question_type] == "question_group"
q[:question_bank_migration_id] = prepend_id(q[:question_bank_migration_id]) if q[:question_bank_migration_id].present?
q[:questions].each do |gq|
gq[:migration_id] = prepend_id(gq[:migration_id])
end
end
if @course[:assessments] && @course[:assessments][:assessments]
prepend_id_to_assessments(@course[:assessments][:assessments])
end
end
end
def prepend_id_to_questions(questions, prepend_value=nil)
questions.each do |q|
q[:migration_id] = prepend_id(q[:migration_id], prepend_value)
q[:question_bank_id] = prepend_id(q[:question_bank_id], prepend_value) if q[:question_bank_id].present?
end
end
def prepend_id_to_assessments(assessments, prepend_value=nil)
assessments.each do |a|
a[:migration_id] = prepend_id(a[:migration_id], prepend_value)
a[:questions].each do |q|
if q[:question_type] == "question_reference"
q[:migration_id] = prepend_id(q[:migration_id], prepend_value)
elsif q[:question_type] == "question_group"
q[:question_bank_migration_id] = prepend_id(q[:question_bank_migration_id], prepend_value) if q[:question_bank_migration_id].present?
q[:questions].each do |gq|
gq[:migration_id] = prepend_id(gq[:migration_id], prepend_value)
end
end
end

View File

@ -17,7 +17,6 @@ module Canvas::Migration
end
def identify_package
# todo verify it's a valid zip file
zip_file = Zip::ZipFile.open(@archive.path)
if zip_file.find_entry("AngelManifest.xml")
:angel_7_4

View File

@ -25,6 +25,7 @@ module CCHelper
IMS_DATE = "%Y-%m-%d"
IMS_DATETIME = "%Y-%m-%dT%H:%M:%S"
CC_EXTENSION = 'imscc'
QTI_EXTENSION = ".xml.qti"
CANVAS_PLATFORM = 'canvas.instructure.com'
# Common Cartridge 1.0

View File

@ -26,6 +26,7 @@ module Canvas::Migration
settings[:content_migration_id] = migration_id
settings[:user_id] = cm.user_id
settings[:attachment_id] = cm.attachment.id rescue nil
settings[:content_migration] = cm
converter_class = Worker::get_converter(settings)
converter = converter_class.new(settings)

View File

@ -22,6 +22,7 @@ module CC::Importer::Standard
include OrgConverter
include DiscussionConverter
include CC::Importer::BLTIConverter
include QuizConverter
MANIFEST_FILE = "imsmanifest.xml"
@ -52,12 +53,11 @@ module CC::Importer::Standard
@course[:file_map].values.each {|f|@file_path_migration_id[f[:path_name]] = f[:migration_id]}
@course[:discussion_topics] = convert_discussions
@course[:external_tools] = convert_blti_links(resources_by_type("imsbasiclti"))
@course[:assessment_questions], @course[:assessments] = convert_quizzes
@course[:modules] = convert_organizations(@manifest)
@course[:all_files_zip] = package_course_files(@course[:file_map])
# check for assignment intendeduse
# handle quizzes
# handle banks
#close up shop
save_to_file

View File

@ -51,7 +51,12 @@ module CC::Importer::Standard
case resource[:type]
when /assessment\z/
when /question-bank\z/
mod[:items] << {
:indent =>indent,
:linked_resource_type => 'ASSESSMENT',
:linked_resource_id => resource[:migration_id],
:linked_resource_title => get_node_val(item_node, 'title'),
}
when /\Aimswl/
item = {:indent => indent, :linked_resource_type => 'URL'}
item[:linked_resource_title] = get_node_val(item_node, 'title')

View File

@ -0,0 +1,92 @@
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
module CC::Importer::Standard
module QuizConverter
include CC::Importer
def convert_quizzes
quizzes = []
questions = []
conversion_dir = File.join(@unzipped_file_path, "temp_qti_conversions")
resources_by_type("imsqti").each do |res|
path = res[:href] || res[:files].first[:href]
path = get_full_path(path)
id = res[:migration_id]
if File.exists?(path)
qti_converted_dir = File.join(conversion_dir, id)
if run_qti_converter(path, qti_converted_dir, id)
# get quizzes/questions
if q_list = convert_questions(qti_converted_dir, id)
questions += q_list
end
if quiz = convert_assessment(qti_converted_dir, id)
quizzes << quiz
end
end
end
end
[{:assessment_questions => questions}, {:assessments => quizzes}]
end
def run_qti_converter(qti_file, out_folder, resource_id)
# convert to 2.1
command = Qti.get_conversion_command(out_folder, qti_file)
logger.debug "Running migration command: #{command}"
python_std_out = `#{command}`
if $?.exitstatus == 0
true
else
add_warning(I18n.t('lib.cc.standard.failed_to_convert_qti', 'Failed to import Assessment %{file_identifier}', :file_identifier => resource_id), "Output of QTI conversion tool: #{python_std_out.last(300)}")
false
end
end
def convert_questions(out_folder, resource_id)
questions = nil
begin
manifest_file = File.join(out_folder, Qti::QtiExporter::MANIFEST_FILE)
questions = Qti.convert_questions(manifest_file, :flavor => Qti::Flavors::COMMON_CARTRIDGE)
prepend_id_to_questions(questions, resource_id)
rescue
add_warning(I18n.t('lib.cc.standard.failed_to_convert_qti', 'Failed to import Assessment %{file_identifier}', :file_identifier => resource_id), $!)
end
questions
end
def convert_assessment(out_folder, resource_id)
quiz = nil
begin
manifest_file = File.join(out_folder, Qti::QtiExporter::MANIFEST_FILE)
quizzes = Qti.convert_assessments(manifest_file, :flavor => Qti::Flavors::COMMON_CARTRIDGE)
prepend_id_to_assessments(quizzes, resource_id)
if quiz = quizzes.first
quiz[:migration_id] = resource_id
end
rescue
add_warning(I18n.t('lib.cc.standard.failed_to_convert_qti', 'Failed to import Assessment %{file_identifier}', :file_identifier => resource_id), $!)
end
quiz
end
end
end

View File

@ -76,7 +76,7 @@ module CC
end
# Create the Canvas-specific QTI data
canvas_qti_rel_path = File.join(ASSESSMENT_NON_CC_FOLDER, cc_qti_migration_id + ".xml")
canvas_qti_rel_path = File.join(ASSESSMENT_NON_CC_FOLDER, cc_qti_migration_id + QTI_EXTENSION)
canvas_qti_path = File.join(@export_dir, canvas_qti_rel_path)
File.open(canvas_qti_path, 'w') do |file|
doc = Builder::XmlMarkup.new(:target=>file, :indent=>2)
@ -112,7 +112,7 @@ module CC
def generate_question_bank(bank)
bank_mig_id = create_key(bank)
rel_path = File.join(ASSESSMENT_NON_CC_FOLDER, bank_mig_id + ".xml")
rel_path = File.join(ASSESSMENT_NON_CC_FOLDER, bank_mig_id + QTI_EXTENSION)
full_path = File.join(@export_dir, rel_path)
File.open(full_path, 'w') do |file|
doc = Builder::XmlMarkup.new(:target=>file, :indent=>2)

View File

@ -52,8 +52,6 @@ describe "Standard Common Cartridge importing" do
mod1 = @course.context_modules.find_by_migration_id("I_00000")
mod1.name.should == "Your Mom, Research, & You"
#mod1.content_tags.count.should == 5
#mod1.content_tags.each{|ct|puts ct.inspect}
tag = mod1.content_tags[0]
tag.content_type.should == 'Attachment'
tag.content_id.should == @course.attachments.find_by_migration_id("I_00001_R").id
@ -62,12 +60,16 @@ describe "Standard Common Cartridge importing" do
tag.content_type.should == 'ContextModuleSubHeader'
tag.title.should == "Study Guide"
tag.indent.should == 0
# todo - once assessments are imported
#tag = mod1.content_tags[2]
#tag.title.should == "Pretest"
#tag.content_type.should == 'AssessmentSomething'
#tag.indent.should == 1
tag = mod1.content_tags[2]
index = 2
if Qti.qti_enabled?
tag = mod1.content_tags[index]
tag.title.should == "Pretest"
tag.content_type.should == 'Quiz'
tag.content_id.should == @course.quizzes.find_by_migration_id("I_00003_R").id
tag.indent.should == 1
index += 1
end
tag = mod1.content_tags[index]
tag.content_type.should == 'ExternalUrl'
tag.title.should == "Wikipedia - Your Mom"
tag.url.should == "http://en.wikipedia.org/wiki/Maternal_insult"
@ -115,17 +117,6 @@ describe "Standard Common Cartridge importing" do
tag.title.should == "BLTI Test"
tag.url.should == "http://www.imsglobal.org/developers/BLTI/tool.php"
tag.indent.should == 0
end
it "should get all the resources" do
@converter.resources['f4'][:intended_use].should == 'assignment'
@converter.resources['I_00004_R'][:intended_user_role].should == 'Instructor'
@converter.resources['I_00006_R'][:dependencies].should == ['I_00006_Media', 'I_media_R']
@converter.resources_by_type("webcontent").length.should == 5
@converter.resources_by_type("webcontent", "associatedcontent").length.should == 6
@converter.resources_by_type("imsdt").length.should == 2
@converter.resources_by_type("imswl").length.should == 3
end
it "should import external tools" do
@ -137,5 +128,26 @@ describe "Standard Common Cartridge importing" do
et.settings[:vendor_extensions].should == [{:platform=>"my.lms.com", :custom_fields=>{"key"=>"value"}}, {:platform=>"your.lms.com", :custom_fields=>{"key"=>"value", "key2"=>"value2"}}]
@migration.warnings.member?("The security parameters for the external tool \"#{et.name}\" need to be set in Course Settings.").should be_true
end
it "should import assessment data" do
if Qti.qti_enabled?
quiz = @course.quizzes.find_by_migration_id("I_00003_R")
quiz.quiz_questions.count.should == 11
quiz.title.should == "Pretest"
quiz.quiz_type.should == 'assignment'
quiz.allowed_attempts.should == 2
quiz.time_limit.should == 120
question = quiz.quiz_questions.first
question.question_data[:points_possible].should == 2
bank = @course.assessment_question_banks.find_by_migration_id("I_00004_R_QDB_1")
bank.assessment_questions.count.should == 11
bank.title.should == "QDB_1"
else
pending("Can't import assessment data with python QTI tool.")
end
end
end

View File

@ -1,6 +1,12 @@
require 'nokogiri'
module Qti
def self.qti_enabled?
if plugin = Canvas::Plugin.find(:qti_exporter)
return plugin.settings[:enabled].to_s == 'true'
end
false
end
end
require 'canvas/migration'

View File

@ -6,5 +6,6 @@ module Qti
D2L = 'd2l'
RESPONDUS = 'respondus'
WEBCT = 'webct'
COMMON_CARTRIDGE = 'common_cartridge'
end
end