ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.assClozeTestExport.php
Go to the documentation of this file.
1<?php
2
20use ILIAS\Refinery\Random\Group as RandomGroup;
21
32{
33 private RandomGroup $randomGroup;
34
35 public function __construct($object)
36 {
37 global $DIC;
38
40
41 $this->randomGroup = $DIC->refinery()->random();
42 }
43
49 public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false): string
50 {
51 global $DIC;
52 $ilias = $DIC['ilias'];
53
54 $eval = new EvalMath();
55 $eval->suppress_errors = true;
56
57 $a_xml_writer = new ilXmlWriter();
58 // set xml header
59 $a_xml_writer->xmlHeader();
60 $a_xml_writer->xmlStartTag("questestinterop");
61 $attrs = [
62 "ident" => "il_" . IL_INST_ID . "_qst_" . $this->object->getId(),
63 "title" => $this->object->getTitle(),
64 "maxattempts" => $this->object->getNrOfTries()
65 ];
66 $a_xml_writer->xmlStartTag("item", $attrs);
67 // add question description
68 $a_xml_writer->xmlElement("qticomment", null, $this->object->getComment());
69 $a_xml_writer->xmlStartTag("itemmetadata");
70 $a_xml_writer->xmlStartTag("qtimetadata");
71 $a_xml_writer->xmlStartTag("qtimetadatafield");
72 $a_xml_writer->xmlElement("fieldlabel", null, "ILIAS_VERSION");
73 $a_xml_writer->xmlElement("fieldentry", null, $ilias->getSetting("ilias_version"));
74 $a_xml_writer->xmlEndTag("qtimetadatafield");
75 $a_xml_writer->xmlStartTag("qtimetadatafield");
76 $a_xml_writer->xmlElement("fieldlabel", null, "QUESTIONTYPE");
77 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getQuestionType());
78 $a_xml_writer->xmlEndTag("qtimetadatafield");
79 $a_xml_writer->xmlStartTag("qtimetadatafield");
80 $a_xml_writer->xmlElement("fieldlabel", null, "AUTHOR");
81 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getAuthor());
82 $a_xml_writer->xmlEndTag("qtimetadatafield");
83
84 // additional content editing information
86 $this->addGeneralMetadata($a_xml_writer);
87
88 $a_xml_writer->xmlStartTag("qtimetadatafield");
89 $a_xml_writer->xmlElement("fieldlabel", null, "textgaprating");
90 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getTextgapRating());
91 $a_xml_writer->xmlEndTag("qtimetadatafield");
92
93 $a_xml_writer->xmlStartTag("qtimetadatafield");
94 $a_xml_writer->xmlElement("fieldlabel", null, "fixedTextLength");
95 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getFixedTextLength());
96 $a_xml_writer->xmlEndTag("qtimetadatafield");
97
98 $a_xml_writer->xmlStartTag("qtimetadatafield");
99 $a_xml_writer->xmlElement("fieldlabel", null, "identicalScoring");
100 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getIdenticalScoring());
101 $a_xml_writer->xmlEndTag("qtimetadatafield");
102
103 $a_xml_writer->xmlStartTag("qtimetadatafield");
104 $a_xml_writer->xmlElement("fieldlabel", null, "feedback_mode");
105 $a_xml_writer->xmlElement("fieldentry", null, $this->object->getFeedbackMode());
106 $a_xml_writer->xmlEndTag("qtimetadatafield");
107
108 $a_xml_writer->xmlStartTag("qtimetadatafield");
109 $a_xml_writer->xmlElement("fieldlabel", null, "combinations");
110 $a_xml_writer->xmlElement("fieldentry", null, base64_encode(json_encode($this->object->getGapCombinations())));
111 $a_xml_writer->xmlEndTag("qtimetadatafield");
112
113 $a_xml_writer->xmlEndTag("qtimetadata");
114 $a_xml_writer->xmlEndTag("itemmetadata");
115
116 // PART I: qti presentation
117 $attrs = [
118 "label" => $this->object->getTitle()
119 ];
120 $a_xml_writer->xmlStartTag("presentation", $attrs);
121 // add flow to presentation
122 $a_xml_writer->xmlStartTag("flow");
123
124 $questionText = $this->object->getQuestion() ? $this->object->getQuestion() : '&nbsp;';
125 $this->addQTIMaterial($a_xml_writer, $questionText);
126
127 $text_parts = preg_split("/\[gap.*?\[\/gap\]/", $this->object->getClozeText());
128
129 // add material with question text to presentation
130 for ($i = 0; $i <= $this->object->getGapCount(); $i++) {
131 $this->addQTIMaterial($a_xml_writer, $text_parts[$i]);
132
133 if ($i < $this->object->getGapCount()) {
134 // add gap
135 $gap = $this->object->getGap($i);
136 switch ($gap->getType()) {
138 // comboboxes
139 $attrs = [
140 "ident" => "gap_$i",
141 "rcardinality" => "Single"
142 ];
143 $a_xml_writer->xmlStartTag("response_str", $attrs);
144 if ($i === 0) {
145 $a_xml_writer = $this->addSuggestedSolution($a_xml_writer);
146 }
147
148 $attrs = ["shuffle" => ($gap->getShuffle() ? "Yes" : "No")];
149 $a_xml_writer->xmlStartTag("render_choice", $attrs);
150
151 // add answers
152 foreach ($gap->getItems($this->randomGroup->dontShuffle()) as $answeritem) {
153 $attrs = [
154 "ident" => $answeritem->getOrder()
155 ];
156 $a_xml_writer->xmlStartTag("response_label", $attrs);
157 $a_xml_writer->xmlStartTag("material");
158 $a_xml_writer->xmlElement("mattext", null, $answeritem->getAnswertext());
159 $a_xml_writer->xmlEndTag("material");
160 $a_xml_writer->xmlEndTag("response_label");
161 }
162 $a_xml_writer->xmlEndTag("render_choice");
163 $a_xml_writer->xmlEndTag("response_str");
164 break;
166 // text fields
167 $attrs = [
168 "ident" => "gap_$i",
169 "rcardinality" => "Single"
170 ];
171 $a_xml_writer->xmlStartTag("response_str", $attrs);
172 if ($i === 0) {
173 $a_xml_writer = $this->addSuggestedSolution($a_xml_writer);
174 }
175 $attrs = [
176 "fibtype" => "String",
177 "prompt" => "Box",
178 "columns" => $gap->getMaxWidth(),
179 "maxchars" => $gap->getGapSize()
180 ];
181 $a_xml_writer->xmlStartTag("render_fib", $attrs);
182 $a_xml_writer->xmlEndTag("render_fib");
183 $a_xml_writer->xmlEndTag("response_str");
184 break;
186 // numeric fields
187 $attrs = [
188 "ident" => "gap_$i",
189 "numtype" => "Decimal",
190 "rcardinality" => "Single"
191 ];
192 $a_xml_writer->xmlStartTag("response_num", $attrs);
193 if ($i === 0) {
194 $a_xml_writer = $this->addSuggestedSolution($a_xml_writer);
195 }
196 $answeritem = $gap->getItem(0);
197 $attrs = [
198 "fibtype" => "Decimal",
199 "prompt" => "Box",
200 "columns" => $gap->getMaxWidth(),
201 "maxchars" => $gap->getGapSize()
202 ];
203 if (is_object($answeritem)) {
204 if ($eval->e($answeritem->getLowerBound()) !== false) {
205 $attrs["minnumber"] = $answeritem->getLowerBound();
206 }
207 if ($eval->e($answeritem->getUpperBound()) !== false) {
208 $attrs["maxnumber"] = $answeritem->getUpperBound();
209 }
210 }
211 $a_xml_writer->xmlStartTag("render_fib", $attrs);
212 $a_xml_writer->xmlEndTag("render_fib");
213 $a_xml_writer->xmlEndTag("response_num");
214 break;
215 }
216 }
217 }
218 $a_xml_writer->xmlEndTag("flow");
219 $a_xml_writer->xmlEndTag("presentation");
220
221 // PART II: qti resprocessing
222 $a_xml_writer->xmlStartTag("resprocessing");
223 $a_xml_writer->xmlStartTag("outcomes");
224 $a_xml_writer->xmlStartTag("decvar");
225 $a_xml_writer->xmlEndTag("decvar");
226 $a_xml_writer->xmlEndTag("outcomes");
227
228 // add response conditions
229 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
230 $gap = $this->object->getGap($i);
231 switch ($gap->getType()) {
233 foreach ($gap->getItems($this->randomGroup->dontShuffle()) as $answer) {
234 $attrs = [
235 "continue" => "Yes"
236 ];
237 $a_xml_writer->xmlStartTag("respcondition", $attrs);
238 // qti conditionvar
239 $a_xml_writer->xmlStartTag("conditionvar");
240
241 $attrs = [
242 "respident" => "gap_$i"
243 ];
244 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
245 $a_xml_writer->xmlEndTag("conditionvar");
246 // qti setvar
247 $attrs = [
248 "action" => "Add"
249 ];
250 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
251 // qti displayfeedback
252 $linkrefid = "$i" . "_Response_" . $answer->getOrder();
253 $attrs = [
254 "feedbacktype" => "Response",
255 "linkrefid" => $linkrefid
256 ];
257 $a_xml_writer->xmlElement("displayfeedback", $attrs);
258 $a_xml_writer->xmlEndTag("respcondition");
259 }
260 break;
263 foreach ($gap->getItems($this->randomGroup->dontShuffle()) as $answer) {
264 $attrs = [
265 "continue" => "Yes"
266 ];
267 $a_xml_writer->xmlStartTag("respcondition", $attrs);
268 // qti conditionvar
269 $a_xml_writer->xmlStartTag("conditionvar");
270 $attrs = [
271 "respident" => "gap_$i"
272 ];
273 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
274 $a_xml_writer->xmlEndTag("conditionvar");
275 // qti setvar
276 $attrs = [
277 "action" => "Add"
278 ];
279 $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
280 // qti displayfeedback
281 $linkrefid = "$i" . "_Response_" . $answer->getOrder();
282 $attrs = [
283 "feedbacktype" => "Response",
284 "linkrefid" => $linkrefid
285 ];
286 $a_xml_writer->xmlElement("displayfeedback", $attrs);
287 $a_xml_writer->xmlEndTag("respcondition");
288 }
289 break;
290 }
291 }
292
293 $feedback_allcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
294 $this->object->getId(),
295 true
296 );
297 if (strlen($feedback_allcorrect)) {
298 $attrs = [
299 "continue" => "Yes"
300 ];
301 $a_xml_writer->xmlStartTag("respcondition", $attrs);
302 // qti conditionvar
303 $a_xml_writer->xmlStartTag("conditionvar");
304
305 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
306 $gap = $this->object->getGap($i);
307 $indexes = $gap->getBestSolutionIndexes();
308 if ($i > 0) {
309 $a_xml_writer->xmlStartTag("and");
310 }
311 switch ($gap->getType()) {
315 $k = 0;
316 foreach ($indexes as $key) {
317 if ($k > 0) {
318 $a_xml_writer->xmlStartTag("or");
319 }
320 $attrs = [
321 "respident" => "gap_$i"
322 ];
323 $answer = $gap->getItem($key);
324 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
325 if ($k > 0) {
326 $a_xml_writer->xmlEndTag("or");
327 }
328 $k++;
329 }
330 break;
331 }
332 if ($i > 0) {
333 $a_xml_writer->xmlEndTag("and");
334 }
335 }
336 $a_xml_writer->xmlEndTag("conditionvar");
337 // qti displayfeedback
338 $attrs = [
339 "feedbacktype" => "Response",
340 "linkrefid" => "response_allcorrect"
341 ];
342 $a_xml_writer->xmlElement("displayfeedback", $attrs);
343 $a_xml_writer->xmlEndTag("respcondition");
344 }
345 $feedback_onenotcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
346 $this->object->getId(),
347 false
348 );
349 if (strlen($feedback_onenotcorrect)) {
350 $attrs = [
351 "continue" => "Yes"
352 ];
353 $a_xml_writer->xmlStartTag("respcondition", $attrs);
354 // qti conditionvar
355 $a_xml_writer->xmlStartTag("conditionvar");
356
357 $a_xml_writer->xmlStartTag("not");
358 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
359 $gap = $this->object->getGap($i);
360 $indexes = $gap->getBestSolutionIndexes();
361 if ($i > 0) {
362 $a_xml_writer->xmlStartTag("and");
363 }
364 switch ($gap->getType()) {
368 $k = 0;
369 foreach ($indexes as $key) {
370 if ($k > 0) {
371 $a_xml_writer->xmlStartTag("or");
372 }
373 $attrs = [
374 "respident" => "gap_$i"
375 ];
376 $answer = $gap->getItem($key);
377 $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
378 if ($k > 0) {
379 $a_xml_writer->xmlEndTag("or");
380 }
381 $k++;
382 }
383 break;
384 }
385 if ($i > 0) {
386 $a_xml_writer->xmlEndTag("and");
387 }
388 }
389 $a_xml_writer->xmlEndTag("not");
390 $a_xml_writer->xmlEndTag("conditionvar");
391 // qti displayfeedback
392 $attrs = [
393 "feedbacktype" => "Response",
394 "linkrefid" => "response_onenotcorrect"
395 ];
396 $a_xml_writer->xmlElement("displayfeedback", $attrs);
397 $a_xml_writer->xmlEndTag("respcondition");
398 }
399
400 $a_xml_writer->xmlEndTag("resprocessing");
401
402 // PART III: qti itemfeedback
403 for ($i = 0; $i < $this->object->getGapCount(); $i++) {
404 $gap = $this->object->getGap($i);
405 switch ($gap->getType()) {
409 break;
410 }
411 }
412 $this->exportAnswerSpecificFeedbacks($a_xml_writer);
413
414 if (strlen($feedback_allcorrect)) {
415 $attrs = [
416 "ident" => "response_allcorrect",
417 "view" => "All"
418 ];
419 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
420 // qti flow_mat
421 $a_xml_writer->xmlStartTag("flow_mat");
422 $this->addQTIMaterial($a_xml_writer, $feedback_allcorrect);
423 $a_xml_writer->xmlEndTag("flow_mat");
424 $a_xml_writer->xmlEndTag("itemfeedback");
425 }
426 if (strlen($feedback_onenotcorrect)) {
427 $attrs = [
428 "ident" => "response_onenotcorrect",
429 "view" => "All"
430 ];
431 $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
432 // qti flow_mat
433 $a_xml_writer->xmlStartTag("flow_mat");
434 $this->addQTIMaterial($a_xml_writer, $feedback_onenotcorrect);
435 $a_xml_writer->xmlEndTag("flow_mat");
436 $a_xml_writer->xmlEndTag("itemfeedback");
437 }
438
439 $a_xml_writer->xmlEndTag("item");
440 $a_xml_writer->xmlEndTag("questestinterop");
441
442 $xml = $a_xml_writer->xmlDumpMem(false);
443 if (!$a_include_header) {
444 $pos = strpos($xml, "?>");
445 $xml = substr($xml, $pos + 2);
446 }
447 return $xml;
448 }
449
453 protected function exportAnswerSpecificFeedbacks(ilXmlWriter $xmlWriter): void
454 {
455 $feedbackIdentifierList = new ilAssSpecificFeedbackIdentifierList();
456 $feedbackIdentifierList->load($this->object->getId());
457
458 foreach ($feedbackIdentifierList as $fbIdentifier) {
459 $feedback = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
460 $this->object->getId(),
461 $fbIdentifier->getQuestionIndex(),
462 $fbIdentifier->getAnswerIndex()
463 );
464
465 $xmlWriter->xmlStartTag("itemfeedback", [
466 "ident" => $this->buildQtiExportIdent($fbIdentifier), "view" => "All"
467 ]);
468
469 $xmlWriter->xmlStartTag("flow_mat");
470 $this->addQTIMaterial($xmlWriter, $feedback);
471 $xmlWriter->xmlEndTag("flow_mat");
472
473 $xmlWriter->xmlEndTag("itemfeedback");
474 }
475 }
476
481 public function buildQtiExportIdent(ilAssSpecificFeedbackIdentifier $fbIdentifier): string
482 {
483 return "{$fbIdentifier->getQuestionIndex()}_{$fbIdentifier->getAnswerIndex()}";
484 }
485}
Class for cloze question exports.
exportAnswerSpecificFeedbacks(ilXmlWriter $xmlWriter)
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 Returns a QTI xml representation of the question and...
__construct($object)
assQuestionExport constructor
buildQtiExportIdent(ilAssSpecificFeedbackIdentifier $fbIdentifier)
Class for question exports.
addGeneralMetadata(ilXmlWriter $xmlwriter)
addQTIMaterial(ilXmlWriter $a_xml_writer, string $a_material, bool $close_material_tag=true, bool $add_mobs=true)
addSuggestedSolution(ilXmlWriter $writer)
addAdditionalContentEditingModeInformation(ilXmlWriter $a_xml_writer)
adds a qti meta data field for ilias specific information of "additional content editing mode" (xml w...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
xmlEndTag(string $tag)
Writes an endtag.
xmlStartTag(string $tag, ?array $attrs=null, bool $empty=false, bool $encode=true, bool $escape=true)
Writes a starttag.
const IL_INST_ID
Definition: constants.php:40
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
global $DIC
Definition: shib_login.php:26