diff --git a/question/type/multianswer/edit_multianswer_form.php b/question/type/multianswer/edit_multianswer_form.php index 1eafd99ea0a..cd792f78a1f 100644 --- a/question/type/multianswer/edit_multianswer_form.php +++ b/question/type/multianswer/edit_multianswer_form.php @@ -366,12 +366,8 @@ class qtype_multianswer_edit_form extends question_edit_form { } foreach ($subquestion->answer as $key => $answer) { if ($subquestion->qtype == 'numerical' && $key == 0) { - if (isset($subquestion->tolerance[0])) { - $default_values[$prefix.'tolerance['.$key.']'] = + $default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0]; - } else { - $default_values[$prefix.'tolerance['.$key.']'] = 0; - } } if (is_array($answer)) { $answer = $answer['text']; @@ -476,11 +472,10 @@ class qtype_multianswer_edit_form extends question_edit_form { $trimmedanswer = trim($answer); if ($trimmedanswer !== '') { $answercount++; - if ($subquestion->qtype == 'numerical' && $trimmedanswer != '*') { - if (!$this->is_valid_number($trimmedanswer)) { - $errors[$prefix.'answer['.$key.']'] = + if ($subquestion->qtype == 'numerical' && + !($this->is_valid_number($trimmedanswer) || $trimmedanswer == '*')) { + $errors[$prefix.'answer['.$key.']'] = get_string('answermustbenumberorstar', 'qtype_numerical'); - } } if ($subquestion->fraction[$key] == 1) { $maxgrade = true; diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php index acd9df055bf..5df9f2cce59 100644 --- a/question/type/multianswer/questiontype.php +++ b/question/type/multianswer/questiontype.php @@ -262,9 +262,9 @@ define('ANSWER_ALTERNATIVE_REGEX_FEEDBACK', 5); // NUMBER_FORMATED_ALTERNATIVE_ANSWER_REGEX is used // for identifying numerical answers in ANSWER_ALTERNATIVE_REGEX_ANSWER. define('NUMBER_REGEX', - '-?(([0-9]+[., ]?[., 0-9]*|[., ][0-9]+)([eE][-+]?[0-9]+)?)'); + '-?(([0-9]+[.,]?[0-9]*|[.,][0-9]+)([eE][-+]?[0-9]+)?)'); define('NUMERICAL_ALTERNATIVE_REGEX', - '^(' . NUMBER_REGEX . ')([ ]*:' . NUMBER_REGEX . ')?$'); + '^(' . NUMBER_REGEX . ')(:' . NUMBER_REGEX . ')?$'); // Parenthesis positions for NUMERICAL_FORMATED_ALTERNATIVE_ANSWER_REGEX. define('NUMERICAL_CORRECT_ANSWER', 1); diff --git a/question/type/numerical/edit_numerical_form.php b/question/type/numerical/edit_numerical_form.php index 488e5998263..2554177cc2e 100644 --- a/question/type/numerical/edit_numerical_form.php +++ b/question/type/numerical/edit_numerical_form.php @@ -59,7 +59,7 @@ class qtype_numerical_edit_form extends question_edit_form { $tolerance = $mform->createElement('text', 'tolerance', get_string('answererror', 'qtype_numerical'), array('size' => 15)); - $repeatedoptions['tolerance']['type'] = PARAM_RAW;// This parameter will be validated in validation(). + $repeatedoptions['tolerance']['type'] = PARAM_FLOAT; $repeatedoptions['tolerance']['default'] = 0; $elements = $repeated[0]->getElements(); $elements[0]->setSize(15); @@ -293,7 +293,7 @@ class qtype_numerical_edit_form extends question_edit_form { if ($data['fraction'][$key] == 1) { $maxgrade = true; } - if ($answer !== '*' && !$this->is_valid_number($data['tolerance'][$key])) { + if ($answer !== '*' && !is_numeric($data['tolerance'][$key])) { $errors['answeroptions['.$key.']'] = get_string('xmustbenumeric', 'qtype_numerical', get_string('acceptederror', 'qtype_numerical')); diff --git a/question/type/numerical/question.php b/question/type/numerical/question.php index 2e39db1c567..8df2ce93486 100644 --- a/question/type/numerical/question.php +++ b/question/type/numerical/question.php @@ -106,6 +106,10 @@ class qtype_numerical_question extends question_graded_automatically { return false; } + if ($this->ap->contains_thousands_seaparator($response['answer'])) { + return false; + } + return true; } @@ -127,6 +131,11 @@ class qtype_numerical_question extends question_graded_automatically { return get_string('unitnotselected', 'qtype_numerical'); } + if ($this->ap->contains_thousands_seaparator($response['answer'])) { + return get_string('pleaseenteranswerwithoutthousandssep', 'qtype_numerical', + $this->ap->get_separator()); + } + return ''; } diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php index 04ca91f0a40..2d290aacb57 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -632,35 +632,17 @@ class qtype_numerical_answer_processor { // Strip spaces (which may be thousands separators) and change other forms // of writing e to e. $response = str_replace(' ', '', $response); - // Strip thousand separators like half space. - if (!in_array($this->thousandssep, array(',', '.', ' '))) { - if (strpos($response, $this->thousandssep) !== false) { - $response = str_replace($this->thousandssep, '', $response); - } - } $response = preg_replace('~(?:e|E|(?:x|\*|×)10(?:\^|\*\*))([+-]?\d+)~', 'e$1', $response); - // Dot . is mostly a decimal separator there a few exceptions where it is a thousand separator - // If a . is present or there are multiple , (i.e. 2,456,789 ) assume, - // is a thousands separator and strip it or else assume it is a decimal - // separator and change it to . - // if only one and it is , then change to . - if (substr_count($response, ',')+ substr_count($response, '.') == 1 ) { - if (strpos($response, ',') !== false) { - $response = str_replace(',', '.', $response); - } - } else if (substr_count($response, ',') == 1 && substr_count($response, '.') == 1) { - if (strpos($response, '.') > strpos($response, ',')) { // Then , is thousand. - $response = str_replace(',', '', $response); - } else { - $response = str_replace('.', '', $response); - $response = str_replace(',', '.', $response); - } - } else if (substr_count($response, ',') > 1) { - $response = str_replace(',', '', $response); - } else if (substr_count($response, '.') > 1) { - $response = str_replace('.', '', $response); - $response = str_replace(',', '.', $response); + + // If a . is present or there are multiple , (i.e. 2,456,789 ) assume , + // is a thouseands separator, and strip it, else assume it is a decimal + // separator, and change it to .. + if (strpos($response, '.') !== false || substr_count($response, ',') > 1) { + $response = str_replace(',', '', $response); + } else { + $response = str_replace(',', '.', $response); } + $regex = '[+-]?(?:\d+(?:\\.\d*)?|\\.\d+)(?:e[-+]?\d+)?'; if ($this->unitsbefore) { $regex = "/$regex$/"; diff --git a/question/type/numerical/tests/answerprocessor_test.php b/question/type/numerical/tests/answerprocessor_test.php index 000df7a8d9e..f27bb2ff890 100644 --- a/question/type/numerical/tests/answerprocessor_test.php +++ b/question/type/numerical/tests/answerprocessor_test.php @@ -131,36 +131,9 @@ class qtype_numerical_answer_processor_test extends advanced_testcase { $ap = new qtype_numerical_answer_processor(array(), false, ',', ' '); $this->assertEquals(array(-1000, '', null), $ap->apply_units('-1 000')); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units('-1 000 000')); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units('-1.000.000')); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units('-1,000,000')); - $this->assertEquals(array(-1000.1, '', null), $ap->apply_units('-1000,1')); - $this->assertEquals(array(-1000.1, '', null), $ap->apply_units('-1.000,1')); - $this->assertEquals(array(-1000.1, '', null), $ap->apply_units('-1,000.1')); $this->assertEquals(array(3.14159, '', null), $ap->apply_units('3,14159')); } - public function test_deutsch_style() { - $ap = new qtype_numerical_answer_processor(array(), false, ',', '.'); - - $this->assertEquals(array(-1000, '', null), $ap->apply_units('-1 000')); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units('-1 000 000')); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units('-1.000.000')); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units('-1.000.000,')); - $this->assertEquals(array(-1000.1, '', null), $ap->apply_units('-1000,1')); - $this->assertEquals(array(-1000.1, '', null), $ap->apply_units('-1.000,1')); - $this->assertEquals(array(-1000.1, '', null), $ap->apply_units('-1,000.1')); - $this->assertEquals(array(3.14159, '', null), $ap->apply_units('3.14159')); - $this->assertEquals(array(3.14159, '', null), $ap->apply_units('3,14159')); - } - public function test_unusual_thousandseps_style() { - // Test with ' as thousandsep as used for currency in Swizterland. - $ap = new qtype_numerical_answer_processor(array(), false, '.', "'"); - - $this->assertEquals(array(-1000, '', null), $ap->apply_units("-1'000")); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units("'-1'000'000")); - $this->assertEquals(array(-1000000, '', null), $ap->apply_units("-1'000'000.")); - } public function test_percent() { $ap = new qtype_numerical_answer_processor(array('%' => 100), false, '.', ',');