Beispiel #1
0
 /**
  * Validate the formula.
  * @param string $formula
  * @return boolean true if calculation possible, false otherwise
  */
 function validate_formula($formulastr)
 {
     global $CFG;
     require_once $CFG->libdir . '/mathslib.php';
     $formulastr = grade_item::normalize_formula($formulastr, $this->courseid);
     if (empty($formulastr)) {
         return true;
     }
     if (strpos($formulastr, '=') !== 0) {
         return get_string('errorcalculationnoequal', 'grades');
     }
     // get used items
     if (preg_match_all('/##gi(\\d+)##/', $formulastr, $matches)) {
         $useditems = array_unique($matches[1]);
         // remove duplicates
     } else {
         $useditems = array();
     }
     // MDL-11902
     // unset the value if formula is trying to reference to itself
     // but array keys does not match itemid
     if (!empty($this->id)) {
         $useditems = array_diff($useditems, array($this->id));
         //unset($useditems[$this->id]);
     }
     // prepare formula and init maths library
     $formula = preg_replace('/##(gi\\d+)##/', '\\1', $formulastr);
     $formula = new calc_formula($formula);
     if (empty($useditems)) {
         $grade_items = array();
     } else {
         $gis = implode(',', $useditems);
         $sql = "SELECT gi.*\n                      FROM {$CFG->prefix}grade_items gi\n                     WHERE gi.id IN ({$gis}) and gi.courseid={$this->courseid}";
         // from the same course only!
         if (!($grade_items = get_records_sql($sql))) {
             $grade_items = array();
         }
     }
     $params = array();
     foreach ($useditems as $itemid) {
         // make sure all grade items exist in this course
         if (!array_key_exists($itemid, $grade_items)) {
             return false;
         }
         // use max grade when testing formula, this should be ok in 99.9%
         // division by 0 is one of possible problems
         $params['gi' . $grade_items[$itemid]->id] = $grade_items[$itemid]->grademax;
     }
     // do the calculation
     $formula->set_params($params);
     $result = $formula->evaluate();
     // false as result indicates some problem
     if ($result === false) {
         // TODO: add more error hints
         return get_string('errorcalculationunknown', 'grades');
     } else {
         return true;
     }
 }
Beispiel #2
0
 public function test_scientific_notation()
 {
     $formula = new calc_formula('=10e10');
     $this->assertWithinMargin($formula->evaluate(), 100000000000.0, 100000000000.0 * 1.0E-15);
     $formula = new calc_formula('=10e-10');
     $this->assertWithinMargin($formula->evaluate(), 1.0E-9, 100000000000.0 * 1.0E-15);
     $formula = new calc_formula('=10e+10');
     $this->assertWithinMargin($formula->evaluate(), 100000000000.0, 100000000000.0 * 1.0E-15);
     $formula = new calc_formula('=10e10*5');
     $this->assertWithinMargin($formula->evaluate(), 500000000000.0, 100000000000.0 * 1.0E-15);
     $formula = new calc_formula('=10e10^2');
     $this->assertWithinMargin($formula->evaluate(), 1.0E+22, 1.0E+22 * 1.0E-15);
 }
Beispiel #3
0
 public function test_rand_float()
 {
     $formula = new calc_formula('=rand_float()');
     $result = $formula->evaluate();
     $this->assertTrue(is_float($result));
 }
