From fb93cfac00e36718b88340801fa28d86d2acf344 Mon Sep 17 00:00:00 2001 From: Henning Bostelmann Date: Mon, 26 Dec 2011 16:41:30 +0000 Subject: [PATCH] MDL-30031 Adaptive question behaviour: ignore invalid answers without penalty --- question/behaviour/adaptive/behaviour.php | 2 +- .../adaptive/lang/en/qbehaviour_adaptive.php | 1 + question/behaviour/adaptive/renderer.php | 18 +++ .../adaptive/simpletest/testwalkthrough.php | 119 +++++++++++++++++- .../behaviour/adaptivenopenalty/renderer.php | 3 + .../simpletest/testwalkthrough.php | 96 ++++++++++++++ 6 files changed, 236 insertions(+), 3 deletions(-) diff --git a/question/behaviour/adaptive/behaviour.php b/question/behaviour/adaptive/behaviour.php index 9144296f543..e85bc6815b0 100644 --- a/question/behaviour/adaptive/behaviour.php +++ b/question/behaviour/adaptive/behaviour.php @@ -119,7 +119,7 @@ class qbehaviour_adaptive extends question_behaviour_with_save { $status = $this->process_save($pendingstep); $response = $pendingstep->get_qt_data(); - if (!$this->question->is_gradable_response($response)) { + if (!$this->question->is_complete_response($response)) { $pendingstep->set_state(question_state::$invalid); if ($this->qa->get_state() != question_state::$invalid) { $status = question_attempt::KEEP; diff --git a/question/behaviour/adaptive/lang/en/qbehaviour_adaptive.php b/question/behaviour/adaptive/lang/en/qbehaviour_adaptive.php index aa64f5bf295..6c415de1836 100644 --- a/question/behaviour/adaptive/lang/en/qbehaviour_adaptive.php +++ b/question/behaviour/adaptive/lang/en/qbehaviour_adaptive.php @@ -23,6 +23,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['disregardedwithoutpenalty'] = 'The submission was invalid, and has been disregarded without penalty.'; $string['gradingdetails'] = 'Marks for this submission: {$a->raw}/{$a->max}.'; $string['gradingdetailsadjustment'] = 'Accounting for previous tries, this gives {$a->cur}/{$a->max}.'; $string['gradingdetailspenalty'] = 'This submission attracted a penalty of {$a}.'; diff --git a/question/behaviour/adaptive/renderer.php b/question/behaviour/adaptive/renderer.php index 2bc4fdb1c57..aa88f9b46c0 100644 --- a/question/behaviour/adaptive/renderer.php +++ b/question/behaviour/adaptive/renderer.php @@ -42,6 +42,16 @@ class qbehaviour_adaptive_renderer extends qbehaviour_renderer { } public function feedback(question_attempt $qa, question_display_options $options) { + if ($qa->get_state() == question_state::$invalid) { + // If the latest answer was invalid, display an informative message + $output = ''; + $info = $this->disregarded_info(); + if ($info) { + $output = html_writer::tag('div', $info, array('class' => 'gradingdetails')); + } + return $output; + } + // Try to find the last graded step. $gradedstep = $qa->get_behaviour()->get_graded_step($qa); @@ -116,4 +126,12 @@ class qbehaviour_adaptive_renderer extends qbehaviour_renderer { return $output; } + + /** + * Display information about a disregarded (incomplete) response. + */ + protected function disregarded_info() { + return get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive'); + } + } diff --git a/question/behaviour/adaptive/simpletest/testwalkthrough.php b/question/behaviour/adaptive/simpletest/testwalkthrough.php index 99727d48626..5d5a49806b4 100644 --- a/question/behaviour/adaptive/simpletest/testwalkthrough.php +++ b/question/behaviour/adaptive/simpletest/testwalkthrough.php @@ -63,6 +63,16 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b return new NoPatternExpectation($penaltypattern); } + protected function get_contains_disregarded_info_expectation() { + $penaltyinfo = get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive'); + return new PatternExpectation('/'.preg_quote($penaltyinfo).'/'); + } + + protected function get_does_not_contain_disregarded_info_expectation() { + $penaltyinfo = get_string('disregardedwithoutpenalty', 'qbehaviour_adaptive'); + return new NoPatternExpectation('/'.preg_quote($penaltyinfo).'/'); + } + public function test_adaptive_multichoice() { // Create a multiple choice, single response question. @@ -542,7 +552,8 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_penalty_info_expectation(), $this->get_does_not_contain_total_penalty_expectation(), - $this->get_contains_validation_error_expectation()); + $this->get_contains_validation_error_expectation(), + $this->get_contains_disregarded_info_expectation()); $this->assertNull($this->quba->get_response_summary($this->slot)); // Now get it wrong. @@ -568,7 +579,7 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b $this->check_current_output( $this->get_contains_mark_summary(0.8), $this->get_contains_submit_button_expectation(true), - $this->get_contains_partcorrect_expectation(), + $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_penalty_info_expectation(), $this->get_does_not_contain_total_penalty_expectation(), $this->get_contains_validation_error_expectation()); @@ -628,4 +639,108 @@ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_b $this->get_contains_incorrect_expectation(), $this->get_does_not_contain_validation_error_expectation()); } + + public function test_adaptive_numerical_invalid() { + + // Create a numerical question + $numq = test_question_maker::make_question('numerical', 'pi'); + $numq->penalty = 0.1; + $this->start_attempt_at_question($numq, 'adaptive'); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation()); + + // Submit a non-numerical answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'Pi')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(1), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_total_penalty_expectation(), + $this->get_contains_validation_error_expectation(), + $this->get_contains_disregarded_info_expectation()); + + // Submit an incorrect answer. + $this->process_submission(array('-submit' => 1, 'answer' => '-5')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.1), + $this->get_does_not_contain_total_penalty_expectation(), + $this->get_does_not_contain_validation_error_expectation(), + $this->get_does_not_contain_disregarded_info_expectation()); + + // Submit another non-numerical answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'Pi*2')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_total_penalty_expectation(), + $this->get_contains_validation_error_expectation(), + $this->get_contains_disregarded_info_expectation()); + + // Submit the correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => '3.14')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(0.9); + $this->check_current_output( + $this->get_contains_mark_summary(0.9), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_total_penalty_expectation(), + $this->get_does_not_contain_validation_error_expectation(), + $this->get_does_not_contain_disregarded_info_expectation()); + + // Submit another non-numerical answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'Pi/3')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(0.9); + $this->check_current_output( + $this->get_contains_mark_summary(0.9), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_total_penalty_expectation(), + $this->get_contains_validation_error_expectation(), + $this->get_contains_disregarded_info_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedwrong); + $this->check_current_mark(0.9); + $this->check_current_output( + $this->get_contains_mark_summary(0.9), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_validation_error_expectation(), + $this->get_does_not_contain_disregarded_info_expectation()); + } } diff --git a/question/behaviour/adaptivenopenalty/renderer.php b/question/behaviour/adaptivenopenalty/renderer.php index 60ca96b662e..d82d73b446d 100644 --- a/question/behaviour/adaptivenopenalty/renderer.php +++ b/question/behaviour/adaptivenopenalty/renderer.php @@ -41,4 +41,7 @@ class qbehaviour_adaptivenopenalty_renderer extends qbehaviour_adaptive_renderer protected function penalty_info($qa, $mark) { return ''; } + protected function disregarded_info() { + return ''; + } } diff --git a/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php b/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php index ecc45bdc72e..44ec3fb3d03 100644 --- a/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php +++ b/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php @@ -38,6 +38,11 @@ require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qbehaviour_adaptivenopenalty_walkthrough_test extends qbehaviour_walkthrough_test_base { + + protected function get_does_not_contain_gradingdetails_expectation() { + return new NoPatternExpectation('/class="gradingdetails"/'); + } + public function test_multichoice() { // Create a multiple choice, single response question. @@ -192,4 +197,95 @@ class qbehaviour_adaptivenopenalty_walkthrough_test extends qbehaviour_walkthrou $this->get_contains_submit_button_expectation(false), $this->get_contains_correct_expectation()); } + + public function test_numerical_invalid() { + + // Create a numerical question + $numq = test_question_maker::make_question('numerical', 'pi'); + $numq->penalty = 0.1; + $this->start_attempt_at_question($numq, 'adaptivenopenalty'); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation()); + + // Submit a non-numerical answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'Pi')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(1), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_contains_validation_error_expectation(), + $this->get_does_not_contain_feedback_expectation()); + + // Submit an incorrect answer. + $this->process_submission(array('-submit' => 1, 'answer' => '-5')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit another non-numerical answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'Pi*2')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_contains_validation_error_expectation(), + $this->get_does_not_contain_gradingdetails_expectation()); + + // Submit the correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => '3.14')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(1.0); + $this->check_current_output( + $this->get_contains_mark_summary(1.0), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit another non-numerical answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'Pi/3')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(1.0); + $this->check_current_output( + $this->get_contains_mark_summary(1.0), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_contains_validation_error_expectation(), + $this->get_does_not_contain_gradingdetails_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedwrong); + $this->check_current_mark(1.0); + $this->check_current_output( + $this->get_contains_mark_summary(1.0), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + } }