MDL-20636 fix broken links in the statistics report.
[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 }
b130270d
TH
190
191 $this->save_hints($question, true);
192
2d279432
PP
193 return true;
194 }
195
cdece95e
TH
196 protected function make_question_instance($questiondata) {
197 question_bank::load_question_definition_classes($this->name());
198 if ($questiondata->options->single) {
199 $class = 'qtype_calculatedmulti_single_question';
fe6ce234 200 } else {
cdece95e 201 $class = 'qtype_calculatedmulti_multi_question';
fe6ce234 202 }
cdece95e
TH
203 return new $class();
204 }
2d279432 205
cdece95e
TH
206 protected function initialise_question_instance(question_definition $question, $questiondata) {
207 question_type::initialise_question_instance($question, $questiondata);
fe6ce234 208
cdece95e
TH
209 $question->shuffleanswers = $questiondata->options->shuffleanswers;
210 $question->answernumbering = $questiondata->options->answernumbering;
211 if (!empty($questiondata->options->layout)) {
212 $question->layout = $questiondata->options->layout;
fe6ce234 213 } else {
cdece95e 214 $question->layout = qtype_multichoice_single_question::LAYOUT_VERTICAL;
fe6ce234 215 }
fe6ce234 216
cdece95e
TH
217 $this->initialise_combined_feedback($question, $questiondata, true);
218 $this->initialise_question_answers($question, $questiondata);
29b68914 219
cdece95e
TH
220 foreach ($questiondata->options->answers as $a) {
221 $question->answers[$a->id]->correctanswerlength = $a->correctanswerlength;
222 $question->answers[$a->id]->correctanswerformat = $a->correctanswerformat;
223 }
2d279432 224
cdece95e 225 $question->datasetloader = new qtype_calculated_dataset_loader($questiondata->id);
2d279432
PP
226 }
227
29b68914 228 public function comment_header($question) {
2d279432
PP
229 $strheader = '';
230 $delimiter = '';
231
232 $answers = $question->options->answers;
233
234 foreach ($answers as $key => $answer) {
235 if (is_string($answer)) {
236 $strheader .= $delimiter.$answer;
237 } else {
238 $strheader .= $delimiter.$answer->answer;
239 }
fe6ce234 240 $delimiter = '<br/>';
2d279432
PP
241 }
242 return $strheader;
243 }
244
29b68914
TH
245 public function comment_on_datasetitems($qtypeobj, $questionid, $questiontext,
246 $answers, $data, $number) {
2d279432 247 global $DB;
0ff4bd08 248 $comment = new stdClass();
2d279432 249 $comment->stranswers = array();
29b68914 250 $comment->outsidelimit = false;
2d279432 251 $comment->answers = array();
2d279432
PP
252
253 $answers = fullclone($answers);
2d279432
PP
254 $errors = '';
255 $delimiter = ': ';
256 foreach ($answers as $key => $answer) {
fe6ce234
DC
257 $answer->answer = $this->substitute_variables($answer->answer, $data);
258 //evaluate the equations i.e {=5+4)
29b68914
TH
259 $qtext = '';
260 $qtextremaining = $answer->answer;
261 while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
fe6ce234
DC
262 $qtextsplits = explode($regs1[0], $qtextremaining, 2);
263 $qtext =$qtext.$qtextsplits[0];
264 $qtextremaining = $qtextsplits[1];
265 if (empty($regs1[1])) {
266 $str = '';
267 } else {
29b68914
TH
268 if ($formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
269 $str=$formulaerrors;
270 } else {
fe6ce234
DC
271 eval('$str = '.$regs1[1].';');
272 }
2d279432 273 }
29b68914 274 $qtext = $qtext.$str;
fe6ce234
DC
275 }
276 $answer->answer = $qtext.$qtextremaining;
277 $comment->stranswers[$key] = $answer->answer;
2d279432
PP
278 }
279 return fullclone($comment);
280 }
281
29b68914 282 public function get_virtual_qtype() {
cdece95e 283 return question_bank::get_qtype('multichoice');
fe6ce234
DC
284 }
285
29b68914 286 public function move_files($questionid, $oldcontextid, $newcontextid) {
fe6ce234 287 $fs = get_file_storage();
fe6ce234 288
5d548d3e
TH
289 parent::move_files($questionid, $oldcontextid, $newcontextid);
290 $this->move_files_in_answers($questionid, $oldcontextid, $newcontextid, true);
291
292 $fs->move_area_files_to_new_context($oldcontextid,
293 $newcontextid, 'qtype_calculatedmulti', 'correctfeedback', $questionid);
294 $fs->move_area_files_to_new_context($oldcontextid,
295 $newcontextid, 'qtype_calculatedmulti', 'partiallycorrectfeedback', $questionid);
296 $fs->move_area_files_to_new_context($oldcontextid,
297 $newcontextid, 'qtype_calculatedmulti', 'incorrectfeedback', $questionid);
fe6ce234
DC
298 }
299
9203b705
TH
300 protected function delete_files($questionid, $contextid) {
301 $fs = get_file_storage();
302
303 parent::delete_files($questionid, $contextid);
304 $this->delete_files_in_answers($questionid, $contextid, true);
cdece95e 305
29b68914
TH
306 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
307 'correctfeedback', $questionid);
308 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
309 'partiallycorrectfeedback', $questionid);
310 $fs->delete_area_files($contextid, 'qtype_calculatedmulti',
311 'incorrectfeedback', $questionid);
9203b705 312 }
2d279432 313}