ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.assKprimChoice.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
25 
33 {
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;
53  private string $answerType = self::ANSWER_TYPE_SINGLE_LINE;
54  private int $thumbSize = self::DEFAULT_THUMB_SIZE;
55  private bool $scorePartialSolutionEnabled = true;
56  private string $option_label = self::OPTION_LABEL_RIGHT_WRONG;
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 
89  public function setShuffleAnswersEnabled(bool $shuffle_answers_enabled): void
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 
119  public function setScorePartialSolutionEnabled($scorePartialSolutionEnabled): void
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 
139  public function setCustomTrueOptionLabel($customTrueOptionLabel): void
140  {
141  $this->customTrueOptionLabel = $customTrueOptionLabel;
142  }
143 
144  public function getCustomTrueOptionLabel()
145  {
147  }
148 
149  public function setCustomFalseOptionLabel($customFalseOptionLabel): void
150  {
151  $this->customFalseOptionLabel = $customFalseOptionLabel;
152  }
153 
154  public function getCustomFalseOptionLabel()
155  {
157  }
158 
159  public function setSpecificFeedbackSetting(int $specific_feedback_setting): void
160  {
161  $this->specific_feedback_setting = $specific_feedback_setting;
162  }
163 
164  public function getSpecificFeedbackSetting(): int
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']);
268  } catch (ilTestQuestionPoolException $e) {
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 
332  public function saveAnswerSpecificDataToDb()
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
832  AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info
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
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 }
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...
getValidOptionLabelsTranslated(ilLanguage $lng)
ilLanguage $lng
setNrOfTries(int $a_nr_of_tries)
$res
Definition: ltiservices.php:66
isValidAnswerType($answerType)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
setScorePartialSolutionEnabled($scorePartialSolutionEnabled)
isCustomOptionLabel($labelValue)
loadAnswerData(int $question_id)
__construct($title='', $comment='', $author='', $owner=-1, $question='')
isValidOptionLabel(?string $option_label)
setOwner(int $owner=-1)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getImagePathWeb()
Returns the web image path for web accessable images of a question.
const PARTIAL_SCORING_NUM_CORRECT_ANSWERS
removeAnswerImage($position)
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setOptionLabel(string $option_label)
$c
Definition: deliver.php:25
setCustomTrueOptionLabel($customTrueOptionLabel)
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
setComment(string $comment="")
addAnswer(ilAssKprimChoiceAnswer $answer)
toLog(AdditionalInformationGenerator $additional_info)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
getImagePath($question_id=null, $object_id=null)
Returns the image path for web accessable images of a question.
buildHashedImageFilename(string $plain_image_filename, bool $unique=false)
isSingleLineAnswerType($answerType)
getAnswerTypeSelectOptions(ilLanguage $lng)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
getTrueOptionLabel(string $option_label)
cloneQuestionTypeSpecificProperties(\assQuestion $target)
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
setLastChange(int $lastChange)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const OPTION_LABEL_APPLICABLE_OR_NOT
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
getTrueOptionLabelTranslation(ilLanguage $lng, string $option_label)
handleFileUploads($answers, $files)
getFalseOptionLabel(string $option_label)
setSpecificFeedbackSetting(int $specific_feedback_setting)
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
setPoints(float $points)
setObjId(int $obj_id=0)
saveQuestionDataToDb(?int $original_id=null)
static _getMobsOfObject(string $a_type, int $a_id, int $a_usage_hist_nr=0, string $a_lang="-")
getInstructionTextTranslation(ilLanguage $lng, $option_label)
calculateReachedPointsForSolution(?array $found_values, int $active_id=0)
setShuffleAnswersEnabled(bool $shuffle_answers_enabled)
$filename
Definition: buildRTE.php:78
setCustomFalseOptionLabel($customFalseOptionLabel)
getSolutionMaxPass(int $active_id)
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
saveToDb(?int $original_id=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setId(int $id=-1)
__construct(Container $dic, ilPlugin $plugin)
setOriginalId(?int $original_id)
setTitle(string $title="")
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
setThumbSize(int $thumbSize)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
handleFileUpload(ilAssKprimChoiceAnswer $answer, $fileData)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
solutionValuesToText(array $solution_values)
getFalseOptionLabelTranslation(ilLanguage $lng, string $option_label)
setAuthor(string $author="")
getCorrectSolutionForTextOutput(int $active_id, int $pass)
$post
Definition: ltitoken.php:46
getLangVarForOptionLabel(string $option_label)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
setAnswerType($answerType)
toJSON()
Returns a JSON representation of the question.
setQuestion(string $question="")