PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/mod/lesson/pagetypes/multichoice.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 576 lines | 461 code | 58 blank | 57 comment | 127 complexity | 9239aeb8bffc006d8478af2261f19c0a MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
  1. <?php
  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. /**
  17. * Multichoice
  18. *
  19. * @package mod
  20. * @subpackage lesson
  21. * @copyright 2009 Sam Hemelryk
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. **/
  24. defined('MOODLE_INTERNAL') || die();
  25. /** Multichoice question type */
  26. define("LESSON_PAGE_MULTICHOICE", "3");
  27. class lesson_page_type_multichoice extends lesson_page {
  28. protected $type = lesson_page::TYPE_QUESTION;
  29. protected $typeidstring = 'multichoice';
  30. protected $typeid = LESSON_PAGE_MULTICHOICE;
  31. protected $string = null;
  32. public function get_typeid() {
  33. return $this->typeid;
  34. }
  35. public function get_typestring() {
  36. if ($this->string===null) {
  37. $this->string = get_string($this->typeidstring, 'lesson');
  38. }
  39. return $this->string;
  40. }
  41. public function get_idstring() {
  42. return $this->typeidstring;
  43. }
  44. /**
  45. * Gets an array of the jumps used by the answers of this page
  46. *
  47. * @return array
  48. */
  49. public function get_jumps() {
  50. global $DB;
  51. $jumps = array();
  52. if ($answers = $this->get_answers()) {
  53. foreach ($answers as $answer) {
  54. if ($answer->answer === '') {
  55. // show only jumps for real branches (==have description)
  56. continue;
  57. }
  58. $jumps[] = $this->get_jump_name($answer->jumpto);
  59. }
  60. } else {
  61. // We get here is the lesson was created on a Moodle 1.9 site and
  62. // the lesson contains question pages without any answers.
  63. $jumps[] = $this->get_jump_name($this->properties->nextpageid);
  64. }
  65. return $jumps;
  66. }
  67. public function get_used_answers() {
  68. $answers = $this->get_answers();
  69. foreach ($answers as $key=>$answer) {
  70. if ($answer->answer === '') {
  71. unset($answers[$key]);
  72. }
  73. }
  74. return $answers;
  75. }
  76. public function display($renderer, $attempt) {
  77. global $CFG, $PAGE;
  78. $answers = $this->get_used_answers();
  79. shuffle($answers);
  80. $action = $CFG->wwwroot.'/mod/lesson/continue.php';
  81. $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents(), 'attempt'=>$attempt);
  82. if ($this->properties->qoption) {
  83. $mform = new lesson_display_answer_form_multichoice_multianswer($action, $params);
  84. } else {
  85. $mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params);
  86. }
  87. $data = new stdClass;
  88. $data->id = $PAGE->cm->id;
  89. $data->pageid = $this->properties->id;
  90. $mform->set_data($data);
  91. return $mform->display();
  92. }
  93. public function check_answer() {
  94. global $DB, $CFG, $PAGE;
  95. $result = parent::check_answer();
  96. $formattextdefoptions = new stdClass();
  97. $formattextdefoptions->noclean = true;
  98. $formattextdefoptions->para = false;
  99. $answers = $this->get_used_answers();
  100. shuffle($answers);
  101. $action = $CFG->wwwroot.'/mod/lesson/continue.php';
  102. $params = array('answers'=>$answers, 'lessonid'=>$this->lesson->id, 'contents'=>$this->get_contents());
  103. if ($this->properties->qoption) {
  104. $mform = new lesson_display_answer_form_multichoice_multianswer($action, $params);
  105. } else {
  106. $mform = new lesson_display_answer_form_multichoice_singleanswer($action, $params);
  107. }
  108. $data = $mform->get_data();
  109. require_sesskey();
  110. if (!$data) {
  111. redirect(new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$this->properties->id)));
  112. }
  113. if ($this->properties->qoption) {
  114. // Multianswer allowed, user's answer is an array
  115. if (empty($data->answer) || !is_array($data->answer)) {
  116. $result->noanswer = true;
  117. return $result;
  118. }
  119. $studentanswers = array();
  120. foreach ($data->answer as $key=>$value) {
  121. $studentanswers[] = (int)$key;
  122. }
  123. // get what the user answered
  124. $result->userresponse = implode(",", $studentanswers);
  125. // get the answers in a set order, the id order
  126. $answers = $this->get_used_answers();
  127. $ncorrect = 0;
  128. $nhits = 0;
  129. $responses = array();
  130. $correctanswerid = 0;
  131. $wronganswerid = 0;
  132. // store student's answers for displaying on feedback page
  133. $result->studentanswer = '';
  134. foreach ($answers as $answer) {
  135. foreach ($studentanswers as $answerid) {
  136. if ($answerid == $answer->id) {
  137. $result->studentanswer .= '<br />'.format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
  138. if (trim(strip_tags($answer->response))) {
  139. $responses[$answerid] = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
  140. }
  141. }
  142. }
  143. }
  144. $correctpageid = null;
  145. $wrongpageid = null;
  146. // this is for custom scores. If score on answer is positive, it is correct
  147. if ($this->lesson->custom) {
  148. $ncorrect = 0;
  149. $nhits = 0;
  150. foreach ($answers as $answer) {
  151. if ($answer->score > 0) {
  152. $ncorrect++;
  153. foreach ($studentanswers as $answerid) {
  154. if ($answerid == $answer->id) {
  155. $nhits++;
  156. }
  157. }
  158. // save the first jumpto page id, may be needed!...
  159. if (!isset($correctpageid)) {
  160. // leave in its "raw" state - will converted into a proper page id later
  161. $correctpageid = $answer->jumpto;
  162. }
  163. // save the answer id for scoring
  164. if ($correctanswerid == 0) {
  165. $correctanswerid = $answer->id;
  166. }
  167. } else {
  168. // save the first jumpto page id, may be needed!...
  169. if (!isset($wrongpageid)) {
  170. // leave in its "raw" state - will converted into a proper page id later
  171. $wrongpageid = $answer->jumpto;
  172. }
  173. // save the answer id for scoring
  174. if ($wronganswerid == 0) {
  175. $wronganswerid = $answer->id;
  176. }
  177. }
  178. }
  179. } else {
  180. foreach ($answers as $answer) {
  181. if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
  182. $ncorrect++;
  183. foreach ($studentanswers as $answerid) {
  184. if ($answerid == $answer->id) {
  185. $nhits++;
  186. }
  187. }
  188. // save the first jumpto page id, may be needed!...
  189. if (!isset($correctpageid)) {
  190. // leave in its "raw" state - will converted into a proper page id later
  191. $correctpageid = $answer->jumpto;
  192. }
  193. // save the answer id for scoring
  194. if ($correctanswerid == 0) {
  195. $correctanswerid = $answer->id;
  196. }
  197. } else {
  198. // save the first jumpto page id, may be needed!...
  199. if (!isset($wrongpageid)) {
  200. // leave in its "raw" state - will converted into a proper page id later
  201. $wrongpageid = $answer->jumpto;
  202. }
  203. // save the answer id for scoring
  204. if ($wronganswerid == 0) {
  205. $wronganswerid = $answer->id;
  206. }
  207. }
  208. }
  209. }
  210. if ((count($studentanswers) == $ncorrect) and ($nhits == $ncorrect)) {
  211. $result->correctanswer = true;
  212. $result->response = implode('<br />', $responses);
  213. $result->newpageid = $correctpageid;
  214. $result->answerid = $correctanswerid;
  215. } else {
  216. $result->response = implode('<br />', $responses);
  217. $result->newpageid = $wrongpageid;
  218. $result->answerid = $wronganswerid;
  219. }
  220. } else {
  221. // only one answer allowed
  222. if (!isset($data->answerid) || (empty($data->answerid) && !is_int($data->answerid))) {
  223. $result->noanswer = true;
  224. return $result;
  225. }
  226. $result->answerid = $data->answerid;
  227. if (!$answer = $DB->get_record("lesson_answers", array("id" => $result->answerid))) {
  228. print_error("Continue: answer record not found");
  229. }
  230. if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
  231. $result->correctanswer = true;
  232. }
  233. if ($this->lesson->custom) {
  234. if ($answer->score > 0) {
  235. $result->correctanswer = true;
  236. } else {
  237. $result->correctanswer = false;
  238. }
  239. }
  240. $result->newpageid = $answer->jumpto;
  241. $result->response = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
  242. $result->userresponse = format_text($answer->answer, $answer->answerformat, $formattextdefoptions);
  243. $result->studentanswer = $result->userresponse;
  244. }
  245. return $result;
  246. }
  247. public function option_description_string() {
  248. if ($this->properties->qoption) {
  249. return " - ".get_string("multianswer", "lesson");
  250. }
  251. return parent::option_description_string();
  252. }
  253. public function display_answers(html_table $table) {
  254. $answers = $this->get_used_answers();
  255. $options = new stdClass;
  256. $options->noclean = true;
  257. $options->para = false;
  258. $i = 1;
  259. foreach ($answers as $answer) {
  260. $cells = array();
  261. if ($this->lesson->custom && $answer->score > 0) {
  262. // if the score is > 0, then it is correct
  263. $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
  264. } else if ($this->lesson->custom) {
  265. $cells[] = '<span class="label">'.get_string("answer", "lesson")." $i</span>: \n";
  266. } else if ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto)) {
  267. // underline correct answers
  268. $cells[] = '<span class="correct">'.get_string("answer", "lesson")." $i</span>: \n";
  269. } else {
  270. $cells[] = '<span class="labelcorrect">'.get_string("answer", "lesson")." $i</span>: \n";
  271. }
  272. $cells[] = format_text($answer->answer, $answer->answerformat, $options);
  273. $table->data[] = new html_table_row($cells);
  274. $cells = array();
  275. $cells[] = "<span class=\"label\">".get_string("response", "lesson")." $i</span>";
  276. $cells[] = format_text($answer->response, $answer->responseformat, $options);
  277. $table->data[] = new html_table_row($cells);
  278. $cells = array();
  279. $cells[] = "<span class=\"label\">".get_string("score", "lesson").'</span>';
  280. $cells[] = $answer->score;
  281. $table->data[] = new html_table_row($cells);
  282. $cells = array();
  283. $cells[] = "<span class=\"label\">".get_string("jump", "lesson").'</span>';
  284. $cells[] = $this->get_jump_name($answer->jumpto);
  285. $table->data[] = new html_table_row($cells);
  286. if ($i === 1){
  287. $table->data[count($table->data)-1]->cells[0]->style = 'width:20%;';
  288. }
  289. $i++;
  290. }
  291. return $table;
  292. }
  293. public function stats(array &$pagestats, $tries) {
  294. if(count($tries) > $this->lesson->maxattempts) { // if there are more tries than the max that is allowed, grab the last "legal" attempt
  295. $temp = $tries[$this->lesson->maxattempts - 1];
  296. } else {
  297. // else, user attempted the question less than the max, so grab the last one
  298. $temp = end($tries);
  299. }
  300. if ($this->properties->qoption) {
  301. $userresponse = explode(",", $temp->useranswer);
  302. foreach ($userresponse as $response) {
  303. if (isset($pagestats[$temp->pageid][$response])) {
  304. $pagestats[$temp->pageid][$response]++;
  305. } else {
  306. $pagestats[$temp->pageid][$response] = 1;
  307. }
  308. }
  309. } else {
  310. if (isset($pagestats[$temp->pageid][$temp->answerid])) {
  311. $pagestats[$temp->pageid][$temp->answerid]++;
  312. } else {
  313. $pagestats[$temp->pageid][$temp->answerid] = 1;
  314. }
  315. }
  316. if (isset($pagestats[$temp->pageid]["total"])) {
  317. $pagestats[$temp->pageid]["total"]++;
  318. } else {
  319. $pagestats[$temp->pageid]["total"] = 1;
  320. }
  321. return true;
  322. }
  323. public function report_answers($answerpage, $answerdata, $useranswer, $pagestats, &$i, &$n) {
  324. $answers = $this->get_used_answers();
  325. $formattextdefoptions = new stdClass;
  326. $formattextdefoptions->para = false; //I'll use it widely in this page
  327. foreach ($answers as $answer) {
  328. if ($this->properties->qoption) {
  329. if ($useranswer == null) {
  330. $userresponse = array();
  331. } else {
  332. $userresponse = explode(",", $useranswer->useranswer);
  333. }
  334. if (in_array($answer->id, $userresponse)) {
  335. // make checked
  336. $data = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
  337. if (!isset($answerdata->response)) {
  338. if ($answer->response == null) {
  339. if ($useranswer->correct) {
  340. $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
  341. } else {
  342. $answerdata->response = get_string("thatsthewronganswer", "lesson");
  343. }
  344. } else {
  345. $answerdata->response = $answer->response;
  346. }
  347. }
  348. if (!isset($answerdata->score)) {
  349. if ($this->lesson->custom) {
  350. $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
  351. } elseif ($useranswer->correct) {
  352. $answerdata->score = get_string("receivedcredit", "lesson");
  353. } else {
  354. $answerdata->score = get_string("didnotreceivecredit", "lesson");
  355. }
  356. }
  357. } else {
  358. // unchecked
  359. $data = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
  360. }
  361. if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
  362. $data = "<div class=highlight>".$data.' '.format_text($answer->answer,$answer->answerformat,$formattextdefoptions)."</div>";
  363. } else {
  364. $data .= format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
  365. }
  366. } else {
  367. if ($useranswer != null and $answer->id == $useranswer->answerid) {
  368. // make checked
  369. $data = "<input readonly=\"readonly\" disabled=\"disabled\" name=\"answer[$i]\" checked=\"checked\" type=\"checkbox\" value=\"1\" />";
  370. if ($answer->response == null) {
  371. if ($useranswer->correct) {
  372. $answerdata->response = get_string("thatsthecorrectanswer", "lesson");
  373. } else {
  374. $answerdata->response = get_string("thatsthewronganswer", "lesson");
  375. }
  376. } else {
  377. $answerdata->response = $answer->response;
  378. }
  379. if ($this->lesson->custom) {
  380. $answerdata->score = get_string("pointsearned", "lesson").": ".$answer->score;
  381. } elseif ($useranswer->correct) {
  382. $answerdata->score = get_string("receivedcredit", "lesson");
  383. } else {
  384. $answerdata->score = get_string("didnotreceivecredit", "lesson");
  385. }
  386. } else {
  387. // unchecked
  388. $data = "<input type=\"checkbox\" readonly=\"readonly\" name=\"answer[$i]\" value=\"0\" disabled=\"disabled\" />";
  389. }
  390. if (($answer->score > 0 && $this->lesson->custom) || ($this->lesson->jumpto_is_correct($this->properties->id, $answer->jumpto) && !$this->lesson->custom)) {
  391. $data = "<div class=\"highlight\">".$data.' '.format_text($answer->answer,FORMAT_MOODLE,$formattextdefoptions)."</div>";
  392. } else {
  393. $data .= format_text($answer->answer,$answer->answerformat,$formattextdefoptions);
  394. }
  395. }
  396. if (isset($pagestats[$this->properties->id][$answer->id])) {
  397. $percent = $pagestats[$this->properties->id][$answer->id] / $pagestats[$this->properties->id]["total"] * 100;
  398. $percent = round($percent, 2);
  399. $percent .= "% ".get_string("checkedthisone", "lesson");
  400. } else {
  401. $percent = get_string("noonecheckedthis", "lesson");
  402. }
  403. $answerdata->answers[] = array($data, $percent);
  404. $answerpage->answerdata = $answerdata;
  405. }
  406. return $answerpage;
  407. }
  408. }
  409. class lesson_add_page_form_multichoice extends lesson_add_page_form_base {
  410. public $qtype = 'multichoice';
  411. public $qtypestring = 'multichoice';
  412. public function custom_definition() {
  413. $this->_form->addElement('checkbox', 'qoption', get_string('options', 'lesson'), get_string('multianswer', 'lesson'));
  414. $this->_form->setDefault('qoption', 0);
  415. $this->_form->addHelpButton('qoption', 'multianswer', 'lesson');
  416. for ($i = 0; $i < $this->_customdata['lesson']->maxanswers; $i++) {
  417. $this->_form->addElement('header', 'answertitle'.$i, get_string('answer').' '.($i+1));
  418. $this->add_answer($i, null, ($i<2));
  419. $this->add_response($i);
  420. $this->add_jumpto($i, null, ($i == 0 ? LESSON_NEXTPAGE : LESSON_THISPAGE));
  421. $this->add_score($i, null, ($i===0)?1:0);
  422. }
  423. }
  424. }
  425. class lesson_display_answer_form_multichoice_singleanswer extends moodleform {
  426. public function definition() {
  427. global $USER, $OUTPUT;
  428. $mform = $this->_form;
  429. $answers = $this->_customdata['answers'];
  430. $lessonid = $this->_customdata['lessonid'];
  431. $contents = $this->_customdata['contents'];
  432. if (array_key_exists('attempt', $this->_customdata)) {
  433. $attempt = $this->_customdata['attempt'];
  434. } else {
  435. $attempt = new stdClass();
  436. $attempt->answerid = null;
  437. }
  438. $mform->addElement('header', 'pageheader');
  439. $mform->addElement('html', $OUTPUT->container($contents, 'contents'));
  440. $hasattempt = false;
  441. $disabled = '';
  442. if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
  443. $hasattempt = true;
  444. $disabled = array('disabled' => 'disabled');
  445. }
  446. $options = new stdClass;
  447. $options->para = false;
  448. $options->noclean = true;
  449. $mform->addElement('hidden', 'id');
  450. $mform->setType('id', PARAM_INT);
  451. $mform->addElement('hidden', 'pageid');
  452. $mform->setType('pageid', PARAM_INT);
  453. $i = 0;
  454. foreach ($answers as $answer) {
  455. $mform->addElement('html', '<div class="answeroption">');
  456. $answer->answer = preg_replace('#>$#', '> ', $answer->answer);
  457. $mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id, $disabled);
  458. $mform->setType('answer'.$i, PARAM_INT);
  459. if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) {
  460. $mform->setDefault('answerid', $USER->modattempts[$lessonid]->answerid);
  461. }
  462. $mform->addElement('html', '</div>');
  463. $i++;
  464. }
  465. if ($hasattempt) {
  466. $this->add_action_buttons(null, get_string("nextpage", "lesson"));
  467. } else {
  468. $this->add_action_buttons(null, get_string("submit", "lesson"));
  469. }
  470. }
  471. }
  472. class lesson_display_answer_form_multichoice_multianswer extends moodleform {
  473. public function definition() {
  474. global $USER, $OUTPUT;
  475. $mform = $this->_form;
  476. $answers = $this->_customdata['answers'];
  477. $lessonid = $this->_customdata['lessonid'];
  478. $contents = $this->_customdata['contents'];
  479. $mform->addElement('header', 'pageheader');
  480. $mform->addElement('html', $OUTPUT->container($contents, 'contents'));
  481. $hasattempt = false;
  482. $disabled = '';
  483. $useranswers = array();
  484. if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
  485. $hasattempt = true;
  486. $disabled = array('disabled' => 'disabled');
  487. $useranswers = explode(',', $USER->modattempts[$lessonid]->useranswer);
  488. }
  489. $options = new stdClass;
  490. $options->para = false;
  491. $options->noclean = true;
  492. $mform->addElement('hidden', 'id');
  493. $mform->setType('id', PARAM_INT);
  494. $mform->addElement('hidden', 'pageid');
  495. $mform->setType('pageid', PARAM_INT);
  496. foreach ($answers as $answer) {
  497. $mform->addElement('html', '<div class="answeroption">');
  498. $answerid = 'answer['.$answer->id.']';
  499. if ($hasattempt && in_array($answer->id, $useranswers)) {
  500. $answerid = 'answer_'.$answer->id;
  501. $mform->addElement('hidden', 'answer['.$answer->id.']', $answer->answer);
  502. $mform->setType('answer['.$answer->id.']', PARAM_NOTAGS);
  503. $mform->setDefault($answerid, true);
  504. $mform->setDefault('answer['.$answer->id.']', true);
  505. }
  506. // NOTE: our silly checkbox supports only value '1' - we can not use it like the radiobox above!!!!!!
  507. $answer->answer = preg_replace('#>$#', '> ', $answer->answer);
  508. $mform->addElement('checkbox', $answerid, null, format_text($answer->answer, $answer->answerformat, $options), $disabled);
  509. $mform->setType($answerid, PARAM_INT);
  510. $mform->addElement('html', '</div>');
  511. }
  512. if ($hasattempt) {
  513. $this->add_action_buttons(null, get_string("nextpage", "lesson"));
  514. } else {
  515. $this->add_action_buttons(null, get_string("submit", "lesson"));
  516. }
  517. }
  518. }