ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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 var $gaps;
33
42
43
45
54
63
75
86
93
95
109 function __construct(
110 $title = "",
111 $comment = "",
112 $author = "",
113 $owner = -1,
114 $question = ""
115 )
116 {
117 parent::__construct($title, $comment, $author, $owner, $question);
118 $this->start_tag = "[gap]";
119 $this->end_tag = "[/gap]";
120 $this->gaps = array();
121 $this->setQuestion($question); // @TODO: Should this be $question?? See setter for why this is not trivial.
122 $this->fixedTextLength = "";
123 $this->identical_scoring = 1;
124 $this->gap_combinations_exists = false;
125 $this->gap_combinations = array();
126 }
127
133 public function isComplete()
134 {
135 if (strlen($this->getTitle())
136 && $this->getAuthor()
137 && $this->getClozeText()
138 && count($this->getGaps())
139 && $this->getMaximumPoints() > 0)
140 {
141 return true;
142 }
143 return false;
144 }
145
153 public function cleanQuestiontext($text)
154 {
155 $text = preg_replace("/\[gap[^\]]*?\]/", "[gap]", $text);
156 $text = preg_replace("/<gap([^>]*?)>/", "[gap]", $text);
157 $text = str_replace("</gap>", "[/gap]", $text);
158 return $text;
159 }
160
167 public function loadFromDb($question_id)
168 {
169 global $ilDB;
170 $result = $ilDB->queryF("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",
171 array("integer"),
172 array($question_id)
173 );
174 if ($result->numRows() == 1)
175 {
176 $data = $ilDB->fetchAssoc($result);
177 $this->setId($question_id);
178 $this->setNrOfTries($data['nr_of_tries']);
179 $this->setObjId($data["obj_fi"]);
180 $this->setTitle($data["title"]);
181 $this->setComment($data["description"]);
182 $this->setOriginalId($data["original_id"]);
183 $this->setAuthor($data["author"]);
184 $this->setPoints($data["points"]);
185 $this->setOwner($data["owner"]);
186 $this->setQuestion($this->cleanQuestiontext($data["question_text"]));
187 $this->setClozeText($data['cloze_text']);
188 $this->setFixedTextLength($data["fixed_textlen"]);
189 $this->setIdenticalScoring(($data['tstamp'] == 0) ? true : $data["identical_scoring"]);
190 // replacement of old syntax with new syntax
191 include_once("./Services/RTE/classes/class.ilRTE.php");
192 $this->question = ilRTE::_replaceMediaObjectImageSrc($this->question, 1);
193 $this->cloze_text = ilRTE::_replaceMediaObjectImageSrc($this->cloze_text, 1);
194 $this->setTextgapRating($data["textgap_rating"]);
195 $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
196
197 try
198 {
199 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
200 }
202 {
203 }
204
205 // open the cloze gaps with all answers
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",
209 array("integer"),
210 array($question_id)
211 );
212 if ($result->numRows() > 0)
213 {
214 $this->gaps = array();
215 while ($data = $ilDB->fetchAssoc($result))
216 {
217 switch ($data["cloze_type"])
218 {
219 case CLOZE_TEXT:
220 if (!array_key_exists($data["gap_id"], $this->gaps))
221 {
222 $this->gaps[$data["gap_id"]] = new assClozeGap(CLOZE_TEXT);
223 }
224 $answer = new assAnswerCloze(
225 $data["answertext"],
226 $data["points"],
227 $data["aorder"]
228 );
229 $this->gaps[$data["gap_id"]]->setGapSize($data['gap_size']);
230
231 $this->gaps[$data["gap_id"]]->addItem($answer);
232 break;
233 case CLOZE_SELECT:
234 if (!array_key_exists($data["gap_id"], $this->gaps))
235 {
236 $this->gaps[$data["gap_id"]] = new assClozeGap(CLOZE_SELECT);
237 $this->gaps[$data["gap_id"]]->setShuffle($data["shuffle"]);
238 }
239 $answer = new assAnswerCloze(
240 $data["answertext"],
241 $data["points"],
242 $data["aorder"]
243 );
244 $this->gaps[$data["gap_id"]]->addItem($answer);
245 break;
246 case CLOZE_NUMERIC:
247 if (!array_key_exists($data["gap_id"], $this->gaps))
248 {
249 $this->gaps[$data["gap_id"]] = new assClozeGap(CLOZE_NUMERIC);
250 }
251 $answer = new assAnswerCloze(
252 $data["answertext"],
253 $data["points"],
254 $data["aorder"]
255 );
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);
260 break;
261 }
262 }
263 }
264 }
265 $assClozeGapCombinationObj = new assClozeGapCombination();
266 $check_for_gap_combinations = $assClozeGapCombinationObj->loadFromDb($question_id);
267 if(count($check_for_gap_combinations) != 0)
268 {
269 $this->setGapCombinationsExists(true);
270 $this->setGapCombinations($check_for_gap_combinations);
271 }
272 parent::loadFromDb($question_id);
273 }
274
275 #region Save question to db
276
286 public function saveToDb($original_id = "")
287 {
291
292 parent::saveToDb($original_id);
293 }
294
299 {
300 global $ilDB;
301
302 $ilDB->manipulateF( "DELETE FROM qpl_a_cloze WHERE question_fi = %s",
303 array( "integer" ),
304 array( $this->getId() )
305 );
306
307 foreach ($this->gaps as $key => $gap)
308 {
309 $this->saveClozeGapItemsToDb( $gap, $key );
310 }
311 }
312
319 {
320 global $ilDB;
321
322 $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
323 array( "integer" ),
324 array( $this->getId() )
325 );
326
327 $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName()
328 . " (question_fi, textgap_rating, identical_scoring, fixed_textlen, cloze_text) VALUES (%s, %s, %s, %s, %s)",
329 array(
330 "integer",
331 "text",
332 "text",
333 "integer",
334 "text"
335 ),
336 array(
337 $this->getId(),
338 $this->getTextgapRating(),
339 $this->getIdenticalScoring(),
340 $this->getFixedTextLength() ? $this->getFixedTextLength() : NULL,
342 )
343 );
344 }
345
352 protected function saveClozeGapItemsToDb($gap, $key)
353 {
354 global $ilDB;
355 foreach ($gap->getItems($this->getShuffler()) as $item)
356 {
357 $query = "";
358 $next_id = $ilDB->nextId( 'qpl_a_cloze' );
359 switch ($gap->getType())
360 {
361 case CLOZE_TEXT:
362 $this->saveClozeTextGapRecordToDb($next_id, $key, $item, $gap );
363 break;
364 case CLOZE_SELECT:
365 $this->saveClozeSelectGapRecordToDb($next_id, $key, $item, $gap );
366 break;
367 case CLOZE_NUMERIC:
368 $this->saveClozeNumericGapRecordToDb($next_id, $key, $item, $gap );
369 break;
370 }
371 }
372 }
373
382 protected function saveClozeTextGapRecordToDb($next_id, $key, $item, $gap)
383 {
384 global $ilDB;
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)",
386 array(
387 "integer",
388 "integer",
389 "integer",
390 "text",
391 "float",
392 "integer",
393 "text",
394 "integer"
395 ),
396 array(
397 $next_id,
398 $this->getId(),
399 $key,
400 strlen( $item->getAnswertext() ) ? $item->getAnswertext() : "",
401 $item->getPoints(),
402 $item->getOrder(),
403 $gap->getType(),
404 (int)$gap->getGapSize()
405 )
406 );
407 }
408
417 protected function saveClozeSelectGapRecordToDb($next_id, $key, $item, $gap)
418 {
419 global $ilDB;
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)",
421 array(
422 "integer",
423 "integer",
424 "integer",
425 "text",
426 "float",
427 "integer",
428 "text",
429 "text"
430 ),
431 array(
432 $next_id,
433 $this->getId(),
434 $key,
435 strlen( $item->getAnswertext() ) ? $item->getAnswertext() : "",
436 $item->getPoints(),
437 $item->getOrder(),
438 $gap->getType(),
439 ($gap->getShuffle()) ? "1" : "0"
440 )
441 );
442 }
443
452 protected function saveClozeNumericGapRecordToDb($next_id, $key, $item, $gap)
453 {
454 global $ilDB;
455
456 include_once "./Services/Math/classes/class.EvalMath.php";
457 $eval = new EvalMath();
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)",
460 array(
461 "integer",
462 "integer",
463 "integer",
464 "text",
465 "float",
466 "integer",
467 "text",
468 "text",
469 "text",
470 "integer"
471 ),
472 array(
473 $next_id,
474 $this->getId(),
475 $key,
476 strlen( $item->getAnswertext() ) ? $item->getAnswertext() : "",
477 $item->getPoints(),
478 $item->getOrder(),
479 $gap->getType(),
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(),
484 (int)$gap->getGapSize()
485 )
486 );
487 }
488
489
490
491 #endregion Save question to db
492
499 function getGaps()
500 {
501 return $this->gaps;
502 }
503
504
511 function flushGaps()
512 {
513 $this->gaps = array();
514 }
515
526 {
527 $this->gaps = array();
529 $this->cloze_text = $cloze_text;
531 }
532
534 {
535 $this->cloze_text = $cloze_text;
536 }
537
545 function getClozeText()
546 {
547 return $this->cloze_text;
548 }
549
557 function getStartTag()
558 {
559 return $this->start_tag;
560 }
561
569 function setStartTag($start_tag = "[gap]")
570 {
571 $this->start_tag = $start_tag;
572 }
573
581 function getEndTag()
582 {
583 return $this->end_tag;
584 }
585
593 function setEndTag($end_tag = "[/gap]")
594 {
595 $this->end_tag = $end_tag;
596 }
597
605 {
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]))
612 {
613 foreach ($found[1] as $gap_index => $answers)
614 {
615 // create text gaps by default
616 $gap = new assClozeGap(CLOZE_TEXT);
617 $textparams = preg_split("/(?<!\\\\),/", $answers);
618 foreach ($textparams as $key => $value)
619 {
620 $answer = new assAnswerCloze($value, 0, $key);
621 $gap->addItem($answer);
622 }
623 $this->gaps[$gap_index] = $gap;
624 }
625 }
626 }
627
633 function setGapType($gap_index, $gap_type)
634 {
635 if (array_key_exists($gap_index, $this->gaps))
636 {
637 $this->gaps[$gap_index]->setType($gap_type);
638 }
639 }
640
650 function setGapShuffle($gap_index = 0, $shuffle = 1)
651 {
652 if (array_key_exists($gap_index, $this->gaps))
653 {
654 $this->gaps[$gap_index]->setShuffle($shuffle);
655 }
656 }
657
664 function clearGapAnswers()
665 {
666 foreach ($this->gaps as $gap_index => $gap)
667 {
668 $this->gaps[$gap_index]->clearItems();
669 }
670 }
671
679 function getGapCount()
680 {
681 if (is_array($this->gaps))
682 {
683 return count($this->gaps);
684 }
685 else
686 {
687 return 0;
688 }
689 }
690
701 function addGapAnswer($gap_index, $order, $answer)
702 {
703 if (array_key_exists($gap_index, $this->gaps))
704 {
705 if ($this->gaps[$gap_index]->getType() == CLOZE_NUMERIC)
706 {
707 // only allow notation with "." for real numbers
708 $answer = str_replace(",", ".", $answer);
709 }
710 $this->gaps[$gap_index]->addItem(new assAnswerCloze($answer, 0, $order));
711 }
712 }
713
722 function getGap($gap_index = 0)
723 {
724 if (array_key_exists($gap_index, $this->gaps))
725 {
726 return $this->gaps[$gap_index];
727 }
728 else
729 {
730 return NULL;
731 }
732 }
733
734 public function setGapSize($gap_index, $order, $size)
735 {
736 if (array_key_exists($gap_index, $this->gaps))
737 {
738 $this->gaps[$gap_index]->setGapSize( $size);
739 }
740 }
741
752 function setGapAnswerPoints($gap_index, $order, $points)
753 {
754 if (array_key_exists($gap_index, $this->gaps))
755 {
756 $this->gaps[$gap_index]->setItemPoints($order, $points);
757 }
758 }
759
768 function addGapText($gap_index)
769 {
770 if (array_key_exists($gap_index, $this->gaps))
771 {
772 include_once "./Modules/TestQuestionPool/classes/class.assAnswerCloze.php";
773 $answer = new assAnswerCloze(
774 "",
775 0,
776 $this->gaps[$gap_index]->getItemCount()
777 );
778 $this->gaps[$gap_index]->addItem($answer);
779 }
780 }
781
790 function addGapAtIndex($gap, $index)
791 {
792 $this->gaps[$index] = $gap;
793 }
794
805 function setGapAnswerLowerBound($gap_index, $order, $bound)
806 {
807 if (array_key_exists($gap_index, $this->gaps))
808 {
809 $this->gaps[$gap_index]->setItemLowerBound($order, $bound);
810 }
811 }
812
823 function setGapAnswerUpperBound($gap_index, $order, $bound)
824 {
825 if (array_key_exists($gap_index, $this->gaps))
826 {
827 $this->gaps[$gap_index]->setItemUpperBound($order, $bound);
828 }
829 }
830
838 {
839 $assClozeGapCombinationObj = new assClozeGapCombination();
840 $points = 0;
841 $gaps_used_in_combination = array();
842 if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
843 {
844 $points = $assClozeGapCombinationObj->getMaxPointsForCombination($this->getId());
845 $gaps_used_in_combination = $assClozeGapCombinationObj->getGapsWhichAreUsedInCombination($this->getId());
846 }
847 foreach ($this->gaps as $gap_index => $gap)
848 {
849 if(! array_key_exists($gap_index, $gaps_used_in_combination))
850 {
851 if ($gap->getType() == CLOZE_TEXT)
852 {
853 $gap_max_points = 0;
854 foreach ($gap->getItems($this->getShuffler()) as $item)
855 {
856 if ($item->getPoints() > $gap_max_points)
857 {
858 $gap_max_points = $item->getPoints();
859 }
860 }
861 $points += $gap_max_points;
862 }
863 else if ($gap->getType() == CLOZE_SELECT)
864 {
865 $srpoints = 0;
866 foreach ($gap->getItems($this->getShuffler()) as $item)
867 {
868 if ($item->getPoints() > $srpoints)
869 {
870 $srpoints = $item->getPoints();
871 }
872 }
873 $points += $srpoints;
874 }
875 else if ($gap->getType() == CLOZE_NUMERIC)
876 {
877 $numpoints = 0;
878 foreach ($gap->getItems($this->getShuffler()) as $item)
879 {
880 if ($item->getPoints() > $numpoints)
881 {
882 $numpoints = $item->getPoints();
883 }
884 }
885 $points += $numpoints;
886 }
887 }
888 }
889
890 return $points;
891 }
892
898 function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
899 {
900 if ($this->id <= 0)
901 {
902 // The question has not been saved. It cannot be duplicated
903 return;
904 }
905 // duplicate the question in database
906 $this_id = $this->getId();
907 $thisObjId = $this->getObjId();
908
909 $clone = $this;
910 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
912 $clone->id = -1;
913
914 if( (int)$testObjId > 0 )
915 {
916 $clone->setObjId($testObjId);
917 }
918
919 if ($title)
920 {
921 $clone->setTitle($title);
922 }
923 if ($author)
924 {
925 $clone->setAuthor($author);
926 }
927 if ($owner)
928 {
929 $clone->setOwner($owner);
930 }
931 if ($for_test)
932 {
933 $clone->saveToDb($original_id);
934 }
935 else
936 {
937 $clone->saveToDb();
938 }
939 if($this->gap_combinations_exists)
940 {
941 $this->copyGapCombination($this_id, $clone->getId());
942 }
943 if ($for_test)
944 {
945 $clone->saveToDb($original_id);
946 }
947 else
948 {
949 $clone->saveToDb();
950 }
951 // copy question page content
952 $clone->copyPageOfQuestion($this_id);
953 // copy XHTML media objects
954 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
955
956 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
957
958 return $clone->getId();
959 }
960
966 function copyObject($target_questionpool_id, $title = "")
967 {
968 if ($this->getId() <= 0)
969 {
970 // The question has not been saved. It cannot be duplicated
971 return;
972 }
973
974 $thisId = $this->getId();
975 $thisObjId = $this->getObjId();
976
977 $clone = $this;
978 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
980 $clone->id = -1;
981 $clone->setObjId($target_questionpool_id);
982 if ($title)
983 {
984 $clone->setTitle($title);
985 }
986
987 $clone->saveToDb();
988
989 if($this->gap_combinations_exists)
990 {
991 $this->copyGapCombination($original_id, $clone->getId());
992 $clone->saveToDb();
993 }
994
995 // copy question page content
996 $clone->copyPageOfQuestion($original_id);
997 // copy XHTML media objects
998 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
999
1000 $clone->onCopy($thisObjId, $thisId, $clone->getObjId(), $clone->getId());
1001
1002 return $clone->getId();
1003 }
1004
1005 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
1006 {
1007 if ($this->id <= 0)
1008 {
1009 // The question has not been saved. It cannot be duplicated
1010 return;
1011 }
1012
1013 include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
1014
1015 $sourceQuestionId = $this->id;
1016 $sourceParentId = $this->getObjId();
1017
1018 // duplicate the question in database
1019 $clone = $this;
1020 $clone->id = -1;
1021
1022 $clone->setObjId($targetParentId);
1023
1024 if ($targetQuestionTitle)
1025 {
1026 $clone->setTitle($targetQuestionTitle);
1027 }
1028
1029 $clone->saveToDb();
1030
1031 if($this->gap_combinations_exists)
1032 {
1033 $this->copyGapCombination($sourceQuestionId, $clone->getId());
1034 }
1035 // copy question page content
1036 $clone->copyPageOfQuestion($sourceQuestionId);
1037 // copy XHTML media objects
1038 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
1039
1040 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
1041
1042 return $clone->id;
1043 }
1044
1045 function copyGapCombination($orgID, $newID)
1046 {
1047 $assClozeGapCombinationObj = new assClozeGapCombination();
1048 $array = $assClozeGapCombinationObj->loadFromDb($orgID);
1049 $assClozeGapCombinationObj->importGapCombinationToDb($newID , $array);
1050 }
1051
1058 {
1059 $output = $this->getClozeText();
1060 foreach ($this->getGaps() as $gap_index => $gap)
1061 {
1062 $answers = array();
1063 foreach ($gap->getItemsRaw() as $item)
1064 {
1065 array_push($answers, str_replace(",", "\\,", $item->getAnswerText()));
1066 }
1067 $output = preg_replace("/\[gap\].*?\[\/gap\]/", "[_gap]" . $this->prepareTextareaOutput(join(",", $answers), true) . "[/_gap]", $output, 1);
1068 }
1069 $output = str_replace("_gap]", "gap]", $output);
1070 $this->cloze_text = $output;
1071 }
1072
1082 function deleteAnswerText($gap_index, $answer_index)
1083 {
1084 if (array_key_exists($gap_index, $this->gaps))
1085 {
1086 if ($this->gaps[$gap_index]->getItemCount() == 1)
1087 {
1088 // this is the last answer text => remove the gap
1089 $this->deleteGap($gap_index);
1090 }
1091 else
1092 {
1093 // remove the answer text
1094 $this->gaps[$gap_index]->deleteItem($answer_index);
1095 $this->updateClozeTextFromGaps();
1096 }
1097 }
1098 }
1099
1108 function deleteGap($gap_index)
1109 {
1110 if (array_key_exists($gap_index, $this->gaps))
1111 {
1112 $output = $this->getClozeText();
1113 foreach ($this->getGaps() as $replace_gap_index => $gap)
1114 {
1115 $answers = array();
1116 foreach ($gap->getItemsRaw() as $item)
1117 {
1118 array_push($answers, str_replace(",", "\\,", $item->getAnswerText()));
1119 }
1120 if ($replace_gap_index == $gap_index)
1121 {
1122 $output = preg_replace("/\[gap\].*?\[\/gap\]/", "", $output, 1);
1123 }
1124 else
1125 {
1126 $output = preg_replace("/\[gap\].*?\[\/gap\]/", "[_gap]" . join(",", $answers) . "[/_gap]", $output, 1);
1127 }
1128 }
1129 $output = str_replace("_gap]", "gap]", $output);
1130 $this->cloze_text = $output;
1131 unset($this->gaps[$gap_index]);
1132 $this->gaps = array_values($this->gaps);
1133 }
1134 }
1135
1145 function getTextgapPoints($a_original, $a_entered, $max_points)
1146 {
1147 include_once "./Services/Utilities/classes/class.ilStr.php";
1148 $result = 0;
1149 $gaprating = $this->getTextgapRating();
1150 switch ($gaprating)
1151 {
1153 if (strcmp(ilStr::strToLower($a_original), ilStr::strToLower($a_entered)) == 0) $result = $max_points;
1154 break;
1156 if (strcmp($a_original, $a_entered) == 0) $result = $max_points;
1157 break;
1159 if (levenshtein($a_original, $a_entered) <= 1) $result = $max_points;
1160 break;
1162 if (levenshtein($a_original, $a_entered) <= 2) $result = $max_points;
1163 break;
1165 if (levenshtein($a_original, $a_entered) <= 3) $result = $max_points;
1166 break;
1168 if (levenshtein($a_original, $a_entered) <= 4) $result = $max_points;
1169 break;
1171 if (levenshtein($a_original, $a_entered) <= 5) $result = $max_points;
1172 break;
1173 }
1174 return $result;
1175 }
1176
1186 function getNumericgapPoints($a_original, $a_entered, $max_points, $lowerBound, $upperBound)
1187 {
1188// fau: fixGapFormula - check entered value by evalMath
1189// if( ! $this->checkForValidFormula($a_entered) )
1190// {
1191// return 0;
1192// }
1193
1194 include_once "./Services/Math/classes/class.EvalMath.php";
1195 $eval = new EvalMath();
1196 $eval->suppress_errors = TRUE;
1197 $result = 0;
1198
1199 if ($eval->e($a_entered) === FALSE)
1200 {
1201 return 0;
1202 }
1203 elseif (($eval->e($lowerBound) !== FALSE) && ($eval->e($upperBound) !== FALSE))
1204// fau.
1205 {
1206 if (($eval->e($a_entered) >= $eval->e($lowerBound)) && ($eval->e($a_entered) <= $eval->e($upperBound))) $result = $max_points;
1207 }
1208 else if ($eval->e($lowerBound) !== FALSE)
1209 {
1210 if (($eval->e($a_entered) >= $eval->e($lowerBound)) && ($eval->e($a_entered) <= $eval->e($a_original))) $result = $max_points;
1211 }
1212 else if ($eval->e($upperBound) !== FALSE)
1213 {
1214 if (($eval->e($a_entered) >= $eval->e($a_original)) && ($eval->e($a_entered) <= $eval->e($upperBound))) $result = $max_points;
1215 }
1216 else
1217 {
1218 if ($eval->e($a_entered) == $eval->e($a_original)) $result = $max_points;
1219 }
1220 return $result;
1221 }
1222
1227 public function checkForValidFormula($value)
1228 {
1229 return preg_match("/^-?(\\d*)(,|\\.|\\/){0,1}(\\d*)$/", $value, $matches);
1230 }
1241 public function calculateReachedPoints($active_id, $pass = NULL, $authorized = true, $returndetails = FALSE)
1242 {
1243 global $ilDB;
1244
1245 if (is_null($pass))
1246 {
1247 $pass = $this->getSolutionMaxPass($active_id);
1248 }
1249
1250 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
1251 $user_result = array();
1252 while ($data = $ilDB->fetchAssoc($result))
1253 {
1254 if (strcmp($data["value2"], "") != 0)
1255 {
1256 $user_result[$data["value1"]] = array(
1257 "gap_id" => $data["value1"],
1258 "value" => $data["value2"]
1259 );
1260 }
1261 }
1262
1263 ksort($user_result); // this is required when identical scoring for same solutions is disabled
1264
1265 if ($returndetails)
1266 {
1267 $detailed = array();
1268 $this->calculateReachedPointsForSolution($user_result, $detailed);
1269 return $detailed;
1270 }
1271
1272 return $this->calculateReachedPointsForSolution($user_result);
1273 }
1274
1275 protected function isValidNumericSubmitValue($submittedValue)
1276 {
1277 if( is_numeric($submittedValue) )
1278 {
1279 return true;
1280 }
1281
1282 if( preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue) )
1283 {
1284 return true;
1285 }
1286
1287 return false;
1288 }
1289
1290 public function validateSolutionSubmit()
1291 {
1292 foreach($this->getSolutionSubmit() as $gapIndex => $value)
1293 {
1294 $gap = $this->getGap($gapIndex);
1295
1296 if($gap->getType() != CLOZE_NUMERIC)
1297 {
1298 continue;
1299 }
1300
1301 if( strlen($value) && !$this->isValidNumericSubmitValue($value) )
1302 {
1303 ilUtil::sendFailure($this->lng->txt("err_no_numeric_value"), true);
1304 return false;
1305 }
1306 }
1307
1308 return true;
1309 }
1310
1311 public function fetchSolutionSubmit($submit)
1312 {
1313 $solutionSubmit = array();
1314
1315 foreach ($submit as $key => $value)
1316 {
1317 if (preg_match("/^gap_(\d+)/", $key, $matches))
1318 {
1319 $value = ilUtil::stripSlashes($value, FALSE);
1320 if (strlen($value))
1321 {
1322 $gap = $this->getGap($matches[1]);
1323 if (is_object($gap))
1324 {
1325 if (!(($gap->getType() == CLOZE_SELECT) && ($value == -1)))
1326 {
1327 if ($gap->getType() == CLOZE_NUMERIC)
1328 {
1329 $value = str_replace(",", ".", $value);
1330 }
1331 $solutionSubmit[trim($matches[1])] = $value;
1332 }
1333 }
1334 }
1335 }
1336 }
1337
1338 return $solutionSubmit;
1339
1340 }
1341
1342 public function getSolutionSubmit()
1343 {
1344 return $this->fetchSolutionSubmit($_POST);
1345 }
1346
1355 public function saveWorkingData($active_id, $pass = NULL, $authorized = true)
1356 {
1357 global $ilDB;
1358 global $ilUser;
1359 if (is_null($pass))
1360 {
1361 include_once "./Modules/Test/classes/class.ilObjTest.php";
1362 $pass = ilObjTest::_getPass($active_id);
1363 }
1364
1365 $entered_values = 0;
1366
1367 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function() use (&$entered_values, $active_id, $pass, $authorized) {
1368
1369 $this->removeCurrentSolution($active_id, $pass, $authorized);
1370
1371 foreach($this->getSolutionSubmit() as $val1 => $val2)
1372 {
1373 $value = trim(ilUtil::stripSlashes($val2, FALSE));
1374 if (strlen($value))
1375 {
1376 $gap = $this->getGap(trim(ilUtil::stripSlashes($val1)));
1377 if (is_object($gap))
1378 {
1379 if (!(($gap->getType() == CLOZE_SELECT) && ($value == -1)))
1380 {
1381 $this->saveCurrentSolution($active_id,$pass, $val1, $value, $authorized);
1382 $entered_values++;
1383 }
1384 }
1385 }
1386 }
1387
1388 });
1389
1390 if ($entered_values)
1391 {
1392 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1394 {
1395 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1396 }
1397 }
1398 else
1399 {
1400 include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1402 {
1403 assQuestion::logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1404 }
1405 }
1406
1407 return TRUE;
1408 }
1409
1413 protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
1414 {
1415 // nothing to rework!
1416 }
1417
1425 {
1426 return "assClozeTest";
1427 }
1428
1437 {
1438 return $this->textgap_rating;
1439 }
1440
1448 function setTextgapRating($a_textgap_rating)
1449 {
1450 switch ($a_textgap_rating)
1451 {
1459 $this->textgap_rating = $a_textgap_rating;
1460 break;
1461 default:
1462 $this->textgap_rating = TEXTGAP_RATING_CASEINSENSITIVE;
1463 break;
1464 }
1465 }
1466
1475 {
1476 return ($this->identical_scoring) ? 1 : 0;
1477 }
1478
1486 function setIdenticalScoring($a_identical_scoring)
1487 {
1488 $this->identical_scoring = ($a_identical_scoring) ? 1 : 0;
1489 }
1490
1498 {
1499 return "qpl_qst_cloze";
1500 }
1501
1509 {
1510 return array("qpl_a_cloze",'qpl_a_cloze_combi_res');
1511 }
1512
1519 function setFixedTextLength($a_text_len)
1520 {
1521 $this->fixedTextLength = $a_text_len;
1522 }
1523
1531 {
1533 }
1534
1543 function getMaximumGapPoints($gap_index)
1544 {
1545 $points = 0;
1546 $gap_max_points = 0;
1547 if (array_key_exists($gap_index, $this->gaps))
1548 {
1549 $gap =& $this->gaps[$gap_index];
1550 foreach ($gap->getItems($this->getShuffler()) as $answer)
1551 {
1552 if ($answer->getPoints() > $gap_max_points)
1553 {
1554 $gap_max_points = $answer->getPoints();
1555 }
1556 }
1557 $points += $gap_max_points;
1558 }
1559 return $points;
1560 }
1561
1567 {
1568 return parent::getRTETextWithMediaObjects() . $this->getClozeText();
1569 }
1571 {
1573 }
1574
1576 {
1578 }
1579
1581 {
1582 $this->gap_combinations_exists = $value;
1583 }
1584
1585 function setGapCombinations($value)
1586 {
1587 $this->gap_combinations = $value;
1588 }
1589
1593 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
1594 {
1595 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
1596
1597 $solution = $this->getSolutionValues($active_id, $pass);
1598 $i = 1;
1599 foreach ($this->getGaps() as $gap_index => $gap)
1600 {
1601 $worksheet->setCell($startrow + $i, 0,$this->lng->txt("gap") . " $i");
1602 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
1603 $checked = FALSE;
1604 foreach ($solution as $solutionvalue)
1605 {
1606 if ($gap_index == $solutionvalue["value1"])
1607 {
1608 $string_escaping_org_value = $worksheet->getStringEscaping();
1609 try {
1610 $worksheet->setStringEscaping(false);
1611
1612 switch ($gap->getType())
1613 {
1614 case CLOZE_SELECT:
1615 $worksheet->setCell($startrow + $i, 1, $gap->getItem($solutionvalue["value2"])->getAnswertext());
1616 break;
1617 case CLOZE_NUMERIC:
1618 case CLOZE_TEXT:
1619 $worksheet->setCell($startrow + $i, 1, $solutionvalue["value2"]);
1620 break;
1621 }
1622
1623 } finally {
1624 $worksheet->setStringEscaping($string_escaping_org_value);
1625 }
1626 }
1627 }
1628 $i++;
1629 }
1630
1631 return $startrow + $i + 1;
1632 }
1633
1638 {
1639 // DO NOT USE SETTER FOR CLOZE TEXT -> SETTER DOES RECREATE GAP OBJECTS without having gap type info ^^
1640 //$this->setClozeText( $migrator->migrateToLmContent($this->getClozeText()) );
1641 $this->cloze_text = $migrator->migrateToLmContent($this->getClozeText());
1642 // DO NOT USE SETTER FOR CLOZE TEXT -> SETTER DOES RECREATE GAP OBJECTS without having gap type info ^^
1643 }
1644
1648 public function toJSON()
1649 {
1650 include_once("./Services/RTE/classes/class.ilRTE.php");
1651 $result = array();
1652 $result['id'] = (int) $this->getId();
1653 $result['type'] = (string) $this->getQuestionType();
1654 $result['title'] = (string) $this->getTitle();
1655 $result['question'] = $this->formatSAQuestion($this->getQuestion());
1656 $result['clozetext'] = $this->formatSAQuestion($this->getClozeText());
1657 $result['nr_of_tries'] = (int) $this->getNrOfTries();
1658 $result['shuffle'] = (bool) $this->getShuffle();
1659 $result['feedback'] = array(
1660 'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1661 'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1662 );
1663
1664 $gaps = array();
1665 foreach ($this->getGaps() as $key => $gap)
1666 {
1667 $items = array();
1668 foreach ($gap->getItems($this->getShuffler()) as $item)
1669 {
1670 $jitem = array();
1671 $jitem['points'] = $item->getPoints();
1672 $jitem['value'] = $this->formatSAQuestion($item->getAnswertext());
1673 $jitem['order'] = $item->getOrder();
1674 if ($gap->getType() == CLOZE_NUMERIC)
1675 {
1676 $jitem['lowerbound'] = $item->getLowerBound();
1677 $jitem['upperbound'] = $item->getUpperBound();
1678 }
1679 else
1680 {
1681 $jitem['value'] = trim($jitem['value']);
1682 }
1683 array_push($items, $jitem);
1684 }
1685
1686 if( $gap->getGapSize() && ($gap->getType() == CLOZE_TEXT || $gap->getType() == CLOZE_NUMERIC) )
1687 {
1688 $jgap['size'] = $gap->getGapSize();
1689 }
1690
1691 $jgap['shuffle'] = $gap->getShuffle();
1692 $jgap['type'] = $gap->getType();
1693 $jgap['item'] = $items;
1694
1695 array_push($gaps, $jgap);
1696 }
1697 $result['gaps'] = $gaps;
1698 $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1699 $result['mobs'] = $mobs;
1700 return json_encode($result);
1701 }
1702
1711 public function getOperators($expression)
1712 {
1713 require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1715 }
1716
1721 public function getExpressionTypes()
1722 {
1723 return array(
1729 );
1730 }
1731
1740 public function getUserQuestionResult($active_id, $pass)
1741 {
1743 global $ilDB;
1744 $result = new ilUserQuestionResult($this, $active_id, $pass);
1745
1746 $maxStep = $this->lookupMaxStep($active_id, $pass);
1747
1748 if( $maxStep !== null )
1749 {
1750 $data = $ilDB->queryF(
1751 "
1752 SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type
1753 FROM tst_solutions sol
1754 INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi
1755 WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s AND sol.step = %s
1756 GROUP BY sol.solution_id, sol.value1+1, sol.value2, cloze.cloze_type
1757 ",
1758 array("integer", "integer", "integer","integer"),
1759 array($active_id, $pass, $this->getId(), $maxStep)
1760 );
1761 }
1762 else
1763 {
1764 $data = $ilDB->queryF(
1765 "
1766 SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type
1767 FROM tst_solutions sol
1768 INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi
1769 WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s
1770 GROUP BY sol.solution_id, sol.value1+1, sol.value2, cloze.cloze_type
1771 ",
1772 array("integer", "integer", "integer"),
1773 array($active_id, $pass, $this->getId())
1774 );
1775 }
1776
1777 while($row = $ilDB->fetchAssoc($data))
1778 {
1779 if($row["cloze_type"] == 1)
1780 {
1781 $row["value2"]++;
1782 }
1783 $result->addKeyValue($row["val"], $row["value2"]);
1784 }
1785
1786 $points = $this->calculateReachedPoints($active_id, $pass);
1787 $max_points = $this->getMaximumPoints();
1788
1789 $result->setReachedPercentage(($points/$max_points) * 100);
1790
1791 return $result;
1792 }
1793
1802 public function getAvailableAnswerOptions($index = null)
1803 {
1804 if($index !== null)
1805 {
1806 return $this->getGap($index);
1807 }
1808 else
1809 {
1810 return $this->getGaps();
1811 }
1812 }
1813
1814 public function calculateCombinationResult($user_result)
1815 {
1816 $points = 0;
1817
1818 $assClozeGapCombinationObj = new assClozeGapCombination();
1819
1820 if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
1821 {
1822 $combinations_for_question = $assClozeGapCombinationObj->getCleanCombinationArray($this->getId());
1823 $gap_answers = array();
1824 $gap_used_in_combination = array();
1825 foreach($user_result as $user_result_build_list)
1826 {
1827 if(is_array($user_result_build_list))
1828 {
1829 $gap_answers[$user_result_build_list['gap_id']] = $user_result_build_list['value'];
1830 }
1831 }
1832
1833 foreach($combinations_for_question as $combination)
1834 {
1835
1836 foreach($combination as $row_key => $row_answers)
1837 {
1838 $combination_fulfilled = true;
1839 $points_for_combination = $row_answers['points'];
1840 foreach($row_answers as $gap_key => $combination_gap_answer)
1841 {
1842 if($gap_key !== 'points')
1843 {
1844 $gap_used_in_combination[$gap_key]= $gap_key;
1845 }
1846 if($combination_fulfilled && array_key_exists($gap_key, $gap_answers))
1847 {
1848 switch($combination_gap_answer['type'])
1849 {
1850 case CLOZE_TEXT:
1851 $is_text_gap_correct = $this->getTextgapPoints($gap_answers[$gap_key], $combination_gap_answer['answer'], 1);
1852 if($is_text_gap_correct != 1)
1853 {
1854 $combination_fulfilled = false;
1855 }
1856 break;
1857 case CLOZE_SELECT:
1858 $answer = $this->gaps[$gap_key]->getItem($gap_answers[$gap_key]);
1859 $answertext = $answer->getAnswertext();
1860 if($answertext != $combination_gap_answer['answer'])
1861 {
1862 $combination_fulfilled = false;
1863 }
1864 break;
1865 case CLOZE_NUMERIC:
1866 $answer = $this->gaps[$gap_key]->getItem(0);
1867 if($combination_gap_answer['answer'] != 'out_of_bound')
1868 {
1869 $is_numeric_gap_correct = $this->getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1870 if($is_numeric_gap_correct != 1)
1871 {
1872 $combination_fulfilled = false;
1873 }
1874 }
1875 else
1876 {
1877 $wrong_is_the_new_right = $this->getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1878 if($wrong_is_the_new_right == 1)
1879 {
1880 $combination_fulfilled = false;
1881 }
1882 }
1883 break;
1884 }
1885 }
1886 else
1887 {
1888 if($gap_key !== 'points')
1889 {
1890 $combination_fulfilled = false;
1891 }
1892 }
1893 }
1894 if($combination_fulfilled)
1895 {
1896 $points += $points_for_combination;
1897 }
1898 }
1899 }
1900 }
1901 return array($points, $gap_used_in_combination);
1902 }
1908 protected function calculateReachedPointsForSolution($user_result, &$detailed = null)
1909 {
1910 if($detailed === null)
1911 {
1912 $detailed = array();
1913 }
1914
1915 $assClozeGapCombinationObj = new assClozeGapCombination();
1916 $combinations[1] = array();
1917 if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
1918 {
1919 $combinations = $this->calculateCombinationResult($user_result);
1920 $points = $combinations[0];
1921 }
1922 $counter = 0;
1923 $solution_values_text = array(); // for identical scoring checks
1924 $solution_values_select = array(); // for identical scoring checks
1925 $solution_values_numeric = array(); // for identical scoring checks
1926 foreach($user_result as $gap_id => $value)
1927 {
1928 if(is_string($value))
1929 {
1930 $value = array("value" => $value);
1931 }
1932
1933 if(array_key_exists($gap_id, $this->gaps) && !array_key_exists ($gap_id, $combinations[1]))
1934 {
1935 switch($this->gaps[$gap_id]->getType())
1936 {
1937 case CLOZE_TEXT:
1938 $gappoints = 0;
1939 for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1940 {
1941 $answer = $this->gaps[$gap_id]->getItem($order);
1942 $gotpoints = $this->getTextgapPoints($answer->getAnswertext(), $value["value"], $answer->getPoints());
1943 if($gotpoints > $gappoints) $gappoints = $gotpoints;
1944 }
1945 if(!$this->getIdenticalScoring())
1946 {
1947 // check if the same solution text was already entered
1948 if((in_array($value["value"], $solution_values_text)) && ($gappoints > 0))
1949 {
1950 $gappoints = 0;
1951 }
1952 }
1953 $points += $gappoints;
1954 $detailed[$gap_id] = array("points" => $gappoints, "best" => ($this->getMaximumGapPoints($gap_id) == $gappoints) ? TRUE : FALSE, "positive" => ($gappoints > 0) ? TRUE : FALSE);
1955 array_push($solution_values_text, $value["value"]);
1956 break;
1957 case CLOZE_NUMERIC:
1958 $gappoints = 0;
1959 for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1960 {
1961 $answer = $this->gaps[$gap_id]->getItem($order);
1962 $gotpoints = $this->getNumericgapPoints($answer->getAnswertext(), $value["value"], $answer->getPoints(), $answer->getLowerBound(), $answer->getUpperBound());
1963 if($gotpoints > $gappoints) $gappoints = $gotpoints;
1964 }
1965 if(!$this->getIdenticalScoring())
1966 {
1967 // check if the same solution value was already entered
1968 include_once "./Services/Math/classes/class.EvalMath.php";
1969 $eval = new EvalMath();
1970 $eval->suppress_errors = TRUE;
1971 $found_value = FALSE;
1972 foreach($solution_values_numeric as $solval)
1973 {
1974 if($eval->e($solval) == $eval->e($value["value"]))
1975 {
1976 $found_value = TRUE;
1977 }
1978 }
1979 if($found_value && ($gappoints > 0))
1980 {
1981 $gappoints = 0;
1982 }
1983 }
1984 $points += $gappoints;
1985 $detailed[$gap_id] = array("points" => $gappoints, "best" => ($this->getMaximumGapPoints($gap_id) == $gappoints) ? TRUE : FALSE, "positive" => ($gappoints > 0) ? TRUE : FALSE);
1986 array_push($solution_values_numeric, $value["value"]);
1987 break;
1988 case CLOZE_SELECT:
1989 if($value["value"] >= 0)
1990 {
1991 for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1992 {
1993 $answer = $this->gaps[$gap_id]->getItem($order);
1994 if($value["value"] == $answer->getOrder())
1995 {
1996 $answerpoints = $answer->getPoints();
1997 if(!$this->getIdenticalScoring())
1998 {
1999 // check if the same solution value was already entered
2000 if((in_array($answer->getAnswertext(), $solution_values_select)) && ($answerpoints > 0))
2001 {
2002 $answerpoints = 0;
2003 }
2004 }
2005 $points += $answerpoints;
2006 $detailed[$gap_id] = array("points" => $answerpoints, "best" => ($this->getMaximumGapPoints($gap_id) == $answerpoints) ? TRUE : FALSE, "positive" => ($answerpoints > 0) ? TRUE : FALSE);
2007 array_push($solution_values_select, $answer->getAnswertext());
2008 }
2009 }
2010 }
2011 break;
2012 }
2013 }
2014 }
2015
2016 return $points;
2017 }
2018
2020 {
2021 $userSolution = array();
2022
2023 foreach($previewSession->getParticipantsSolution() as $key => $val)
2024 {
2025 $userSolution[] = array('gap_id' => $key, 'value' => $val);
2026 }
2027
2028 $reachedPoints = $this->calculateReachedPointsForSolution($userSolution);
2029 $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $reachedPoints);
2030
2031 return $this->ensureNonNegativePoints($reachedPoints);
2032 }
2033}
$worksheet
$result
$size
Definition: RandomTest.php:79
$_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="")
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}
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)
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
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)
calculateReachedPoints($active_id, $pass=NULL, $authorized=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
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.
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.
prepareTextareaOutput($txt_output, $prepare_for_latex_output=FALSE, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output in tests.
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.
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
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)
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
$text
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.
global $ilDB
$mobs
$ilUser
Definition: imgupload.php:18