From fb74929e971bbe5fe2c55e85cb23a40aedc32b37 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 2 Oct 2013 17:33:14 +0100 Subject: [PATCH] MDL-32188 question CBM: alter score handling. We now change so that minfraction is -6 and maxfraction is 3, so getting the question right a low certainty gives maxmark marks, and you get a bonus for being more confident (rather than being penalised for being unconfident). Mathematically it is the same, but the difference is importnat psychologically. We also change how partially correct scores are handled. It is too harsh to penalise a partially correct score with full certainty by doing a linear interpolation between -6 and +3. Instead, any partially correct score (e.g. 0.5) becomes that fraction of the correct score (e.g. 0.5 * 3 = 1.5). Also, any incorrect score is treated as 0, so if you have a multiple choice question that normally gives a negative score for a wrong choice, this will now never give a score of less than -6. Finally we change how this is displayed to students beside the question. Rather than saying "Marked out of 1.00", we say "Base mark 1.00", and then later we say "CBM mark 3.00" (or whatever it is). --- question/behaviour/behaviourbase.php | 22 ++++--- question/behaviour/deferredcbm/behaviour.php | 4 +- .../lang/en/qbehaviour_deferredcbm.php | 3 +- question/behaviour/deferredcbm/renderer.php | 21 +++--- .../deferredcbm/tests/behaviourtype_test.php | 64 +++++++++++++++++++ .../deferredcbm/tests/question_cbm_test.php | 56 ++++++++++++++++ .../deferredcbm/tests/walkthrough_test.php | 24 +++---- .../immediatecbm/tests/walkthrough_test.php | 12 ++-- question/engine/tests/questioncbm_test.php | 48 -------------- 9 files changed, 167 insertions(+), 87 deletions(-) create mode 100644 question/behaviour/deferredcbm/tests/behaviourtype_test.php create mode 100644 question/behaviour/deferredcbm/tests/question_cbm_test.php delete mode 100644 question/engine/tests/questioncbm_test.php diff --git a/question/behaviour/behaviourbase.php b/question/behaviour/behaviourbase.php index 24a69157afb..0aea96505aa 100644 --- a/question/behaviour/behaviourbase.php +++ b/question/behaviour/behaviourbase.php @@ -643,16 +643,16 @@ abstract class question_cbm { /** @var array list of all the certainty levels. */ public static $certainties = array(self::LOW, self::MED, self::HIGH); - /**#@+ @var array coefficients used to adjust the fraction based on certainty.. */ - protected static $factor = array( - self::LOW => 0.333333333333333, - self::MED => 1.333333333333333, + /**#@+ @var array coefficients used to adjust the fraction based on certainty. */ + protected static $rightscore = array( + self::LOW => 1, + self::MED => 2, self::HIGH => 3, ); - protected static $offset = array( - self::LOW => 0, - self::MED => -0.666666666666667, - self::HIGH => -2, + protected static $wrongscore = array( + self::LOW => 0, + self::MED => -2, + self::HIGH => -6, ); /**#@-*/ @@ -671,7 +671,11 @@ abstract class question_cbm { * @return number the adjusted fraction taking the certainty into account. */ public static function adjust_fraction($fraction, $certainty) { - return self::$offset[$certainty] + self::$factor[$certainty] * $fraction; + if ($fraction <= 0.00000005) { + return self::$wrongscore[$certainty]; + } else { + return self::$rightscore[$certainty] * $fraction; + } } /** diff --git a/question/behaviour/deferredcbm/behaviour.php b/question/behaviour/deferredcbm/behaviour.php index e5464fa1cc0..6df93c1322a 100644 --- a/question/behaviour/deferredcbm/behaviour.php +++ b/question/behaviour/deferredcbm/behaviour.php @@ -46,11 +46,11 @@ class qbehaviour_deferredcbm extends qbehaviour_deferredfeedback { const IS_ARCHETYPAL = true; public function get_min_fraction() { - return question_cbm::adjust_fraction(parent::get_min_fraction(), question_cbm::HIGH); + return question_cbm::adjust_fraction(0, question_cbm::HIGH); } public function get_max_fraction() { - return question_cbm::adjust_fraction(parent::get_max_fraction(), question_cbm::HIGH); + return question_cbm::adjust_fraction(1, question_cbm::HIGH); } public function get_expected_data() { diff --git a/question/behaviour/deferredcbm/lang/en/qbehaviour_deferredcbm.php b/question/behaviour/deferredcbm/lang/en/qbehaviour_deferredcbm.php index 7a78f17edfd..31bb35fe4f1 100644 --- a/question/behaviour/deferredcbm/lang/en/qbehaviour_deferredcbm.php +++ b/question/behaviour/deferredcbm/lang/en/qbehaviour_deferredcbm.php @@ -24,9 +24,10 @@ */ $string['assumingcertainty'] = 'You did not select a certainty. Assuming: {$a}.'; +$string['basemark'] = 'Base mark {$a}'; +$string['cbmmark'] = 'CBM mark {$a}'; $string['certainty1'] = 'Not very (less than 67%)'; $string['certainty2'] = 'Fairly (more than 67%)'; $string['certainty3'] = 'Very (more than 80%)'; $string['howcertainareyou'] = 'How certain are you? {$a}'; -$string['markadjustment'] = 'Based on the certainty you expressed, your base mark of {$a->rawmark} was adjusted to {$a->mark}.'; $string['pluginname'] = 'Deferred feedback with CBM'; diff --git a/question/behaviour/deferredcbm/renderer.php b/question/behaviour/deferredcbm/renderer.php index 25d3d727359..1f33df3dc3d 100644 --- a/question/behaviour/deferredcbm/renderer.php +++ b/question/behaviour/deferredcbm/renderer.php @@ -87,15 +87,18 @@ class qbehaviour_deferredcbm_renderer extends qbehaviour_renderer { question_cbm::get_string($qa->get_last_behaviour_var('_assumedcertainty')))); } - if ($options->marks >= question_display_options::MARK_AND_MAX) { - $a = new stdClass(); - $a->rawmark = format_float($qa->get_last_behaviour_var('_rawfraction') * - $qa->get_max_mark(), $options->markdp); - $a->mark = $qa->format_mark($options->markdp); - $feedback .= html_writer::tag('p', - get_string('markadjustment', 'qbehaviour_deferredcbm', $a)); - } - return $feedback; } + + public function marked_out_of_max(question_attempt $qa, core_question_renderer $qoutput, + question_display_options $options) { + return get_string('basemark', 'qbehaviour_deferredcbm', $qa->format_fraction_as_mark( + question_cbm::adjust_fraction(1, question_cbm::default_certainty()), + $options->markdp)); + } + + public function mark_out_of_max(question_attempt $qa, core_question_renderer $qoutput, + question_display_options $options) { + return get_string('cbmmark', 'qbehaviour_deferredcbm', $qa->format_mark($options->markdp)); + } } \ No newline at end of file diff --git a/question/behaviour/deferredcbm/tests/behaviourtype_test.php b/question/behaviour/deferredcbm/tests/behaviourtype_test.php new file mode 100644 index 00000000000..e060468653e --- /dev/null +++ b/question/behaviour/deferredcbm/tests/behaviourtype_test.php @@ -0,0 +1,64 @@ +. + +/** + * This file contains tests that walks a question through the deferred feedback + * with certainty base marking behaviour. + * + * @package qbehaviour + * @subpackage deferredcbm + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once(dirname(__FILE__) . '/../../../engine/lib.php'); +require_once(dirname(__FILE__) . '/../../../engine/tests/helpers.php'); + + +/** + * Unit tests for the deferred feedback with certainty base marking behaviour. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qbehaviour_deferredcbm_type_test extends qbehaviour_walkthrough_test_base { + + /** @var qbehaviour_deferredcbm_type */ + protected $behaviourtype; + + public function setUp() { + parent::setUp(); + $this->behaviourtype = question_engine::get_behaviour_type('deferredcbm'); + } + + public function test_is_archetypal() { + $this->assertTrue($this->behaviourtype->is_archetypal()); + } + + public function test_get_unused_display_options() { + $this->assertEquals(array('correctness', 'marks', 'specificfeedback', 'generalfeedback', 'rightanswer'), + $this->behaviourtype->get_unused_display_options()); + } + + public function test_adjust_random_guess_score() { + $this->assertEquals(0, $this->behaviourtype->adjust_random_guess_score(0)); + $this->assertEquals(1, $this->behaviourtype->adjust_random_guess_score(1)); + } +} diff --git a/question/behaviour/deferredcbm/tests/question_cbm_test.php b/question/behaviour/deferredcbm/tests/question_cbm_test.php new file mode 100644 index 00000000000..f67025d62fe --- /dev/null +++ b/question/behaviour/deferredcbm/tests/question_cbm_test.php @@ -0,0 +1,56 @@ +. + +/** + * This file contains tests that walks a question through the deferred feedback + * with certainty base marking behaviour. + * + * @package qbehaviour + * @subpackage deferredcbm + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once(dirname(__FILE__) . '/../../../engine/lib.php'); + + +/** + * Unit tests for the deferred feedback with certainty base marking behaviour. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qbehaviour_deferredcbm_cbm_test extends basic_testcase { + + public function test_adjust_fraction() { + $this->assertEquals( 1, question_cbm::adjust_fraction( 1, question_cbm::LOW), '', 0.0000001); + $this->assertEquals( 2, question_cbm::adjust_fraction( 1, question_cbm::MED), '', 0.0000001); + $this->assertEquals( 3, question_cbm::adjust_fraction( 1, question_cbm::HIGH), '', 0.0000001); + $this->assertEquals( 0, question_cbm::adjust_fraction( 0, question_cbm::LOW), '', 0.0000001); + $this->assertEquals(-2, question_cbm::adjust_fraction( 0, question_cbm::MED), '', 0.0000001); + $this->assertEquals(-6, question_cbm::adjust_fraction( 0, question_cbm::HIGH), '', 0.0000001); + $this->assertEquals( 0.5, question_cbm::adjust_fraction( 0.5, question_cbm::LOW), '', 0.0000001); + $this->assertEquals( 1, question_cbm::adjust_fraction( 0.5, question_cbm::MED), '', 0.0000001); + $this->assertEquals( 1.5, question_cbm::adjust_fraction( 0.5, question_cbm::HIGH), '', 0.0000001); + $this->assertEquals( 0, question_cbm::adjust_fraction(-0.25, question_cbm::LOW), '', 0.0000001); + $this->assertEquals(-2, question_cbm::adjust_fraction(-0.25, question_cbm::MED), '', 0.0000001); + $this->assertEquals(-6, question_cbm::adjust_fraction(-0.25, question_cbm::HIGH), '', 0.0000001); + } +} diff --git a/question/behaviour/deferredcbm/tests/walkthrough_test.php b/question/behaviour/deferredcbm/tests/walkthrough_test.php index 19d7124916d..f483562b3b9 100644 --- a/question/behaviour/deferredcbm/tests/walkthrough_test.php +++ b/question/behaviour/deferredcbm/tests/walkthrough_test.php @@ -90,18 +90,18 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes // Verify. $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(2); + $this->check_current_mark(6); $this->check_current_output( $this->get_contains_tf_true_radio_expectation(false, true), $this->get_contains_cbm_radio_expectation(3, false, true), $this->get_contains_correct_expectation()); // Process a manual comment. - $this->manual_grade('Not good enough!', 1, FORMAT_HTML); + $this->manual_grade('Not good enough!', 5, FORMAT_HTML); // Verify. - $this->check_current_state(question_state::$mangrpartial); - $this->check_current_mark(1); + $this->check_current_state(question_state::$mangrright); + $this->check_current_mark(5); $this->check_current_output(new question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/')); @@ -110,10 +110,10 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes $this->quba->regrade_all_questions(); // Verify. - $this->check_current_state(question_state::$mangrpartial); - $this->check_current_mark(1); + $this->check_current_state(question_state::$mangrright); + $this->check_current_mark(5); $autogradedstep = $this->get_step($this->get_step_count() - 2); - $this->assertEquals($autogradedstep->get_fraction(), -2, '', 0.0000001); + $this->assertEquals(-6, $autogradedstep->get_fraction(), '', 0.0000001); } public function test_deferred_cbm_truefalse_low_certainty() { @@ -147,7 +147,7 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes // Verify. $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(0.6666667); + $this->check_current_mark(2); $this->check_current_output($this->get_contains_correct_expectation(), $this->get_contains_cbm_radio_expectation(1, false, true)); $this->assertEquals(get_string('true', 'qtype_truefalse') . ' [' . @@ -177,7 +177,7 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes // Verify. $qa = $this->quba->get_question_attempt($this->slot); $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(0.6666667); + $this->check_current_mark(2); $this->check_current_output($this->get_contains_correct_expectation(), $this->get_contains_cbm_radio_expectation(1, false, false), new question_pattern_expectation('/' . preg_quote( @@ -194,7 +194,7 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes $mc = test_question_maker::make_a_multichoice_single_question(); // Attempt it getting it wrong. - $this->start_attempt_at_question($mc, 'deferredcbm', 3); + $this->start_attempt_at_question($mc, 'deferredcbm', 1); $rightindex = $this->get_mc_right_answer_index($mc); $wrongindex = ($rightindex + 1) % 3; $this->process_submission(array('answer' => $wrongindex, '-certainty' => 2)); @@ -202,7 +202,7 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes // Verify. $this->check_current_state(question_state::$gradedwrong); - $this->check_current_mark(-3.3333333); + $this->check_current_mark(-2); $this->check_current_output( $this->get_contains_mc_radio_expectation($wrongindex, false, true), $this->get_contains_cbm_radio_expectation(2, false, true), @@ -220,7 +220,7 @@ class qbehaviour_deferredcbm_walkthrough_test extends qbehaviour_walkthrough_tes // Reinitialise. $this->setUp(); $this->quba->set_preferred_behaviour('deferredcbm'); - $this->slot = $this->quba->add_question($mc, 3); + $this->slot = $this->quba->add_question($mc, 1); $this->quba->start_question_based_on($this->slot, $oldqa); // Verify. diff --git a/question/behaviour/immediatecbm/tests/walkthrough_test.php b/question/behaviour/immediatecbm/tests/walkthrough_test.php index 66e58d6defc..6610629b094 100644 --- a/question/behaviour/immediatecbm/tests/walkthrough_test.php +++ b/question/behaviour/immediatecbm/tests/walkthrough_test.php @@ -84,7 +84,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te // Verify. $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(2/3); + $this->check_current_mark(2); $this->check_current_output( $this->get_contains_mc_radio_expectation($rightindex, false, true), $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), @@ -101,7 +101,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te // Verify. $this->assertEquals($numsteps, $this->get_step_count()); $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(2/3); + $this->check_current_mark(2); $this->check_current_output( $this->get_contains_mc_radio_expectation($rightindex, false, true), $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), @@ -130,7 +130,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te $this->get_contains_partcorrect_expectation()); $autogradedstep = $this->get_step($this->get_step_count() - 2); - $this->assertEquals($autogradedstep->get_fraction(), -10/9, '', 0.0000001); + $this->assertEquals($autogradedstep->get_fraction(), -2, '', 0.0000001); } public function test_immediatecbm_feedback_multichoice_try_to_submit_blank() { @@ -215,7 +215,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te // Verify. $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(1); + $this->check_current_mark(3); $this->check_current_output( $this->get_does_not_contain_validation_error_expectation()); } @@ -258,7 +258,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te // Verify. $this->check_current_state(question_state::$gradedwrong); - $this->check_current_mark(-3); + $this->check_current_mark(-6); $this->check_current_output( $this->get_contains_mc_radio_expectation($wrongindex, false, true), $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), @@ -270,7 +270,7 @@ class qbehaviour_immediatecbm_walkthrough_test extends qbehaviour_walkthrough_te // Create a true-false question with correct answer true. $tf = test_question_maker::make_question('truefalse', 'true'); - $this->start_attempt_at_question($tf, 'deferredcbm', 2); + $this->start_attempt_at_question($tf, 'immediatecbm', 2); // Verify. $this->check_current_state(question_state::$todo); diff --git a/question/engine/tests/questioncbm_test.php b/question/engine/tests/questioncbm_test.php deleted file mode 100644 index 1757fd82f5c..00000000000 --- a/question/engine/tests/questioncbm_test.php +++ /dev/null @@ -1,48 +0,0 @@ -. - -/** - * This file contains tests for the question_cbm class. - * - * @package moodlecore - * @subpackage questionengine - * @copyright 2009 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -defined('MOODLE_INTERNAL') || die(); - -global $CFG; -require_once(dirname(__FILE__) . '/../lib.php'); - - -/** - * Unit tests for the question_cbm class. - * - * @copyright 2009 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class question_cbm_test extends advanced_testcase { - public function test_adjust_fraction() { - $this->assertEquals(0, question_cbm::adjust_fraction(0, question_cbm::LOW), '', 0.0000001); - $this->assertEquals(-2/3, question_cbm::adjust_fraction(0, question_cbm::MED), '', 0.0000001); - $this->assertEquals(-2, question_cbm::adjust_fraction(0, question_cbm::HIGH), '', 0.0000001); - $this->assertEquals(1/3, question_cbm::adjust_fraction(1, question_cbm::LOW), '', 0.0000001); - $this->assertEquals(2/3, question_cbm::adjust_fraction(1, question_cbm::MED), '', 0.0000001); - $this->assertEquals(1, question_cbm::adjust_fraction(1, question_cbm::HIGH), '', 0.0000001); - } -} \ No newline at end of file