weekly release 2.3.1+
[moodle.git] / question / format / examview / format.php
CommitLineData
aeb15530 1<?php
d3603157
TH
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
41a89a07 17/**
d3603157
TH
18 * Examview question importer.
19 *
20 * @package qformat
21 * @subpackage examview
22 * @copyright 2005 Howard Miller
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41a89a07 24 */
84769fd8 25
84769fd8 26
a17b297d
TH
27defined('MOODLE_INTERNAL') || die();
28
d3603157
TH
29require_once ($CFG->libdir . '/xmlize.php');
30
31
32/**
33 * Examview question importer.
34 *
35 * @copyright 2005 Howard Miller
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
f5565b69 38class qformat_examview extends qformat_default {
f34488b2 39
40 public $qtypes = array(
85474b26 41 'tf' => TRUEFALSE,
42 'mc' => MULTICHOICE,
43 'yn' => TRUEFALSE,
44 'co' => SHORTANSWER,
45 'ma' => MATCH,
46 'mtf' => 99,
47 'nr' => NUMERICAL,
48 'pr' => 99,
49 'es' => 99,
50 'ca' => 99,
51 'ot' => 99,
52 'sa' => ESSAY
53 );
f34488b2 54
55 public $matching_questions = array();
84769fd8 56
57 function provide_import() {
58 return true;
59 }
f34488b2 60
0b3940f8 61 /**
62 * unxmlise reconstructs part of the xml data structure in order
63 * to identify the actual data therein
64 * @param array $xml section of the xml data structure
65 * @return string data with evrything else removed
66 */
67 function unxmlise( $xml ) {
68 // if it's not an array then it's probably just data
69 if (!is_array($xml)) {
294ce987 70 $text = s($xml);
0b3940f8 71 }
72 else {
73 // otherwise parse the array
74 $text = '';
75 foreach ($xml as $tag=>$data) {
76 // if tag is '@' then it's attributes and we don't care
f34488b2 77 if ($tag!=='@') {
0b3940f8 78 $text = $text . $this->unxmlise( $data );
79 }
84769fd8 80 }
84769fd8 81 }
0b3940f8 82
83 // currently we throw the tags we found
84 $text = strip_tags($text);
85 return $text;
84769fd8 86 }
f34488b2 87
84769fd8 88 function parse_matching_groups($matching_groups)
89 {
90 if (empty($matching_groups)) {
91 return;
92 }
93 foreach($matching_groups as $match_group) {
94 $newgroup = NULL;
95 $groupname = trim($match_group['@']['name']);
0b3940f8 96 $questiontext = $this->unxmlise($match_group['#']['text'][0]['#']);
84769fd8 97 $newgroup->questiontext = trim($questiontext);
98 $newgroup->subchoices = array();
99 $newgroup->subquestions = array();
100 $newgroup->subanswers = array();
101 $choices = $match_group['#']['choices']['0']['#'];
102 foreach($choices as $key => $value) {
103 if (strpos(trim($key),'choice-') !== FALSE) {
104 $key = strtoupper(trim(str_replace('choice-', '', $key)));
105 $newgroup->subchoices[$key] = trim($value['0']['#']);
106 }
107 }
108 $this->matching_questions[$groupname] = $newgroup;
109 }
110 }
f34488b2 111
84769fd8 112 function parse_ma($qrec, $groupname)
113 {
114 $match_group = $this->matching_questions[$groupname];
0b3940f8 115 $phrase = trim($this->unxmlise($qrec['text']['0']['#']));
116 $answer = trim($this->unxmlise($qrec['answer']['0']['#']));
117 $answer = strip_tags( $answer );
84769fd8 118 $match_group->subquestions[] = $phrase;
119 $match_group->subanswers[] = $match_group->subchoices[$answer];
120 $this->matching_questions[$groupname] = $match_group;
121 return NULL;
122 }
f34488b2 123
84769fd8 124 function process_matches(&$questions)
125 {
126 if (empty($this->matching_questions)) {
127 return;
128 }
129 foreach($this->matching_questions as $match_group) {
130 $question = $this->defaultquestion();
294ce987 131 $htmltext = s($match_group->questiontext);
84769fd8 132 $question->questiontext = $htmltext;
133 $question->name = $question->questiontext;
134 $question->qtype = MATCH;
84769fd8 135 $question->subquestions = array();
136 $question->subanswers = array();
137 foreach($match_group->subquestions as $key => $value) {
294ce987 138 $htmltext = s($value);
84769fd8 139 $question->subquestions[] = $htmltext;
140
294ce987 141 $htmltext = s($match_group->subanswers[$key]);
84769fd8 142 $question->subanswers[] = $htmltext;
143 }
144 $questions[] = $question;
145 }
146 }
f34488b2 147
2befe778 148 function cleanUnicode($text) {
2befe778 149 return str_replace('&#x2019;', "'", $text);
150 }
06968068 151
4d188926 152 protected function readquestions($lines) {
84769fd8 153 /// Parses an array of lines into an array of questions,
154 /// where each item is a question object as defined by
155 /// readquestion().
06968068 156
84769fd8 157 $questions = array();
158 $currentquestion = array();
f34488b2 159
84769fd8 160 $text = implode($lines, ' ');
2befe778 161 $text = $this->cleanUnicode($text);
84769fd8 162
163 $xml = xmlize($text, 0);
06968068 164 if (!empty($xml['examview']['#']['matching-group'])) {
165 $this->parse_matching_groups($xml['examview']['#']['matching-group']);
166 }
f34488b2 167
84769fd8 168 $questionNode = $xml['examview']['#']['question'];
169 foreach($questionNode as $currentquestion) {
170 if ($question = $this->readquestion($currentquestion)) {
171 $questions[] = $question;
172 }
173 }
f34488b2 174
84769fd8 175 $this->process_matches($questions);
84769fd8 176 return $questions;
177 }
178 // end readquestions
f34488b2 179
84769fd8 180 function readquestion($qrec)
181 {
f34488b2 182
84769fd8 183 $type = trim($qrec['@']['type']);
184 $question = $this->defaultquestion();
a60b5c16 185 if (array_key_exists($type, $this->qtypes)) {
186 $question->qtype = $this->qtypes[$type];
187 }
188 else {
189 $question->qtype = null;
190 }
84769fd8 191 $question->single = 1;
192 // Only one answer is allowed
0b3940f8 193 $htmltext = $this->unxmlise($qrec['#']['text'][0]['#']);
84769fd8 194 $question->questiontext = $htmltext;
85474b26 195 $question->name = shorten_text( $question->questiontext, 250 );
f34488b2 196
84769fd8 197 switch ($question->qtype) {
198 case MULTICHOICE:
199 $question = $this->parse_mc($qrec['#'], $question);
200 break;
201 case MATCH:
202 $groupname = trim($qrec['@']['group']);
203 $question = $this->parse_ma($qrec['#'], $groupname);
204 break;
205 case TRUEFALSE:
206 $question = $this->parse_tf_yn($qrec['#'], $question);
207 break;
208 case SHORTANSWER:
209 $question = $this->parse_co($qrec['#'], $question);
210 break;
85474b26 211 case ESSAY:
212 $question = $this->parse_sa($qrec['#'], $question);
213 break;
84769fd8 214 case NUMERICAL:
215 $question = $this->parse_nr($qrec['#'], $question);
216 break;
217 break;
218 default:
219 print("<p>Question type ".$type." import not supported for ".$question->questiontext."<p>");
220 $question = NULL;
221 }
222 // end switch ($question->qtype)
f34488b2 223
84769fd8 224 return $question;
225 }
226 // end readquestion
f34488b2 227
84769fd8 228 function parse_tf_yn($qrec, $question)
229 {
230 $choices = array('T' => 1, 'Y' => 1, 'F' => 0, 'N' => 0 );
231 $answer = trim($qrec['answer'][0]['#']);
232 $question->answer = $choices[$answer];
fa5cb18b 233 $question->correctanswer = $question->answer;
84769fd8 234 if ($question->answer == 1) {
235 $question->feedbacktrue = 'Correct';
236 $question->feedbackfalse = 'Incorrect';
237 } else {
238 $question->feedbacktrue = 'Incorrect';
239 $question->feedbackfalse = 'Correct';
240 }
241 return $question;
242 }
f34488b2 243
84769fd8 244 function parse_mc($qrec, $question)
245 {
246 $answer = 'choice-'.strtolower(trim($qrec['answer'][0]['#']));
f34488b2 247
84769fd8 248 $choices = $qrec['choices'][0]['#'];
249 foreach($choices as $key => $value) {
250 if (strpos(trim($key),'choice-') !== FALSE) {
f34488b2 251
0b3940f8 252 $question->answer[$key] = s($this->unxmlise($value[0]['#']));
84769fd8 253 if (strcmp($key, $answer) == 0) {
254 $question->fraction[$key] = 1;
255 $question->feedback[$key] = 'Correct';
256 } else {
257 $question->fraction[$key] = 0;
258 $question->feedback[$key] = 'Incorrect';
259 }
260 }
261 }
262 return $question;
263 }
f34488b2 264
84769fd8 265 function parse_co($qrec, $question)
266 {
267 $question->usecase = 0;
0b3940f8 268 $answer = trim($this->unxmlise($qrec['answer'][0]['#']));
269 $answer = strip_tags( $answer );
84769fd8 270 $answers = explode("\n",$answer);
f34488b2 271
84769fd8 272 foreach($answers as $key => $value) {
273 $value = trim($value);
274 if (strlen($value) > 0) {
294ce987 275 $question->answer[$key] = $value;
84769fd8 276 $question->fraction[$key] = 1;
277 $question->feedback[$key] = "Correct";
278 }
279 }
280 return $question;
281 }
85474b26 282
283 function parse_sa($qrec, $question) {
284 $feedback = trim($this->unxmlise($qrec['answer'][0]['#']));
285 $question->feedback = $feedback;
286 $question->fraction = 0;
287 return $question;
288 }
f34488b2 289
84769fd8 290 function parse_nr($qrec, $question)
291 {
0b3940f8 292 $answer = trim($this->unxmlise($qrec['answer'][0]['#']));
293 $answer = strip_tags( $answer );
84769fd8 294 $answers = explode("\n",$answer);
f34488b2 295
84769fd8 296 foreach($answers as $key => $value) {
297 $value = trim($value);
298 if (is_numeric($value)) {
299 $errormargin = 0;
300 $question->answer[$key] = $value;
301 $question->fraction[$key] = 1;
302 $question->feedback[$key] = "Correct";
303 $question->min[$key] = $question->answer[$key] - $errormargin;
304 $question->max[$key] = $question->answer[$key] + $errormargin;
305 }
306 }
307 return $question;
f34488b2 308 }
309
84769fd8 310}
311// end class
312
aeb15530 313