[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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_match 21 * @copyright 2009 The Open University 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 29 /** 30 * Represents a matching question. 31 * 32 * @copyright 2009 The Open University 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class qtype_match_question extends question_graded_automatically_with_countback { 36 /** @var boolean Whether the question stems should be shuffled. */ 37 public $shufflestems; 38 39 public $correctfeedback; 40 public $correctfeedbackformat; 41 public $partiallycorrectfeedback; 42 public $partiallycorrectfeedbackformat; 43 public $incorrectfeedback; 44 public $incorrectfeedbackformat; 45 46 /** @var array of question stems. */ 47 public $stems; 48 /** @var array of choices that can be matched to each stem. */ 49 public $choices; 50 /** @var array index of the right choice for each stem. */ 51 public $right; 52 53 /** @var array shuffled stem indexes. */ 54 protected $stemorder; 55 /** @var array shuffled choice indexes. */ 56 protected $choiceorder; 57 58 public function start_attempt(question_attempt_step $step, $variant) { 59 $this->stemorder = array_keys($this->stems); 60 if ($this->shufflestems) { 61 shuffle($this->stemorder); 62 } 63 $step->set_qt_var('_stemorder', implode(',', $this->stemorder)); 64 65 $choiceorder = array_keys($this->choices); 66 shuffle($choiceorder); 67 $step->set_qt_var('_choiceorder', implode(',', $choiceorder)); 68 $this->set_choiceorder($choiceorder); 69 } 70 71 public function apply_attempt_state(question_attempt_step $step) { 72 $this->stemorder = explode(',', $step->get_qt_var('_stemorder')); 73 $this->set_choiceorder(explode(',', $step->get_qt_var('_choiceorder'))); 74 } 75 76 /** 77 * Helper method used by both {@link start_attempt()} and 78 * {@link apply_attempt_state()}. 79 * @param array $choiceorder the choices, in order. 80 */ 81 protected function set_choiceorder($choiceorder) { 82 $this->choiceorder = array(); 83 foreach ($choiceorder as $key => $value) { 84 $this->choiceorder[$key + 1] = $value; 85 } 86 } 87 88 public function get_question_summary() { 89 $question = $this->html_to_text($this->questiontext, $this->questiontextformat); 90 $stems = array(); 91 foreach ($this->stemorder as $stemid) { 92 $stems[] = $this->html_to_text($this->stems[$stemid], $this->stemformat[$stemid]); 93 } 94 $choices = array(); 95 foreach ($this->choiceorder as $choiceid) { 96 $choices[] = $this->choices[$choiceid]; 97 } 98 return $question . ' {' . implode('; ', $stems) . '} -> {' . 99 implode('; ', $choices) . '}'; 100 } 101 102 public function summarise_response(array $response) { 103 $matches = array(); 104 foreach ($this->stemorder as $key => $stemid) { 105 if (array_key_exists($this->field($key), $response) && $response[$this->field($key)]) { 106 $matches[] = $this->html_to_text($this->stems[$stemid], 107 $this->stemformat[$stemid]) . ' -> ' . 108 $this->choices[$this->choiceorder[$response[$this->field($key)]]]; 109 } 110 } 111 if (empty($matches)) { 112 return null; 113 } 114 return implode('; ', $matches); 115 } 116 117 public function classify_response(array $response) { 118 $selectedchoices = array(); 119 foreach ($this->stemorder as $key => $stemid) { 120 if (array_key_exists($this->field($key), $response) && $response[$this->field($key)]) { 121 $selectedchoices[$stemid] = $this->choiceorder[$response[$this->field($key)]]; 122 } else { 123 $selectedchoices[$stemid] = 0; 124 } 125 } 126 127 $parts = array(); 128 foreach ($this->stems as $stemid => $stem) { 129 if (empty($selectedchoices[$stemid])) { 130 $parts[$stemid] = question_classified_response::no_response(); 131 continue; 132 } 133 $choice = $this->choices[$selectedchoices[$stemid]]; 134 $parts[$stemid] = new question_classified_response( 135 $selectedchoices[$stemid], $choice, 136 ($selectedchoices[$stemid] == $this->right[$stemid]) / count($this->stems)); 137 } 138 return $parts; 139 } 140 141 public function clear_wrong_from_response(array $response) { 142 foreach ($this->stemorder as $key => $stemid) { 143 if (!array_key_exists($this->field($key), $response) || 144 $response[$this->field($key)] != $this->get_right_choice_for($stemid)) { 145 $response[$this->field($key)] = 0; 146 } 147 } 148 return $response; 149 } 150 151 public function get_num_parts_right(array $response) { 152 $numright = 0; 153 foreach ($this->stemorder as $key => $stemid) { 154 $fieldname = $this->field($key); 155 if (!array_key_exists($fieldname, $response)) { 156 continue; 157 } 158 159 $choice = $response[$fieldname]; 160 if ($choice && $this->choiceorder[$choice] == $this->right[$stemid]) { 161 $numright += 1; 162 } 163 } 164 return array($numright, count($this->stemorder)); 165 } 166 167 /** 168 * @param int $key stem number 169 * @return string the question-type variable name. 170 */ 171 protected function field($key) { 172 return 'sub' . $key; 173 } 174 175 public function get_expected_data() { 176 $vars = array(); 177 foreach ($this->stemorder as $key => $notused) { 178 $vars[$this->field($key)] = PARAM_INT; 179 } 180 return $vars; 181 } 182 183 public function get_correct_response() { 184 $response = array(); 185 foreach ($this->stemorder as $key => $stemid) { 186 $response[$this->field($key)] = $this->get_right_choice_for($stemid); 187 } 188 return $response; 189 } 190 191 public function prepare_simulated_post_data($simulatedresponse) { 192 $postdata = array(); 193 $stemtostemids = array_flip(clean_param_array($this->stems, PARAM_NOTAGS)); 194 $choicetochoiceno = array_flip($this->choices); 195 $choicenotochoiceselectvalue = array_flip($this->choiceorder); 196 foreach ($simulatedresponse as $stem => $choice) { 197 $choice = clean_param($choice, PARAM_NOTAGS); 198 $stemid = $stemtostemids[$stem]; 199 $shuffledstemno = array_search($stemid, $this->stemorder); 200 if (empty($choice)) { 201 $choiceselectvalue = 0; 202 } else if ($choicetochoiceno[$choice]) { 203 $choiceselectvalue = $choicenotochoiceselectvalue[$choicetochoiceno[$choice]]; 204 } else { 205 throw new coding_exception("Unknown choice {$choice} in matching question - {$this->name}."); 206 } 207 $postdata[$this->field($shuffledstemno)] = $choiceselectvalue; 208 } 209 return $postdata; 210 } 211 212 public function get_student_response_values_for_simulation($postdata) { 213 $simulatedresponse = array(); 214 foreach ($this->stemorder as $shuffledstemno => $stemid) { 215 if (!empty($postdata[$this->field($shuffledstemno)])) { 216 $choiceselectvalue = $postdata[$this->field($shuffledstemno)]; 217 $choiceno = $this->choiceorder[$choiceselectvalue]; 218 $choice = clean_param($this->choices[$choiceno], PARAM_NOTAGS); 219 $stem = clean_param($this->stems[$stemid], PARAM_NOTAGS); 220 $simulatedresponse[$stem] = $choice; 221 } 222 } 223 ksort($simulatedresponse); 224 return $simulatedresponse; 225 } 226 227 public function get_right_choice_for($stemid) { 228 foreach ($this->choiceorder as $choicekey => $choiceid) { 229 if ($this->right[$stemid] == $choiceid) { 230 return $choicekey; 231 } 232 } 233 } 234 235 public function is_complete_response(array $response) { 236 $complete = true; 237 foreach ($this->stemorder as $key => $stemid) { 238 $complete = $complete && !empty($response[$this->field($key)]); 239 } 240 return $complete; 241 } 242 243 public function is_gradable_response(array $response) { 244 foreach ($this->stemorder as $key => $stemid) { 245 if (!empty($response[$this->field($key)])) { 246 return true; 247 } 248 } 249 return false; 250 } 251 252 public function get_validation_error(array $response) { 253 if ($this->is_complete_response($response)) { 254 return ''; 255 } 256 return get_string('pleaseananswerallparts', 'qtype_match'); 257 } 258 259 public function is_same_response(array $prevresponse, array $newresponse) { 260 foreach ($this->stemorder as $key => $notused) { 261 $fieldname = $this->field($key); 262 if (!question_utils::arrays_same_at_key_integer( 263 $prevresponse, $newresponse, $fieldname)) { 264 return false; 265 } 266 } 267 return true; 268 } 269 270 public function grade_response(array $response) { 271 list($right, $total) = $this->get_num_parts_right($response); 272 $fraction = $right / $total; 273 return array($fraction, question_state::graded_state_for_fraction($fraction)); 274 } 275 276 public function compute_final_grade($responses, $totaltries) { 277 $totalstemscore = 0; 278 foreach ($this->stemorder as $key => $stemid) { 279 $fieldname = $this->field($key); 280 281 $lastwrongindex = -1; 282 $finallyright = false; 283 foreach ($responses as $i => $response) { 284 if (!array_key_exists($fieldname, $response) || !$response[$fieldname] || 285 $this->choiceorder[$response[$fieldname]] != $this->right[$stemid]) { 286 $lastwrongindex = $i; 287 $finallyright = false; 288 } else { 289 $finallyright = true; 290 } 291 } 292 293 if ($finallyright) { 294 $totalstemscore += max(0, 1 - ($lastwrongindex + 1) * $this->penalty); 295 } 296 } 297 298 return $totalstemscore / count($this->stemorder); 299 } 300 301 public function get_stem_order() { 302 return $this->stemorder; 303 } 304 305 public function get_choice_order() { 306 return $this->choiceorder; 307 } 308 309 public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { 310 if ($component == 'qtype_match' && $filearea == 'subquestion') { 311 $subqid = reset($args); // Itemid is sub question id. 312 return array_key_exists($subqid, $this->stems); 313 314 } else if ($component == 'question' && in_array($filearea, 315 array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) { 316 return $this->check_combined_feedback_file_access($qa, $options, $filearea); 317 318 } else if ($component == 'question' && $filearea == 'hint') { 319 return $this->check_hint_file_access($qa, $options, $args); 320 321 } else { 322 return parent::check_file_access($qa, $options, $component, $filearea, 323 $args, $forcedownload); 324 } 325 } 326 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |