84769fd8 |
1 | <?php // $Id$ |
2 | /* |
3 | ** |
4 | ** Examview 4.0 XML format into Moodle 1.4.3 |
5 | ** Author: Dan McCuaig ( dmccuaig@wvc.edu ) |
6 | ** |
7 | ** @TODO: |
8 | ** Take care of odd unicode character mapping (ex: curly quotes) |
9 | ** Image and table support |
10 | ** Formatting support |
11 | ** Support of rejoinders |
12 | ** |
13 | ** $Log$ |
14 | ** Revision 1.1 2006/02/24 15:14:04 thepurpleblob |
15 | ** Moving quiz import/export files to new question area. |
16 | ** |
17 | ** Revision 1.3 2006/02/13 16:17:22 thepurpleblob |
18 | ** Now used defaultquestion() method. Bug #4752 |
19 | ** |
20 | ** Revision 1.2 2005/05/31 15:19:00 thepurpleblob |
21 | ** merged from STABLE |
22 | ** |
23 | ** Revision 1.1.2.1 2005/05/31 15:17:20 thepurpleblob |
24 | ** Took out dos line endings |
25 | ** |
26 | ** Revision 1.1 2005/05/16 08:12:40 thepurpleblob |
27 | ** Added support for Examview import. |
28 | ** |
29 | ** |
30 | */ |
31 | |
32 | // Based on default.php, included by ../import.php |
33 | |
34 | require_once("$CFG->libdir/xmlize.php"); |
35 | //require_once("xmlize.php"); |
36 | |
37 | /* |
38 | define("SHORTANSWER", "1"); |
39 | define("TRUEFALSE", "2"); |
40 | define("MULTICHOICE", "3"); |
41 | define("MATCH", "5"); |
42 | define("DESCRIPTION", "7"); |
43 | define("NUMERICAL", "8"); |
44 | define("MULTIANSWER", "9"); |
45 | define("CALCULATED", "10"); |
46 | */ |
47 | |
48 | class quiz_format_examview extends quiz_default_format { |
49 | |
50 | var $qtypes = array('tf' => TRUEFALSE, |
51 | 'mc' => MULTICHOICE, |
52 | 'yn' => TRUEFALSE, |
53 | 'co' => SHORTANSWER, |
54 | 'ma' => MATCH, |
55 | 'mtf' => 99, |
56 | 'nr' => NUMERICAL, |
57 | 'pr' => 99, |
58 | 'es' => 99, |
59 | 'ca' => 99, |
60 | 'ot' => 99 |
61 | ); |
62 | |
63 | var $matching_questions = array(); |
64 | |
65 | function provide_import() { |
66 | return true; |
67 | } |
68 | |
69 | function print_matching_questions() |
70 | { |
71 | foreach($this->matching_questions as $key => $value) { |
72 | print("$key => $value->questiontext<BR>"); |
73 | print("Questions:<UL>"); |
74 | foreach($value->subquestions as $qkey => $qvalue) { |
75 | print("<LI>$qkey => $qvalue</LI>"); |
76 | } |
77 | print("</UL>"); |
78 | print("Choices:<UL>"); |
79 | foreach($value->subchoices as $ckey => $cvalue) { |
80 | print("<LI>$ckey => $cvalue</LI>"); |
81 | } |
82 | print("</UL>"); |
83 | print("Answers:<UL>"); |
84 | foreach($value->subanswers as $akey => $avalue) { |
85 | print("<LI>$akey => $avalue</LI>"); |
86 | } |
87 | print("</UL>"); |
88 | } |
89 | } |
90 | |
91 | function parse_matching_groups($matching_groups) |
92 | { |
93 | if (empty($matching_groups)) { |
94 | return; |
95 | } |
96 | foreach($matching_groups as $match_group) { |
97 | $newgroup = NULL; |
98 | $groupname = trim($match_group['@']['name']); |
99 | $questiontext = $this->ArrayTagToString($match_group['#']['text']['0']['#']); |
100 | $newgroup->questiontext = trim($questiontext); |
101 | $newgroup->subchoices = array(); |
102 | $newgroup->subquestions = array(); |
103 | $newgroup->subanswers = array(); |
104 | $choices = $match_group['#']['choices']['0']['#']; |
105 | foreach($choices as $key => $value) { |
106 | if (strpos(trim($key),'choice-') !== FALSE) { |
107 | $key = strtoupper(trim(str_replace('choice-', '', $key))); |
108 | $newgroup->subchoices[$key] = trim($value['0']['#']); |
109 | } |
110 | } |
111 | $this->matching_questions[$groupname] = $newgroup; |
112 | } |
113 | } |
114 | |
115 | function parse_ma($qrec, $groupname) |
116 | { |
117 | $match_group = $this->matching_questions[$groupname]; |
118 | $phrase = trim($qrec['text']['0']['#']); |
119 | $answer = trim($qrec['answer']['0']['#']); |
120 | $match_group->subquestions[] = $phrase; |
121 | $match_group->subanswers[] = $match_group->subchoices[$answer]; |
122 | $this->matching_questions[$groupname] = $match_group; |
123 | return NULL; |
124 | } |
125 | |
126 | function process_matches(&$questions) |
127 | { |
128 | if (empty($this->matching_questions)) { |
129 | return; |
130 | } |
131 | foreach($this->matching_questions as $match_group) { |
132 | $question = $this->defaultquestion(); |
133 | $htmltext = $this->htmlPrepare($match_group->questiontext); |
134 | $htmltext = addslashes($htmltext); |
135 | $question->questiontext = $htmltext; |
136 | $question->name = $question->questiontext; |
137 | $question->qtype = MATCH; |
138 | // No images with this format |
139 | // print($question->questiontext.' '.$question->id."<BR>"); |
140 | |
141 | $question->subquestions = array(); |
142 | $question->subanswers = array(); |
143 | foreach($match_group->subquestions as $key => $value) { |
144 | $htmltext = $this->htmlPrepare($value); |
145 | $htmltext = addslashes($htmltext); |
146 | $question->subquestions[] = $htmltext; |
147 | |
148 | $htmltext = $this->htmlPrepare($match_group->subanswers[$key]); |
149 | $htmltext = addslashes($htmltext); |
150 | $question->subanswers[] = $htmltext; |
151 | } |
152 | $questions[] = $question; |
153 | } |
154 | } |
155 | |
156 | // cleans unicode characters from string |
157 | // add to the array unicode_array as necessary |
158 | function cleanUnicode($text) { |
159 | //$unicode_array = array( "ߣ" => "'"); |
160 | //return strtr($text, $unicode_array); |
161 | return str_replace('’', "'", $text); |
162 | } |
163 | |
164 | function readquestions($lines) |
165 | { |
166 | /// Parses an array of lines into an array of questions, |
167 | /// where each item is a question object as defined by |
168 | /// readquestion(). |
169 | |
170 | $questions = array(); |
171 | $currentquestion = array(); |
172 | |
173 | $text = implode($lines, ' '); |
174 | $text = $this->cleanUnicode($text); |
175 | |
176 | $xml = xmlize($text, 0); |
177 | $this->parse_matching_groups($xml['examview']['#']['matching-group']); |
178 | |
179 | $questionNode = $xml['examview']['#']['question']; |
180 | foreach($questionNode as $currentquestion) { |
181 | if ($question = $this->readquestion($currentquestion)) { |
182 | $questions[] = $question; |
183 | } |
184 | } |
185 | |
186 | // print('<hr>'); |
187 | // $this->print_matching_questions(); |
188 | $this->process_matches($questions); |
189 | // print('<hr>'); |
190 | |
191 | return $questions; |
192 | } |
193 | // end readquestions |
194 | |
195 | function htmlPrepare($htmltext) |
196 | { |
197 | $text = trim($text); |
198 | $text = htmlentities($htmltext, ENT_QUOTES); |
199 | //$htmltext = nl2br($text); |
200 | return $text; |
201 | } |
202 | |
203 | function ArrayTagToString($aTag) |
204 | { |
205 | if (!is_array($aTag)) { |
206 | return $aTag; |
207 | } |
208 | $out = ''; |
209 | foreach($aTag as $key => $value) { |
210 | if (is_array($value)) { |
211 | $out = $out.$this->ArrayTagToString($value); |
212 | } else { |
213 | $out = $value; |
214 | } |
215 | } |
216 | return $out; |
217 | } |
218 | |
219 | |
220 | function readquestion($qrec) |
221 | { |
222 | |
223 | $type = trim($qrec['@']['type']); |
224 | $question = $this->defaultquestion(); |
225 | $question->qtype = $this->qtypes[$type]; |
226 | $question->single = 1; |
227 | // Only one answer is allowed |
228 | $htmltext = $this->ArrayTagToString($qrec['#']['text'][0]['#']); |
229 | $htmltext = $this->htmlPrepare($htmltext); |
230 | $htmltext = addslashes($htmltext); |
231 | $question->questiontext = $htmltext; |
232 | $question->name = $question->questiontext; |
233 | |
234 | switch ($question->qtype) { |
235 | case MULTICHOICE: |
236 | $question = $this->parse_mc($qrec['#'], $question); |
237 | break; |
238 | case MATCH: |
239 | $groupname = trim($qrec['@']['group']); |
240 | $question = $this->parse_ma($qrec['#'], $groupname); |
241 | break; |
242 | case TRUEFALSE: |
243 | $question = $this->parse_tf_yn($qrec['#'], $question); |
244 | break; |
245 | case SHORTANSWER: |
246 | $question = $this->parse_co($qrec['#'], $question); |
247 | break; |
248 | case NUMERICAL: |
249 | $question = $this->parse_nr($qrec['#'], $question); |
250 | break; |
251 | break; |
252 | default: |
253 | print("<p>Question type ".$type." import not supported for ".$question->questiontext."<p>"); |
254 | $question = NULL; |
255 | } |
256 | // end switch ($question->qtype) |
257 | |
258 | return $question; |
259 | } |
260 | // end readquestion |
261 | |
262 | function parse_tf_yn($qrec, $question) |
263 | { |
264 | $choices = array('T' => 1, 'Y' => 1, 'F' => 0, 'N' => 0 ); |
265 | $answer = trim($qrec['answer'][0]['#']); |
266 | $question->answer = $choices[$answer]; |
267 | if ($question->answer == 1) { |
268 | $question->feedbacktrue = 'Correct'; |
269 | $question->feedbackfalse = 'Incorrect'; |
270 | } else { |
271 | $question->feedbacktrue = 'Incorrect'; |
272 | $question->feedbackfalse = 'Correct'; |
273 | } |
274 | return $question; |
275 | } |
276 | |
277 | function parse_mc($qrec, $question) |
278 | { |
279 | $answer = 'choice-'.strtolower(trim($qrec['answer'][0]['#'])); |
280 | |
281 | $choices = $qrec['choices'][0]['#']; |
282 | foreach($choices as $key => $value) { |
283 | if (strpos(trim($key),'choice-') !== FALSE) { |
284 | |
285 | $question->answer[$key] = $this->htmlPrepare($value[0]['#']); |
286 | if (strcmp($key, $answer) == 0) { |
287 | $question->fraction[$key] = 1; |
288 | $question->feedback[$key] = 'Correct'; |
289 | } else { |
290 | $question->fraction[$key] = 0; |
291 | $question->feedback[$key] = 'Incorrect'; |
292 | } |
293 | } |
294 | } |
295 | return $question; |
296 | } |
297 | |
298 | function parse_co($qrec, $question) |
299 | { |
300 | $question->usecase = 0; |
301 | $answer = trim($qrec['answer'][0]['#']); |
302 | $answers = explode("\n",$answer); |
303 | |
304 | foreach($answers as $key => $value) { |
305 | $value = trim($value); |
306 | if (strlen($value) > 0) { |
307 | $question->answer[$key] = addslashes($value); |
308 | $question->fraction[$key] = 1; |
309 | $question->feedback[$key] = "Correct"; |
310 | } |
311 | } |
312 | return $question; |
313 | } |
314 | |
315 | function parse_nr($qrec, $question) |
316 | { |
317 | $answer = trim($qrec['answer'][0]['#']); |
318 | $answers = explode("\n",$answer); |
319 | |
320 | foreach($answers as $key => $value) { |
321 | $value = trim($value); |
322 | if (is_numeric($value)) { |
323 | $errormargin = 0; |
324 | $question->answer[$key] = $value; |
325 | $question->fraction[$key] = 1; |
326 | $question->feedback[$key] = "Correct"; |
327 | $question->min[$key] = $question->answer[$key] - $errormargin; |
328 | $question->max[$key] = $question->answer[$key] + $errormargin; |
329 | } |
330 | } |
331 | return $question; |
332 | } |
333 | |
334 | } |
335 | // end class |
336 | |
337 | ?> |