Merge branch 'MDL-70041-310' of git://github.com/mihailges/moodle into MOODLE_310_STABLE
[moodle.git] / question / type / calculatedsimple / questiontype.php
CommitLineData
aeb15530 1<?php
fe6ce234
DC
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
d3603157
TH
17/**
18 * Question type class for the simple calculated question type.
19 *
20 * @package qtype
21 * @subpackage calculatedsimple
22 * @copyright 2009 Pierre Pichet
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26
a17b297d
TH
27defined('MOODLE_INTERNAL') || die();
28
efe3e87b
TH
29require_once($CFG->dirroot . '/question/type/calculated/questiontype.php');
30
a17b297d 31
d3603157
TH
32/**
33 * The simple calculated question type.
34 *
35 * @copyright 2009 Pierre Pichet
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
8200ccd6 38class qtype_calculatedsimple extends qtype_calculated {
83a15d02 39
3d9645ae 40 // Used by the function custom_generator_tools.
8200ccd6 41 public $wizard_pages_number = 1;
83a15d02 42
8200ccd6
TH
43 public function save_question_options($question) {
44 global $CFG, $DB;
fe6ce234 45 $context = $question->context;
83a15d02 46
66de66fe
TH
47 // Make it impossible to save bad formulas anywhere.
48 $this->validate_question_data($question);
83a15d02 49
3d9645ae 50 // Get old versions of the objects.
8200ccd6
TH
51 if (!$oldanswers = $DB->get_records('question_answers',
52 array('question' => $question->id), 'id ASC')) {
83a15d02 53 $oldanswers = array();
54 }
55
8200ccd6
TH
56 if (!$oldoptions = $DB->get_records('question_calculated',
57 array('question' => $question->id), 'answer ASC')) {
83a15d02 58 $oldoptions = array();
59 }
60
61 // Save the units.
efe3e87b
TH
62 $virtualqtype = $this->get_virtual_qtype();
63 $result = $virtualqtype->save_units($question);
83a15d02 64 if (isset($result->error)) {
65 return $result;
66 } else {
67 $units = &$result->units;
68 }
3d9645ae 69 // Insert all the new answers.
66de66fe 70 foreach ($question->answer as $key => $answerdata) {
e6d76583
TH
71 if (is_array($answerdata)) {
72 $answerdata = $answerdata['text'];
cde2709a 73 }
e6d76583
TH
74 if (trim($answerdata) == '') {
75 continue;
76 }
77
78 // Update an existing answer if possible.
79 $answer = array_shift($oldanswers);
80 if (!$answer) {
0ff4bd08 81 $answer = new stdClass();
83a15d02 82 $answer->question = $question->id;
e6d76583
TH
83 $answer->answer = '';
84 $answer->feedback = '';
85 $answer->id = $DB->insert_record('question_answers', $answer);
86 }
83a15d02 87
e6d76583
TH
88 $answer->answer = trim($answerdata);
89 $answer->fraction = $question->fraction[$key];
90 $answer->feedback = $this->import_or_save_files($question->feedback[$key],
91 $context, 'question', 'answerfeedback', $answer->id);
92 $answer->feedbackformat = $question->feedback[$key]['format'];
83a15d02 93
e6d76583 94 $DB->update_record("question_answers", $answer);
83a15d02 95
3d9645ae 96 // Set up the options object.
e6d76583
TH
97 if (!$options = array_shift($oldoptions)) {
98 $options = new stdClass();
99 }
100 $options->question = $question->id;
101 $options->answer = $answer->id;
102 $options->tolerance = trim($question->tolerance[$key]);
103 $options->tolerancetype = trim($question->tolerancetype[$key]);
104 $options->correctanswerlength = trim($question->correctanswerlength[$key]);
105 $options->correctanswerformat = trim($question->correctanswerformat[$key]);
106
3d9645ae 107 // Save options.
e6d76583 108 if (isset($options->id)) {
3d9645ae 109 // Reusing existing record.
e6d76583
TH
110 $DB->update_record('question_calculated', $options);
111 } else {
3d9645ae 112 // New options.
e6d76583 113 $DB->insert_record('question_calculated', $options);
83a15d02 114 }
115 }
e6d76583 116
3d9645ae 117 // Delete old answer records.
83a15d02 118 if (!empty($oldanswers)) {
8200ccd6 119 foreach ($oldanswers as $oa) {
83a15d02 120 $DB->delete_records('question_answers', array('id' => $oa->id));
121 }
122 }
123
3d9645ae 124 // Delete old answer records.
83a15d02 125 if (!empty($oldoptions)) {
8200ccd6 126 foreach ($oldoptions as $oo) {
83a15d02 127 $DB->delete_records('question_calculated', array('id' => $oo->id));
128 }
129 }
130
e6d76583 131 if (isset($question->import_process) && $question->import_process) {
83a15d02 132 $this->import_datasets($question);
fe6ce234 133 } else {
3d9645ae 134 // Save datasets and datatitems from form i.e in question.
8200ccd6
TH
135 $question->dataset = $question->datasetdef;
136
3d9645ae 137 // Save datasets.
fe6ce234
DC
138 $datasetdefinitions = $this->get_dataset_definitions($question->id, $question->dataset);
139 $tmpdatasets = array_flip($question->dataset);
140 $defids = array_keys($datasetdefinitions);
141 $datasetdefs = array();
142 foreach ($defids as $defid) {
143 $datasetdef = &$datasetdefinitions[$defid];
144 if (isset($datasetdef->id)) {
145 if (!isset($tmpdatasets[$defid])) {
3d9645ae 146 // This dataset is not used any more, delete it.
8200ccd6
TH
147 $DB->delete_records('question_datasets', array('question' => $question->id,
148 'datasetdefinition' => $datasetdef->id));
149 $DB->delete_records('question_dataset_definitions',
150 array('id' => $datasetdef->id));
151 $DB->delete_records('question_dataset_items',
152 array('definition' => $datasetdef->id));
fe6ce234 153 }
3d9645ae 154 // This has already been saved or just got deleted.
fe6ce234
DC
155 unset($datasetdefinitions[$defid]);
156 continue;
157 }
158 $datasetdef->id = $DB->insert_record('question_dataset_definitions', $datasetdef);
e6d76583 159 $datasetdefs[] = clone($datasetdef);
0ff4bd08 160 $questiondataset = new stdClass();
fe6ce234
DC
161 $questiondataset->question = $question->id;
162 $questiondataset->datasetdefinition = $datasetdef->id;
163 $DB->insert_record('question_datasets', $questiondataset);
83a15d02 164 unset($datasetdefinitions[$defid]);
83a15d02 165 }
fe6ce234 166 // Remove local obsolete datasets as well as relations
3d9645ae 167 // to datasets in other categories.
fe6ce234
DC
168 if (!empty($datasetdefinitions)) {
169 foreach ($datasetdefinitions as $def) {
8200ccd6
TH
170 $DB->delete_records('question_datasets', array('question' => $question->id,
171 'datasetdefinition' => $def->id));
3d9645ae 172 if ($def->category == 0) { // Question local dataset.
8200ccd6
TH
173 $DB->delete_records('question_dataset_definitions',
174 array('id' => $def->id));
175 $DB->delete_records('question_dataset_items',
176 array('definition' => $def->id));
fe6ce234 177 }
83a15d02 178 }
179 }
fe6ce234 180 $datasetdefs = $this->get_dataset_definitions($question->id, $question->dataset);
3d9645ae 181 // Handle adding and removing of dataset items.
fe6ce234
DC
182 $i = 1;
183 ksort($question->definition);
184 foreach ($question->definition as $key => $defid) {
185 $addeditem = new stdClass();
186 $addeditem->definition = $datasetdefs[$defid]->id;
187 $addeditem->value = $question->number[$i];
188 $addeditem->itemnumber = ceil($i / count($datasetdefs));
189 if (empty($question->makecopy) && $question->itemid[$i]) {
3d9645ae 190 // Reuse any previously used record.
fe6ce234
DC
191 $addeditem->id = $question->itemid[$i];
192 $DB->update_record('question_dataset_items', $addeditem);
193 } else {
194 $DB->insert_record('question_dataset_items', $addeditem);
195 }
196 $i++;
83a15d02 197 }
fe6ce234 198 $maxnumber = -1;
8200ccd6 199 if (isset($addeditem->itemnumber) && $maxnumber < $addeditem->itemnumber) {
fe6ce234
DC
200 $maxnumber = $addeditem->itemnumber;
201 foreach ($datasetdefs as $key => $newdef) {
202 if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
203 $newdef->itemcount = $maxnumber;
3d9645ae 204 // Save the new value for options.
fe6ce234
DC
205 $DB->update_record('question_dataset_definitions', $newdef);
206 }
83a15d02 207 }
208 }
209 }
e6d76583
TH
210
211 $this->save_hints($question);
212
83a15d02 213 // Report any problems.
8200ccd6 214 if (!empty($question->makecopy) && !empty($question->convert)) {
e6d76583 215 $DB->set_field('question', 'qtype', 'calculated', array('id' => $question->id));
bb4b6010 216 }
efe3e87b
TH
217
218 $result = $virtualqtype->save_unit_options($question);
b003b818 219 if (isset($result->error)) {
220 return $result;
221 }
222
83a15d02 223 if (!empty($result->notice)) {
224 return $result;
225 }
b130270d 226
83a15d02 227 return true;
228 }
8200ccd6 229
72553162 230 public function finished_edit_wizard($form) {
8200ccd6 231 return true;
83a15d02 232 }
233
8200ccd6
TH
234 public function wizard_pages_number() {
235 return 1;
236 }
bd806d55 237
efe3e87b 238 public function custom_generator_tools_part($mform, $idx, $j) {
83a15d02 239
240 $minmaxgrp = array();
47538a6b 241 $minmaxgrp[] = $mform->createElement('float', "calcmin[{$idx}]",
8200ccd6 242 get_string('calcmin', 'qtype_calculated'));
47538a6b 243 $minmaxgrp[] = $mform->createElement('float', "calcmax[{$idx}]",
8200ccd6
TH
244 get_string('calcmax', 'qtype_calculated'));
245 $mform->addGroup($minmaxgrp, 'minmaxgrp',
246 get_string('minmax', 'qtype_calculated'), ' - ', false);
83a15d02 247
248 $precisionoptions = range(0, 10);
f4fe3968 249 $mform->addElement('select', "calclength[{$idx}]",
8200ccd6 250 get_string('calclength', 'qtype_calculated'), $precisionoptions);
83a15d02 251
8200ccd6
TH
252 $distriboptions = array('uniform' => get_string('uniform', 'qtype_calculated'),
253 'loguniform' => get_string('loguniform', 'qtype_calculated'));
f4fe3968
TH
254 $mform->addElement('hidden', "calcdistribution[{$idx}]", 'uniform');
255 $mform->setType("calcdistribution[{$idx}]", PARAM_INT);
83a15d02 256 }
aeb15530 257
8200ccd6 258 public function comment_header($answers) {
83a15d02 259 $strheader = "";
260 $delimiter = '';
261
83a15d02 262 foreach ($answers as $key => $answer) {
9ddb8a56 263 $ans = shorten_text($answer->answer, 17, true);
264 $strheader .= $delimiter.$ans;
83a15d02 265 $delimiter = '<br/><br/><br/>';
266 }
267 return $strheader;
268 }
269
8200ccd6
TH
270 public function tolerance_types() {
271 return array(
272 '1' => get_string('relative', 'qtype_numerical'),
5e8a85aa 273 '2' => get_string('nominal', 'qtype_numerical'),
fe6ce234 274 );
83a15d02 275 }
276
8200ccd6 277 public function dataset_options($form, $name, $mandatory = true, $renameabledatasets = false) {
fe6ce234 278 // Takes datasets from the parent implementation but
3d9645ae 279 // filters options that are currently not accepted by calculated.
280 // It also determines a default selection
281 // $renameabledatasets not implemented anywhere.
8200ccd6
TH
282 list($options, $selected) = $this->dataset_options_from_database(
283 $form, $name, '', 'qtype_calculated');
83a15d02 284
285 foreach ($options as $key => $whatever) {
6dbcacee 286 if (!preg_match('~^1-~', $key) && $key != '0') {
83a15d02 287 unset($options[$key]);
288 }
289 }
290 if (!$selected) {
8200ccd6 291 if ($mandatory) {
f4fe3968 292 $selected = "1-0-{$name}"; // Default.
8200ccd6 293 } else {
3d9645ae 294 $selected = "0"; // Default.
83a15d02 295 }
296 }
297 return array($options, $selected);
298 }
83a15d02 299}