19 declare(strict_types=1);
68 $this->textsize = self::DEFAULT_TEXT_SIZE;
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"]);
170 $this->
setParsedErrorText(json_decode($data[
'parsed_errortext'] ?? json_encode([]),
true));
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'];
276 bool $authorized =
true 278 if (is_null($pass)) {
283 function () use ($active_id, $pass, $authorized) {
286 foreach ($selected as $position) {
305 $this->questionpool_request->string(
'qst_' . $this->getId())
311 return 'assErrorText';
316 return 'qpl_qst_errortext';
321 return 'qpl_a_errortext';
327 $this->errordata = [];
329 $has_too_long_errors =
false;
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;
350 if ($has_too_long_errors) {
351 $this->tpl->setOnScreenMessage(
353 $this->
lng->txt(
'qst_error_text_too_long')
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)
378 foreach ($this->errordata as $error) {
379 $position = $error->getPosition();
381 if (array_key_exists($position, $paragraph)) {
382 $this->parsed_errortext[$key][$position][
'text_correct'] =
383 $error->getTextCorrect();
384 $this->parsed_errortext[$key][$position][
'points'] =
398 $this->errordata = [];
400 foreach ($errors as $error) {
402 $this->errordata[] = $answer;
410 if ($error->getPosition() ===
null) {
411 unset($this->errordata[$index]);
414 $this->errordata = array_values($this->errordata);
423 array $current_error_data,
426 foreach ($current_error_data as $answer_object) {
427 if (strcmp($answer_object->getTextWrong(), $text_wrong) === 0) {
429 $answer_object->getTextCorrect(),
430 $answer_object->getPoints()
439 bool $graphical_output =
false,
440 bool $show_correct_solution =
false,
441 bool $use_link_tags =
true,
442 array $correctness_icons = []
446 $array_reduce_function = fn(?
string $carry,
int $position)
452 $show_correct_solution,
456 $output_array[] =
'<p>' . trim(array_reduce(array_keys($paragraph), $array_reduce_function)) .
'</p>';
459 return implode(
"\n", $output_array);
466 bool $graphical_output,
467 bool $show_correct_solution,
469 array $correctness_icons
489 bool $show_correct_solution
491 $v = $paragraph[$position];
492 if ($show_correct_solution ===
true 493 && ($v[
'error_type'] ===
'in_passage' 494 || $v[
'error_type'] ===
'passage_end')) {
497 if ($show_correct_solution
498 && ($v[
'error_type'] ===
'passage_start' 499 || $v[
'error_type'] ===
'word')) {
500 return $v[
'text_correct'] ??
'';
508 bool $show_correct_solution,
511 if ($show_correct_solution !==
true 512 && in_array($position, $selections[
'user'])) {
513 return 'ilc_qetitem_ErrorTextSelected';
516 if ($show_correct_solution ===
true 517 && in_array($position, $selections[
'best'])) {
518 return 'ilc_qetitem_ErrorTextSelected';
521 return 'ilc_qetitem_ErrorTextItem';
526 bool $graphical_output,
528 array $correctness_icons
530 if ($graphical_output ===
true 531 && (in_array($position, $selections[
'user']) && !in_array($position, $selections[
'best'])
532 || !in_array($position, $selections[
'user']) && in_array($position, $selections[
'best']))) {
533 return $correctness_icons[
'not_correct'];
536 if ($graphical_output ===
true 537 && in_array($position, $selections[
'user']) && in_array($position, $selections[
'best'])) {
538 return $correctness_icons[
'correct'];
546 if (!is_array($selections)) {
551 $array_reduce_function =
function ($carry, $k) use ($paragraph, $selections) {
552 $text = $paragraph[$k][
'text'];
553 if (in_array($k, $selections)) {
554 $text = self::ERROR_WORD_MARKER . $paragraph[$k][
'text'] . self::ERROR_WORD_MARKER;
556 return $carry .
' ' . $text;
558 $output_array[] = trim(array_reduce(array_keys($paragraph), $array_reduce_function));
560 return implode(
"\n", $output_array);
567 foreach ($positions_array as $position => $position_data) {
569 || $with_positive_points_only && $position_data[
'points'] <= 0) {
573 $selections[] = $position;
574 if ($position_data[
'length'] > 1) {
575 for ($i = 1;$i < $position_data[
'length'];$i++) {
576 $selections[] = $position + $i;
593 foreach ($correct_positions as $correct_position => $correct_position_data) {
594 $selected_word_key = array_search($correct_position, $selected_word_positions);
595 if ($selected_word_key ===
false) {
599 if ($correct_position_data[
'length'] === 1) {
600 $points += $correct_position_data[
'points'];
601 unset($selected_word_positions[$selected_word_key]);
605 $passage_complete =
true;
606 for ($i = 1;$i < $correct_position_data[
'length'];$i++) {
607 $selected_passage_element_key = array_search($correct_position + $i, $selected_word_positions);
608 if ($selected_passage_element_key ===
false) {
609 $passage_complete =
false;
612 unset($selected_word_positions[$selected_passage_element_key]);
615 if ($passage_complete) {
616 $points += $correct_position_data[
'points'];
617 unset($selected_word_positions[$selected_word_key]);
621 foreach ($selected_word_positions as $word_position) {
622 if (!array_key_exists($word_position, $correct_positions)) {
633 $this->errordata = [];
651 $correct_answers = [];
652 foreach ($this->
getErrorData() as $index => $answer_obj) {
653 $correct_answers[] = [
654 'answertext_wrong' => $answer_obj->getTextWrong(),
655 'answertext_correct' => $answer_obj->getTextCorrect(),
656 'points' => $answer_obj->getPoints(),
657 'length' => $answer_obj->getLength(),
658 'pos' => $this->
getId() .
'_' . $answer_obj->getPosition()
661 return $correct_answers;
666 return $this->errortext ??
'';
671 $this->errortext = $text ??
'';
682 foreach ($this->parsed_errortext as $paragraph) {
683 foreach ($paragraph as $position => $word) {
685 'answertext' => $word[
'text'],
686 'order' => $this->
getId() .
'_' . $position
690 'answertext' =>
'###' 711 if ($a_value ===
null) {
714 $this->textsize = $a_value;
724 $this->points_wrong = $a_value;
730 $result[
'id'] = $this->
getId();
737 $result[
'feedback'] = [
738 'onenotcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
739 'allcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
746 $result[
'mobs'] = $mobs;
748 return json_encode($result);
753 return ilOperatorsExpressionMapping::getOperatorsByExpression($expression);
772 $data = $this->db->queryF(
773 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = ( 774 SELECT MAX(step) FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s 776 [
"integer",
"integer",
"integer",
"integer",
"integer",
"integer"],
777 [$active_id, $pass, $this->
getId(), $active_id, $pass, $this->
getId()]
780 while ($row = $this->db->fetchAssoc(
$data)) {
781 $result->addKeyValue($row[
"value1"], $row[
"value1"]);
787 $result->setReachedPercentage((
$points / $max_points) * 100);
794 $text_by_paragraphs = preg_split(self::PARAGRAPH_SPLIT_REGEXP, $this->
getErrorText());
797 foreach ($text_by_paragraphs as $paragraph) {
799 preg_split(self::WORD_SPLIT_REGEXP, trim($paragraph)),
802 $offset += count(end($text_array));
814 $paragraph_with_error_info = [];
815 $passage_start =
null;
816 foreach ($paragraph as $position => $word) {
817 $actual_position = $position + $offset;
818 if ($passage_start !==
null 819 && (mb_strrpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'end']) === mb_strlen($word) - 2
820 || mb_strrpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'end']) === mb_strlen($word) - 3
821 && preg_match(self::FIND_PUNCTUATION_REGEXP, mb_substr($word, -1)) === 1)) {
824 $paragraph_with_error_info[$passage_start][
'text_wrong'] .=
826 $paragraph_with_error_info[$actual_position] = [
827 'text' => $actual_word,
828 'error_type' =>
'passage_end' 830 $passage_start =
null;
833 if ($passage_start !==
null) {
834 $paragraph_with_error_info[$passage_start][
'text_wrong'] .=
' ' . $word;
835 $paragraph_with_error_info[$actual_position] = [
837 'error_type' =>
'in_passage' 841 if (mb_strpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'start']) === 0) {
842 $paragraph_with_error_info[$actual_position] = [
843 'text' => substr($word, 2),
844 'text_wrong' => substr($word, 2),
845 'error_type' =>
'passage_start',
846 'error_position' => $actual_position,
848 $passage_start = $actual_position;
851 if (mb_strpos($word, self::ERROR_WORD_MARKER) === 0) {
852 $paragraph_with_error_info[$actual_position] = [
853 'text' => substr($word, 1),
854 'text_wrong' => substr($word, 1),
855 'error_type' =>
'word',
856 'error_position' => $actual_position,
861 $paragraph_with_error_info[$actual_position] = [
863 'error_type' =>
'none',
868 return $paragraph_with_error_info;
873 if (mb_substr($word, -2) === self::ERROR_PARAGRAPH_DELIMITERS[
'end']) {
874 return mb_substr($word, 0, -2);
876 return mb_substr($word, 0, -3) . mb_substr($word, -1);
888 $error_text_array = array_reduce(
889 $this->parsed_errortext,
890 fn(
$c, $v) =>
$c + $v
893 if ($index ===
null) {
894 return $error_text_array;
897 if (array_key_exists($index, $error_text_array)) {
898 return $error_text_array[$index];
906 $array_by_position = [];
907 foreach ($this->errordata as $error) {
908 $array_by_position[$error->getPosition()] = [
909 'length' => $error->getLength(),
910 'points' => $error->getPoints(),
911 'text' => $error->getTextWrong(),
912 'text_correct' => $error->getTextCorrect()
915 ksort($array_by_position);
916 return $array_by_position;
940 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->
getQuestionType(),
944 AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info
946 AdditionalInformationGenerator::KEY_FEEDBACK => [
947 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
948 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
953 $result[AdditionalInformationGenerator::KEY_QUESTION_CORRECT_ANSWER_OPTIONS] = array_reduce(
954 array_keys($error_data),
955 static function (array
$c,
int $k) use ($error_data): array {
957 'text_wrong' => $error_data[$k]->getTextWrong(),
958 'text_correct' => $error_data[$k]->getTextCorrect(),
959 'points' => $error_data[$k]->getPoints()
971 array $solution_values
975 static fn(array $v):
string => $v[
'value1'],
985 static fn(array $v):
string => $v[
'value1'],
getPointsForSelectedPositions(array $selected_word_positions)
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...
const FIND_PUNCTUATION_REGEXP
setNrOfTries(int $a_nr_of_tries)
static getInstance($identifier)
const PercentageResultExpression
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
const NumberOfResultExpression
const ExclusiveResultExpression
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
loadFromDb($question_id)
Loads the object from the database.
addErrorInformationToTextParagraphArray(array $paragraph, int $offset)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
ensureNonNegativePoints(float $points)
getCorrectSolutionForTextOutput(int $active_id, int $pass)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getClassForPosition(int $position, bool $show_correct_solution, array $selections)
getTextForPosition(int $position, array $paragraph, bool $show_correct_solution)
getErrorDataAsArrayForJS()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getParticipantsSolution()
toLog(AdditionalInformationGenerator $additional_info)
setComment(string $comment="")
const ERROR_PARAGRAPH_DELIMITERS
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $preview_session)
getCorrectnessIconForPosition(int $position, bool $graphical_output, array $selections, array $correctness_icons)
setParticipantsSolution($participantSolution)
generateArrayByPositionFromErrorData()
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
removeErrorDataWithoutPosition()
setErrorData(array $errors)
getParsedErrorTextForJS()
__construct(string $title='', string $comment='', string $author='', int $owner=-1, string $question='')
createErrorTextExport(array $selections)
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
parsePassageEndWord(string $word)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
assembleErrorTextOutput(array $selections, bool $graphical_output=false, bool $show_correct_solution=false, bool $use_link_tags=true, array $correctness_icons=[])
const PARAGRAPH_SPLIT_REGEXP
Class for error text questions.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
getExpressionTypes()
Get all available expression types for a specific question.
setErrorText(?string $text)
addPositionToErrorAnswer(assAnswerErrorText $error)
savePreviewData(ilAssQuestionPreviewSession $previewSession)
saveQuestionDataToDb(?int $original_id=null)
getBestSelection(bool $with_positive_points_only=true)
generateOutputStringFromPosition(int $position, array $selections, array $paragraph, bool $graphical_output, bool $show_correct_solution, bool $use_link_tags, array $correctness_icons)
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
getSolutionMaxPass(int $active_id)
getOperators(string $expression)
Get all available operations for a specific question.
correctDataAfterParserUpdate()
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
__construct(Container $dic, ilPlugin $plugin)
setOriginalId(?int $original_id)
solutionValuesToText(array $solution_values)
setTitle(string $title="")
setErrorsFromParsedErrorText()
saveToDb(?int $original_id=null)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
saveAdditionalQuestionDataToDb()
Saves the data for the additional data table.
completeParsedErrorTextFromErrorData()
withPosition(int $position)
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
setAuthor(string $author="")
setParsedErrorText(array $parsed_errortext)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
getAdditionalInformationFromExistingErrorDataByErrorText(array $current_error_data, string $text_wrong)
static getDraftInstance()
getErrorTokenHtml($item, $class, $useLinkTags)
getUserQuestionResult(int $active_id, int $pass)
Get the user solution for a question by active_id and the test pass.
setQuestion(string $question="")
const EmptyAnswerExpression