From 4d0a7e2b1f755888d9ac4c94a6f34c2ef6d92aa6 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 24 Aug 2012 15:34:20 +0100 Subject: [PATCH] MDL-35055 question import: slight error with the Match grades option. Even in the 'Error if grade not listed case', it was applying a small tolerance. In the case of a fuzzy match, it was returning the inexact grade from the import file, rather than the precise grade that Moodle was expecting. That causes problems when the editing form is displayed, because the value from the database does not match any of the available options, so the grade is changed to 0%. --- lib/questionlib.php | 41 +++++++++++++++++++--------------- lib/tests/questionlib_test.php | 15 +++++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/questionlib.php b/lib/questionlib.php index db7be64e8b8..7b74499c194 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -209,39 +209,44 @@ function get_grade_options() { } /** - * match grade options - * if no match return error or match nearest + * Check whether a given grade is one of a list of allowed options. If not, + * depending on $matchgrades, either return the nearest match, or return false + * to signal an error. * @param array $gradeoptionsfull list of valid options * @param int $grade grade to be tested * @param string $matchgrades 'error' or 'nearest' - * @return mixed either 'fixed' value or false if erro + * @return mixed either 'fixed' value or false if error. */ -function match_grade_options($gradeoptionsfull, $grade, $matchgrades='error') { +function match_grade_options($gradeoptionsfull, $grade, $matchgrades = 'error') { + if ($matchgrades == 'error') { - // if we just need an error... + // (Almost) exact match, or an error. foreach ($gradeoptionsfull as $value => $option) { - // slightly fuzzy test, never check floats for equality :-) + // Slightly fuzzy test, never check floats for equality. if (abs($grade - $value) < 0.00001) { - return $grade; + return $value; // Be sure the return the proper value. } } - // didn't find a match so that's an error + // Didn't find a match so that's an error. return false; + } else if ($matchgrades == 'nearest') { - // work out nearest value - $hownear = array(); + // Work out nearest value + $best = false; + $bestmismatch = 2; foreach ($gradeoptionsfull as $value => $option) { - if ($grade==$value) { - return $grade; + $newmismatch = abs($grade - $value); + if ($newmismatch < $bestmismatch) { + $best = $value; + $bestmismatch = $newmismatch; } - $hownear[ $value ] = abs( $grade - $value ); } - // reverse sort list of deltas and grab the last (smallest) - asort( $hownear, SORT_NUMERIC ); - reset( $hownear ); - return key( $hownear ); + return $best; + } else { - return false; + // Unknow option passed. + throw new coding_exception('Unknown $matchgrades ' . $matchgrades . + ' passed to match_grade_options'); } } diff --git a/lib/tests/questionlib_test.php b/lib/tests/questionlib_test.php index 2e5d431cb63..d7d767d8343 100644 --- a/lib/tests/questionlib_test.php +++ b/lib/tests/questionlib_test.php @@ -55,4 +55,19 @@ class questionlib_testcase extends basic_testcase { array(0 => 't1', 1 => 't2', 2 => 't3')); } + public function test_match_grade_options() { + $gradeoptions = question_bank::fraction_options_full(); + + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.3333333, 'error')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.333333, 'error')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33333, 'error')); + $this->assertFalse(match_grade_options($gradeoptions, 0.3333, 'error')); + + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.3333333, 'nearest')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.333333, 'nearest')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33333, 'nearest')); + $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33, 'nearest')); + + $this->assertEquals(-0.1428571, match_grade_options($gradeoptions, -0.15, 'nearest')); + } }