2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Question type class for the simple calculated question type.
21 * @subpackage calculatedsimple
22 * @copyright 2009 Pierre Pichet
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') ||
die();
29 require_once($CFG->dirroot
. '/question/type/calculated/questiontype.php');
33 * The simple calculated question type.
35 * @copyright 2009 Pierre Pichet
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 class qtype_calculatedsimple
extends qtype_calculated
{
40 // Used by the function custom_generator_tools.
41 public $wizard_pages_number = 1;
43 public function save_question_options($question) {
45 $context = $question->context
;
47 // Make it impossible to save bad formulas anywhere.
48 $this->validate_question_data($question);
50 // Get old versions of the objects.
51 if (!$oldanswers = $DB->get_records('question_answers',
52 array('question' => $question->id
), 'id ASC')) {
53 $oldanswers = array();
56 if (!$oldoptions = $DB->get_records('question_calculated',
57 array('question' => $question->id
), 'answer ASC')) {
58 $oldoptions = array();
62 $virtualqtype = $this->get_virtual_qtype();
63 $result = $virtualqtype->save_units($question);
64 if (isset($result->error
)) {
67 $units = &$result->units
;
69 // Insert all the new answers.
70 foreach ($question->answer
as $key => $answerdata) {
71 if (is_array($answerdata)) {
72 $answerdata = $answerdata['text'];
74 if (trim($answerdata) == '') {
78 // Update an existing answer if possible.
79 $answer = array_shift($oldanswers);
81 $answer = new stdClass();
82 $answer->question
= $question->id
;
84 $answer->feedback
= '';
85 $answer->id
= $DB->insert_record('question_answers', $answer);
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'];
94 $DB->update_record("question_answers", $answer);
96 // Set up the options object.
97 if (!$options = array_shift($oldoptions)) {
98 $options = new stdClass();
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]);
108 if (isset($options->id
)) {
109 // Reusing existing record.
110 $DB->update_record('question_calculated', $options);
113 $DB->insert_record('question_calculated', $options);
117 // Delete old answer records.
118 if (!empty($oldanswers)) {
119 foreach ($oldanswers as $oa) {
120 $DB->delete_records('question_answers', array('id' => $oa->id
));
124 // Delete old answer records.
125 if (!empty($oldoptions)) {
126 foreach ($oldoptions as $oo) {
127 $DB->delete_records('question_calculated', array('id' => $oo->id
));
131 if (isset($question->import_process
) && $question->import_process
) {
132 $this->import_datasets($question);
134 // Save datasets and datatitems from form i.e in question.
135 $question->dataset
= $question->datasetdef
;
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])) {
146 // This dataset is not used any more, delete it.
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
));
154 // This has already been saved or just got deleted.
155 unset($datasetdefinitions[$defid]);
158 $datasetdef->id
= $DB->insert_record('question_dataset_definitions', $datasetdef);
159 $datasetdefs[] = clone($datasetdef);
160 $questiondataset = new stdClass();
161 $questiondataset->question
= $question->id
;
162 $questiondataset->datasetdefinition
= $datasetdef->id
;
163 $DB->insert_record('question_datasets', $questiondataset);
164 unset($datasetdefinitions[$defid]);
166 // Remove local obsolete datasets as well as relations
167 // to datasets in other categories.
168 if (!empty($datasetdefinitions)) {
169 foreach ($datasetdefinitions as $def) {
170 $DB->delete_records('question_datasets', array('question' => $question->id
,
171 'datasetdefinition' => $def->id
));
172 if ($def->category
== 0) { // Question local dataset.
173 $DB->delete_records('question_dataset_definitions',
174 array('id' => $def->id
));
175 $DB->delete_records('question_dataset_items',
176 array('definition' => $def->id
));
180 $datasetdefs = $this->get_dataset_definitions($question->id
, $question->dataset
);
181 // Handle adding and removing of dataset items.
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]) {
190 // Reuse any previously used record.
191 $addeditem->id
= $question->itemid
[$i];
192 $DB->update_record('question_dataset_items', $addeditem);
194 $DB->insert_record('question_dataset_items', $addeditem);
199 if (isset($addeditem->itemnumber
) && $maxnumber < $addeditem->itemnumber
) {
200 $maxnumber = $addeditem->itemnumber
;
201 foreach ($datasetdefs as $key => $newdef) {
202 if (isset($newdef->id
) && $newdef->itemcount
<= $maxnumber) {
203 $newdef->itemcount
= $maxnumber;
204 // Save the new value for options.
205 $DB->update_record('question_dataset_definitions', $newdef);
211 $this->save_hints($question);
213 // Report any problems.
214 if (!empty($question->makecopy
) && !empty($question->convert
)) {
215 $DB->set_field('question', 'qtype', 'calculated', array('id' => $question->id
));
218 $result = $virtualqtype->save_unit_options($question);
219 if (isset($result->error
)) {
223 if (!empty($result->notice
)) {
230 public function finished_edit_wizard($form) {
234 public function wizard_pages_number() {
238 public function custom_generator_tools_part($mform, $idx, $j) {
240 $minmaxgrp = array();
241 $minmaxgrp[] = $mform->createElement('text', "calcmin[{$idx}]",
242 get_string('calcmin', 'qtype_calculated'));
243 $minmaxgrp[] = $mform->createElement('text', "calcmax[{$idx}]",
244 get_string('calcmax', 'qtype_calculated'));
245 $mform->addGroup($minmaxgrp, 'minmaxgrp',
246 get_string('minmax', 'qtype_calculated'), ' - ', false);
247 $mform->setType("calcmin[{$idx}]", PARAM_FLOAT
);
248 $mform->setType("calcmax[{$idx}]", PARAM_FLOAT
);
250 $precisionoptions = range(0, 10);
251 $mform->addElement('select', "calclength[{$idx}]",
252 get_string('calclength', 'qtype_calculated'), $precisionoptions);
254 $distriboptions = array('uniform' => get_string('uniform', 'qtype_calculated'),
255 'loguniform' => get_string('loguniform', 'qtype_calculated'));
256 $mform->addElement('hidden', "calcdistribution[{$idx}]", 'uniform');
257 $mform->setType("calcdistribution[{$idx}]", PARAM_INT
);
260 public function comment_header($answers) {
264 foreach ($answers as $key => $answer) {
265 $ans = shorten_text($answer->answer
, 17, true);
266 $strheader .= $delimiter.$ans;
267 $delimiter = '<br/><br/><br/>';
272 public function tolerance_types() {
274 '1' => get_string('relative', 'qtype_numerical'),
275 '2' => get_string('nominal', 'qtype_numerical'),
279 public function dataset_options($form, $name, $mandatory = true, $renameabledatasets = false) {
280 // Takes datasets from the parent implementation but
281 // filters options that are currently not accepted by calculated.
282 // It also determines a default selection
283 // $renameabledatasets not implemented anywhere.
284 list($options, $selected) = $this->dataset_options_from_database(
285 $form, $name, '', 'qtype_calculated');
287 foreach ($options as $key => $whatever) {
288 if (!preg_match('~^1-~', $key) && $key != '0') {
289 unset($options[$key]);
294 $selected = "1-0-{$name}"; // Default.
296 $selected = "0"; // Default.
299 return array($options, $selected);