]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-20296 Units handling for numerical and calculatedquestions
authorpichetp <pichetp>
Wed, 7 Oct 2009 20:50:38 +0000 (20:50 +0000)
committerpichetp <pichetp>
Wed, 7 Oct 2009 20:50:38 +0000 (20:50 +0000)
commented echo diagnostic lines as has been left for the testing period.

question/type/calculated/questiontype.php
question/type/numerical/questiontype.php
question/type/shortanswer/questiontype.php

index bbad745c9e6e22185e41f4336645d3f4d8e7890c..2c62d7acf71d20214d997990724fd4bbb12b463f 100644 (file)
@@ -1,3 +1,4 @@
+
 <?php  // $Id$
 
 /////////////////
@@ -28,7 +29,7 @@ class question_calculated_qtype extends default_questiontype {
 
     function get_question_options(&$question) {
         // First get the datasets and default options
-         global $CFG, $DB, $OUTPUT,$QTYPES;
+         global $CFG, $DB, $OUTPUT, $QTYPES;
         if (!$question->options = $DB->get_record('question_calculated_options', array('question' => $question->id))) {
           //  echo $OUTPUT->notification('Error: Missing question options for calculated question'.$question->id.'!');
           //  return false;
@@ -36,6 +37,14 @@ class question_calculated_qtype extends default_questiontype {
           $question->options->multichoice = 0;
           
         }
+    //    echo "<p> questionoptions <pre>";print_r($question);echo "</pre></p>";
+        $QTYPES['numerical']->get_numerical_options($question);
+         /* $question->options->unitgradingtype = 0;
+          $question->options->unitpenalty = 0;
+          $question->options->showunits = 0 ;
+          $question->options->unitsleft = 0 ;
+          $question->options->instructions = '' ;*/
+   //     echo "<p> questionoptions <pre>";print_r($question);echo "</pre></p>";
 
         if (!$question->options->answers = $DB->get_records_sql(
                                 "SELECT a.*, c.tolerance, c.tolerancetype, c.correctanswerlength, c.correctanswerformat " .
@@ -318,12 +327,14 @@ class question_calculated_qtype extends default_questiontype {
         $state->options->dataset =
          $this->pick_question_dataset($question,$state->options->datasetitem);
         $state->responses = array('' => $regs[2]);
-        if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
-                   $virtualqtype = $this->get_virtual_qtype( $question);
+        $virtualqtype = $this->get_virtual_qtype( $question);
+   //     if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
+                   
 
             return $virtualqtype->restore_session_and_responses($question, $state);
-        }
-        return true;
+      //  }else { // numerical
+            
+      //  return true;
     }
 
     function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
@@ -364,7 +375,7 @@ class question_calculated_qtype extends default_questiontype {
         };  
         $state->options->dataset =
          $this->pick_question_dataset($question,$state->options->datasetitem);
-        $state->responses = array('' => '');
+      //  $state->responses = array('' => '');
         if ($question->options->multichoice == 1 ) {
                     // create an array of answerids ??? why so complicated ???
             $answerids = array_values(array_map(create_function('$val',
@@ -380,8 +391,12 @@ class question_calculated_qtype extends default_questiontype {
             } else {
                 $state->responses = array();
             }
-        }                
-        return true;
+            return true;
+        } else { // numerical   
+                    $virtualqtype = $this->get_virtual_qtype( $question);
+            return $virtualqtype->create_session_and_responses($question, $state, $cmoptions, $attempt); 
+        }
+        
     }
     
     function save_session_and_responses(&$question, &$state) {
@@ -402,7 +417,15 @@ class question_calculated_qtype extends default_questiontype {
             $responses .= implode(',', $state->responses);
         }else {
         // regular numeric type 
-        $responses .=  $state->responses[''];
+         if(isset($state->responses['unit']) && isset($question->options->units[$state->responses['unit']])){
+            $responses .= $state->responses['answer'].'|||||'.$question->options->units[$state->responses['unit']]->unit;
+        }else if(isset($state->responses['unit'])){
+            $responses .= $state->responses['answer'].'|||||'.$state->responses['unit'] ;
+        }else {
+            $responses .= $state->responses['answer'].'|||||';
+        }
+
+        
         }
          
         // Set the legacy answer field        
@@ -732,6 +755,15 @@ class question_calculated_qtype extends default_questiontype {
         $DB->delete_records("question_datasets", array("question" => $questionid));
         return true;
     }
+    function test_response(&$question, &$state, $answer) {
+          $virtualqtype = $this->get_virtual_qtype( $question);
+        return $virtualqtype->test_response($question, $state, $answer);       
+            
+    }
+    function compare_responses(&$question, $state, $teststate) {
+                 $virtualqtype = $this->get_virtual_qtype( $question);
+        return $virtualqtype->compare_responses($question, $state, $teststate);
+    }
 
     function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
         // Substitute variables in questiontext before giving the data to the
@@ -1897,9 +1929,9 @@ class question_calculated_qtype extends default_questiontype {
                     fwrite ($bf,full_tag("ANSWERNUMBERING",$level+1,false,$calculated_option->answernumbering));
                     $status = fwrite ($bf,end_tag("CALCULATED_OPTIONS",$level,true));
                 }
-                //Now print question_answers
-                $status = question_backup_answers($bf,$preferences,$question);
             }
+            $status = question_backup_numerical_options($bf,$preferences,$question,$level);
+
         }
         return $status;
     }
@@ -1993,7 +2025,7 @@ class question_calculated_qtype extends default_questiontype {
             }
             //Now restore numerical_units
             $status = question_restore_numerical_units ($old_question_id,$new_question_id,$cal_info,$restore);
-
+            $status = question_restore_numerical_options($old_question_id,$new_question_id,$info,$restore);
             //Now restore dataset_definitions
             if ($status && $newid) {
                 $status = question_restore_dataset_definitions ($old_question_id,$new_question_id,$cal_info,$restore);
@@ -2201,21 +2233,21 @@ function qtype_calculated_find_formula_errors($formula) {
             case 'is_infinite': case 'is_nan': case 'log10': case 'log1p':
             case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt':
             case 'tan': case 'tanh':
-                if ($regs[4] || empty($regs[3])) {
+                if (!empty($regs[4]) || empty($regs[3])) {
                     return get_string('functiontakesonearg','quiz',$regs[2]);
                 }
                 break;
 
             // Functions that take one or two arguments
             case 'log': case 'round':
-                if ($regs[5] || empty($regs[3])) {
+                if (!empty($regs[5]) || empty($regs[3])) {
                     return get_string('functiontakesoneortwoargs','quiz',$regs[2]);
                 }
                 break;
 
             // Functions that must have two arguments
             case 'atan2': case 'fmod': case 'pow':
-                if ($regs[5] || empty($regs[4])) {
+                if (!empty($regs[5]) || empty($regs[4])) {
                     return get_string('functiontakestwoargs', 'quiz', $regs[2]);
                 }
                 break;
index e1ae906fcf722f1a0e5a25bc0168d6681a7e406f..2deeec97c08a1d429cfffea07eedd0c38682f0d2 100644 (file)
@@ -22,6 +22,11 @@ require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
  */
 class question_numerical_qtype extends question_shortanswer_qtype {
 
+    public $virtualqtype = false;
+    public $unitpenalty = 0;
+    public $raw_unitgrade = 0 ;
+    public $raw_unitpenalty = 0 ;
+    public $valid_numerical_unit = true ;
     function name() {
         return 'numerical';
     }
@@ -40,6 +45,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         //       the question table as is usually the case for qtype
         //       specific tables.
         global $CFG, $DB, $OUTPUT;
+        $this->get_numerical_options($question);
         if (!$question->options->answers = $DB->get_records_sql(
                                 "SELECT a.*, n.tolerance " .
                                 "FROM {question_answers} a, " .
@@ -64,9 +70,30 @@ class question_numerical_qtype extends question_shortanswer_qtype {
                 }
             }
         }
+
+        return true;
+    }
+    function get_numerical_options(&$question) {
+        global $DB;
+                if (!$options = $DB->get_record('question_numerical_options', array('question' => $question->id))) {
+          //  echo $OUTPUT->notification('Error: Missing question options for numerical question'.$question->id.'!');
+          //  return false;
+          $question->options->unitgradingtype = 0;
+          $question->options->unitpenalty = 0;
+          $question->options->showunits = 0 ;
+          $question->options->unitsleft = 0 ;
+          $question->options->instructions = '' ;
+        } else {
+          $question->options->unitgradingtype = $options->unitgradingtype;
+          $question->options->unitpenalty = $options->unitpenalty;
+          $question->options->showunits = $options->showunits ;
+          $question->options->unitsleft = $options->unitsleft ;
+          $question->options->instructions = $options->instructions ;
+        }
+            
+        
         return true;
     }
-
     function get_numerical_units(&$question) {
         global $DB;
         if ($units = $DB->get_records('question_numerical_units', array('question' => $question->id), 'id ASC')) {
@@ -177,7 +204,10 @@ class question_numerical_qtype extends question_shortanswer_qtype {
                 $DB->delete_records('question_numerical', array('id' => $oo->id));
             }
         }
-
+        $result = $this->save_numerical_options($question);
+        if (isset($result->error)) {
+            return $result;
+        }
         // Report any problems.
         if (!empty($result->notice)) {
             return $result;
@@ -185,6 +215,56 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         return true;
     }
 
+    function save_numerical_options(&$question) {
+        global $DB;
+        $result = new stdClass;
+        // numerical options
+        $update = true ; 
+        $options = $DB->get_record("question_numerical_options", array("question" => $question->id));
+        if (!$options) {
+            $update = false;
+            $options = new stdClass;
+            $options->question = $question->id;
+        }
+        if(isset($question->unitgradingtype)){
+            $options->unitgradingtype = $question->unitgradingtype;
+        }else {
+            $options->unitgradingtype = 0 ;
+        }
+        if(isset($question->unitpenalty)){
+            $options->unitpenalty = $question->unitpenalty;
+        }else {
+            $options->unitpenalty = 0 ;
+        }
+        if(isset($question->showunits)){
+            $options->showunits = $question->showunits;
+        }else {
+            $options->showunits = 0 ;
+        }
+        if(isset($question->unitsleft)){
+            $options->unitsleft = $question->unitsleft;
+        }else {
+            $options->unitsleft = 0 ;
+        }
+        if(isset($question->instructions)){
+            $options->instructions = trim($question->instructions);
+        }else {
+            $options->instructions = '' ;
+        }
+        if ($update) {
+            if (!$DB->update_record("question_numerical_options", $options)) {
+                $result->error = "Could not update numerical question options! (id=$options->id)";
+                return $result;
+            }
+        } else {
+            if (!$DB->insert_record("question_numerical_options", $options)) {
+                $result->error = "Could not insert numerical question options!";
+                return $result;
+            }
+        }
+        return $result;
+    }
+
     function save_numerical_units($question) {
         global $DB;
         $result = new stdClass;
@@ -216,7 +296,82 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         return $result;
     }
 
-    /**
+    function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
+        $state->responses = array();
+        $state->responses['answer'] =  '';
+        $state->responses['unit'] = '';
+     /*   if ($question->options->showunits == 1){
+            $state->responses['unit'] = '0';
+        }*/
+            
+        return true;
+    }
+    function restore_session_and_responses(&$question, &$state) {
+       if(false === strpos($state->responses[''], '|||||')){
+        // temporary
+             $state->responses['answer']= $state->responses[''];                    
+             $state->responses['unit'] = '';
+             split_old_answer($state->responses[''], $question->options->units, $state->responses['answer'] ,$state->responses['unit'] );
+       }else {
+            $responses = explode('|||||', $state->responses['']);
+            $state->responses['answer']= $responses[0];
+            $state->responses['unit'] = $responses[1];
+       }
+      //                             echo "<p> restore response $responses ||   <pre>";print_r($state);echo "</pre></p>";
+
+      /* 
+       if ($question->options->showunits == 1 && isset($question->options->units)){
+            $state->responses['unit']=$this->find_unit_index($question,$state->responses['unit']);
+       }*/
+       return true;
+    }
+
+    function find_unit_index(&$question,$value){
+            $length = 0;
+            $goodkey = 0 ;
+            foreach ($question->options->units as $key => $unit){
+                    if($unit->unit ==$value ) {
+                    return $key ;
+                }
+            }            
+        return 0 ;
+    }
+
+    function split_old_answer($rawresponse, $units, &$answer ,&$unit ) {
+        $answer = $rawresponse ;
+        // remove spaces and normalise decimal places.
+        $search  = array(' ', ',');
+        $replace = array('', '.');
+        $rawresponse = str_replace($search, $replace, trim($rawresponse));
+        if (preg_match('~^([+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$~',
+                $rawresponse, $responseparts)) {
+            $unit = $responseparts[5] ;
+            $answer = $responseparts[1] ;
+        }
+        return ;
+    }
+
+
+    function save_session_and_responses(&$question, &$state) {
+        global $DB;
+        //                            echo "<p> save session   <pre>";print_r($state);echo "</pre></p>";
+
+        $responses = '';
+        if(isset($state->responses['unit']) && isset($question->options->units[$state->responses['unit']])){
+            $responses = $state->responses['answer'].'|||||'.$question->options->units[$state->responses['unit']]->unit;
+        }else if(isset($state->responses['unit'])){
+            $responses = $state->responses['answer'].'|||||'.$state->responses['unit'] ;
+        }else {
+            $responses = $state->responses['answer'].'|||||';
+        }
+        // Set the legacy answer field
+        if (!$DB->set_field('question_states', 'answer', $responses, array('id' => $state->id))) {
+            return false;
+        }
+        return true;
+    }
+
+/**
      * Deletes question from the question-type specific tables
      *
      * @return boolean Success/Failure
@@ -225,13 +380,36 @@ class question_numerical_qtype extends question_shortanswer_qtype {
     function delete_question($questionid) {
         global $DB;
         $DB->delete_records("question_numerical", array("question" => $questionid));
+        $DB->delete_records("question_numerical_options", array("question" => $questionid));
         $DB->delete_records("question_numerical_units", array("question" => $questionid));
         return true;
     }
 
     function compare_responses(&$question, $state, $teststate) {
-        if (isset($state->responses['']) && isset($teststate->responses[''])) {
-            return $state->responses[''] == $teststate->responses[''];
+               if ($question->options->showunits == 1 && isset($question->options->units) && isset($question->options->units[$state->responses['unit']] )){
+            $state->responses['unit']=$question->options->units[$state->responses['unit']]->unit;
+        };
+
+        
+        $responses = '';
+        $testresponses = '';
+        if (isset($state->responses['answer'])){
+            $responses = $state->responses['answer'];
+        }
+        if (isset($state->responses['unit'])){
+            $responses .= $state->responses['unit'];
+        }
+        if (isset($teststate->responses['answer'])){
+            $testresponses = $teststate->responses['answer'];
+        }
+        if (isset($teststate->responses['unit'])){
+            $testresponses .= $teststate->responses['unit'];
+        }
+        if ( isset($responses)  && isset($testresponses )) {
+                         //   echo "<p> compare response $responses || $testresponses  <pre>";print_r($state);echo "</pre></p>";
+
+            return $responses == $testresponses ;
         }
         return false;
     }
@@ -246,8 +424,23 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         if ($answer->answer === '*') {
             return true;
         }
-
+        //        echo "<p> test response numerical state  <pre>";print_r($state);echo "</pre></p>";
+        //        echo "<p> test response numerical question  <pre>";print_r($question);echo "</pre></p>";
+
+       if( isset($state->responses['unit']) && isset($question->options->units[$state->responses['unit']])){
+            $state->responses['']=$state->responses['answer'].$question->options->units[$state->responses['unit']]->unit;
+           //  echo "<p>test responses valid unit </p>";
+        }else if(isset($state->responses['unit'])){
+            $state->responses['']= $state->responses['answer'].$state->responses['unit'] ;// why?
+        }
+         //       echo "<p> test response numerical state  before apply <pre>";print_r($state);echo "</pre></p>";
+           
         $response = $this->apply_unit($state->responses[''], $question->options->units);
+                     //  $this->valid_numerical_unit = $this->valid_unit($state->responses[''], $question->options->units);
+     //  if ($this->valid_numerical_unit) echo "<p>test responses valid unit </p>"; 
+     //  if (!$this->valid_numerical_unit) echo "<p>test responses not valid unit </p>"; 
+       // echo "<p>state response test $response <pre>";print_r($state);echo "</pre></p>";
+       // $this->raw_unitpenalty = 0.1 ;
 
         if ($response === false) {
             return false; // The student did not type a number.
@@ -258,6 +451,67 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         return ($answer->min <= $response && $response <= $answer->max);
     }
 
+    function grade_responses(&$question, &$state, $cmoptions) {
+        // The default implementation uses the test_response method to
+        // compare what the student entered against each of the possible
+        // answers stored in the question, and uses the grade from the
+        // first one that matches. It also sets the marks and penalty.
+        // This should be good enought for most simple question types.
+          //     echo "<p>grade responses  <pre>";print_r($state->responses);echo "</pre></p>";
+  
+        //first the split response from unit choice display is converted as 
+        // standard numerical response value.unit
+  /*      if (!empty($question->options->showunits) && isset($state->responses['unit'])){
+            $state->responses[''] .= $question->options->units[$state->responses['unit']]->unit ;
+        }
+  */      
+        //to apply the unit penalty we need to analyse the response in a more complex way
+        //the apply_unit() function analysis could be used to obtain the infos
+        // however it is used to detect good or bad numbers but also 
+        // gives false  
+        $state->raw_grade = 0;
+        foreach($question->options->answers as $answer) {
+            if($this->test_response($question, $state, $answer)) {
+                $state->raw_grade = $answer->fraction;
+                $this->raw_unitgrade = $answer->fraction;
+                if( $question->options->showunits == 3) {
+                    $this->valid_numerical_unit == true ;
+                }else {
+                $this->valid_numerical_unit = $this->valid_unit($state->responses[''], $question->options->units);
+            }
+                break;
+            }
+        }
+     //  if ($this->valid_numerical_unit) echo "<p>grade responses valid unit </p>"; 
+     //  if (!$this->valid_numerical_unit) echo "<p>grade responses not valid unit </p>"; 
+        // apply unit penalty
+        $this->raw_unitpenalty = 0 ;
+        if(!empty($question->options->unitpenalty)&& !$this->valid_numerical_unit ){
+            if($question->options->unitgradingtype == 1){
+                $this->raw_unitpenalty = $question->options->unitpenalty* $state->raw_grade ;
+            }else {
+                $this->raw_unitpenalty = $question->options->unitpenalty* $question->maxgrade;
+            }
+            $state->raw_grade -= $question->options->unitpenalty ;
+        }
+      //  echo "<p>grade responses  <pre>";print_r($state->responses);echo "</pre></p>";
+
+       // $this->raw_unitpenalty = $question->options->unitpenalty ;
+        // Make sure we don't assign negative or too high marks.
+     //   $state->raw_grade -= $question->options->unitpenalty ;
+        $state->raw_grade = min(max((float) $state->raw_grade,
+                            0.0), 1.0) * $question->maxgrade;
+
+        // Update the penalty.
+        $state->penalty = $question->penalty * $question->maxgrade;
+
+        // mark the state as graded
+        $state->event = ($state->event ==  QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE;
+
+        return true;
+    }
+    
+    
     function get_correct_responses(&$question, &$state) {
         $correct = parent::get_correct_responses($question, $state);
         $unit = $this->get_default_numerical_unit($question);
@@ -293,6 +547,22 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         $result->responses = $answers;
         return $result;
     }
+    function get_actual_response($question, $state) {
+      //   echo "<p>state response numerical GET ACTUAL RESPONSE $question->id $question->qtype <pre>";print_r($state);echo "</pre></p>";
+       if (!empty($state->responses) && !empty($state->responses[''])) {           
+           if(false === strpos($state->responses[''], '|||||')){
+                $responses[] = $state->responses[''];
+            }else {
+                $resp = explode('|||||', $state->responses['']);
+                $responses[] = $resp[0].$resp[1];                
+            }
+       } else {
+           $responses[] = '';
+        }
+       
+       return $responses;
+    }
+
 
     function get_tolerance_interval(&$answer) {
         // No tolerance
@@ -374,6 +644,81 @@ class question_numerical_qtype extends question_shortanswer_qtype {
                 if (isset($tmpunits[$responseparts[5]])) {
                     // Valid number with unit.
                     return (float)$responseparts[1] / $tmpunits[$responseparts[5]];
+                } else {
+                    // Valid number with invalid unit. 
+                    return (float)$responseparts[1];
+                }
+
+            } else {
+                // Valid number without unit.
+                return (float)$responseparts[1];
+            }
+        }
+        // Invalid number. Must be wrong.
+        return false;
+    }
+    function edit_numerical_options(&$mform, &$that){
+        $mform->addElement('header', 'unithandling', get_string("Units handling", 'qtype_numerical'));
+        $currentgrp1 = array();
+        
+        $currentgrp1[] =& $mform->createElement('text', 'unitpenalty', get_string('Penalty for bad unit', 'qtype_numerical') ,
+                array('size' => 3));
+        $currentgrp1[] =& $mform->createElement('static', 'penalty1','hello', get_string('as decimal fraction (0-1) of', 'qtype_numerical'));
+        $mform->addGroup($currentgrp1, 'penaltygrp', get_string('Penalty for bad unit', 'qtype_numerical'), null, false);
+        $mform->setType('unitpenalty', PARAM_NUMBER);
+        //$mform->addRule('unitpenalty', null, 'required', null, 'client');
+        $mform->setDefault('unitpenalty', 0.1);
+        $currentgrp = array();
+        $currentgrp[] =& $mform->createElement('radio', 'unitgradingtype', 'or', get_string('question grade', 'qtype_numerical'),1);
+        $currentgrp[] =& $mform->createElement('radio', 'unitgradingtype', '', get_string(' response grade', 'qtype_numerical'),2);
+        $mform->setDefault('unitgradingtype', 1);
+        $mform->addGroup($currentgrp, 'penaltychoicegrp', '',' or ', false);
+        $mform->setHelpButton('penaltychoicegrp', array('penaltygrp', get_string('unitpenalty', 'qtype_numerical'), 'qtype_numerical'));
+        $mform->addElement('radio', 'showunits', 'Edit unit  ', get_string('Editable text input element', 'qtype_numerical'),0);
+        $mform->addElement('radio', 'showunits', 'Select units ', get_string('Choice radio element', 'qtype_numerical'),1);
+        $mform->addElement('radio', 'showunits', 'Display unit ', get_string('NON editable text of Unit No1', 'qtype_numerical'),2);
+        $mform->addElement('radio', 'showunits', 'No unit display', get_string("Only numerical answer will be graded leave Unit No1 empty", 'qtype_numerical'),3);
+        $mform->setDefault('showunits', 0);
+        $currentgrp = array();
+        $leftgrp[] =& $mform->createElement('radio', 'unitsleft', '', get_string('left as $1.00', 'qtype_numerical'),1);
+        $leftgrp[] =& $mform->createElement('radio', 'unitsleft', '', get_string('rigth as 1.00cm', 'qtype_numerical'),0);
+        $mform->addGroup($leftgrp, 'unitsleft', 'Unit position',' or ', false);
+        $mform->setDefault('unitsleft', 0);
+        $mform->addElement('htmleditor', 'instructions', get_string('instructions', 'quiz'),
+                array('rows' => 10, 'course' => $that->coursefilesid));
+        $mform->setType('instructions', PARAM_RAW);
+        $mform->setHelpButton('instructions', array('instructions', get_string('instructions', 'quiz'), 'quiz'));
+
+        
+    }
+
+    function print_question_grading_details(&$question, &$state, $cmoptions, $options) {
+      //           echo "<p>state uestion_grading_details $question->id $question->qtype <pre>";print_r($state);echo "</pre></p>";
+
+        parent::print_question_grading_details(&$question, &$state, $cmoptions, $options);
+        
+    } 
+        
+        function valid_unit($rawresponse, $units) {
+        // Make units more useful
+        $tmpunits = array();
+        foreach ($units as $unit) {
+            $tmpunits[$unit->unit] = $unit->multiplier;
+        }
+        // remove spaces and normalise decimal places.
+        $search  = array(' ', ',');
+        $replace = array('', '.');
+        $rawresponse = str_replace($search, $replace, trim($rawresponse));
+
+        // Apply any unit that is present.
+        if (preg_match('~^([+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$~',
+                $rawresponse, $responseparts)) {
+
+            if (!empty($responseparts[5])) {
+
+                if (isset($tmpunits[$responseparts[5]])) {
+                    // Valid number with unit.
+                    return true ; //(float)$responseparts[1] / $tmpunits[$responseparts[5]];
                 } else {
                     // Valid number with invalid unit. Must be wrong.
                     return false;
@@ -381,7 +726,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
 
             } else {
                 // Valid number without unit.
-                return (float)$responseparts[1];
+                return false ; //(float)$responseparts[1];
             }
         }
         // Invalid number. Must be wrong.
@@ -413,6 +758,22 @@ class question_numerical_qtype extends question_shortanswer_qtype {
                 $status = question_backup_numerical_units($bf,$preferences,$question,7);
                 $status = fwrite ($bf,end_tag("NUMERICAL",$level,true));
             }
+            $status = question_backup_numerical_options($bf,$preferences,$question,$level);
+            /*            $numerical_options = $DB->get_records("question_numerical_options",array("questionid" => $question),"id");
+            if ($numerical_options) {
+                //Iterate over each numerical_option
+                foreach ($numerical_options as $numerical_option) {
+                    $status = fwrite ($bf,start_tag("NUMERICAL_OPTIONS",$level,true));
+                    //Print numerical_option contents
+                    fwrite ($bf,full_tag("INSTRUCTIONS",$level+1,false,$numerical_option->instructions));
+                    fwrite ($bf,full_tag("SHOWUNITS",$level+1,false,$numerical_option->showunits));
+                    fwrite ($bf,full_tag("UNITSLEFT",$level+1,false,$numerical_option->unitsleft));
+                    fwrite ($bf,full_tag("UNITGRADINGTYPE",$level+1,false,$numerical_option->unitgradingtype));
+                    fwrite ($bf,full_tag("UNITPENALTY",$level+1,false,$numerical_option->unitpenalty));
+                    $status = fwrite ($bf,end_tag("NUMERICAL_OPTIONS",$level,true));
+                }
+            }*/
+
             //Now print question_answers
             $status = question_backup_answers($bf,$preferences,$question);
         }
@@ -471,6 +832,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
             //Now restore numerical_units
             $status = question_restore_numerical_units ($old_question_id,$new_question_id,$num_info,$restore);
 
+            //Now restore numerical_options
+            $status = question_restore_numerical_options ($old_question_id,$new_question_id,$num_info,$restore);
+
             if (!$newid) {
                 $status = false;
             }
index 92d3a29ebbb72ff4fa08695daf486e92c241ff75..061fdec38ea7d48e25d6ab03e52f871f29d37b83 100644 (file)
@@ -98,6 +98,11 @@ class question_shortanswer_qtype extends default_questiontype {
 
     function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
         global $CFG;
+      //  $state->raw_unitgrade = 2 ;
+    //    echo "<p>state <pre>";print_r($state);echo "</pre></p>";
+  //      echo "<p>cmoptions <pre>";print_r($cmoptions);echo "</pre></p>";
+  //      echo "<p>options <pre>";print_r($options);echo "</pre></p>";
+    //    echo "<p> questionoptions <pre>";print_r($question);echo "</pre></p>";
     /// This implementation is also used by question type 'numerical'
         $readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
         $formatoptions = new stdClass;
@@ -128,6 +133,7 @@ class question_shortanswer_qtype extends default_questiontype {
         if ($options->feedback) {
             $class = question_get_feedback_class(0);
             $feedbackimg = question_get_feedback_image(0);
+            //this is OK for the first answer with a good response 
             foreach($question->options->answers as $answer) {
 
                 if ($this->test_response($question, $state, $answer)) {
@@ -310,6 +316,10 @@ class question_shortanswer_qtype extends default_questiontype {
                 $grade->cur = question_format_grade($cmoptions, $state->last_graded->grade);
                 $grade->max = question_format_grade($cmoptions, $question->maxgrade);
                 $grade->raw = question_format_grade($cmoptions, $state->last_graded->raw_grade);
+                $grade->numerical = "";
+                if($this->raw_unitpenalty > 0.0 ){
+                $grade->numerical = "This submission attracted a penalty of $this->raw_unitpenalty for bad unit." ;//question_format_grade($cmoptions, $state->last_graded->raw_grade);
+            }
 
                 // let student know wether the answer was correct
                 $class = question_get_feedback_class($state->last_graded->raw_grade / 
@@ -336,7 +346,9 @@ class question_shortanswer_qtype extends default_questiontype {
                     if (($state->last_graded->raw_grade < $question->maxgrade) and (QUESTION_EVENTCLOSEANDGRADE != $state->event)) {
                         if ('' !== $state->last_graded->penalty && ((float)$state->last_graded->penalty) > 0.0) {
                             // A penalty was applied so display it
-                            echo ' ';
+                            echo ' '.$grade->numerical;
+                            //print_string('gradingdetailspenalty', 'quiz', question_format_grade($cmoptions, $this->raw_unitpenalty));
+                           echo ' ';      
                             print_string('gradingdetailspenalty', 'quiz', question_format_grade($cmoptions, $state->last_graded->penalty));
                         } else {
                             /* No penalty was applied even though the answer was