mirror of
https://github.com/moodle/moodle.git
synced 2025-04-15 05:25:08 +02:00
MDL-73808 core_question: Add efficiency in question migration
This commit will add some efficiency around the question migration. Co-Authored-By: Safat Shahin <safatshahin@catalyst-au.net> Co-Authored-By: Andrew Lyons <andrew@nicols.co.uk>
This commit is contained in:
parent
9344149aba
commit
6045e6b67b
@ -3926,14 +3926,182 @@ privatefiles,moodle|/user/files.php';
|
||||
}
|
||||
|
||||
if ($oldversion < 2022020200.02) {
|
||||
// Next, split question records into the new tables.
|
||||
upgrade_migrate_question_table();
|
||||
// Define a new temporary field in the question_bank_entries tables.
|
||||
// Creating temporary field questionid to populate the data in question version table.
|
||||
// This will make sure the appropriate question id is inserted the version table without making any complex joins.
|
||||
$table = new xmldb_table('question_bank_entries');
|
||||
$field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_TYPE_INTEGER);
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
upgrade_set_timeout(3600);
|
||||
// Create the data for the question_bank_entries table with, including the new temporary field.
|
||||
$sql = <<<EOF
|
||||
INSERT INTO {question_bank_entries}
|
||||
(questionid, questioncategoryid, idnumber, ownerid)
|
||||
SELECT id, category, idnumber, createdby
|
||||
FROM {question} q
|
||||
EOF;
|
||||
|
||||
// Inserting question_bank_entries data.
|
||||
$DB->execute($sql);
|
||||
|
||||
$transaction->allow_commit();
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2022020200.02);
|
||||
}
|
||||
|
||||
// Finally, drop fields from question table.
|
||||
if ($oldversion < 2022020200.03) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
upgrade_set_timeout(3600);
|
||||
// Create the question_versions using that temporary field.
|
||||
$sql = <<<EOF
|
||||
INSERT INTO {question_versions}
|
||||
(questionbankentryid, questionid, status)
|
||||
SELECT
|
||||
qbe.id,
|
||||
q.id,
|
||||
CASE
|
||||
WHEN q.hidden > 0 THEN 'hidden'
|
||||
ELSE 'ready'
|
||||
END
|
||||
FROM {question_bank_entries} qbe
|
||||
INNER JOIN {question} q ON qbe.questionid = q.id
|
||||
EOF;
|
||||
|
||||
// Inserting question_versions data.
|
||||
$DB->execute($sql);
|
||||
|
||||
$transaction->allow_commit();
|
||||
|
||||
// Dropping temporary field questionid.
|
||||
$table = new xmldb_table('question_bank_entries');
|
||||
$field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_TYPE_INTEGER);
|
||||
if ($dbman->field_exists($table, $field)) {
|
||||
$dbman->drop_field($table, $field);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2022020200.03);
|
||||
}
|
||||
|
||||
if ($oldversion < 2022020200.04) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
upgrade_set_timeout(3600);
|
||||
// Create the base data for the random questions in the set_references table.
|
||||
// This covers most of the hard work in one go.
|
||||
$concat = $DB->sql_concat("'{\"questioncategoryid\":\"'", 'q.category', "'\",\"includingsubcategories\":\"'",
|
||||
'qs.includingsubcategories', "'\"}'");
|
||||
$sql = <<<EOF
|
||||
INSERT INTO {question_set_references}
|
||||
(usingcontextid, component, questionarea, itemid, questionscontextid, filtercondition)
|
||||
SELECT
|
||||
c.id,
|
||||
'mod_quiz',
|
||||
'slot',
|
||||
qs.id,
|
||||
qc.contextid,
|
||||
$concat
|
||||
FROM {question} q
|
||||
INNER JOIN {quiz_slots} qs on q.id = qs.questionid
|
||||
INNER JOIN {course_modules} cm ON cm.instance = qs.quizid AND cm.module = :quizmoduleid
|
||||
INNER JOIN {context} c ON cm.id = c.instanceid AND c.contextlevel = :contextmodule
|
||||
INNER JOIN {question_categories} qc ON qc.id = q.category
|
||||
WHERE q.qtype = :random
|
||||
EOF;
|
||||
|
||||
// Inserting question_set_references data.
|
||||
$DB->execute($sql, [
|
||||
'quizmoduleid' => $DB->get_field('modules', 'id', ['name' => 'quiz']),
|
||||
'contextmodule' => CONTEXT_MODULE,
|
||||
'random' => 'random',
|
||||
]);
|
||||
|
||||
$transaction->allow_commit();
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2022020200.04);
|
||||
}
|
||||
|
||||
if ($oldversion < 2022020200.05) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
upgrade_set_timeout(3600);
|
||||
|
||||
// Count all the slot tags to be migrated (for progress bar).
|
||||
$total = $DB->count_records('quiz_slot_tags');
|
||||
$pbar = new progress_bar('migratequestiontags', 1000, true);
|
||||
$i = 0;
|
||||
// Updating slot_tags for random question tags.
|
||||
// Now fetch any quiz slot tags and update those slot details into the question_set_references.
|
||||
$slottags = $DB->get_recordset('quiz_slot_tags', [], 'slotid ASC');
|
||||
|
||||
$tagstrings = [];
|
||||
$lastslot = null;
|
||||
$runinsert = function (int $lastslot, array $tagstrings) use ($DB) {
|
||||
$conditiondata = $DB->get_field('question_set_references', 'filtercondition',
|
||||
['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
|
||||
$condition = json_decode($conditiondata);
|
||||
$condition->tags = $tagstrings;
|
||||
$DB->set_field('question_set_references', 'filtercondition', json_encode($condition),
|
||||
['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']);
|
||||
};
|
||||
|
||||
foreach ($slottags as $tag) {
|
||||
upgrade_set_timeout(3600);
|
||||
if ($lastslot && $tag->slotid != $lastslot) {
|
||||
if (!empty($tagstrings)) {
|
||||
// Insert the data.
|
||||
$runinsert($lastslot, $tagstrings);
|
||||
}
|
||||
// Prepare for the next slot id.
|
||||
$tagstrings = [];
|
||||
}
|
||||
|
||||
$lastslot = $tag->slotid;
|
||||
$tagstrings[] = "{$tag->tagid},{$tag->tagname}";
|
||||
// Update progress.
|
||||
$i++;
|
||||
$pbar->update($i, $total, "Migrating question tags - $i/$total.");
|
||||
}
|
||||
if ($tagstrings) {
|
||||
$runinsert($lastslot, $tagstrings);
|
||||
}
|
||||
$slottags->close();
|
||||
|
||||
$transaction->allow_commit();
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2022020200.05);
|
||||
}
|
||||
|
||||
if ($oldversion < 2022020200.06) {
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
upgrade_set_timeout(3600);
|
||||
// Create question_references record for each question.
|
||||
// Except if qtype is random. That case is handled by question_set_reference.
|
||||
$sql = "INSERT INTO {question_references}
|
||||
(usingcontextid, component, questionarea, itemid, questionbankentryid)
|
||||
SELECT c.id, 'mod_quiz', 'slot', qs.id, qv.questionbankentryid
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON q.id = qv.questionid
|
||||
JOIN {quiz_slots} qs ON q.id = qs.questionid
|
||||
JOIN {modules} m ON m.name = 'quiz'
|
||||
JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = qs.quizid
|
||||
JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = " . CONTEXT_MODULE . "
|
||||
WHERE q.qtype <> 'random'";
|
||||
|
||||
// Inserting question_references data.
|
||||
$DB->execute($sql);
|
||||
|
||||
$transaction->allow_commit();
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2022020200.06);
|
||||
}
|
||||
|
||||
// Finally, drop fields from question table.
|
||||
if ($oldversion < 2022020200.07) {
|
||||
// Define fields to be dropped from questions.
|
||||
$table = new xmldb_table('question');
|
||||
|
||||
@ -3976,7 +4144,7 @@ privatefiles,moodle|/user/files.php';
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2022020200.03);
|
||||
upgrade_main_savepoint(true, 2022020200.07);
|
||||
}
|
||||
|
||||
if ($oldversion < 2022021100.01) {
|
||||
|
@ -1275,140 +1275,6 @@ function upgrade_calendar_override_events_fix(stdClass $info, bool $output = tru
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split question table in 2 new tables:
|
||||
*
|
||||
* question_bank_entries
|
||||
* question_versions
|
||||
*
|
||||
* Move the random questions records to the following table:
|
||||
* question_set_reference
|
||||
*
|
||||
* Move the question related records from quiz_slots table to:
|
||||
* question_reference
|
||||
*
|
||||
* Move the tag related data from quiz_slot_tags to:
|
||||
* question_references
|
||||
*
|
||||
* For more information: https://moodle.org/mod/forum/discuss.php?d=417599#p1688163
|
||||
*/
|
||||
function upgrade_migrate_question_table(): void {
|
||||
global $DB;
|
||||
|
||||
// Maximum size of array.
|
||||
$maxlength = 30000;
|
||||
|
||||
// Array of question_versions objects.
|
||||
$questionversions = [];
|
||||
|
||||
// Array of question_set_references objects.
|
||||
$questionsetreferences = [];
|
||||
|
||||
// The actual update/insert done with multiple DB access, so we do it in a transaction.
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
|
||||
// Count all questions to be migrated (for progress bar).
|
||||
$total = $DB->count_records('question');
|
||||
$pbar = new progress_bar('migratequestions', 1000, true);
|
||||
$i = 0;
|
||||
// Get all records in question table, we dont need the subquestions, just regular questions and random questions.
|
||||
$questions = $DB->get_recordset('question');
|
||||
foreach ($questions as $question) {
|
||||
upgrade_set_timeout(60);
|
||||
// Populate table question_bank_entries.
|
||||
$questionbankentry = new \stdClass();
|
||||
$questionbankentry->questioncategoryid = $question->category;
|
||||
$questionbankentry->idnumber = $question->idnumber;
|
||||
$questionbankentry->ownerid = $question->createdby;
|
||||
// Insert a question_bank_entries record here as the id is required to populate other tables.
|
||||
$questionbankentry->id = $DB->insert_record('question_bank_entries', $questionbankentry);
|
||||
|
||||
// Create question_versions records to be added.
|
||||
$questionversion = new \stdClass();
|
||||
$questionversion->questionbankentryid = $questionbankentry->id;
|
||||
$questionversion->questionid = $question->id;
|
||||
$questionstatus = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
|
||||
if ((int)$question->hidden === 1) {
|
||||
$questionstatus = \core_question\local\bank\question_version_status::QUESTION_STATUS_HIDDEN;
|
||||
}
|
||||
$questionversion->status = $questionstatus;
|
||||
$questionversions[] = $questionversion;
|
||||
|
||||
// Insert the records if the array limit is reached.
|
||||
if (count($questionversions) >= $maxlength) {
|
||||
$DB->insert_records('question_versions', $questionversions);
|
||||
$questionversions = [];
|
||||
}
|
||||
|
||||
// Create question_set_references records to be added.
|
||||
// Only if the question type is random and the question is used in a quiz.
|
||||
if ($question->qtype === 'random') {
|
||||
$quizslots = $DB->get_records('quiz_slots', ['questionid' => $question->id]);
|
||||
foreach ($quizslots as $quizslot) {
|
||||
$questionsetreference = new \stdClass();
|
||||
$cm = get_coursemodule_from_instance('quiz', $quizslot->quizid);
|
||||
$questionsetreference->usingcontextid = context_module::instance($cm->id)->id;
|
||||
$questionsetreference->component = 'mod_quiz';
|
||||
$questionsetreference->questionarea = 'slot';
|
||||
$questionsetreference->itemid = $quizslot->id;
|
||||
$catcontext = $DB->get_field('question_categories', 'contextid', ['id' => $question->category]);
|
||||
$questionsetreference->questionscontextid = $catcontext;
|
||||
// Migration of the slot tags and filter identifiers from slot table to filtercondition.
|
||||
$filtercondition = new stdClass();
|
||||
$filtercondition->questioncategoryid = $question->category;
|
||||
$filtercondition->includingsubcategories = $quizslot->includingsubcategories;
|
||||
$tags = $DB->get_records('quiz_slot_tags', ['slotid' => $quizslot->id]);
|
||||
$tagstrings = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tagstrings [] = "{$tag->id},{$tag->name}";
|
||||
}
|
||||
if (!empty($tagstrings)) {
|
||||
$filtercondition->tags = $tagstrings;
|
||||
}
|
||||
$questionsetreference->filtercondition = json_encode($filtercondition);
|
||||
|
||||
$questionsetreferences[] = $questionsetreference;
|
||||
|
||||
// Insert the records if the array limit is reached.
|
||||
if (count($questionsetreferences) >= $maxlength) {
|
||||
$DB->insert_records('question_set_references', $questionsetreferences);
|
||||
$questionsetreferences = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update progress.
|
||||
$i++;
|
||||
$pbar->update($i, $total, "Migrating questions - $i/$total.");
|
||||
}
|
||||
$questions->close();
|
||||
|
||||
// Insert the remaining question_versions records.
|
||||
if ($questionversions) {
|
||||
$DB->insert_records('question_versions', $questionversions);
|
||||
}
|
||||
|
||||
// Insert the remaining question_set_references records.
|
||||
if ($questionsetreferences) {
|
||||
$DB->insert_records('question_set_references', $questionsetreferences);
|
||||
}
|
||||
|
||||
// Create question_references record for each question.
|
||||
// Except if qtype is random. That case is handled by question_set_reference.
|
||||
$sql = "INSERT INTO {question_references}
|
||||
(usingcontextid, component, questionarea, itemid, questionbankentryid)
|
||||
SELECT c.id, 'mod_quiz', 'slot', qs.id, qv.questionbankentryid
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON q.id = qv.questionid
|
||||
JOIN {quiz_slots} qs ON q.id = qs.questionid
|
||||
JOIN {modules} m ON m.name = 'quiz'
|
||||
JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = qs.quizid
|
||||
JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = " . CONTEXT_MODULE . "
|
||||
WHERE q.qtype <> 'random'";
|
||||
$DB->execute($sql);
|
||||
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new item at the end of the usermenu.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user