mod-lesson MDL-21006 Huge refactoring of the lesson code
[moodle.git] / mod / lesson / pagetypes / matching.php
CommitLineData
0a4abb73
SH
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Matching
20 *
21 * @package lesson
22 * @copyright 2009 Sam Hemelryk
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 **/
25
26/** Matching question type */
27define("LESSON_PAGE_MATCHING", "5");
28
29class lesson_page_type_matching extends lesson_page {
30
31 protected $type = lesson_page::TYPE_QUESTION;
32 protected $typeid = LESSON_PAGE_MATCHING;
33 protected $typeidstring = 'matching';
34 protected $string = null;
35
36 public function get_typeid() {
37 return $this->typeid;
38 }
39 public function get_typestring() {
40 if ($this->string===null) {
41 $this->string = get_string($this->typeidstring, 'lesson');
42 }
43 return $this->string;
44 }
45 public function get_idstring() {
46 return $this->typeidstring;
47 }
48 public function display($renderer, $attempt) {
49 global $USER, $CFG, $PAGE;
50 $mform = $this->make_answer_form($attempt);
51 $data = new stdClass;
52 $data->id = $PAGE->cm->id;
53 $data->pageid = $this->properties->id;
54 $mform->set_data($data);
55 return $mform->display();
56 }
57
58 protected function make_answer_form($attempt=null) {
59 global $USER, $CFG;
60 // don't suffle answers (could be an option??)
61 $answers = array_slice($this->get_answers(), 2);
62 $responses = array();
63 foreach ($answers as $answer) {
64 // get all the response
65 if ($answer->response != NULL) {
66 $responses[] = trim($answer->response);
67 }
68 }
69
70 $responseoptions = array();
71 if (!empty($responses)) {
72 shuffle($responses);
73 $responses = array_unique($responses);
74 foreach ($responses as $response) {
75 $responseoptions[htmlspecialchars(trim($response))] = $response;
76 }
77 }
78 if (isset($USER->modattempts[$this->lesson->id]) && !empty($attempt->useranswer)) {
79 $useranswers = explode(',', $attempt->useranswer);
80 $t = 0;
81 } else {
82 $useranswers = array();
83 }
84
85 $action = $CFG->wwwroot.'/mod/lesson/continue.php';
86 $params = array('answers'=>$answers, 'useranswers'=>$useranswers, 'responseoptions'=>$responseoptions, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
87 $mform = new lesson_display_answer_form_matching($action, $params);
88 return $mform;
89 }
90
91 public function create_answers($properties) {
92 global $DB;
93 // now add the answers
94 $newanswer = new stdClass;
95 $newanswer->lessonid = $this->lesson->id;
96 $newanswer->pageid = $this->properties->id;
97 $newanswer->timecreated = $this->properties->timecreated;
98
99 $answers = array();
100
101 // need to add two to offset correct response and wrong response
102 $this->lesson->maxanswers = $this->lesson->maxanswers + 2;
103 for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
104 $answer = clone($newanswer);
105 if (!empty($properties->answer[$i])) {
106 $answer->answer = format_text($properties->answer[$i], FORMAT_PLAIN);
107 if (isset($properties->response[$i])) {
108 $answer->response = format_text($properties->response[$i], FORMAT_PLAIN);
109 }
110 if (isset($properties->jumpto[$i])) {
111 $answer->jumpto = $properties->jumpto[$i];
112 }
113 if ($this->lesson->custom && isset($properties->score[$i])) {
114 $answer->score = $properties->score[$i];
115 }
116 $answer->id = $DB->insert_record("lesson_answers", $answer);
117 $answers[$answer->id] = new lesson_page_answer($answer);
118 } else if ($i < 2) {
119 $answer->id = $DB->insert_record("lesson_answers", $answer);
120 $answers[$answer->id] = new lesson_page_answer($answer);
121 } else {
122 break;
123 }
124 }
125 $this->answers = $answers;
126 return $answers;
127 }
128
129 public function check_answer() {
130 global $CFG;
131 $result = parent::check_answer();
132
133 $mform = $this->make_answer_form();
134
135 $data = $mform->get_data();
136 require_sesskey();
137
138 if (!$data) {
139 redirect(new moodle_url($CFG->wwwroot.'/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$this->properties->id)));
140 }
141
142 $response = $data->response;
143 if (!is_array($response)) {
144 $result->noanswer = true;
145 return $result;
146 }
147 $answers = $this->get_answers();
148
149 $ncorrect = 0;
150 $i = 0;
151 foreach ($answers as $answer) {
152 if ($i < 2) {
153 // ignore first two answers, they are correct response
154 // and wrong response
155 $i++;
156 continue;
157 }
158 if ($answer->response == $response[$answer->id]) {
159 $ncorrect++;
160 }
161 if ($i == 2) {
162 $correctpageid = $answer->jumpto;
163 $correctanswerid = $answer->id;
164 }
165 if ($i == 3) {
166 $wrongpageid = $answer->jumpto;
167 $wronganswerid = $answer->id;
168 }
169 $i++;
170 }
171 // get he users exact responses for record keeping
172 $userresponse = array();
173 foreach ($response as $key => $value) {
174 foreach($answers as $answer) {
175 if ($value == $answer->response) {
176 $userresponse[] = $answer->id;
177 }
178 if ((int)$answer->id === (int)$key) {
179 $result->studentanswer .= '<br />'.$answer->answer.' = '.$value;
180 }
181 }
182 }
183 $result->userresponse = implode(",", $userresponse);
184
185 if ($ncorrect == count($answers)-2) { // dont count correct/wrong responses in the total.
186 foreach ($answers as $answer) {
187 if ($answer->response == NULL && $answer->answer != NULL) {
188 $result->response = $answer->answer;
189 break;
190 }
191 }
192 if (isset($correctpageid)) {
193 $result->newpageid = $correctpageid;
194 }
195 if (isset($correctanswerid)) {
196 $result->answerid = $correctanswerid;
197 }
198 $result->correctanswer = true;
199 } else {
200 $t = 0;
201 foreach ($answers as $answer) {
202 if ($answer->response == NULL && $answer->answer != NULL) {
203 if ($t == 1) {
204 $result->response = $answer->answer;
205 break;
206 }
207 $t++;
208 }
209 }
210 if (isset($wrongpageid)) {
211 $result->newpageid = $wrongpageid;
212 }
213 if (isset($wronganswerid)) {
214 $result->answerid = $wronganswerid;
215 }
216 }
217 return $result;
218 }
219
220 public function option_description_string() {
221 return get_string("firstanswershould", "lesson");
222 }
223
224 public function display_answers(html_table $table) {
225 $answers = $this->get_answers();
226 $options = new stdClass;
227 $options->noclean = true;
228 $options->para = false;
229 $i = 1;
230 $n = 0;
231
232 foreach ($answers as $answer) {
233 if ($n < 2) {
234 if ($answer->answer != NULL) {
235 $cells = array();
236 if ($n == 0) {
237 $cells[] = "<span class=\"label\">".get_string("correctresponse", "lesson").'</span>';
238 } else {
239 $cells[] = "<span class=\"label\">".get_string("wrongresponse", "lesson").'</span>';
240 }
241 $cells[] = format_text($answer->answer, FORMAT_MOODLE, $options);
242 $table->data[] = html_table_row::make($cells);
243 }
244 $n++;
245 $i--;
246 } else {
247 $cells = array();
248 if ($this->lesson->custom && $answer->score > 0) {
249 // if the score is > 0, then it is correct
250 $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
251 } else if ($this->lesson->custom) {
252 $cells[] = '<span class="label">'.get_string("answer", "lesson")." $i</span>: \n";
253 } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
254 $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
255 } else {
256 $cells[] = '<span class="label">'.get_string("answer", "lesson")." $i</span>: \n";
257 }
258 $cells[] = format_text($answer->answer, FORMAT_MOODLE, $options);
259 $table->data[] = html_table_row::make($cells);
260
261 $cells = array();
262 $cells[] = '<span class="label">'.get_string("matchesanswer", "lesson")." $i</span>: ";
263 $cells[] = format_text($answer->response, FORMAT_MOODLE, $options);
264 $table->data[] = html_table_row::make($cells);
265 }
266
267 if ($i == 1) {
268 $cells = array();
269 $cells[] = '<span class="label">'.get_string("correctanswerscore", "lesson")." $i</span>: ";
270 $cells[] = $answer->score;
271 $table->data[] = html_table_row::make($cells);
272
273 $cells = array();
274 $cells[] = '<span class="label">'.get_string("correctanswerjump", "lesson")." $i</span>: ";
275 $cells[] = $this->get_jump_name($answer->jumpto);
276 $table->data[] = html_table_row::make($cells);
277 } elseif ($i == 2) {
278 $cells = array();
279 $cells[] = '<span class="label">'.get_string("wronganswerscore", "lesson")." $i</span>: ";
280 $cells[] = $answer->score;
281 $table->data[] = html_table_row::make($cells);
282
283 $cells = array();
284 $cells[] = '<span class="label">'.get_string("wronganswerjump", "lesson")." $i</span>: ";
285 $cells[] = $this->get_jump_name($answer->jumpto);
286 $table->data[] = html_table_row::make($cells);
287 }
288
289 if ($i === 1){
290 $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
291 }
292
293 $i++;
294 }
295 return $table;
296 }
297 public function update($properties) {
298 global $DB, $PAGE;
299 $answers = $this->get_answers();
300 $properties->id = $this->properties->id;
301 $properties->lessonid = $this->lesson->id;
302 $properties = file_postupdate_standard_editor($properties, 'contents', array('noclean'=>true, 'maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$PAGE->course->maxbytes), get_context_instance(CONTEXT_MODULE, $PAGE->cm->id), 'lesson_page_contents', $properties->id);
303 $DB->update_record("lesson_pages", $properties);
304
305 // need to add two to offset correct response and wrong response
306 $this->lesson->maxanswers += 2;
307 for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
308 if (!array_key_exists($i, $this->answers)) {
309 $this->answers[$i] = new stdClass;
310 $this->answers[$i]->lessonid = $this->lesson->id;
311 $this->answers[$i]->pageid = $this->id;
312 $this->answers[$i]->timecreated = $this->timecreated;
313 }
314 if (!empty($properties->answer[$i])) {
315 $this->answers[$i]->answer = format_text($properties->answer[$i], FORMAT_PLAIN);
316 if (isset($properties->response[$i])) {
317 $this->answers[$i]->response = format_text($properties->response[$i], FORMAT_PLAIN);
318 }
319 if (isset($properties->jumpto[$i])) {
320 $this->answers[$i]->jumpto = $properties->jumpto[$i];
321 }
322 if ($this->lesson->custom && isset($properties->score[$i])) {
323 $this->answers[$i]->score = $properties->score[$i];
324 }
325 if (!isset($this->answers[$i]->id)) {
326 $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]);
327 } else {
328 $DB->update_record("lesson_answers", $this->answers[$i]->properties());
329 }
330
331 } else if ($i < 2) {
332 if (!isset($this->answers[$i]->id)) {
333 $this->answers[$i]->id = $DB->insert_record("lesson_answers", $this->answers[$i]);
334 } else {
335 $DB->update_record("lesson_answers", $this->answers[$i]->properties());
336 }
337
338 } else {
339 break;
340 }
341 }
342 return true;
343 }
344 public function stats(array &$pagestats, $tries) {
345 if(count($tries) > $this->lesson->maxattempts) { // if there are more tries than the max that is allowed, grab the last "legal" attempt
346 $temp = $tries[$this->lesson->maxattempts - 1];
347 } else {
348 // else, user attempted the question less than the max, so grab the last one
349 $temp = end($tries);
350 }
351 if ($temp->correct) {
352 if (isset($pagestats[$temp->pageid]["correct"])) {
353 $pagestats[$temp->pageid]["correct"]++;
354 } else {
355 $pagestats[$temp->pageid]["correct"] = 1;
356 }
357 }
358 if (isset($pagestats[$temp->pageid]["total"])) {
359 $pagestats[$temp->pageid]["total"]++;
360 } else {
361 $pagestats[$temp->pageid]["total"] = 1;
362 }
363 return true;
364 }
365 public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
366 $answers = array();
367 foreach ($this->get_answers() as $answer) {
368 $answers[$answer->id] = $answer;
369 }
370 $formattextdefoptions = new stdClass;
371 $formattextdefoptions->para = false; //I'll use it widely in this page
372 foreach ($answers as $answer) {
373 if ($n == 0 && $useranswer != NULL && $useranswer->correct) {
374 if ($answer->response == NULL && $useranswer != NULL) {
375 $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
376 } else {
377 $answerdata->response = $answer->response;
378 }
379 } elseif ($n == 1 && $useranswer != NULL && !$useranswer->correct) {
380 if ($answer->response == NULL && $useranswer != NULL) {
381 $answerdata->response = get_string("thatsthewronganswer", "lesson");
382 } else {
383 $answerdata->response = $answer->response;
384 }
385 } elseif ($n > 1) {
386 if ($n == 2 && $useranswer != NULL && $useranswer->correct) {
387 if ($this->lesson->custom) {
388 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
389 } else {
390 $answerdata->score = get_string("receivedcredit", "lesson");
391 }
392 } elseif ($n == 3 && $useranswer != NULL && !$useranswer->correct) {
393 if ($this->lesson->custom) {
394 $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
395 } else {
396 $answerdata->score = get_string("didnotreceivecredit", "lesson");
397 }
398 }
399 $data = "<select disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->answer))."</option></select>";
400 if ($useranswer != NULL) {
401 $userresponse = explode(",", $useranswer->useranswer);
402 $data .= "<select disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answers[$userresponse[$i]]->response))."</option></select>";
403 } else {
404 $data .= "<select disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->response))."</option></select>";
405 }
406
407 if ($n == 2) {
408 if (isset($pagestats[$this->properties->id])) {
409 if (!array_key_exists('correct', $pagestats[$this->properties->id])) {
410 $pagestats[$this->properties->id]["correct"] = 0;
411 }
412 $percent = $pagestats[$this->properties->id]["correct"] / $pagestats[$this->properties->id]["total"] * 100;
413 $percent = round($percent, 2);
414 $percent .= "% ".get_string("answeredcorrectly", "lesson");
415 } else {
416 $percent = get_string("nooneansweredthisquestion", "lesson");
417 }
418 } else {
419 $percent = "";
420 }
421
422 $answerdata->answers[] = array($data, $percent);
423 $i++;
424 }
425 $n++;
426 $answerpage->answerdata = $answerdata;
427 }
428 return $answerpage;
429 }
430 public function get_jumps() {
431 global $DB;
432 // The jumps for matching question type is stored
433 // in the 3rd and 4rth answer record.
434 $jumps = array();
435 $params = array ("lessonid" => $this->lesson->id, "pageid" => $this->properties->id);
436 if ($answers = $DB->get_records_select("lesson_answers", "lessonid = :lessonid and pageid = :pageid", $params, 'id', '*', '2', '2')) {
437 foreach ($answers as $answer) {
438 $jumps[] = $this->get_jump_name($answer->jumpto);
439 }
440 }
441 return $jumps;
442 }
443}
444
445class lesson_add_page_form_matching extends lesson_add_page_form_base {
446
447 public $qtype = 'matching';
448 public $qtypestring = 'matching';
449
450 public function custom_definition() {
451
452 $this->_form->addElement('header', 'correctresponse', get_string('correctresponse', 'lesson'));
453 $this->add_textarea('answer', 0, get_string('correctresponse', 'lesson'));
454 $this->add_jumpto(2, get_string('correctanswerjump','lesson'));
455 $this->add_score(2, get_string("correctanswerscore", "lesson"));
456
457 $this->_form->addElement('header', 'wrongresponse', get_string('wrongresponse', 'lesson'));
458 $this->add_textarea('answer', 1, get_string('wrongresponse', 'lesson'));
459 $this->add_jumpto(3, get_string('wronganswerjump','lesson'));
460 $this->add_score(3, get_string("wronganswerscore", "lesson"));
461
462 for ($i = 2; $i < $this->_customdata['lesson']->maxanswers+2; $i++) {
463 $this->_form->addElement('header', 'matchingpair'.($i-1), get_string('matchingpair', 'lesson', $i-1));
464 $this->add_answer($i);
465 $this->add_response($i, get_string('matchesanswer','lesson'));
466 }
467 }
468}
469
470
471class lesson_display_answer_form_matching extends moodleform {
472
473 public function definition() {
474 global $USER, $OUTPUT;
475 $mform = $this->_form;
476 $answers = $this->_customdata['answers'];
477 $useranswers = $this->_customdata['useranswers'];
478 $responseoptions = $this->_customdata['responseoptions'];
479 $lessonid = $this->_customdata['lessonid'];
480 $contents = $this->_customdata['contents'];
481
482 $mform->addElement('header', 'pageheader', $OUTPUT->box($contents, 'contents'));
483
484 $options = new stdClass;
485 $options->para = false;
486 $options->noclean = true;
487
488 $mform->addElement('hidden', 'id');
489 $mform->setType('id', PARAM_INT);
490
491 $mform->addElement('hidden', 'pageid');
492 $mform->setType('pageid', PARAM_INT);
493
494 $i = 0;
495 foreach ($answers as $answer) {
496 $mform->addElement('html', '<div class="answeroption">');
497 if ($answer->response != NULL) {
498 $mform->addElement('select', 'response['.$answer->id.']', format_text($answer->answer,FORMAT_MOODLE,$options), $responseoptions);
499 $mform->setType('response['.$answer->id.']', PARAM_TEXT);
500 if (isset($USER->modattempts[$lessonid])) {
501 $mform->setDefault('response['.$answer->id.']', htmlspecialchars(trim($answers[$useranswers[$t]]->response)));
502 } else {
503 $mform->setDefault('response['.$answer->id.']', 'answeroption');
504 }
505 }
506 $mform->addElement('html', '</div>');
507 $i++;
508 }
509
510 $this->add_action_buttons(null, get_string("pleasematchtheabovepairs", "lesson"));
511 }
512
513}