ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5
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 
4 include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assQuestionExport.php";
5 
16 {
26  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
67  $this->addAdditionalContentEditingModeInformation($a_xml_writer);
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  {
109  $this->object->addQTIMaterial($a_xml_writer, $text_parts[$i]);
110 
111  if ($i < $this->object->getGapCount())
112  {
113  // add gap
114  $gap = $this->object->getGap($i);
115  switch ($gap->getType())
116  {
117  case CLOZE_SELECT:
118  // comboboxes
119  $attrs = array(
120  "ident" => "gap_$i",
121  "rcardinality" => "Single"
122  );
123  $a_xml_writer->xmlStartTag("response_str", $attrs);
124  $solution = $this->object->getSuggestedSolution($i);
125  if (count($solution))
126  {
127  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
128  {
129  $attrs = array(
130  "label" => "suggested_solution"
131  );
132  $a_xml_writer->xmlStartTag("material", $attrs);
133  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
134  if (strcmp($matches[1], "") != 0)
135  {
136  $intlink = $solution["internal_link"];
137  }
138  $a_xml_writer->xmlElement("mattext", NULL, $intlink);
139  $a_xml_writer->xmlEndTag("material");
140  }
141  }
142 
143  $attrs = array("shuffle" => ($gap->getShuffle() ? "Yes" : "No"));
144  $a_xml_writer->xmlStartTag("render_choice", $attrs);
145 
146  // add answers
147  foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answeritem)
148  {
149  $attrs = array(
150  "ident" => $answeritem->getOrder()
151  );
152  $a_xml_writer->xmlStartTag("response_label", $attrs);
153  $a_xml_writer->xmlStartTag("material");
154  $a_xml_writer->xmlElement("mattext", NULL, $answeritem->getAnswertext());
155  $a_xml_writer->xmlEndTag("material");
156  $a_xml_writer->xmlEndTag("response_label");
157  }
158  $a_xml_writer->xmlEndTag("render_choice");
159  $a_xml_writer->xmlEndTag("response_str");
160  break;
161  case CLOZE_TEXT:
162  // text fields
163  $attrs = array(
164  "ident" => "gap_$i",
165  "rcardinality" => "Single"
166  );
167  $a_xml_writer->xmlStartTag("response_str", $attrs);
168  $solution = $this->object->getSuggestedSolution($i);
169  if (count($solution))
170  {
171  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
172  {
173  $attrs = array(
174  "label" => "suggested_solution"
175  );
176  $a_xml_writer->xmlStartTag("material", $attrs);
177  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
178  if (strcmp($matches[1], "") != 0)
179  {
180  $intlink = $solution["internal_link"];
181  }
182  $a_xml_writer->xmlElement("mattext", NULL, $intlink);
183  $a_xml_writer->xmlEndTag("material");
184  }
185  }
186  $attrs = array(
187  "fibtype" => "String",
188  "prompt" => "Box",
189  "columns" => $gap->getMaxWidth(),
190  "maxchars" => $gap->getGapSize()
191  );
192  $a_xml_writer->xmlStartTag("render_fib", $attrs);
193  $a_xml_writer->xmlEndTag("render_fib");
194  $a_xml_writer->xmlEndTag("response_str");
195  break;
196  case CLOZE_NUMERIC:
197  // numeric fields
198  $attrs = array(
199  "ident" => "gap_$i",
200  "numtype" => "Decimal",
201  "rcardinality" => "Single"
202  );
203  $a_xml_writer->xmlStartTag("response_num", $attrs);
204  $solution = $this->object->getSuggestedSolution($i);
205  if (count($solution))
206  {
207  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
208  {
209  $attrs = array(
210  "label" => "suggested_solution"
211  );
212  $a_xml_writer->xmlStartTag("material", $attrs);
213  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
214  if (strcmp($matches[1], "") != 0)
215  {
216  $intlink = $solution["internal_link"];
217  }
218  $a_xml_writer->xmlElement("mattext", NULL, $intlink);
219  $a_xml_writer->xmlEndTag("material");
220  }
221  }
222  $answeritem = $gap->getItem(0);
223  $attrs = array(
224  "fibtype" => "Decimal",
225  "prompt" => "Box",
226  "columns" => $gap->getMaxWidth(),
227  "maxchars" => $gap->getGapSize()
228  );
229  if (is_object($answeritem))
230  {
231  if ($eval->e($answeritem->getLowerBound()) !== FALSE)
232  {
233  $attrs["minnumber"] = $answeritem->getLowerBound();
234  }
235  if ($eval->e($answeritem->getUpperBound()) !== FALSE)
236  {
237  $attrs["maxnumber"] = $answeritem->getUpperBound();
238  }
239  }
240  $a_xml_writer->xmlStartTag("render_fib", $attrs);
241  $a_xml_writer->xmlEndTag("render_fib");
242  $a_xml_writer->xmlEndTag("response_num");
243  break;
244  }
245  }
246  }
247  $a_xml_writer->xmlEndTag("flow");
248  $a_xml_writer->xmlEndTag("presentation");
249 
250  // PART II: qti resprocessing
251  $a_xml_writer->xmlStartTag("resprocessing");
252  $a_xml_writer->xmlStartTag("outcomes");
253  $a_xml_writer->xmlStartTag("decvar");
254  $a_xml_writer->xmlEndTag("decvar");
255  $a_xml_writer->xmlEndTag("outcomes");
256 
257  // add response conditions
258  for ($i = 0; $i < $this->object->getGapCount(); $i++)
259  {
260  $gap = $this->object->getGap($i);
261  switch ($gap->getType())
262  {
263  case CLOZE_SELECT:
264  foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answer)
265  {
266  $attrs = array(
267  "continue" => "Yes"
268  );
269  $a_xml_writer->xmlStartTag("respcondition", $attrs);
270  // qti conditionvar
271  $a_xml_writer->xmlStartTag("conditionvar");
272 
273  $attrs = array(
274  "respident" => "gap_$i"
275  );
276  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
277  $a_xml_writer->xmlEndTag("conditionvar");
278  // qti setvar
279  $attrs = array(
280  "action" => "Add"
281  );
282  $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
283  // qti displayfeedback
284  $linkrefid = "";
285  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
286  $attrs = array(
287  "feedbacktype" => "Response",
288  "linkrefid" => $linkrefid
289  );
290  $a_xml_writer->xmlElement("displayfeedback", $attrs);
291  $a_xml_writer->xmlEndTag("respcondition");
292  }
293  break;
294  case CLOZE_TEXT:
295  foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answer)
296  {
297  $attrs = array(
298  "continue" => "Yes"
299  );
300  $a_xml_writer->xmlStartTag("respcondition", $attrs);
301  // qti conditionvar
302  $a_xml_writer->xmlStartTag("conditionvar");
303  $attrs = array(
304  "respident" => "gap_$i"
305  );
306  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
307  $a_xml_writer->xmlEndTag("conditionvar");
308  // qti setvar
309  $attrs = array(
310  "action" => "Add"
311  );
312  $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
313  // qti displayfeedback
314  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
315  $attrs = array(
316  "feedbacktype" => "Response",
317  "linkrefid" => $linkrefid
318  );
319  $a_xml_writer->xmlElement("displayfeedback", $attrs);
320  $a_xml_writer->xmlEndTag("respcondition");
321  }
322  break;
323  case CLOZE_NUMERIC:
324  foreach ($gap->getItems(new ilArrayElementOrderKeeper()) as $answer)
325  {
326  $attrs = array(
327  "continue" => "Yes"
328  );
329  $a_xml_writer->xmlStartTag("respcondition", $attrs);
330  // qti conditionvar
331  $a_xml_writer->xmlStartTag("conditionvar");
332  $attrs = array(
333  "respident" => "gap_$i"
334  );
335  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
336  $a_xml_writer->xmlEndTag("conditionvar");
337  // qti setvar
338  $attrs = array(
339  "action" => "Add"
340  );
341  $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
342  // qti displayfeedback
343  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
344  $attrs = array(
345  "feedbacktype" => "Response",
346  "linkrefid" => $linkrefid
347  );
348  $a_xml_writer->xmlElement("displayfeedback", $attrs);
349  $a_xml_writer->xmlEndTag("respcondition");
350  }
351  break;
352  }
353  }
354 
355  $feedback_allcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
356  $this->object->getId(), true
357  );
358  if (strlen($feedback_allcorrect))
359  {
360  $attrs = array(
361  "continue" => "Yes"
362  );
363  $a_xml_writer->xmlStartTag("respcondition", $attrs);
364  // qti conditionvar
365  $a_xml_writer->xmlStartTag("conditionvar");
366 
367  for ($i = 0; $i < $this->object->getGapCount(); $i++)
368  {
369  $gap = $this->object->getGap($i);
370  $indexes = $gap->getBestSolutionIndexes();
371  if ($i > 0)
372  {
373  $a_xml_writer->xmlStartTag("and");
374  }
375  switch ($gap->getType())
376  {
377  case CLOZE_SELECT:
378  $k = 0;
379  foreach ($indexes as $key)
380  {
381  if ($k > 0)
382  {
383  $a_xml_writer->xmlStartTag("or");
384  }
385  $attrs = array(
386  "respident" => "gap_$i"
387  );
388  $answer = $gap->getItem($key);
389  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
390  if ($k > 0)
391  {
392  $a_xml_writer->xmlEndTag("or");
393  }
394  $k++;
395  }
396  break;
397  case CLOZE_TEXT:
398  $k = 0;
399  foreach ($indexes as $key)
400  {
401  if ($k > 0)
402  {
403  $a_xml_writer->xmlStartTag("or");
404  }
405  $attrs = array(
406  "respident" => "gap_$i"
407  );
408  $answer = $gap->getItem($key);
409  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
410  if ($k > 0)
411  {
412  $a_xml_writer->xmlEndTag("or");
413  }
414  $k++;
415  }
416  break;
417  case CLOZE_NUMERIC:
418  $k = 0;
419  foreach ($indexes as $key)
420  {
421  if ($k > 0)
422  {
423  $a_xml_writer->xmlStartTag("or");
424  }
425  $attrs = array(
426  "respident" => "gap_$i"
427  );
428  $answer = $gap->getItem($key);
429  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
430  if ($k > 0)
431  {
432  $a_xml_writer->xmlEndTag("or");
433  }
434  $k++;
435  }
436  break;
437  }
438  if ($i > 0)
439  {
440  $a_xml_writer->xmlEndTag("and");
441  }
442  }
443  $a_xml_writer->xmlEndTag("conditionvar");
444  // qti displayfeedback
445  $attrs = array(
446  "feedbacktype" => "Response",
447  "linkrefid" => "response_allcorrect"
448  );
449  $a_xml_writer->xmlElement("displayfeedback", $attrs);
450  $a_xml_writer->xmlEndTag("respcondition");
451  }
452  $feedback_onenotcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
453  $this->object->getId(), false
454  );
455  if (strlen($feedback_onenotcorrect))
456  {
457  $attrs = array(
458  "continue" => "Yes"
459  );
460  $a_xml_writer->xmlStartTag("respcondition", $attrs);
461  // qti conditionvar
462  $a_xml_writer->xmlStartTag("conditionvar");
463 
464  $a_xml_writer->xmlStartTag("not");
465  for ($i = 0; $i < $this->object->getGapCount(); $i++)
466  {
467  $gap = $this->object->getGap($i);
468  $indexes = $gap->getBestSolutionIndexes();
469  if ($i > 0)
470  {
471  $a_xml_writer->xmlStartTag("and");
472  }
473  switch ($gap->getType())
474  {
475  case CLOZE_SELECT:
476  $k = 0;
477  foreach ($indexes as $key)
478  {
479  if ($k > 0)
480  {
481  $a_xml_writer->xmlStartTag("or");
482  }
483  $attrs = array(
484  "respident" => "gap_$i"
485  );
486  $answer = $gap->getItem($key);
487  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
488  if ($k > 0)
489  {
490  $a_xml_writer->xmlEndTag("or");
491  }
492  $k++;
493  }
494  break;
495  case CLOZE_TEXT:
496  $k = 0;
497  foreach ($indexes as $key)
498  {
499  if ($k > 0)
500  {
501  $a_xml_writer->xmlStartTag("or");
502  }
503  $attrs = array(
504  "respident" => "gap_$i"
505  );
506  $answer = $gap->getItem($key);
507  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
508  if ($k > 0)
509  {
510  $a_xml_writer->xmlEndTag("or");
511  }
512  $k++;
513  }
514  break;
515  case CLOZE_NUMERIC:
516  $k = 0;
517  foreach ($indexes as $key)
518  {
519  if ($k > 0)
520  {
521  $a_xml_writer->xmlStartTag("or");
522  }
523  $attrs = array(
524  "respident" => "gap_$i"
525  );
526  $answer = $gap->getItem($key);
527  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
528  if ($k > 0)
529  {
530  $a_xml_writer->xmlEndTag("or");
531  }
532  $k++;
533  }
534  break;
535  }
536  if ($i > 0)
537  {
538  $a_xml_writer->xmlEndTag("and");
539  }
540  }
541  $a_xml_writer->xmlEndTag("not");
542  $a_xml_writer->xmlEndTag("conditionvar");
543  // qti displayfeedback
544  $attrs = array(
545  "feedbacktype" => "Response",
546  "linkrefid" => "response_onenotcorrect"
547  );
548  $a_xml_writer->xmlElement("displayfeedback", $attrs);
549  $a_xml_writer->xmlEndTag("respcondition");
550  }
551 
552  $a_xml_writer->xmlEndTag("resprocessing");
553 
554  // PART III: qti itemfeedback
555  for ($i = 0; $i < $this->object->getGapCount(); $i++)
556  {
557  $gap = $this->object->getGap($i);
558  switch ($gap->getType())
559  {
560  case CLOZE_SELECT:
561  break;
562  case CLOZE_TEXT:
563  break;
564  case CLOZE_NUMERIC:
565  break;
566  }
567  /*foreach ($gap->getItems() as $answer)
568  {
569  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
570  $attrs = array(
571  "ident" => $linkrefid,
572  "view" => "All"
573  );
574  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
575  // qti flow_mat
576  $a_xml_writer->xmlStartTag("flow_mat");
577 // $a_xml_writer->xmlStartTag("material");
578 // $a_xml_writer->xmlElement("mattext");
579 // $a_xml_writer->xmlEndTag("material");
580  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
581  $this->object->getId(), $index
582  );
583  $this->object->addQTIMaterial($a_xml_writer, $fb);
584  $a_xml_writer->xmlEndTag("flow_mat");
585  $a_xml_writer->xmlEndTag("itemfeedback");
586  }*/
587 
588  $attrs = array(
589  "ident" => $i,
590  "view" => "All"
591  );
592  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
593  // qti flow_mat
594  $a_xml_writer->xmlStartTag("flow_mat");
595  // $a_xml_writer->xmlStartTag("material");
596  // $a_xml_writer->xmlElement("mattext");
597  // $a_xml_writer->xmlEndTag("material");
598  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
599  $this->object->getId(), $i
600  );
601  $this->object->addQTIMaterial($a_xml_writer, $fb);
602  $a_xml_writer->xmlEndTag("flow_mat");
603  $a_xml_writer->xmlEndTag("itemfeedback");
604  }
605 
606  if (strlen($feedback_allcorrect))
607  {
608  $attrs = array(
609  "ident" => "response_allcorrect",
610  "view" => "All"
611  );
612  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
613  // qti flow_mat
614  $a_xml_writer->xmlStartTag("flow_mat");
615  $this->object->addQTIMaterial($a_xml_writer, $feedback_allcorrect);
616  $a_xml_writer->xmlEndTag("flow_mat");
617  $a_xml_writer->xmlEndTag("itemfeedback");
618  }
619  if (strlen($feedback_onenotcorrect))
620  {
621  $attrs = array(
622  "ident" => "response_onenotcorrect",
623  "view" => "All"
624  );
625  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
626  // qti flow_mat
627  $a_xml_writer->xmlStartTag("flow_mat");
628  $this->object->addQTIMaterial($a_xml_writer, $feedback_onenotcorrect);
629  $a_xml_writer->xmlEndTag("flow_mat");
630  $a_xml_writer->xmlEndTag("itemfeedback");
631  }
632 
633  $a_xml_writer->xmlEndTag("item");
634  $a_xml_writer->xmlEndTag("questestinterop");
635 
636  $xml = $a_xml_writer->xmlDumpMem(FALSE);
637  if (!$a_include_header)
638  {
639  $pos = strpos($xml, "?>");
640  $xml = substr($xml, $pos + 2);
641  }
642  return $xml;
643  }
644 }
645 
646 ?>
const CLOZE_TEXT
Cloze question constants.
XML writer class.
Class for question exports.
addGeneralMetadata(ilXmlWriter $xmlwriter)
const CLOZE_SELECT
Class for cloze question exports.
xmlHeader()
Writes xml header public.
addAdditionalContentEditingModeInformation(ilXmlWriter $a_xml_writer)
adds a qti meta data field for ilias specific information of "additional content editing mode" (xml w...
const CLOZE_NUMERIC
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.
const CLOZE_TEST_IDENTIFIER
Question identifier constants.