ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.assClozeTestExport.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assQuestionExport.php";
5
16{
26 public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
27 {
28 global $ilias;
29
30 include_once "./Services/Math/classes/class.EvalMath.php";
31 $eval = new EvalMath();
32 $eval->suppress_errors = true;
33 include_once("./Services/Xml/classes/class.ilXmlWriter.php");
34 $a_xml_writer = new ilXmlWriter;
35 // set xml header
36 $a_xml_writer->xmlHeader();
37 $a_xml_writer->xmlStartTag("questestinterop");
38 $attrs = array(
39 "ident" => "il_" . IL_INST_ID . "_qst_" . $this->object->getId(),
40 "title" => $this->object->getTitle(),
41 "maxattempts" => $this->object->getNrOfTries()
42 );
43 $a_xml_writer->xmlStartTag("item", $attrs);
44 // add question description
45 $a_xml_writer->xmlElement("qticomment", null, $this->object->getComment());
46 // add estimated working time
47 $workingtime = $this->object->getEstimatedWorkingTime();
48 $duration = sprintf("P0Y0M0DT%dH%dM%dS", $workingtime["h"], $workingtime["m"], $workingtime["s"]);
49 $a_xml_writer->xmlElement("duration", null, $duration);
50 // add ILIAS specific metadata
51 $a_xml_writer->xmlStartTag("itemmetadata");
52 $a_xml_writer->xmlStartTag("qtimetadata");
53 $a_xml_writer->xmlStartTag("qtimetadatafield");
54 $a_xml_writer->xmlElement("fieldlabel", null, "ILIAS_VERSION");
55 $a_xml_writer->xmlElement("fieldentry", null, $ilias->getSetting("ilias_version"));
56 $a_xml_writer->xmlEndTag("qtimetadatafield");
57 $a_xml_writer->xmlStartTag("qtimetadatafield");
58 $a_xml_writer->xmlElement("fieldlabel", null, "QUESTIONTYPE");
59 $a_xml_writer->xmlElement("fieldentry", null, CLOZE_TEST_IDENTIFIER);
60 $a_xml_writer->xmlEndTag("qtimetadatafield");
61 $a_xml_writer->xmlStartTag("qtimetadatafield");
62 $a_xml_writer->xmlElement("fieldlabel", null, "AUTHOR");
63 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getAuthor());
64 $a_xml_writer->xmlEndTag("qtimetadatafield");
65
66 // additional content editing information
68 $this->addGeneralMetadata($a_xml_writer);
69
70 $a_xml_writer->xmlStartTag("qtimetadatafield");
71 $a_xml_writer->xmlElement("fieldlabel", null, "textgaprating");
72 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getTextgapRating());
73 $a_xml_writer->xmlEndTag("qtimetadatafield");
74
75 $a_xml_writer->xmlStartTag("qtimetadatafield");
76 $a_xml_writer->xmlElement("fieldlabel", null, "fixedTextLength");
77 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getFixedTextLength());
78 $a_xml_writer->xmlEndTag("qtimetadatafield");
79
80 $a_xml_writer->xmlStartTag("qtimetadatafield");
81 $a_xml_writer->xmlElement("fieldlabel", null, "identicalScoring");
82 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getIdenticalScoring());
83 $a_xml_writer->xmlEndTag("qtimetadatafield");
84
85 $a_xml_writer->xmlStartTag("qtimetadatafield");
86 $a_xml_writer->xmlElement("fieldlabel", null, "combinations");
87 $a_xml_writer->xmlElement("fieldentry", null, base64_encode(json_encode($this->object->getGapCombinations())));
88 $a_xml_writer->xmlEndTag("qtimetadatafield");
89
90 $a_xml_writer->xmlEndTag("qtimetadata");
91 $a_xml_writer->xmlEndTag("itemmetadata");
92
93 // PART I: qti presentation
94 $attrs = array(
95 "label" => $this->object->getTitle()
96 );
97 $a_xml_writer->xmlStartTag("presentation", $attrs);
98 // add flow to presentation
99 $a_xml_writer->xmlStartTag("flow");
100
101 $questionText = $this->object->getQuestion() ? $this->object->getQuestion() : '&nbsp;';
102 $this->object->addQTIMaterial($a_xml_writer, $questionText);
103
104 $text_parts = preg_split("/\[gap.*?\[\/gap\]/", $this->object->getClozeText());
105
106 // add material with question text to presentation
107 for ($i = 0; $i <= $this->object->getGapCount(); $i++) {
108 $this->object->addQTIMaterial($a_xml_writer, $text_parts[$i]);
109
110 if ($i < $this->object->getGapCount()) {
111 // add gap
112 $gap = $this->object->getGap($i);
113 switch ($gap->getType()) {
114 case CLOZE_SELECT:
115 // comboboxes
116 $attrs = array(
117 "ident" => "gap_$i",
118 "rcardinality" => "Single"
119 );
120 $a_xml_writer->xmlStartTag("response_str", $attrs);
121 $solution = $this->object->getSuggestedSolution($i);
122 if (count($solution)) {
123 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches)) {
124 $attrs = array(
125 "label" => "suggested_solution"
126 );
127 $a_xml_writer->xmlStartTag("material", $attrs);
128 $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
129 if (strcmp($matches[1], "") != 0) {
130 $intlink = $solution["internal_link"];
131 }
132 $a_xml_writer->xmlElement("mattext", null, $intlink);
133 $a_xml_writer->xmlEndTag("material");
134 }
135 }
136
137 $attrs = array("shuffle" => ($gap->getShuffle() ? "Yes" : "No"));
138 $a_xml_writer->xmlStartTag("render_choice", $attrs);
139
140 // add answers
141 foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answeritem) {
142 $attrs = array(
143 "ident" => $answeritem->getOrder()
144 );
145 $a_xml_writer->xmlStartTag("response_label", $attrs);
146 $a_xml_writer->xmlStartTag("material");
147 $a_xml_writer->xmlElement("mattext", null, $answeritem->getAnswertext());
148 $a_xml_writer->xmlEndTag("material");
149 $a_xml_writer->xmlEndTag("response_label");
150 }
151 $a_xml_writer->xmlEndTag("render_choice");
152 $a_xml_writer->xmlEndTag("response_str");
153 break;
154 case CLOZE_TEXT:
155 // text fields
156 $attrs = array(
157 "ident" => "gap_$i",
158 "rcardinality" => "Single"
159 );
160 $a_xml_writer->xmlStartTag("response_str", $attrs);
161 $solution = $this->object->getSuggestedSolution($i);
162 if (count($solution)) {
163 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches)) {
164 $attrs = array(
165 "label" => "suggested_solution"
166 );
167 $a_xml_writer->xmlStartTag("material", $attrs);
168 $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
169 if (strcmp($matches[1], "") != 0) {
170 $intlink = $solution["internal_link"];
171 }
172 $a_xml_writer->xmlElement("mattext", null, $intlink);
173 $a_xml_writer->xmlEndTag("material");
174 }
175 }
176 $attrs = array(
177 "fibtype" => "String",
178 "prompt" => "Box",
179 "columns" => $gap->getMaxWidth(),
180 "maxchars" => $gap->getGapSize()
181 );
182 $a_xml_writer->xmlStartTag("render_fib", $attrs);
183 $a_xml_writer->xmlEndTag("render_fib");
184 $a_xml_writer->xmlEndTag("response_str");
185 break;
186 case CLOZE_NUMERIC:
187 // numeric fields
188 $attrs = array(
189 "ident" => "gap_$i",
190 "numtype" => "Decimal",
191 "rcardinality" => "Single"
192 );
193 $a_xml_writer->xmlStartTag("response_num", $attrs);
194 $solution = $this->object->getSuggestedSolution($i);
195 if (count($solution)) {
196 if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches)) {
197 $attrs = array(
198 "label" => "suggested_solution"
199 );
200 $a_xml_writer->xmlStartTag("material", $attrs);
201 $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
202 if (strcmp($matches[1], "") != 0) {
203 $intlink = $solution["internal_link"];
204 }
205 $a_xml_writer->xmlElement("mattext", null, $intlink);
206 $a_xml_writer->xmlEndTag("material");
207 }
208 }
209 $answeritem = $gap->getItem(0);
210 $attrs = array(
211 "fibtype" => "Decimal",
212 "prompt" => "Box",
213 "columns" => $gap->getMaxWidth(),
214 "maxchars" => $gap->getGapSize()
215 );
216 if (is_object($answeritem)) {
217 if ($eval->e($answeritem->getLowerBound()) !== false) {
218 $attrs["minnumber"] = $answeritem->getLowerBound();
219 }
220 if ($eval->e($answeritem->getUpperBound()) !== false) {
221 $attrs["maxnumber"] = $answeritem->getUpperBound();
222 }
223 }
224 $a_xml_writer->xmlStartTag("render_fib", $attrs);
225 $a_xml_writer->xmlEndTag("render_fib");
226 $a_xml_writer->xmlEndTag("response_num");
227 break;
228 }
229 }
230 }
231 $a_xml_writer->xmlEndTag("flow");
232 $a_xml_writer->xmlEndTag("presentation");
233
234 // PART II: qti resprocessing
235 $a_xml_writer->xmlStartTag("resprocessing");
236 $a_xml_writer->xmlStartTag("outcomes");
237 $a_xml_writer->xmlStartTag("decvar");
238 $a_xml_writer->xmlEndTag("decvar");
239 $a_xml_writer->xmlEndTag("outcomes");
240
241 // add response conditions
242 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
243 $gap = $this->object->getGap($i);
244 switch ($gap->getType()) {
245 case CLOZE_SELECT:
246 foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answer) {
247 $attrs = array(
248 "continue" => "Yes"
249 );
250 $a_xml_writer->xmlStartTag("respcondition", $attrs);
251 // qti conditionvar
252 $a_xml_writer->xmlStartTag("conditionvar");
253
254 $attrs = array(
255 "respident" => "gap_$i"
256 );
257 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
258 $a_xml_writer->xmlEndTag("conditionvar");
259 // qti setvar
260 $attrs = array(
261 "action" => "Add"
262 );
263 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
264 // qti displayfeedback
265 $linkrefid = "";
266 $linkrefid = "$i" . "_Response_" . $answer->getOrder();
267 $attrs = array(
268 "feedbacktype" => "Response",
269 "linkrefid" => $linkrefid
270 );
271 $a_xml_writer->xmlElement("displayfeedback", $attrs);
272 $a_xml_writer->xmlEndTag("respcondition");
273 }
274 break;
275 case CLOZE_TEXT:
276 foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answer) {
277 $attrs = array(
278 "continue" => "Yes"
279 );
280 $a_xml_writer->xmlStartTag("respcondition", $attrs);
281 // qti conditionvar
282 $a_xml_writer->xmlStartTag("conditionvar");
283 $attrs = array(
284 "respident" => "gap_$i"
285 );
286 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
287 $a_xml_writer->xmlEndTag("conditionvar");
288 // qti setvar
289 $attrs = array(
290 "action" => "Add"
291 );
292 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
293 // qti displayfeedback
294 $linkrefid = "$i" . "_Response_" . $answer->getOrder();
295 $attrs = array(
296 "feedbacktype" => "Response",
297 "linkrefid" => $linkrefid
298 );
299 $a_xml_writer->xmlElement("displayfeedback", $attrs);
300 $a_xml_writer->xmlEndTag("respcondition");
301 }
302 break;
303 case CLOZE_NUMERIC:
304 foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answer) {
305 $attrs = array(
306 "continue" => "Yes"
307 );
308 $a_xml_writer->xmlStartTag("respcondition", $attrs);
309 // qti conditionvar
310 $a_xml_writer->xmlStartTag("conditionvar");
311 $attrs = array(
312 "respident" => "gap_$i"
313 );
314 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
315 $a_xml_writer->xmlEndTag("conditionvar");
316 // qti setvar
317 $attrs = array(
318 "action" => "Add"
319 );
320 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
321 // qti displayfeedback
322 $linkrefid = "$i" . "_Response_" . $answer->getOrder();
323 $attrs = array(
324 "feedbacktype" => "Response",
325 "linkrefid" => $linkrefid
326 );
327 $a_xml_writer->xmlElement("displayfeedback", $attrs);
328 $a_xml_writer->xmlEndTag("respcondition");
329 }
330 break;
331 }
332 }
333
334 $feedback_allcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
335 $this->object->getId(),
336 true
337 );
338 if (strlen($feedback_allcorrect)) {
339 $attrs = array(
340 "continue" => "Yes"
341 );
342 $a_xml_writer->xmlStartTag("respcondition", $attrs);
343 // qti conditionvar
344 $a_xml_writer->xmlStartTag("conditionvar");
345
346 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
347 $gap = $this->object->getGap($i);
348 $indexes = $gap->getBestSolutionIndexes();
349 if ($i > 0) {
350 $a_xml_writer->xmlStartTag("and");
351 }
352 switch ($gap->getType()) {
353 case CLOZE_SELECT:
354 $k = 0;
355 foreach ($indexes as $key) {
356 if ($k > 0) {
357 $a_xml_writer->xmlStartTag("or");
358 }
359 $attrs = array(
360 "respident" => "gap_$i"
361 );
362 $answer = $gap->getItem($key);
363 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
364 if ($k > 0) {
365 $a_xml_writer->xmlEndTag("or");
366 }
367 $k++;
368 }
369 break;
370 case CLOZE_TEXT:
371 $k = 0;
372 foreach ($indexes as $key) {
373 if ($k > 0) {
374 $a_xml_writer->xmlStartTag("or");
375 }
376 $attrs = array(
377 "respident" => "gap_$i"
378 );
379 $answer = $gap->getItem($key);
380 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
381 if ($k > 0) {
382 $a_xml_writer->xmlEndTag("or");
383 }
384 $k++;
385 }
386 break;
387 case CLOZE_NUMERIC:
388 $k = 0;
389 foreach ($indexes as $key) {
390 if ($k > 0) {
391 $a_xml_writer->xmlStartTag("or");
392 }
393 $attrs = array(
394 "respident" => "gap_$i"
395 );
396 $answer = $gap->getItem($key);
397 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
398 if ($k > 0) {
399 $a_xml_writer->xmlEndTag("or");
400 }
401 $k++;
402 }
403 break;
404 }
405 if ($i > 0) {
406 $a_xml_writer->xmlEndTag("and");
407 }
408 }
409 $a_xml_writer->xmlEndTag("conditionvar");
410 // qti displayfeedback
411 $attrs = array(
412 "feedbacktype" => "Response",
413 "linkrefid" => "response_allcorrect"
414 );
415 $a_xml_writer->xmlElement("displayfeedback", $attrs);
416 $a_xml_writer->xmlEndTag("respcondition");
417 }
418 $feedback_onenotcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
419 $this->object->getId(),
420 false
421 );
422 if (strlen($feedback_onenotcorrect)) {
423 $attrs = array(
424 "continue" => "Yes"
425 );
426 $a_xml_writer->xmlStartTag("respcondition", $attrs);
427 // qti conditionvar
428 $a_xml_writer->xmlStartTag("conditionvar");
429
430 $a_xml_writer->xmlStartTag("not");
431 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
432 $gap = $this->object->getGap($i);
433 $indexes = $gap->getBestSolutionIndexes();
434 if ($i > 0) {
435 $a_xml_writer->xmlStartTag("and");
436 }
437 switch ($gap->getType()) {
438 case CLOZE_SELECT:
439 $k = 0;
440 foreach ($indexes as $key) {
441 if ($k > 0) {
442 $a_xml_writer->xmlStartTag("or");
443 }
444 $attrs = array(
445 "respident" => "gap_$i"
446 );
447 $answer = $gap->getItem($key);
448 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
449 if ($k > 0) {
450 $a_xml_writer->xmlEndTag("or");
451 }
452 $k++;
453 }
454 break;
455 case CLOZE_TEXT:
456 $k = 0;
457 foreach ($indexes as $key) {
458 if ($k > 0) {
459 $a_xml_writer->xmlStartTag("or");
460 }
461 $attrs = array(
462 "respident" => "gap_$i"
463 );
464 $answer = $gap->getItem($key);
465 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
466 if ($k > 0) {
467 $a_xml_writer->xmlEndTag("or");
468 }
469 $k++;
470 }
471 break;
472 case CLOZE_NUMERIC:
473 $k = 0;
474 foreach ($indexes as $key) {
475 if ($k > 0) {
476 $a_xml_writer->xmlStartTag("or");
477 }
478 $attrs = array(
479 "respident" => "gap_$i"
480 );
481 $answer = $gap->getItem($key);
482 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
483 if ($k > 0) {
484 $a_xml_writer->xmlEndTag("or");
485 }
486 $k++;
487 }
488 break;
489 }
490 if ($i > 0) {
491 $a_xml_writer->xmlEndTag("and");
492 }
493 }
494 $a_xml_writer->xmlEndTag("not");
495 $a_xml_writer->xmlEndTag("conditionvar");
496 // qti displayfeedback
497 $attrs = array(
498 "feedbacktype" => "Response",
499 "linkrefid" => "response_onenotcorrect"
500 );
501 $a_xml_writer->xmlElement("displayfeedback", $attrs);
502 $a_xml_writer->xmlEndTag("respcondition");
503 }
504
505 $a_xml_writer->xmlEndTag("resprocessing");
506
507 // PART III: qti itemfeedback
508 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
509 $gap = $this->object->getGap($i);
510 switch ($gap->getType()) {
511 case CLOZE_SELECT:
512 break;
513 case CLOZE_TEXT:
514 break;
515 case CLOZE_NUMERIC:
516 break;
517 }
518 /*foreach ($gap->getItems() as $answer)
519 {
520 $linkrefid = "$i" . "_Response_" . $answer->getOrder();
521 $attrs = array(
522 "ident" => $linkrefid,
523 "view" => "All"
524 );
525 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
526 // qti flow_mat
527 $a_xml_writer->xmlStartTag("flow_mat");
528// $a_xml_writer->xmlStartTag("material");
529// $a_xml_writer->xmlElement("mattext");
530// $a_xml_writer->xmlEndTag("material");
531 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
532 $this->object->getId(), $index
533 );
534 $this->object->addQTIMaterial($a_xml_writer, $fb);
535 $a_xml_writer->xmlEndTag("flow_mat");
536 $a_xml_writer->xmlEndTag("itemfeedback");
537 }*/
538
539 $attrs = array(
540 "ident" => $i,
541 "view" => "All"
542 );
543 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
544 // qti flow_mat
545 $a_xml_writer->xmlStartTag("flow_mat");
546 // $a_xml_writer->xmlStartTag("material");
547 // $a_xml_writer->xmlElement("mattext");
548 // $a_xml_writer->xmlEndTag("material");
549 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
550 $this->object->getId(),
551 $i
552 );
553 $this->object->addQTIMaterial($a_xml_writer, $fb);
554 $a_xml_writer->xmlEndTag("flow_mat");
555 $a_xml_writer->xmlEndTag("itemfeedback");
556 }
557
558 if (strlen($feedback_allcorrect)) {
559 $attrs = array(
560 "ident" => "response_allcorrect",
561 "view" => "All"
562 );
563 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
564 // qti flow_mat
565 $a_xml_writer->xmlStartTag("flow_mat");
566 $this->object->addQTIMaterial($a_xml_writer, $feedback_allcorrect);
567 $a_xml_writer->xmlEndTag("flow_mat");
568 $a_xml_writer->xmlEndTag("itemfeedback");
569 }
570 if (strlen($feedback_onenotcorrect)) {
571 $attrs = array(
572 "ident" => "response_onenotcorrect",
573 "view" => "All"
574 );
575 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
576 // qti flow_mat
577 $a_xml_writer->xmlStartTag("flow_mat");
578 $this->object->addQTIMaterial($a_xml_writer, $feedback_onenotcorrect);
579 $a_xml_writer->xmlEndTag("flow_mat");
580 $a_xml_writer->xmlEndTag("itemfeedback");
581 }
582
583 $a_xml_writer->xmlEndTag("item");
584 $a_xml_writer->xmlEndTag("questestinterop");
585
586 $xml = $a_xml_writer->xmlDumpMem(false);
587 if (!$a_include_header) {
588 $pos = strpos($xml, "?>");
589 $xml = substr($xml, $pos + 2);
590 }
591 return $xml;
592 }
593}
sprintf('%.4f', $callTime)
An exception for terminatinating execution or to throw for unit testing.
Class for cloze question exports.
toXML($a_include_header=true, $a_include_binary=true, $a_shuffle=false, $test_output=false, $force_image_references=false)
Returns a QTI xml representation of the question.
Class for question exports.
addGeneralMetadata(ilXmlWriter $xmlwriter)
addAdditionalContentEditingModeInformation(ilXmlWriter $a_xml_writer)
adds a qti meta data field for ilias specific information of "additional content editing mode" (xml w...
XML writer class.
xmlHeader()
Writes xml header @access public.
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
const CLOZE_TEST_IDENTIFIER
Question identifier constants.
const CLOZE_NUMERIC
const CLOZE_SELECT
const CLOZE_TEXT
Cloze question constants.
$xml
Definition: metadata.php:240