Skip to content

Commit abf9d8f

Browse files
committed
assessment question bank migration content selection
test plan: * should be able to select question banks and quizzes separately for migrations in any combination, for any package type closes #CNVS-8526 #CNVS-13333 Change-Id: I566ab00f906369c3c28848b9d8985e7965378d1d Reviewed-on: https://gerrit.instructure.com/35354 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> QA-Review: Clare Strong <clare@instructure.com> Product-Review: Hilary Scharton <hilary@instructure.com>
1 parent 81963de commit abf9d8f

15 files changed

Lines changed: 307 additions & 220 deletions

app/models/importers/account_content_importer.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class AccountContentImporter < Importer
55
Importers.register_content_importer(self)
66

77
def self.import_content(account, data, params, migration)
8-
9-
migration.migration_settings[:import_quiz_questions_without_quiz] = true
108
Importers::AssessmentQuestionImporter.process_migration(data, migration)
119
Importers::LearningOutcomeImporter.process_migration(data, migration)
1210

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
module Importers
2+
class AssessmentQuestionBankImporter < Importer
3+
4+
self.item_class = AssessmentQuestionBank
5+
6+
# extract and compile the assessment question bank data from inside the individual questions
7+
def self.preprocess_migration_data(data)
8+
return if data[:assessment_question_banks]
9+
10+
questions = (data[:assessment_questions] && data[:assessment_questions][:assessment_questions]) || []
11+
12+
banks = []
13+
questions.each do |q|
14+
q.delete(:question_bank_name) if q[:question_bank_name].blank?
15+
16+
if id = q.delete(:question_bank_id)
17+
q[:question_bank_migration_id] ||= id
18+
end
19+
20+
if q[:question_bank_migration_id] && bank = banks.detect{|b| b[:migration_id] == q[:question_bank_migration_id]}
21+
bank[:count] += 1
22+
bank[:title] ||= q[:question_bank_name]
23+
q[:question_bank_name] ||= bank[:title]
24+
bank[:migration_id] ||= CC::CCHelper.create_key(bank[:title], 'assessment_question_bank') if bank[:title]
25+
elsif q[:question_bank_name] && bank = banks.detect{|b| b[:title] == q[:question_bank_name]}
26+
bank[:count] += 1
27+
q[:question_bank_migration_id] = bank[:migration_id]
28+
else
29+
bank = {:count => 1}
30+
bank[:title] = q[:question_bank_name] if q[:question_bank_name]
31+
bank[:migration_id] = q[:question_bank_migration_id] if q[:question_bank_migration_id]
32+
bank[:migration_id] ||= CC::CCHelper.create_key(bank[:title], 'assessment_question_bank') if bank[:title]
33+
q[:question_bank_migration_id] ||= bank[:migration_id]
34+
banks << bank
35+
end
36+
end
37+
38+
data[:assessment_question_banks] = banks
39+
end
40+
end
41+
end

app/models/importers/assessment_question_importer.rb

Lines changed: 67 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3,125 +3,90 @@ class AssessmentQuestionImporter < Importer
33

44
self.item_class = AssessmentQuestion
55

