MDL-20636 Finished backup and restore of attempt data. Yay
This commit is contained in:
@@ -205,9 +205,11 @@ abstract class backup_qtype_plugin extends backup_plugin {
|
||||
/**
|
||||
* Returns one array with filearea => mappingname elements for the qtype
|
||||
*
|
||||
* Used by {@link get_components_and_fileareas} to know about all the qtype- * files to be processed both in backup and restore.
|
||||
* Used by {@link get_components_and_fileareas} to know about all the qtype
|
||||
* files to be processed both in backup and restore.
|
||||
*/
|
||||
public static function get_qtype_fileareas() {
|
||||
// By default, return empty array, only qtypes having own fileareas wil- return array();
|
||||
// By default, return empty array, only qtypes having own fileareas will override this
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ abstract class backup_questions_activity_structure_step extends backup_activity_
|
||||
}
|
||||
|
||||
$quba = new backup_nested_element('question_usage', array('id'),
|
||||
array('preferredbehaviour'));
|
||||
array('component', 'preferredbehaviour'));
|
||||
|
||||
$qas = new backup_nested_element('question_attempts');
|
||||
$qa = new backup_nested_element('question_attempt', array('id'), array(
|
||||
@@ -201,8 +201,8 @@ abstract class backup_questions_activity_structure_step extends backup_activity_
|
||||
$step = new backup_nested_element('step', array('id'), array(
|
||||
'sequencenumber', 'state', 'fraction', 'timecreated', 'userid'));
|
||||
|
||||
$data = new backup_nested_element('data');
|
||||
$value = new backup_nested_element('value', array('name', 'value'));
|
||||
$response = new backup_nested_element('response');
|
||||
$variable = new backup_nested_element('variable', null, array('name', 'value'));
|
||||
|
||||
// Build the tree
|
||||
$element->add_child($quba);
|
||||
@@ -210,24 +210,33 @@ abstract class backup_questions_activity_structure_step extends backup_activity_
|
||||
$qas->add_child($qa);
|
||||
$qa->add_child($steps);
|
||||
$steps->add_child($step);
|
||||
$step->add_child($data);
|
||||
$data->add_child($value);
|
||||
$step->add_child($response);
|
||||
$response->add_child($variable);
|
||||
|
||||
// Set the sources
|
||||
$quba->set_source_table('question_usages',
|
||||
array('id' => '../' . $usageidname));
|
||||
$qa->set_source_table('question_attempts',
|
||||
$qa->set_source_sql('
|
||||
SELECT *
|
||||
FROM {question_attempts}
|
||||
WHERE questionusageid = :questionusageid
|
||||
ORDER BY slot',
|
||||
array('questionusageid' => backup::VAR_PARENTID));
|
||||
$step->set_source_table('question_attempt_steps',
|
||||
$step->set_source_sql('
|
||||
SELECT *
|
||||
FROM {question_attempt_steps}
|
||||
WHERE questionattemptid = :questionattemptid
|
||||
ORDER BY sequencenumber',
|
||||
array('questionattemptid' => backup::VAR_PARENTID));
|
||||
$value->set_source_table('question_attempt_step_data',
|
||||
$variable->set_source_table('question_attempt_step_data',
|
||||
array('attemptstepid' => backup::VAR_PARENTID));
|
||||
|
||||
// Annotate ids
|
||||
$qa->annotate_ids('question', 'questionid');
|
||||
$step->annotate_ids('user', 'userid');
|
||||
|
||||
// Annotate files
|
||||
$fileareas = question_engine_data_mapper::get_all_response_file_areas();
|
||||
$fileareas = question_engine::get_all_response_file_areas();
|
||||
foreach ($fileareas as $filearea) {
|
||||
$step->annotate_files('question', $filearea, 'id');
|
||||
}
|
||||
@@ -1677,7 +1686,12 @@ class backup_questions_structure_step extends backup_structure_step {
|
||||
|
||||
$question->set_source_table('question', array('category' => backup::VAR_PARENTID));
|
||||
|
||||
$qhint->set_source_table('question_hints', array('questionid' => backup::VAR_PARENTID));
|
||||
$qhint->set_source_sql('
|
||||
SELECT *
|
||||
FROM {question_hints}
|
||||
WHERE questionid = :questionid
|
||||
ORDER BY id',
|
||||
array('questionid' => backup::VAR_PARENTID));
|
||||
|
||||
// don't need to annotate ids nor files
|
||||
// (already done by {@link backup_annotate_all_question_files}
|
||||
|
||||
@@ -315,11 +315,14 @@ abstract class restore_qtype_plugin extends restore_plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode one question_states for this qtype (default impl)
|
||||
* Do any re-coding necessary in the student response.
|
||||
* @param int $questionid the new id of the question
|
||||
* @param int $sequencenumber of the step within the qusetion attempt.
|
||||
* @param array the response data from the backup.
|
||||
* @return array the recoded response.
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
// By default, return answer unmodified, qtypes needing recode will override this
|
||||
return $state->answer;
|
||||
public function recode_response($questionid, $sequencenumber, array $response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2522,6 +2522,10 @@ class restore_create_question_files extends restore_execution_step {
|
||||
* (like the quiz module), to support qtype plugins, states and sessions
|
||||
*/
|
||||
abstract class restore_questions_activity_structure_step extends restore_activity_structure_step {
|
||||
/** @var array question_attempt->id to qtype. */
|
||||
protected $qtypes = array();
|
||||
/** @var array question_attempt->id to questionid. */
|
||||
protected $newquestionids = array();
|
||||
|
||||
/**
|
||||
* Attach below $element (usually attempts) the needed restore_path_elements
|
||||
@@ -2541,9 +2545,14 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
$paths[] = new restore_path_element('question_attempt',
|
||||
$element->get_path() . '/question_usage/question_attempts/question_attempt');
|
||||
$paths[] = new restore_path_element('question_attempt_step',
|
||||
$element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step');
|
||||
$element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step',
|
||||
true);
|
||||
$paths[] = new restore_path_element('question_attempt_step_data',
|
||||
$element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step/data/value');
|
||||
$element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step/response/variable');
|
||||
|
||||
// TODO Put back code for restoring legacy 2.0 backups.
|
||||
// $paths[] = new restore_path_element('question_state', $element->get_path() . '/states/state');
|
||||
// $paths[] = new restore_path_element('question_session', $element->get_path() . '/sessions/session');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2551,46 +2560,52 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
*/
|
||||
protected function process_question_usage($data) {
|
||||
global $DB;
|
||||
// TODO
|
||||
|
||||
// Clear our caches.
|
||||
$this->qtypes = array();
|
||||
$this->newquestionids = array();
|
||||
|
||||
$data = (object)$data;
|
||||
$oldid = $data->id;
|
||||
|
||||
// Get complete question mapping, we'll need info
|
||||
$question = $this->get_mapping('question', $data->question);
|
||||
$oldcontextid = $this->get_task()->get_old_contextid();
|
||||
$data->contextid = $this->get_mappingid('context', $this->task->get_old_contextid());
|
||||
|
||||
// In the quiz_attempt mapping we are storing uniqueid
|
||||
// and not id, so this gets the correct question_attempt to point to
|
||||
$data->attempt = $this->get_new_parentid('quiz_attempt');
|
||||
$data->question = $question->newitemid;
|
||||
$data->answer = $this->restore_recode_answer($data, $question->info->qtype); // Delegate recoding of answer
|
||||
$data->timestamp= $this->apply_date_offset($data->timestamp);
|
||||
// Everything ready, insert (no mapping needed)
|
||||
$newitemid = $DB->insert_record('question_usages', $data);
|
||||
|
||||
// Everything ready, insert and create mapping (needed by question_sessions)
|
||||
$newitemid = $DB->insert_record('question_states', $data);
|
||||
$this->set_mapping('question_state', $oldid, $newitemid);
|
||||
$this->inform_new_usage_id($newitemid);
|
||||
|
||||
$this->set_mapping('question_usage', $oldid, $newitemid, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* When process_question_usage creates the new usage, it calls this method
|
||||
* to let the activity link to the new usage. For example, the quiz uses
|
||||
* this method to set quiz_attempts.uniqueid to the new usage id.
|
||||
* @param integer $newusageid
|
||||
*/
|
||||
abstract protected function inform_new_usage_id($newusageid);
|
||||
|
||||
/**
|
||||
* Process question_attempts
|
||||
*/
|
||||
protected function process_question_attempt($data) {
|
||||
global $DB;
|
||||
// TODO
|
||||
|
||||
$data = (object)$data;
|
||||
$oldid = $data->id;
|
||||
$question = $this->get_mapping('question', $data->questionid);
|
||||
|
||||
// In the quiz_attempt mapping we are storing uniqueid
|
||||
// and not id, so this gets the correct question_attempt to point to
|
||||
$data->attemptid = $this->get_new_parentid('quiz_attempt');
|
||||
$data->questionid = $this->get_mappingid('question', $data->questionid);
|
||||
$data->newest = $this->get_mappingid('question_state', $data->newest);
|
||||
$data->newgraded = $this->get_mappingid('question_state', $data->newgraded);
|
||||
$data->questionusageid = $this->get_new_parentid('question_usage');
|
||||
$data->questionid = $question->newitemid;
|
||||
$data->timemodified = $this->apply_date_offset($data->timemodified);
|
||||
|
||||
// Everything ready, insert (no mapping needed)
|
||||
$newitemid = $DB->insert_record('question_sessions', $data);
|
||||
$newitemid = $DB->insert_record('question_attempts', $data);
|
||||
|
||||
// Note: question_sessions haven't files associated. On purpose manualcomment is lacking
|
||||
// support for them, so we don't need to handle them here.
|
||||
$this->set_mapping('question_attempt', $oldid, $newitemid);
|
||||
$this->qtypes[$newitemid] = $question->info->qtype;
|
||||
$this->newquestionids[$newitemid] = $data->questionid;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2598,15 +2613,41 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
*/
|
||||
protected function process_question_attempt_step($data) {
|
||||
global $DB;
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Process question_attempt_step_data
|
||||
*/
|
||||
protected function process_question_attempt_step_data($data) {
|
||||
global $DB;
|
||||
// TODO
|
||||
$data = (object)$data;
|
||||
$oldid = $data->id;
|
||||
|
||||
// Pull out the response data.
|
||||
$response = array();
|
||||
if (!empty($data->response['variable'])) {
|
||||
foreach ($data->response['variable'] as $variable) {
|
||||
$response[$variable['name']] = $variable['value'];
|
||||
}
|
||||
}
|
||||
unset($data->response);
|
||||
|
||||
$data->questionattemptid = $this->get_new_parentid('question_attempt');
|
||||
$data->timecreated = $this->apply_date_offset($data->timecreated);
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
|
||||
// Everything ready, insert and create mapping (needed by question_sessions)
|
||||
$newitemid = $DB->insert_record('question_attempt_steps', $data);
|
||||
$this->set_mapping('question_attempt_step', $oldid, $newitemid, true);
|
||||
|
||||
// Now process the response data.
|
||||
$qtyperestorer = $this->get_qtype_restorer($this->qtypes[$data->questionattemptid]);
|
||||
if ($qtyperestorer) {
|
||||
$response = $qtyperestorer->recode_response(
|
||||
$this->newquestionids[$data->questionattemptid],
|
||||
$data->sequencenumber, $response);
|
||||
}
|
||||
foreach ($response as $name => $value) {
|
||||
$row = new stdClass();
|
||||
$row->attemptstepid = $newitemid;
|
||||
$row->name = $name;
|
||||
$row->value = $value;
|
||||
$DB->insert_record('question_attempt_step_data', $row, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2627,4 +2668,35 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
}
|
||||
return implode(',', $questionids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the restore_qtype_plugin subclass for a specific question type.
|
||||
* @param string $qtype e.g. multichoice.
|
||||
* @return restore_qtype_plugin instance.
|
||||
*/
|
||||
public function get_qtype_restorer($qtype) {
|
||||
// Build one static cache to store {@link restore_qtype_plugin}
|
||||
// while we are needing them, just to save zillions of instantiations
|
||||
// or using static stuff that will break our nice API
|
||||
static $qtypeplugins = array();
|
||||
|
||||
if (!isset($qtypeplugins[$qtype])) {
|
||||
$classname = 'restore_qtype_' . $qtype . '_plugin';
|
||||
if (class_exists($classname)) {
|
||||
$qtypeplugins[$qtype] = new $classname('qtype', $qtype, $this);
|
||||
} else {
|
||||
$qtypeplugins[$qtype] = null;
|
||||
}
|
||||
}
|
||||
return $qtypeplugins[$qtype];
|
||||
}
|
||||
|
||||
protected function after_execute() {
|
||||
parent::after_execute();
|
||||
|
||||
// Restore any files belonging to responses.
|
||||
foreach (question_engine::get_all_response_file_areas() as $filearea) {
|
||||
$this->add_related_files('question', $filearea, 'question_attempt_step');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,11 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
|
||||
// All the rest of elements only happen if we are including user info
|
||||
if ($userinfo) {
|
||||
$grade->set_source_table('quiz_grades', array('quiz' => backup::VAR_PARENTID));
|
||||
$attempt->set_source_table('quiz_attempts', array('quiz' => backup::VAR_PARENTID));
|
||||
$attempt->set_source_sql('
|
||||
SELECT *
|
||||
FROM {quiz_attempts}
|
||||
WHERE quiz = :quiz AND preview = 0',
|
||||
array('quiz' => backup::VAR_PARENTID));
|
||||
}
|
||||
|
||||
// Define source alias
|
||||
|
||||
@@ -265,7 +265,7 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
|
||||
$data->quiz = $this->get_new_parentid('quiz');
|
||||
$data->attempt = $data->attemptnum;
|
||||
|
||||
$data->uniqueid = question_new_attempt_uniqueid('quiz');
|
||||
$data->uniqueid = 0; // filled in later by {@link inform_new_usage_id()}
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
|
||||
@@ -275,14 +275,18 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
|
||||
|
||||
$newitemid = $DB->insert_record('quiz_attempts', $data);
|
||||
|
||||
// Save quiz_attempt->uniqueid as quiz_attempt mapping, both question_states and
|
||||
// question_sessions have Fk to it and not to quiz_attempts->id at all.
|
||||
$this->set_mapping('quiz_attempt', $olduniqueid, $data->uniqueid, false);
|
||||
// Also save quiz_attempt->id mapping, because logs use it
|
||||
$this->set_mapping('quiz_attempt_id', $oldid, $newitemid, false);
|
||||
// Save quiz_attempt->id mapping, because logs use it
|
||||
$this->set_mapping('quiz_attempt', $oldid, $newitemid, false);
|
||||
}
|
||||
|
||||
protected function inform_new_usage_id($newusageid) {
|
||||
global $DB;
|
||||
$DB->set_field('quiz_attempts', 'uniqueid', $newusageid, array('id' =>
|
||||
$this->get_new_parentid('quiz_attempt')));
|
||||
}
|
||||
|
||||
protected function after_execute() {
|
||||
parent::after_execute();
|
||||
// Add quiz related files, no need to match by itemname (just internally handled context)
|
||||
$this->add_related_files('mod_quiz', 'intro', null);
|
||||
// Add feedback related files, matching by itemname = 'quiz_feedback'
|
||||
|
||||
@@ -701,7 +701,7 @@ ORDER BY
|
||||
*/
|
||||
protected function delete_response_files($contextid, $itemidstest, $params) {
|
||||
$fs = get_file_storage();
|
||||
foreach ($this->get_all_response_file_areas() as $filearea) {
|
||||
foreach (question_engine::get_all_response_file_areas() as $filearea) {
|
||||
$fs->delete_area_files_select($contextid, 'question', $filearea,
|
||||
$itemidstest, $params);
|
||||
}
|
||||
@@ -853,22 +853,6 @@ ORDER BY
|
||||
'questionid ' . $test . ' AND questionusageid ' .
|
||||
$qubaids->usage_id_in(), $params + $qubaids->usage_id_in_params());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array all the file area names that may contain response files.
|
||||
*/
|
||||
public static function get_all_response_file_areas() {
|
||||
$variables = array();
|
||||
foreach (question_bank::get_all_qtypes() as $qtype) {
|
||||
$variables += $qtype->response_file_areas();
|
||||
}
|
||||
|
||||
$areas = array();
|
||||
foreach (array_unique($variables) as $variable) {
|
||||
$areas[] = 'response_' . $variable;
|
||||
}
|
||||
return $areas;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -315,6 +315,22 @@ abstract class question_engine {
|
||||
return get_string('pluginname', 'qbehaviour_' . $behaviour);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array all the file area names that may contain response files.
|
||||
*/
|
||||
public static function get_all_response_file_areas() {
|
||||
$variables = array();
|
||||
foreach (question_bank::get_all_qtypes() as $qtype) {
|
||||
$variables += $qtype->response_file_areas();
|
||||
}
|
||||
|
||||
$areas = array();
|
||||
foreach (array_unique($variables) as $variable) {
|
||||
$areas[] = 'response_' . $variable;
|
||||
}
|
||||
return $areas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid choices for the number of decimal places for showing
|
||||
* question marks. For use in the user interface.
|
||||
|
||||
@@ -78,46 +78,6 @@ class restore_qtype_ddwtos_plugin extends restore_qtype_plugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for ddwtos questions
|
||||
*
|
||||
* answer are two (hypen speparated) lists of comma separated question_answers
|
||||
* the first to specify the order of the answers and the second to specify the
|
||||
* responses. Note the order list (the first one) can be optional
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
$answer = $state->answer;
|
||||
$orderarr = array();
|
||||
$responsesarr = array();
|
||||
$lists = explode(':', $answer);
|
||||
// if only 1 list, answer is missing the order list, adjust
|
||||
if (count($lists) == 1) {
|
||||
$lists[1] = $lists[0]; // here we have the responses
|
||||
$lists[0] = ''; // here we have the order
|
||||
}
|
||||
// Map order
|
||||
foreach (explode(',', $lists[0]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$orderarr[] = $newid;
|
||||
}
|
||||
}
|
||||
// Map responses
|
||||
foreach (explode(',', $lists[1]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$responsesarr[] = $newid;
|
||||
}
|
||||
}
|
||||
// Build the final answer, if not order, only responses
|
||||
$result = '';
|
||||
if (empty($orderarr)) {
|
||||
$result = implode(',', $responsesarr);
|
||||
} else {
|
||||
$result = implode(',', $orderarr) . ':' . implode(',', $responsesarr);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of this qtype to be processed by the links decoder
|
||||
*/
|
||||
|
||||
@@ -79,46 +79,6 @@ class restore_qtype_gapselect_plugin extends restore_qtype_plugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for gapselect questions
|
||||
*
|
||||
* answer are two (hypen speparated) lists of comma separated question_answers
|
||||
* the first to specify the order of the answers and the second to specify the
|
||||
* responses. Note the order list (the first one) can be optional
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
$answer = $state->answer;
|
||||
$orderarr = array();
|
||||
$responsesarr = array();
|
||||
$lists = explode(':', $answer);
|
||||
// if only 1 list, answer is missing the order list, adjust
|
||||
if (count($lists) == 1) {
|
||||
$lists[1] = $lists[0]; // here we have the responses
|
||||
$lists[0] = ''; // here we have the order
|
||||
}
|
||||
// Map order
|
||||
foreach (explode(',', $lists[0]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$orderarr[] = $newid;
|
||||
}
|
||||
}
|
||||
// Map responses
|
||||
foreach (explode(',', $lists[1]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$responsesarr[] = $newid;
|
||||
}
|
||||
}
|
||||
// Build the final answer, if not order, only responses
|
||||
$result = '';
|
||||
if (empty($orderarr)) {
|
||||
$result = implode(',', $responsesarr);
|
||||
} else {
|
||||
$result = implode(',', $orderarr) . ':' . implode(',', $responsesarr);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of this qtype to be processed by the links decoder
|
||||
*/
|
||||
|
||||
@@ -162,24 +162,29 @@ class restore_qtype_match_plugin extends restore_qtype_plugin {
|
||||
$rs->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for match questions
|
||||
*
|
||||
* answer is one comma separated list of hypen separated pairs
|
||||
* containing question_match_sub->id and question_match_sub->code
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
$answer = $state->answer;
|
||||
$resultarr = array();
|
||||
foreach (explode(',', $answer) as $pair) {
|
||||
$pairarr = explode('-', $pair);
|
||||
$id = $pairarr[0];
|
||||
$code = $pairarr[1];
|
||||
$newid = $this->get_mappingid('question_match_sub', $id);
|
||||
$resultarr[] = implode('-', array($newid, $code));
|
||||
public function recode_response($questionid, $sequencenumber, array $response) {
|
||||
if (array_key_exists('_stemorder', $response)) {
|
||||
$response['_stemorder'] = $this->recode_match_sub_order($response['_stemorder']);
|
||||
}
|
||||
return implode(',', $resultarr);
|
||||
if (array_key_exists('_choiceorder', $response)) {
|
||||
$response['_choiceorder'] = $this->recode_match_sub_order($response['_choiceorder']);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recode the choice order as stored in the response.
|
||||
* @param string $order the original order.
|
||||
* @return string the recoded order.
|
||||
*/
|
||||
protected function recode_match_sub_order($order) {
|
||||
$neworder = array();
|
||||
foreach (explode(',', $order) as $id) {
|
||||
if ($newid = $this->get_mappingid('question_match_sub', $id)) {
|
||||
$neworder[] = $newid;
|
||||
}
|
||||
}
|
||||
return implode(',', $neworder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-34
@@ -86,44 +86,26 @@ class restore_qtype_multichoice_plugin extends restore_qtype_plugin {
|
||||
}
|
||||
}
|
||||
|
||||
public function recode_response($questionid, $sequencenumber, array $response) {
|
||||
if (array_key_exists('_order', $response)) {
|
||||
$response['_order'] = $this->recode_choice_order($response['_order']);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for multichoice questions
|
||||
*
|
||||
* answer are two (hypen speparated) lists of comma separated question_answers
|
||||
* the first to specify the order of the answers and the second to specify the
|
||||
* responses. Note the order list (the first one) can be optional
|
||||
* Recode the choice order as stored in the response.
|
||||
* @param string $order the original order.
|
||||
* @return string the recoded order.
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
$answer = $state->answer;
|
||||
$orderarr = array();
|
||||
$responsesarr = array();
|
||||
$lists = explode(':', $answer);
|
||||
// if only 1 list, answer is missing the order list, adjust
|
||||
if (count($lists) == 1) {
|
||||
$lists[1] = $lists[0]; // here we have the responses
|
||||
$lists[0] = ''; // here we have the order
|
||||
}
|
||||
// Map order
|
||||
foreach (explode(',', $lists[0]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$orderarr[] = $newid;
|
||||
protected function recode_choice_order($order) {
|
||||
$neworder = array();
|
||||
foreach (explode(',', $order) as $id) {
|
||||
if ($newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$neworder[] = $newid;
|
||||
}
|
||||
}
|
||||
// Map responses
|
||||
foreach (explode(',', $lists[1]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$responsesarr[] = $newid;
|
||||
}
|
||||
}
|
||||
// Build the final answer, if not order, only responses
|
||||
$result = '';
|
||||
if (empty($orderarr)) {
|
||||
$result = implode(',', $responsesarr);
|
||||
} else {
|
||||
$result = implode(',', $orderarr) . ':' . implode(',', $responsesarr);
|
||||
}
|
||||
return $result;
|
||||
return implode(',', $neworder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-34
@@ -80,44 +80,26 @@ class restore_qtype_oumultiresponse_plugin extends restore_qtype_plugin {
|
||||
}
|
||||
}
|
||||
|
||||
public function recode_response($questionid, $sequencenumber, array $response) {
|
||||
if (array_key_exists('_order', $response)) {
|
||||
$response['_order'] = $this->recode_choice_order($response['_order']);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for oumultiresponse questions
|
||||
*
|
||||
* answer are two (hypen speparated) lists of comma separated question_answers
|
||||
* the first to specify the order of the answers and the second to specify the
|
||||
* responses. Note the order list (the first one) can be optional
|
||||
* Recode the choice order as stored in the response.
|
||||
* @param string $order the original order.
|
||||
* @return string the recoded order.
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
$answer = $state->answer;
|
||||
$orderarr = array();
|
||||
$responsesarr = array();
|
||||
$lists = explode(':', $answer);
|
||||
// if only 1 list, answer is missing the order list, adjust
|
||||
if (count($lists) == 1) {
|
||||
$lists[1] = $lists[0]; // here we have the responses
|
||||
$lists[0] = ''; // here we have the order
|
||||
}
|
||||
// Map order
|
||||
foreach (explode(',', $lists[0]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$orderarr[] = $newid;
|
||||
protected function recode_choice_order($order) {
|
||||
$neworder = array();
|
||||
foreach (explode(',', $order) as $id) {
|
||||
if ($newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$neworder[] = $newid;
|
||||
}
|
||||
}
|
||||
// Map responses
|
||||
foreach (explode(',', $lists[1]) as $id) {
|
||||
if (!empty($id) && $newid = $this->get_mappingid('question_answer', $id)) {
|
||||
$responsesarr[] = $newid;
|
||||
}
|
||||
}
|
||||
// Build the final answer, if not order, only responses
|
||||
$result = '';
|
||||
if (empty($orderarr)) {
|
||||
$result = implode(',', $responsesarr);
|
||||
} else {
|
||||
$result = implode(',', $orderarr) . ':' . implode(',', $responsesarr);
|
||||
}
|
||||
return $result;
|
||||
return implode(',', $neworder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,43 +34,4 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class restore_qtype_random_plugin extends restore_qtype_plugin {
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for random questions
|
||||
*
|
||||
* answer format is randomxx-yy, with xx being question->id and
|
||||
* yy the actual response to the question. We'll delegate the recode
|
||||
* to the corresponding qtype
|
||||
*
|
||||
* also, some old states can contain, simply, one question->id,
|
||||
* support them, just in case
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
global $DB;
|
||||
|
||||
$answer = $state->answer;
|
||||
$result = '';
|
||||
|
||||
if (preg_match('~^random([0-9]+)-~', $answer, $matches)) {
|
||||
// randomxx-yy answer format
|
||||
$questionid = $matches[1];
|
||||
$subanswer = substr($answer, strlen('random' . $questionid . '-'));
|
||||
$newquestionid = $this->get_mappingid('question', $questionid);
|
||||
$questionqtype = $DB->get_field('question', 'qtype', array('id' => $newquestionid));
|
||||
|
||||
// Delegate subanswer recode to proper qtype, faking one question_states record
|
||||
$substate = new stdClass();
|
||||
$substate->question = $newquestionid;
|
||||
$substate->answer = $subanswer;
|
||||
$newanswer = $this->step->restore_recode_answer($substate, $questionqtype);
|
||||
$result = 'random' . $newquestionid . '-' . $newanswer;
|
||||
|
||||
} else {
|
||||
// simple question id format
|
||||
$newquestionid = $this->get_mappingid('question', $answer);
|
||||
$result = $newquestionid;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,19 +79,4 @@ class restore_qtype_truefalse_plugin extends restore_qtype_plugin {
|
||||
$this->set_mapping('question_truefalse', $oldid, $newitemid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one question_states record, return the answer
|
||||
* recoded pointing to all the restored stuff for truefalse questions
|
||||
*
|
||||
* if not empty, answer is one question_answers->id
|
||||
*/
|
||||
public function recode_state_answer($state) {
|
||||
$answer = $state->answer;
|
||||
$result = '';
|
||||
if ($answer) {
|
||||
$result = $this->get_mappingid('question_answer', $answer);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user