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:
parent
169e54d87b
commit
349eacb763
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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'
|
||||
|
|
|
@ -6,5 +6,6 @@ module Qti
|
|||
D2L = 'd2l'
|
||||
RESPONDUS = 'respondus'
|
||||
WEBCT = 'webct'
|
||||
COMMON_CARTRIDGE = 'common_cartridge'
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue