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 
4 include_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
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  $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 }
const CLOZE_TEXT
Cloze question constants.
XML writer class.
Class for question exports.
addGeneralMetadata(ilXmlWriter $xmlwriter)
$xml
Definition: metadata.php:240
const CLOZE_SELECT
Class for cloze question exports.
xmlHeader()
Writes xml header public.
Create styles array
The data for the language used.
addAdditionalContentEditingModeInformation(ilXmlWriter $a_xml_writer)
adds a qti meta data field for ilias specific information of "additional content editing mode" (xml w...
Create new PHPExcel object
obj_idprivate
$i
Definition: disco.tpl.php:19
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.
$key
Definition: croninfo.php:18
const CLOZE_TEST_IDENTIFIER
Question identifier constants.