From 3d96b4945aa99f14f3db5f816ab527555f19ed42 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 17 Jan 2014 16:58:13 +0000 Subject: [PATCH] MDL-43733 use any auto-saved responses when questions are finished. Before this, autosave was only working to save data when a student went back in to continue an attempt. If the student, having crashed out, never went back in and continued the attempt, their auto-saved responses were not used when the attempt was automatically finished. That was a rather bad oversight, which should now be fixed. --- question/engine/questionattempt.php | 19 +++++++ .../tests/questionusage_autosave_test.php | 57 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/question/engine/questionattempt.php b/question/engine/questionattempt.php index 3c3acffb58d0d..9d4d0f4e04ee0 100644 --- a/question/engine/questionattempt.php +++ b/question/engine/questionattempt.php @@ -869,6 +869,24 @@ public function discard_autosaved_step() { $this->observer->notify_step_deleted($autosaved, $this); } + /** + * If there is an autosaved step, convert it into a real save, so that it + * is preserved. + */ + protected function convert_autosaved_step_to_real_step() { + if ($this->autosavedstep === null) { + return; + } + + $laststep = end($this->steps); + if ($laststep !== $this->autosavedstep) { + throw new coding_exception('Cannot convert autosaved step to real step, since other steps have been added.'); + } + + $this->observer->notify_step_modified($this->autosavedstep, $this, key($this->steps)); + $this->autosavedstep = null; + } + /** * Use a strategy to pick a variant. * @param question_variant_selection_strategy $variantstrategy a strategy. @@ -1174,6 +1192,7 @@ public function process_autosave($submitteddata, $timestamp = null, $userid = nu * @param int $userid the user to attribute the aciton to. (If not given, use the current user.) */ public function finish($timestamp = null, $userid = null) { + $this->convert_autosaved_step_to_real_step(); $this->process_action(array('-finish' => 1), $timestamp, $userid); } diff --git a/question/engine/tests/questionusage_autosave_test.php b/question/engine/tests/questionusage_autosave_test.php index 8b0a45c8282f0..14315408c79c7 100644 --- a/question/engine/tests/questionusage_autosave_test.php +++ b/question/engine/tests/questionusage_autosave_test.php @@ -628,4 +628,61 @@ public function test_autosave_with_wrong_seq_number_ignored() { $this->delete_quba(); } + + public function test_finish_with_unhandled_autosave_data() { + $this->resetAfterTest(); + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $cat = $generator->create_question_category(); + $question = $generator->create_question('shortanswer', null, + array('category' => $cat->id)); + + // Start attempt at a shortanswer question. + $q = question_bank::load_question($question->id); + $this->start_attempt_at_question($q, 'deferredfeedback', 1); + + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_step_count(1); + + // Process a response and check the expected result. + $this->process_submission(array('answer' => 'cat')); + + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_step_count(2); + $this->save_quba(); + + // Now check how that is re-displayed. + $this->render(); + $this->check_output_contains_text_input('answer', 'cat'); + $this->check_output_contains_hidden_input(':sequencecheck', 2); + + // Process an autosave. + $this->load_quba(); + $this->process_autosave(array('answer' => 'frog')); + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_step_count(3); + $this->save_quba(); + + // Now check how that is re-displayed. + $this->load_quba(); + $this->render(); + $this->check_output_contains_text_input('answer', 'frog'); + $this->check_output_contains_hidden_input(':sequencecheck', 2); + + // Now finishe the attempt, without having done anything since the autosave. + $this->finish(); + $this->save_quba(); + + // Now check how that has been graded and is re-displayed. + $this->load_quba(); + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(1); + $this->render(); + $this->check_output_contains_text_input('answer', 'frog', false); + $this->check_output_contains_hidden_input(':sequencecheck', 4); + + $this->delete_quba(); + } }