6+
def self.preprocess_migration_data(data)
7+
return if data['assessment_questions'] && data['assessment_questions']['preprocessed']
8+
9+
Importers::QuizImporter.preprocess_migration_data(data)
10+
Importers::AssessmentQuestionBankImporter.preprocess_migration_data(data)
11+
12+
data['assessment_questions'] ||= {}
13+
data['assessment_questions']['preprocessed'] = true
14+
end
15+
616
def self.process_migration(data, migration)
7-
question_data = {:aq_data=>{}, :qq_data=>{}}
17+
data = data.with_indifferent_access
18+
Importers::AssessmentQuestionImporter.preprocess_migration_data(data) # just in case
19+
question_data = {:aq_data => {}, :qq_ids => {}}
820
questions = data['assessment_questions'] ? data['assessment_questions']['assessment_questions'] : []
921
questions ||= []
10-
to_import = migration.to_import 'quizzes'
11-
total = questions.length
12-
13-
# If a question doesn't have a specified question bank
14-
# we want to put it in a bank named after the assessment it's in
15-
bank_map = {}
16-
assessments = data['assessments'] ? data['assessments']['assessments'] : []
17-
assessments ||= []
18-
assessments.each do |assmnt|
19-
next unless assmnt['questions']
20-
assmnt['questions'].each do |q|
21-
if q["question_type"] == "question_reference"
22-
bank_map[q['migration_id']] = [assmnt['title'], assmnt['migration_id']] if q['migration_id']
23-
elsif q["question_type"] == "question_group"
24-
q['questions'].each do |ref|
25-
bank_map[ref['migration_id']] = [assmnt['title'], assmnt['migration_id']] if ref['migration_id']
26-
end
22+
23+
existing_questions = migration.context.assessment_questions.
24+
except(:select).
25+
select("assessment_questions.id, assessment_questions.migration_id").
26+
where("assessment_questions.migration_id IS NOT NULL").
27+
index_by(&:migration_id)
28+
questions.each do |q|
29+
existing_question = existing_questions[q['migration_id']]
30+
q['assessment_question_id'] = existing_question.id if existing_question
31+
end
32+
33+
default_bank = migration.question_bank_id ? migration.context.assessment_question_banks.find_by_id(migration.question_bank_id) : nil
34+
migration.question_bank_name = default_bank.title if default_bank
35+
default_title = migration.question_bank_name || AssessmentQuestionBank.default_imported_title
36+
37+
bank_map = migration.context.assessment_question_banks.reload.index_by(&:migration_id)
38+
bank_map[CC::CCHelper.create_key(default_title, 'assessment_question_bank')] = default_bank if default_bank
39+
40+
questions.each do |question|
41+
question_data[:aq_data][question['migration_id']] = question
42+
43+
bank_mig_id = question[:question_bank_migration_id] || CC::CCHelper.create_key(default_title, 'assessment_question_bank')
44+
next unless migration.import_object?("assessment_question_banks", bank_mig_id)
45+
46+
question_bank = bank_map[bank_mig_id]
47+
48+
if !question_bank
49+
question_bank = migration.context.assessment_question_banks.new
50+
if bank_hash = data['assessment_question_banks'].detect{|qb_hash| qb_hash['migration_id'] == bank_mig_id}
51+
question_bank.title = bank_hash['title']
2752
end
53+
question_bank.title ||= default_title
54+
question_bank.migration_id = bank_mig_id
55+
question_bank.save!
56+
migration.add_imported_item(question_bank)
57+
bank_map[question_bank.migration_id] = question_bank
2858
end
29-
end
30-
if migration.to_import('assessment_questions') != false || (to_import && !to_import.empty?)
31-
32-
existing_questions = migration.context.assessment_questions.
33-
except(:select).
34-
select("assessment_questions.id, assessment_questions.migration_id").
35-
where("assessment_questions.migration_id IS NOT NULL").
36-
index_by(&:migration_id)
37-
questions.each do |q|
38-
existing_question = existing_questions[q['migration_id']]
39-
q['assessment_question_id'] = existing_question.id if existing_question
59+
60+
if question_bank.workflow_state == 'deleted'
61+
question_bank.workflow_state = 'active'
62+
question_bank.save!
4063
end
4164

42-
logger.debug "adding #{total} assessment questions"
43-
44-
default_bank = migration.question_bank_id ? migration.context.assessment_question_banks.find_by_id(migration.question_bank_id) : nil
45-
banks = {}
46-
questions.each do |question|
47-
question_bank = nil
48-
question[:question_bank_name] = nil if question[:question_bank_name] == ''
49-
question[:question_bank_name], question[:question_bank_migration_id] = bank_map[question[:migration_id]] if question[:question_bank_name].blank?
50-
if default_bank
51-
question_bank = default_bank
52-
else
53-
question[:question_bank_name] ||= migration.question_bank_name
54-
question[:question_bank_name] ||= AssessmentQuestionBank.default_imported_title
55-
end
56-
if question[:assessment_question_migration_id] && !migration.migration_settings[:import_quiz_questions_without_quiz]
57-
question_data[:qq_data][question['migration_id']] = question
58-
next
59-
end
60-
next if question[:question_bank_migration_id] &&
61-
!migration.import_object?("quizzes", question[:question_bank_migration_id]) &&
62-
!migration.import_object?("assessment_question_banks", question[:question_bank_migration_id])
63-
64-
if !question_bank
65-
hash_id = "#{question[:question_bank_id]}_#{question[:question_bank_name]}"
66-
if !banks[hash_id]
67-
bank_mig_id = question[:question_bank_id] || question[:question_bank_migration_id]
68-
unless bank = migration.context.assessment_question_banks.find_by_title_and_migration_id(question[:question_bank_name], bank_mig_id)
69-
bank = migration.context.assessment_question_banks.new
70-
bank.title = question[:question_bank_name]
71-
bank.migration_id = bank_mig_id
72-
bank.save!
73-
end
74-
if bank.workflow_state == 'deleted'
75-
bank.workflow_state = 'active'
76-
bank.save!
77-
end
78-
banks[hash_id] = bank
79-
end
80-
question_bank = banks[hash_id]
81-
end
65+
begin
66+
question = self.import_from_migration(question, migration.context, migration, question_bank)
8267

