Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402]
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 /** 18 * Matching question definition class. 19 * 20 * @package qtype_randomsamatch 21 * @copyright 2013 Jean-Michel Vedrine 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once($CFG->dirroot . '/question/type/match/question.php'); 29 30 /** 31 * Represents a randomsamatch question. 32 * 33 * @copyright 22013 Jean-Michel Vedrine 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class qtype_randomsamatch_question extends qtype_match_question { 37 /** @var qtype_randomsamatch_question_loader helper for loading the shortanswer questions. */ 38 public $questionsloader; 39 40 /** @var int the number of subquestions to randomly create. */ 41 public $choose; 42 43 /** @var bool whether to include questions from subcategories when making the random selection. */ 44 public $subcats; 45 46 public function start_attempt(question_attempt_step $step, $variant) { 47 $saquestions = $this->questionsloader->load_questions(); 48 foreach ($saquestions as $wrappedquestion) { 49 // Store and save stem text and format. 50 $this->stems[$wrappedquestion->id] = $wrappedquestion->questiontext; 51 $this->stemformat[$wrappedquestion->id] = $wrappedquestion->questiontextformat; 52 $step->set_qt_var('_stem_' . $wrappedquestion->id, $this->stems[$wrappedquestion->id]); 53 $step->set_qt_var('_stemformat_' . $wrappedquestion->id, $this->stemformat[$wrappedquestion->id]); 54 55 // Find, store and save right choice id. 56 $key = $this->find_right_answer($wrappedquestion); 57 $this->right[$wrappedquestion->id] = $key; 58 $step->set_qt_var('_right_' . $wrappedquestion->id, $key); 59 // No need to save saquestions, it will be saved by parent class in _stemorder. 60 } 61 62 // Save all the choices. 63 foreach ($this->choices as $key => $answer) { 64 $step->set_qt_var('_choice_' . $key, $answer); 65 } 66 67 parent::start_attempt($step, $variant); 68 } 69 70 /** 71 * Find the corresponding choice id of the first correct answer of a shortanswer question. 72 * choice is added to the randomsamatch question if it doesn't already exist. 73 * @param object $wrappedquestion short answer question. 74 * @return int correct choice id. 75 */ 76 public function find_right_answer($wrappedquestion) { 77 // We only take into account *one* (the first) correct answer. 78 while ($answer = array_shift($wrappedquestion->answers)) { 79 if (!question_state::graded_state_for_fraction( 80 $answer->fraction)->is_incorrect()) { 81 // Store this answer as a choice, only if this is a new one. 82 $key = array_search($answer->answer, $this->choices); 83 if ($key === false) { 84 $key = $answer->id; 85 $this->choices[$key] = $answer->answer; 86 } 87 return $key; 88 } 89 } 90 // We should never get there. 91 throw new coding_exception('shortanswerquestionwithoutrightanswer', $wrappedquestion->id); 92 93 } 94 95 public function apply_attempt_state(question_attempt_step $step) { 96 $saquestions = explode(',', $step->get_qt_var('_stemorder')); 97 foreach ($saquestions as $questionid) { 98 $this->stems[$questionid] = $step->get_qt_var('_stem_' . $questionid); 99 $this->stemformat[$questionid] = $step->get_qt_var('_stemformat_' . $questionid); 100 $key = $step->get_qt_var('_right_' . $questionid); 101 $this->right[$questionid] = $key; 102 $this->choices[$key] = $step->get_qt_var('_choice_' . $key); 103 } 104 parent::apply_attempt_state($step); 105 } 106 } 107 108 /** 109 * This class is responsible for loading the questions that a question needs from the database. 110 * 111 * @copyright 2013 Jean-Michel vedrine 112 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 113 */ 114 class qtype_randomsamatch_question_loader { 115 /** @var array hold available shortanswers questionid to choose from. */ 116 protected $availablequestions; 117 /** @var int how many questions to load. */ 118 protected $choose; 119 120 /** 121 * Constructor 122 * @param array $availablequestions array of available question ids. 123 * @param int $choose how many questions to load. 124 */ 125 public function __construct($availablequestions, $choose) { 126 $this->availablequestions = $availablequestions; 127 $this->choose = $choose; 128 } 129 130 /** 131 * Choose and load the desired number of questions. 132 * @return array of short answer questions. 133 */ 134 public function load_questions() { 135 if ($this->choose > count($this->availablequestions)) { 136 throw new coding_exception('notenoughtshortanswerquestions'); 137 } 138 139 $questionids = draw_rand_array($this->availablequestions, $this->choose); 140 $questions = array(); 141 foreach ($questionids as $questionid) { 142 $questions[] = question_bank::load_question($questionid); 143 } 144 return $questions; 145 } 146 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body