3b81121d8cb0ee227ead06a42b00291cdc482530
[moodle.git] / question / type / calculatedmulti / edit_calculatedmulti_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_calculatedmulti_form extends question_edit_form {
16     /**
17      * Handle to the question type for this question.
18      *
19      * @var question_calculatedmulti_qtype
20      */
21     var $qtypeobj;
22     public $questiondisplay ;
23     public $initialname = '';
24     public $reload = false ;
25     function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
26         global $QTYPES, $SESSION, $CFG, $DB;
27         $this->question = $question;
28         $this->qtypeobj =& $QTYPES[$this->question->qtype];
29         if  (  "1" == optional_param('reload','', PARAM_INT )) {
30             $this->reload = true ;
31         }else {
32             $this->reload = false ;
33         }
34         if(!$this->reload ){ // use database data as this is first pass
35             if(isset($this->question->id )){
36                 // remove prefix #{..}# if exists
37                 $this->initialname = $question->name ;
38                 $regs= array();
39                 if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
40                     $question->name = str_replace($regs[0], '', $question->name);
41                 };                             
42             }
43         }else {
44         }    
45         parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
46     }
48     function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
49    //     $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
50            $repeated = array();
51         $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
52      //   if ($this->editasmultichoice == 1){
53         $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
54         $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
55         $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
56                                 array('course' => $this->coursefilesid));
57         $repeatedoptions['answer']['type'] = PARAM_RAW;
58         $repeatedoptions['fraction']['default'] = 0;
59         $answersoption = 'answers';
61         $mform->setType('answer', PARAM_NOTAGS);
63         $addrepeated = array();
64             $addrepeated[] =& $mform->createElement('hidden', 'tolerance');
65             $addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
66         $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
67         $repeatedoptions['tolerance']['default'] = 0.01;
69         $addrepeated[] =&  $mform->createElement('select', 'correctanswerlength', get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
70         $repeatedoptions['correctanswerlength']['default'] = 2;
72         $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
73         $addrepeated[] =&  $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
74         array_splice($repeated, 3, 0, $addrepeated);
75              $repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');       
77         return $repeated;
78     }
80     /**
81      * Add question-type specific form fields.
82      *
83      * @param MoodleQuickForm $mform the form being built.
84      */
85     function definition_inner(&$mform) {
86         global $QTYPES;
87         $this->qtypeobj =& $QTYPES[$this->qtype()];
88       // echo code left for testing period 
89        // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
90         $label = get_string("sharedwildcards", "qtype_calculated");
91         $mform->addElement('hidden', 'initialcategory', 1);
92         $mform->addElement('hidden', 'reload', 1);
93         $mform->setType('initialcategory', PARAM_INT);
95    //     $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
96    $html2 ="";
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);
104         $this->editasmultichoice =  1 ;
105             
107         $mform->insertElementBefore(    $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
108         $mform->registerNoSubmitButton('createoptionbutton');
109             $mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
110             $mform->setType('multichoice', PARAM_INT);
111                                             
113 //            $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
114             $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
115             $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
116             $mform->setDefault('single', 1);
117     
118             $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
119             $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
120             $mform->setDefault('shuffleanswers', 1);
121     
122             $numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
123             $menu = array();
124             foreach ($numberingoptions as $numberingoption) {
125                 $menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
126             }
127             $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
128             $mform->setDefault('answernumbering', 'abc');
130         $creategrades = get_grade_options();
131             $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
132                 $creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
133             
135         $repeated = array();
136      //   if ($this->editasmultichoice == 1){
137             $nounits = optional_param('nounits', 1, PARAM_INT);
138             $mform->addElement('hidden', 'nounits', $nounits);
139             $mform->setType('nounits', PARAM_INT);
140             $mform->setConstants(array('nounits'=>$nounits));
141             for ($i=0; $i< $nounits; $i++) {
142                 $mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
143                 $mform->setType('unit'."[$i]", PARAM_NOTAGS); 
144                 $mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
145                 $mform->setType('multiplier'."[$i]", PARAM_NUMBER);
146             }  
147           $mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ;
148           $mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ;
149           $mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ;
150           $mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ; 
151           $mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ;  
153             $mform->setType('addunits','hidden');
154             $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
156             foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
157                 $mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
158                                     array('course' => $this->coursefilesid));
159                 $mform->setType($feedbackname, PARAM_RAW);
160             }
161         //hidden elements
162         $mform->addElement('hidden', 'synchronize', '');
163         $mform->setType('synchronize', PARAM_INT);
164         if (isset($this->question->options)&& isset($this->question->options->synchronize) ){
165             $mform->setDefault("synchronize", $this->question->options->synchronize);
166         } else {
167             $mform->setDefault("synchronize", 0 );
168         }
169         $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
170         $mform->setType('wizard', PARAM_ALPHA);
173     }
175     function set_data($question) {
176              $default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
177         if (isset($question->options)){
178             $answers = $question->options->answers;
179             if (count($answers)) {
180                 $key = 0;
181                 foreach ($answers as $answer){
182                     $default_values['answer['.$key.']'] = $answer->answer;
183                     $default_values['fraction['.$key.']'] = $answer->fraction;
184                     $default_values['tolerance['.$key.']'] = $answer->tolerance;
185                     $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
186                     $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
187                     $default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
188                     $default_values['feedback['.$key.']'] = $answer->feedback;
189                     $key++;
190                 }
191             }
192         //  $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
193         //  $default_values['unitpenalty'] = $question->options->unitpenalty ;
194         //  $default_values['showunits'] = $question->options->showunits ;
195         //  $default_values['unitsleft'] = $question->options->unitsleft ;
196         //  $default_values['instructions'] = $question->options->instructions  ;
197           $default_values['synchronize'] = $question->options->synchronize ;
199             if (isset($question->options->units)){
200                 $units  = array_values($question->options->units);
201                 // make sure the default unit is at index 0
202                 usort($units, create_function('$a, $b',
203                 'if (1.0 === (float)$a->multiplier) { return -1; } else '.
204                 'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
205                 if (count($units)) {
206                     $key = 0;
207                     foreach ($units as $unit){
208                         $default_values['unit['.$key.']'] = $unit->unit;
209                         $default_values['multiplier['.$key.']'] = $unit->multiplier;
210                         $key++;
211                     }
212                 }
213             }
214         }
215         if (isset($question->options->single)){
216         $default_values['single'] =  $question->options->single;
217         $default_values['answernumbering'] =  $question->options->answernumbering;
218         $default_values['shuffleanswers'] =  $question->options->shuffleanswers;
219         $default_values['correctfeedback'] =  $question->options->correctfeedback;
220         $default_values['partiallycorrectfeedback'] =  $question->options->partiallycorrectfeedback;
221         $default_values['incorrectfeedback'] =  $question->options->incorrectfeedback;
222     }
223         $default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
224         $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
225         /* set the wild cards category display given that on loading the category element is
226         unselected when processing this function but have a valid value when processing the
227         update category button. The value can be obtain by
228          $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
229          but is coded using existing functions
230         */
231          $qu = new stdClass;
232          $el = new stdClass;
233          /* no need to call elementExists() here */
234          if ($this->_form->elementExists('category')){
235             $el=$this->_form->getElement('category');
236          } else {
237             $el=$this->_form->getElement('categorymoveto');
238          }
239          if($value =$el->getSelected()) {
240             $qu->category =$value[0];
241         }else {
242             $qu->category=$question->category;// on load  $question->category is set by question.php
243         }
244         $html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
245         $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
246         $question = (object)((array)$question + $default_values);
248         parent::set_data($question);
249     }
251     function qtype() {
252         return 'calculatedmulti';
253     }
255     function validation($data, $files) {
256               // echo code left for testing period 
258               //  echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
259               //  echo "<p>data <pre>";print_r($data);echo "</pre></p>";
261         $errors = parent::validation($data, $files);
262         //verifying for errors in {=...} in question text;
263         $qtext = "";
264         $qtextremaining = $data['questiontext'] ;
265         $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
266             foreach ($possibledatasets as $name => $value) {
267             $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
268         }
269     //     echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
270         while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
271             $qtextsplits = explode($regs1[0], $qtextremaining, 2);
272             $qtext =$qtext.$qtextsplits[0];
273             $qtextremaining = $qtextsplits[1];
274             if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
275                 if(!isset($errors['questiontext'])){
276                     $errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
277                 }else {
278                     $errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
279                 }
280             }
281         }
282         $answers = $data['answer'];
283         $answercount = 0;
284         $maxgrade = false;
285         $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
286         $mandatorydatasets = array();
287         foreach ($answers as $key => $answer){
288             $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
289         }
290         if ( count($mandatorydatasets )==0){
291           //  $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
292             foreach ($answers as $key => $answer){
293                 $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
294             }
295         }
296         if ($data['multichoice']== 1 ){
297             foreach ($answers as $key => $answer){
298                 $trimmedanswer = trim($answer);
299                 if (($trimmedanswer!='')||$answercount==0){    
300                     //verifying for errors in {=...} in answer text;
301                     $qanswer = "";
302                     $qanswerremaining =  $trimmedanswer ;
303                     $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
304                         foreach ($possibledatasets as $name => $value) {
305                         $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
306                     }
307                 //     echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
308                     while  (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
309                         $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
310                         $qanswer =$qanswer.$qanswersplits[0];
311                         $qanswerremaining = $qanswersplits[1];
312                         if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
313                             if(!isset($errors['answer['.$key.']'])){
314                                 $errors['answer['.$key.']'] = $formulaerrors.':'.$regs1[1] ;
315                             }else {
316                                 $errors['answer['.$key.']'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
317                             }
318                         }
319                     }
320                 }
321                 if ($trimmedanswer!=''){
322                     if ('2' == $data['correctanswerformat'][$key]
323                             && '0' == $data['correctanswerlength'][$key]) {
324                         $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
325                     }
326                     if (!is_numeric($data['tolerance'][$key])){
327                         $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
328                     }
329                     if ($data['fraction'][$key] == 1) {
330                        $maxgrade = true;
331                     }
332     
333                     $answercount++;
334                 }
335                 //check grades
336                 $totalfraction = 0 ;
337                 $maxfraction = 0 ;
338                 if ($answer != '') {
339                     if ($data['fraction'][$key] > 0) {
340                         $totalfraction += $data['fraction'][$key];
341                     }
342                     if ($data['fraction'][$key] > $maxfraction) {
343                         $maxfraction = $data['fraction'][$key];
344                     }
345                 }        
346             }
347             if ($answercount==0){
348                 $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
349                 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
350             } elseif ($answercount==1){
351                 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
352     
353             }
355             /// Perform sanity checks on fractional grades
356             if ($data['single']) {
357                 if ($maxfraction > 0.999 ) {
358                     $maxfraction = $maxfraction * 100;
359                     $errors['fraction[0]'] = get_string('errfractionsnomax', 'qtype_multichoice', $maxfraction);
360                 }
361             } else {
362                 $totalfraction = round($totalfraction,2);
363                 if ($totalfraction != 1) {
364                     $totalfraction = $totalfraction * 100;
365                     $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
366                 }
367             }
368             
369         
370             if ($answercount==0){
371                 $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
372             }
373             if ($maxgrade == false) {
374                 $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
375             }
376         
377         }
378         return $errors;
379     }