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