ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.assKprimChoice.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
23use ILIAS\TestQuestionPool\ManipulateImagesInChoiceQuestionsTrait;
25
33{
34 use ManipulateImagesInChoiceQuestionsTrait;
35
36 public const NUM_REQUIRED_ANSWERS = 4;
37
39
40 public const ANSWER_TYPE_SINGLE_LINE = 'singleLine';
41 public const ANSWER_TYPE_MULTI_LINE = 'multiLine';
42
43 public const OPTION_LABEL_RIGHT_WRONG = 'right_wrong';
44 public const OPTION_LABEL_PLUS_MINUS = 'plus_minus';
45 public const OPTION_LABEL_APPLICABLE_OR_NOT = 'applicable_or_not';
46 public const OPTION_LABEL_ADEQUATE_OR_NOT = 'adequate_or_not';
47 public const OPTION_LABEL_CUSTOM = 'customlabel';
48
49 public const DEFAULT_THUMB_SIZE = 150;
50 public const THUMB_PREFIX = 'thumb.';
51
52 private bool $shuffle_answers_enabled = true;
55 private bool $scorePartialSolutionEnabled = true;
57 private string $customTrueOptionLabel = '';
58 private string $customFalseOptionLabel = '';
60
61 private $answers = [];
62
63 public function __construct($title = '', $comment = '', $author = '', $owner = -1, $question = '')
64 {
66
67 for ($i = count($this->answers); $i < self::NUM_REQUIRED_ANSWERS; $i++) {
68 $answer = new ilAssKprimChoiceAnswer();
69 $answer->setPosition($i);
70 $this->answers[$answer->getPosition()] = $answer;
71 }
72 }
73
74 public function getQuestionType(): string
75 {
76 return 'assKprimChoice';
77 }
78
79 public function getAdditionalTableName(): string
80 {
81 return "qpl_qst_kprim";
82 }
83
84 public function getAnswerTableName(): string
85 {
86 return "qpl_a_kprim";
87 }
88
90 {
91 $this->shuffle_answers_enabled = $shuffle_answers_enabled;
92 }
93
94 public function isShuffleAnswersEnabled(): bool
95 {
97 }
98
99 public function setAnswerType($answerType): void
100 {
101 $this->answerType = $answerType;
102 }
103
104 public function getAnswerType(): string
105 {
106 return $this->answerType;
107 }
108
109 public function setThumbSize(int $thumbSize): void
110 {
111 $this->thumbSize = $thumbSize;
112 }
113
114 public function getThumbSize(): int
115 {
116 return $this->thumbSize;
117 }
118
120 {
121 $this->scorePartialSolutionEnabled = $scorePartialSolutionEnabled;
122 }
123
124 public function isScorePartialSolutionEnabled(): bool
125 {
127 }
128
129 public function setOptionLabel(string $option_label): void
130 {
131 $this->option_label = $option_label;
132 }
133
134 public function getOptionLabel(): string
135 {
136 return $this->option_label;
137 }
138
140 {
141 $this->customTrueOptionLabel = $customTrueOptionLabel;
142 }
143
144 public function getCustomTrueOptionLabel()
145 {
147 }
148
150 {
151 $this->customFalseOptionLabel = $customFalseOptionLabel;
152 }
153
155 {
157 }
158
160 {
161 $this->specific_feedback_setting = $specific_feedback_setting;
162 }
163
165 {
167 }
168
169 public function setAnswers($answers): void
170 {
171 if (is_null($answers)) {
172 return;
173 }
174 $clean_answer_text = function (ilAssKprimChoiceAnswer $answer) {
175 $answer->setAnswertext(
176 $this->getHtmlQuestionContentPurifier()->purify($answer->getAnswertext())
177 );
178 return $answer;
179 };
180 $this->answers = array_map($clean_answer_text, $answers);
181 }
182
186 public function getAnswers(): array
187 {
188 return $this->answers;
189 }
190
191 public function getAnswer($position): ?ilAssKprimChoiceAnswer
192 {
193 foreach ($this->getAnswers() as $answer) {
194 if ($answer->getPosition() == $position) {
195 return $answer;
196 }
197 }
198
199 return null;
200 }
201
202 public function addAnswer(ilAssKprimChoiceAnswer $answer): void
203 {
204 $answer->setAnswertext(
205 $this->getHtmlQuestionContentPurifier()->purify($answer->getAnswertext())
206 );
207 $this->answers[] = $answer;
208 }
209
210 public function loadFromDb($questionId): void
211 {
212 $res = $this->db->queryF($this->buildQuestionDataQuery(), ['integer'], [$questionId]);
213
214 while ($data = $this->db->fetchAssoc($res)) {
215 $this->setId($questionId);
216
217 $this->setOriginalId($data['original_id']);
218
219 $this->setObjId($data['obj_fi']);
220
221 $this->setTitle($data['title'] ?? '');
222 $this->setNrOfTries($data['nr_of_tries']);
223 $this->setComment($data['description'] ?? '');
224 $this->setAuthor($data['author']);
225 $this->setPoints($data['points']);
226 $this->setOwner($data['owner']);
227 $this->setLastChange($data['tstamp']);
228 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data['question_text'] ?? '', 1));
229
230 $this->setShuffleAnswersEnabled((bool) $data['shuffle_answers']);
231
232 if ($this->isValidAnswerType($data['answer_type'])) {
233 $this->setAnswerType($data['answer_type']);
234 }
235
236 if (is_numeric($data['thumb_size'])) {
237 $this->setThumbSize((int) $data['thumb_size']);
238 }
239
240 if ($this->isValidOptionLabel($data['opt_label'])) {
241 $this->setOptionLabel($data['opt_label']);
242 }
243
244 if ($data['custom_true'] !== null) {
245 $this->setCustomTrueOptionLabel($data['custom_true']);
246 }
247
248 if ($data['custom_false'] !== null) {
249 $this->setCustomFalseOptionLabel($data['custom_false']);
250 }
251
252 if ($data['score_partsol'] !== null) {
253 $this->setScorePartialSolutionEnabled((bool) $data['score_partsol']);
254 }
255
256 if (isset($data['feedback_setting'])) {
257 $this->setSpecificFeedbackSetting((int) $data['feedback_setting']);
258 }
259
260 try {
261 $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
264 }
265
266 try {
267 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
269 }
270 }
271
272 $this->loadAnswerData($questionId);
273
274 parent::loadFromDb($questionId);
275 }
276
277 private function loadAnswerData(int $question_id): void
278 {
279 $res = $this->db->queryF(
280 "SELECT * FROM {$this->getAnswerTableName()} WHERE question_fi = %s ORDER BY position ASC",
281 ['integer'],
282 [$question_id]
283 );
284
285 while ($data = $this->db->fetchAssoc($res)) {
286 $answer = new ilAssKprimChoiceAnswer();
287
288 $answer->setPosition($data['position']);
289
290 $answer->setAnswertext(ilRTE::_replaceMediaObjectImageSrc($data['answertext'] ?? '', 1));
291
292 $answer->setImageFile($data['imagefile']);
293 $answer->setThumbPrefix($this->getThumbPrefix());
294 $answer->setImageFsDir($this->getImagePath());
295 $answer->setImageWebDir($this->getImagePathWeb());
296
297 $answer->setCorrectness($data['correctness']);
298
299 $this->answers[$answer->getPosition()] = $answer;
300 }
301 }
302
303 public function saveToDb(?int $original_id = null): void
304 {
308
309 parent::saveToDb();
310 }
311
313 {
314 $this->db->replace(
315 $this->getAdditionalTableName(),
316 [
317 'question_fi' => ['integer', $this->getId()]
318 ],
319 [
320 'shuffle_answers' => ['integer', (int) $this->isShuffleAnswersEnabled()],
321 'answer_type' => ['text', $this->getAnswerType()],
322 'thumb_size' => ['integer', $this->getThumbSize()],
323 'opt_label' => ['text', $this->getOptionLabel()],
324 'custom_true' => ['text', $this->getCustomTrueOptionLabel()],
325 'custom_false' => ['text', $this->getCustomFalseOptionLabel()],
326 'score_partsol' => ['integer', (int) $this->isScorePartialSolutionEnabled()],
327 'feedback_setting' => ['integer', $this->getSpecificFeedbackSetting()]
328 ]
329 );
330 }
331
333 {
334 foreach ($this->getAnswers() as $answer) {
335 $this->db->replace(
336 $this->getAnswerTableName(),
337 [
338 'question_fi' => ['integer', $this->getId()],
339 'position' => ['integer', (int) $answer->getPosition()]
340 ],
341 [
342 'answertext' => ['text', $answer->getAnswertext()],
343 'imagefile' => ['text', $answer->getImageFile()],
344 'correctness' => ['integer', (int) $answer->getCorrectness()]
345 ]
346 );
347 }
348 }
349
350 public function isComplete(): bool
351 {
352 foreach ([$this->title, $this->author, $this->question] as $text) {
353 if (!strlen($text)) {
354 return false;
355 }
356 }
357
358 if (!isset($this->points)) {
359 return false;
360 }
361
362 foreach ($this->getAnswers() as $answer) {
363 /* @var ilAssKprimChoiceAnswer $answer */
364
365 if (is_null($answer->getCorrectness())) {
366 return false;
367 }
368
369 if (
370 (!is_string($answer->getAnswertext()) || $answer->getAnswertext() === '') &&
371 (!is_string($answer->getImageFile()) || $answer->getImageFile() === '')
372 ) {
373 return false;
374 }
375 }
376
377 return true;
378 }
379
380 public function saveWorkingData(
381 int $active_id,
382 ?int $pass = null,
383 bool $authorized = true
384 ): bool {
385 if ($pass === null) {
386 $pass = ilObjTest::_getPass($active_id);
387 }
388
389 $answer = $this->getSolutionSubmit();
390 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(
391 function () use ($answer, $active_id, $pass, $authorized) {
392 $this->removeCurrentSolution($active_id, $pass, $authorized);
393 foreach ($answer as $index => $value) {
394 if ($value !== null) {
395 $this->saveCurrentSolution($active_id, $pass, (int) $index, (int) $value, $authorized);
396 }
397 }
398 }
399 );
400
401 return true;
402 }
403
404 public function calculateReachedPoints(
405 int $active_id,
406 ?int $pass = null,
407 bool $authorized_solution = true
408 ): float {
409 $found_values = [];
410 if (is_null($pass)) {
411 $pass = $this->getSolutionMaxPass($active_id);
412 }
413
414 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized_solution);
415
416 while ($data = $this->db->fetchAssoc($result)) {
417 $found_values[(int) $data['value1']] = (int) $data['value2'];
418 }
419
420 $points = $this->calculateReachedPointsForSolution($found_values, $active_id);
421
422 return $points;
423 }
424
425 public function getValidAnswerTypes(): array
426 {
427 return [self::ANSWER_TYPE_SINGLE_LINE, self::ANSWER_TYPE_MULTI_LINE];
428 }
429
430 public function isValidAnswerType($answerType): bool
431 {
432 $validTypes = $this->getValidAnswerTypes();
433 return in_array($answerType, $validTypes);
434 }
435
436 public function isSingleLineAnswerType($answerType): bool
437 {
438 return $answerType == assKprimChoice::ANSWER_TYPE_SINGLE_LINE;
439 }
440
446 {
447 return [
448 self::ANSWER_TYPE_SINGLE_LINE => $lng->txt('answers_singleline'),
449 self::ANSWER_TYPE_MULTI_LINE => $lng->txt('answers_multiline')
450 ];
451 }
452
453 public function getValidOptionLabels(): array
454 {
455 return [
456 self::OPTION_LABEL_RIGHT_WRONG,
457 self::OPTION_LABEL_PLUS_MINUS,
458 self::OPTION_LABEL_APPLICABLE_OR_NOT,
459 self::OPTION_LABEL_ADEQUATE_OR_NOT,
460 self::OPTION_LABEL_CUSTOM
461 ];
462 }
463
465 {
466 return array_reduce(
467 $this->getValidOptionLabels(),
468 function (array $c, string $option_label) use ($lng): array {
469 $c[$option_label] = $lng->txt($this->getLangVarForOptionLabel($option_label));
470 return $c;
471 },
472 []
473 );
474 }
475
476 public function getLangVarForOptionLabel(string $option_label): string
477 {
478 return match ($option_label) {
479 self::OPTION_LABEL_RIGHT_WRONG => 'option_label_right_wrong',
480 self::OPTION_LABEL_PLUS_MINUS => 'option_label_plus_minus',
481 self::OPTION_LABEL_APPLICABLE_OR_NOT => 'option_label_applicable_or_not',
482 self::OPTION_LABEL_ADEQUATE_OR_NOT => 'option_label_adequate_or_not',
483 self::OPTION_LABEL_CUSTOM => 'option_label_custom'
484 };
485 }
486
487 public function isValidOptionLabel(?string $option_label): bool
488 {
489 $valid_labels = $this->getValidOptionLabels();
490 return in_array($option_label, $valid_labels);
491 }
492
493 public function getTrueOptionLabelTranslation(ilLanguage $lng, string $option_label): string
494 {
495 if ($option_label === self::OPTION_LABEL_CUSTOM) {
496 return $this->getCustomTrueOptionLabel();
497 }
498
499 return $lng->txt(
500 $this->getTrueOptionLabel($option_label)
501 );
502 }
503
504 public function getTrueOptionLabel(string $option_label): string
505 {
506 switch ($option_label) {
507 case self::OPTION_LABEL_RIGHT_WRONG:
508 return 'option_label_right';
509
510 case self::OPTION_LABEL_PLUS_MINUS:
511 return 'option_label_plus';
512
513 case self::OPTION_LABEL_APPLICABLE_OR_NOT:
514 return 'option_label_applicable';
515
516 case self::OPTION_LABEL_ADEQUATE_OR_NOT:
517 return 'option_label_adequate';
518
519 default:
520 throw new \ErrorException('Invalide Option Label');
521 }
522 }
523
524 public function getFalseOptionLabelTranslation(ilLanguage $lng, string $option_label): string
525 {
526 if ($option_label === self::OPTION_LABEL_CUSTOM) {
527 return $this->getCustomFalseOptionLabel();
528 }
529
530 return $lng->txt(
531 $this->getFalseOptionLabel($option_label)
532 );
533 }
534
535 private function getFalseOptionLabel(string $option_label): string
536 {
537 switch ($option_label) {
538 case self::OPTION_LABEL_RIGHT_WRONG:
539 return 'option_label_wrong';
540
541 case self::OPTION_LABEL_PLUS_MINUS:
542 return 'option_label_minus';
543
544 case self::OPTION_LABEL_APPLICABLE_OR_NOT:
545 return 'option_label_not_applicable';
546
547 case self::OPTION_LABEL_ADEQUATE_OR_NOT:
548 return 'option_label_not_adequate';
549
550 default:
551 throw new \ErrorException('Invalide Option Label');
552 }
553 }
554
555 public function getInstructionTextTranslation(ilLanguage $lng, $option_label): string
556 {
557 return sprintf(
558 $lng->txt('kprim_instruction_text'),
559 $this->getTrueOptionLabelTranslation($lng, $option_label),
560 $this->getFalseOptionLabelTranslation($lng, $option_label)
561 );
562 }
563
564 public function isCustomOptionLabel($labelValue): bool
565 {
566 return $labelValue == self::OPTION_LABEL_CUSTOM;
567 }
568
569 public function handleFileUploads($answers, $files): void
570 {
571 foreach ($answers as $answer) {
572 /* @var ilAssKprimChoiceAnswer $answer */
573
574 if (!isset($files[$answer->getPosition()])) {
575 continue;
576 }
577
578 $this->handleFileUpload($answer, $files[$answer->getPosition()]);
579 }
580 }
581
582 private function handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData): int
583 {
584 $imagePath = $this->getImagePath();
585
586 if (!file_exists($imagePath)) {
587 ilFileUtils::makeDirParents($imagePath);
588 }
589
590 $filename = $this->buildHashedImageFilename($fileData['name'], true);
591
592 $answer->setImageFsDir($imagePath);
593 $answer->setImageFile($filename);
594
595 if (!ilFileUtils::moveUploadedFile($fileData['tmp_name'], $filename, $answer->getImageFsPath())) {
596 return 2;
597 }
598
599 $this->generateThumbForFile($filename, $this->getImagePath(), $this->getThumbSize());
600
601 return 0;
602 }
603
604 public function removeAnswerImage($position): void
605 {
606 $answer = $this->getAnswer($position);
607
608 if (file_exists($answer->getImageFsPath())) {
609 ilFileUtils::delDir($answer->getImageFsPath());
610 }
611
612 if (file_exists($answer->getThumbFsPath())) {
613 ilFileUtils::delDir($answer->getThumbFsPath());
614 }
615
616 $answer->setImageFile(null);
617 }
618
619 protected function getSolutionSubmit(): array
620 {
621 $solutionSubmit = [];
622 $post = $this->dic->http()->wrapper()->post();
623
624 foreach ($this->getAnswers() as $index => $a) {
625 if ($post->has("kprim_choice_result_$index")) {
626 $value = $post->retrieve(
627 "kprim_choice_result_$index",
628 $this->dic->refinery()->kindlyTo()->string()
629 );
630 if (is_numeric($value)) {
631 $solutionSubmit[] = $value;
632 }
633 } else {
634 $solutionSubmit[] = null;
635 }
636 }
637 return $solutionSubmit;
638 }
639
640 protected function calculateReachedPointsForSolution(?array $found_values, int $active_id = 0): float
641 {
642 $numCorrect = 0;
643 if ($found_values === null) {
644 $found_values = [];
645 }
646 foreach ($this->getAnswers() as $answer) {
647 if (!isset($found_values[$answer->getPosition()])) {
648 continue;
649 }
650
651 if ($found_values[$answer->getPosition()] == $answer->getCorrectness()) {
652 $numCorrect++;
653 }
654 }
655
656 if ($numCorrect >= self::NUM_REQUIRED_ANSWERS) {
657 $points = $this->getPoints();
658 } elseif ($this->isScorePartialSolutionEnabled() && $numCorrect >= self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS) {
659 $points = $this->getPoints() / 2;
660 } else {
661 $points = 0;
662 }
663
664 if ($active_id) {
665 if (count($found_values) == 0) {
666 $points = 0;
667 }
668 }
669 return (float) $points;
670 }
671
673 \assQuestion $target
674 ): \assQuestion {
675 $this->cloneImages(
676 $this->getId(),
677 $this->getObjId(),
678 $target->getId(),
679 $target->getObjId(),
680 $this->getAnswers()
681 );
682 return $target;
683 }
684
685 protected function getRTETextWithMediaObjects(): string
686 {
687 $combinedText = parent::getRTETextWithMediaObjects();
688
689 foreach ($this->getAnswers() as $answer) {
690 $combinedText .= $answer->getAnswertext();
691 }
692
693 return $combinedText;
694 }
695
700 {
701 foreach ($this->getAnswers() as $answer) {
702 /* @var ilAssKprimChoiceAnswer $answer */
703 $answer->setAnswertext($migrator->migrateToLmContent($answer->getAnswertext()));
704 }
705 }
706
710 public function toJSON(): string
711 {
712 $this->lng->loadLanguageModule('assessment');
713
714 $result = [];
715 $result['id'] = $this->getId();
716 $result['type'] = $this->getQuestionType();
717 $result['title'] = $this->getTitleForHTMLOutput();
718 $result['question'] = $this->formatSAQuestion($this->getQuestion());
719 $result['instruction'] = $this->getInstructionTextTranslation(
720 $this->lng,
721 $this->getOptionLabel()
722 );
723 $result['nr_of_tries'] = $this->getNrOfTries();
724 $result['shuffle'] = $this->isShuffleAnswersEnabled();
725 $result['feedback'] = [
726 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
727 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
728 ];
729
730 $result['trueOptionLabel'] = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
731 $result['falseOptionLabel'] = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
732
733 $result['num_allowed_failures'] = $this->getNumAllowedFailures();
734
735 $answers = [];
736 $has_image = false;
737
738 foreach ($this->getAnswers() as $key => $answer) {
739 if (strlen((string) $answer->getImageFile())) {
740 $has_image = true;
741 }
742
743 $answers[] = [
744 'answertext' => $this->formatSAQuestion($answer->getAnswertext() ?? ''),
745 'correctness' => (bool) $answer->getCorrectness(),
746 'order' => (int) $answer->getPosition(),
747 'image' => (string) $answer->getImageFile(),
748 'feedback' => $this->formatSAQuestion(
749 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
750 )
751 ];
752 }
753
754 $result['answers'] = $answers;
755
756 if ($has_image) {
757 $result['path'] = $this->getImagePathWeb();
758 $result['thumb'] = $this->getThumbSize();
759 }
760
761 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
762 $result['mobs'] = $mobs;
763
764 return json_encode($result);
765 }
766
767 private function getNumAllowedFailures(): int
768 {
769 if ($this->isScorePartialSolutionEnabled()) {
770 return self::NUM_REQUIRED_ANSWERS - self::PARTIAL_SCORING_NUM_CORRECT_ANSWERS;
771 }
772
773 return 0;
774 }
775
777 {
778 return 'feedback_correct_kprim';
779 }
780
781 public function moveAnswerDown($position): bool
782 {
783 if ($position < 0 || $position >= (self::NUM_REQUIRED_ANSWERS - 1)) {
784 return false;
785 }
786
787 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
788 if ($i == $position) {
789 $movingAnswer = $this->answers[$i];
790 $targetAnswer = $this->answers[ $i + 1 ];
791
792 $movingAnswer->setPosition($position + 1);
793 $targetAnswer->setPosition($position);
794
795 $this->answers[ $i + 1 ] = $movingAnswer;
796 $this->answers[$i] = $targetAnswer;
797 }
798 }
799 return true;
800 }
801
802 public function moveAnswerUp($position): bool
803 {
804 if ($position <= 0 || $position > (self::NUM_REQUIRED_ANSWERS - 1)) {
805 return false;
806 }
807
808 for ($i = 0, $max = count($this->answers); $i < $max; $i++) {
809 if ($i == $position) {
810 $movingAnswer = $this->answers[$i];
811 $targetAnswer = $this->answers[ $i - 1 ];
812
813 $movingAnswer->setPosition($position - 1);
814 $targetAnswer->setPosition($position);
815
816 $this->answers[ $i - 1 ] = $movingAnswer;
817 $this->answers[$i] = $targetAnswer;
818 }
819 }
820
821 return true;
822 }
823
824 public function toLog(AdditionalInformationGenerator $additional_info): array
825 {
826 $result = [
827 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(),
828 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(),
829 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()),
830 AdditionalInformationGenerator::KEY_QUESTION_KPRIM_OPTION_LABEL => $additional_info
831 ->getTagForLangVar($this->getLangVarForOptionLabel($this->getOptionLabel())),
832 AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info
833 ->getTrueFalseTagForBool($this->getShuffle()),
834 AdditionalInformationGenerator::KEY_FEEDBACK => [
835 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
836 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
837 ]
838 ];
839
840 $result[AdditionalInformationGenerator::KEY_QUESTION_KPRIM_SCORE_PARTIAL_SOLUTION_ENABLED] = $additional_info
841 ->getEnabledDisabledTagForBool($this->getNumAllowedFailures() > 0);
842
843 $answers = [];
844 foreach ($this->getAnswers() as $key => $answer) {
845 $answers[$key + 1] = [
846 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION => $this->formatSAQuestion($answer->getAnswertext() ?? ''),
847 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION_CORRECTNESS => $additional_info->getTrueFalseTagForBool((bool) $answer->getCorrectness()),
848 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION_ORDER => (int) $answer->getPosition(),
849 AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTION_IMAGE => (string) $answer->getImageFile(),
850 AdditionalInformationGenerator::KEY_FEEDBACK => $this->formatSAQuestion(
851 $this->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation($this->getId(), 0, $key)
852 )
853 ];
854 }
855
856 $result[AdditionalInformationGenerator::KEY_QUESTION_ANSWER_OPTIONS] = $answers;
857
858 return $result;
859 }
860
861 protected function solutionValuesToLog(
862 AdditionalInformationGenerator $additional_info,
863 array $solution_values
864 ): array {
865 $parsed_solution = [];
866 $true_option_label = $this->getOptionLabel() === self::OPTION_LABEL_CUSTOM
867 ? $this->getCustomTrueOptionLabel()
868 : $this->getTrueOptionLabel($this->getOptionLabel());
869 $false_option_label = $this->getOptionLabel() === self::OPTION_LABEL_CUSTOM
870 ? $this->getCustomFalseOptionLabel()
871 : $this->getFalseOptionLabel($this->getOptionLabel());
872 foreach ($this->getAnswers() as $id => $answer) {
873 $value = $additional_info->getNoneTag();
874 foreach ($solution_values as $solution) {
875 if ($solution['value1'] !== (string) $id) {
876 continue;
877 }
878
879 $value = $false_option_label;
880 if ($solution['value2'] === '1') {
881 $value = $true_option_label;
882 }
883 break;
884 }
885 $parsed_solution[$answer->getAnswertext()] = $value;
886 }
887 return $parsed_solution;
888 }
889
890 public function solutionValuesToText(array $solution_values): array
891 {
892 $parsed_solution = [];
893 $true_option_label = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
894 $false_option_label = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
895 foreach ($this->getAnswers() as $id => $answer) {
896 $value = $this->lng->txt('none');
897 foreach ($solution_values as $solution) {
898 if ($solution['value1'] !== (string) $id) {
899 continue;
900 }
901
902 $value = $false_option_label;
903 if ($solution['value2'] === '1') {
904 $value = $true_option_label;
905 }
906 break;
907 }
908 $parsed_solution[] = "{$answer->getAnswertext()} ({$value})";
909 }
910 return $parsed_solution;
911 }
912
913 public function getCorrectSolutionForTextOutput(int $active_id, int $pass): array
914 {
915 $true_option_label = $this->getTrueOptionLabelTranslation($this->lng, $this->getOptionLabel());
916 $false_option_label = $this->getFalseOptionLabelTranslation($this->lng, $this->getOptionLabel());
917 return array_map(
918 fn(ilAssKprimChoiceAnswer $v): string => $v->getAnswertext()
919 . ' (' . $v->getCorrectness() ? $true_option_label : $false_option_label . ')',
920 $this->getAnswers()
921 );
922 }
923}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$filename
Definition: buildRTE.php:78
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
MUST convert the given solution values into an array or a string that can be stored in the log.
const PARTIAL_SCORING_NUM_CORRECT_ANSWERS
toJSON()
Returns a JSON representation of the question.
isSingleLineAnswerType($answerType)
setCustomTrueOptionLabel($customTrueOptionLabel)
handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
handleFileUploads($answers, $files)
setSpecificFeedbackSetting(int $specific_feedback_setting)
setShuffleAnswersEnabled(bool $shuffle_answers_enabled)
getLangVarForOptionLabel(string $option_label)
setCustomFalseOptionLabel($customFalseOptionLabel)
getFalseOptionLabelTranslation(ilLanguage $lng, string $option_label)
setOptionLabel(string $option_label)
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
addAnswer(ilAssKprimChoiceAnswer $answer)
solutionValuesToText(array $solution_values)
MUST convert the given solution values into text.
removeAnswerImage($position)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
cloneQuestionTypeSpecificProperties(\assQuestion $target)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
getFalseOptionLabel(string $option_label)
getValidOptionLabelsTranslated(ilLanguage $lng)
getTrueOptionLabel(string $option_label)
setAnswerType($answerType)
getInstructionTextTranslation(ilLanguage $lng, $option_label)
loadAnswerData(int $question_id)
getTrueOptionLabelTranslation(ilLanguage $lng, string $option_label)
isCustomOptionLabel($labelValue)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
saveToDb(?int $original_id=null)
isValidAnswerType($answerType)
toLog(AdditionalInformationGenerator $additional_info)
MUST return an array of the question settings that can be stored in the log.
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
getCorrectSolutionForTextOutput(int $active_id, int $pass)
calculateReachedPointsForSolution(?array $found_values, int $active_id=0)
isValidOptionLabel(?string $option_label)
getAnswerTypeSelectOptions(ilLanguage $lng)
const OPTION_LABEL_APPLICABLE_OR_NOT
__construct($title='', $comment='', $author='', $owner=-1, $question='')
setScorePartialSolutionEnabled($scorePartialSolutionEnabled)
setThumbSize(int $thumbSize)
setOriginalId(?int $original_id)
setId(int $id=-1)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
setQuestion(string $question="")
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
setAuthor(string $author="")
setComment(string $comment="")
setObjId(int $obj_id=0)
setLastChange(int $lastChange)
setOwner(int $owner=-1)
setNrOfTries(int $a_nr_of_tries)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
setTitle(string $title="")
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
saveQuestionDataToDb(?int $original_id=null)
getImagePathWeb()
Returns the web image path for web accessable images of a question.
setPoints(float $points)
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
language handling
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
$c
Definition: deliver.php:25
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$res
Definition: ltiservices.php:69
$post
Definition: ltitoken.php:46
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
global $lng
Definition: privfeed.php:31
if(!file_exists('../ilias.ini.php'))