19 require_once
'./Modules/Test/classes/inc.AssessmentConstants.php';
73 $this->textsize = self::DEFAULT_TEXT_SIZE;
83 if (mb_strlen($this->title)
112 $this->db->manipulateF(
113 "DELETE FROM qpl_a_errortext WHERE question_fi = %s",
119 foreach ($this->errordata as $error) {
120 $next_id = $this->db->nextId(
'qpl_a_errortext');
121 $this->db->manipulateF(
122 "INSERT INTO qpl_a_errortext (answer_id, question_fi, text_wrong, text_correct, points, sequence, position) VALUES (%s, %s, %s, %s, %s, %s, %s)",
123 [
'integer',
'integer',
'text',
'text',
'float',
'integer',
'integer'],
127 $error->getTextWrong(),
128 $error->getTextCorrect(),
131 $error->getPosition()
144 $this->db->manipulateF(
150 $this->db->manipulateF(
151 "INSERT INTO " . $this->
getAdditionalTableName() .
" (question_fi, errortext, parsed_errortext, textsize, points_wrong) VALUES (%s, %s, %s, %s, %s)",
152 [
"integer",
"text",
"text",
"float",
"float"],
171 $db_question = $this->db->queryF(
176 if ($db_question->numRows() === 1) {
177 $data = $this->db->fetchAssoc($db_question);
178 $this->
setId($question_id);
181 $this->
setComment((
string) $data[
"description"]);
205 $db_error_text = $this->db->queryF(
206 "SELECT * FROM qpl_a_errortext WHERE question_fi = %s ORDER BY sequence ASC",
210 if ($db_error_text->numRows() > 0) {
211 while (
$data = $this->db->fetchAssoc($db_error_text)) {
213 (
string)
$data[
'text_wrong'],
214 (
string) $data[
'text_correct'],
215 (
float) $data[
'points'],
223 parent::loadFromDb($question_id);
231 $needs_finalizing =
false;
233 $needs_finalizing =
true;
237 if (isset($this->errordata[0])
238 && $this->errordata[0]->getPosition() === null) {
239 foreach ($this->errordata as
$key => $error) {
245 if ($needs_finalizing) {
256 if ($this->
id <= 0) {
261 $this_id = $this->
getId();
268 if ((
int) $testObjId > 0) {
269 $clone->setObjId($testObjId);
289 $clone->copyPageOfQuestion($this_id);
291 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
293 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
302 if ($this->
getId() <= 0) {
303 throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
307 $thisId = $this->
getId();
313 $clone->setObjId($target_questionpool_id);
324 $clone->onCopy($thisObjId, $thisId, $clone->getObjId(), $clone->getId());
331 if ($this->
getId() <= 0) {
332 throw new RuntimeException(
'The question has not been saved. It cannot be duplicated');
336 $sourceParentId = $this->
getObjId();
342 $clone->setObjId($targetParentId);
344 if ($targetQuestionTitle) {
345 $clone->setTitle($targetQuestionTitle);
350 $clone->copyPageOfQuestion($sourceQuestionId);
352 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
354 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
367 foreach ($this->errordata as $error) {
368 if ($error->getPoints() > 0) {
369 $maxpoints += $error->getPoints();
385 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution =
true, $returndetails =
false): float
387 if ($returndetails) {
388 throw new ilTestException(
'return details not implemented for ' . __METHOD__);
393 if (is_null($pass)) {
398 while ($row = $this->db->fetchAssoc($result)) {
399 $positions[] = $row[
'value1'];
422 if (is_null($pass)) {
428 function () use ($selected, $active_id, $pass, $authorized) {
431 foreach ($selected as $position) {
450 private function logUserAction(
bool $user_entered_values,
int $active_id): void
452 $log_text = $this->
lng->txtlng(
454 $user_entered_values ?
'log_user_entered_values' :
'log_user_not_entered_values',
462 if (mb_strlen($_POST[
"qst_" . $this->
getId()])) {
463 return explode(
',', $_POST[
"qst_{$this->getId()}"]);
471 return 'assErrorText';
476 return 'qpl_qst_errortext';
481 return 'qpl_a_errortext';
489 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
494 if (is_array($solutions)) {
495 foreach ($solutions as $solution) {
496 $selections[] = $solution[
'value1'];
501 $worksheet->
setCell($startrow +
$i, 2, $errortext);
504 return $startrow +
$i + 1;
507 public function fromXML($item,
int $questionpool_id, ?
int $tst_id, &$tst_object,
int &$question_counter, array $import_mapping, array &$solutionhints = []): array
510 return $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
513 public function toXML($a_include_header =
true, $a_include_binary =
true, $a_shuffle =
false, $test_output =
false, $force_image_references =
false): string
516 return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
522 $this->errordata = [];
524 $has_too_long_errors =
false;
526 foreach ($paragraph as $position => $word) {
527 if ($word[
'error_type'] ===
'in_passage' 528 || $word[
'error_type'] ===
'passage_end' 529 || $word[
'error_type'] ===
'none') {
533 $text_wrong = $word[
'text_wrong'];
534 if (mb_strlen($text_wrong) > self::ERROR_MAX_LENGTH) {
535 $has_too_long_errors =
true;
545 if ($has_too_long_errors) {
546 $this->tpl->setOnScreenMessage(
548 $this->
lng->txt(
'qst_error_text_too_long')
556 foreach ($paragraph as $position => $word) {
557 if (isset($word[
'text_wrong'])
559 || mb_substr($word[
'text_wrong'], 0, -1) === $error->
getTextWrong()
560 && preg_match(self::FIND_PUNCTUATION_REGEXP, mb_substr($word[
'text_wrong'], -1)) === 1)
574 foreach ($this->errordata as $error) {
575 $position = $error->getPosition();
577 if (array_key_exists($position, $paragraph)) {
578 $this->parsed_errortext[
$key][$position][
'text_correct'] =
579 $error->getTextCorrect();
580 $this->parsed_errortext[
$key][$position][
'points'] =
594 $this->errordata = [];
596 foreach ($errors as $error) {
598 $this->errordata[] = $answer;
606 if ($error->getPosition() === null) {
607 unset($this->errordata[
$index]);
610 $this->errordata = array_values($this->errordata);
619 array $current_error_data,
622 foreach ($current_error_data as $answer_object) {
623 if (strcmp($answer_object->getTextWrong(), $text_wrong) === 0) {
625 $answer_object->getTextCorrect(),
626 $answer_object->getPoints()
635 bool $graphical_output =
false,
636 bool $show_correct_solution =
false,
637 bool $use_link_tags =
true,
638 array $correctness_icons = []
642 $array_reduce_function = fn (?
string $carry,
int $position)
648 $show_correct_solution,
652 $output_array[] =
'<p>' . trim(array_reduce(array_keys($paragraph), $array_reduce_function)) .
'</p>';
655 return implode(
"\n", $output_array);
662 bool $graphical_output,
663 bool $show_correct_solution,
665 array $correctness_icons
685 bool $show_correct_solution
687 $v = $paragraph[$position];
688 if ($show_correct_solution ===
true 689 && ($v[
'error_type'] ===
'in_passage' 690 || $v[
'error_type'] ===
'passage_end')) {
693 if ($show_correct_solution
694 && ($v[
'error_type'] ===
'passage_start' 695 || $v[
'error_type'] ===
'word')) {
696 return $v[
'text_correct'] ??
'';
704 bool $show_correct_solution,
707 if ($show_correct_solution !==
true 708 && in_array($position, $selections[
'user'])) {
709 return 'ilc_qetitem_ErrorTextSelected';
712 if ($show_correct_solution ===
true 713 && in_array($position, $selections[
'best'])) {
714 return 'ilc_qetitem_ErrorTextSelected';
717 return 'ilc_qetitem_ErrorTextItem';
722 bool $graphical_output,
724 array $correctness_icons
726 if ($graphical_output ===
true 727 && (in_array($position, $selections[
'user']) && !in_array($position, $selections[
'best'])
728 || !in_array($position, $selections[
'user']) && in_array($position, $selections[
'best']))) {
729 return $correctness_icons[
'not_correct'];
732 if ($graphical_output ===
true 733 && in_array($position, $selections[
'user']) && in_array($position, $selections[
'best'])) {
734 return $correctness_icons[
'correct'];
742 if (!is_array($selections)) {
747 $array_reduce_function =
function ($carry, $k) use ($paragraph, $selections) {
748 $text = $paragraph[$k][
'text'];
749 if (in_array($k, $selections)) {
750 $text = self::ERROR_WORD_MARKER . $paragraph[$k][
'text'] . self::ERROR_WORD_MARKER;
752 return $carry .
' ' . $text;
754 $output_array[] = trim(array_reduce(array_keys($paragraph), $array_reduce_function));
756 return implode(
"\n", $output_array);
763 foreach ($positions_array as $position => $position_data) {
765 || $withPositivePointsOnly && $position_data[
'points'] < 1) {
769 $selections[] = $position;
770 if ($position_data[
'length'] > 1) {
771 for (
$i = 1;
$i < $position_data[
'length'];
$i++) {
772 $selections[] = $position +
$i;
789 foreach ($correct_positions as $correct_position => $correct_position_data) {
790 $selected_word_key = array_search($correct_position, $selected_word_positions);
791 if ($selected_word_key ===
false) {
795 if ($correct_position_data[
'length'] === 1) {
796 $points += $correct_position_data[
'points'];
797 unset($selected_word_positions[$selected_word_key]);
801 $passage_complete =
true;
802 for (
$i = 1;
$i < $correct_position_data[
'length'];
$i++) {
803 $selected_passage_element_key = array_search($correct_position +
$i, $selected_word_positions);
804 if ($selected_passage_element_key ===
false) {
805 $passage_complete =
false;
808 unset($selected_word_positions[$selected_passage_element_key]);
811 if ($passage_complete) {
812 $points += $correct_position_data[
'points'];
813 unset($selected_word_positions[$selected_word_key]);
817 foreach ($selected_word_positions as $word_position) {
818 if (!array_key_exists($word_position, $correct_positions)) {
829 $this->errordata = [];
847 $correct_answers = [];
849 $correct_answers[] = [
850 'answertext_wrong' => $answer_obj->getTextWrong(),
851 'answertext_correct' => $answer_obj->getTextCorrect(),
852 'points' => $answer_obj->getPoints(),
853 'length' => $answer_obj->getLength(),
854 'pos' => $this->
getId() .
'_' . $answer_obj->getPosition()
857 return $correct_answers;
862 return $this->errortext ??
'';
867 $this->errortext = $text ??
'';
878 foreach ($this->parsed_errortext as $paragraph) {
879 foreach ($paragraph as $position => $word) {
881 'answertext' => $word[
'text'],
882 'order' => $this->
getId() .
'_' . $position
886 'answertext' =>
'###' 907 if ($a_value === null) {
910 $this->textsize = $a_value;
920 $this->points_wrong = $a_value;
926 $result[
'id'] = $this->
getId();
933 $result[
'feedback'] = [
934 'onenotcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
935 'allcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
942 $result[
'mobs'] =
$mobs;
944 return json_encode($result);
984 $data = $this->db->queryF(
985 "SELECT value1+1 as value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = ( 986 SELECT MAX(step) FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s 988 [
"integer",
"integer",
"integer",
"integer",
"integer",
"integer"],
989 [$active_id, $pass, $this->
getId(), $active_id, $pass, $this->
getId()]
992 while ($row = $this->db->fetchAssoc(
$data)) {
993 $result->addKeyValue($row[
"value1"], $row[
"value1"]);
999 $result->setReachedPercentage((
$points / $max_points) * 100);
1006 $text_by_paragraphs = preg_split(self::PARAGRAPH_SPLIT_REGEXP, $this->
getErrorText());
1009 foreach ($text_by_paragraphs as $paragraph) {
1011 preg_split(self::WORD_SPLIT_REGEXP, trim($paragraph)),
1014 $offset += count(end($text_array));
1026 $paragraph_with_error_info = [];
1027 $passage_start = null;
1028 foreach ($paragraph as $position => $word) {
1029 $actual_position = $position + $offset;
1030 if ($passage_start !== null
1031 && (mb_strrpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'end']) === mb_strlen($word) - 2
1032 || mb_strrpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'end']) === mb_strlen($word) - 3
1033 && preg_match(self::FIND_PUNCTUATION_REGEXP, mb_substr($word, -1)) === 1)) {
1037 $paragraph_with_error_info[$passage_start][
'text_wrong'] .=
1039 $paragraph_with_error_info[$actual_position] = [
1040 'text' => $actual_word,
1041 'error_type' =>
'passage_end' 1043 $passage_start = null;
1046 if ($passage_start !== null) {
1047 $paragraph_with_error_info[$passage_start][
'text_wrong'] .=
' ' . $word;
1048 $paragraph_with_error_info[$actual_position] = [
1050 'error_type' =>
'in_passage' 1054 if (mb_strpos($word, self::ERROR_PARAGRAPH_DELIMITERS[
'start']) === 0) {
1055 $paragraph_with_error_info[$actual_position] = [
1056 'text' => substr($word, 2),
1057 'text_wrong' => substr($word, 2),
1058 'error_type' =>
'passage_start',
1059 'error_position' => $actual_position,
1061 $passage_start = $actual_position;
1064 if (mb_strpos($word, self::ERROR_WORD_MARKER) === 0) {
1065 $paragraph_with_error_info[$actual_position] = [
1066 'text' => substr($word, 1),
1067 'text_wrong' => substr($word, 1),
1068 'error_type' =>
'word',
1069 'error_position' => $actual_position,
1074 $paragraph_with_error_info[$actual_position] = [
1076 'error_type' =>
'none',
1081 return $paragraph_with_error_info;
1086 if (mb_substr($word, -2) === self::ERROR_PARAGRAPH_DELIMITERS[
'end']) {
1087 return mb_substr($word, 0, -2);
1089 return mb_substr($word, 0, -3) . mb_substr($word, -1);
1101 $error_text_array = array_reduce(
1102 $this->parsed_errortext,
1103 fn (
$c, $v) =>
$c + $v
1107 return $error_text_array;
1110 if (array_key_exists(
$index, $error_text_array)) {
1111 return $error_text_array[
$index];
1119 $array_by_position = [];
1120 foreach ($this->errordata as $error) {
1121 $array_by_position[$error->getPosition()] = [
1122 'length' => $error->getLength(),
1123 'points' => $error->getPoints(),
1124 'text' => $error->getTextWrong(),
1125 'text_correct' => $error->getTextCorrect()
1128 ksort($array_by_position);
1129 return $array_by_position;
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...
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
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
Abstract basic class which is to be extended by the concrete assessment question type classes...
loadFromDb($question_id)
Loads the object from the database.
addErrorInformationToTextParagraphArray(array $paragraph, int $offset)
toXML($a_include_header=true, $a_include_binary=true, $a_shuffle=false, $test_output=false, $force_image_references=false)
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
ensureNonNegativePoints($points)
saveToDb($original_id="")
Saves a the object to the database.
getClassForPosition(int $position, bool $show_correct_solution, array $selections)
static _getOriginalId(int $question_id)
getTextForPosition(int $position, array $paragraph, bool $show_correct_solution)
copyObject($target_questionpool_id, $title="")
Copies an object.
getErrorDataAsArrayForJS()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getParticipantsSolution()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setComment(string $comment="")
__construct( $title='', $comment='', $author='', $owner=-1, $question='')
assErorText constructor
float $points
The maximum available points for the question.
const ERROR_PARAGRAPH_DELIMITERS
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $preview_session)
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getCorrectnessIconForPosition(int $position, bool $graphical_output, array $selections, array $correctness_icons)
setParticipantsSolution($participantSolution)
generateArrayByPositionFromErrorData()
duplicate(bool $for_test=true, string $title="", string $author="", string $owner="", $testObjId=null)
Duplicates the object.
removeErrorDataWithoutPosition()
setErrorData(array $errors)
getParsedErrorTextForJS()
isComplete()
Returns true, if a single choice question is complete for use.
createErrorTextExport(array $selections)
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
static _enabledAssessmentLogging()
parsePassageEndWord(string $word)
logUserAction(bool $user_entered_values, int $active_id)
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=[])
fromXML($item, int $questionpool_id, ?int $tst_id, &$tst_object, int &$question_counter, array $import_mapping, array &$solutionhints=[])
static logAction(string $logtext, int $active_id, int $question_id)
const PARAGRAPH_SPLIT_REGEXP
getOperators($expression)
Get all available operations for a specific question.
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...
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)
string $question
The question text.
generateOutputStringFromPosition(int $position, array $selections, array $paragraph, bool $graphical_output, bool $show_correct_solution, bool $use_link_tags, array $correctness_icons)
static getOperatorsByExpression($expression)
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass)
{}
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
saveQuestionDataToDb(int $original_id=-1)
getSolutionMaxPass(int $active_id)
correctDataAfterParserUpdate()
removeCurrentSolution(int $active_id, int $pass, bool $authorized=true)
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getBestSelection($withPositivePointsOnly=true)
__construct(Container $dic, ilPlugin $plugin)
setOriginalId(?int $original_id)
setTitle(string $title="")
setErrorsFromParsedErrorText()
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)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setQuestion(string $question="")
const EmptyAnswerExpression