83-
begin
84-
question = self.import_from_migration(question, migration.context, migration, question_bank)
85-
86-
# If the question appears to have links, we need to translate them so that file links point
87-
# to the AssessmentQuestion. Ideally we would just do this before saving the question, but
88-
# the link needs to include the id of the AQ, which we don't have until it's saved. This will
89-
# be a problem as long as we use the question as a context for its attachments. (We're turning this
90-
# hash into a string so we can quickly check if anywhere in the hash might have a URL.)
91-
if question.to_s =~ %r{/files/\d+/(download|preview)}
92-
AssessmentQuestion.find(question[:assessment_question_id]).translate_links
93-
end
94-
95-
question_data[:aq_data][question['migration_id']] = question
96-
rescue
97-
migration.add_import_warning(t('#migration.quiz_question_type', "Quiz Question"), question[:question_name], $!)
68+
# If the question appears to have links, we need to translate them so that file links point
69+
# to the AssessmentQuestion. Ideally we would just do this before saving the question, but
70+
# the link needs to include the id of the AQ, which we don't have until it's saved. This will
71+
# be a problem as long as we use the question as a context for its attachments. (We're turning this
72+
# hash into a string so we can quickly check if anywhere in the hash might have a URL.)
73+
if question.to_s =~ %r{/files/\d+/(download|preview)}
74+
AssessmentQuestion.find(question[:assessment_question_id]).translate_links
9875
end
76+
77+
question_data[:aq_data][question['migration_id']] = question
78+
rescue
79+
migration.add_import_warning(t('#migration.quiz_question_type', "Quiz Question"), question[:question_name], $!)
9980
end
10081
end
10182

10283
question_data
10384
end
10485

105-
def self.import_from_migration(hash, context, migration=nil, bank=nil, options={})
86+
def self.import_from_migration(hash, context, migration, bank, options={})
10687
hash = hash.with_indifferent_access
107-
if !bank
108-
hash[:question_bank_name] = nil if hash[:question_bank_name] == ''
109-
hash[:question_bank_name] ||= AssessmentQuestionBank::default_imported_title
110-
migration_id = hash[:question_bank_id] || hash[:question_bank_migration_id]
111-
unless bank = AssessmentQuestionBank.find_by_context_type_and_context_id_and_title_and_migration_id(context.class.to_s, context.id, hash[:question_bank_name], migration_id)
112-
bank ||= context.assessment_question_banks.new
113-
bank.title = hash[:question_bank_name]
114-
bank.migration_id = migration_id
115-
bank.save!
116-
end
117-
if bank.workflow_state == 'deleted'
118-
bank.workflow_state = 'active'
119-
bank.save!
120-
end
121-
end
12288
hash.delete(:question_bank_migration_id) if hash.has_key?(:question_bank_migration_id)
12389

124-
migration.add_imported_item(bank) if migration
12590
self.prep_for_import(hash, context, migration)
12691

12792
missing_links = hash.delete(:missing_links) || {}
@@ -159,6 +124,7 @@ def self.import_from_migration(hash, context, migration=nil, bank=nil, options={
159124
end
160125

161126
def self.prep_for_import(hash, context, migration=nil)
127+
return hash if hash[:prepped_for_import]
162128
hash[:missing_links] = {}
163129
[:question_text, :correct_comments_html, :incorrect_comments_html, :neutral_comments_html, :more_comments_html].each do |field|
164130
hash[:missing_links][field] = []

app/models/importers/quiz_group_importer.rb

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,9 @@ def self.import_from_migration(hash, context, quiz, question_data, position = ni
4444
end
4545
item.save!
4646
hash[:questions].each_with_index do |question, i|
47-
if qq = question_data[:qq_data][question[:migration_id]]
48-
qq[:position] = i + 1
49-
if qq[:assessment_question_migration_id]
50-
if aq = question_data[:aq_data][qq[:assessment_question_migration_id]]
51-
qq['assessment_question_id'] = aq['assessment_question_id']
52-
aq_hash = Importers::AssessmentQuestionImporter.prep_for_import(qq, context, migration)
53-
Importers::QuizQuestionImporter.import_from_migration(aq_hash, context, migration, quiz, item)
54-
else
55-
aq_hash = Importers::AssessmentQuestionImporter.import_from_migration(qq, context, migration)
56-
qq['assessment_question_id'] = aq_hash['assessment_question_id']
57-
Importers::QuizQuestionImporter.import_from_migration(aq_hash, context, migration, quiz, item)
58-
end
59-
end
60-
elsif aq = question_data[:aq_data][question[:migration_id]]
61-
aq[:points_possible] = question[:points_possible] if question[:points_possible]
62-
aq[:position] = i + 1
63-
Importers::QuizQuestionImporter.import_from_migration(aq, context, migration, quiz, item)
47+
if aq = (question_data[:aq_data][question[:migration_id]] || question_data[:aq_data][question[:assessment_question_migration_id]])
48+
Importers::QuizQuestionImporter.import_from_migration(aq, question, i + 1,
49+
question_data[:qq_ids][quiz.migration_id], context, migration, quiz, item)
6450
end
6551
end
6652

0 commit comments

Comments
 (0)