weekly release 2.5dev
[moodle.git] / question / type / calculatedmulti / edit_calculatedmulti_form.php
CommitLineData
2d279432 1<?php
d3603157
TH
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
2d279432 17/**
d3603157 18 * Defines the editing form for calculated multiple-choice questions.
2d279432 19 *
b04a4319 20 * @package qtype
d3603157 21 * @subpackage calculatedmulti
b04a4319
TH
22 * @copyright 2007 Jamie Pratt me@jamiep.org
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2d279432
PP
24 */
25
d3603157 26
a17b297d
TH
27defined('MOODLE_INTERNAL') || die();
28
29
2d279432 30/**
d3603157 31 * Calculated multiple-choice question editing form.
b04a4319
TH
32 *
33 * @copyright 2007 Jamie Pratt me@jamiep.org
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2d279432 35 */
29b68914 36class qtype_calculatedmulti_edit_form extends question_edit_form {
2d279432
PP
37 /**
38 * Handle to the question type for this question.
39 *
40 * @var question_calculatedmulti_qtype
41 */
fe6ce234 42 public $qtypeobj;
29b68914 43 public $questiondisplay;
d90b016b 44 public $initialname = '';
29b68914 45 public $reload = false;
cdece95e 46
29b68914
TH
47 public function __construct($submiturl, $question, $category,
48 $contexts, $formeditable = true) {
2d279432 49 $this->question = $question;
cdece95e 50 $this->qtypeobj = question_bank::get_qtype('calculatedmulti');
7d087744 51 $this->reload = optional_param('reload', false, PARAM_BOOL);
29b68914
TH
52 if (!$this->reload) {
53 // use database data as this is first pass
54 if (isset($this->question->id)) {
d90b016b 55 // remove prefix #{..}# if exists
29b68914 56 $this->initialname = $question->name;
d90b016b 57 $regs= array();
29b68914 58 if (preg_match('~#\{([^[:space:]]*)#~', $question->name , $regs)) {
d90b016b 59 $question->name = str_replace($regs[0], '', $question->name);
fe6ce234 60 };
d90b016b 61 }
fe6ce234 62 }
29b68914 63 parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
2d279432
PP
64 }
65
29b68914
TH
66 public function get_per_answer_fields($mform, $label, $gradeoptions,
67 &$repeatedoptions, &$answersoption) {
fe6ce234 68 $repeated = array();
29b68914
TH
69 $repeated[] = $mform->createElement('header', 'answerhdr', $label);
70 $repeated[] = $mform->createElement('text', 'answer',
71 get_string('answer', 'question'), array('size' => 50));
72 $repeated[] = $mform->createElement('select', 'fraction',
73 get_string('grade'), $gradeoptions);
74 $repeated[] = $mform->createElement('editor', 'feedback',
75 get_string('feedback', 'question'), null, $this->editoroptions);
2d279432
PP
76 $repeatedoptions['answer']['type'] = PARAM_RAW;
77 $repeatedoptions['fraction']['default'] = 0;
78 $answersoption = 'answers';
79
80 $mform->setType('answer', PARAM_NOTAGS);
81
82 $addrepeated = array();
29b68914
TH
83 $addrepeated[] = $mform->createElement('hidden', 'tolerance');
84 $addrepeated[] = $mform->createElement('hidden', 'tolerancetype', 1);
61cca0b7 85 $repeatedoptions['tolerance']['type'] = PARAM_FLOAT;
2d279432
PP
86 $repeatedoptions['tolerance']['default'] = 0.01;
87
29b68914
TH
88 $addrepeated[] = $mform->createElement('select', 'correctanswerlength',
89 get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
2d279432
PP
90 $repeatedoptions['correctanswerlength']['default'] = 2;
91
29b68914
TH
92 $answerlengthformats = array(
93 '1' => get_string('decimalformat', 'qtype_numerical'),
94 '2' => get_string('significantfiguresformat', 'qtype_calculated')
95 );
96 $addrepeated[] = $mform->createElement('select', 'correctanswerformat',
97 get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
2d279432 98 array_splice($repeated, 3, 0, $addrepeated);
fe6ce234 99 $repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');
2d279432
PP
100
101 return $repeated;
102 }
103
c7df5006 104 protected function definition_inner($mform) {
29b68914
TH
105
106 $label = get_string('sharedwildcards', 'qtype_calculated');
2d279432 107 $mform->addElement('hidden', 'initialcategory', 1);
d90b016b 108 $mform->addElement('hidden', 'reload', 1);
2d279432
PP
109 $mform->setType('initialcategory', PARAM_INT);
110
29b68914
TH
111 $html2 = '';
112 $mform->insertElementBefore(
113 $mform->createElement('static', 'listcategory', $label, $html2), 'name');
114 if (isset($this->question->id)) {
115 $mform->insertElementBefore($mform->createElement('static', 'initialname',
116 get_string('questionstoredname', 'qtype_calculated'),
117 $this->initialname), 'name');
d90b016b 118 };
29b68914
TH
119 $addfieldsname = 'updatecategory';
120 $addstring = get_string('updatecategory', 'qtype_calculated');
fe6ce234 121 $mform->registerNoSubmitButton($addfieldsname);
29b68914 122 $this->editasmultichoice = 1;
2d279432 123
29b68914
TH
124 $mform->insertElementBefore(
125 $mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
2d279432 126 $mform->registerNoSubmitButton('createoptionbutton');
29b68914 127 $mform->addElement('hidden', 'multichoice', $this->editasmultichoice);
fe6ce234 128 $mform->setType('multichoice', PARAM_INT);
2d279432 129
29b68914
TH
130 $menu = array(get_string('answersingleno', 'qtype_multichoice'),
131 get_string('answersingleyes', 'qtype_multichoice'));
132 $mform->addElement('select', 'single',
133 get_string('answerhowmany', 'qtype_multichoice'), $menu);
fe6ce234
DC
134 $mform->setDefault('single', 1);
135
29b68914
TH
136 $mform->addElement('advcheckbox', 'shuffleanswers',
137 get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0, 1));
ed14ad02 138 $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
fe6ce234
DC
139 $mform->setDefault('shuffleanswers', 1);
140
cdece95e 141 $numberingoptions = question_bank::get_qtype('multichoice')->get_numbering_styles();
29b68914 142 $mform->addElement('select', 'answernumbering',
cdece95e 143 get_string('answernumbering', 'qtype_multichoice'), $numberingoptions);
fe6ce234 144 $mform->setDefault('answernumbering', 'abc');
2d279432 145
fe6ce234 146 $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
92111e8d 147 question_bank::fraction_options_full(), max(5, QUESTION_NUMANS_START));
2d279432
PP
148
149 $repeated = array();
29b68914 150 // if ($this->editasmultichoice == 1) {
fe6ce234
DC
151 $nounits = optional_param('nounits', 1, PARAM_INT);
152 $mform->addElement('hidden', 'nounits', $nounits);
153 $mform->setType('nounits', PARAM_INT);
154 $mform->setConstants(array('nounits'=>$nounits));
29b68914
TH
155 for ($i = 0; $i < $nounits; $i++) {
156 $mform->addElement('hidden', 'unit'."[$i]",
7d087744 157 optional_param("unit[$i]", '', PARAM_NOTAGS));
fe6ce234 158 $mform->setType('unit'."[$i]", PARAM_NOTAGS);
29b68914 159 $mform->addElement('hidden', 'multiplier'."[$i]",
61cca0b7
RT
160 optional_param("multiplier[$i]", '', PARAM_FLOAT));
161 $mform->setType("multiplier[$i]", PARAM_FLOAT);
fe6ce234 162 }
2d279432 163
b130270d
TH
164 $this->add_combined_feedback_fields(true);
165 $mform->disabledIf('shownumcorrect', 'single', 'eq', 1);
166
167 $this->add_interactive_settings(true, true);
168
2d279432
PP
169 //hidden elements
170 $mform->addElement('hidden', 'synchronize', '');
171 $mform->setType('synchronize', PARAM_INT);
29b68914
TH
172 if (isset($this->question->options) && isset($this->question->options->synchronize)) {
173 $mform->setDefault('synchronize', $this->question->options->synchronize);
2d279432 174 } else {
29b68914 175 $mform->setDefault('synchronize', 0);
2d279432
PP
176 }
177 $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
178 $mform->setType('wizard', PARAM_ALPHA);
2d279432
PP
179 }
180
29b68914 181 public function data_preprocessing($question) {
5cf69d7f 182 $question = parent::data_preprocessing($question);
0cba6a8d 183 $question = $this->data_preprocessing_answers($question, false);
5cf69d7f
TH
184 $question = $this->data_preprocessing_combined_feedback($question, true);
185 $question = $this->data_preprocessing_hints($question, true, true);
186
29b68914 187 if (isset($question->options)) {
5cf69d7f
TH
188 $question->synchronize = $question->options->synchronize;
189 $question->single = $question->options->single;
190 $question->answernumbering = $question->options->answernumbering;
191 $question->shuffleanswers = $question->options->shuffleanswers;
fe6ce234 192 }
5cf69d7f
TH
193
194 return $question;
195 }
196
072db71c
PS
197 protected function data_preprocessing_answers($question, $withanswerfiles = false) {
198 $question = parent::data_preprocessing_answers($question, $withanswerfiles);
5cf69d7f
TH
199 if (empty($question->options->answers)) {
200 return $question;
fe6ce234 201 }
5cf69d7f
TH
202
203 $key = 0;
204 foreach ($question->options->answers as $answer) {
205 // See comment in the parent method about this hack.
206 unset($this->_form->_defaultValues["tolerance[$key]"]);
207 unset($this->_form->_defaultValues["tolerancetype[$key]"]);
208 unset($this->_form->_defaultValues["correctanswerlength[$key]"]);
209 unset($this->_form->_defaultValues["correctanswerformat[$key]"]);
210
211 $question->tolerance[$key] = $answer->tolerance;
212 $question->tolerancetype[$key] = $answer->tolerancetype;
213 $question->correctanswerlength[$key] = $answer->correctanswerlength;
214 $question->correctanswerformat[$key] = $answer->correctanswerformat;
215 $key++;
2d279432 216 }
5cf69d7f 217
fe6ce234 218 return $question;
2d279432
PP
219 }
220
29b68914 221 public function validation($data, $files) {
2d279432 222 $errors = parent::validation($data, $files);
29b68914 223
2d279432 224 //verifying for errors in {=...} in question text;
29b68914 225 $qtext = '';
fe6ce234
DC
226 $qtextremaining = $data['questiontext']['text'];
227 $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
228 foreach ($possibledatasets as $name => $value) {
2d279432
PP
229 $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
230 }
29b68914 231
fe6ce234 232 while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
2d279432 233 $qtextsplits = explode($regs1[0], $qtextremaining, 2);
29b68914 234 $qtext = $qtext.$qtextsplits[0];
2d279432 235 $qtextremaining = $qtextsplits[1];
29b68914
TH
236 if (!empty($regs1[1]) && $formulaerrors =
237 qtype_calculated_find_formula_errors($regs1[1])) {
238 if (!isset($errors['questiontext'])) {
239 $errors['questiontext'] = $formulaerrors.':'.$regs1[1];
240 } else {
2d279432
PP
241 $errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
242 }
243 }
244 }
245 $answers = $data['answer'];
246 $answercount = 0;
247 $maxgrade = false;
fe6ce234 248 $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
2d279432 249 $mandatorydatasets = array();
29b68914 250 foreach ($answers as $key => $answer) {
2d279432
PP
251 $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
252 }
29b68914
TH
253 if (count($mandatorydatasets) == 0) {
254 foreach ($answers as $key => $answer) {
255 $errors['answer['.$key.']'] =
efe3e87b 256 get_string('atleastonewildcard', 'qtype_calculated');
2d279432
PP
257 }
258 }
0cba6a8d 259 $totalfraction = 0;
260 $maxfraction = -1;
261 foreach ($answers as $key => $answer) {
262 $trimmedanswer = trim($answer);
263 $fraction = (float) $data['fraction'][$key];
264 if (empty($trimmedanswer) && $trimmedanswer != '0' && empty($fraction)) {
265 continue;
266 }
267 if (empty($trimmedanswer)) {
268 $errors['fraction['.$key.']'] = get_string('errgradesetanswerblank', 'qtype_multichoice');
269 }
270 if ($trimmedanswer != '' || $answercount == 0) {
271 //verifying for errors in {=...} in answer text;
272 $qanswer = '';
273 $qanswerremaining = $trimmedanswer;
274 $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
275 foreach ($possibledatasets as $name => $value) {
276 $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
277 }
29b68914 278
0cba6a8d 279 while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
280 $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
281 $qanswer = $qanswer . $qanswersplits[0];
282 $qanswerremaining = $qanswersplits[1];
283 if (!empty($regs1[1]) && $formulaerrors =
284 qtype_calculated_find_formula_errors($regs1[1])) {
285 if (!isset($errors['answer['.$key.']'])) {
286 $errors['answer['.$key.']'] = $formulaerrors.':'.$regs1[1];
287 } else {
288 $errors['answer['.$key.']'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
2d279432
PP
289 }
290 }
291 }
0cba6a8d 292 }
293 if ($trimmedanswer != '') {
294 if ('2' == $data['correctanswerformat'][$key] &&
295 '0' == $data['correctanswerlength'][$key]) {
296 $errors['correctanswerlength['.$key.']'] =
297 get_string('zerosignificantfiguresnotallowed', 'qtype_calculated');
2d279432 298 }
0cba6a8d 299 if (!is_numeric($data['tolerance'][$key])) {
300 $errors['tolerance['.$key.']'] =
55748620 301 get_string('xmustbenumeric', 'qtype_numerical',
c4b2600d 302 get_string('acceptederror', 'qtype_numerical'));
fe6ce234 303 }
0cba6a8d 304 if ($data['fraction'][$key] > 0) {
305 $totalfraction += $data['fraction'][$key];
2d279432 306 }
0cba6a8d 307 if ($data['fraction'][$key] > $maxfraction) {
308 $maxfraction = $data['fraction'][$key];
2d279432 309 }
0cba6a8d 310
311 $answercount++;
2d279432 312 }
0cba6a8d 313 }
314 if ($answercount == 0) {
315 $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
316 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
317 } else if ($answercount == 1) {
318 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
fe6ce234 319
0cba6a8d 320 }
321 /// Perform sanity checks on fractional grades
322 if ($data['single']== 1 ) {
323 if ($maxfraction != 1) {
324 $errors['fraction[0]'] = get_string('errfractionsnomax', 'qtype_multichoice',
325 $maxfraction * 100);
2d279432 326 }
0cba6a8d 327 } else {
328 $totalfraction = round($totalfraction, 2);
329 if ($totalfraction != 1) {
330 $totalfraction = $totalfraction * 100;
331 $errors['fraction[0]'] =
332 get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
2d279432 333 }
2d279432
PP
334 }
335 return $errors;
336 }
cdece95e
TH
337
338 public function qtype() {
339 return 'calculatedmulti';
340 }
2d279432 341}