From bea1a6a73a0db4113acc7f957a5cc12e0bf80a25 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 28 Apr 2011 21:21:43 +0100 Subject: [PATCH] MDL-20636 Work-in-progress on quiz attempt backup and restore. --- backup/moodle2/backup_stepslib.php | 87 ++++++++----------- backup/moodle2/restore_stepslib.php | 80 +++++++---------- .../backup/moodle2/backup_quiz_stepslib.php | 3 +- .../backup/moodle2/restore_quiz_stepslib.php | 5 +- 4 files changed, 71 insertions(+), 104 deletions(-) diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php index 97cf2f7a70b..d7eace54607 100644 --- a/backup/moodle2/backup_stepslib.php +++ b/backup/moodle2/backup_stepslib.php @@ -174,78 +174,65 @@ abstract class backup_questions_activity_structure_step extends backup_activity_ /** * Attach to $element (usually attempts) the needed backup structures - * for question_states for a given question_attempt + * for question_usages and all the associated data. */ - protected function add_question_attempts_states($element, $questionattemptname) { + protected function add_question_usages($element, $usageidname) { // Check $element is one nested_backup_element if (! $element instanceof backup_nested_element) { throw new backup_step_exception('question_states_bad_parent_element', $element); } - // Check that the $questionattemptname is final element in $element if (! $element->get_final_element($questionattemptname)) { throw new backup_step_exception('question_states_bad_question_attempt_element', $questionattemptname); } - // TODO: Some day we should stop these "encrypted" state->answers and - // TODO: delegate to qtypes plugin to proper XML writting the needed info on each question + $quba = new backup_nested_element('question_usage', array('id'), + array('preferredbehaviour')); - // TODO: Should be doing here some introspection in the "answer" element, based on qtype, - // TODO: to know which real questions are being used (for randoms and other qtypes...) - // TODO: Not needed if consistency is guaranteed, but it isn't right now :-( + $qas = new backup_nested_element('question_attempts'); + $qa = new backup_nested_element('question_attempt', array('id'), array( + 'slot', 'behaviour', 'questionid', 'maxmark', 'minfraction', + 'flagged', 'questionsummary', 'rightanswer', 'responsesummary', + 'timemodified')); - // Define the elements - $states = new backup_nested_element('states'); - $state = new backup_nested_element('state', array('id'), array( - 'question', 'seq_number', 'answer', 'timestamp', - 'event', 'grade', 'raw_grade', 'penalty')); + $steps = new backup_nested_element('steps'); + $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'), + array('value')); // Build the tree - $element->add_child($states); - $states->add_child($state); + $element->add_child($quba); + $quba->add_child($qas); + $qas->add_child($qa); + $qa->add_child($steps); + $steps->add_child($step); + $step->add_child($data); + $data->add_child($value); // Set the sources - $state->set_source_table('question_states', array('attempt' => '../../' . $questionattemptname)); + $quba->set_source_table('question_usages', + array('id' => '../../' . $usageidname)); + $qa->set_source_table('question_attempts', + array('questionusageid' => backup::VAR_PARENTID)); + $step->set_source_table('question_attempt_steps', + array('questionattemptid' => backup::VAR_PARENTID)); + $value->set_source_table('question_attempt_step_data', + array('attemptstepid' => backup::VAR_PARENTID)); // Annotate ids - $state->annotate_ids('question', 'question'); - } - - /** - * Attach to $element (usually attempts) the needed backup structures - * for question_sessions for a given question_attempt - */ - protected function add_question_attempts_sessions($element, $questionattemptname) { - // Check $element is one nested_backup_element - if (! $element instanceof backup_nested_element) { - throw new backup_step_exception('question_sessions_bad_parent_element', $element); - } - // Check that the $questionattemptname is final element in $element - if (! $element->get_final_element($questionattemptname)) { - throw new backup_step_exception('question_sessions_bad_question_attempt_element', $questionattemptname); - } - - // Define the elements - $sessions = new backup_nested_element('sessions'); - $session = new backup_nested_element('session', array('id'), array( - 'questionid', 'newest', 'newgraded', 'sumpenalty', - 'manualcomment', 'manualcommentformat', 'flagged')); - - // Build the tree - $element->add_child($sessions); - $sessions->add_child($session); - - // Set the sources - $session->set_source_table('question_sessions', array('attemptid' => '../../' . $questionattemptname)); - - // Annotate ids - $session->annotate_ids('question', 'questionid'); + $qa->annotate_ids('question', 'questionid'); // Annotate files - // Note: question_sessions haven't files associated. On purpose manualcomment is lacking - // support for them, so we don't need to annotated them here. + $fileareas = question_engine_data_mapper::get_all_response_file_areas(); + foreach ($fileareas as $filearea) { + $step->annotate_files('question', $filearea, 'id'); + } } } + /** * backup structure step in charge of calculating the categories to be * included in backup, based in the context being backuped (module/course) diff --git a/backup/moodle2/restore_stepslib.php b/backup/moodle2/restore_stepslib.php index d6d7a488a29..dfff7295592 100644 --- a/backup/moodle2/restore_stepslib.php +++ b/backup/moodle2/restore_stepslib.php @@ -2524,9 +2524,9 @@ abstract class restore_questions_activity_structure_step extends restore_activit /** * Attach below $element (usually attempts) the needed restore_path_elements - * to restore question_states + * to restore question_usages and all they contain. */ - protected function add_question_attempts_states($element, &$paths) { + protected function add_question_usages($element, &$paths) { // Check $element is restore_path_element if (! $element instanceof restore_path_element) { throw new restore_step_exception('element_must_be_restore_path_element', $element); @@ -2535,31 +2535,22 @@ abstract class restore_questions_activity_structure_step extends restore_activit if (!is_array($paths)) { throw new restore_step_exception('paths_must_be_array', $paths); } - $paths[] = new restore_path_element('question_state', $element->get_path() . '/states/state'); + $paths[] = new restore_path_element('question_usage', + $element->get_path() . '/question_usage'); + $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'); + $paths[] = new restore_path_element('question_attempt_step_data', + $element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step/data/value'); } /** - * Attach below $element (usually attempts) the needed restore_path_elements - * to restore question_sessions + * Process question_usages */ - protected function add_question_attempts_sessions($element, &$paths) { - // Check $element is restore_path_element - if (! $element instanceof restore_path_element) { - throw new restore_step_exception('element_must_be_restore_path_element', $element); - } - // Check $paths is one array - if (!is_array($paths)) { - throw new restore_step_exception('paths_must_be_array', $paths); - } - $paths[] = new restore_path_element('question_session', $element->get_path() . '/sessions/session'); - } - - /** - * Process question_states - */ - protected function process_question_state($data) { + protected function process_question_usage($data) { global $DB; - +// TODO $data = (object)$data; $oldid = $data->id; @@ -2579,11 +2570,11 @@ abstract class restore_questions_activity_structure_step extends restore_activit } /** - * Process question_sessions + * Process question_attempts */ - protected function process_question_session($data) { + protected function process_question_attempt($data) { global $DB; - +// TODO $data = (object)$data; $oldid = $data->id; @@ -2601,6 +2592,22 @@ abstract class restore_questions_activity_structure_step extends restore_activit // support for them, so we don't need to handle them here. } + /** + * Process question_attempt_steps + */ + 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 + } + /** * Given a list of question->ids, separated by commas, returns the * recoded list, with all the restore question mappings applied. @@ -2619,27 +2626,4 @@ abstract class restore_questions_activity_structure_step extends restore_activit } return implode(',', $questionids); } - - /** - * Given one question_states record, return the answer - * recoded pointing to all the restored stuff - */ - public function restore_recode_answer($state, $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 we haven't the corresponding restore_qtype_plugin for current qtype - // instantiate it and add to cache - if (!isset($qtypeplugins[$qtype])) { - $classname = 'restore_qtype_' . $qtype . '_plugin'; - if (class_exists($classname)) { - $qtypeplugins[$qtype] = new $classname('qtype', $qtype, $this); - } else { - $qtypeplugins[$qtype] = false; - } - } - return !empty($qtypeplugins[$qtype]) ? $qtypeplugins[$qtype]->recode_state_answer($state) : $state->answer; - } } diff --git a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php index 5ae20f1b3e3..a0af928061e 100644 --- a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php @@ -83,8 +83,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru // This module is using questions, so produce the related question states and sessions // attaching them to the $attempt element based in 'uniqueid' matching - $this->add_question_attempts_states($attempt, 'uniqueid'); - $this->add_question_attempts_sessions($attempt, 'uniqueid'); + $this->add_question_usages($attempt, 'uniqueid'); // Build the tree diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index b3a32e72f7e..d19c774ad3e 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -50,8 +50,7 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st '/activity/quiz/attempts/attempt'); $paths[] = $quizattempt; // Add states and sessions - $this->add_question_attempts_states($quizattempt, $paths); - $this->add_question_attempts_sessions($quizattempt, $paths); + $this->add_question_usages($quizattempt, $paths); } // Return the paths wrapped into standard activity structure @@ -274,8 +273,6 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st $data->timefinish = $this->apply_date_offset($data->timefinish); $data->timemodified = $this->apply_date_offset($data->timemodified); - $data->layout = $this->questions_recode_layout($data->layout); - $newitemid = $DB->insert_record('quiz_attempts', $data); // Save quiz_attempt->uniqueid as quiz_attempt mapping, both question_states and