Differences Between: [Versions 310 and 311] [Versions 39 and 311]
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 * Examview question importer. 19 * 20 * @package qformat_examview 21 * @copyright 2005 Howard Miller 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->libdir . '/xmlize.php'); 29 30 31 /** 32 * Examview question importer. 33 * 34 * @copyright 2005 Howard Miller 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class qformat_examview extends qformat_based_on_xml { 38 39 public $qtypes = array( 40 'tf' => 'truefalse', 41 'mc' => 'multichoice', 42 'yn' => 'truefalse', 43 'co' => 'shortanswer', 44 'ma' => 'match', 45 'mtf' => 99, 46 'nr' => 'numerical', 47 'pr' => 99, 48 'es' => 'essay', 49 'ca' => 99, 50 'ot' => 99, 51 'sa' => 'shortanswer', 52 ); 53 54 public $matchingquestions = array(); 55 56 public function provide_import() { 57 return true; 58 } 59 60 public function mime_type() { 61 return 'application/xml'; 62 } 63 64 /** 65 * Validate the given file. 66 * 67 * For more expensive or detailed integrity checks. 68 * 69 * @param stored_file $file the file to check 70 * @return string the error message that occurred while validating the given file 71 */ 72 public function validate_file(stored_file $file): string { 73 return $this->validate_is_utf8_file($file); 74 } 75 76 /** 77 * unxmlise reconstructs part of the xml data structure in order 78 * to identify the actual data therein 79 * @param array $xml section of the xml data structure 80 * @return string data with evrything else removed 81 */ 82 protected function unxmlise( $xml ) { 83 // If it's not an array then it's probably just data. 84 if (!is_array($xml)) { 85 $text = s($xml); 86 } else { 87 // Otherwise parse the array. 88 $text = ''; 89 foreach ($xml as $tag => $data) { 90 // If tag is '@' then it's attributes and we don't care. 91 if ($tag!=='@') { 92 $text = $text . $this->unxmlise( $data ); 93 } 94 } 95 } 96 97 // Currently we throw the tags we found. 98 $text = strip_tags($text); 99 return $text; 100 } 101 102 public function parse_matching_groups($matchinggroups) { 103 if (empty($matchinggroups)) { 104 return; 105 } 106 foreach ($matchinggroups as $matchgroup) { 107 $newgroup = new stdClass(); 108 $groupname = trim($matchgroup['@']['name']); 109 $questiontext = $this->unxmlise($matchgroup['#']['text'][0]['#']); 110 $newgroup->questiontext = trim($questiontext); 111 $newgroup->subchoices = array(); 112 $newgroup->subquestions = array(); 113 $newgroup->subanswers = array(); 114 $choices = $matchgroup['#']['choices']['0']['#']; 115 foreach ($choices as $key => $value) { 116 if (strpos(trim($key), 'choice-') !== false) { 117 $key = strtoupper(trim(str_replace('choice-', '', $key))); 118 $newgroup->subchoices[$key] = trim($value['0']['#']); 119 } 120 } 121 $this->matching_questions[$groupname] = $newgroup; 122 } 123 } 124 125 protected function parse_ma($qrec, $groupname) { 126 $matchgroup = $this->matching_questions[$groupname]; 127 $phrase = trim($this->unxmlise($qrec['text']['0']['#'])); 128 $answer = trim($this->unxmlise($qrec['answer']['0']['#'])); 129 $answer = strip_tags( $answer ); 130 $matchgroup->mappings[$phrase] = $matchgroup->subchoices[$answer]; 131 $this->matching_questions[$groupname] = $matchgroup; 132 return null; 133 } 134 135 protected function process_matches(&$questions) { 136 if (empty($this->matching_questions)) { 137 return; 138 } 139 140 foreach ($this->matching_questions as $matchgroup) { 141 $question = $this->defaultquestion(); 142 $htmltext = s($matchgroup->questiontext); 143 $question->questiontext = $htmltext; 144 $question->questiontextformat = FORMAT_HTML; 145 $question->questiontextfiles = array(); 146 $question->name = $this->create_default_question_name($question->questiontext, get_string('questionname', 'question')); 147 $question->qtype = 'match'; 148 $question = $this->add_blank_combined_feedback($question); 149 $question->subquestions = array(); 150 $question->subanswers = array(); 151 foreach ($matchgroup->subchoices as $subchoice) { 152 $fiber = array_keys ($matchgroup->mappings, $subchoice); 153 $subquestion = ''; 154 foreach ($fiber as $subquestion) { 155 $question->subquestions[] = $this->text_field($subquestion); 156 $question->subanswers[] = $subchoice; 157 } 158 if ($subquestion == '') { // Then in this case, $subchoice is a distractor. 159 $question->subquestions[] = $this->text_field(''); 160 $question->subanswers[] = $subchoice; 161 } 162 } 163 $questions[] = $question; 164 } 165 } 166 167 protected function cleanunicode($text) { 168 return str_replace('’', "'", $text); 169 } 170 171 public function readquestions($lines) { 172 // Parses an array of lines into an array of questions, 173 // where each item is a question object as defined by 174 // readquestion(). 175 176 $questions = array(); 177 $currentquestion = array(); 178 179 $text = implode(' ', $lines); 180 $text = $this->cleanunicode($text); 181 182 $xml = xmlize($text, 0); 183 if (!empty($xml['examview']['#']['matching-group'])) { 184 $this->parse_matching_groups($xml['examview']['#']['matching-group']); 185 } 186 187 $questionnode = $xml['examview']['#']['question']; 188 foreach ($questionnode as $currentquestion) { 189 if ($question = $this->readquestion($currentquestion)) { 190 $questions[] = $question; 191 } 192 } 193 194 $this->process_matches($questions); 195 return $questions; 196 } 197 198 public function readquestion($qrec) { 199 global $OUTPUT; 200 201 $type = trim($qrec['@']['type']); 202 $question = $this->defaultquestion(); 203 if (array_key_exists($type, $this->qtypes)) { 204 $question->qtype = $this->qtypes[$type]; 205 } else { 206 $question->qtype = null; 207 } 208 $question->single = 1; 209 210 // Only one answer is allowed. 211 $htmltext = $this->unxmlise($qrec['#']['text'][0]['#']); 212 213 $question->questiontext = $this->cleaninput($htmltext); 214 $question->questiontextformat = FORMAT_HTML; 215 $question->questiontextfiles = array(); 216 $question->name = $this->create_default_question_name($question->questiontext, get_string('questionname', 'question')); 217 218 switch ($question->qtype) { 219 case 'multichoice': 220 $question = $this->parse_mc($qrec['#'], $question); 221 break; 222 case 'match': 223 $groupname = trim($qrec['@']['group']); 224 $question = $this->parse_ma($qrec['#'], $groupname); 225 break; 226 case 'truefalse': 227 $question = $this->parse_tf_yn($qrec['#'], $question); 228 break; 229 case 'shortanswer': 230 $question = $this->parse_co($qrec['#'], $question); 231 break; 232 case 'essay': 233 $question = $this->parse_es($qrec['#'], $question); 234 break; 235 case 'numerical': 236 $question = $this->parse_nr($qrec['#'], $question); 237 break; 238 break; 239 default: 240 echo $OUTPUT->notification(get_string('unknownorunhandledtype', 'question', $type)); 241 $question = null; 242 } 243 244 return $question; 245 } 246 247 protected function parse_tf_yn($qrec, $question) { 248 $choices = array('T' => 1, 'Y' => 1, 'F' => 0, 'N' => 0 ); 249 $answer = trim($qrec['answer'][0]['#']); 250 $question->answer = $choices[$answer]; 251 $question->correctanswer = $question->answer; 252 if ($question->answer == 1) { 253 $question->feedbacktrue = $this->text_field(get_string('correct', 'question')); 254 $question->feedbackfalse = $this->text_field(get_string('incorrect', 'question')); 255 } else { 256 $question->feedbacktrue = $this->text_field(get_string('incorrect', 'question')); 257 $question->feedbackfalse = $this->text_field(get_string('correct', 'question')); 258 } 259 return $question; 260 } 261 262 protected function parse_mc($qrec, $question) { 263 $question = $this->add_blank_combined_feedback($question); 264 $answer = 'choice-'.strtolower(trim($qrec['answer'][0]['#'])); 265 266 $choices = $qrec['choices'][0]['#']; 267 foreach ($choices as $key => $value) { 268 if (strpos(trim($key), 'choice-') !== false) { 269 270 $question->answer[] = $this->text_field(s($this->unxmlise($value[0]['#']))); 271 if (strcmp($key, $answer) == 0) { 272 $question->fraction[] = 1; 273 $question->feedback[] = $this->text_field(get_string('correct', 'question')); 274 } else { 275 $question->fraction[] = 0; 276 $question->feedback[] = $this->text_field(get_string('incorrect', 'question')); 277 } 278 } 279 } 280 return $question; 281 } 282 283 protected function parse_co($qrec, $question) { 284 $question->usecase = 0; 285 $answer = trim($this->unxmlise($qrec['answer'][0]['#'])); 286 $answer = strip_tags( $answer ); 287 $answers = explode("\n", $answer); 288 289 foreach ($answers as $key => $value) { 290 $value = trim($value); 291 if (strlen($value) > 0) { 292 $question->answer[] = $value; 293 $question->fraction[] = 1; 294 $question->feedback[] = $this->text_field(get_string('correct', 'question')); 295 } 296 } 297 $question->answer[] = '*'; 298 $question->fraction[] = 0; 299 $question->feedback[] = $this->text_field(get_string('incorrect', 'question')); 300 301 return $question; 302 } 303 304 protected function parse_es($qrec, $question) { 305 $feedback = trim($this->unxmlise($qrec['answer'][0]['#'])); 306 $question->graderinfo = $this->text_field($feedback); 307 $question->responsetemplate = $this->text_field(''); 308 $question->responserequired = 1; 309 $question->feedback = $feedback; 310 $question->responseformat = 'editor'; 311 $question->responsefieldlines = 15; 312 $question->attachments = 0; 313 $question->attachmentsrequired = 0; 314 $question->fraction = 0; 315 return $question; 316 } 317 318 protected function parse_nr($qrec, $question) { 319 $answer = trim($this->unxmlise($qrec['answer'][0]['#'])); 320 $answer = strip_tags( $answer ); 321 $answers = explode("\n", $answer); 322 323 foreach ($answers as $key => $value) { 324 $value = trim($value); 325 if (is_numeric($value)) { 326 $errormargin = 0; 327 $question->answer[] = $value; 328 $question->fraction[] = 1; 329 $question->feedback[] = $this->text_field(get_string('correct', 'question')); 330 $question->tolerance[] = $errormargin; 331 } 332 } 333 return $question; 334 } 335 336 } 337 // End class. 338 339
title
Description
Body
title
Description
Body
title
Description
Body
title
Body