MDL-27412 Upgrade the calculatedmulti question type to the new question engine.
[moodle.git] / question / type / calculatedmulti / questiontype.php
CommitLineData
2d279432 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 calculated multiple-choice question type.
19 *
20 * @package qtype
21 * @subpackage calculatedmulti
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
cdece95e
TH
29require_once($CFG->dirroot . '/question/type/multichoice/questiontype.php');
30require_once($CFG->dirroot . '/question/type/calculated/questiontype.php');
31
a17b297d 32
d3603157
TH
33/**
34 * The calculated multiple-choice question type.
35 *
36 * @copyright 2009 Pierre Pichet
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 */
29b68914 39class qtype_calculatedmulti extends qtype_calculated {
f5382dd2 40
2d279432 41 // Used by the function custom_generator_tools:
fe6ce234 42 public $calcgenerateidhasbeenadded = false;
2d279432 43
29b68914
TH
44 public function requires_qtypes() {
45 return array('calculated', 'multichoice');
2d279432 46 }
2d279432 47
29b68914
TH
48 public function save_question_options($question) {
49 global $CFG, $DB;
fe6ce234 50 $context = $question->context;
2d279432
PP
51 if (isset($question->answer) && !isset($question->answers)) {
52 $question->answers = $question->answer;
53 }
54 // calculated options
29b68914
TH
55 $update = true;
56 $options = $DB->get_record('question_calculated_options',
57 array('question' => $question->id));
2d279432
PP
58 if (!$options) {
59 $update = false;
0ff4bd08 60 $options = new stdClass();
2d279432
PP
61 $options->question = $question->id;
62 }
63 $options->synchronize = $question->synchronize;
2d279432
PP
64 $options->single = $question->single;
65 $options->answernumbering = $question->answernumbering;
66 $options->shuffleanswers = $question->shuffleanswers;
fe6ce234
DC
67
68 // save question feedback files
69 foreach (array('correct', 'partiallycorrect', 'incorrect') as $feedbacktype) {
70 $feedbackname = $feedbacktype . 'feedback';
71 $feedbackformat = $feedbackname . 'format';
72 $feedback = $question->$feedbackname;
73 $options->$feedbackformat = $feedback['format'];
cde2709a
DC
74 if (isset($feedback['files'])) {
75 $options->$feedbackname = trim($feedback['text']);
76 $files = $feedback['files'];
77 foreach ($files as $file) {
29b68914
TH
78 $this->import_file($question->context, 'qtype_calculatedmulti',
79 $feedbackname, $question->id, $file);
cde2709a
DC
80 }
81 } else {
29b68914
TH
82 $options->$feedbackname = file_save_draft_area_files($feedback['itemid'],
83 $context->id, 'qtype_calculatedmulti', $feedbackname,
84 $question->id, $this->fileoptionsa, trim($feedback['text']));
cde2709a 85 }
fe6ce234
DC
86 }
87
2d279432 88 if ($update) {
29b68914 89 $DB->update_record('question_calculated_options', $options);
2d279432 90 } else {
29b68914 91 $DB->insert_record('question_calculated_options', $options);
2d279432
PP
92 }
93
94 // Get old versions of the objects
29b68914
TH
95 if (!$oldanswers = $DB->get_records('question_answers',
96 array('question' => $question->id), 'id ASC')) {
2d279432
PP
97 $oldanswers = array();
98 }
99
29b68914
TH
100 if (!$oldoptions = $DB->get_records('question_calculated',
101 array('question' => $question->id), 'answer ASC')) {
2d279432
PP
102 $oldoptions = array();
103 }
104
2d279432
PP
105 // Insert all the new answers
106 if (isset($question->answer) && !isset($question->answers)) {
fe6ce234 107 $question->answers = $question->answer;
2d279432
PP
108 }
109 foreach ($question->answers as $key => $dataanswer) {
cde2709a
DC
110 if (is_array($dataanswer)) {
111 $dataanswer = $dataanswer['text'];
112 }
29b68914 113 if (trim($dataanswer) != '') {
0ff4bd08 114 $answer = new stdClass();
2d279432
PP
115 $answer->question = $question->id;
116 $answer->answer = trim($dataanswer);
117 $answer->fraction = $question->fraction[$key];
fe6ce234
DC
118 $answer->feedback = trim($question->feedback[$key]['text']);
119 $answer->feedbackformat = $question->feedback[$key]['format'];
cde2709a
DC
120 if (isset($question->feedback[$key]['files'])) {
121 $files = $question->feedback[$key]['files'];
122 }
2d279432 123
29b68914
TH
124 if ($oldanswer = array_shift($oldanswers)) {
125 // Existing answer, so reuse it
2d279432 126 $answer->id = $oldanswer->id;
29b68914
TH
127 $answer->feedback = file_save_draft_area_files(
128 $question->feedback[$key]['itemid'], $context->id, 'question',
129 'answerfeedback', $answer->id, $this->fileoptionsa, $answer->feedback);
130 $DB->update_record('question_answers', $answer);
131 } else {
132 // This is a completely new answer
133 $answer->id = $DB->insert_record('question_answers', $answer);
cde2709a
DC
134 if (isset($files)) {
135 $feedbacktext = $answer->feedback;
136 foreach ($files as $file) {
29b68914
TH
137 $this->import_file($context, 'question', 'answerfeedback',
138 $answer->id, $file);
cde2709a
DC
139 }
140 } else {
29b68914
TH
141 $feedbacktext = file_save_draft_area_files(
142 $question->feedback[$key]['itemid'], $context->id,
143 'question', 'answerfeedback', $answer->id,
144 $this->fileoptionsa, $answer->feedback);
cde2709a 145 }
29b68914
TH
146 $DB->set_field('question_answers', 'feedback', $feedbacktext,
147 array('id'=>$answer->id));
2d279432
PP
148 }
149
150 // Set up the options object
151 if (!$options = array_shift($oldoptions)) {
0ff4bd08 152 $options = new stdClass();
2d279432
PP
153 }
154 $options->question = $question->id;
155 $options->answer = $answer->id;
156 $options->tolerance = trim($question->tolerance[$key]);
157 $options->tolerancetype = trim($question->tolerancetype[$key]);
158 $options->correctanswerlength = trim($question->correctanswerlength[$key]);
159 $options->correctanswerformat = trim($question->correctanswerformat[$key]);
160
161 // Save options
162 if (isset($options->id)) { // reusing existing record
163 $DB->update_record('question_calculated', $options);
164 } else { // new options
165 $DB->insert_record('question_calculated', $options);
166 }
167 }
168 }
169 // delete old answer records
170 if (!empty($oldanswers)) {
29b68914 171 foreach ($oldanswers as $oa) {
2d279432
PP
172 $DB->delete_records('question_answers', array('id' => $oa->id));
173 }
174 }
175
176 // delete old answer records
177 if (!empty($oldoptions)) {
29b68914 178 foreach ($oldoptions as $oo) {
2d279432
PP
179 $DB->delete_records('question_calculated', array('id' => $oo->id));
180 }
181 }
2d279432 182
29b68914 183 if (isset($question->import_process) && $question->import_process) {
2d279432 184 $this->import_datasets($question);
fe6ce234 185 }
2d279432
PP
186 // Report any problems.
187 if (!empty($result->notice)) {
188 return $result;
189 }
190 return true;
191 }
192
cdece95e
TH
193 protected function make_question_instance($questiondata) {
194 question_bank::load_question_definition_classes($this->name());
195 if ($questiondata->options->single) {
196 $class = 'qtype_calculatedmulti_single_question';
fe6ce234 197 } else {
cdece95e 198 $class = 'qtype_calculatedmulti_multi_question';
fe6ce234 199 }
cdece95e
TH
200 return new $class();
201 }
2d279432 202
cdece95e
TH
203 protected function initialise_question_instance(question_definition $question, $questiondata) {
204 question_type::initialise_question_instance($question, $questiondata);
fe6ce234 205
cdece95e
TH
206 $question->shuffleanswers = $questiondata->options->shuffleanswers;
207 $question->answernumbering = $questiondata->options->answernumbering;
208 if (!empty($questiondata->options->layout)) {
209 $question->layout = $questiondata->options->layout;
fe6ce234 210 } else {
cdece95e 211 $question->layout = qtype_multichoice_single_question::LAYOUT_VERTICAL;
fe6ce234 212 }
fe6ce234 213
cdece95e
TH
214 $this->initialise_combined_feedback($question, $questiondata, true);
215 $this->initialise_question_answers($question, $questiondata);
29b68914 216
cdece95e
TH
217 foreach ($questiondata->options->answers as $a) {
218 $question->answers[$a->id]->correctanswerlength = $a->correctanswerlength;
219 $question->answers[$a->id]->correctanswerformat = $a->correctanswerformat;
220 }
2d279432 221
cdece95e 222 $question->datasetloader = new qtype_calculated_dataset_loader($questiondata->id);
2d279432
PP
223 }
224
29b68914 225 public function comment_header($question) {
2d279432
PP
226 $strheader = '';
227 $delimiter = '';
228
229 $answers = $question->options->answers;
230
231 foreach ($answers as $key => $answer) {
232 if (is_string($answer)) {
233 $strheader .= $delimiter.$answer;
234 } else {
235 $strheader .= $delimiter.$answer->answer;
236 }
fe6ce234 237 $delimiter = '<br/>';
2d279432
PP
238 }
239 return $strheader;
240 }
241
29b68914
TH
242 public function comment_on_datasetitems($qtypeobj, $questionid, $questiontext,
243 $answers, $data, $number) {
2d279432 244 global $DB;
0ff4bd08 245 $comment = new stdClass();
2d279432 246 $comment->stranswers = array();
29b68914 247 $comment->outsidelimit = false;
2d279432 248 $comment->answers = array();
2d279432
PP
249
250 $answers = fullclone($answers);
2d279432
PP
251 $errors = '';
252 $delimiter = ': ';
253 foreach ($answers as $key => $answer) {
fe6ce234
DC
254 $answer->answer = $this->substitute_variables($answer->answer, $data);
255 //evaluate the equations i.e {=5+4)
29b68914
TH
256 $qtext = '';
257 $qtextremaining = $answer->answer;
258 while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
fe6ce234
DC
259 $qtextsplits = explode($regs1[0], $qtextremaining, 2);
260 $qtext =$qtext.$qtextsplits[0];
261 $qtextremaining = $qtextsplits[1];
262 if (empty($regs1[1])) {
263 $str = '';
264 } else {
29b68914
TH
265 if ($formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
266 $str=$formulaerrors;
267 } else {
fe6ce234
DC
268 eval('$str = '.$regs1[1].';');
269 }
2d279432 270 }
29b68914 271 $qtext = $qtext.$str;
fe6ce234
DC
272 }
273 $answer->answer = $qtext.$qtextremaining;
274 $comment->stranswers[$key] = $answer->answer;
2d279432
PP
275 }
276 return fullclone($comment);
277 }
278
29b68914 279 public function get_virtual_qtype() {
cdece95e 280 return question_bank::get_qtype('multichoice');
fe6ce234
DC
281 }
282
29b68914 283 public function move_files($questionid, $oldcontextid, $newcontextid) {
fe6ce234 284 $fs = get_file_storage();
fe6ce234 285
5d548d3e
TH
286 parent::move_files($questionid, $oldcontextid, $newcontextid);
287 $this->move_files_in_answers($questionid, $oldcontextid, $newcontextid, true);
288
289 $fs->move_area_files_to_new_context($oldcontextid,
290 $newcontextid, 'qtype_calculatedmulti', 'correctfeedback', $questionid);
291 $fs->move_area_files_to_new_context($oldcontextid,
292 $newcontextid, 'qtype_calculatedmulti', 'partiallycorrectfeedback', $questionid);
293 $fs->move_area_files_to_new_context($oldcontextid,
294 $newcontextid, 'qtype_calculatedmulti', 'incorrectfeedback', $questionid);
fe6ce234
DC
295 }
296
9203b705
TH
297 protected function delete_files($questionid, $contextid) {
298 $fs = get_file_storage();
299
300 parent::delete_files($questionid, $contextid);
301 $this->delete_files_in_answers($questionid, $contextid, true);
cdece95e 302
29b68914
TH
303 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
304 'correctfeedback', $questionid);
305 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
306 'partiallycorrectfeedback', $questionid);
307 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
308 'incorrectfeedback', $questionid);
9203b705 309 }
2d279432 310}