ILIAS  release_8 Revision v8.24
class.ilAssClozeTestFeedback.php
Go to the documentation of this file.
1<?php
2
19use ILIAS\Refinery\Random\Group as RandomGroup;
20
30{
34 public const FB_MODE_GAP_QUESTION = 'gapQuestion';
35 public const FB_MODE_GAP_ANSWERS = 'gapAnswers';
36
40 public const FB_TEXT_GAP_EMPTY_INDEX = -1;
41 public const FB_TEXT_GAP_NOMATCH_INDEX = -2; // indexes for preset answers: 0 - n
42 public const FB_SELECT_GAP_EMPTY_INDEX = -1; // indexes for given select options: 0 - n
43 public const FB_NUMERIC_GAP_EMPTY_INDEX = -1;
48
49 public const SINGLE_GAP_FB_ANSWER_INDEX = -10;
50
51 public function isSaveableInPageObjectEditingMode(): bool
52 {
53 return true;
54 }
55
60 protected function buildGapFeedbackLabel(int $gapIndex, assClozeGap $gap): string
61 {
62 $answers = array();
63
64 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $item) {
65 $answers[] = '"' . ilLegacyFormElementsUtil::prepareFormOutput($item->getAnswertext()) . '"';
66 }
67
68 $answers = implode(' / ', $answers);
69
70 return sprintf(
71 $this->lng->txt('ass_cloze_gap_fb_gap_label'),
72 $gapIndex + 1,
73 $answers
74 );
75 }
76
77 protected function buildTextGapGivenAnswerFeedbackLabel(int $gapIndex, assAnswerCloze $item): string
78 {
79 return sprintf(
80 $this->lng->txt('ass_cloze_gap_fb_txt_match_label'),
81 $gapIndex + 1,
83 );
84 }
85
86 protected function buildTextGapWrongAnswerFeedbackLabel(int $gapIndex): string
87 {
88 return sprintf($this->lng->txt('ass_cloze_gap_fb_txt_nomatch_label'), $gapIndex + 1);
89 }
90
91 protected function buildTextGapEmptyFeedbackLabel(int $gapIndex): string
92 {
93 return sprintf($this->lng->txt('ass_cloze_gap_fb_txt_empty_label'), $gapIndex + 1);
94 }
95
96 protected function buildSelectGapOptionFeedbackLabel(int $gapIndex, assAnswerCloze $item): string
97 {
98 return sprintf(
99 $this->lng->txt('ass_cloze_gap_fb_sel_opt_label'),
100 $gapIndex + 1,
102 );
103 }
104
105 protected function buildSelectGapEmptyFeedbackLabel(int $gapIndex): string
106 {
107 return sprintf($this->lng->txt('ass_cloze_gap_fb_sel_empty_label'), $gapIndex + 1);
108 }
109
110 protected function buildNumericGapValueHitFeedbackLabel(int $gapIndex): string
111 {
112 return sprintf($this->lng->txt('ass_cloze_gap_fb_num_valuehit_label'), $gapIndex + 1);
113 }
114
115 protected function buildNumericGapRangeHitFeedbackLabel(int $gapIndex): string
116 {
117 return sprintf($this->lng->txt('ass_cloze_gap_fb_num_rangehit_label'), $gapIndex + 1);
118 }
119
120 protected function buildNumericGapTooLowFeedbackLabel(int $gapIndex): string
121 {
122 return sprintf($this->lng->txt('ass_cloze_gap_fb_num_toolow_label'), $gapIndex + 1);
123 }
124
125 protected function buildNumericGapTooHighFeedbackLabel(int $gapIndex): string
126 {
127 return sprintf($this->lng->txt('ass_cloze_gap_fb_num_toohigh_label'), $gapIndex + 1);
128 }
129
130 protected function buildNumericGapEmptyFeedbackLabel(int $gapIndex): string
131 {
132 return sprintf($this->lng->txt('ass_cloze_gap_fb_num_empty_label'), $gapIndex + 1);
133 }
134
136 {
137 if (!$this->questionOBJ->getSelfAssessmentEditingMode()) {
138 $header = new ilFormSectionHeaderGUI();
139 $header->setTitle($this->lng->txt('feedback_answers'));
140 $form->addItem($header);
141
142 $feedbackMode = new ilRadioGroupInputGUI(
143 $this->lng->txt('ass_cloze_fb_mode'),
144 'feedback_mode'
145 );
146 $feedbackMode->setRequired(true);
147 $form->addItem($feedbackMode);
148
149 $fbModeGapQuestion = new ilRadioOption(
150 $this->lng->txt('ass_cloze_fb_mode_gap_qst'),
151 self::FB_MODE_GAP_QUESTION,
152 $this->lng->txt('ass_cloze_fb_mode_gap_qst_info')
153 );
154 $this->completeFormPropsForFeedbackModeGapQuestion($fbModeGapQuestion);
155 $feedbackMode->addOption($fbModeGapQuestion);
156
157 $fbModeGapAnswers = new ilRadioOption(
158 $this->lng->txt('ass_cloze_fb_mode_gap_answ'),
159 self::FB_MODE_GAP_ANSWERS,
160 $this->lng->txt('ass_cloze_fb_mode_gap_answ_info')
161 );
162 $this->completeFormPropsForFeedbackModeGapAnswers($fbModeGapAnswers);
163 $feedbackMode->addOption($fbModeGapAnswers);
164 }
165 }
166
168 {
169 foreach ($this->questionOBJ->getGaps() as $gapIndex => $gap) {
170 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
171 $this->buildGapFeedbackLabel($gapIndex, $gap),
172 true
173 );
174
175 $fbModeOpt->addSubItem(
177 $propertyLabel,
178 $this->buildPostVarForFbFieldPerGapQuestion($gapIndex),
179 $this->questionOBJ->isAdditionalContentEditingModePageObject()
180 )
181 );
182 }
183 }
184
186 {
187 foreach ($this->questionOBJ->getGaps() as $gapIndex => $gap) {
188 switch ($gap->getType()) {
190
191 $this->completeFbPropsForTextGap($fbModeOpt, $gap, $gapIndex);
192 break;
193
195
196 $this->completeFbPropsForSelectGap($fbModeOpt, $gap, $gapIndex);
197 break;
198
200
201 $this->completeFbPropsForNumericGap($fbModeOpt, $gap, $gapIndex);
202 break;
203 }
204 }
205 }
206
207 protected function completeFbPropsForTextGap(ilRadioOption $fbModeOpt, assClozeGap $gap, int $gapIndex): void
208 {
209 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $answerIndex => $item) {
210 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
211 $this->buildTextGapGivenAnswerFeedbackLabel($gapIndex, $item),
212 true
213 );
214
215 $propertyPostVar = "feedback_answer_{$gapIndex}_{$answerIndex}";
216
218 $propertyLabel,
219 $propertyPostVar,
220 $this->questionOBJ->isAdditionalContentEditingModePageObject()
221 ));
222 }
223
224 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
225 $this->buildTextGapWrongAnswerFeedbackLabel($gapIndex),
226 true
227 );
228
229 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_TEXT_GAP_NOMATCH_INDEX;
230
232 $propertyLabel,
233 $propertyPostVar,
234 $this->questionOBJ->isAdditionalContentEditingModePageObject()
235 ));
236
237 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
238 $this->buildTextGapEmptyFeedbackLabel($gapIndex),
239 true
240 );
241
242 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_TEXT_GAP_EMPTY_INDEX;
243
245 $propertyLabel,
246 $propertyPostVar,
247 $this->questionOBJ->isAdditionalContentEditingModePageObject()
248 ));
249 }
250
251 protected function completeFbPropsForSelectGap(ilRadioOption $fbModeOpt, assClozeGap $gap, int $gapIndex): void
252 {
253 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $optIndex => $item) {
254 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
255 $this->buildSelectGapOptionFeedbackLabel($gapIndex, $item),
256 true
257 );
258
259 $propertyPostVar = "feedback_answer_{$gapIndex}_{$optIndex}";
260
262 $propertyLabel,
263 $propertyPostVar,
264 $this->questionOBJ->isAdditionalContentEditingModePageObject()
265 ));
266 }
267
268 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
269 $this->buildSelectGapEmptyFeedbackLabel($gapIndex),
270 true
271 );
272
273 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_SELECT_GAP_EMPTY_INDEX;
274
276 $propertyLabel,
277 $propertyPostVar,
278 $this->questionOBJ->isAdditionalContentEditingModePageObject()
279 ));
280 }
281
287 protected function completeFbPropsForNumericGap(ilRadioOption $fbModeOpt, assClozeGap $gap, int $gapIndex): void
288 {
289 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
290 $this->buildNumericGapValueHitFeedbackLabel($gapIndex),
291 true
292 );
293
294 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_NUMERIC_GAP_VALUE_HIT_INDEX;
295
297 $propertyLabel,
298 $propertyPostVar,
299 $this->questionOBJ->isAdditionalContentEditingModePageObject()
300 ));
301
302 if ($gap->numericRangeExists()) {
303 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
304 $this->buildNumericGapRangeHitFeedbackLabel($gapIndex),
305 true
306 );
307
308 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_NUMERIC_GAP_RANGE_HIT_INDEX;
309
311 $propertyLabel,
312 $propertyPostVar,
313 $this->questionOBJ->isAdditionalContentEditingModePageObject()
314 ));
315 }
316
317 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
318 $this->buildNumericGapTooLowFeedbackLabel($gapIndex),
319 true
320 );
321
322 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_NUMERIC_GAP_TOO_LOW_INDEX;
323
325 $propertyLabel,
326 $propertyPostVar,
327 $this->questionOBJ->isAdditionalContentEditingModePageObject()
328 ));
329
330 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
331 $this->buildNumericGapTooHighFeedbackLabel($gapIndex),
332 true
333 );
334
335 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_NUMERIC_GAP_TOO_HIGH_INDEX;
336
338 $propertyLabel,
339 $propertyPostVar,
340 $this->questionOBJ->isAdditionalContentEditingModePageObject()
341 ));
342
343 $propertyLabel = $this->questionOBJ->prepareTextareaOutput(
344 $this->buildNumericGapEmptyFeedbackLabel($gapIndex),
345 true
346 );
347
348 $propertyPostVar = "feedback_answer_{$gapIndex}_" . self::FB_NUMERIC_GAP_EMPTY_INDEX;
349
351 $propertyLabel,
352 $propertyPostVar,
353 $this->questionOBJ->isAdditionalContentEditingModePageObject()
354 ));
355 }
356
358 {
359 if (!$this->questionOBJ->getSelfAssessmentEditingMode()) {
360 /* @var ilRadioGroupInputGUI $fbMode */
361 $fbMode = $form->getItemByPostVar('feedback_mode');
362 $fbMode->setValue($this->questionOBJ->getFeedbackMode());
363
364 if ($this->questionOBJ->isAdditionalContentEditingModePageObject()) {
367 } else {
368 switch ($this->questionOBJ->getFeedbackMode()) {
370
372 break;
373
375
377 break;
378 }
379 }
380 }
381 }
382
384 {
385 foreach ($this->questionOBJ->getGaps() as $gapIndex => $gap) {
386 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::SINGLE_GAP_FB_ANSWER_INDEX);
387 $form->getItemByPostVar($this->buildPostVarForFbFieldPerGapQuestion($gapIndex))->setValue($value);
388 }
389 }
390
392 {
393 foreach ($this->questionOBJ->getGaps() as $gapIndex => $gap) {
394 switch ($gap->getType()) {
396
397 $this->initFbPropsForTextGap($form, $gap, $gapIndex);
398 break;
399
401
402 $this->initFbPropsForSelectGap($form, $gap, $gapIndex);
403 break;
404
406
407 $this->initFbPropsForNumericGap($form, $gapIndex, $gap);
408 break;
409 }
410 }
411 }
412
413 protected function initFbPropsForTextGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex): void
414 {
415 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $answerIndex => $item) {
416 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, $answerIndex);
417 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, $answerIndex);
418 $form->getItemByPostVar($postVar)->setValue($value);
419 }
420
421 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_TEXT_GAP_NOMATCH_INDEX);
422 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_TEXT_GAP_NOMATCH_INDEX);
423 $form->getItemByPostVar($postVar)->setValue($value);
424
425 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_TEXT_GAP_EMPTY_INDEX);
426 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_TEXT_GAP_EMPTY_INDEX);
427 $form->getItemByPostVar($postVar)->setValue($value);
428 }
429
430 protected function initFbPropsForSelectGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex): void
431 {
432 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $optIndex => $item) {
433 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, $optIndex);
434 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, $optIndex);
435 $form->getItemByPostVar($postVar)->setValue($value);
436 }
437
438 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_SELECT_GAP_EMPTY_INDEX);
439 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_SELECT_GAP_EMPTY_INDEX);
440 $form->getItemByPostVar($postVar)->setValue($value);
441 }
442
443 protected function initFbPropsForNumericGap(ilPropertyFormGUI $form, int $gapIndex, assClozeGap $gap): void
444 {
445 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_NUMERIC_GAP_VALUE_HIT_INDEX);
446 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_VALUE_HIT_INDEX);
447 $form->getItemByPostVar($postVar)->setValue($value);
448
449 if ($gap->numericRangeExists()) {
450 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_NUMERIC_GAP_RANGE_HIT_INDEX);
451 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_RANGE_HIT_INDEX);
452 $form->getItemByPostVar($postVar)->setValue($value);
453 }
454
455 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_NUMERIC_GAP_TOO_LOW_INDEX);
456 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_TOO_LOW_INDEX);
457 $form->getItemByPostVar($postVar)->setValue($value);
458
459 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_NUMERIC_GAP_TOO_HIGH_INDEX);
460 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_TOO_HIGH_INDEX);
461 $form->getItemByPostVar($postVar)->setValue($value);
462
463 $value = $this->getSpecificAnswerFeedbackFormValue($gapIndex, self::FB_NUMERIC_GAP_EMPTY_INDEX);
464 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_EMPTY_INDEX);
465 $form->getItemByPostVar($postVar)->setValue($value);
466 }
467
469 {
470 if (!$this->questionOBJ->getSelfAssessmentEditingMode()) {
471 $fbMode = $form->getItemByPostVar('feedback_mode')->getValue();
472
473 if ($fbMode != $this->questionOBJ->getFeedbackMode()) {
474 $this->cleanupSpecificAnswerFeedbacks($this->questionOBJ->getFeedbackMode());
475 }
476
477 $this->saveSpecificFeedbackMode($this->questionOBJ->getId(), $fbMode);
478
479 switch ($this->questionOBJ->getFeedbackMode()) {
481
483 break;
484
486
488 break;
489 }
490 }
491 }
492
494 {
495 foreach ($this->questionOBJ->getGaps() as $gapIndex => $gap) {
496 $postVar = $this->buildPostVarForFbFieldPerGapQuestion($gapIndex);
497 $value = $form->getItemByPostVar($postVar)->getValue();
498
500 $this->questionOBJ->getId(),
501 $gapIndex,
502 self::SINGLE_GAP_FB_ANSWER_INDEX,
503 $value
504 );
505 }
506 }
507
509 {
510 foreach ($this->questionOBJ->getGaps() as $gapIndex => $gap) {
511 switch ($gap->getType()) {
513
514 $this->saveFbPropsForTextGap($form, $gap, $gapIndex);
515 break;
516
518
519 $this->saveFbPropsForSelectGap($form, $gap, $gapIndex);
520 break;
521
523
524 $this->saveFbPropsForNumericGap($form, $gap, $gapIndex);
525 break;
526 }
527 }
528 }
529
530 protected function saveFbPropsForTextGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex): void
531 {
532 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $answerIndex => $item) {
533 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, $answerIndex);
534 $value = $form->getItemByPostVar($postVar)->getValue();
536 $this->questionOBJ->getId(),
537 $gapIndex,
538 $answerIndex,
539 $value
540 );
541 }
542
543 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_TEXT_GAP_NOMATCH_INDEX);
544 $value = $form->getItemByPostVar($postVar)->getValue();
546 $this->questionOBJ->getId(),
547 $gapIndex,
548 self::FB_TEXT_GAP_NOMATCH_INDEX,
549 $value
550 );
551
552 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_TEXT_GAP_EMPTY_INDEX);
553 $value = $form->getItemByPostVar($postVar)->getValue();
555 $this->questionOBJ->getId(),
556 $gapIndex,
557 self::FB_TEXT_GAP_EMPTY_INDEX,
558 $value
559 );
560 }
561
562 protected function saveFbPropsForSelectGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex): void
563 {
564 foreach ($gap->getItems($this->randomGroup()->dontShuffle()) as $optIndex => $item) {
565 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, $optIndex);
566 $value = $form->getItemByPostVar($postVar)->getValue();
568 $this->questionOBJ->getId(),
569 $gapIndex,
570 $optIndex,
571 $value
572 );
573 }
574
575 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_SELECT_GAP_EMPTY_INDEX);
576 $value = $form->getItemByPostVar($postVar)->getValue();
578 $this->questionOBJ->getId(),
579 $gapIndex,
580 self::FB_SELECT_GAP_EMPTY_INDEX,
581 $value
582 );
583 }
584
585 protected function saveFbPropsForNumericGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex): void
586 {
587 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_VALUE_HIT_INDEX);
588 $value = $form->getItemByPostVar($postVar)->getValue();
590 $this->questionOBJ->getId(),
591 $gapIndex,
592 self::FB_NUMERIC_GAP_VALUE_HIT_INDEX,
593 $value
594 );
595
596 if ($gap->numericRangeExists()) {
597 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_RANGE_HIT_INDEX);
598 $value = $form->getItemByPostVar($postVar)->getValue();
600 $this->questionOBJ->getId(),
601 $gapIndex,
602 self::FB_NUMERIC_GAP_RANGE_HIT_INDEX,
603 $value
604 );
605 }
606
607 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_TOO_LOW_INDEX);
608 $value = $form->getItemByPostVar($postVar)->getValue();
610 $this->questionOBJ->getId(),
611 $gapIndex,
612 self::FB_NUMERIC_GAP_TOO_LOW_INDEX,
613 $value
614 );
615
616 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_TOO_HIGH_INDEX);
617 $value = $form->getItemByPostVar($postVar)->getValue();
619 $this->questionOBJ->getId(),
620 $gapIndex,
621 self::FB_NUMERIC_GAP_TOO_HIGH_INDEX,
622 $value
623 );
624
625 $postVar = $this->buildPostVarForFbFieldPerGapAnswers($gapIndex, self::FB_NUMERIC_GAP_EMPTY_INDEX);
626 $value = $form->getItemByPostVar($postVar)->getValue();
628 $this->questionOBJ->getId(),
629 $gapIndex,
630 self::FB_NUMERIC_GAP_EMPTY_INDEX,
631 $value
632 );
633 }
634
635 protected function duplicateSpecificFeedback(int $originalQuestionId, int $duplicateQuestionId): void
636 {
637 $this->syncSpecificFeedbackSetting($originalQuestionId, $duplicateQuestionId);
638
639 parent::duplicateSpecificFeedback($originalQuestionId, $duplicateQuestionId);
640 }
641
642 private function syncSpecificFeedbackSetting(int $sourceQuestionId, int $targetQuestionId): void
643 {
644 $res = $this->db->queryF(
645 "SELECT feedback_mode FROM {$this->questionOBJ->getAdditionalTableName()} WHERE question_fi = %s",
646 array('integer'),
647 array($sourceQuestionId)
648 );
649
650 $row = $this->db->fetchAssoc($res);
651
652 $this->db->update(
653 $this->questionOBJ->getAdditionalTableName(),
654 array( 'feedback_mode' => array('text', $row['feedback_mode']) ),
655 array( 'question_fi' => array('integer', $targetQuestionId) )
656 );
657 }
658
659 protected function syncSpecificFeedback(int $originalQuestionId, int $duplicateQuestionId): void
660 {
661 $this->syncSpecificFeedbackSetting($originalQuestionId, $duplicateQuestionId);
662 parent::syncSpecificFeedback($originalQuestionId, $duplicateQuestionId);
663 }
664
669 protected function saveSpecificFeedbackMode(int $questionId, string $feedbackMode): void
670 {
671 $this->questionOBJ->setFeedbackMode($feedbackMode);
672
673 $this->db->update(
674 $this->questionOBJ->getAdditionalTableName(),
675 array('feedback_mode' => array('text', $feedbackMode)),
676 array('question_fi' => array('integer', $questionId))
677 );
678 }
679
680 protected function buildPostVarForFbFieldPerGapQuestion(int $gapIndex): string
681 {
682 return "feedback_answer_{$gapIndex}";
683 }
684
685 protected function buildPostVarForFbFieldPerGapAnswers(int $gapIndex, int $answerIndex): string
686 {
687 return "feedback_answer_{$gapIndex}_{$answerIndex}";
688 }
689
690 protected function getSpecificAnswerFeedbackFormValue(int $gapIndex, int $answerIndex): string
691 {
692 if ($this->questionOBJ->isAdditionalContentEditingModePageObject()) {
693 $pageObjectId = $this->getSpecificAnswerFeedbackPageObjectId(
694 $this->questionOBJ->getId(),
695 $gapIndex,
696 $answerIndex
697 );
698
699 $value = $this->getPageObjectNonEditableValueHTML(
701 $pageObjectId
702 );
703 } else {
704 $value = $this->questionOBJ->prepareTextareaOutput(
705 $this->getSpecificAnswerFeedbackContent($this->questionOBJ->getId(), $gapIndex, $answerIndex)
706 );
707 }
708
709 return $value;
710 }
711
712 protected function cleanupSpecificAnswerFeedbacks(string $fbMode): void
713 {
714 switch ($fbMode) {
716 $feedbackIds = $this->fetchFeedbackIdsForGapQuestionMode();
717 break;
718
720 $feedbackIds = $this->fetchFeedbackIdsForGapAnswersMode();
721 break;
722
723 default: $feedbackIds = array();
724 }
725
726 $this->deleteSpecificAnswerFeedbacksByIds($feedbackIds);
727 }
728
732 protected function fetchFeedbackIdsForGapQuestionMode(): array
733 {
734 require_once 'Modules/TestQuestionPool/classes/feedback/class.ilAssSpecificFeedbackIdentifierList.php';
735 $feedbackIdentifiers = new ilAssSpecificFeedbackIdentifierList();
736 $feedbackIdentifiers->load($this->questionOBJ->getId());
737
738 $feedbackIds = array();
739
740 foreach ($feedbackIdentifiers as $identifier) {
741 if ($identifier->getAnswerIndex() != self::SINGLE_GAP_FB_ANSWER_INDEX) {
742 continue;
743 }
744
745 $feedbackIds[] = $identifier->getFeedbackId();
746 }
747
748 return $feedbackIds;
749 }
750
754 protected function fetchFeedbackIdsForGapAnswersMode(): array
755 {
756 require_once 'Modules/TestQuestionPool/classes/feedback/class.ilAssSpecificFeedbackIdentifierList.php';
757 $feedbackIdentifiers = new ilAssSpecificFeedbackIdentifierList();
758 $feedbackIdentifiers->load($this->questionOBJ->getId());
759
760 $feedbackIds = array();
761
762 foreach ($feedbackIdentifiers as $identifier) {
763 if ($identifier->getAnswerIndex() == self::SINGLE_GAP_FB_ANSWER_INDEX) {
764 continue;
765 }
766
767 $feedbackIds[] = $identifier->getFeedbackId();
768 }
769
770 return $feedbackIds;
771 }
772
773 public function isSpecificAnswerFeedbackAvailable(int $question_id): bool
774 {
775 if ($this->questionOBJ->getFeedbackMode() === self::FB_MODE_GAP_QUESTION) {
776 $feedback_ids = $this->fetchFeedbackIdsForGapQuestionMode();
777 } else {
778 $feedback_ids = $this->fetchFeedbackIdsForGapAnswersMode();
779 }
780
781 if ($this->questionOBJ->isAdditionalContentEditingModePageObject()) {
782 $all_feedback_content = '';
783 foreach ($feedback_ids as $feedback_id) {
784 $all_feedback_content .= $this->getPageObjectXML(
786 $feedback_id
787 );
788 }
789 return trim(strip_tags($all_feedback_content)) !== '';
790 }
791
792 return implode('', $this->getSpecificFeedbackContentForFeedbackIds($feedback_ids)) !== '';
793 }
794
798 protected function deleteSpecificAnswerFeedbacksByIds(array $feedbackIds): void
799 {
800 if ($this->questionOBJ->isAdditionalContentEditingModePageObject()) {
801 foreach ($feedbackIds as $fbId) {
803 }
804 }
805
806 $IN_feedbackIds = $this->db->in('feedback_id', $feedbackIds, false, 'integer');
807 $this->db->manipulate("DELETE FROM {$this->getSpecificFeedbackTableName()} WHERE {$IN_feedbackIds}");
808 }
809
810 public function determineTestOutputGapFeedback(int $gapIndex, int $answerIndex): string
811 {
812 if ($this->questionOBJ->getFeedbackMode() == self::FB_MODE_GAP_QUESTION) {
814 $this->questionOBJ->getId(),
815 $gapIndex,
816 self::SINGLE_GAP_FB_ANSWER_INDEX
817 );
818 }
819
820 return $this->getSpecificAnswerFeedbackTestPresentation($this->questionOBJ->getId(), $gapIndex, $answerIndex);
821 }
822
823 public function determineAnswerIndexForAnswerValue(assClozeGap $gap, string $answerValue): int
824 {
825 switch ($gap->getType()) {
826 case CLOZE_TEXT:
827
828 if (!strlen($answerValue)) {
830 }
831
832 $items = $gap->getItems($this->randomGroup()->dontShuffle());
833
834 foreach ($items as $answerIndex => $answer) {
835 /* @var assAnswerCloze $answer */
836
837 if ($answer->getAnswertext() == $answerValue) {
838 return $answerIndex;
839 }
840 }
841
843
844 case CLOZE_SELECT:
845
846 if (strlen($answerValue)) {
847 return $answerValue;
848 }
849
851
852 default:
853 case CLOZE_NUMERIC:
854
855 if (!strlen($answerValue)) {
857 }
858
859 /* @var assAnswerCloze $item */
860
861 $item = current($gap->getItems($this->randomGroup()->dontShuffle()));
862
863 if ($answerValue == $item->getAnswertext()) {
865 }
866
867 require_once 'Services/Math/classes/class.EvalMath.php';
868 $math = new EvalMath();
869
870 $item = $gap->getItem(0);
871 $lowerBound = $math->evaluate($item->getLowerBound());
872 $upperBound = $math->evaluate($item->getUpperBound());
873 $preciseValue = $math->evaluate($item->getAnswertext());
874
875 $solutionValue = $math->evaluate($answerValue);
876
877 if ($solutionValue == $preciseValue) {
879 } elseif ($solutionValue >= $lowerBound && $solutionValue <= $upperBound) {
881 } elseif ($solutionValue < $lowerBound) {
883 }
884
885 // if ($solutionValue > $upperBound) {
887 //}
888 }
889 }
890
891 private function randomGroup(): RandomGroup
892 {
893 global $DIC;
894
895 return $DIC->refinery()->random();
896 }
897}
getAnswertext()
Gets the answer text.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class for cloze question gaps.
getItem($a_index)
Gets the item with a given index.
getItems(Transformation $shuffler, ?int $gap_index=null)
Gets the items of a cloze gap.
completeFbPropsForNumericGap(ilRadioOption $fbModeOpt, assClozeGap $gap, int $gapIndex)
const FB_MODE_GAP_QUESTION
constants for different feedback modes (per gap or per gap-answers/options)
completeFormPropsForFeedbackModeGapQuestion(ilRadioOption $fbModeOpt)
completeFormPropsForFeedbackModeGapAnswers(ilRadioOption $fbModeOpt)
buildTextGapGivenAnswerFeedbackLabel(int $gapIndex, assAnswerCloze $item)
completeFbPropsForTextGap(ilRadioOption $fbModeOpt, assClozeGap $gap, int $gapIndex)
saveFbPropsForNumericGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex)
isSpecificAnswerFeedbackAvailable(int $question_id)
syncSpecificFeedbackSetting(int $sourceQuestionId, int $targetQuestionId)
initFbPropsForTextGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex)
saveFbPropsForTextGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex)
saveFbPropsForSelectGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex)
initFbPropsForSelectGap(ilPropertyFormGUI $form, assClozeGap $gap, int $gapIndex)
determineAnswerIndexForAnswerValue(assClozeGap $gap, string $answerValue)
const FB_TEXT_GAP_EMPTY_INDEX
constants for answer indexes in case of FB_MODE_GAP_ANSWERS
initFeedbackFieldsPerGapQuestion(ilPropertyFormGUI $form)
duplicateSpecificFeedback(int $originalQuestionId, int $duplicateQuestionId)
duplicates the SPECIFIC feedback relating to the given original question id and saves it for the give...
deleteSpecificAnswerFeedbacksByIds(array $feedbackIds)
isSaveableInPageObjectEditingMode()
returns the fact wether the feedback editing form is saveable in page object editing or not.
initSpecificFormProperties(ilPropertyFormGUI $form)
initialises a given form object's specific form properties relating to this question type
getSpecificAnswerFeedbackFormValue(int $gapIndex, int $answerIndex)
saveFeedbackFieldsPerGapQuestion(ilPropertyFormGUI $form)
saveSpecificFeedbackMode(int $questionId, string $feedbackMode)
saves the given specific feedback mode for the given question id to the db.
saveSpecificFormProperties(ilPropertyFormGUI $form)
saves a given form object's SPECIFIC form properties relating to this question type
completeSpecificFormProperties(ilPropertyFormGUI $form)
completes a given form object with the specific form properties required by this question type
initFbPropsForNumericGap(ilPropertyFormGUI $form, int $gapIndex, assClozeGap $gap)
initFeedbackFieldsPerGapAnswers(ilPropertyFormGUI $form)
buildGapFeedbackLabel(int $gapIndex, assClozeGap $gap)
builds an answer option label from given (mixed type) index and answer (overwrites parent method from...
buildPostVarForFbFieldPerGapAnswers(int $gapIndex, int $answerIndex)
buildSelectGapOptionFeedbackLabel(int $gapIndex, assAnswerCloze $item)
completeFbPropsForSelectGap(ilRadioOption $fbModeOpt, assClozeGap $gap, int $gapIndex)
determineTestOutputGapFeedback(int $gapIndex, int $answerIndex)
saveFeedbackFieldsPerGapAnswers(ilPropertyFormGUI $form)
syncSpecificFeedback(int $originalQuestionId, int $duplicateQuestionId)
syncs the SPECIFIC feedback from a duplicated question back to the original question
buildTextGapWrongAnswerFeedbackLabel(int $gapIndex)
saveSpecificAnswerFeedbackContent(int $questionId, int $questionIndex, int $answerIndex, string $feedbackContent)
getSpecificAnswerFeedbackContent(int $questionId, int $questionIndex, int $answerIndex)
getSpecificAnswerFeedbackTestPresentation(int $questionId, int $questionIndex, int $answerIndex)
returns the html of SPECIFIC feedback for the given question id and answer index for test presentatio...
getSpecificAnswerFeedbackPageObjectId(int $questionId, int $questionIndex, int $answerIndex)
returns a useable page object id for specific answer feedback page objects for the given question id ...
buildFeedbackContentFormProperty(string $label, string $postVar, bool $asNonEditable)
builds and returns a form property gui object with the given label and postvar that is addable to pro...
ensurePageObjectDeleted(string $pageObjectType, int $pageObjectId)
getPageObjectNonEditableValueHTML(string $pageObjectType, int $pageObjectId)
returns html content to be used as value for non editable value form properties in feedback editing f...
getPageObjectXML(string $pageObjectType, int $pageObjectId)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static prepareFormOutput($a_str, bool $a_strip=false)
This class represents a property form user interface.
getItemByPostVar(string $a_post_var)
This class represents a property in a property form.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
global $DIC
Definition: feed.php:28
const CLOZE_NUMERIC
const CLOZE_SELECT
const CLOZE_TEXT
Cloze question constants.
$res
Definition: ltiservices.php:69