ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assTextSubset.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
5 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
6 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
7 
24 {
32  var $answers;
33 
42 
51 
65  function __construct(
66  $title = "",
67  $comment = "",
68  $author = "",
69  $owner = -1,
70  $question = ""
71  )
72  {
74  $this->answers = array();
75  $this->correctanswers = 0;
76  }
77 
84  function isComplete()
85  {
86  if (strlen($this->title) and ($this->author) and ($this->question) and (count($this->answers) >= $this->correctanswers) and ($this->getMaximumPoints() > 0))
87  {
88  return true;
89  }
90  else
91  {
92  return false;
93  }
94  }
95 
102  function saveToDb($original_id = "")
103  {
104  global $ilDB;
105 
107 
108  // save additional data
109  $affectedRows = $ilDB->manipulateF("DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
110  array("integer"),
111  array($this->getId())
112  );
113 
114  $affectedRows = $ilDB->manipulateF("INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, textgap_rating, correctanswers) VALUES (%s, %s, %s)",
115  array("integer", "text", "integer"),
116  array(
117  $this->getId(),
118  $this->getTextRating(),
119  $this->getCorrectAnswers()
120  )
121  );
122 
123  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_a_textsubset WHERE question_fi = %s",
124  array('integer'),
125  array($this->getId())
126  );
127 
128  foreach ($this->answers as $key => $value)
129  {
130  $answer_obj = $this->answers[$key];
131  $next_id = $ilDB->nextId('qpl_a_textsubset');
132  $query = $ilDB->manipulateF("INSERT INTO qpl_a_textsubset (answer_id, question_fi, answertext, points, aorder, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
133  array('integer', 'integer', 'text', 'float', 'integer', 'integer'),
134  array(
135  $next_id,
136  $this->getId(),
137  $answer_obj->getAnswertext(),
138  $answer_obj->getPoints(),
139  $answer_obj->getOrder(),
140  time()
141  )
142  );
143  }
144 
146  }
147 
155  function loadFromDb($question_id)
156  {
157  global $ilDB;
158 
159  $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",
160  array("integer"),
161  array($question_id)
162  );
163  if ($result->numRows() == 1)
164  {
165  $data = $ilDB->fetchAssoc($result);
166  $this->setId($question_id);
167  $this->setObjId($data["obj_fi"]);
168  $this->setNrOfTries($data['nr_of_tries']);
169  $this->setTitle($data["title"]);
170  $this->setComment($data["description"]);
171  $this->setOriginalId($data["original_id"]);
172  $this->setAuthor($data["author"]);
173  $this->setPoints($data["points"]);
174  $this->setOwner($data["owner"]);
175  include_once("./Services/RTE/classes/class.ilRTE.php");
176  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
177  $this->setCorrectAnswers($data["correctanswers"]);
178  $this->setTextRating($data["textgap_rating"]);
179  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
180  }
181 
182 
183  $result = $ilDB->queryF("SELECT * FROM qpl_a_textsubset WHERE question_fi = %s ORDER BY aorder ASC",
184  array('integer'),
185  array($question_id)
186  );
187  include_once "./Modules/TestQuestionPool/classes/class.assAnswerBinaryStateImage.php";
188  if ($result->numRows() > 0)
189  {
190  while ($data = $ilDB->fetchAssoc($result))
191  {
192  array_push($this->answers, new ASS_AnswerBinaryStateImage($data["answertext"], $data["points"], $data["aorder"]));
193  }
194  }
195 
196  parent::loadFromDb($question_id);
197  }
198 
204  function addAnswer($answertext, $points, $order)
205  {
206  include_once "./Modules/TestQuestionPool/classes/class.assAnswerBinaryStateImage.php";
207  if (array_key_exists($order, $this->answers))
208  {
209  // insert answer
210  $answer = new ASS_AnswerBinaryStateImage($answertext, $points, $order);
211  $newchoices = array();
212  for ($i = 0; $i < $order; $i++)
213  {
214  array_push($newchoices, $this->answers[$i]);
215  }
216  array_push($newchoices, $answer);
217  for ($i = $order; $i < count($this->answers); $i++)
218  {
219  $changed = $this->answers[$i];
220  $changed->setOrder($i+1);
221  array_push($newchoices, $changed);
222  }
223  $this->answers = $newchoices;
224  }
225  else
226  {
227  // add answer
228  array_push($this->answers, new ASS_AnswerBinaryStateImage($answertext, $points, count($this->answers)));
229  }
230  }
231 
237  function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
238  {
239  if ($this->id <= 0)
240  {
241  // The question has not been saved. It cannot be duplicated
242  return;
243  }
244  // duplicate the question in database
245  $this_id = $this->getId();
246 
247  if( (int)$testObjId > 0 )
248  {
249  $thisObjId = $this->getObjId();
250  }
251 
252  $clone = $this;
253  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
255  $clone->id = -1;
256 
257  if( (int)$testObjId > 0 )
258  {
259  $clone->setObjId($testObjId);
260  }
261 
262  if ($title)
263  {
264  $clone->setTitle($title);
265  }
266 
267  if ($author)
268  {
269  $clone->setAuthor($author);
270  }
271  if ($owner)
272  {
273  $clone->setOwner($owner);
274  }
275 
276  if ($for_test)
277  {
278  $clone->saveToDb($original_id);
279  }
280  else
281  {
282  $clone->saveToDb();
283  }
284 
285  // copy question page content
286  $clone->copyPageOfQuestion($this_id);
287  // copy XHTML media objects
288  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
289  // duplicate the generic feedback
290  $clone->duplicateGenericFeedback($this_id);
291 
292  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
293 
294  return $clone->id;
295  }
296 
302  function copyObject($target_questionpool, $title = "")
303  {
304  if ($this->id <= 0)
305  {
306  // The question has not been saved. It cannot be duplicated
307  return;
308  }
309  // duplicate the question in database
310  $clone = $this;
311  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
313  $clone->id = -1;
314  $source_questionpool = $this->getObjId();
315  $clone->setObjId($target_questionpool);
316  if ($title)
317  {
318  $clone->setTitle($title);
319  }
320  $clone->saveToDb();
321 
322  // copy question page content
323  $clone->copyPageOfQuestion($original_id);
324  // copy XHTML media objects
325  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
326  // duplicate the generic feedback
327  $clone->duplicateGenericFeedback($original_id);
328  $clone->onCopy($this->getObjId(), $this->getId());
329 
330  return $clone->id;
331  }
332 
340  function getAnswerCount()
341  {
342  return count($this->answers);
343  }
344 
354  function getAnswer($index = 0)
355  {
356  if ($index < 0) return NULL;
357  if (count($this->answers) < 1) return NULL;
358  if ($index >= count($this->answers)) return NULL;
359 
360  return $this->answers[$index];
361  }
362 
371  function deleteAnswer($index = 0)
372  {
373  if ($index < 0) return;
374  if (count($this->answers) < 1) return;
375  if ($index >= count($this->answers)) return;
376  unset($this->answers[$index]);
377  $this->answers = array_values($this->answers);
378  for ($i = 0; $i < count($this->answers); $i++)
379  {
380  if ($this->answers[$i]->getOrder() > $index)
381  {
382  $this->answers[$i]->setOrder($i);
383  }
384  }
385  }
386 
393  function flushAnswers()
394  {
395  $this->answers = array();
396  }
397 
404  function getMaximumPoints()
405  {
406  $points = array();
407  foreach ($this->answers as $answer)
408  {
409  if ($answer->getPoints() > 0)
410  {
411  array_push($points, $answer->getPoints());
412  }
413  }
414  rsort($points, SORT_NUMERIC);
415  $maxpoints = 0;
416  for ($counter = 0; $counter < $this->getCorrectAnswers(); $counter++)
417  {
418  $maxpoints += $points[$counter];
419  }
420  return $maxpoints;
421  }
422 
430  {
431  $available_answers = array();
432  foreach ($this->answers as $answer)
433  {
434  array_push($available_answers, $answer->getAnswertext());
435  }
436  return $available_answers;
437  }
438 
449  function isAnswerCorrect($answers, $answer)
450  {
451  include_once "./Services/Utilities/classes/class.ilStr.php";
452  $result = 0;
453  $textrating = $this->getTextRating();
454  foreach ($answers as $key => $value)
455  {
456  switch ($textrating)
457  {
459  if (strcmp(ilStr::strToLower($value), ilStr::strToLower($answer)) == 0 && $this->answers[$key]->getPoints() > 0) return $key;
460  break;
462  if (strcmp($value, $answer) == 0 && $this->answers[$key]->getPoints() > 0) return $key;
463  break;
465  if (levenshtein($value, $answer) <= 1 && $this->answers[$key]->getPoints() > 0) return $key;
466  break;
468  if (levenshtein($value, $answer) <= 2 && $this->answers[$key]->getPoints() > 0) return $key;
469  break;
471  if (levenshtein($value, $answer) <= 3 && $this->answers[$key]->getPoints() > 0) return $key;
472  break;
474  if (levenshtein($value, $answer) <= 4 && $this->answers[$key]->getPoints() > 0) return $key;
475  break;
477  if (levenshtein($value, $answer) <= 5 && $this->answers[$key]->getPoints() > 0) return $key;
478  break;
479  }
480  }
481  return FALSE;
482  }
483 
491  function getTextRating()
492  {
493  return $this->text_rating;
494  }
495 
503  function setTextRating($a_text_rating)
504  {
505  switch ($a_text_rating)
506  {
514  $this->text_rating = $a_text_rating;
515  break;
516  default:
517  $this->text_rating = TEXTGAP_RATING_CASEINSENSITIVE;
518  break;
519  }
520  }
521 
532  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
533  {
534  if( $returndetails )
535  {
536  throw new ilTestException('return details not implemented for '.__METHOD__);
537  }
538 
539  global $ilDB;
540 
541  $available_answers =& $this->getAvailableAnswers();
542  $found_counter = 0;
543 
544  if (is_null($pass))
545  {
546  $pass = $this->getSolutionMaxPass($active_id);
547  }
548  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
549  array('integer','integer','integer'),
550  array($active_id, $this->getId(), $pass)
551  );
552  while ($data = $ilDB->fetchAssoc($result))
553  {
554  $enteredtext = $data["value1"];
555  $index = $this->isAnswerCorrect($available_answers, $enteredtext);
556  if ($index !== FALSE)
557  {
558  unset($available_answers[$index]);
559  $points += $this->answers[$index]->getPoints();
560  }
561  }
562 
563  return $points;
564  }
565 
572  function setCorrectAnswers($a_correct_answers)
573  {
574  $this->correctanswers = $a_correct_answers;
575  }
576 
583  function getCorrectAnswers()
584  {
585  return $this->correctanswers;
586  }
587 
596  public function saveWorkingData($active_id, $pass = NULL)
597  {
598  global $ilDB;
599  global $ilUser;
600 
601  if (is_null($pass))
602  {
603  include_once "./Modules/Test/classes/class.ilObjTest.php";
604  $pass = ilObjTest::_getPass($active_id);
605  }
606  $entered_values = 0;
607 
608  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
609  array('integer','integer','integer'),
610  array($active_id, $this->getId(), $pass)
611  );
612  foreach ($_POST as $key => $value)
613  {
614  if (preg_match("/^TEXTSUBSET_(\d+)/", $key, $matches))
615  {
616  if (strlen($value))
617  {
618  $next_id = $ilDB->nextId('tst_solutions');
619  $affectedRows = $ilDB->insert("tst_solutions", array(
620  "solution_id" => array("integer", $next_id),
621  "active_fi" => array("integer", $active_id),
622  "question_fi" => array("integer", $this->getId()),
623  "value1" => array("clob", trim($value)),
624  "value2" => array("clob", null),
625  "pass" => array("integer", $pass),
626  "tstamp" => array("integer", time())
627  ));
628  $entered_values++;
629  }
630  }
631  }
632  if ($entered_values)
633  {
634  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
636  {
637  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
638  }
639  }
640  else
641  {
642  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
644  {
645  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
646  }
647  }
648 
649  return true;
650  }
651 
660  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
661  {
662  // nothing to rework!
663  }
664 
671  function getQuestionType()
672  {
673  return "assTextSubset";
674  }
675 
682  function &joinAnswers()
683  {
684  $join = array();
685  foreach ($this->answers as $answer)
686  {
687  if (!is_array($join[$answer->getPoints() . ""]))
688  {
689  $join[$answer->getPoints() . ""] = array();
690  }
691  array_push($join[$answer->getPoints() . ""], $answer->getAnswertext());
692  }
693  return $join;
694  }
695 
703  {
704  $maxwidth = 0;
705  foreach ($this->answers as $answer)
706  {
707  $len = strlen($answer->getAnswertext());
708  if ($len > $maxwidth) $maxwidth = $len;
709  }
710  return $maxwidth + 3;
711  }
712 
720  {
721  return "qpl_qst_textsubset";
722  }
723 
731  {
732  return "qpl_a_textsubset";
733  }
734 
740  {
742  }
743 
756  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
757  {
758  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
759  $solutions = $this->getSolutionValues($active_id, $pass);
760  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
761  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
762  $i = 1;
763  foreach ($solutions as $solution)
764  {
765  $worksheet->write($startrow + $i, 0, ilExcelUtils::_convert_text($solution["value1"]));
766  $i++;
767  }
768  return $startrow + $i + 1;
769  }
770 
771  public function getAnswers()
772  {
773  return $this->answers;
774  }
775 
779  public function toJSON()
780  {
781  include_once("./Services/RTE/classes/class.ilRTE.php");
782  $result = array();
783  $result['id'] = (int) $this->getId();
784  $result['type'] = (string) $this->getQuestionType();
785  $result['title'] = (string) $this->getTitle();
786  $result['question'] = $this->formatSAQuestion($this->getQuestion());
787  $result['nr_of_tries'] = (int) $this->getNrOfTries();
788  $result['matching_method'] = (string) $this->getTextRating();
789  $result['feedback'] = array(
790  "onenotcorrect" => ilRTE::_replaceMediaObjectImageSrc($this->getFeedbackGeneric(0), 0),
791  "allcorrect" => ilRTE::_replaceMediaObjectImageSrc($this->getFeedbackGeneric(1), 0)
792  );
793 
794  $answers = array();
795  foreach ($this->getAnswers() as $key => $answer_obj)
796  {
797  array_push($answers, array(
798  "answertext" => (string) $answer_obj->getAnswertext(),
799  "points" => (float)$answer_obj->getPoints(),
800  "order" => (int)$answer_obj->getOrder()
801  ));
802  }
803  $result['correct_answers'] = $answers;
804 
805  $answers = array();
806  for($loop = 1; $loop <= (int) $this->getCorrectAnswers(); $loop++)
807  {
808  array_push($answers, array(
809  "answernr" => $loop
810  ));
811  }
812  $result['answers'] = $answers;
813 
814  $mobs = ilObjMediaObject::_getMobsOfObject("qpl:html", $this->getId());
815  $result['mobs'] = $mobs;
816 
817  return json_encode($result);
818  }
819 }
820 
821 ?>