7c187f42c63fc582df0434351c60fd19d15bad4c
[moodle.git] / question / type / calculated / edit_calculated_form.php
1 <?php
2 /**
3  * Defines the editing form for the calculated question type.
4  *
5  * @copyright &copy; 2007 Jamie Pratt
6  * @author Jamie Pratt me@jamiep.org
7  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
8  * @package questionbank
9  * @subpackage questiontypes
10  */
12 /**
13  * calculated editing form definition.
14  */
15 class question_edit_calculated_form extends question_edit_form {
16     /**
17      * Handle to the question type for this question.
18      *
19      * @var question_calculated_qtype
20      */
21     public $qtypeobj;
22     public $questiondisplay ;
23     public $activecategory ;
24     public $categorychanged = false ;
25     public $initialname = '';
26     public $reload = false ;
27     
28     function question_edit_calculated_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
29         global $QTYPES, $SESSION, $CFG, $DB;
30         $this->question = $question;
31         $this->qtypeobj =& $QTYPES[$this->question->qtype];
32         if  (  "1" == optional_param('reload','', PARAM_INT )) {
33             $this->reload = true ;
34         }else {
35             $this->reload = false ;
36         }
38         if(!$this->reload ){ // use database data as this is first pass
39             if(isset($this->question->id )){
40                 // remove prefix #{..}# if exists
41                 $this->initialname = $question->name ;
42                 $regs= array();
43                 if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
44                     $question->name = str_replace($regs[0], '', $question->name);
45                 };                             
46             }
47         }else {
48         }    
49         parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
50     }
51     function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
52    //     $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
53            $repeated = array();
54         $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
55         $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
56         $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
57         $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
58                                 array('course' => $this->coursefilesid));
59         $repeatedoptions['answer']['type'] = PARAM_RAW;
60         $repeatedoptions['fraction']['default'] = 0;
61         $answersoption = 'answers';
63         $mform->setType('answer', PARAM_NOTAGS);
65         $addrepeated = array();
66             $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
67             $addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
68         $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
69         $repeatedoptions['tolerance']['default'] = 0.01;
71         $addrepeated[] =&  $mform->createElement('select', 'correctanswerlength', get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
72         $repeatedoptions['correctanswerlength']['default'] = 2;
74         $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
75         $addrepeated[] =&  $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
76         array_splice($repeated, 3, 0, $addrepeated);
77             $repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
79         return $repeated;
80     }
82     /**
83      * Add question-type specific form fields.
84      *
85      * @param MoodleQuickForm $mform the form being built.
86      */
87     function definition_inner(&$mform) {
88         global $QTYPES;
89         $this->qtypeobj =& $QTYPES[$this->qtype()];
90       // echo code left for testing period
91       //  echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
92         $label = get_string('sharedwildcards', 'qtype_calculated');
93         $mform->addElement('hidden', 'initialcategory', 1);
94         $mform->addElement('hidden', 'reload', 1);
95         $mform->setType('initialcategory', PARAM_INT);
96         $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
97         $mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name');
98         if(isset($this->question->id )){
99             $mform->insertElementBefore($mform->createElement('static','initialname',get_string('questionstoredname','qtype_calculated'),$this->initialname),'name');
100         };
101         $addfieldsname='updatecategory';
102         $addstring=get_string("updatecategory", "qtype_calculated");
103                 $mform->registerNoSubmitButton($addfieldsname);
105         $mform->insertElementBefore(    $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
106         $mform->registerNoSubmitButton('createoptionbutton');
108         //editing as regular
109             $mform->setType('single', PARAM_INT);
111             $mform->addElement('hidden','shuffleanswers', '1');
112             $mform->setType('shuffleanswers', PARAM_INT);
113             $mform->addElement('hidden','answernumbering', 'abc');
114             $mform->setType('answernumbering', PARAM_SAFEDIR);
116         $creategrades = get_grade_options();
118             $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
119                 $creategrades->gradeoptions, 1, 1);
122         $repeated = array();
124         $QTYPES['numerical']->add_units_options($mform,$this);
125         $QTYPES['numerical']->add_units_elements($mform,$this);
126         foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
127             $mform->addElement('hidden', $feedbackname);
128             $mform->setType($feedbackname, PARAM_RAW);
129         }
130         //hidden elements
131         $mform->addElement('hidden', 'synchronize', '');
132         $mform->setType('synchronize', PARAM_INT);
133         $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
134         $mform->setType('wizard', PARAM_ALPHA);
137     }
139     function set_data($question) {
140         global $QTYPES;
142         $default_values = array();
143         if (isset($question->options)){
144             $answers = $question->options->answers;
145             if (count($answers)) {
146                 $key = 0;
147                 foreach ($answers as $answer){
148                     $default_values['answer['.$key.']'] = $answer->answer;
149                     $default_values['fraction['.$key.']'] = $answer->fraction;
150                     $default_values['tolerance['.$key.']'] = $answer->tolerance;
151                     $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
152                     $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
153                     $default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
154                     $default_values['feedback['.$key.']'] = $answer->feedback;
155                     $key++;
156                 }
157             }
158           $default_values['synchronize'] = $question->options->synchronize ;
159          $QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
160         }
161         if (isset($question->options->single)){
162             $default_values['single'] =  $question->options->single;
163             $default_values['answernumbering'] =  $question->options->answernumbering;
164             $default_values['shuffleanswers'] =  $question->options->shuffleanswers;
165             $default_values['correctfeedback'] =  $question->options->correctfeedback;
166             $default_values['partiallycorrectfeedback'] =  $question->options->partiallycorrectfeedback;
167             $default_values['incorrectfeedback'] =  $question->options->incorrectfeedback;
168         }
169         $default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
170         $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
171         /* set the wild cards category display given that on loading the category element is
172         unselected when processing this function but have a valid value when processing the
173         update category button. The value can be obtain by
174          $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
175          but is coded using existing functions
176         */
177          $qu = new stdClass;
178          $el = new stdClass;
179          /* no need to call elementExists() here */
180          if ($this->_form->elementExists('category')){
181             $el=$this->_form->getElement('category');
182          } else {
183             $el=$this->_form->getElement('categorymoveto');
184          }
185          if($value =$el->getSelected()) {
186             $qu->category =$value[0];
187         }else {
188             $qu->category=$question->category;// on load  $question->category is set by question.php
189         }
190         $html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
191         $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
192         $question = (object)((array)$question + $default_values);
194         parent::set_data($question);
195     }
197     function qtype() {
198         return 'calculated';
199     }
201     function validation($data, $files) {
202         global $QTYPES;
203               // echo code left for testing period
205               //  echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
206               //  echo "<p>data <pre>";print_r($data);echo "</pre></p>";
208         $errors = parent::validation($data, $files);
209         //verifying for errors in {=...} in question text;
210         $qtext = "";
211         $qtextremaining = $data['questiontext'] ;
212         $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
213             foreach ($possibledatasets as $name => $value) {
214             $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
215         }
216     //     echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
217         while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
218             $qtextsplits = explode($regs1[0], $qtextremaining, 2);
219             $qtext =$qtext.$qtextsplits[0];
220             $qtextremaining = $qtextsplits[1];
221             if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
222                 if(!isset($errors['questiontext'])){
223                     $errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
224                 }else {
225                     $errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
226                 }
227             }
228         }
229         $answers = $data['answer'];
230         $answercount = 0;
231         $maxgrade = false;
232         $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
233         $mandatorydatasets = array();
234         foreach ($answers as $key => $answer){
235             $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
236         }
237         if ( count($mandatorydatasets )==0){
238           //  $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
239             foreach ($answers as $key => $answer){
240                 $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
241             }
242         }
243  // regular calculated
244             foreach ($answers as $key => $answer){
245                 //check no of choices
246                 // the * for everykind of answer not actually implemented
247                 $trimmedanswer = trim($answer);
248                 if (($trimmedanswer!='')||$answercount==0){
249                     $eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
250                     if (FALSE !== $eqerror){
251                         $errors['answer['.$key.']'] = $eqerror;
252                     }
253                 }
254                 if ($trimmedanswer!=''){
255                     if ('2' == $data['correctanswerformat'][$key]
256                             && '0' == $data['correctanswerlength'][$key]) {
257                         $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
258                     }
259                     if (!is_numeric($data['tolerance'][$key])){
260                         $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
261                     }
262                     if ($data['fraction'][$key] == 1) {
263                        $maxgrade = true;
264                     }
266                     $answercount++;
267                 }
268                 //check grades
270                 //TODO how should grade checking work here??
271                 /*if ($answer != '') {
272                     if ($data['fraction'][$key] > 0) {
273                         $totalfraction += $data['fraction'][$key];
274                     }
275                     if ($data['fraction'][$key] > $maxfraction) {
276                         $maxfraction = $data['fraction'][$key];
277                     }
278                 }*/
279             }
281             //grade checking :
282             /// Perform sanity checks on fractional grades
283             /*if ( ) {
284                 if ($maxfraction != 1) {
285                     $maxfraction = $maxfraction * 100;
286                     $errors['fraction[0]'] = get_string('errfractionsnomax', 'qtype_multichoice', $maxfraction);
287                 }
288             } else {
289                 $totalfraction = round($totalfraction,2);
290                 if ($totalfraction != 1) {
291                     $totalfraction = $totalfraction * 100;
292                     $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
293                 }
294             }
295             $units  = $data['unit'];
296             if (count($units)) {
297                 foreach ($units as $key => $unit){
298                     if (is_numeric($unit)){
299                         $errors['unit['.$key.']'] = get_string('mustnotbenumeric', 'qtype_calculated');
300                     }
301                     $trimmedunit = trim($unit);
302                     $trimmedmultiplier = trim($data['multiplier'][$key]);
303                     if (!empty($trimmedunit)){
304                         if (empty($trimmedmultiplier)){
305                             $errors['multiplier['.$key.']'] = get_string('youmustenteramultiplierhere', 'qtype_calculated');
306                         }
307                         if (!is_numeric($trimmedmultiplier)){
308                             $errors['multiplier['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
309                         }
311                     }
312                 }                
313             }*/
314             $QTYPES['numerical']->validate_numerical_options($data, $errors) ;
315             if ($answercount==0){
316                 $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
317             }
318             if ($maxgrade == false) {
319                 $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
320             }
321         
323         return $errors;
324     }