MDL-31680 Correcting the format of calculated types correct response
authorppichet <pichet.pierre@uqam.ca>
Thu, 6 Jun 2013 03:44:39 +0000 (23:44 -0400)
committerppichet <pichet.pierre@uqam.ca>
Thu, 6 Jun 2013 03:44:39 +0000 (23:44 -0400)
question/type/calculated/datasetitems_form.php
question/type/calculated/question.php
question/type/calculated/questiontype.php
question/type/calculated/renderer.php
question/type/calculated/tests/helper.php
question/type/calculated/tests/question_test.php
question/type/calculatedmulti/questiontype.php
question/type/calculatedsimple/edit_calculatedsimple_form.php
question/type/calculatedsimple/questiontype.php

index 43b2c9d..d1f147c 100644 (file)
@@ -139,7 +139,7 @@ class question_dataset_dependent_items_form extends question_wizard_form {
                 $name = get_string('wildcard', 'qtype_calculated', $datasetdef->name);
             }
             $mform->addElement('text', "number[$j]", $name);
-            $mform->setType("number[$j]", PARAM_FLOAT);
+            $mform->setType("number[$j]", PARAM_RAW); // This parameter will be validated in validation().
             $this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
             $idx++;
             $mform->addElement('hidden', "definition[$j]");
