4 require_once
'./Modules/TestQuestionPool/classes/class.assQuestion.php';
5 require_once
'./Modules/Test/classes/inc.AssessmentConstants.php';
6 require_once
'./Modules/TestQuestionPool/classes/class.assClozeGapCombination.php';
7 require_once
'./Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
8 require_once
'./Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
9 require_once
'./Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
10 require_once
'./Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
118 $this->start_tag =
"[gap]";
119 $this->end_tag =
"[/gap]";
120 $this->gaps = array();
122 $this->fixedTextLength =
"";
123 $this->identical_scoring = 1;
124 $this->gap_combinations_exists =
false;
125 $this->gap_combinations = array();
155 $text = preg_replace(
"/\[gap[^\]]*?\]/",
"[gap]", $text);
156 $text = preg_replace(
"/<gap([^>]*?)>/",
"[gap]", $text);
157 $text = str_replace(
"</gap>",
"[/gap]", $text);
176 $data = $ilDB->fetchAssoc(
$result);
177 $this->
setId($question_id);
191 include_once(
"./Services/RTE/classes/class.ilRTE.php");
195 $this->
setEstimatedWorkingTime(substr($data[
"working_time"], 0, 2), substr($data[
"working_time"], 3, 2), substr($data[
"working_time"], 6, 2));
206 include_once
"./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
207 include_once
"./Modules/TestQuestionPool/classes/class.assClozeGap.php";
208 $result = $ilDB->queryF(
"SELECT * FROM qpl_a_cloze WHERE question_fi = %s ORDER BY gap_id, aorder ASC",
214 $this->gaps = array();
215 while ($data = $ilDB->fetchAssoc(
$result))
217 switch ($data[
"cloze_type"])
220 if (!array_key_exists($data[
"gap_id"], $this->gaps))
229 $this->gaps[$data[
"gap_id"]]->setGapSize($data[
'gap_size']);
231 $this->gaps[$data[
"gap_id"]]->addItem($answer);
234 if (!array_key_exists($data[
"gap_id"], $this->gaps))
237 $this->gaps[$data[
"gap_id"]]->setShuffle($data[
"shuffle"]);
244 $this->gaps[$data[
"gap_id"]]->addItem($answer);
247 if (!array_key_exists($data[
"gap_id"], $this->gaps))
256 $this->gaps[$data[
"gap_id"]]->setGapSize($data[
'gap_size']);
257 $answer->setLowerBound($data[
"lowerlimit"]);
258 $answer->setUpperBound($data[
"upperlimit"]);
259 $this->gaps[$data[
"gap_id"]]->addItem($answer);
266 $check_for_gap_combinations = $assClozeGapCombinationObj->loadFromDb($question_id);
267 if(count($check_for_gap_combinations) != 0)
275 #region Save question to db
302 $ilDB->manipulateF(
"DELETE FROM qpl_a_cloze WHERE question_fi = %s",
304 array( $this->
getId() )
307 foreach ($this->gaps as $key => $gap)
324 array( $this->
getId() )
328 .
" (question_fi, textgap_rating, identical_scoring, fixed_textlen, cloze_text) VALUES (%s, %s, %s, %s, %s)",
355 foreach ($gap->getItems() as $item)
358 $next_id = $ilDB->nextId(
'qpl_a_cloze' );
359 switch ($gap->getType())
385 $ilDB->manipulateF(
"INSERT INTO qpl_a_cloze (answer_id, question_fi, gap_id, answertext, points, aorder, cloze_type, gap_size) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
400 strlen( $item->getAnswertext() ) ? $item->getAnswertext() :
"",
420 $ilDB->manipulateF(
"INSERT INTO qpl_a_cloze (answer_id, question_fi, gap_id, answertext, points, aorder, cloze_type, shuffle) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
435 strlen( $item->getAnswertext() ) ? $item->getAnswertext() :
"",
439 ($gap->getShuffle()) ?
"1" :
"0"
456 include_once
"./Services/Math/classes/class.EvalMath.php";
458 $eval->suppress_errors = TRUE;
459 $ilDB->manipulateF(
"INSERT INTO qpl_a_cloze (answer_id, question_fi, gap_id, answertext, points, aorder, cloze_type, lowerlimit, upperlimit, gap_size) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
476 strlen( $item->getAnswertext() ) ? $item->getAnswertext() :
"",
480 ($eval->e( $item->getLowerBound() !== FALSE ) && strlen( $item->getLowerBound()
481 ) > 0) ? $item->getLowerBound() : $item->getAnswertext(),
482 ($eval->e( $item->getUpperBound() !== FALSE ) && strlen( $item->getUpperBound()
483 ) > 0) ? $item->getUpperBound() : $item->getAnswertext(),
491 #endregion Save question to db
513 $this->gaps = array();
527 $this->gaps = array();
606 include_once
"./Modules/TestQuestionPool/classes/class.assClozeGap.php";
607 include_once
"./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
608 $search_pattern =
"|\[gap\](.*?)\[/gap\]|i";
609 preg_match_all($search_pattern, $this->
getClozeText(), $found);
610 $this->gaps = array();
611 if (count($found[0]))
613 foreach ($found[1] as $gap_index => $answers)
617 $textparams = preg_split(
"/(?<!\\\\),/", $answers);
618 foreach ($textparams as $key => $value)
621 $gap->addItem($answer);
623 $this->gaps[$gap_index] = $gap;
635 if (array_key_exists($gap_index, $this->gaps))
637 $this->gaps[$gap_index]->setType($gap_type);
652 if (array_key_exists($gap_index, $this->gaps))
654 $this->gaps[$gap_index]->setShuffle(
$shuffle);
666 foreach ($this->gaps as $gap_index => $gap)
668 $this->gaps[$gap_index]->clearItems();
681 if (is_array($this->gaps))
683 return count($this->gaps);
703 if (array_key_exists($gap_index, $this->gaps))
708 $answer = str_replace(
",",
".", $answer);
710 $this->gaps[$gap_index]->addItem(
new assAnswerCloze($answer, 0, $order));
724 if (array_key_exists($gap_index, $this->gaps))
726 return $this->gaps[$gap_index];
736 if (array_key_exists($gap_index, $this->gaps))
738 $this->gaps[$gap_index]->setGapSize(
$size);
754 if (array_key_exists($gap_index, $this->gaps))
756 $this->gaps[$gap_index]->setItemPoints($order,
$points);
770 if (array_key_exists($gap_index, $this->gaps))
772 include_once
"./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
776 $this->gaps[$gap_index]->getItemCount()
778 $this->gaps[$gap_index]->addItem($answer);
792 $this->gaps[$index] = $gap;
807 if (array_key_exists($gap_index, $this->gaps))
809 $this->gaps[$gap_index]->setItemLowerBound($order, $bound);
825 if (array_key_exists($gap_index, $this->gaps))
827 $this->gaps[$gap_index]->setItemUpperBound($order, $bound);
841 $gaps_used_in_combination = array();
842 if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
844 $points = $assClozeGapCombinationObj->getMaxPointsForCombination($this->
getId());
845 $gaps_used_in_combination = $assClozeGapCombinationObj->getGapsWhichAreUsedInCombination($this->
getId());
847 foreach ($this->gaps as $gap_index => $gap)
849 if(! array_key_exists($gap_index, $gaps_used_in_combination))
854 foreach ($gap->getItems() as $item)
856 if ($item->getPoints() > $gap_max_points)
858 $gap_max_points = $item->getPoints();
866 foreach ($gap->getItems() as $item)
868 if ($item->getPoints() > $srpoints)
870 $srpoints = $item->getPoints();
878 foreach ($gap->getItems() as $item)
880 if ($item->getPoints() > $numpoints)
882 $numpoints = $item->getPoints();
906 $this_id = $this->
getId();
910 include_once (
"./Modules/TestQuestionPool/classes/class.assQuestion.php");
914 if( (
int)$testObjId > 0 )
916 $clone->setObjId($testObjId);
939 if($this->gap_combinations_exists)
952 $clone->copyPageOfQuestion($this_id);
954 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
956 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
958 return $clone->getId();
968 if ($this->
getId() <= 0)
974 $thisId = $this->
getId();
978 include_once (
"./Modules/TestQuestionPool/classes/class.assQuestion.php");
981 $clone->setObjId($target_questionpool_id);
986 if($this->gap_combinations_exists)
997 $clone->onCopy($thisObjId, $thisId, $clone->getObjId(), $clone->getId());
999 return $clone->getId();
1010 include_once (
"./Modules/TestQuestionPool/classes/class.assQuestion.php");
1013 $sourceParentId = $this->
getObjId();
1019 $clone->setObjId($targetParentId);
1021 if ($targetQuestionTitle)
1023 $clone->setTitle($targetQuestionTitle);
1028 if($this->gap_combinations_exists)
1033 $clone->copyPageOfQuestion($sourceQuestionId);
1035 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
1037 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
1045 $array = $assClozeGapCombinationObj->loadFromDb($orgID);
1046 $assClozeGapCombinationObj->importGapCombinationToDb($newID , $array);
1057 foreach ($this->
getGaps() as $gap_index => $gap)
1060 foreach ($gap->getItemsRaw() as $item)
1062 array_push($answers, str_replace(
",",
"\\,", $item->getAnswerText()));
1064 $output = preg_replace(
"/\[gap\].*?\[\/gap\]/",
"[_gap]" . $this->
prepareTextareaOutput(join(
",", $answers),
true) .
"[/_gap]", $output, 1);
1066 $output = str_replace(
"_gap]",
"gap]", $output);
1067 $this->cloze_text = $output;
1081 if (array_key_exists($gap_index, $this->gaps))
1083 if ($this->gaps[$gap_index]->getItemCount() == 1)
1091 $this->gaps[$gap_index]->deleteItem($answer_index);
1107 if (array_key_exists($gap_index, $this->gaps))
1110 foreach ($this->
getGaps() as $replace_gap_index => $gap)
1113 foreach ($gap->getItemsRaw() as $item)
1115 array_push($answers, str_replace(
",",
"\\,", $item->getAnswerText()));
1117 if ($replace_gap_index == $gap_index)
1119 $output = preg_replace(
"/\[gap\].*?\[\/gap\]/",
"", $output, 1);
1123 $output = preg_replace(
"/\[gap\].*?\[\/gap\]/",
"[_gap]" . join(
",", $answers) .
"[/_gap]", $output, 1);
1126 $output = str_replace(
"_gap]",
"gap]", $output);
1127 $this->cloze_text = $output;
1128 unset($this->gaps[$gap_index]);
1129 $this->gaps = array_values($this->gaps);
1144 include_once
"./Services/Utilities/classes/class.ilStr.php";
1153 if (strcmp($a_original, $a_entered) == 0)
$result = $max_points;
1156 if (levenshtein($a_original, $a_entered) <= 1)
$result = $max_points;
1159 if (levenshtein($a_original, $a_entered) <= 2)
$result = $max_points;
1162 if (levenshtein($a_original, $a_entered) <= 3)
$result = $max_points;
1165 if (levenshtein($a_original, $a_entered) <= 4)
$result = $max_points;
1168 if (levenshtein($a_original, $a_entered) <= 5)
$result = $max_points;
1191 include_once
"./Services/Math/classes/class.EvalMath.php";
1193 $eval->suppress_errors = TRUE;
1196 if ($eval->e($a_entered) === FALSE)
1200 elseif (($eval->e($lowerBound) !== FALSE) && ($eval->e($upperBound) !== FALSE))
1203 if (($eval->e($a_entered) >= $eval->e($lowerBound)) && ($eval->e($a_entered) <= $eval->e($upperBound)))
$result = $max_points;
1205 else if ($eval->e($lowerBound) !== FALSE)
1207 if (($eval->e($a_entered) >= $eval->e($lowerBound)) && ($eval->e($a_entered) <= $eval->e($a_original)))
$result = $max_points;
1209 else if ($eval->e($upperBound) !== FALSE)
1211 if (($eval->e($a_entered) >= $eval->e($a_original)) && ($eval->e($a_entered) <= $eval->e($upperBound)))
$result = $max_points;
1215 if ($eval->e($a_entered) == $eval->e($a_original))
$result = $max_points;
1226 return preg_match(
"/^-?(\\d*)(,|\\.|\\/){0,1}(\\d*)$/", $value, $matches);
1247 $result = $this->getCurrentSolutionResultSet($active_id,
$pass);
1248 $user_result = array();
1249 while ($data = $ilDB->fetchAssoc(
$result))
1251 if (strcmp($data[
"value2"],
"") != 0)
1253 $user_result[$data[
"value1"]] = array(
1254 "gap_id" => $data[
"value1"],
1255 "value" => $data[
"value2"]
1260 ksort($user_result);
1264 $detailed = array();
1274 $solutionSubmit = array();
1276 foreach (
$_POST as $key => $value)
1278 if (preg_match(
"/^gap_(\d+)/", $key, $matches))
1283 $gap = $this->
getGap($matches[1]);
1284 if (is_object($gap))
1286 if (!(($gap->getType() ==
CLOZE_SELECT) && ($value == -1)))
1290 $value = str_replace(
",",
".", $value);
1292 $solutionSubmit[trim($matches[1])] = $value;
1299 return $solutionSubmit;
1316 include_once
"./Modules/Test/classes/class.ilObjTest.php";
1322 $affectedRows = $this->removeCurrentSolution($active_id,
$pass);
1324 $entered_values = 0;
1332 if (is_object($gap))
1334 if (!(($gap->getType() ==
CLOZE_SELECT) && ($value == -1)))
1336 $affectedRows = $this->saveCurrentSolution($active_id,
$pass, $val1, $value);
1345 if ($entered_values)
1347 include_once (
"./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1355 include_once (
"./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1386 return "assClozeTest";
1410 switch ($a_textgap_rating)
1419 $this->textgap_rating = $a_textgap_rating;
1436 return ($this->identical_scoring) ? 1 : 0;
1448 $this->identical_scoring = ($a_identical_scoring) ? 1 : 0;
1459 return "qpl_qst_cloze";
1470 return array(
"qpl_a_cloze",
'qpl_a_cloze_combi_res');
1481 $this->fixedTextLength = $a_text_len;
1506 $gap_max_points = 0;
1507 if (array_key_exists($gap_index, $this->gaps))
1509 $gap =& $this->gaps[$gap_index];
1510 foreach ($gap->getItems() as $answer)
1512 if ($answer->getPoints() > $gap_max_points)
1514 $gap_max_points = $answer->getPoints();
1542 $this->gap_combinations_exists = $value;
1547 $this->gap_combinations = $value;
1563 include_once (
"./Services/Excel/classes/class.ilExcelUtils.php");
1568 foreach ($this->
getGaps() as $gap_index => $gap)
1572 foreach ($solution as $solutionvalue)
1574 if ($gap_index == $solutionvalue[
"value1"])
1576 switch ($gap->getType())
1579 $worksheet->writeString($startrow + $i, 1, $gap->getItem($solutionvalue[
"value2"])->getAnswertext());
1583 $worksheet->writeString($startrow + $i, 1, $solutionvalue[
"value2"]);
1590 return $startrow + $i + 1;
1598 include_once(
"./Services/RTE/classes/class.ilRTE.php");
1609 'onenotcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
false)),
1610 'allcorrect' => $this->
formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(),
true))
1614 foreach ($this->
getGaps() as $key => $gap)
1617 foreach ($gap->getItems() as $item)
1620 $jitem[
'points'] = $item->getPoints();
1622 $jitem[
'order'] = $item->getOrder();
1625 $jitem[
'lowerbound'] = $item->getLowerBound();
1626 $jitem[
'upperbound'] = $item->getUpperBound();
1630 $jitem[
'value'] = trim($jitem[
'value']);
1632 array_push($items, $jitem);
1637 $jgap[
'size'] = $gap->getGapSize();
1640 $jgap[
'shuffle'] = $gap->getShuffle();
1641 $jgap[
'type'] = $gap->getType();
1642 $jgap[
'item'] = $items;
1644 array_push($gaps, $jgap);
1662 require_once
"./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1695 $data = $ilDB->queryF(
1696 "SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type FROM tst_solutions sol INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s AND sol.step = (
1697 SELECT MAX(step) FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s
1698 ) GROUP BY sol.solution_id",
1699 array(
"integer",
"integer",
"integer",
"integer",
"integer",
"integer"),
1703 while(
$row = $ilDB->fetchAssoc($data))
1705 if(
$row[
"cloze_type"] == 1)
1732 return $this->
getGap($index);
1746 if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
1748 $combinations_for_question = $assClozeGapCombinationObj->getCleanCombinationArray($this->
getId());
1749 $gap_answers = array();
1750 $gap_used_in_combination = array();
1751 foreach($user_result as $user_result_build_list)
1753 if(is_array($user_result_build_list))
1755 $gap_answers[$user_result_build_list[
'gap_id']] = $user_result_build_list[
'value'];
1759 foreach($combinations_for_question as $combination)
1762 foreach($combination as $row_key => $row_answers)
1764 $combination_fulfilled =
true;
1765 $points_for_combination = $row_answers[
'points'];
1766 foreach($row_answers as $gap_key => $combination_gap_answer)
1768 if($gap_key !==
'points')
1770 $gap_used_in_combination[$gap_key]= $gap_key;
1772 if($combination_fulfilled && array_key_exists($gap_key, $gap_answers))
1774 switch($combination_gap_answer[
'type'])
1777 $is_text_gap_correct = $this->
getTextgapPoints($gap_answers[$gap_key], $combination_gap_answer[
'answer'], 1);
1778 if($is_text_gap_correct != 1)
1780 $combination_fulfilled =
false;
1784 $answer = $this->gaps[$gap_key]->getItem($gap_answers[$gap_key]);
1785 $answertext = $answer->getAnswertext();
1786 if($answertext != $combination_gap_answer[
'answer'])
1788 $combination_fulfilled =
false;
1792 $answer = $this->gaps[$gap_key]->getItem(0);
1793 if($combination_gap_answer[
'answer'] !=
'out_of_bound')
1795 $is_numeric_gap_correct = $this->
getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1796 if($is_numeric_gap_correct != 1)
1798 $combination_fulfilled =
false;
1803 $wrong_is_the_new_right = $this->
getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1804 if($wrong_is_the_new_right == 1)
1806 $combination_fulfilled =
false;
1814 if($gap_key !==
'points')
1816 $combination_fulfilled =
false;
1820 if($combination_fulfilled)
1822 $points += $points_for_combination;
1827 return array(
$points, $gap_used_in_combination);
1836 if($detailed === null)
1838 $detailed = array();
1842 $combinations[1] = array();
1843 if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
1849 $solution_values_text = array();
1850 $solution_values_select = array();
1851 $solution_values_numeric = array();
1852 foreach($user_result as $gap_id => $value)
1854 if(is_string($value))
1856 $value = array(
"value" => $value);
1859 if(array_key_exists($gap_id, $this->gaps) && !array_key_exists ($gap_id, $combinations[1]))
1861 switch($this->gaps[$gap_id]->getType())
1865 for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1867 $answer = $this->gaps[$gap_id]->getItem($order);
1868 $gotpoints = $this->
getTextgapPoints($answer->getAnswertext(), $value[
"value"], $answer->getPoints());
1869 if($gotpoints > $gappoints) $gappoints = $gotpoints;
1874 if((in_array($value[
"value"], $solution_values_text)) && ($gappoints > 0))
1880 $detailed[$gap_id] = array(
"points" => $gappoints,
"best" => ($this->
getMaximumGapPoints($gap_id) == $gappoints) ? TRUE : FALSE,
"positive" => ($gappoints > 0) ? TRUE : FALSE);
1881 array_push($solution_values_text, $value[
"value"]);
1885 for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1887 $answer = $this->gaps[$gap_id]->getItem($order);
1888 $gotpoints = $this->
getNumericgapPoints($answer->getAnswertext(), $value[
"value"], $answer->getPoints(), $answer->getLowerBound(), $answer->getUpperBound());
1889 if($gotpoints > $gappoints) $gappoints = $gotpoints;
1894 include_once
"./Services/Math/classes/class.EvalMath.php";
1896 $eval->suppress_errors = TRUE;
1897 $found_value = FALSE;
1898 foreach($solution_values_numeric as $solval)
1900 if($eval->e($solval) == $eval->e($value[
"value"]))
1902 $found_value = TRUE;
1905 if($found_value && ($gappoints > 0))
1911 $detailed[$gap_id] = array(
"points" => $gappoints,
"best" => ($this->
getMaximumGapPoints($gap_id) == $gappoints) ? TRUE : FALSE,
"positive" => ($gappoints > 0) ? TRUE : FALSE);
1912 array_push($solution_values_numeric, $value[
"value"]);
1915 if($value[
"value"] >= 0)
1917 for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1919 $answer = $this->gaps[$gap_id]->getItem($order);
1920 if($value[
"value"] == $answer->getOrder())
1922 $answerpoints = $answer->getPoints();
1926 if((in_array($answer->getAnswertext(), $solution_values_select)) && ($answerpoints > 0))
1932 $detailed[$gap_id] = array(
"points" => $answerpoints,
"best" => ($this->
getMaximumGapPoints($gap_id) == $answerpoints) ? TRUE : FALSE,
"positive" => ($answerpoints > 0) ? TRUE : FALSE);
1933 array_push($solution_values_select, $answer->getAnswertext());
1947 $userSolution = array();
1951 $userSolution[] = array(
'gap_id' => $key,
'value' => $val);