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 * Question type class for the randomsamatch question type. 19 * 20 * @package qtype_randomsamatch 21 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 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/questiontypebase.php'); 29 require_once($CFG->dirroot . '/question/type/questionbase.php'); 30 require_once($CFG->dirroot . '/question/type/numerical/question.php'); 31 32 /** 33 * The randomsamatch question type class. 34 * 35 * TODO: Make sure short answer questions chosen by a randomsamatch question 36 * can not also be used by a random question 37 * 38 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class qtype_randomsamatch extends question_type { 42 /** 43 * Cache of available shortanswer question ids from a particular category. 44 * @var array two-dimensional array. The first key is a category id, the 45 * second key is wether subcategories should be included. 46 */ 47 private $availablesaquestionsbycategory = array(); 48 const MAX_SUBQUESTIONS = 10; 49 50 public function is_usable_by_random() { 51 return false; 52 } 53 54 public function get_question_options($question) { 55 global $DB; 56 parent::get_question_options($question); 57 $question->options = $DB->get_record('qtype_randomsamatch_options', 58 array('questionid' => $question->id)); 59 60 return true; 61 62 } 63 64 public function save_question_options($question) { 65 global $DB; 66 67 if (2 > $question->choose) { 68 $result = new stdClass(); 69 $result->error = "At least two shortanswer questions need to be chosen!"; 70 return $result; 71 } 72 73 $context = $question->context; 74 75 // Save the question options. 76 $options = $DB->get_record('qtype_randomsamatch_options', array('questionid' => $question->id)); 77 if (!$options) { 78 $options = new stdClass(); 79 $options->questionid = $question->id; 80 $options->correctfeedback = ''; 81 $options->partiallycorrectfeedback = ''; 82 $options->incorrectfeedback = ''; 83 $options->id = $DB->insert_record('qtype_randomsamatch_options', $options); 84 } 85 86 $options->choose = $question->choose; 87 $options->subcats = $question->subcats; 88 $options = $this->save_combined_feedback_helper($options, $question, $context, true); 89 $DB->update_record('qtype_randomsamatch_options', $options); 90 91 $this->save_hints($question, true); 92 93 return true; 94 } 95 96 protected function make_hint($hint) { 97 return question_hint_with_parts::load_from_record($hint); 98 } 99 100 public function delete_question($questionid, $contextid) { 101 global $DB; 102 $DB->delete_records('qtype_randomsamatch_options', array('questionid' => $questionid)); 103 104 parent::delete_question($questionid, $contextid); 105 } 106 107 public function move_files($questionid, $oldcontextid, $newcontextid) { 108 parent::move_files($questionid, $oldcontextid, $newcontextid); 109 110 $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); 111 $this->move_files_in_hints($questionid, $oldcontextid, $newcontextid); 112 } 113 114 protected function delete_files($questionid, $contextid) { 115 parent::delete_files($questionid, $contextid); 116 117 $this->delete_files_in_combined_feedback($questionid, $contextid); 118 $this->delete_files_in_hints($questionid, $contextid); 119 } 120 121 protected function initialise_question_instance(question_definition $question, $questiondata) { 122 parent::initialise_question_instance($question, $questiondata); 123 $availablesaquestions = $this->get_available_saquestions_from_category( 124 $question->category, $questiondata->options->subcats); 125 $question->shufflestems = false; 126 $question->stems = array(); 127 $question->choices = array(); 128 $question->right = array(); 129 $this->initialise_combined_feedback($question, $questiondata); 130 $question->questionsloader = new qtype_randomsamatch_question_loader( 131 $availablesaquestions, $questiondata->options->choose); 132 } 133 134 public function can_analyse_responses() { 135 return false; 136 } 137 138 /** 139 * Get all the usable shortanswer questions from a particular question category. 140 * 141 * @param integer $categoryid the id of a question category. 142 * @param bool $subcategories whether to include questions from subcategories. 143 * @return array of question records. 144 */ 145 public function get_available_saquestions_from_category($categoryid, $subcategories) { 146 if (isset($this->availablesaquestionsbycategory[$categoryid][$subcategories])) { 147 return $this->availablesaquestionsbycategory[$categoryid][$subcategories]; 148 } 149 150 if ($subcategories) { 151 $categoryids = question_categorylist($categoryid); 152 } else { 153 $categoryids = array($categoryid); 154 } 155 156 $questionids = question_bank::get_finder()->get_questions_from_categories( 157 $categoryids, "qtype = 'shortanswer'"); 158 $this->availablesaquestionsbycategory[$categoryid][$subcategories] = $questionids; 159 return $questionids; 160 } 161 162 /** 163 * @param object $question 164 * @return mixed either a integer score out of 1 that the average random 165 * guess by a student might give or an empty string which means will not 166 * calculate. 167 */ 168 public function get_random_guess_score($question) { 169 return 1/$question->options->choose; 170 } 171 172 /** 173 * Defines the table which extends the question table. This allows the base questiontype 174 * to automatically save, backup and restore the extra fields. 175 * 176 * @return an array with the table name (first) and then the column names (apart from id and questionid) 177 */ 178 public function extra_question_fields() { 179 return array('qtype_randomsamatch_options', 180 'choose', // Number of shortanswer questions to choose. 181 'subcats', // Questions can be choosen from subcategories. 182 ); 183 } 184 185 /** 186 * Imports the question from Moodle XML format. 187 * 188 * @param array $xml structure containing the XML data 189 * @param object $fromform question object to fill: ignored by this function (assumed to be null) 190 * @param qformat_xml $format format class exporting the question 191 * @param object $extra extra information (not required for importing this question in this format) 192 * @return object question object 193 */ 194 public function import_from_xml($xml, $fromform, qformat_xml $format, $extra=null) { 195 // Return if data type is not our own one. 196 if (!isset($xml['@']['type']) || $xml['@']['type'] != $this->name()) { 197 return false; 198 } 199 200 // Import the common question headers and set the corresponding field. 201 $fromform = $format->import_headers($xml); 202 $fromform->qtype = $this->name(); 203 $format->import_combined_feedback($fromform, $xml, true); 204 $format->import_hints($fromform, $xml, true); 205 206 $extras = $this->extra_question_fields(); 207 array_shift($extras); 208 foreach ($extras as $extra) { 209 $fromform->$extra = $format->getpath($xml, array('#', $extra, 0, '#'), '', true); 210 } 211 212 return $fromform; 213 } 214 215 /** 216 * Exports the question to Moodle XML format. 217 * 218 * @param object $question question to be exported into XML format 219 * @param qformat_xml $format format class exporting the question 220 * @param object $extra extra information (not required for exporting this question in this format) 221 * @return string containing the question data in XML format 222 */ 223 public function export_to_xml($question, qformat_xml $format, $extra=null) { 224 $expout = ''; 225 $expout .= $format->write_combined_feedback($question->options, 226 $question->id, 227 $question->contextid); 228 $extraquestionfields = $this->extra_question_fields(); 229 array_shift($extraquestionfields); 230 foreach ($extraquestionfields as $extra) { 231 $expout .= " <{$extra}>" . $question->options->$extra . "</{$extra}>\n"; 232 } 233 return $expout; 234 } 235 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body