Moodle release 2.5beta
[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
29b68914
TH
41 public function save_question_options($question) {
42 global $CFG, $DB;
fe6ce234 43 $context = $question->context;
e6d76583
TH
44
45 // Calculated options
29b68914
TH
46 $update = true;
47 $options = $DB->get_record('question_calculated_options',
48 array('question' => $question->id));
2d279432 49 if (!$options) {
0ff4bd08 50 $options = new stdClass();
2d279432 51 $options->question = $question->id;
e6d76583
TH
52 $options->correctfeedback = '';
53 $options->partiallycorrectfeedback = '';
54 $options->incorrectfeedback = '';
55 $options->id = $DB->insert_record('question_calculated_options', $options);
2d279432
PP
56 }
57 $options->synchronize = $question->synchronize;
2d279432
PP
58 $options->single = $question->single;
59 $options->answernumbering = $question->answernumbering;
60 $options->shuffleanswers = $question->shuffleanswers;
e6d76583
TH
61 $options = $this->save_combined_feedback_helper($options, $question, $context, true);
62 $DB->update_record('question_calculated_options', $options);
2d279432
PP
63
64 // Get old versions of the objects
29b68914
TH
65 if (!$oldanswers = $DB->get_records('question_answers',
66 array('question' => $question->id), 'id ASC')) {
2d279432
PP
67 $oldanswers = array();
68 }
29b68914
TH
69 if (!$oldoptions = $DB->get_records('question_calculated',
70 array('question' => $question->id), 'answer ASC')) {
2d279432
PP
71 $oldoptions = array();
72 }
73
2d279432
PP
74 // Insert all the new answers
75 if (isset($question->answer) && !isset($question->answers)) {
fe6ce234 76 $question->answers = $question->answer;
2d279432 77 }
e6d76583
TH
78 foreach ($question->answers as $key => $answerdata) {
79 if (is_array($answerdata)) {
80 $answerdata = $answerdata['text'];
cde2709a 81 }
e6d76583
TH
82 if (trim($answerdata) == '') {
83 continue;
84 }
85
86 // Update an existing answer if possible.
87 $answer = array_shift($oldanswers);
88 if (!$answer) {
0ff4bd08 89 $answer = new stdClass();
2d279432 90 $answer->question = $question->id;
e6d76583
TH
91 $answer->answer = '';
92 $answer->feedback = '';
93 $answer->id = $DB->insert_record('question_answers', $answer);
94 }
95
96 if (is_array($answerdata)) {
97 // Doing an import
98 $answer->answer = $this->import_or_save_files($answerdata,
99 $context, 'question', 'answer', $answer->id);
100 $answer->answerformat = $answerdata['format'];
101 } else {
102 // Saving the form
103 $answer->answer = $answerdata;
104 $answer->answerformat = FORMAT_HTML;
105 }
106 $answer->fraction = $question->fraction[$key];
107 $answer->feedback = $this->import_or_save_files($question->feedback[$key],
108 $context, 'question', 'answerfeedback', $answer->id);
109 $answer->feedbackformat = $question->feedback[$key]['format'];
110
111 $DB->update_record("question_answers", $answer);
112
113 // Set up the options object
114 if (!$options = array_shift($oldoptions)) {
115 $options = new stdClass();
116 }
117 $options->question = $question->id;
118 $options->answer = $answer->id;
119 $options->tolerance = trim($question->tolerance[$key]);
120 $options->tolerancetype = trim($question->tolerancetype[$key]);
121 $options->correctanswerlength = trim($question->correctanswerlength[$key]);
122 $options->correctanswerformat = trim($question->correctanswerformat[$key]);
123
124 // Save options
125 if (isset($options->id)) {
126 // reusing existing record
127 $DB->update_record('question_calculated', $options);
128 } else {
129 // new options
130 $DB->insert_record('question_calculated', $options);
2d279432
PP
131 }
132 }
e6d76583 133
2d279432
PP
134 // delete old answer records
135 if (!empty($oldanswers)) {
29b68914 136 foreach ($oldanswers as $oa) {
2d279432
PP
137 $DB->delete_records('question_answers', array('id' => $oa->id));
138 }
139 }
2d279432 140 if (!empty($oldoptions)) {
29b68914 141 foreach ($oldoptions as $oo) {
2d279432
PP
142 $DB->delete_records('question_calculated', array('id' => $oo->id));
143 }
144 }
2d279432 145
e6d76583
TH
146 $this->save_hints($question, true);
147
29b68914 148 if (isset($question->import_process) && $question->import_process) {
2d279432 149 $this->import_datasets($question);
fe6ce234 150 }
2d279432
PP
151 // Report any problems.
152 if (!empty($result->notice)) {
153 return $result;
154 }
b130270d 155
2d279432
PP
156 return true;
157 }
158
cdece95e
TH
159 protected function make_question_instance($questiondata) {
160 question_bank::load_question_definition_classes($this->name());
161 if ($questiondata->options->single) {
162 $class = 'qtype_calculatedmulti_single_question';
fe6ce234 163 } else {
cdece95e 164 $class = 'qtype_calculatedmulti_multi_question';
fe6ce234 165 }
cdece95e
TH
166 return new $class();
167 }
2d279432 168
cdece95e
TH
169 protected function initialise_question_instance(question_definition $question, $questiondata) {
170 question_type::initialise_question_instance($question, $questiondata);
fe6ce234 171
cdece95e
TH
172 $question->shuffleanswers = $questiondata->options->shuffleanswers;
173 $question->answernumbering = $questiondata->options->answernumbering;
174 if (!empty($questiondata->options->layout)) {
175 $question->layout = $questiondata->options->layout;
fe6ce234 176 } else {
cdece95e 177 $question->layout = qtype_multichoice_single_question::LAYOUT_VERTICAL;
fe6ce234 178 }
fe6ce234 179
e35ba43c
TH
180 $question->synchronised = $questiondata->options->synchronize;
181
cdece95e
TH
182 $this->initialise_combined_feedback($question, $questiondata, true);
183 $this->initialise_question_answers($question, $questiondata);
29b68914 184
cdece95e
TH
185 foreach ($questiondata->options->answers as $a) {
186 $question->answers[$a->id]->correctanswerlength = $a->correctanswerlength;
187 $question->answers[$a->id]->correctanswerformat = $a->correctanswerformat;
188 }
2d279432 189
cdece95e 190 $question->datasetloader = new qtype_calculated_dataset_loader($questiondata->id);
2d279432
PP
191 }
192
29b68914 193 public function comment_header($question) {
2d279432
PP
194 $strheader = '';
195 $delimiter = '';
196
197 $answers = $question->options->answers;
198
199 foreach ($answers as $key => $answer) {
200 if (is_string($answer)) {
201 $strheader .= $delimiter.$answer;
202 } else {
203 $strheader .= $delimiter.$answer->answer;
204 }
fe6ce234 205 $delimiter = '<br/>';
2d279432
PP
206 }
207 return $strheader;
208 }
209
29b68914
TH
210 public function comment_on_datasetitems($qtypeobj, $questionid, $questiontext,
211 $answers, $data, $number) {
2d279432 212 global $DB;
0ff4bd08 213 $comment = new stdClass();
2d279432 214 $comment->stranswers = array();
29b68914 215 $comment->outsidelimit = false;
2d279432 216 $comment->answers = array();
2d279432
PP
217
218 $answers = fullclone($answers);
2d279432
PP
219 $errors = '';
220 $delimiter = ': ';
221 foreach ($answers as $key => $answer) {
fe6ce234
DC
222 $answer->answer = $this->substitute_variables($answer->answer, $data);
223 //evaluate the equations i.e {=5+4)
29b68914
TH
224 $qtext = '';
225 $qtextremaining = $answer->answer;
226 while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
fe6ce234
DC
227 $qtextsplits = explode($regs1[0], $qtextremaining, 2);
228 $qtext =$qtext.$qtextsplits[0];
229 $qtextremaining = $qtextsplits[1];
230 if (empty($regs1[1])) {
231 $str = '';
232 } else {
29b68914
TH
233 if ($formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
234 $str=$formulaerrors;
235 } else {
fe6ce234
DC
236 eval('$str = '.$regs1[1].';');
237 }
2d279432 238 }
29b68914 239 $qtext = $qtext.$str;
fe6ce234
DC
240 }
241 $answer->answer = $qtext.$qtextremaining;
242 $comment->stranswers[$key] = $answer->answer;
2d279432
PP
243 }
244 return fullclone($comment);
245 }
246
29b68914 247 public function get_virtual_qtype() {
cdece95e 248 return question_bank::get_qtype('multichoice');
fe6ce234
DC
249 }
250
d1770e42
TH
251 public function get_possible_responses($questiondata) {
252 if ($questiondata->options->single) {
253 $responses = array();
254
255 foreach ($questiondata->options->answers as $aid => $answer) {
256 $responses[$aid] = new question_possible_response($answer->answer,
257 $answer->fraction);
258 }
259
260 $responses[null] = question_possible_response::no_response();
261 return array($questiondata->id => $responses);
262 } else {
263 $parts = array();
264
265 foreach ($questiondata->options->answers as $aid => $answer) {
266 $parts[$aid] = array($aid =>
267 new question_possible_response($answer->answer, $answer->fraction));
268 }
269
270 return $parts;
271 }
272 }
273
29b68914 274 public function move_files($questionid, $oldcontextid, $newcontextid) {
fe6ce234 275 $fs = get_file_storage();
fe6ce234 276
5d548d3e
TH
277 parent::move_files($questionid, $oldcontextid, $newcontextid);
278 $this->move_files_in_answers($questionid, $oldcontextid, $newcontextid, true);
d44480f6 279 $this->move_files_in_hints($questionid, $oldcontextid, $newcontextid);
5d548d3e
TH
280
281 $fs->move_area_files_to_new_context($oldcontextid,
282 $newcontextid, 'qtype_calculatedmulti', 'correctfeedback', $questionid);
283 $fs->move_area_files_to_new_context($oldcontextid,
284 $newcontextid, 'qtype_calculatedmulti', 'partiallycorrectfeedback', $questionid);
285 $fs->move_area_files_to_new_context($oldcontextid,
286 $newcontextid, 'qtype_calculatedmulti', 'incorrectfeedback', $questionid);
fe6ce234
DC
287 }
288
9203b705
TH
289 protected function delete_files($questionid, $contextid) {
290 $fs = get_file_storage();
291
292 parent::delete_files($questionid, $contextid);
293 $this->delete_files_in_answers($questionid, $contextid, true);
d44480f6 294 $this->delete_files_in_hints($questionid, $contextid);
cdece95e 295
29b68914
TH
296 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
297 'correctfeedback', $questionid);
298 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
299 'partiallycorrectfeedback', $questionid);
300 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
301 'incorrectfeedback', $questionid);
9203b705 302 }
2d279432 303}