ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
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 
4 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
5 require_once './Modules/Test/classes/inc.AssessmentConstants.php';
6 require_once './Modules/TestQuestionPool/classes/class.assClozeGapCombination.php';
7 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
8 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
9 require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
10 require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
11 
24 {
32  var $gaps;
33 
42 
43 
45 
54 
62  var $end_tag;
63 
75 
86 
93 
94  public $cloze_text;
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 
298  public function saveAnswerSpecificDataToDb()
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 
525  function setClozeText($cloze_text = "")
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 
837  function getMaximumPoints()
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( !$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 getSolutionSubmit()
1312  {
1313  $solutionSubmit = array();
1314 
1315  foreach ($_POST 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 
1349  public function saveWorkingData($active_id, $pass = NULL, $authorized = true)
1350  {
1351  global $ilDB;
1352  global $ilUser;
1353  if (is_null($pass))
1354  {
1355  include_once "./Modules/Test/classes/class.ilObjTest.php";
1356  $pass = ilObjTest::_getPass($active_id);
1357  }
1358 
1359  $this->getProcessLocker()->requestUserSolutionUpdateLock();
1360 
1361  $affectedRows = $this->removeCurrentSolution($active_id, $pass, $authorized);
1362 
1363  $entered_values = 0;
1364 
1365  foreach($this->getSolutionSubmit() as $val1 => $val2)
1366  {
1367  $value = trim(ilUtil::stripSlashes($val2, FALSE));
1368  if (strlen($value))
1369  {
1370  $gap = $this->getGap(trim(ilUtil::stripSlashes($val1)));
1371  if (is_object($gap))
1372  {
1373  if (!(($gap->getType() == CLOZE_SELECT) && ($value == -1)))
1374  {
1375  $affectedRows = $this->saveCurrentSolution($active_id,$pass, $val1, $value, $authorized);
1376  $entered_values++;
1377  }
1378  }
1379  }
1380  }
1381 
1382  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
1383 
1384  if ($entered_values)
1385  {
1386  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1388  {
1389  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1390  }
1391  }
1392  else
1393  {
1394  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
1396  {
1397  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
1398  }
1399  }
1400 
1401  return TRUE;
1402  }
1403 
1412  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
1413  {
1414  // nothing to rework!
1415  }
1416 
1423  function getQuestionType()
1424  {
1425  return "assClozeTest";
1426  }
1427 
1435  function getTextgapRating()
1436  {
1437  return $this->textgap_rating;
1438  }
1439 
1447  function setTextgapRating($a_textgap_rating)
1448  {
1449  switch ($a_textgap_rating)
1450  {
1458  $this->textgap_rating = $a_textgap_rating;
1459  break;
1460  default:
1461  $this->textgap_rating = TEXTGAP_RATING_CASEINSENSITIVE;
1462  break;
1463  }
1464  }
1465 
1474  {
1475  return ($this->identical_scoring) ? 1 : 0;
1476  }
1477 
1485  function setIdenticalScoring($a_identical_scoring)
1486  {
1487  $this->identical_scoring = ($a_identical_scoring) ? 1 : 0;
1488  }
1489 
1497  {
1498  return "qpl_qst_cloze";
1499  }
1500 
1508  {
1509  return array("qpl_a_cloze",'qpl_a_cloze_combi_res');
1510  }
1511 
1518  function setFixedTextLength($a_text_len)
1519  {
1520  $this->fixedTextLength = $a_text_len;
1521  }
1522 
1530  {
1531  return $this->fixedTextLength;
1532  }
1533 
1542  function getMaximumGapPoints($gap_index)
1543  {
1544  $points = 0;
1545  $gap_max_points = 0;
1546  if (array_key_exists($gap_index, $this->gaps))
1547  {
1548  $gap =& $this->gaps[$gap_index];
1549  foreach ($gap->getItems($this->getShuffler()) as $answer)
1550  {
1551  if ($answer->getPoints() > $gap_max_points)
1552  {
1553  $gap_max_points = $answer->getPoints();
1554  }
1555  }
1556  $points += $gap_max_points;
1557  }
1558  return $points;
1559  }
1560 
1566  {
1567  return parent::getRTETextWithMediaObjects() . $this->getClozeText();
1568  }
1570  {
1572  }
1573 
1575  {
1576  return $this->gap_combinations;
1577  }
1578 
1579  function setGapCombinationsExists($value)
1580  {
1581  $this->gap_combinations_exists = $value;
1582  }
1583 
1584  function setGapCombinations($value)
1585  {
1586  $this->gap_combinations = $value;
1587  }
1600  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
1601  {
1602  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
1603  $solution = $this->getSolutionValues($active_id, $pass);
1604  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
1605  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
1606  $i = 1;
1607  foreach ($this->getGaps() as $gap_index => $gap)
1608  {
1609  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($this->lng->txt("gap") . " $i"), $format_bold);
1610  $checked = FALSE;
1611  foreach ($solution as $solutionvalue)
1612  {
1613  if ($gap_index == $solutionvalue["value1"])
1614  {
1615  switch ($gap->getType())
1616  {
1617  case CLOZE_SELECT:
1618  $worksheet->writeString($startrow + $i, 1, $gap->getItem($solutionvalue["value2"])->getAnswertext());
1619  break;
1620  case CLOZE_NUMERIC:
1621  case CLOZE_TEXT:
1622  $worksheet->writeString($startrow + $i, 1, $solutionvalue["value2"]);
1623  break;
1624  }
1625  }
1626  }
1627  $i++;
1628  }
1629  return $startrow + $i + 1;
1630  }
1631 
1636  {
1637  // DO NOT USE SETTER FOR CLOZE TEXT -> SETTER DOES RECREATE GAP OBJECTS without having gap type info ^^
1638  //$this->setClozeText( $migrator->migrateToLmContent($this->getClozeText()) );
1639  $this->cloze_text = $migrator->migrateToLmContent($this->getClozeText());
1640  // DO NOT USE SETTER FOR CLOZE TEXT -> SETTER DOES RECREATE GAP OBJECTS without having gap type info ^^
1641  }
1642 
1646  public function toJSON()
1647  {
1648  include_once("./Services/RTE/classes/class.ilRTE.php");
1649  $result = array();
1650  $result['id'] = (int) $this->getId();
1651  $result['type'] = (string) $this->getQuestionType();
1652  $result['title'] = (string) $this->getTitle();
1653  $result['question'] = $this->formatSAQuestion($this->getQuestion());
1654  $result['clozetext'] = $this->formatSAQuestion($this->getClozeText());
1655  $result['nr_of_tries'] = (int) $this->getNrOfTries();
1656  $result['shuffle'] = (bool) $this->getShuffle();
1657  $result['feedback'] = array(
1658  'onenotcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
1659  'allcorrect' => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
1660  );
1661 
1662  $gaps = array();
1663  foreach ($this->getGaps() as $key => $gap)
1664  {
1665  $items = array();
1666  foreach ($gap->getItems($this->getShuffler()) as $item)
1667  {
1668  $jitem = array();
1669  $jitem['points'] = $item->getPoints();
1670  $jitem['value'] = $this->formatSAQuestion($item->getAnswertext());
1671  $jitem['order'] = $item->getOrder();
1672  if ($gap->getType() == CLOZE_NUMERIC)
1673  {
1674  $jitem['lowerbound'] = $item->getLowerBound();
1675  $jitem['upperbound'] = $item->getUpperBound();
1676  }
1677  else
1678  {
1679  $jitem['value'] = trim($jitem['value']);
1680  }
1681  array_push($items, $jitem);
1682  }
1683 
1684  if( $gap->getGapSize() && ($gap->getType() == CLOZE_TEXT || $gap->getType() == CLOZE_NUMERIC) )
1685  {
1686  $jgap['size'] = $gap->getGapSize();
1687  }
1688 
1689  $jgap['shuffle'] = $gap->getShuffle();
1690  $jgap['type'] = $gap->getType();
1691  $jgap['item'] = $items;
1692 
1693  array_push($gaps, $jgap);
1694  }
1695  $result['gaps'] = $gaps;
1696  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
1697  $result['mobs'] = $mobs;
1698  return json_encode($result);
1699  }
1700 
1709  public function getOperators($expression)
1710  {
1711  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
1713  }
1714 
1719  public function getExpressionTypes()
1720  {
1721  return array(
1727  );
1728  }
1729 
1738  public function getUserQuestionResult($active_id, $pass)
1739  {
1741  global $ilDB;
1742  $result = new ilUserQuestionResult($this, $active_id, $pass);
1743 
1744  $maxStep = $this->lookupMaxStep($active_id, $pass);
1745 
1746  if( $maxStep !== null )
1747  {
1748  $data = $ilDB->queryF(
1749  "
1750  SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type
1751  FROM tst_solutions sol
1752  INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi
1753  WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s AND sol.step = %s
1754  GROUP BY sol.solution_id, sol.value1+1, sol.value2, cloze.cloze_type
1755  ",
1756  array("integer", "integer", "integer","integer"),
1757  array($active_id, $pass, $this->getId(), $maxStep)
1758  );
1759  }
1760  else
1761  {
1762  $data = $ilDB->queryF(
1763  "
1764  SELECT sol.value1+1 as val, sol.value2, cloze.cloze_type
1765  FROM tst_solutions sol
1766  INNER JOIN qpl_a_cloze cloze ON cloze.gap_id = value1 AND cloze.question_fi = sol.question_fi
1767  WHERE sol.active_fi = %s AND sol.pass = %s AND sol.question_fi = %s
1768  GROUP BY sol.solution_id, sol.value1+1, sol.value2, cloze.cloze_type
1769  ",
1770  array("integer", "integer", "integer"),
1771  array($active_id, $pass, $this->getId())
1772  );
1773  }
1774 
1775  while($row = $ilDB->fetchAssoc($data))
1776  {
1777  if($row["cloze_type"] == 1)
1778  {
1779  $row["value2"]++;
1780  }
1781  $result->addKeyValue($row["val"], $row["value2"]);
1782  }
1783 
1784  $points = $this->calculateReachedPoints($active_id, $pass);
1785  $max_points = $this->getMaximumPoints();
1786 
1787  $result->setReachedPercentage(($points/$max_points) * 100);
1788 
1789  return $result;
1790  }
1791 
1800  public function getAvailableAnswerOptions($index = null)
1801  {
1802  if($index !== null)
1803  {
1804  return $this->getGap($index);
1805  }
1806  else
1807  {
1808  return $this->getGaps();
1809  }
1810  }
1811 
1812  public function calculateCombinationResult($user_result)
1813  {
1814  $points = 0;
1815 
1816  $assClozeGapCombinationObj = new assClozeGapCombination();
1817 
1818  if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
1819  {
1820  $combinations_for_question = $assClozeGapCombinationObj->getCleanCombinationArray($this->getId());
1821  $gap_answers = array();
1822  $gap_used_in_combination = array();
1823  foreach($user_result as $user_result_build_list)
1824  {
1825  if(is_array($user_result_build_list))
1826  {
1827  $gap_answers[$user_result_build_list['gap_id']] = $user_result_build_list['value'];
1828  }
1829  }
1830 
1831  foreach($combinations_for_question as $combination)
1832  {
1833 
1834  foreach($combination as $row_key => $row_answers)
1835  {
1836  $combination_fulfilled = true;
1837  $points_for_combination = $row_answers['points'];
1838  foreach($row_answers as $gap_key => $combination_gap_answer)
1839  {
1840  if($gap_key !== 'points')
1841  {
1842  $gap_used_in_combination[$gap_key]= $gap_key;
1843  }
1844  if($combination_fulfilled && array_key_exists($gap_key, $gap_answers))
1845  {
1846  switch($combination_gap_answer['type'])
1847  {
1848  case CLOZE_TEXT:
1849  $is_text_gap_correct = $this->getTextgapPoints($gap_answers[$gap_key], $combination_gap_answer['answer'], 1);
1850  if($is_text_gap_correct != 1)
1851  {
1852  $combination_fulfilled = false;
1853  }
1854  break;
1855  case CLOZE_SELECT:
1856  $answer = $this->gaps[$gap_key]->getItem($gap_answers[$gap_key]);
1857  $answertext = $answer->getAnswertext();
1858  if($answertext != $combination_gap_answer['answer'])
1859  {
1860  $combination_fulfilled = false;
1861  }
1862  break;
1863  case CLOZE_NUMERIC:
1864  $answer = $this->gaps[$gap_key]->getItem(0);
1865  if($combination_gap_answer['answer'] != 'out_of_bound')
1866  {
1867  $is_numeric_gap_correct = $this->getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1868  if($is_numeric_gap_correct != 1)
1869  {
1870  $combination_fulfilled = false;
1871  }
1872  }
1873  else
1874  {
1875  $wrong_is_the_new_right = $this->getNumericgapPoints($answer->getAnswertext(), $gap_answers[$gap_key], 1, $answer->getLowerBound(), $answer->getUpperBound());
1876  if($wrong_is_the_new_right == 1)
1877  {
1878  $combination_fulfilled = false;
1879  }
1880  }
1881  break;
1882  }
1883  }
1884  else
1885  {
1886  if($gap_key !== 'points')
1887  {
1888  $combination_fulfilled = false;
1889  }
1890  }
1891  }
1892  if($combination_fulfilled)
1893  {
1894  $points += $points_for_combination;
1895  }
1896  }
1897  }
1898  }
1899  return array($points, $gap_used_in_combination);
1900  }
1906  protected function calculateReachedPointsForSolution($user_result, &$detailed = null)
1907  {
1908  if($detailed === null)
1909  {
1910  $detailed = array();
1911  }
1912 
1913  $assClozeGapCombinationObj = new assClozeGapCombination();
1914  $combinations[1] = array();
1915  if($assClozeGapCombinationObj->combinationExistsForQid($this->getId()))
1916  {
1917  $combinations = $this->calculateCombinationResult($user_result);
1918  $points = $combinations[0];
1919  }
1920  $counter = 0;
1921  $solution_values_text = array(); // for identical scoring checks
1922  $solution_values_select = array(); // for identical scoring checks
1923  $solution_values_numeric = array(); // for identical scoring checks
1924  foreach($user_result as $gap_id => $value)
1925  {
1926  if(is_string($value))
1927  {
1928  $value = array("value" => $value);
1929  }
1930 
1931  if(array_key_exists($gap_id, $this->gaps) && !array_key_exists ($gap_id, $combinations[1]))
1932  {
1933  switch($this->gaps[$gap_id]->getType())
1934  {
1935  case CLOZE_TEXT:
1936  $gappoints = 0;
1937  for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1938  {
1939  $answer = $this->gaps[$gap_id]->getItem($order);
1940  $gotpoints = $this->getTextgapPoints($answer->getAnswertext(), $value["value"], $answer->getPoints());
1941  if($gotpoints > $gappoints) $gappoints = $gotpoints;
1942  }
1943  if(!$this->getIdenticalScoring())
1944  {
1945  // check if the same solution text was already entered
1946  if((in_array($value["value"], $solution_values_text)) && ($gappoints > 0))
1947  {
1948  $gappoints = 0;
1949  }
1950  }
1951  $points += $gappoints;
1952  $detailed[$gap_id] = array("points" => $gappoints, "best" => ($this->getMaximumGapPoints($gap_id) == $gappoints) ? TRUE : FALSE, "positive" => ($gappoints > 0) ? TRUE : FALSE);
1953  array_push($solution_values_text, $value["value"]);
1954  break;
1955  case CLOZE_NUMERIC:
1956  $gappoints = 0;
1957  for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1958  {
1959  $answer = $this->gaps[$gap_id]->getItem($order);
1960  $gotpoints = $this->getNumericgapPoints($answer->getAnswertext(), $value["value"], $answer->getPoints(), $answer->getLowerBound(), $answer->getUpperBound());
1961  if($gotpoints > $gappoints) $gappoints = $gotpoints;
1962  }
1963  if(!$this->getIdenticalScoring())
1964  {
1965  // check if the same solution value was already entered
1966  include_once "./Services/Math/classes/class.EvalMath.php";
1967  $eval = new EvalMath();
1968  $eval->suppress_errors = TRUE;
1969  $found_value = FALSE;
1970  foreach($solution_values_numeric as $solval)
1971  {
1972  if($eval->e($solval) == $eval->e($value["value"]))
1973  {
1974  $found_value = TRUE;
1975  }
1976  }
1977  if($found_value && ($gappoints > 0))
1978  {
1979  $gappoints = 0;
1980  }
1981  }
1982  $points += $gappoints;
1983  $detailed[$gap_id] = array("points" => $gappoints, "best" => ($this->getMaximumGapPoints($gap_id) == $gappoints) ? TRUE : FALSE, "positive" => ($gappoints > 0) ? TRUE : FALSE);
1984  array_push($solution_values_numeric, $value["value"]);
1985  break;
1986  case CLOZE_SELECT:
1987  if($value["value"] >= 0)
1988  {
1989  for($order = 0; $order < $this->gaps[$gap_id]->getItemCount(); $order++)
1990  {
1991  $answer = $this->gaps[$gap_id]->getItem($order);
1992  if($value["value"] == $answer->getOrder())
1993  {
1994  $answerpoints = $answer->getPoints();
1995  if(!$this->getIdenticalScoring())
1996  {
1997  // check if the same solution value was already entered
1998  if((in_array($answer->getAnswertext(), $solution_values_select)) && ($answerpoints > 0))
1999  {
2000  $answerpoints = 0;
2001  }
2002  }
2003  $points += $answerpoints;
2004  $detailed[$gap_id] = array("points" => $answerpoints, "best" => ($this->getMaximumGapPoints($gap_id) == $answerpoints) ? TRUE : FALSE, "positive" => ($answerpoints > 0) ? TRUE : FALSE);
2005  array_push($solution_values_select, $answer->getAnswertext());
2006  }
2007  }
2008  }
2009  break;
2010  }
2011  }
2012  }
2013 
2014  return $points;
2015  }
2016 
2018  {
2019  $userSolution = array();
2020 
2021  foreach($previewSession->getParticipantsSolution() as $key => $val)
2022  {
2023  $userSolution[] = array('gap_id' => $key, 'value' => $val);
2024  }
2025 
2026  return $this->calculateReachedPointsForSolution($userSolution);
2027  }
2028 }
getId()
Gets the id of the assQuestion object.
toJSON()
Returns a JSON representation of the question.
getGapCount()
Returns the number of gaps.
static _getOriginalId($question_id)
Returns the original id of a question.
formatSAQuestion($a_q)
Format self assessment question.
const TEXTGAP_RATING_LEVENSHTEIN5
Class for cloze question numeric answers.
$size
Definition: RandomTest.php:79
getMaximumGapPoints($gap_index)
Returns the maximum points for a gap.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
getQuestionType()
Returns the question type of the question.
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
Class iQuestionCondition.
$_POST['username']
Definition: cron.php:12
setEndTag($end_tag="[/gap]")
Sets the end tag of a cloze gap.
clearGapAnswers()
Removes all answers from the gaps.
Class for cloze tests.
getAdditionalTableName()
Returns the name of the additional question data table in the database.
saveAnswerSpecificDataToDb()
Save all gaps to the database.
getAnswerTableName()
Returns the name of the answer table in the database.
$result
lmMigrateQuestionTypeSpecificContent(ilAssSelfAssessmentMigrator $migrator)
const TEXTGAP_RATING_LEVENSHTEIN2
const TEXTGAP_RATING_LEVENSHTEIN1
Abstract basic class which is to be extended by the concrete assessment question type classes...
_getPass($active_id)
Retrieves the actual pass of a given user for a given test.
const CLOZE_TEXT
Cloze question constants.
setGapSize($gap_index, $order, $size)
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
_convert_text($a_text, $a_target="has been removed")
calculateCombinationResult($user_result)
getOperators($expression)
Get all available operations for a specific question.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
prepareTextareaOutput($txt_output, $prepare_for_latex_output=FALSE, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output in tests.
setId($id=-1)
Sets the id of the assQuestion object.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
Creates an Excel worksheet for the detailed cumulated results of this question.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second. ...
copyGapCombination($orgID, $newID)
getGap($gap_index=0)
Returns the gap at a given index.
setGapAnswerPoints($gap_index, $order, $points)
Sets the points of a gap with a given index and an answer with a given order.
getTextgapRating()
Returns the rating option for text gaps.
Class for cloze question gaps.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true)
saveToDb($original_id="")
Saves a assClozeTest object to a database.
static strToLower($a_string)
Definition: class.ilStr.php:91
setClozeTextValue($cloze_text="")
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
checkForValidFormula($value)
setNrOfTries($a_nr_of_tries)
_enabledAssessmentLogging()
check wether assessment logging is enabled or not
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
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...
createGapsFromQuestiontext()
Create gap entries by parsing the question text.
getObjId()
Get the object id of the container object.
getShuffle()
Gets the shuffle flag.
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...
getStartTag()
Returns the start tag of a cloze gap.
calculateReachedPoints($active_id, $pass=NULL, $authorized=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
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...
setGapType($gap_index, $gap_type)
Set the type of a gap with a given index.
setGapCombinationsExists($value)
getGaps()
Returns the array of gaps.
setAuthor($author="")
Sets the authors name of the assQuestion object.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assClozeTest.
const CLOZE_SELECT
getAuthor()
Gets the authors name of the assQuestion object.
const TEXTGAP_RATING_LEVENSHTEIN3
$data
$mobs
setGapAnswerLowerBound($gap_index, $order, $bound)
Sets the lower bound of a gap with a given index and an answer with a given order.
Class ilUserQuestionResult.
removeCurrentSolution($active_id, $pass, $authorized=true, $ignoredSolutionIds=array())
setFixedTextLength($a_text_len)
Sets a fixed text length for all text fields in the cloze question.
isValidNumericSubmitValue($submittedValue)
copyObject($target_questionpool_id, $title="")
Copies an assClozeTest object.
const TEXTGAP_RATING_CASESENSITIVE
Interface ilObjAnswerScoringAdjustable.
getQuestion()
Gets the question string of the question object.
addGapAtIndex($gap, $index)
Adds a ClozeGap object at a given index.
static stripSlashes($a_str, $a_strip_html=true, $a_allow="")
strip slashes if magic qoutes is enabled
setGapAnswerUpperBound($gap_index, $order, $bound)
Sets the upper bound of a gap with a given index and an answer with a given order.
saveClozeTextGapRecordToDb($next_id, $key, $item, $gap)
Saves a gap-item record.
saveAdditionalQuestionDataToDb()
Saves the data for the additional data table.
addGapAnswer($gap_index, $order, $answer)
Sets the answer text of a gap with a given index.
setGapShuffle($gap_index=0, $shuffle=1)
Sets the shuffle state of a gap with a given index.
setClozeText($cloze_text="")
Evaluates the text gap solutions from the cloze text.
cleanQuestiontext($text)
Cleans cloze question text to remove attributes or tags from older ILIAS versions.
getExpressionTypes()
Get all available expression types for a specific question.
_getLogLanguage()
retrieve the log language for assessment logging
flushGaps()
Deletes all gaps without changing the cloze text.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
_getMobsOfObject($a_type, $a_id, $a_usage_hist_nr=0, $a_lang="-")
get mobs of object
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
saveClozeNumericGapRecordToDb($next_id, $key, $item, $gap)
Saves a gap-item record.
deleteGap($gap_index)
Deletes a gap with a given index.
updateClozeTextFromGaps()
Updates the gap parameters in the cloze text from the form input.
setTextgapRating($a_textgap_rating)
Sets the rating option for text gaps.
global $ilUser
Definition: imgupload.php:15
setQuestion($question="")
Sets the question string of the question object.
Interface ilObjQuestionScoringAdjustable.
getEndTag()
Returns the end tag of a cloze gap.
setStartTag($start_tag="[gap]")
Sets the start tag of a cloze gap.
addGapText($gap_index)
Adds a new answer text value to a text gap with a given index.
deleteAnswerText($gap_index, $answer_index)
Deletes the answer text of a gap with a given index and an answer with a given order.
getFixedTextLength()
Gets the fixed text length for all text fields in the cloze question.
const TEXTGAP_RATING_LEVENSHTEIN4
global $ilDB
setOriginalId($original_id)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
$text
saveClozeSelectGapRecordToDb($next_id, $key, $item, $gap)
Saves a gap-item record.
getClozeText()
Returns the cloze text.
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
reworkWorkingData($active_id, $pass, $obligationsAnswered)
Reworks the allready saved working data if neccessary.
logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getTitle()
Gets the title string of the assQuestion object.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assClozeTest constructor
loadFromDb($question_id)
Loads a assClozeTest object from a database.
const CLOZE_NUMERIC
isComplete()
Returns TRUE, if a cloze test is complete for use.
getIdenticalScoring()
Returns the identical scoring status of the question.
setTitle($title="")
Sets the title string of the assQuestion object.
setObjId($obj_id=0)
Set the object id of the container object.
setIdenticalScoring($a_identical_scoring)
Sets the identical scoring option for cloze questions.
setComment($comment="")
Sets the comment string of the assQuestion object.
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
saveClozeGapItemsToDb($gap, $key)
Save all items belonging to one cloze gap to the db.
calculateReachedPointsForSolution($user_result, &$detailed=null)
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
const TEXTGAP_RATING_CASEINSENSITIVE