Beispiel #4
0
 /**
  * Tests special chars
  */
 function test__specialchars()
 {
     $formula = new calc_formula('=gi1 + gi2 + gi11', array('gi1' => 10, 'gi2' => 20, 'gi11' => 30));
     $res = $formula->evaluate();
     $this->assertEqual($res, 60, 'sum is: %s');
 }
 /**
  *
  */
 protected function process_calculations($text)
 {
     global $CFG;
     // HACK removing occurences of [[entryid]] because they are
     // currently not resolved in new entries.
     $text = str_replace('[[entryid]]', '', $text);
     if (preg_match_all("/%%F\\d*:=[^%]*%%/", $text, $matches)) {
         require_once "{$CFG->libdir}/mathslib.php";
         sort($matches[0]);
         // List of formulas.
         $formulas = array();
         // Formula replacements.
         $replacements = array();
         // Register all formulas according to formula identifier.
         foreach ($matches[0] as $pattern) {
             $cleanpattern = trim($pattern, '%');
             list($fid, $formula) = explode(':=', $cleanpattern, 2);
             // Skip an empty formula.
             if (empty($formula) and $formula !== 0) {
                 continue;
             }
             isset($formulas[$fid]) or $formulas[$fid] = array();
             // Enclose formula in brackets to preserve precedence.
             $formulas[$fid][] = "({$formula})";
             $replacements[$pattern] = $formula;
         }
         // Process group formulas in formulas (e.g. _F1_).
         // We put 0 if we can't find a replacement so that the formula could be calculated.
         foreach ($formulas as $fid => $formulae) {
             foreach ($formulae as $key => $formula) {
                 if (preg_match_all("/_F\\d+_/", $formula, $frefs)) {
                     foreach ($frefs[0] as $fref) {
                         $fref = trim($fref, '_');
                         if (isset($formulas[$fref])) {
                             $replace = implode(',', $formulas[$fref]);
                         } else {
                             $replace = 0;
                         }
                         $formula = str_replace("_{$fref}_", $replace, $formula);
                     }
                     $formulae[$key] = $formula;
                 }
             }
             $formulas[$fid] = $formulae;
         }
         // Process group formulas in replacements (e.g. _F1_).
         // We put 0 if we can't find a replacement so that the formula could be calculated.
         foreach ($replacements as $pattern => $formula) {
             if (preg_match_all("/_F\\d+_/", $formula, $frefs)) {
                 foreach ($frefs[0] as $fref) {
                     $fref = trim($fref, '_');
                     if (isset($formulas[$fref])) {
                         $replace = implode(',', $formulas[$fref]);
                     } else {
                         $replace = 0;
                     }
                     $formula = str_replace("_{$fref}_", $replace, $formula);
                 }
                 $replacements[$pattern] = $formula;
             }
         }
         // Calculate.
         foreach ($replacements as $pattern => $formula) {
             // Number of decimals can be set as ;n at the end of the formula.
             $decimals = null;
             if (strpos($formula, ';')) {
                 list($formula, $decimals) = explode(';', $formula);
             }
             $calc = new \calc_formula("={$formula}");
             $result = $calc->evaluate();
             if ($result === false) {
                 // False as result indicates some problem.
                 // We remove the formula altogether.
                 $replacements[$pattern] = '';
             } else {
                 // Set decimals.
                 if (is_numeric($decimals)) {
                     $result = sprintf("%4.{$decimals}f", $result);
                 }
                 $replacements[$pattern] = $result;
             }
         }
         $text = str_replace(array_keys($replacements), $replacements, $text);
     }
     return $text;
 }
 /**
  * Returns user's calculated grades for the specified grade item.
  *
  * @param grade_item $gradeitem
  * @param array $userids The user ids whose grades should be retrieved.
  * @return array|null
  */
 protected function get_user_grades_calculated($gradeitem, array $userids)
 {
     global $CFG;
     if (!($df = $this->df)) {
         return null;
     }
     require_once "{$CFG->libdir}/mathslib.php";
     $formula = $gradeitem->gradecalc;
     // Patterns container.
     $patterns = array();
     // Users container.
     $users = array_fill_keys($userids, array());
     // Grades container.
     $grades = array();
     foreach ($users as $userid => $unused) {
         $grades[$userid] = (object) array('id' => $userid, 'userid' => $userid, 'rawgrade' => null);
         // Num entries pattern.
         if (strpos($formula, '##numentries##') !== false) {
             $patterns['##numentries##'] = 0;
             if ($numentries = $df->get_entries_count_per_user($df::COUNT_ALL, $userid)) {
                 foreach ($numentries as $userid => $count) {
                     $users[$userid]['##numentries##'] = $count->numentries;
                 }
             }
         }
         // Extract grading field patterns from the formula.
         if (preg_match_all("/##\\d*:[^#]+##/", $formula, $matches)) {
             // Get the entry ids per user.
             $entryids = $df->get_entry_ids_per_user($userid);
             foreach ($matches[0] as $pattern) {
                 $patterns[$pattern] = 0;
                 list($targetval, $fieldpattern) = explode(':', trim($pattern, '#'), 2);
                 // Get the field from the pattern.
                 if (!($field = $df->field_manager->get_field_by_pattern("[[{$fieldpattern}]]"))) {
                     continue;
                 }
                 $uservalues = null;
                 // Get user values for the pattern.
                 // The field must either has helper\contentperuser component,
                 // or be an instance of interface grading.
                 $helper = "dataformfield_{$field->type}\\helper\\contentperuser";
                 if (class_exists($helper)) {
                     $uservalues = $helper::get_content($field, $fieldpattern, $entryids);
                 } else {
                     if ($field instanceof mod_dataform\interfaces\grading) {
                         // BC - this method for grading user values is depracated.
                         $uservalues = $field->get_user_values($fieldpattern, $entryids, $userid);
                     }
                 }
                 // Leave pattern value at 0 if no user values.
                 if (!$uservalues) {
                     continue;
                 }
                 // Register pattern values for users.
                 foreach ($uservalues as $userid => $values) {
                     // Keep only target val if specified.
                     if ($targetval) {
                         foreach ($values as $key => $value) {
                             if ($value != $targetval) {
                                 unset($values[$key]);
                             }
                         }
                     }
                     if ($values) {
                         $users[$userid][$pattern] = implode(',', $values);
                     }
                 }
             }
         }
     }
     // For each user calculate the formula and create a grade object.
     foreach ($grades as $userid => $grade) {
         // If no values, no grade for this user.
         if (empty($users[$userid])) {
             continue;
         }
         $values = $users[$userid];
         $replacements = array_merge($patterns, $values);
         $calculation = str_replace(array_keys($replacements), $replacements, $formula);
         $calc = new calc_formula("={$calculation}");
         $result = $calc->evaluate();
         // False as result indicates some problem.
         if ($result !== false) {
             $grade->rawgrade = $result;
         }
     }
     return $grades;
 }
Beispiel #7
0
    public function test_scientific_notation() {
        $formula = new calc_formula('=10e10');
        $this->assertEquals($formula->evaluate(), 1e11, '', 1e11*1e-15);

        $formula = new calc_formula('=10e-10');
        $this->assertEquals($formula->evaluate(), 1e-9, '', 1e11*1e-15);

        $formula = new calc_formula('=10e+10');
        $this->assertEquals($formula->evaluate(), 1e11, '', 1e11*1e-15);

        $formula = new calc_formula('=10e10*5');
        $this->assertEquals($formula->evaluate(), 5e11, '', 1e11*1e-15);

        $formula = new calc_formula('=10e10^2');
        $this->assertEquals($formula->evaluate(), 1e22, '', 1e22*1e-15);

    }