diff --git a/question/type/multianswer/edit_multianswer_form.php b/question/type/multianswer/edit_multianswer_form.php index cd792f78a1f..1eafd99ea0a 100644 --- a/question/type/multianswer/edit_multianswer_form.php +++ b/question/type/multianswer/edit_multianswer_form.php @@ -366,8 +366,12 @@ class qtype_multianswer_edit_form extends question_edit_form { } foreach ($subquestion->answer as $key => $answer) { if ($subquestion->qtype == 'numerical' && $key == 0) { - $default_values[$prefix.'tolerance['.$key.']'] = + if (isset($subquestion->tolerance[0])) { + $default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0]; + } else { + $default_values[$prefix.'tolerance['.$key.']'] = 0; + } } if (is_array($answer)) { $answer = $answer['text']; @@ -472,10 +476,11 @@ class qtype_multianswer_edit_form extends question_edit_form { $trimmedanswer = trim($answer); if ($trimmedanswer !== '') { $answercount++; - if ($subquestion->qtype == 'numerical' && - !($this->is_valid_number($trimmedanswer) || $trimmedanswer == '*')) { - $errors[$prefix.'answer['.$key.']'] = + if ($subquestion->qtype == 'numerical' && $trimmedanswer != '*') { + if (!$this->is_valid_number($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 5df9f2cce59..acd9df055bf 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 2554177cc2e..488e5998263 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_FLOAT; + $repeatedoptions['tolerance']['type'] = PARAM_RAW;// This parameter will be validated in validation(). $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 !== '*' && !is_numeric($data['tolerance'][$key])) { + if ($answer !== '*' && !$this->is_valid_number($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 8df2ce93486..2e39db1c567 100644 --- a/question/type/numerical/question.php +++ b/question/type/numerical/question.php @@ -106,10 +106,6 @@ class qtype_numerical_question extends question_graded_automatically { return false; } - if ($this->ap->contains_thousands_seaparator($response['answer'])) { - return false; - } - return true; } @@ -131,11 +127,6 @@ 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 2d290aacb57..04ca91f0a40 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -632,17 +632,35 @@ 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); - $response = preg_replace('~(?:e|E|(?:x|\*|×)10(?:\^|\*\*))([+-]?\d+)~', 'e$1', $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); + // 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); } - $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 f27bb2ff890..000df7a8d9e 100644 --- a/question/type/numerical/tests/answerprocessor_test.php +++ b/question/type/numerical/tests/answerprocessor_test.php @@ -131,9 +131,36 @@ 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, '.', ',');