ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assTextSubset.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/interfaces/interface.ilObjQuestionScoringAdjustable.php';
7 require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
8 
25 {
33  var $answers;
34 
43 
52 
64  public function __construct(
65  $title = "",
66  $comment = "",
67  $author = "",
68  $owner = -1,
69  $question = ""
70  )
71  {
73  $this->answers = array();
74  $this->correctanswers = 0;
75  }
76 
83  function isComplete()
84  {
85  if (
86  strlen($this->title)
87  && $this->author
88  && $this->question &&
89  count($this->answers) >= $this->correctanswers
90  && $this->getMaximumPoints() > 0
91  )
92  {
93  return true;
94  }
95  return false;
96  }
97 
104  public function saveToDb($original_id = "")
105  {
106  global $ilDB;
107 
111 
113  }
114 
122  function loadFromDb($question_id)
123  {
124  global $ilDB;
125 
126  $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",
127  array("integer"),
128  array($question_id)
129  );
130  if ($result->numRows() == 1)
131  {
132  $data = $ilDB->fetchAssoc($result);
133  $this->setId($question_id);
134  $this->setObjId($data["obj_fi"]);
135  $this->setNrOfTries($data['nr_of_tries']);
136  $this->setTitle($data["title"]);
137  $this->setComment($data["description"]);
138  $this->setOriginalId($data["original_id"]);
139  $this->setAuthor($data["author"]);
140  $this->setPoints($data["points"]);
141  $this->setOwner($data["owner"]);
142  include_once("./Services/RTE/classes/class.ilRTE.php");
143  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
144  $this->setCorrectAnswers($data["correctanswers"]);
145  $this->setTextRating($data["textgap_rating"]);
146  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
147 
148  try
149  {
150  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
151  }
153  {
154  }
155  }
156 
157 
158  $result = $ilDB->queryF("SELECT * FROM qpl_a_textsubset WHERE question_fi = %s ORDER BY aorder ASC",
159  array('integer'),
160  array($question_id)
161  );
162  include_once "./Modules/TestQuestionPool/classes/class.assAnswerBinaryStateImage.php";
163  if ($result->numRows() > 0)
164  {
165  while ($data = $ilDB->fetchAssoc($result))
166  {
167  array_push($this->answers, new ASS_AnswerBinaryStateImage($data["answertext"], $data["points"], $data["aorder"]));
168  }
169  }
170 
171  parent::loadFromDb($question_id);
172  }
173 
179  function addAnswer($answertext, $points, $order)
180  {
181  include_once "./Modules/TestQuestionPool/classes/class.assAnswerBinaryStateImage.php";
182  if (array_key_exists($order, $this->answers))
183  {
184  // insert answer
185  $answer = new ASS_AnswerBinaryStateImage($answertext, $points, $order);
186  $newchoices = array();
187  for ($i = 0; $i < $order; $i++)
188  {
189  array_push($newchoices, $this->answers[$i]);
190  }
191  array_push($newchoices, $answer);
192  for ($i = $order; $i < count($this->answers); $i++)
193  {
194  $changed = $this->answers[$i];
195  $changed->setOrder($i+1);
196  array_push($newchoices, $changed);
197  }
198  $this->answers = $newchoices;
199  }
200  else
201  {
202  // add answer
203  array_push($this->answers, new ASS_AnswerBinaryStateImage($answertext, $points, count($this->answers)));
204  }
205  }
206 
212  function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
213  {
214  if ($this->id <= 0)
215  {
216  // The question has not been saved. It cannot be duplicated
217  return;
218  }
219  // duplicate the question in database
220  $this_id = $this->getId();
221  $thisObjId = $this->getObjId();
222 
223  $clone = $this;
224  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
226  $clone->id = -1;
227 
228  if( (int)$testObjId > 0 )
229  {
230  $clone->setObjId($testObjId);
231  }
232 
233  if ($title)
234  {
235  $clone->setTitle($title);
236  }
237 
238  if ($author)
239  {
240  $clone->setAuthor($author);
241  }
242  if ($owner)
243  {
244  $clone->setOwner($owner);
245  }
246 
247  if ($for_test)
248  {
249  $clone->saveToDb($original_id);
250  }
251  else
252  {
253  $clone->saveToDb();
254  }
255 
256  // copy question page content
257  $clone->copyPageOfQuestion($this_id);
258  // copy XHTML media objects
259  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
260 
261  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
262 
263  return $clone->id;
264  }
265 
271  function copyObject($target_questionpool_id, $title = "")
272  {
273  if ($this->id <= 0)
274  {
275  // The question has not been saved. It cannot be duplicated
276  return;
277  }
278  // duplicate the question in database
279  $clone = $this;
280  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
282  $clone->id = -1;
283  $source_questionpool_id = $this->getObjId();
284  $clone->setObjId($target_questionpool_id);
285  if ($title)
286  {
287  $clone->setTitle($title);
288  }
289  $clone->saveToDb();
290  // copy question page content
291  $clone->copyPageOfQuestion($original_id);
292  // copy XHTML media objects
293  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
294 
295  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
296 
297  return $clone->id;
298  }
299 
300  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
301  {
302  if ($this->id <= 0)
303  {
304  // The question has not been saved. It cannot be duplicated
305  return;
306  }
307 
308  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
309 
310  $sourceQuestionId = $this->id;
311  $sourceParentId = $this->getObjId();
312 
313  // duplicate the question in database
314  $clone = $this;
315  $clone->id = -1;
316 
317  $clone->setObjId($targetParentId);
318 
319  if ($targetQuestionTitle)
320  {
321  $clone->setTitle($targetQuestionTitle);
322  }
323 
324  $clone->saveToDb();
325  // copy question page content
326  $clone->copyPageOfQuestion($sourceQuestionId);
327  // copy XHTML media objects
328  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
329 
330  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
331 
332  return $clone->id;
333  }
334 
342  function getAnswerCount()
343  {
344  return count($this->answers);
345  }
346 
356  function getAnswer($index = 0)
357  {
358  if ($index < 0) return NULL;
359  if (count($this->answers) < 1) return NULL;
360  if ($index >= count($this->answers)) return NULL;
361 
362  return $this->answers[$index];
363  }
364 
373  function deleteAnswer($index = 0)
374  {
375  if ($index < 0) return;
376  if (count($this->answers) < 1) return;
377  if ($index >= count($this->answers)) return;
378  unset($this->answers[$index]);
379  $this->answers = array_values($this->answers);
380  for ($i = 0; $i < count($this->answers); $i++)
381  {
382  if ($this->answers[$i]->getOrder() > $index)
383  {
384  $this->answers[$i]->setOrder($i);
385  }
386  }
387  }
388 
395  function flushAnswers()
396  {
397  $this->answers = array();
398  }
399 
406  function getMaximumPoints()
407  {
408  $points = array();
409  foreach ($this->answers as $answer)
410  {
411  if ($answer->getPoints() > 0)
412  {
413  array_push($points, $answer->getPoints());
414  }
415  }
416  rsort($points, SORT_NUMERIC);
417  $maxpoints = 0;
418  for ($counter = 0; $counter < $this->getCorrectAnswers(); $counter++)
419  {
420  $maxpoints += $points[$counter];
421  }
422  return $maxpoints;
423  }
424 
432  {
433  $available_answers = array();
434  foreach ($this->answers as $answer)
435  {
436  array_push($available_answers, $answer->getAnswertext());
437  }
438  return $available_answers;
439  }
440 
451  function isAnswerCorrect($answers, $answer)
452  {
453  include_once "./Services/Utilities/classes/class.ilStr.php";
454  $result = 0;
455  $textrating = $this->getTextRating();
456  foreach ($answers as $key => $value)
457  {
458  switch ($textrating)
459  {
461  if (strcmp(ilStr::strToLower($value), ilStr::strToLower($answer)) == 0 && $this->answers[$key]->getPoints() > 0) return $key;
462  break;
464  if (strcmp($value, $answer) == 0 && $this->answers[$key]->getPoints() > 0) return $key;
465  break;
467  if (levenshtein($value, $answer) <= 1 && $this->answers[$key]->getPoints() > 0) return $key;
468  break;
470  if (levenshtein($value, $answer) <= 2 && $this->answers[$key]->getPoints() > 0) return $key;
471  break;
473  if (levenshtein($value, $answer) <= 3 && $this->answers[$key]->getPoints() > 0) return $key;
474  break;
476  if (levenshtein($value, $answer) <= 4 && $this->answers[$key]->getPoints() > 0) return $key;
477  break;
479  if (levenshtein($value, $answer) <= 5 && $this->answers[$key]->getPoints() > 0) return $key;
480  break;
481  }
482  }
483  return FALSE;
484  }
485 
493  function getTextRating()
494  {
495  return $this->text_rating;
496  }
497 
505  function setTextRating($a_text_rating)
506  {
507  switch ($a_text_rating)
508  {
516  $this->text_rating = $a_text_rating;
517  break;
518  default:
519  $this->text_rating = TEXTGAP_RATING_CASEINSENSITIVE;
520  break;
521  }
522  }
523 
534  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
535  {
536  if( $returndetails )
537  {
538  throw new ilTestException('return details not implemented for '.__METHOD__);
539  }
540 
541  global $ilDB;
542 
543  $available_answers =& $this->getAvailableAnswers();
544  $found_counter = 0;
545 
546  if (is_null($pass))
547  {
548  $pass = $this->getSolutionMaxPass($active_id);
549  }
550  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
551  array('integer','integer','integer'),
552  array($active_id, $this->getId(), $pass)
553  );
554  while ($data = $ilDB->fetchAssoc($result))
555  {
556  $enteredtext = $data["value1"];
557  $index = $this->isAnswerCorrect($available_answers, $enteredtext);
558  if ($index !== FALSE)
559  {
560  unset($available_answers[$index]);
561  $points += $this->answers[$index]->getPoints();
562  }
563  }
564 
565  return $points;
566  }
567 
574  function setCorrectAnswers($a_correct_answers)
575  {
576  $this->correctanswers = $a_correct_answers;
577  }
578 
585  function getCorrectAnswers()
586  {
587  return $this->correctanswers;
588  }
589 
598  public function saveWorkingData($active_id, $pass = NULL)
599  {
600  global $ilDB;
601  global $ilUser;
602 
603  if (is_null($pass))
604  {
605  include_once "./Modules/Test/classes/class.ilObjTest.php";
606  $pass = ilObjTest::_getPass($active_id);
607  }
608  $entered_values = 0;
609 
610  $this->getProcessLocker()->requestUserSolutionUpdateLock();
611 
612  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
613  array('integer','integer','integer'),
614  array($active_id, $this->getId(), $pass)
615  );
616  require_once 'Services/Utilities/classes/class.ilUtil.php';
617  foreach ($_POST as $key => $value)
618  {
619  if (preg_match("/^TEXTSUBSET_(\d+)/", $key, $matches))
620  {
621  if (strlen($value))
622  {
623  $value = ilUtil::secureString($value);
624  $next_id = $ilDB->nextId('tst_solutions');
625  $affectedRows = $ilDB->insert("tst_solutions", array(
626  "solution_id" => array("integer", $next_id),
627  "active_fi" => array("integer", $active_id),
628  "question_fi" => array("integer", $this->getId()),
629  "value1" => array("clob", trim($value)),
630  "value2" => array("clob", null),
631  "pass" => array("integer", $pass),
632  "tstamp" => array("integer", time())
633  ));
634  $entered_values++;
635  }
636  }
637  }
638 
639  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
640 
641  if ($entered_values)
642  {
643  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
645  {
646  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
647  }
648  }
649  else
650  {
651  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
653  {
654  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
655  }
656  }
657 
658  return true;
659  }
660 
661  public function saveAdditionalQuestionDataToDb()
662  {
664  global $ilDB;
665 
666  // save additional data
667  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
668  array( "integer" ),
669  array( $this->getId() )
670  );
671 
672  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName(
673  ) . " (question_fi, textgap_rating, correctanswers) VALUES (%s, %s, %s)",
674  array( "integer", "text", "integer" ),
675  array(
676  $this->getId(),
677  $this->getTextRating(),
678  $this->getCorrectAnswers()
679  )
680  );
681  }
682 
683  public function saveAnswerSpecificDataToDb()
684  {
686  global $ilDB;
687  $ilDB->manipulateF( "DELETE FROM qpl_a_textsubset WHERE question_fi = %s",
688  array( 'integer' ),
689  array( $this->getId() )
690  );
691 
692  foreach ($this->answers as $key => $value)
693  {
694  $answer_obj = $this->answers[$key];
695  $next_id = $ilDB->nextId( 'qpl_a_textsubset' );
696  $ilDB->manipulateF( "INSERT INTO qpl_a_textsubset (answer_id, question_fi, answertext, points, aorder, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
697  array( 'integer', 'integer', 'text', 'float', 'integer', 'integer' ),
698  array(
699  $next_id,
700  $this->getId(),
701  $answer_obj->getAnswertext(),
702  $answer_obj->getPoints(),
703  $answer_obj->getOrder(),
704  time()
705  )
706  );
707  }
708  }
709 
718  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
719  {
720  // nothing to rework!
721  }
722 
729  function getQuestionType()
730  {
731  return "assTextSubset";
732  }
733 
740  function &joinAnswers()
741  {
742  $join = array();
743  foreach ($this->answers as $answer)
744  {
745  if (!is_array($join[$answer->getPoints() . ""]))
746  {
747  $join[$answer->getPoints() . ""] = array();
748  }
749  array_push($join[$answer->getPoints() . ""], $answer->getAnswertext());
750  }
751  return $join;
752  }
753 
761  {
762  $maxwidth = 0;
763  foreach ($this->answers as $answer)
764  {
765  $len = strlen($answer->getAnswertext());
766  if ($len > $maxwidth) $maxwidth = $len;
767  }
768  return $maxwidth + 3;
769  }
770 
778  {
779  return "qpl_qst_textsubset";
780  }
781 
789  {
790  return "qpl_a_textsubset";
791  }
792 
798  {
800  }
801 
814  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
815  {
816  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
817  $solutions = $this->getSolutionValues($active_id, $pass);
818  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
819  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
820  $i = 1;
821  foreach ($solutions as $solution)
822  {
823  $worksheet->write($startrow + $i, 0, ilExcelUtils::_convert_text($solution["value1"]));
824  $i++;
825  }
826  return $startrow + $i + 1;
827  }
828 
829  public function getAnswers()
830  {
831  return $this->answers;
832  }
833 
837  public function toJSON()
838  {
839  include_once("./Services/RTE/classes/class.ilRTE.php");
840  $result = array();
841  $result['id'] = (int) $this->getId();
842  $result['type'] = (string) $this->getQuestionType();
843  $result['title'] = (string) $this->getTitle();
844  $result['question'] = $this->formatSAQuestion($this->getQuestion());
845  $result['nr_of_tries'] = (int) $this->getNrOfTries();
846  $result['matching_method'] = (string) $this->getTextRating();
847  $result['feedback'] = array(
848  "onenotcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false),
849  "allcorrect" => $this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true)
850  );
851 
852  $answers = array();
853  foreach ($this->getAnswers() as $key => $answer_obj)
854  {
855  array_push($answers, array(
856  "answertext" => (string) $answer_obj->getAnswertext(),
857  "points" => (float)$answer_obj->getPoints(),
858  "order" => (int)$answer_obj->getOrder()
859  ));
860  }
861  $result['correct_answers'] = $answers;
862 
863  $answers = array();
864  for($loop = 1; $loop <= (int) $this->getCorrectAnswers(); $loop++)
865  {
866  array_push($answers, array(
867  "answernr" => $loop
868  ));
869  }
870  $result['answers'] = $answers;
871 
872  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
873  $result['mobs'] = $mobs;
874 
875  return json_encode($result);
876  }
877 }
878 
879 ?>