19declare(strict_types=1);
73 if (mb_strlen($this->title)
93 $this->db->manipulateF(
94 "DELETE FROM qpl_a_errortext WHERE question_fi = %s",
100 foreach ($this->errordata as $error) {
101 $next_id = $this->db->nextId(
'qpl_a_errortext');
102 $this->db->manipulateF(
103 "INSERT INTO qpl_a_errortext (answer_id, question_fi, text_wrong, text_correct, points, sequence, position) VALUES (%s, %s, %s, %s, %s, %s, %s)",
104 [
'integer',
'integer',
'text',
'text',
'float',
'integer',
'integer'],
108 $error->getTextWrong(),
109 $error->getTextCorrect(),
112 $error->getPosition()
125 $this->db->manipulateF(
131 $this->db->manipulateF(
132 "INSERT INTO " . $this->
getAdditionalTableName() .
" (question_fi, errortext, parsed_errortext, textsize, points_wrong) VALUES (%s, %s, %s, %s, %s)",
133 [
"integer",
"text",
"text",
"float",
"float"],
152 $db_question = $this->db->queryF(
157 if ($db_question->numRows() === 1) {
158 $data = $this->db->fetchAssoc($db_question);
159 $this->
setId($question_id);
162 $this->
setComment((
string) $data[
"description"]);
186 $db_error_text = $this->db->queryF(
187 "SELECT * FROM qpl_a_errortext WHERE question_fi = %s ORDER BY sequence ASC",
192 if ($db_error_text->numRows() > 0) {
193 while (
$data = $this->db->fetchAssoc($db_error_text)) {
195 (
string)
$data[
'text_wrong'],
196 (
string)
$data[
'text_correct'],
197 (
float)
$data[
'points'],
205 parent::loadFromDb($question_id);
213 $needs_finalizing =
false;
215 $needs_finalizing =
true;
219 if (isset($this->errordata[0])
220 && $this->errordata[0]->getPosition() ===
null) {
221 foreach ($this->errordata as $key => $error) {
227 if ($needs_finalizing) {
241 foreach ($this->errordata as $error) {
242 if ($error->getPoints() > 0) {
243 $maxpoints += $error->getPoints();
252 bool $authorized_solution =
true
254 if ($pass === null) {
260 while ($row = $this->db->fetchAssoc($result)) {
261 $positions[] = $row[
'value1'];
270 return $this->ensureNonNegativePoints($reached_points);
276 bool $authorized =
true
278 if (is_null($pass)) {
282 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(
283 function () use ($active_id, $pass, $authorized) {
284 $selected = $this->getAnswersFromRequest();
285 $this->removeCurrentSolution($active_id, $pass, $authorized);
286 foreach ($selected as $position) {
287 $this->saveCurrentSolution($active_id, $pass, $position,
null, $authorized);
297 $selection = $this->getAnswersFromRequest();
305 $this->questionpool_request->string(
'qst_' . $this->getId())
311 return 'assErrorText';
316 return 'qpl_qst_errortext';
321 return 'qpl_a_errortext';
326 $current_error_data = $this->getErrorData();
327 $this->errordata = [];
329 $has_too_long_errors =
false;
330 foreach ($this->getParsedErrorText() as $paragraph) {
331 foreach ($paragraph as $position => $word) {
332 if ($word[
'error_type'] ===
'in_passage'
333 || $word[
'error_type'] ===
'passage_end'
334 || $word[
'error_type'] ===
'none') {
338 $text_wrong = $word[
'text_wrong'];
339 if (mb_strlen($text_wrong) > self::ERROR_MAX_LENGTH) {
340 $has_too_long_errors =
true;
344 list($text_correct, $points) =
345 $this->getAdditionalInformationFromExistingErrorDataByErrorText($current_error_data, $text_wrong);
346 $this->errordata[] =
new assAnswerErrorText($text_wrong, $text_correct, $points, $position);
350 if ($has_too_long_errors) {
351 $this->tpl->setOnScreenMessage(
353 $this->
lng->txt(
'qst_error_text_too_long')
360 foreach ($this->getParsedErrorText() as $paragraph) {
361 foreach ($paragraph as $position => $word) {
362 if (isset($word[
'text_wrong'])
364 || mb_substr($word[
'text_wrong'], 0, -1) === $error->
getTextWrong()
365 && preg_match(self::FIND_PUNCTUATION_REGEXP, mb_substr($word[
'text_wrong'], -1)) === 1)
366 && !array_key_exists($position, $this->generateArrayByPositionFromErrorData())
378 foreach ($this->errordata as $error) {
379 $position = $error->getPosition();
380 if ($position ===
null) {
384 foreach ($this->getParsedErrorText() as $key => $paragraph) {
385 if (array_key_exists($position, $paragraph)) {
386 $this->parsed_errortext[$key][$position][
'text_correct'] =
387 $error->getTextCorrect();
388 $this->parsed_errortext[$key][$position][
'points'] =
402 $this->errordata = [];
404 foreach ($errors as $error) {
405 $answer = $this->addPositionToErrorAnswer($error);
406 $this->errordata[] = $answer;
408 $this->completeParsedErrorTextFromErrorData();
413 foreach ($this->getErrorData() as $index => $error) {
414 if ($error->getPosition() ===
null) {
415 unset($this->errordata[$index]);
418 $this->errordata = array_values($this->errordata);
427 array $current_error_data,
430 foreach ($current_error_data as $answer_object) {
431 if (strcmp($answer_object->getTextWrong(), $text_wrong) === 0) {
433 $answer_object->getTextCorrect(),
434 $answer_object->getPoints()
443 bool $graphical_output =
false,
444 bool $show_correct_solution =
false,
445 bool $use_link_tags =
true,
446 array $correctness_icons = []
449 foreach ($this->getParsedErrorText() as $paragraph) {
450 $array_reduce_function = fn(?
string $carry,
int $position)
451 => $carry . $this->generateOutputStringFromPosition(
456 $show_correct_solution,
460 $output_array[] =
'<p>' . trim(array_reduce(array_keys($paragraph), $array_reduce_function)) .
'</p>';
463 return implode(
"\n", $output_array);
470 bool $graphical_output,
471 bool $show_correct_solution,
473 array $correctness_icons
475 $text = $this->getTextForPosition($position, $paragraph, $show_correct_solution);
479 $class = $this->getClassForPosition($position, $show_correct_solution, $selections);
480 $img = $this->getCorrectnessIconForPosition(
487 return ' ' . $this->getErrorTokenHtml(
$text, $class, $use_link_tags) . $img;
493 bool $show_correct_solution
495 $v = $paragraph[$position];
496 if ($show_correct_solution ===
true
497 && ($v[
'error_type'] ===
'in_passage'
498 || $v[
'error_type'] ===
'passage_end')) {
501 if ($show_correct_solution
502 && ($v[
'error_type'] ===
'passage_start'
503 || $v[
'error_type'] ===
'word')) {
504 return $v[
'text_correct'] ??
'';
512 bool $show_correct_solution,
515 if ($show_correct_solution !==
true
516 && in_array($position, $selections[
'user'])) {
517 return 'ilc_qetitem_ErrorTextSelected';
520 if ($show_correct_solution ===
true
521 && in_array($position, $selections[
'best'])) {
522 return 'ilc_qetitem_ErrorTextSelected';
525 return 'ilc_qetitem_ErrorTextItem';
530 bool $graphical_output,
532 array $correctness_icons
534 if ($graphical_output ===
true
535 && (in_array($position, $selections[
'user']) && !in_array($position, $selections[
'best'])
536 || !in_array($position, $selections[
'user']) && in_array($position, $selections[
'best']))) {
537 return $correctness_icons[
'not_correct'];
540 if ($graphical_output ===
true
541 && in_array($position, $selections[
'user']) && in_array($position, $selections[
'best'])) {
542 return $correctness_icons[
'correct'];
550 if (!is_array($selections)) {
554 foreach ($this->getParsedErrorText() as $paragraph) {
555 $array_reduce_function =
function ($carry, $k) use ($paragraph, $selections) {
556 $text = $paragraph[$k][
'text'];
557 if (in_array($k, $selections)) {
558 $text = self::ERROR_WORD_MARKER . $paragraph[$k][
'text'] . self::ERROR_WORD_MARKER;
560 return $carry .
' ' .
$text;
562 $output_array[] = trim(array_reduce(array_keys($paragraph), $array_reduce_function));
564 return implode(
"\n", $output_array);
569 $positions_array = $this->generateArrayByPositionFromErrorData();
571 foreach ($positions_array as $position => $position_data) {
573 || $with_positive_points_only && $position_data[
'points'] <= 0) {
577 $selections[] = $position;
578 if ($position_data[
'length'] > 1) {
579 for ($i = 1;$i < $position_data[
'length'];$i++) {
580 $selections[] = $position + $i;
595 $correct_positions = $this->generateArrayByPositionFromErrorData();
597 foreach ($correct_positions as $correct_position => $correct_position_data) {
598 $selected_word_key = array_search($correct_position, $selected_word_positions);
599 if ($selected_word_key ===
false) {
603 if ($correct_position_data[
'length'] === 1) {
604 $points += $correct_position_data[
'points'];
605 unset($selected_word_positions[$selected_word_key]);
609 $passage_complete =
true;
610 for ($i = 1;$i < $correct_position_data[
'length'];$i++) {
611 $selected_passage_element_key = array_search($correct_position + $i, $selected_word_positions);
612 if ($selected_passage_element_key ===
false) {
613 $passage_complete =
false;
616 unset($selected_word_positions[$selected_passage_element_key]);
619 if ($passage_complete) {
620 $points += $correct_position_data[
'points'];
621 unset($selected_word_positions[$selected_word_key]);
625 foreach ($selected_word_positions as $word_position) {
626 if (!array_key_exists($word_position, $correct_positions)) {
627 $points += $this->getPointsWrong();
637 $this->errordata = [];
646 return $this->errordata;
655 $correct_answers = [];
656 foreach ($this->getErrorData() as $index => $answer_obj) {
657 $correct_answers[] = [
658 'answertext_wrong' => $answer_obj->getTextWrong(),
659 'answertext_correct' => $answer_obj->getTextCorrect(),
660 'points' => $answer_obj->getPoints(),
661 'length' => $answer_obj->getLength(),
662 'pos' => $this->
getId() .
'_' . $answer_obj->getPosition()
665 return $correct_answers;
670 return $this->errortext ??
'';
675 $this->errortext =
$text ??
'';
680 return $this->parsed_errortext;
686 foreach ($this->parsed_errortext as $paragraph) {
687 foreach ($paragraph as $position => $word) {
689 'answertext' => $word[
'text'],
690 'order' => $this->
getId() .
'_' . $position
694 'answertext' =>
'###'
704 $this->parsed_errortext = $parsed_errortext;
709 return $this->textsize;
715 if ($a_value ===
null) {
718 $this->textsize = $a_value;
723 return $this->points_wrong;
728 $this->points_wrong = $a_value;
734 $result[
'id'] = $this->
getId();
735 $result[
'type'] = (string) $this->getQuestionType();
736 $result[
'title'] = $this->getTitleForHTMLOutput();
737 $result[
'question'] = $this->formatSAQuestion($this->getQuestion());
739 $result[
'nr_of_tries'] = $this->getNrOfTries();
740 $result[
'shuffle'] = $this->getShuffle();
741 $result[
'feedback'] = [
742 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
743 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
746 $result[
'correct_answers'] = $this->getErrorDataAsArrayForJS();
747 $result[
'answers'] = $this->getParsedErrorTextForJS();
750 $result[
'mobs'] = $mobs;
752 return json_encode($result);
776 $data = $this->db->queryF(
777 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = (
778 SELECT MAX(step) FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s
780 [
"integer",
"integer",
"integer",
"integer",
"integer",
"integer"],
781 [$active_id, $pass, $this->
getId(), $active_id, $pass, $this->
getId()]
784 while ($row = $this->db->fetchAssoc(
$data)) {
785 $result->addKeyValue($row[
"value1"], $row[
"value1"]);
788 $points = $this->calculateReachedPoints($active_id, $pass);
789 $max_points = $this->getMaximumPoints();
791 $result->setReachedPercentage(($points / $max_points) * 100);
798 $text_by_paragraphs = preg_split(self::PARAGRAPH_SPLIT_REGEXP, $this->getErrorText());
801 foreach ($text_by_paragraphs as $paragraph) {
802 $text_array[] = $this->addErrorInformationToTextParagraphArray(
803 preg_split(self::WORD_SPLIT_REGEXP, trim($paragraph)),
806 $offset += count(end($text_array));
808 $this->setParsedErrorText($text_array);
818 $paragraph_with_error_info = [];
819 $passage_start =
null;
820 foreach ($paragraph as $position => $word) {
821 $actual_position = $position + $offset;
822 if ($passage_start !==
null
823 && (mb_strrpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'end']) === mb_strlen($word) - 2
824 || mb_strrpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'end']) === mb_strlen($word) - 3
825 && preg_match(self::FIND_PUNCTUATION_REGEXP, mb_substr($word, -1)) === 1)) {
826 $actual_word = $this->parsePassageEndWord($word);
828 $paragraph_with_error_info[$passage_start][
'text_wrong'] .=
830 $paragraph_with_error_info[$actual_position] = [
831 'text' => $actual_word,
832 'error_type' =>
'passage_end'
834 $passage_start =
null;
837 if ($passage_start !==
null) {
838 $paragraph_with_error_info[$passage_start][
'text_wrong'] .=
' ' . $word;
839 $paragraph_with_error_info[$actual_position] = [
841 'error_type' =>
'in_passage'
845 if (mb_strpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'start']) === 0) {
846 $paragraph_with_error_info[$actual_position] = [
847 'text' => substr($word, 2),
848 'text_wrong' => substr($word, 2),
849 'error_type' =>
'passage_start',
850 'error_position' => $actual_position,
852 $passage_start = $actual_position;
855 if (mb_strpos($word, self::ERROR_WORD_MARKER) === 0) {
856 $paragraph_with_error_info[$actual_position] = [
857 'text' => substr($word, 1),
858 'text_wrong' => substr($word, 1),
859 'error_type' =>
'word',
860 'error_position' => $actual_position,
865 $paragraph_with_error_info[$actual_position] = [
867 'error_type' =>
'none',
868 'points' => $this->getPointsWrong()
872 return $paragraph_with_error_info;
877 if (mb_substr($word, -2) === self::ERROR_PARAGRAPH_DELIMITERS[
'end']) {
878 return mb_substr($word, 0, -2);
880 return mb_substr($word, 0, -3) . mb_substr($word, -1);
892 $error_text_array = array_reduce(
893 $this->parsed_errortext,
894 fn(
$c, $v) =>
$c + $v
897 if ($index ===
null) {
898 return $error_text_array;
901 if (array_key_exists($index, $error_text_array)) {
902 return $error_text_array[$index];
910 $array_by_position = [];
911 foreach ($this->errordata as $error) {
912 $position = $error->getPosition();
913 if ($position ===
null) {
916 $array_by_position[$position] = [
917 'length' => $error->getLength(),
918 'points' => $error->getPoints(),
919 'text' => $error->getTextWrong(),
920 'text_correct' => $error->getTextCorrect()
923 ksort($array_by_position);
924 return $array_by_position;
948 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(),
949 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(),
950 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()),
952 AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info
954 AdditionalInformationGenerator::KEY_FEEDBACK => [
955 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
956 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
960 $error_data = $this->getErrorData();
961 $result[AdditionalInformationGenerator::KEY_QUESTION_CORRECT_ANSWER_OPTIONS] = array_reduce(
962 array_keys($error_data),
963 static function (array
$c,
int $k) use ($error_data): array {
965 'text_wrong' => $error_data[$k]->getTextWrong(),
966 'text_correct' => $error_data[$k]->getTextCorrect(),
967 'points' => $error_data[$k]->getPoints()
979 array $solution_values
981 return $this->createErrorTextExport(
983 static fn(array $v): string => $v[
'value1'],
991 return $this->createErrorTextExport(
993 static fn(array $v):
string => $v[
'value1'],
1001 return $this->createErrorTextExport($this->getBestSelection());
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
withPosition(int $position)
Class for error text questions.
getErrorDataAsArrayForJS()
setParsedErrorText(array $parsed_errortext)
addErrorInformationToTextParagraphArray(array $paragraph, int $offset)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
getClassForPosition(int $position, bool $show_correct_solution, array $selections)
loadFromDb($question_id)
Loads the object from the database.
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.
getOperators(string $expression)
Get all available operations for a specific question.
removeErrorDataWithoutPosition()
solutionValuesToText(array $solution_values)
MUST convert the given solution values into text.
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
getCorrectSolutionForTextOutput(int $active_id, int $pass)
setErrorData(array $errors)
getParsedErrorTextForJS()
saveToDb(?int $original_id=null)
savePreviewData(ilAssQuestionPreviewSession $previewSession)
completeParsedErrorTextFromErrorData()
getAdditionalInformationFromExistingErrorDataByErrorText(array $current_error_data, string $text_wrong)
const PARAGRAPH_SPLIT_REGEXP
const ERROR_PARAGRAPH_DELIMITERS
assembleErrorTextOutput(array $selections, bool $graphical_output=false, bool $show_correct_solution=false, bool $use_link_tags=true, array $correctness_icons=[])
createErrorTextExport(array $selections)
addPositionToErrorAnswer(assAnswerErrorText $error)
parsePassageEndWord(string $word)
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
toLog(AdditionalInformationGenerator $additional_info)
MUST return an array of the question settings that can be stored in the log.
__construct(string $title='', string $comment='', string $author='', int $owner=-1, string $question='')
getTextForPosition(int $position, array $paragraph, bool $show_correct_solution)
getErrorTokenHtml($item, $class, $useLinkTags)
correctDataAfterParserUpdate()
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
generateOutputStringFromPosition(int $position, array $selections, array $paragraph, bool $graphical_output, bool $show_correct_solution, bool $use_link_tags, array $correctness_icons)
setErrorText(?string $text)
const FIND_PUNCTUATION_REGEXP
getCorrectnessIconForPosition(int $position, bool $graphical_output, array $selections, array $correctness_icons)
getPointsForSelectedPositions(array $selected_word_positions)
getBestSelection(bool $with_positive_points_only=true)
saveAdditionalQuestionDataToDb()
Saves the data for the additional data table.
setErrorsFromParsedErrorText()
generateArrayByPositionFromErrorData()
getExpressionTypes()
Get all available expression types for a specific question.
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $preview_session)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
getUserQuestionResult(int $active_id, int $pass)
Get the user solution for a question by active_id and the test pass.
setOriginalId(?int $original_id)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
setQuestion(string $question="")
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
setAuthor(string $author="")
setComment(string $comment="")
getSolutionMaxPass(int $active_id)
setNrOfTries(int $a_nr_of_tries)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
setTitle(string $title="")
saveQuestionDataToDb(?int $original_id=null)
static getDraftInstance()
static getInstance($identifier)
getParticipantsSolution()
setParticipantsSolution($participantSolution)
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static getOperatorsByExpression(string $expression)
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...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
return['delivery_method'=> 'php',]
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...
const PercentageResultExpression
const EmptyAnswerExpression
const ExclusiveResultExpression
const NumberOfResultExpression
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...
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
if(!file_exists('../ilias.ini.php'))