ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.assClozeTest.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
5require_once './Modules/Test/classes/inc.AssessmentConstants.php';
6require_once './Modules/TestQuestionPool/classes/class.assClozeGapCombination.php';
7require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
8require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
9require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
10require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
11
24{
32 public $gaps;
33
42
43
45
53 public $start_tag;
54
62 public $end_tag;
63
75
86
93
95
109 public function __construct(
110 $title = "",
111 $comment = "",
112 $author = "",
113 $owner = -1,
114 $question = ""
115 ) {
116 parent::__construct($title, $comment, $author, $owner, $question);
117 $this->start_tag = "[gap]";
118 $this->end_tag = "[/gap]";
119 $this->gaps = array();
120 $this->setQuestion($question); // @TODO: Should this be $question?? See setter for why this is not trivial.
121 $this->fixedTextLength = "";
122 $this->identical_scoring = 1;
123 $this->gap_combinations_exists = false;
124 $this->gap_combinations = array();
125 }
126
132 public function isComplete()
133 {
134 if (strlen($this->getTitle())
135 && $this->getAuthor()
136 && $this->getClozeText()
137 && count($this->getGaps())
138 && $this->getMaximumPoints() > 0) {
139 return true;
140 }
141 return false;
142 }
143
151 public function cleanQuestiontext($text)
152 {
153 $text = preg_replace("/\[gap[^\]]*?\]/", "[gap]", $text);
154 $text = preg_replace("/<gap([^>]*?)>/", "[gap]", $text);
155 $text = str_replace("</gap>", "[/gap]", $text);
156 return $text;
157 }
158
165 public function loadFromDb($question_id)
166 {
167 global $ilDB;
168 $result = $ilDB->queryF(
169 "SELECT qpl_questions.*, " . $this->getAdditionalTableName() . ".* FROM qpl_questions LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = qpl_questions.question_id WHERE qpl_questions.question_id = %s",
170 array("integer"),
171 array($question_id)
172 );
173 if ($result->numRows() == 1) {
174 $data = $ilDB->fetchAssoc($result);
175 $this->setId($question_id);
176 $this->setNrOfTries($data['nr_of_tries']);
177 $this->setObjId($data["obj_fi"]);
178 $this->setTitle($data["title"]);
179 $this->setComment($data["description"]);
180 $this->setOriginalId($data["original_id"]);
181 $this->setAuthor($data["author"]);
182 $this->setPoints($data["points"]);
183 $this->setOwner($data["owner"]);
184 $this->setQuestion($this->cleanQuestiontext($data["question_text"]));
185 $this->setClozeText($data['cloze_text']);
186 $this->setFixedTextLength($data["fixed_textlen"]);
187 $this->setIdenticalScoring(($data['tstamp'] == 0) ? true : $data["identical_scoring"]);
188 // replacement of old syntax with new syntax
189 include_once("./Services/RTE/classes/class.ilRTE.php");
190 $this->question = ilRTE::_replaceMediaObjectImageSrc($this->question, 1);
191 $this->cloze_text = ilRTE::_replaceMediaObjectImageSrc($this->cloze_text, 1);
192 $this->setTextgapRating($data["textgap_rating"]);
193 $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
194
195 try {
196 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
197 } catch (ilTestQuestionPoolException $e) {
198 }
199
200 // open the cloze gaps with all answers
201 include_once "./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
202 include_once "./Modules/TestQuestionPool/classes/class.assClozeGap.php";
203 $result = $ilDB->queryF(
204 "SELECT * FROM qpl_a_cloze WHERE question_fi = %s ORDER BY gap_id, aorder ASC",
205 array("integer"),
206 array($question_id)
207 );
208 if ($result->numRows() > 0) {
209 $this->gaps = array();
210 while ($data = $ilDB->fetchAssoc($result)) {
211 switch ($data["cloze_type"]) {
212 case CLOZE_TEXT:
213 if (!array_key_exists($data["gap_id"], $this->gaps)) {
214 $this->gaps[$data["gap_id"]] = new assClozeGap(CLOZE_TEXT);
215 }
216 $answer = new assAnswerCloze(
217 $data["answertext"],
218 $data["points"],
219 $data["aorder"]
220 );
221 $this->gaps[$data["gap_id"]]->setGapSize($data['gap_size']);
222
223 $this->gaps[$data["gap_id"]]->addItem($answer);
224 break;
225 case CLOZE_SELECT:
226 if (!array_key_exists($data["gap_id"], $this->gaps)) {
227 $this->gaps[$data["gap_id"]] = new assClozeGap(CLOZE_SELECT);
228 $this->gaps[$data["gap_id"]]->setShuffle($data["shuffle"]);
229 }
230 $answer = new assAnswerCloze(
231 $data["answertext"],
232 $data["points"],
233 $data["aorder"]
234 );
235 $this->gaps[$data["gap_id"]]->addItem($answer);
236 break;
237 case CLOZE_NUMERIC:
238 if (!array_key_exists($data["gap_id"], $this->gaps)) {
239 $this->gaps[$data["gap_id"]] = new assClozeGap(CLOZE_NUMERIC);
240 }
241 $answer = new assAnswerCloze(
242 $data["answertext"],
243 $data["points"],
244 $data["aorder"]
245 );
246 $this->gaps[$data["gap_id"]]->setGapSize($data['gap_size']);
247 $answer->setLowerBound($data["lowerlimit"]);
248 $answer->setUpperBound($data["upperlimit"]);
249 $this->gaps[$data["gap_id"]]->addItem($answer);
250 break;
251 }
252 }
253 }
254 }
255 $assClozeGapCombinationObj = new assClozeGapCombination();
256 $check_for_gap_combinations = $assClozeGapCombinationObj->loadFromDb($question_id);
257 if (count($check_for_gap_combinations) != 0) {
258 $this->setGapCombinationsExists(true);
259 $this->setGapCombinations($check_for_gap_combinations);
260 }
261 parent::loadFromDb($question_id);
262 }
263
264 #region Save question to db
265
275 public function saveToDb($original_id = "")
276 {
280
281 parent::saveToDb($original_id);
282 }
283
288 {
289 global $ilDB;
290
291 $ilDB->manipulateF(
292 "DELETE FROM qpl_a_cloze WHERE question_fi = %s",
293 array( "integer" ),
294 array( $this->getId() )
295 );
296
297 foreach ($this->gaps as $key => $gap) {
298 $this->saveClozeGapItemsToDb($gap, $key);
299 }
300 }
301
308 {
309 global $ilDB;
310
311 $ilDB->manipulateF(
312 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
313 array( "integer" ),
314 array( $this->getId() )
315 );
316
317 $ilDB->manipulateF(
318 "INSERT INTO " . $this->getAdditionalTableName()
319 . " (question_fi, textgap_rating, identical_scoring, fixed_textlen, cloze_text) VALUES (%s, %s, %s, %s, %s)",
320 array(
321 "integer",
322 "text",
323 "text",
324 "integer",
325 "text"
326 ),
327 array(
328 $this->getId(),
329 $this->getTextgapRating(),
330 $this->getIdenticalScoring(),
331 $this->getFixedTextLength() ? $this->getFixedTextLength() : null,
333 )
334 );
335 }
336
343 protected function saveClozeGapItemsToDb($gap, $key)
344 {
345 global $ilDB;
346 foreach ($gap->getItems($this->getShuffler()) as $item) {
347 $query = "";
348 $next_id = $ilDB->nextId('qpl_a_cloze');
349 switch ($gap->getType()) {
350 case CLOZE_TEXT:
351 $this->saveClozeTextGapRecordToDb($next_id, $key, $item, $gap);
352 break;
353 case CLOZE_SELECT:
354 $this->saveClozeSelectGapRecordToDb($next_id, $key, $item, $gap);
355 break;
356 case CLOZE_NUMERIC:
357 $this->saveClozeNumericGapRecordToDb($next_id, $key, $item, $gap);
358 break;
359 }
360 }
361 }
362
371 protected function saveClozeTextGapRecordToDb($next_id, $key, $item, $gap)
372 {
373 global $ilDB;
374 $ilDB->manipulateF(
375 "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)",
376 array(
377 "integer",
378 "integer",
379 "integer",
380 "text",
381 "float",
382 "integer",
383 "text",
384 "integer"
385 ),
386 array(
387 $next_id,
388 $this->getId(),
389 $key,
390 strlen($item->getAnswertext()) ? $item->getAnswertext() : "",
391 $item->getPoints(),
392 $item->getOrder(),
393 $gap->getType(),
394 (int) $gap->getGapSize()
395 )
396 );
397 }
398
407 protected function saveClozeSelectGapRecordToDb($next_id, $key, $item, $gap)
408 {
409 global $ilDB;
410 $ilDB->manipulateF(
411 "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)",
412 array(
413 "integer",
414 "integer",
415 "integer",
416 "text",
417 "float",
418 "integer",
419 "text",
420 "text"
421 ),
422 array(
423 $next_id,
424 $this->getId(),
425 $key,
426 strlen($item->getAnswertext()) ? $item->getAnswertext() : "",
427 $item->getPoints(),
428 $item->getOrder(),
429 $gap->getType(),
430 ($gap->getShuffle()) ? "1" : "0"
431 )
432 );
433 }
434
443 protected function saveClozeNumericGapRecordToDb($next_id, $key, $item, $gap)
444 {
445 global $ilDB;
446
447 include_once "./Services/Math/classes/class.EvalMath.php";
448 $eval = new EvalMath();
449 $eval->suppress_errors = true;
450 $ilDB->manipulateF(
451 "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)",
452 array(
453 "integer",
454 "integer",
455 "integer",
456 "text",
457 "float",
458 "integer",
459 "text",
460 "text",
461 "text",
462 "integer"
463 ),
464 array(
465 $next_id,
466 $this->getId(),
467 $key,
468 strlen($item->getAnswertext()) ? $item->getAnswertext() : "",
469 $item->getPoints(),
470 $item->getOrder(),
471 $gap->getType(),
472 ($eval->e($item->getLowerBound() !== false) && strlen(
473 $item->getLowerBound()
474 ) > 0) ? $item->getLowerBound() : $item->getAnswertext(),
475 ($eval->e($item->getUpperBound() !== false) && strlen(
476 $item->getUpperBound()
477 ) > 0) ? $item->getUpperBound() : $item->getAnswertext(),
478 (int) $gap->getGapSize()
479 )
480 );
481 }
482
483
484
485 #endregion Save question to db
486
493 public function getGaps()
494 {
495 return $this->gaps;
496 }
497
498
505 public function flushGaps()
506 {
507 $this->gaps = array();
508 }
509
519 public function setClozeText($cloze_text = "")
520 {
521 $this->gaps = array();
523 $this->cloze_text = $cloze_text;
525 }
526
527 public function setClozeTextValue($cloze_text = "")
528 {
529 $this->cloze_text = $cloze_text;
530 }
531
539 public function getClozeText()
540 {
541 return $this->cloze_text;
542 }
543
551 public function getStartTag()
552 {
553 return $this->start_tag;
554 }
555
563 public function setStartTag($start_tag = "[gap]")
564 {
565 $this->start_tag = $start_tag;
566 }
567
575 public function getEndTag()
576 {
577 return $this->end_tag;
578 }
579
587 public function setEndTag($end_tag = "[/gap]")
588 {
589 $this->end_tag = $end_tag;
590 }
591
599 {
600 include_once "./Modules/TestQuestionPool/classes/class.assClozeGap.php";
601 include_once "./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
602 $search_pattern = "|\[gap\](.*?)\[/gap\]|i";
603 preg_match_all($search_pattern, $this->getClozeText(), $found);
604 $this->gaps = array();
605 if (count($found[0])) {
606 foreach ($found[1] as $gap_index => $answers) {
607 // create text gaps by default
608 $gap = new assClozeGap(CLOZE_TEXT);
609 $textparams = preg_split("/(?<!\\\\),/", $answers);
610 foreach ($textparams as $key => $value) {
611 $answer = new assAnswerCloze($value, 0, $key);
612 $gap->addItem($answer);
613 }
614 $this->gaps[$gap_index] = $gap;
615 }
616 }
617 }
618
624 public function setGapType($gap_index, $gap_type)
625 {
626 if (array_key_exists($gap_index, $this->gaps)) {
627 $this->gaps[$gap_index]->setType($gap_type);
628 }
629 }
630
640 public function setGapShuffle($gap_index = 0, $shuffle = 1)
641 {
642 if (array_key_exists($gap_index, $this->gaps)) {
643 $this->gaps[$gap_index]->setShuffle($shuffle);
644 }
645 }
646
653 public function clearGapAnswers()
654 {
655 foreach ($this->gaps as $gap_index => $gap) {
656 $this->gaps[$gap_index]->clearItems();
657 }
658 }
659
667 public function getGapCount()
668 {
669 if (is_array($this->gaps)) {
670 return count($this->gaps);
671 } else {
672 return 0;
673 }
674 }
675
686 public function addGapAnswer($gap_index, $order, $answer)
687 {
688 if (array_key_exists($gap_index, $this->gaps)) {
689 if ($this->gaps[$gap_index]->getType() == CLOZE_NUMERIC) {
690 // only allow notation with "." for real numbers
691 $answer = str_replace(",", ".", $answer);
692 }
693 $this->gaps[$gap_index]->addItem(new assAnswerCloze($answer, 0, $order));
694 }
695 }
696
705 public function getGap($gap_index = 0)
706 {
707 if (array_key_exists($gap_index, $this->gaps)) {
708 return $this->gaps[$gap_index];
709 } else {
710 return null;
711 }
712 }
713
714 public function setGapSize($gap_index, $order, $size)
715 {
716 if (array_key_exists($gap_index, $this->gaps)) {
717 $this->gaps[$gap_index]->setGapSize($size);
718 }
719 }
720
731 public function setGapAnswerPoints($gap_index, $order, $points)
732 {
733 if (array_key_exists($gap_index, $this->gaps)) {
734 $this->gaps[$gap_index]->setItemPoints($order, $points);
735 }
736 }
737
746 public function addGapText($gap_index)
747 {
748 if (array_key_exists($gap_index, $this->gaps)) {
749 include_once "./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
750 $answer = new assAnswerCloze(
751 "",
752 0,
753 $this->gaps[$gap_index]->getItemCount()
754 );
755 $this->gaps[$gap_index]->addItem($answer);
756 }
757 }
758
767 public function addGapAtIndex($gap, $index)
768 {
769 $this->gaps[$index] = $gap;
770 }
771
782 public function setGapAnswerLowerBound($gap_index, $order, $bound)
783 {
784 if (array_key_exists($gap_index, $this->gaps)) {
785 $this->gaps[$gap_index]->setItemLowerBound($order, $bound);
786 }
787 }
788
799 public function setGapAnswerUpperBound($gap_index, $order, $bound)
800 {
801 if (array_key_exists($gap_index, $this->gaps)) {
802 $this->gaps[$gap_index]->setItemUpperBound($order, $bound);
803 }
804 }
805
812 public function getMaximumPoints()
813 {
814 $assClozeGapCombinationObj = new assClozeGapCombination();
815 $points = 0;
816 $gaps_used_in_combination = array();
817 if ($assClozeGapCombinationObj->combinationExistsForQid($this->getId())) {
818 $points = $assClozeGapCombinationObj->getMaxPointsForCombination($this->getId());
819 $gaps_used_in_combination = $assClozeGapCombinationObj->getGapsWhichAreUsedInCombination($this->getId());
820 }
821 foreach ($this->gaps as $gap_index => $gap) {
822 if (!array_key_exists($gap_index, $gaps_used_in_combination)) {
823 if ($gap->getType() == CLOZE_TEXT) {
824 $gap_max_points = 0;
825 foreach ($gap->getItems($this->getShuffler()) as $item) {
826 if ($item->getPoints() > $gap_max_points) {
827 $gap_max_points = $item->getPoints();
828 }
829 }
830 $points += $gap_max_points;
831 } elseif ($gap->getType() == CLOZE_SELECT) {
832 $srpoints = 0;
833 foreach ($gap->getItems($this->getShuffler()) as $item) {
834 if ($item->getPoints() > $srpoints) {
835 $srpoints = $item->getPoints();
836 }
837 }
838 $points += $srpoints;
839 } elseif ($gap->getType() == CLOZE_NUMERIC) {
840 $numpoints = 0;
841 foreach ($gap->getItems($this->getShuffler()) as $item) {
842 if ($item->getPoints() > $numpoints) {
843 $numpoints = $item->getPoints();
844 }
845 }
846 $points += $numpoints;
847 }
848 }
849 }
850
851 return $points;
852 }
853
859 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
860 {
861 if ($this->id <= 0) {
862 // The question has not been saved. It cannot be duplicated
863 return;
864 }
865 // duplicate the question in database
866 $this_id = $this->getId();
867 $thisObjId = $this->getObjId();
868
869 $clone = $this;
870 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
872 $clone->id = -1;
873
874 if ((int) $testObjId > 0) {
875 $clone->setObjId($testObjId);
876 }
877
878 if ($title) {
879 $clone->setTitle($title);
880 }
881 if ($author) {
882 $clone->setAuthor($author);
883 }
884 if ($owner) {
885 $clone->setOwner($owner);
886 }
887 if ($for_test) {
888 $clone->saveToDb($original_id);
889 } else {
890 $clone->saveToDb();
891 }
892 if ($this->gap_combinations_exists) {
893 $this->copyGapCombination($this_id, $clone->getId());
894 }
895 if ($for_test) {
896 $clone->saveToDb($original_id);
897 } else {
898 $clone->saveToDb();
899 }
900 // copy question page content
901 $clone->copyPageOfQuestion($this_id);
902 // copy XHTML media objects
903 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
904
905 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
906
907 return $clone->getId();
908 }
909
915 public function copyObject($target_questionpool_id, $title = "")
916 {
917 if ($this->getId() <= 0) {
918 // The question has not been saved. It cannot be duplicated
919 return;
920 }
921
922 $thisId = $this->getId();
923 $thisObjId = $this->getObjId();
924
925 $clone = $this;
926 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
928 $clone->id = -1;
929 $clone->setObjId($target_questionpool_id);
930 if ($title) {
931 $clone->setTitle($title);
932 }
933
934 $clone->saveToDb();
935
936 if ($this->gap_combinations_exists) {
937 $this->copyGapCombination($original_id, $clone->getId());
938 $clone->saveToDb();
939 }
940
941 // copy question page content
942 $clone->copyPageOfQuestion($original_id);
943 // copy XHTML media objects
944 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
945
946 $clone->onCopy($thisObjId, $thisId, $clone->getObjId(), $clone->getId());
947
948 return $clone->getId();
949 }
950
951 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
952 {
953 if ($this->id <= 0) {
954 // The question has not been saved. It cannot be duplicated
955 return;
956 }
957
958 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
959
960 $sourceQuestionId = $this->id;
961 $sourceParentId = $this->getObjId();
962
963 // duplicate the question in database
964 $clone = $this;
965 $clone->id = -1;
966
967 $clone->setObjId($targetParentId);
968
969 if ($targetQuestionTitle) {
970 $clone->setTitle($targetQuestionTitle);
971 }
972
973 $clone->saveToDb();
974
975 if ($this->gap_combinations_exists) {
976 $this->copyGapCombination($sourceQuestionId, $clone->getId());
977 $clone->saveToDb();
978 }
979 // copy question page content
980 $clone->copyPageOfQuestion($sourceQuestionId);
981 // copy XHTML media objects
982 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
983
984 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
985
986 return $clone->id;
987 }
988
989 public function copyGapCombination($orgID, $newID)
990 {
991 $assClozeGapCombinationObj = new assClozeGapCombination();
992 $array = $assClozeGapCombinationObj->loadFromDb($orgID);
993 $assClozeGapCombinationObj->importGapCombinationToDb($newID, $array);
994 }
995
1001 public function updateClozeTextFromGaps()
1002 {
1003 $output = $this->getClozeText();
1004 foreach ($this->getGaps() as $gap_index => $gap) {
1005 $answers = array();
1006 foreach ($gap->getItemsRaw() as $item) {
1007 array_push($answers, str_replace(",", "\\,", $item->getAnswerText()));
1008 }
1009 $output = preg_replace("/\[gap\].*?\[\/gap\]/", "[_gap]" . $this->prepareTextareaOutput(join(",", $answers), true) . "[/_gap]", $output, 1);
1010 }
1011 $output = str_replace("_gap]", "gap]", $output);
1012 $this->cloze_text = $output;
1013 }
1014
1024 public function deleteAnswerText($gap_index, $answer_index)
1025 {
1026 if (array_key_exists($gap_index, $this->gaps)) {
1027 if ($this->gaps[$gap_index]->getItemCount() == 1) {
1028 // this is the last answer text => remove the gap
1029 $this->deleteGap($gap_index);
1030 } else {
1031 // remove the answer text
1032 $this->gaps[$gap_index]->deleteItem($answer_index);
1033 $this->updateClozeTextFromGaps();
1034 }
1035 }
1036 }
1037
1046 public function deleteGap($gap_index)
1047 {
1048 if (array_key_exists($gap_index, $this->gaps)) {
1049 $output = $this->getClozeText();
1050 foreach ($this->getGaps() as $replace_gap_index => $gap) {
1051 $answers = array();
1052 foreach ($gap->getItemsRaw() as $item) {
1053 array_push($answers, str_replace(",", "\\,", $item->getAnswerText()));
1054 }
1055 if ($replace_gap_index == $gap_index) {
1056 $output = preg_replace("/\[gap\].*?\[\/gap\]/", "", $output, 1);
1057 } else {
1058 $output = preg_replace("/\[gap\].*?\[\/gap\]/", "[_gap]" . join(",", $answers) . "[/_gap]", $output, 1);
1059 }
1060 }
1061 $output = str_replace("_gap]", "gap]", $output);
1062 $this->cloze_text = $output;
1063 unset($this->gaps[$gap_index]);
1064 $this->gaps = array_values($this->gaps);
1065 }
1066 }
1067
1077 public function getTextgapPoints($a_original, $a_entered, $max_points)
1078 {
1079 include_once "./Services/Utilities/classes/class.ilStr.php";
1080 $result = 0;
1081 $gaprating = $this->getTextgapRating();
1082 switch ($gaprating) {
1084 if (strcmp(ilStr::strToLower($a_original), ilStr::strToLower($a_entered)) == 0) {
1085 $result = $max_points;
1086 }
1087 break;
1089 if (strcmp($a_original, $a_entered) == 0) {
1090 $result = $max_points;
1091 }
1092 break;
1094 if (levenshtein($a_original, $a_entered) <= 1) {
1095 $result = $max_points;
1096 }
1097 break;
1099 if (levenshtein($a_original, $a_entered) <= 2) {
1100 $result = $max_points;
1101 }
1102 break;
1104 if (levenshtein($a_original, $a_entered) <= 3) {
1105 $result = $max_points;
1106 }
1107 break;
1109 if (levenshtein($a_original, $a_entered) <= 4) {
1110 $result = $max_points;
1111 }
1112 break;
1114 if (levenshtein($a_original, $a_entered) <= 5) {
1115 $result = $max_points;
1116 }
1117 break;
1118 }
1119 return $result;
1120 }
1121
1131 public function getNumericgapPoints($a_original, $a_entered, $max_points, $lowerBound, $upperBound)
1132 {
1133 // fau: fixGapFormula - check entered value by evalMath
1134 // if( ! $this->checkForValidFormula($a_entered) )
1135 // {
1136 // return 0;
1137 // }
1138
1139 include_once "./Services/Math/classes/class.EvalMath.php";
1140 $eval = new EvalMath();
1141 $eval->suppress_errors = true;
1142 $result = 0;
1143
1144 if ($eval->e($a_entered) === false) {
1145 return 0;
1146 } elseif (($eval->e($lowerBound) !== false) && ($eval->e($upperBound) !== false)) {
1147 // fau.
1148 if (($eval->e($a_entered) >= $eval->e($lowerBound)) && ($eval->e($a_entered) <= $eval->e($upperBound))) {
1149 $result = $max_points;
1150 }
1151 } elseif ($eval->e($lowerBound) !== false) {
1152 if (($eval->e($a_entered) >= $eval->e($lowerBound)) && ($eval->e($a_entered) <= $eval->e($a_original))) {
1153 $result = $max_points;
1154 }
1155 } elseif ($eval->e($upperBound) !== false) {
1156 if (($eval->e($a_entered) >= $eval->e($a_original)) && ($eval->e($a_entered) <= $eval->e($upperBound))) {
1157 $result = $max_points;
1158 }
1159 } else {
1160 if ($eval->e($a_entered) == $eval->e($a_original)) {
1161 $result = $max_points;
1162 }
1163 }
1164 return $result;
1165 }
1166
1171 public function checkForValidFormula($value)
1172 {
1173 return preg_match("/^-?(\\d*)(,|\\.|\\/){0,1}(\\d*)$/", $value, $matches);
1174 }
1185 public function calculateReachedPoints($active_id, $pass = null, $authorized = true, $returndetails = false)
1186 {
1187 global $ilDB;
1188
1189 if (is_null($pass)) {
1190 $pass = $this->getSolutionMaxPass($active_id);
1191 }
1192
1193 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
1194 $user_result = array();
1195 while ($data = $ilDB->fetchAssoc($result)) {
1196 if (strcmp($data["value2"], "") != 0) {
1197 $user_result[$data["value1"]] = array(
1198 "gap_id" => $data["value1"],
1199 "value" => $data["value2"]
1200 );
1201 }
1202 }
1203
1204 ksort($user_result); // this is required when identical scoring for same solutions is disabled
1205
1206 if ($returndetails) {
1207 $detailed = array();
1208 $this->calculateReachedPointsForSolution($user_result, $detailed);
1209 return $detailed;
1210 }
1211
1212 return $this->calculateReachedPointsForSolution($user_result);
1213 }
1214
1215 protected function isValidNumericSubmitValue($submittedValue)
1216 {
1217 if (is_numeric($submittedValue)) {
1218 return true;
1219 }
1220
1221 if (preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue)) {
1222 return true;
1223 }
1224
1225 return false;
1226 }
1227
1228 public function validateSolutionSubmit()
1229 {
1230 foreach ($this->getSolutionSubmit() as $gapIndex => $value) {
1231 $gap = $this->getGap($gapIndex);
1232
1233 if ($gap->getType() != CLOZE_NUMERIC) {
1234 continue;
1235 }
1236
1237 if (strlen($value) && !$this->isValidNumericSubmitValue($value)) {
1238 ilUtil::sendFailure($this->lng->txt("err_no_numeric_value"), true);
1239 return false;
1240 }
1241 }
1242
1243 return true;
1244 }
1245
1246 public function fetchSolutionSubmit($submit)
1247 {
1248 $solutionSubmit = array();
1249
1250 foreach ($submit as $key => $value) {
1251 if (preg_match("/^gap_(\d+)/", $key, $matches)) {
1252 $value = ilUtil::stripSlashes($value, false);
1253 if (strlen($value)) {
1254 $gap = $this->getGap($matches[1]);
1255 if (is_object($gap)) {
1256 if (!(($gap->getType() == CLOZE_SELECT) && ($value == -1))) {
1257 if ($gap->getType() == CLOZE_NUMERIC) {
1258 $value = str_replace(",", ".", $value);
1259 }
1260 $solutionSubmit[trim($matches[1])] = $value;
1261 }
1262 }
1263 }
1264 }
1265 }
1266
1267 return $solutionSubmit;
1268 }
1269
1270 public function getSolutionSubmit()
1271 {
1272 return $this->fetchSolutionSubmit($_POST);
1273 }
1274
1283 public function saveWorkingData($active_id, $pass = null, $authorized = true)
1284 {
1285 global $ilDB;
1286 global $ilUser;
1287 if (is_null($pass)) {
1288 include_once "./Modules/Test/classes/class.ilObjTest.php";
1289 $pass = ilObjTest::_getPass($active_id);
1290 }
1291
1292 $entered_values = 0;
1293
1294 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $active_id, $pass, $authorized) {
1295 $this->removeCurrentSolution($active_id, $pass, $authorized);
1296
1297 foreach ($this->getSolutionSubmit() as $val1 => $val2) {
1298 $value = trim(ilUtil::stripSlashes($val2, false));
1299 if (strlen($value)) {
1300 $gap = $this->getGap(trim(ilUtil::stripSlashes($val1)));
1301 if (is_object($gap)) {
1302 if (!(($gap->getType() == CLOZE_SELECT) && ($value == -1))) {
1303 $this->saveCurrentSolution($active_id, $pass, $val1, $value, $authorized);
1304 $entered_values++;
1305 }
1306 }
1307 }
1308 }
1309 });
1310
1311 if ($entered_values) {
1312 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1314 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1315 }
1316 } else {
1317 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1319 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1320 }
1321 }
1322
1323 return true;
1324 }
1325
1329 protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
1330 {
1331 // nothing to rework!
1332 }
1333
1340 public function getQuestionType()
1341 {
1342 return "assClozeTest";
1343 }
1344
1352 public function getTextgapRating()
1353 {
1354 return $this->textgap_rating;
1355 }
1356
1364 public function setTextgapRating($a_textgap_rating)
1365 {
1366 switch ($a_textgap_rating) {
1374 $this->textgap_rating = $a_textgap_rating;
1375 break;
1376 default:
1377 $this->textgap_rating = TEXTGAP_RATING_CASEINSENSITIVE;
1378 break;
1379 }
1380 }
1381
1389 public function getIdenticalScoring()
1390 {
1391 return ($this->identical_scoring) ? 1 : 0;
1392 }
1393
1401 public function setIdenticalScoring($a_identical_scoring)
1402 {
1403 $this->identical_scoring = ($a_identical_scoring) ? 1 : 0;
1404 }
1405
1412 public function getAdditionalTableName()
1413 {
1414 return "qpl_qst_cloze";
1415 }
1416
1423 public function getAnswerTableName()
1424 {
1425 return array("qpl_a_cloze",'qpl_a_cloze_combi_res');
1426 }
1427
1434 public function setFixedTextLength($a_text_len)
1435 {
1436 $this->fixedTextLength = $a_text_len;
1437 }
1438
1445 public function getFixedTextLength()
1446 {
1448 }
1449
1458 public function getMaximumGapPoints($gap_index)
1459 {
1460 $points = 0;
1461 $gap_max_points = 0;
1462 if (array_key_exists($gap_index, $this->gaps)) {
1463 $gap =&$this->gaps[$gap_index];
1464 foreach ($gap->getItems($this->getShuffler()) as $answer) {
1465 if ($answer->getPoints() > $gap_max_points) {
1466 $gap_max_points = $answer->getPoints();
1467 }
1468 }
1469 $points += $gap_max_points;
1470 }
1471 return $points;
1472 }
1473
1479 {
1480 return parent::getRTETextWithMediaObjects() . $this->getClozeText();
1481 }
1483 {
1485 }
1486
1487 public function getGapCombinations()
1488 {
1490 }
1491
1492 public function setGapCombinationsExists($value)
1493 {
1494 $this->gap_combinations_exists = $value;
1495 }
1496
1497 public function setGapCombinations($value)
1498 {
1499 $this->gap_combinations = $value;
1500 }
1501
1505 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
1506 {
1507 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
1508
1509 $solution = $this->getSolutionValues($active_id, $pass);
1510 $i = 1;
1511 foreach ($this->getGaps() as $gap_index => $gap) {
1512 $worksheet->setCell($startrow + $i, 0, $this->lng->txt("gap") . " $i");
1513 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
1514 $checked = false;
1515 foreach ($solution as $solutionvalue) {
1516 if ($gap_index == $solutionvalue["value1"]) {
1517 $string_escaping_org_value = $worksheet->getStringEscaping();
1518 try {
1519 $worksheet->setStringEscaping(false);
1520
1521 switch ($gap->getType()) {
1522 case CLOZE_SELECT:
1523 $worksheet->setCell($startrow + $i, 1, $gap->getItem($solutionvalue["value2"])->getAnswertext());
1524 break;
1525 case CLOZE_NUMERIC:
1526 case CLOZE_TEXT:
1527 $worksheet->setCell($startrow + $i, 1, $solutionvalue["value2"]);
1528 break;
1529 }
1530 } finally {
1531 $worksheet->setStringEscaping($string_escaping_org_value);
1532 }
1533 }
1534 }
1535 $i++;
1536 }
1537
1538 return $startrow + $i + 1;
1539 }
1540
1545 {
1546 // DO NOT USE SETTER FOR CLOZE TEXT -> SETTER DOES RECREATE GAP OBJECTS without having gap type info ^^
1547 //$this->setClozeText( $migrator->migrateToLmContent($this->getClozeText()) );
1548 $this->cloze_text = $migrator->migrateToLmContent($this->getClozeText());
1549 // DO NOT USE SETTER FOR CLOZE TEXT -> SETTER DOES RECREATE GAP OBJECTS without having gap type info ^^
1550 }
1551
1555 public function toJSON()
1556 {
1557 include_once("./Services/RTE/classes/class.ilRTE.php");
1558 $result = array();
1559 $result['id'] = (int) $this->getId();
1560 $result['type'] = (string) $this->getQuestionType();
1561 $result['title'] = (string) $this->getTitle();
1562 $result['question'] = $this->formatSAQuestion($this->getQuestion());
1563 $result['clozetext'] = $this->formatSAQuestion($this->getClozeText());
1564 $result['nr_of_tries'] = (int) $this->getNrOfTries();
1565 $result['shuffle'] = (bool) $this->getShuffle();
1566 $result['feedback'] = array(
1567 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1568 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1569 );
1570
1571 $gaps = array();
1572 foreach ($this->getGaps() as $key => $gap) {
1573 $items = array();
1574 foreach ($gap->getItems($this->getShuffler()) as $item) {
1575 $jitem = array();
1576 $jitem['points'] = $item->getPoints();
1577 $jitem['value'] = $this->formatSAQuestion($item->getAnswertext());
1578 $jitem['order'] = $item->getOrder();
1579 if ($gap->getType() == CLOZE_NUMERIC) {
1580 $jitem['lowerbound'] = $item->getLowerBound();
1581 $jitem['upperbound'] = $item->getUpperBound();
1582 } else {
1583 $jitem['value'] = trim($jitem['value']);
1584 }
1585 array_push($items, $jitem);
1586 }
1587
1588 if ($gap->getGapSize() && ($gap->getType() == CLOZE_TEXT || $gap->getType() == CLOZE_NUMERIC)) {
1589 $jgap['size'] = $gap->getGapSize();
1590 }
1591
1592 $jgap['shuffle'] = $gap->getShuffle();
1593 $jgap['type'] = $gap->getType();
1594 $jgap['item'] = $items;
1595
1596 array_push($gaps, $jgap);
1597 }
1598 $result['gaps'] = $gaps;
1599 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1600 $result['mobs'] = $mobs;
1601 return json_encode($result);
1602 }
1603
1612 public function getOperators($expression)
1613 {
1614 require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1616 }
1617
1622 public function getExpressionTypes()
1623 {
1624 return array(
1630 );
1631 }
1632
1641 public function getUserQuestionResult($active_id, $pass)
1642 {
1644 global $ilDB;
1645 $result = new ilUserQuestionResult($this, $active_id, $pass);
1646
1647 $maxStep = $this->lookupMaxStep($active_id, $pass);
1648
1649 if ($maxStep !== null) {
1650 $data = $ilDB->queryF(
1651 "
1652 SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type
1653 FROM tst_solutions sol
1654 INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi
1655 WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s AND sol.step = %s
1656 GROUP BY sol.solution_id, sol.value1+1, sol.value2, cloze.cloze_type
1657 ",
1658 array("integer", "integer", "integer","integer"),
1659 array($active_id, $pass, $this->getId(), $maxStep)
1660 );
1661 } else {
1662 $data = $ilDB->queryF(
1663 "
1664 SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type
1665 FROM tst_solutions sol
1666 INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi
1667 WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s
1668 GROUP BY sol.solution_id, sol.value1+1, sol.value2, cloze.cloze_type
1669 ",
1670 array("integer", "integer", "integer"),
1671 array($active_id, $pass, $this->getId())
1672 );
1673 }
1674
1675 while ($row = $ilDB->fetchAssoc($data)) {
1676 if ($row["cloze_type"] == 1) {
1677 $row["value2"]++;
1678 }
1679 $result->addKeyValue($row["val"], $row["value2"]);
1680 }
1681
1682 $points = $this->calculateReachedPoints($active_id, $pass);
1683 $max_points = $this->getMaximumPoints();
1684
1685 $result->setReachedPercentage(($points/$max_points) * 100);
1686
1687 return $result;
1688 }
1689
1698 public function getAvailableAnswerOptions($index = null)
1699 {
1700 if ($index !== null) {
1701 return $this->getGap($index);
1702 } else {
1703 return $this->getGaps();
1704 }
1705 }
1706
1707 public function calculateCombinationResult($user_result)
1708 {
1709 $points = 0;
1710
1711 $assClozeGapCombinationObj = new assClozeGapCombination();
1712
1713 if ($assClozeGapCombinationObj->combinationExistsForQid($this->getId())) {
1714 $combinations_for_question = $assClozeGapCombinationObj->getCleanCombinationArray($this->getId());
1715 $gap_answers = array();
1716 $gap_used_in_combination = array();
1717 foreach ($user_result as $user_result_build_list) {
1718 if (is_array($user_result_build_list)) {
1719 $gap_answers[$user_result_build_list['gap_id']] = $user_result_build_list['value'];
1720 }
1721 }
1722
1723 foreach ($combinations_for_question as $combination) {
1724 foreach ($combination as $row_key => $row_answers) {
1725 $combination_fulfilled = true;
1726 $points_for_combination = $row_answers['points'];
1727 foreach ($row_answers as $gap_key => $combination_gap_answer) {
1728 if ($gap_key !== 'points') {
1729 $gap_used_in_combination[$gap_key]= $gap_key;
1730 }
1731 if ($combination_fulfilled && array_key_exists($gap_key, $gap_answers)) {
1732 switch ($combination_gap_answer['type']) {
1733 case CLOZE_TEXT:
1734 $is_text_gap_correct = $this->getTextgapPoints($gap_answers[$gap_key], $combination_gap_answer['answer'], 1);
1735 if ($is_text_gap_correct != 1) {
1736 $combination_fulfilled = false;
1737 }
1738 break;
1739 case CLOZE_SELECT:
1740 $answer = $this->gaps[$gap_key]->getItem($gap_answers[$gap_key]);
1741 $answertext = $answer->getAnswertext();
1742 if ($answertext != $combination_gap_answer['answer']) {
1743 $combination_fulfilled = false;
1744 }
1745 break;
1746 case CLOZE_NUMERIC:
1747 $answer = $this->gaps[$gap_key]->getItem(0);
1748 if ($combination_gap_answer['answer'] != 'out_of_bound') {
1749 $is_numeric_gap_correct = $this->getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1750 if ($is_numeric_gap_correct != 1) {
1751 $combination_fulfilled = false;
1752 }
1753 } else {
1754 $wrong_is_the_new_right = $this->getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1755 if ($wrong_is_the_new_right == 1) {
1756 $combination_fulfilled = false;
1757 }
1758 }
1759 break;
1760 }
1761 } else {
1762 if ($gap_key !== 'points') {
1763 $combination_fulfilled = false;
1764 }
1765 }
1766 }
1767 if ($combination_fulfilled) {
1768 $points += $points_for_combination;
1769 }
1770 }
1771 }
1772 }
1773 return array($points, $gap_used_in_combination);
1774 }
1780 protected function calculateReachedPointsForSolution($user_result, &$detailed = null)
1781 {
1782 if ($detailed === null) {
1783 $detailed = array();
1784 }
1785
1786 $assClozeGapCombinationObj = new assClozeGapCombination();
1787 $combinations[1] = array();
1788 if ($assClozeGapCombinationObj->combinationExistsForQid($this->getId())) {
1789 $combinations = $this->calculateCombinationResult($user_result);
1790 $points = $combinations[0];
1791 }
1792 $counter = 0;
1793 $solution_values_text = array(); // for identical scoring checks
1794 $solution_values_select = array(); // for identical scoring checks
1795 $solution_values_numeric = array(); // for identical scoring checks
1796 foreach ($user_result as $gap_id => $value) {
1797 if (is_string($value)) {
1798 $value = array("value" => $value);
1799 }
1800
1801 if (array_key_exists($gap_id, $this->gaps) && !array_key_exists($gap_id, $combinations[1])) {
1802 switch ($this->gaps[$gap_id]->getType()) {
1803 case CLOZE_TEXT:
1804 $gappoints = 0;
1805 for ($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++) {
1806 $answer = $this->gaps[$gap_id]->getItem($order);
1807 $gotpoints = $this->getTextgapPoints($answer->getAnswertext(), $value["value"], $answer->getPoints());
1808 if ($gotpoints > $gappoints) {
1809 $gappoints = $gotpoints;
1810 }
1811 }
1812 if (!$this->getIdenticalScoring()) {
1813 // check if the same solution text was already entered
1814 if ((in_array($value["value"], $solution_values_text)) && ($gappoints > 0)) {
1815 $gappoints = 0;
1816 }
1817 }
1818 $points += $gappoints;
1819 $detailed[$gap_id] = array("points" => $gappoints, "best" => ($this->getMaximumGapPoints($gap_id) == $gappoints) ? true : false, "positive" => ($gappoints > 0) ? true : false);
1820 array_push($solution_values_text, $value["value"]);
1821 break;
1822 case CLOZE_NUMERIC:
1823 $gappoints = 0;
1824 for ($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++) {
1825 $answer = $this->gaps[$gap_id]->getItem($order);
1826 $gotpoints = $this->getNumericgapPoints($answer->getAnswertext(), $value["value"], $answer->getPoints(), $answer->getLowerBound(), $answer->getUpperBound());
1827 if ($gotpoints > $gappoints) {
1828 $gappoints = $gotpoints;
1829 }
1830 }
1831 if (!$this->getIdenticalScoring()) {
1832 // check if the same solution value was already entered
1833 include_once "./Services/Math/classes/class.EvalMath.php";
1834 $eval = new EvalMath();
1835 $eval->suppress_errors = true;
1836 $found_value = false;
1837 foreach ($solution_values_numeric as $solval) {
1838 if ($eval->e($solval) == $eval->e($value["value"])) {
1839 $found_value = true;
1840 }
1841 }
1842 if ($found_value && ($gappoints > 0)) {
1843 $gappoints = 0;
1844 }
1845 }
1846 $points += $gappoints;
1847 $detailed[$gap_id] = array("points" => $gappoints, "best" => ($this->getMaximumGapPoints($gap_id) == $gappoints) ? true : false, "positive" => ($gappoints > 0) ? true : false);
1848 array_push($solution_values_numeric, $value["value"]);
1849 break;
1850 case CLOZE_SELECT:
1851 if ($value["value"] >= 0) {
1852 for ($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++) {
1853 $answer = $this->gaps[$gap_id]->getItem($order);
1854 if ($value["value"] == $answer->getOrder()) {
1855 $answerpoints = $answer->getPoints();
1856 if (!$this->getIdenticalScoring()) {
1857 // check if the same solution value was already entered
1858 if ((in_array($answer->getAnswertext(), $solution_values_select)) && ($answerpoints > 0)) {
1859 $answerpoints = 0;
1860 }
1861 }
1862 $points += $answerpoints;
1863 $detailed[$gap_id] = array("points" => $answerpoints, "best" => ($this->getMaximumGapPoints($gap_id) == $answerpoints) ? true : false, "positive" => ($answerpoints > 0) ? true : false);
1864 array_push($solution_values_select, $answer->getAnswertext());
1865 }
1866 }
1867 }
1868 break;
1869 }
1870 }
1871 }
1872
1873 return $points;
1874 }
1875
1877 {
1878 $userSolution = array();
1879
1880 foreach ($previewSession->getParticipantsSolution() as $key => $val) {
1881 // fau: fixEmptyGapPreview - add the gap_id as array index
1882 $userSolution[$key] = array('gap_id' => $key, 'value' => $val);
1883 // fau.
1884 }
1885
1886 $reachedPoints = $this->calculateReachedPointsForSolution($userSolution);
1887 $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $reachedPoints);
1888
1889 return $this->ensureNonNegativePoints($reachedPoints);
1890 }
1891}
$worksheet
$result
$size
Definition: RandomTest.php:84
$_POST["username"]
An exception for terminatinating execution or to throw for unit testing.
Class for cloze question numeric answers.
Class for cloze question gaps.
Class for cloze tests.
clearGapAnswers()
Removes all answers from the gaps.
getEndTag()
Returns the end tag of a cloze gap.
addGapAnswer($gap_index, $order, $answer)
Sets the answer text of a gap with a given index.
isComplete()
Returns TRUE, if a cloze test is complete for use.
setGapShuffle($gap_index=0, $shuffle=1)
Sets the shuffle state of a gap with a given index.
getClozeText()
Returns the cloze text.
getTextgapPoints($a_original, $a_entered, $max_points)
Returns the points for a text gap and compares the given solution with the entered solution using the...
isValidNumericSubmitValue($submittedValue)
setGapAnswerPoints($gap_index, $order, $points)
Sets the points of a gap with a given index and an answer with a given order.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
copyObject($target_questionpool_id, $title="")
Copies an assClozeTest object.
setClozeTextValue($cloze_text="")
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
addGapAtIndex($gap, $index)
Adds a ClozeGap object at a given index.
setTextgapRating($a_textgap_rating)
Sets the rating option for text gaps.
getAdditionalTableName()
Returns the name of the additional question data table in the database.
setGapSize($gap_index, $order, $size)
setGapAnswerUpperBound($gap_index, $order, $bound)
Sets the upper bound of a gap with a given index and an answer with a given order.
createGapsFromQuestiontext()
Create gap entries by parsing the question text.
getAnswerTableName()
Returns the name of the answer table in the database.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
updateClozeTextFromGaps()
Updates the gap parameters in the cloze text from the form input.
flushGaps()
Deletes all gaps without changing the cloze text.
getStartTag()
Returns the start tag of a cloze gap.
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
saveAnswerSpecificDataToDb()
Save all gaps to the database.
getFixedTextLength()
Gets the fixed text length for all text fields in the cloze question.
addGapText($gap_index)
Adds a new answer text value to a text gap with a given index.
setIdenticalScoring($a_identical_scoring)
Sets the identical scoring option for cloze questions.
saveToDb($original_id="")
Saves a assClozeTest object to a database.
getGaps()
Returns the array of gaps.
getOperators($expression)
Get all available operations for a specific question.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assClozeTest constructor
getQuestionType()
Returns the question type of the question.
checkForValidFormula($value)
getTextgapRating()
Returns the rating option for text gaps.
deleteGap($gap_index)
Deletes a gap with a given index.
getGap($gap_index=0)
Returns the gap at a given index.
setGapType($gap_index, $gap_type)
Set the type of a gap with a given index.
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{Creates an Excel worksheet for the detailed cumulated results of this question.object}
calculateReachedPoints($active_id, $pass=null, $authorized=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{Reworks the allready saved working data if neccessary.}
copyGapCombination($orgID, $newID)
calculateReachedPointsForSolution($user_result, &$detailed=null)
getExpressionTypes()
Get all available expression types for a specific question.
saveClozeTextGapRecordToDb($next_id, $key, $item, $gap)
Saves a gap-item record.
getGapCount()
Returns the number of gaps.
setGapCombinationsExists($value)
saveClozeGapItemsToDb($gap, $key)
Save all items belonging to one cloze gap to the db.
setGapAnswerLowerBound($gap_index, $order, $bound)
Sets the lower bound of a gap with a given index and an answer with a given order.
setEndTag($end_tag="[/gap]")
Sets the end tag of a cloze gap.
getMaximumGapPoints($gap_index)
Returns the maximum points for a gap.
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
saveAdditionalQuestionDataToDb()
Saves the data for the additional data table.
deleteAnswerText($gap_index, $answer_index)
Deletes the answer text of a gap with a given index and an answer with a given order.
saveClozeSelectGapRecordToDb($next_id, $key, $item, $gap)
Saves a gap-item record.
fetchSolutionSubmit($submit)
toJSON()
Returns a JSON representation of the question.
loadFromDb($question_id)
Loads a assClozeTest object from a database.
setFixedTextLength($a_text_len)
Sets a fixed text length for all text fields in the cloze question.
calculateCombinationResult($user_result)
setClozeText($cloze_text="")
Evaluates the text gap solutions from the cloze text.
saveClozeNumericGapRecordToDb($next_id, $key, $item, $gap)
Saves a gap-item record.
setStartTag($start_tag="[gap]")
Sets the start tag of a cloze gap.
getNumericgapPoints($a_original, $a_entered, $max_points, $lowerBound, $upperBound)
Returns the points for a text gap and compares the given solution with the entered solution using the...
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assClozeTest.
cleanQuestiontext($text)
Cleans cloze question text to remove attributes or tags from older ILIAS versions.
getIdenticalScoring()
Returns the identical scoring status of the question.
Abstract basic class which is to be extended by the concrete assessment question type classes.
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass.
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
setId($id=-1)
Sets the id of the assQuestion object.
setOriginalId($original_id)
setObjId($obj_id=0)
Set the object id of the container object.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
saveQuestionDataToDb($original_id="")
getId()
Gets the id of the assQuestion object.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
getObjId()
Get the object id of the container object.
setTitle($title="")
Sets the title string of the assQuestion object.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second.
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
removeCurrentSolution($active_id, $pass, $authorized=true)
setAuthor($author="")
Sets the authors name of the assQuestion object.
prepareTextareaOutput($txt_output, $prepare_for_latex_output=false, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output in tests.
getShuffle()
Gets the shuffle flag.
getTitle()
Gets the title string of the assQuestion object.
setPoints($a_points)
Sets the maximum available points for the question.
setComment($comment="")
Sets the comment string of the assQuestion object.
getAuthor()
Gets the authors name of the assQuestion object.
setNrOfTries($a_nr_of_tries)
getQuestion()
Gets the question string of the question object.
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
setQuestion($question="")
Sets the question string of the question object.
ensureNonNegativePoints($points)
static _getLogLanguage()
retrieve the log language for assessment logging
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
static _getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static _replaceMediaObjectImageSrc($a_text, $a_direction=0, $nic=IL_INST_ID)
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
static strToLower($a_string)
Definition: class.ilStr.php:87
Class ilUserQuestionResult.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
static stripSlashes($a_str, $a_strip_html=true, $a_allow="")
strip slashes if magic qoutes is enabled
$counter
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\s+" &#(? foreach( $entity_files as $file) $output
const TEXTGAP_RATING_LEVENSHTEIN5
const TEXTGAP_RATING_LEVENSHTEIN4
const TEXTGAP_RATING_LEVENSHTEIN3
const TEXTGAP_RATING_CASESENSITIVE
const CLOZE_NUMERIC
const TEXTGAP_RATING_LEVENSHTEIN2
const TEXTGAP_RATING_CASEINSENSITIVE
const CLOZE_SELECT
const TEXTGAP_RATING_LEVENSHTEIN1
const CLOZE_TEXT
Cloze question constants.
Class iQuestionCondition.
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
Interface ilObjAnswerScoringAdjustable.
Interface ilObjQuestionScoringAdjustable.
$index
Definition: metadata.php:60
$query
global $ilDB
$mobs
$ilUser
Definition: imgupload.php:18
$text
Definition: errorreport.php:18