@@ -161,11 +161,10 @@ class question_dataset_dependent_items_form extends question_wizard_form {
         $answers = fullclone($this->question->options->answers);
         $key1 =1;
         foreach ($answers as $key => $answer) {
-            if ('' === $answer->answer) {
-                // Do nothing.
-            } else if ('*' === $answer->answer) {
+            $ans = shorten_text($answer->answer, 17, true);
+            if ($ans === '*') {
                 $mform->addElement('static',
-                        'answercomment[' . ($this->noofitems+$key1) . ']', $answer->answer);
+                        'answercomment[' . ($this->noofitems+$key1) . ']', $ans);
                 $mform->addElement('hidden', 'tolerance['.$key.']', '');
                 $mform->setType('tolerance['.$key.']', PARAM_RAW);
                 $mform->setAdvanced('tolerance['.$key.']', true);
@@ -178,9 +177,9 @@ class question_dataset_dependent_items_form extends question_wizard_form {
                 $mform->addElement('hidden', 'correctanswerformat['.$key.']', '');
                 $mform->setType('correctanswerformat['.$key.']', PARAM_RAW);
                 $mform->setAdvanced('correctanswerformat['.$key.']', true);
-            } else {
+            } else if ( $ans !== '' ) {
                 $mform->addElement('static', 'answercomment[' . ($this->noofitems+$key1) . ']',
-                        $answer->answer);
+                        $ans);
                 $mform->addElement('text', 'tolerance['.$key.']',
                         get_string('tolerance', 'qtype_calculated'));
                 $mform->setType('tolerance['.$key.']', PARAM_RAW);
@@ -290,7 +289,7 @@ class question_dataset_dependent_items_form extends question_wizard_form {
                 } else {
                     $mform->addElement('hidden', "number[$j]" , '');
                 }
-                $mform->setType("number[$j]", PARAM_FLOAT);
+                $mform->setType("number[$j]", PARAM_RAW); // This parameter will be validated in validation().
                 $mform->addElement('hidden', "itemid[$j]");
                 $mform->setType("itemid[$j]", PARAM_INT);
 
@@ -312,11 +311,11 @@ class question_dataset_dependent_items_form extends question_wizard_form {
                             'Formulas {=..} in question text');
                     foreach ($textequations as $key => $equation) {
                         if ($formulaerrors = qtype_calculated_find_formula_errors($equation)) {
-                            $str=$formulaerrors;
+                            $str = $formulaerrors;
                         } else {
                             eval('$str = '.$equation.';');
                         }
-
+                        $equation = shorten_text($equation, 17, true);
                         $mform->addElement('static', "textequation", "{=$equation}", "=".$str);
                     }
                 }
@@ -485,19 +484,18 @@ class question_dataset_dependent_items_form extends question_wizard_form {
         $numbers = $data['number'];
         foreach ($numbers as $key => $number) {
             if (! is_numeric($number)) {
-                if (stristr($number, ', ')) {
-                    $errors['number['.$key.']'] = get_string(
-                        'The , cannot be used, use . as in 0.013 or 1.3e-2', 'qtype_calculated');
+                if (stristr($number, ',')) {
+                    $errors['number['.$key.']'] = get_string('nocommaallowed', 'qtype_calculated');
                 } else {
-                    $errors['number['.$key.']'] = get_string(
-                            'This is not a valid number', 'qtype_calculated');
+                    $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
                 }
             } else if (stristr($number, 'x')) {
-                $errors['number['.$key.']'] = get_string(
-                        'Hexadecimal format (i.e. 0X12d) is not allowed', 'qtype_calculated');
+                $a = new stdClass();
+                $a->name = '';
+                $a->value = $number;
+                $errors['number['.$key.']'] = get_string('hexanotallowed', 'qtype_calculated', $a);
             } else if (is_nan($number)) {
-                $errors['number['.$key.']'] = get_string(
-                        'is a NAN number', 'qtype_calculated');
+                $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
             }
         }
         return $errors;
index b324685..71b25a6 100644 (file)
@@ -86,6 +86,25 @@ class qtype_calculated_question extends qtype_numerical_question
             return parent::get_variants_selection_seed();
         }
     }
+
+    public function get_correct_response() {
+        $answer = $this->get_correct_answer();
+        if (!$answer) {
+            return array();
+        }
+
+        $response = array('answer' => $this->vs->format_float($answer->answer,
+            $answer->correctanswerlength, $answer->correctanswerformat));
+
+        if ($this->has_separate_unit_field()) {
+            $response['unit'] = $this->ap->get_default_unit();
+        } else if ($this->unitdisplay == qtype_numerical::UNITINPUT) {
+            $response['answer'] = $this->ap->add_unit($response['answer']);
+        }
+
+        return $response;
+    }
+
 }
 
 
@@ -318,9 +337,67 @@ class qtype_calculated_variable_substituter {
             if ($format == '1' ) { // Answer is to have $length decimals.
                 // Decimal places.
                 $x = sprintf('%.' . $length . 'F', $x);
-            } else if ($format == 2) {
-                // Significant figures.
-                $x = sprintf('%.' . $length . 'g', $x);
+
+            } else if ($x) { // Significant figures does only apply if the result is non-zero.
+                $answer = $x;
+                // Convert to positive answer.
+                if ($answer < 0) {
+                    $answer = -$answer;
+                    $sign = '-';
+                } else {
+                    $sign = '';
+                }
+
+                // Determine the format 0.[1-9][0-9]* for the answer...
+                $p10 = 0;
+                while ($answer < 1) {
+                    --$p10;
+                    $answer *= 10;
+                }
+                while ($answer >= 1) {
+                    ++$p10;
+                    $answer /= 10;
+                }
+                // ... and have the answer rounded of to the correct length.
+                $answer = round($answer, $length);
+
+                // If we rounded up to 1.0, place the answer back into 0.[1-9][0-9]* format.
+                if ($answer >= 1) {
+                    ++$p10;
+                    $answer /= 10;
+                }
+
+                // Have the answer written on a suitable format.
+                // Either scientific or plain numeric.
+                if (-2 > $p10 || 4 < $p10) {
+                    // Use scientific format.
+                    $exponent = 'e'.--$p10;
+                    $answer *= 10;
+                    if (1 == $length) {
+                        $x = $sign.$answer.$exponent;
+                    } else {
+                        // Attach additional zeros at the end of $answer.
+                        $answer .= (1 == strlen($answer) ? '.' : '')
+                            . '00000000000000000000000000000000000000000x';
+                        $x = $sign
+                            .substr($answer, 0, $length +1).$exponent;
+                    }
+                } else {
+                    // Stick to plain numeric format.
+                    $answer *= "1e$p10";
+                    if (0.1 <= $answer / "1e$length") {
+                        $x = $sign.$answer;
+                    } else {
+                        // Could be an idea to add some zeros here.
+                        $answer .= (preg_match('~^[0-9]*$~', $answer) ? '.' : '')
+                            . '00000000000000000000000000000000000000000x';
+                        $oklen = $length + ($p10 < 1 ? 2-$p10 : 1);
+                        $x = $sign.substr($answer, 0, $oklen);
+                    }
+                }
+
+            } else {
+                $x = 0.0;
             }
         }
         return str_replace('.', $this->decimalpoint, $x);
index 3e9143e..75a047e 100644 (file)
@@ -1032,11 +1032,8 @@ class qtype_calculated extends question_type {
         $answers = $question->options->answers;
 
         foreach ($answers as $key => $answer) {
-            if (is_string($answer)) {
-                $strheader .= $delimiter.$answer;
-            } else {
-                $strheader .= $delimiter.$answer->answer;
-            }
+            $ans = shorten_text($answer->answer, 17, true);
+            $strheader .= $delimiter.$ans;
             $delimiter = '<br/><br/><br/>';
         }
         return $strheader;
@@ -1083,10 +1080,11 @@ class qtype_calculated extends question_type {
                 $comment->stranswers[$key] = $formula . ' = ' .
                         get_string('anyvalue', 'qtype_calculated') . '<br/><br/><br/>';
             } else {
+                $formula = shorten_text($formula, 57, true);
                 $comment->stranswers[$key] = $formula . ' = ' . $formattedanswer->answer . '<br/>';
                 $correcttrue = new stdClass();
                 $correcttrue->correct = $formattedanswer->answer;
-                $correcttrue->true = $answer->answer;
+                $correcttrue->true = '';
                 if ($formattedanswer->answer < $answer->min ||
                         $formattedanswer->answer > $answer->max) {
                     $comment->outsidelimit = true;
@@ -1106,57 +1104,6 @@ class qtype_calculated extends question_type {
         }
         return fullclone($comment);
     }
-    public function multichoice_comment_on_datasetitems($questionid, $questiontext,
-            $answers, $data, $number) {
-        global $DB;
-        $comment = new stdClass();
-        $comment->stranswers = array();
-        $comment->outsidelimit = false;
-        $comment->answers = array();
-        // Find a default unit.
-        if (!empty($questionid) && $unit = $DB->get_record('question_numerical_units',
-                array('question' => $questionid, 'multiplier' => 1.0))) {
-            $unit = $unit->unit;
-        } else {
-            $unit = '';
-        }
-
-        $answers = fullclone($answers);
-        $errors = '';
-        $delimiter = ': ';
-        foreach ($answers as $key => $answer) {
-            $answer->answer = $this->substitute_variables($answer->answer, $data);
-            // Evaluate the equations i.e {=5+4).
-            $qtext = '';
-            $qtextremaining = $answer->answer;
-            while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-                $qtextsplits = explode($regs1[0], $qtextremaining, 2);
-                $qtext = $qtext.$qtextsplits[0];
-                $qtextremaining = $qtextsplits[1];
-                if (empty($regs1[1])) {
-                    $str = '';
-                } else {
-                    if ($formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
-                        $str = $formulaerrors;
-                    } else {
-                        eval('$str = '.$regs1[1].';');
-
-                        $texteval= qtype_calculated_calculate_answer(
-                            $str, $data, $answer->tolerance,
-                            $answer->tolerancetype, $answer->correctanswerlength,
-                            $answer->correctanswerformat, '');
-                        $str = $texteval->answer;
-
-                    }
-                }
-                $qtext = $qtext.$str;
-            }
-            $answer->answer = $qtext.$qtextremaining;
-            $comment->stranswers[$key]= $answer->answer;
-
-        }
-        return fullclone($comment);
-    }
 
     public function tolerance_types() {
         return array(
@@ -1878,32 +1825,9 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
         $calculated->answer = NAN;
         return $calculated;
     }
-    if ('1' == $answerformat) { /* Answer is to have $answerlength decimals */
-        /*** Adjust to the correct number of decimals ***/
-        if (stripos($answer, 'e')>0) {
-            $answerlengthadd = strlen($answer)-stripos($answer, 'e');
-        } else {
-            $answerlengthadd = 0;
-        }
-        $calculated->answer = round(floatval($answer), $answerlength+$answerlengthadd);
-
-        if ($answerlength) {
-            /* Try to include missing zeros at the end */
-
-            if (preg_match('~^(.*\\.)(.*)$~', $calculated->answer, $regs)) {
-                $calculated->answer = $regs[1] . substr(
-                    $regs[2] . '00000000000000000000000000000000000000000x',
-                    0, $answerlength)
-                    . $unit;
-            } else {
-                $calculated->answer .=
-                    substr('.00000000000000000000000000000000000000000x',
-                        0, $answerlength + 1) . $unit;
-            }
-        } else {
-            /* Attach unit */
-            $calculated->answer .= $unit;
-        }
+    if ('1' == $answerformat) { // Answer is to have $answerlength decimals.
+        // Decimal places.
+        $calculated->answer = sprintf('%.' . $answerlength . 'F', $answer);
 
     } else if ($answer) { // Significant figures does only apply if the result is non-zero.
 
@@ -1941,31 +1865,34 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
             $exponent = 'e'.--$p10;
             $answer *= 10;
             if (1 == $answerlength) {
-                $calculated->answer = $sign.$answer.$exponent.$unit;
+                $calculated->answer = $sign.$answer.$exponent;
             } else {
                 // Attach additional zeros at the end of $answer.
                 $answer .= (1 == strlen($answer) ? '.' : '')
                     . '00000000000000000000000000000000000000000x';
                 $calculated->answer = $sign
-                    .substr($answer, 0, $answerlength +1).$exponent.$unit;
+                    .substr($answer, 0, $answerlength +1).$exponent;
             }
         } else {
             // Stick to plain numeric format.
             $answer *= "1e$p10";
             if (0.1 <= $answer / "1e$answerlength") {
-                $calculated->answer = $sign.$answer.$unit;
+                $calculated->answer = $sign.$answer;
             } else {
                 // Could be an idea to add some zeros here.
                 $answer .= (preg_match('~^[0-9]*$~', $answer) ? '.' : '')
                     . '00000000000000000000000000000000000000000x';
                 $oklen = $answerlength + ($p10 < 1 ? 2-$p10 : 1);
-                $calculated->answer = $sign.substr($answer, 0, $oklen).$unit;
+                $calculated->answer = $sign.substr($answer, 0, $oklen);
             }
         }
 
     } else {
         $calculated->answer = 0.0;
     }
+    if ($unit != '') {
+            $calculated->answer = $calculated->answer . ' ' . $unit;
+    }
 
     // Return the result.
     return $calculated;
index 28a2313..930551b 100644 (file)
@@ -36,4 +36,19 @@ require_once($CFG->dirroot . '/question/type/numerical/renderer.php');
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_calculated_renderer extends qtype_numerical_renderer {
+    public function correct_response(question_attempt $qa) {
+        $question = $qa->get_question();
+        $answer = $question->get_correct_response();
+        if (!$answer) {
+            return '';
+        }
+
+        $response = $answer['answer'];
+        if ($question->unitdisplay != qtype_numerical::UNITNONE && $question->unitdisplay != qtype_numerical::UNITINPUT) {
+            $response = $question->ap->add_unit($response);
+        }
+
+        return get_string('correctansweris', 'qtype_shortanswer', $response);
+    }
+
 }
index a7a821f..8910ad4 100644 (file)
@@ -28,6 +28,8 @@ defined('MOODLE_INTERNAL') || die();
 
 global $CFG;
 require_once($CFG->dirroot . '/question/type/calculated/question.php');
+require_once($CFG->dirroot . '/question/type/numerical/question.php');
+require_once($CFG->dirroot . '/question/type/numerical/questiontype.php');
 require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
 
 
@@ -75,6 +77,8 @@ class qtype_calculated_test_helper extends question_test_helper {
         $q->datasetloader = new qtype_calculated_test_dataset_loader(0, array(
             array('a' => 1, 'b' => 5),
             array('a' => 3, 'b' => 4),
+            array('a' => 3, 'b' => 0.01416),
+            array('a' => 31, 'b' => 0.01416),
         ));
 
         return $q;
index 43e1a41..924f62e 100644 (file)
@@ -67,12 +67,41 @@ class qtype_calculated_question_test extends advanced_testcase {
     }
 
     public function test_get_correct_response() {
+        // Testing with 3.0 + 0.1416.
+        $question = test_question_maker::make_question('calculated');
+        $question->start_attempt(new question_attempt_step(), 3);
+        $values = $question->vs->get_values();
+        $this->assertSame(array('answer' => '3.01' ), $question->get_correct_response());
+        foreach ($question->answers as $answer) {
+            $answer->correctanswerlength = 2;
+            $answer->correctanswerformat = 2;
+        }
+        $this->assertSame(array('answer' => '3.0' ), $question->get_correct_response());
+
+        // Testing with 1.0 + 5.0.
         $question = test_question_maker::make_question('calculated');
         $question->start_attempt(new question_attempt_step(), 1);
         $values = $question->vs->get_values();
+        $this->assertSame(array('answer' => '6.00' ), $question->get_correct_response());
 
-        $this->assertEquals(array('answer' => $values['a'] + $values['b']),
+        foreach ($question->answers as $answer) {
+            $answer->correctanswerlength = 2;
+            $answer->correctanswerformat = 2;
+        }
+        $this->assertSame(array('answer' => '6.0' ),
                 $question->get_correct_response());
+        // Testing with 31.0 + 0.01416 .
+        $question = test_question_maker::make_question('calculated');
+        $question->start_attempt(new question_attempt_step(), 4);
+        $values = $question->vs->get_values();
+        $this->assertSame(array('answer' => '31.01' ), $question->get_correct_response());
+
+        foreach ($question->answers as $answer) {
+            $answer->correctanswerlength = 3;
+            $answer->correctanswerformat = 2;
+        }
+        $this->assertSame(array('answer' => '31.0' ), $question->get_correct_response());
+
     }
 
     public function test_get_question_summary() {
index 2235145..3522e0d 100644 (file)
@@ -197,12 +197,9 @@ class qtype_calculatedmulti extends qtype_calculated {
         $answers = $question->options->answers;
 
         foreach ($answers as $key => $answer) {
-            if (is_string($answer)) {
-                $strheader .= $delimiter.$answer;
-            } else {
-                $strheader .= $delimiter.$answer->answer;
-            }
-            $delimiter = '<br/>';
+            $ans = shorten_text($answer->answer, 17, true);
+            $strheader .= $delimiter.$ans;
+            $delimiter = '<br/><br/>';
         }
         return $strheader;
     }
@@ -219,14 +216,14 @@ class qtype_calculatedmulti extends qtype_calculated {
         $errors = '';
         $delimiter = ': ';
         foreach ($answers as $key => $answer) {
-            $answer->answer = $this->substitute_variables($answer->answer, $data);
+            $anssubstituted = $this->substitute_variables($answer->answer, $data);
             // Evaluate the equations i.e {=5+4).
-            $qtext = '';
-            $qtextremaining = $answer->answer;
-            while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-                $qtextsplits = explode($regs1[0], $qtextremaining, 2);
-                $qtext =$qtext.$qtextsplits[0];
-                $qtextremaining = $qtextsplits[1];
+            $anstext = '';
+            $anstextremaining = $anssubstituted;
+            while (preg_match('~\{=([^[:space:]}]*)}~', $anstextremaining, $regs1)) {
+                $anstextsplits = explode($regs1[0], $anstextremaining, 2);
+                $anstext =$anstext.$anstextsplits[0];
+                $anstextremaining = $anstextsplits[1];
                 if (empty($regs1[1])) {
                     $str = '';
                 } else {
@@ -236,10 +233,10 @@ class qtype_calculatedmulti extends qtype_calculated {
                         eval('$str = '.$regs1[1].';');
                     }
                 }
-                $qtext = $qtext.$str;
+                $anstext = $anstext.$str;
             }
-            $answer->answer = $qtext.$qtextremaining;
-            $comment->stranswers[$key] = $answer->answer;
+            $anstext .= $anstextremaining;
+            $comment->stranswers[$key] = $anssubstituted.'<br/>'.$anstext;
         }
         return fullclone($comment);
     }
index 3613d8e..298547d 100644 (file)
@@ -255,7 +255,7 @@ class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form {
                     !($datasettoremove ||$newdataset ||$newdatasetvalues)) {
                 $i = 1;
                 $fromformdefinition = optional_param_array('definition', '', PARAM_NOTAGS);
-                $fromformnumber = optional_param_array('number', '', PARAM_INT);
+                $fromformnumber = optional_param_array('number', '', PARAM_RAW);// This parameter will be validated in the form.
                 $fromformitemid = optional_param_array('itemid', '', PARAM_INT);
                 ksort($fromformdefinition);
 
@@ -508,7 +508,7 @@ class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form {
                             $mform->addElement('hidden', "number[$j]", get_string(
                                     'wildcard', 'qtype_calculatedsimple', $datasetdef->name));
                         }
-                        $mform->setType("number[$j]", PARAM_FLOAT);
+                        $mform->setType("number[$j]", PARAM_RAW); // This parameter will be validated in validation().
 
                         $mform->addElement('hidden', "itemid[$j]");
                         $mform->setType("itemid[$j]", PARAM_INT);
@@ -585,6 +585,23 @@ class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form {
 
     public function validation($data, $files) {
         $errors = parent::validation($data, $files);
+        $numbers = $data['number'];
+        foreach ($numbers as $key => $number) {
+            if (! is_numeric($number)) {
+                if (stristr($number, ',')) {
+                    $errors['number['.$key.']'] = get_string('nocommaallowed', 'qtype_calculated');
+                } else {
+                    $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
+                }
+            } else if (stristr($number, 'x')) {
+                $a = new stdClass();
+                $a->name = '';
+                $a->value = $number;
+                $errors['number['.$key.']'] = get_string('hexanotallowed', 'qtype_calculated', $a);
+            } else if (is_nan($number)) {
+                $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
+            }
+        }
 
         if (empty($data['definition'])) {
             $errors['selectadd'] = get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple');
index c98f307..8a0a264 100644 (file)
@@ -267,7 +267,8 @@ class qtype_calculatedsimple extends qtype_calculated {
         $delimiter = '';
 
         foreach ($answers as $key => $answer) {
-            $strheader .= $delimiter.$answer->answer;
+            $ans = shorten_text($answer->answer, 17, true);
+            $strheader .= $delimiter.$ans;
             $delimiter = '<br/><br/><br/>';
         }
         return $strheader;