MDL-32188 question CBM: alter score handling.
authorTim Hunt <T.J.Hunt@open.ac.uk>
Wed, 2 Oct 2013 16:33:14 +0000 (17:33 +0100)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Fri, 4 Oct 2013 16:52:29 +0000 (17:52 +0100)
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
question/behaviour/deferredcbm/behaviour.php
question/behaviour/deferredcbm/lang/en/qbehaviour_deferredcbm.php
question/behaviour/deferredcbm/renderer.php
question/behaviour/deferredcbm/tests/behaviourtype_test.php [new file with mode: 0644]
question/behaviour/deferredcbm/tests/question_cbm_test.php [new file with mode: 0644]
question/behaviour/deferredcbm/tests/walkthrough_test.php
question/behaviour/immediatecbm/tests/walkthrough_test.php
question/engine/tests/questioncbm_test.php [deleted file]

index 24a6915..0aea965 100644 (file)
@@ -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;
+        }
     }
 
     /**
index e5464fa..6df93c1 100644 (file)
@@ -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() {
index 7a78f17..31bb35f 100644 (file)
  */
 
 $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';
index 25d3d72..1f33df3 100644 (file)
@@ -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 (file)
index 0000000..e060468
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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 (file)
index 0000000..f67025d
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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);
+    }
+}
index 19d7124..f483562 100644 (file)
@@ -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.
index 66e58d6..6610629 100644 (file)
@@ -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 (file)
index 1757fd8..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * 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