From 4eae27fda8857a70587fe2003b12a5863a3df510 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Mon, 4 Jan 2010 17:58:26 +0000 Subject: [PATCH] MDL-19931 Number of errors - grading strategy logic implemented --- .../noerrors/simpletest/teststrategy.php | 201 ++++++++++-------- mod/workshop/grading/noerrors/strategy.php | 47 +++- 2 files changed, 159 insertions(+), 89 deletions(-) diff --git a/mod/workshop/grading/noerrors/simpletest/teststrategy.php b/mod/workshop/grading/noerrors/simpletest/teststrategy.php index 3dfd168c18c..eba90807647 100644 --- a/mod/workshop/grading/noerrors/simpletest/teststrategy.php +++ b/mod/workshop/grading/noerrors/simpletest/teststrategy.php @@ -39,6 +39,9 @@ class testable_workshop_noerrors_strategy extends workshop_noerrors_strategy { /** allows to set dimensions manually */ public $dimensions = array(); + /** allow to set mappings manually */ + public $mappings = array(); + /** * This is where the calculation of suggested grade for submission is done */ @@ -83,7 +86,8 @@ class workshop_noerrors_strategy_test extends UnitTestCase { public function test_calculate_peer_grade_null_grade() { // fixture set-up - $this->dimensions = array(); + $this->strategy->dimensions = array(); + $this->strategy->mappings = array(); $grades = array(); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); @@ -91,138 +95,167 @@ class workshop_noerrors_strategy_test extends UnitTestCase { $this->assertNull($suggested); } - public function test_calculate_peer_grade_one_numerical() { + public function test_calculate_peer_grade_no_error() { // fixture set-up - $this->strategy->dimensions[1003] = (object)array('grade' => 20, 'weight' => 1); - $grades[] = (object)array('dimensionid' => 1003, 'grade' => 5); + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 1); + $this->strategy->dimensions[111] = (object)array('weight' => 1); + $this->strategy->mappings = array(); + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 1); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 1); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 1); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); // validate - $this->assertEqual(5/20, $suggested); + $this->assertEqual($suggested, 1.0); } - public function test_calculate_peer_grade_negative_weight() { + public function test_calculate_peer_grade_one_error() { // fixture set-up - $this->strategy->dimensions[1003] = (object)array('grade' => 20, 'weight' => -1); - $grades[] = (object)array('dimensionid' => 1003, 'grade' => 20); - $this->expectException('coding_exception'); - // excercise SUT - $suggested = $this->strategy->calculate_peer_grade($grades); - } + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 1); + $this->strategy->dimensions[111] = (object)array('weight' => 1); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => 80.0), + 2 => (object)array('grade' => 60.0), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 1); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 1); - public function test_calculate_peer_grade_one_numerical_weighted() { - // fixture set-up - $this->strategy->dimensions[1003] = (object)array('grade' => 20, 'weight' => 3); - $grades[] = (object)array('dimensionid' => 1003, 'grade' => 5); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); // validate - $this->assertEqual(5/20, $suggested); + $this->assertEqual($suggested, 0.8); } - public function test_calculate_peer_grade_three_numericals_same_weight() { + public function test_calculate_peer_grade_three_errors_same_weight_a() { // fixture set-up - $this->strategy->dimensions[1003] = (object)array('grade' =>20, 'weight' => 2); - $this->strategy->dimensions[1004] = (object)array('grade' =>100, 'weight' => 2); - $this->strategy->dimensions[1005] = (object)array('grade' =>10, 'weight' => 2); + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 1); + $this->strategy->dimensions[111] = (object)array('weight' => 1); - $grades[] = (object)array('dimensionid' => 1003, 'grade' => 11); - $grades[] = (object)array('dimensionid' => 1004, 'grade' => 87); - $grades[] = (object)array('dimensionid' => 1005, 'grade' => 10); + $this->strategy->mappings = array( + 1 => (object)array('grade' => 80.0), + 2 => (object)array('grade' => 60.0), + 3 => (object)array('grade' => 10.0), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 0); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); - // validate - $this->assertEqual((11/20 + 87/100 + 10/10)/3, $suggested); + $this->assertEqual($suggested, 0.1); } - public function test_calculate_peer_grade_three_numericals_different_weights() { + public function test_calculate_peer_grade_three_errors_same_weight_b() { // fixture set-up - $this->strategy->dimensions[1003] = (object)array('grade' =>15, 'weight' => 3); - $this->strategy->dimensions[1004] = (object)array('grade' =>80, 'weight' => 1); - $this->strategy->dimensions[1005] = (object)array('grade' =>5, 'weight' => 2); + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 1); + $this->strategy->dimensions[111] = (object)array('weight' => 1); - $grades[] = (object)array('dimensionid' => 1003, 'grade' => 7); - $grades[] = (object)array('dimensionid' => 1004, 'grade' => 66); - $grades[] = (object)array('dimensionid' => 1005, 'grade' => 4); + $this->strategy->mappings = array( + 1 => (object)array('grade' => 80.0), + 2 => (object)array('grade' => 60.0), + 3 => (object)array('grade' => 0.0), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 0); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); - // validate - $this->assertEqual((7/15*3 + 66/80*1 + 4/5*2)/6, $suggested); + $this->assertEqual($suggested, 0); } - public function test_calculate_peer_grade_one_scale_max() { - global $DB; - + public function test_calculate_peer_grade_one_error_weighted() { // fixture set-up - $mockscale = 'E,D,C,B,A'; - $this->strategy->dimensions[1008] = (object)array('grade' => -10, 'weight' => 1); - $grades[] = (object)array('dimensionid' => 1008, 'grade' => 5); - $DB->expectOnce('get_field', array("scales", "scale", array("id" => 10), MUST_EXIST)); - $DB->setReturnValue('get_field', $mockscale); + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 2); + $this->strategy->dimensions[111] = (object)array('weight' => 0); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => 66.0), + 2 => (object)array('grade' => 33.0), + 3 => (object)array('grade' => 0.0), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 1); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 1); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 0); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); - // validate - $this->assertEqual(1, $suggested); + $this->assertEqual($suggested, 0.33); } - public function test_calculate_peer_grade_one_scale_min_with_scale_caching() { - global $DB; - + public function test_calculate_peer_grade_zero_weight() { // fixture set-up - $this->strategy->dimensions[1008] = (object)array('grade' => -10, 'weight' => 1); - $grades[] = (object)array('dimensionid' => 1008, 'grade' => 1); - $DB->expectNever('get_field', array("scales", "scale", array("id" => 10), MUST_EXIST)); // cached + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 2); + $this->strategy->dimensions[111] = (object)array('weight' => 0); + + $this->strategy->mappings = array( + 1 => (object)array('grade' => 66.0), + 2 => (object)array('grade' => 33.0), + 3 => (object)array('grade' => 0.0), + ); + + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 1); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 1); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); - // validate - $this->assertEqual(0, $suggested); + $this->assertEqual($suggested, 1.0); } - public function test_calculate_peer_grade_two_scales_weighted() { - global $DB; - + public function test_calculate_peer_grade_sum_weight() { // fixture set-up - $mockscale13 = 'Poor,Good,Excellent'; - $mockscale17 = '-,*,**,***,****,*****,******'; + $this->strategy->dimensions = array(); + $this->strategy->dimensions[108] = (object)array('weight' => 1); + $this->strategy->dimensions[109] = (object)array('weight' => 2); + $this->strategy->dimensions[111] = (object)array('weight' => 3); - $this->strategy->dimensions[1012] = (object)array('grade' => -13, 'weight' => 2); - $this->strategy->dimensions[1019] = (object)array('grade' => -17, 'weight' => 3); + $this->strategy->mappings = array( + 1 => (object)array('grade' => 90.0), + 2 => (object)array('grade' => 80.0), + 3 => (object)array('grade' => 70.0), + 4 => (object)array('grade' => 60.0), + 5 => (object)array('grade' => 30.0), + 6 => (object)array('grade' => 5.0), + 7 => (object)array('grade' => 0.0), + ); - $grades[] = (object)array('dimensionid' => 1012, 'grade' => 2); // "Good" - $grades[] = (object)array('dimensionid' => 1019, 'grade' => 5); // "****" - - $DB->expectAt(0, 'get_field', array("scales", "scale", array("id" => 13), MUST_EXIST)); - $DB->setReturnValueAt(0, 'get_field', $mockscale13); - - $DB->expectAt(1, 'get_field', array("scales", "scale", array("id" => 17), MUST_EXIST)); - $DB->setReturnValueAt(1, 'get_field', $mockscale17); + $grades = array(); + $grades[] = (object)array('dimensionid' => 108, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 111, 'grade' => 0); + $grades[] = (object)array('dimensionid' => 109, 'grade' => 0); // excercise SUT $suggested = $this->strategy->calculate_peer_grade($grades); - // validate - $this->assertEqual((1/2*2 + 4/6*3)/5, $suggested); - } - - public function test_calculate_peer_grade_scale_exception() { - global $DB; - - // fixture set-up - $mockscale13 = 'Poor,Good,Excellent'; - $this->strategy->dimensions[1012] = (object)array('grade' => -13, 'weight' => 1); - $DB->expectNever('get_field', array("scales", "scale", array("id" => 13), MUST_EXIST)); // cached - $grades[] = (object)array('dimensionid' => 1012, 'grade' => 4); // exceeds the number of scale items - $this->expectException('coding_exception'); - - // excercise SUT - $suggested = $this->strategy->calculate_peer_grade($grades); + $this->assertEqual($suggested, 0.05); } } diff --git a/mod/workshop/grading/noerrors/strategy.php b/mod/workshop/grading/noerrors/strategy.php index e5b44802686..e92b2c48f84 100644 --- a/mod/workshop/grading/noerrors/strategy.php +++ b/mod/workshop/grading/noerrors/strategy.php @@ -446,17 +446,54 @@ class workshop_noerrors_strategy implements workshop_strategy { * Calculates the aggregated grade given by the reviewer * * @param array $grades Grade records as returned by {@link get_current_assessment_data} - * @uses $this->mappings * @return float|null Raw grade (0 to 1) for submission as suggested by the peer */ protected function calculate_peer_grade(array $grades) { - if (empty($grades)) { return null; } - $sumgrades = 0; - $sumweights = 0; - return 1; + $sumerrors = 0; // sum of the weighted errors (ie the negative responses) + foreach ($grades as $grade) { + if (empty($grade->grade)) { + // negative reviewer's response + $sumerrors += $this->dimensions[$grade->dimensionid]->weight; + } + } + return $this->errors_to_grade($sumerrors); } + /** + * Returns a grade 0..1 for the given number of errors + * + * This is where we use the mapping table defined by the teacher. If a grade for the given + * number of errors (negative assertions) is not defined, the most recently defined one is used. + * Example of the defined mapping: + * Number of errors | Grade + * 0 | 100% (always) + * 1 | - (not defined) + * 2 | 80% + * 3 | 60% + * 4 | - + * 5 | 30% + * 6 | 0% + * With this mapping, one error is mapped to 100% grade and 4 errors is mapped to 60%. + * + * @param mixed $noerrors Number of errors + * @return float Raw grade (0 to 1) for the given number of negative assertions + */ + protected function errors_to_grade($noerrors) { + $grade = 100; + for ($i = 1; $i <= $noerrors; $i++) { + if (isset($this->mappings[$i])) { + $grade = $this->mappings[$i]->grade; + } + } + if ($grade > 100) { + $grade = 100; + } + if ($grade < 0) { + $grade = 0; + } + return $grade/100; + } }