ILIAS  eassessment Revision 61809
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assErrorText.php
Go to the documentation of this file.
1 <?php
2  /*
3  +----------------------------------------------------------------------------+
4  | ILIAS open source |
5  +----------------------------------------------------------------------------+
6  | Copyright (c) 1998-2001 ILIAS open source, University of Cologne |
7  | |
8  | This program is free software; you can redistribute it and/or |
9  | modify it under the terms of the GNU General Public License |
10  | as published by the Free Software Foundation; either version 2 |
11  | of the License, or (at your option) any later version. |
12  | |
13  | This program is distributed in the hope that it will be useful, |
14  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16  | GNU General Public License for more details. |
17  | |
18  | You should have received a copy of the GNU General Public License |
19  | along with this program; if not, write to the Free Software |
20  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21  +----------------------------------------------------------------------------+
22 */
23 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
24 include_once "./Modules/Test/classes/inc.AssessmentConstants.php";
25 
34 {
35  protected $errortext;
36  protected $textsize;
37  protected $errordata;
38  protected $points_wrong;
39 
52  function __construct(
53  $title = "",
54  $comment = "",
55  $author = "",
56  $owner = -1,
57  $question = ""
58  )
59  {
61  $this->errortext = "";
62  $this->textsize = 100.0;
63  $this->errordata = array();
64  }
65 
71  public function isComplete()
72  {
73  if (($this->title) and ($this->author) and ($this->question) and ($this->getMaximumPoints() > 0))
74  {
75  return true;
76  }
77  else
78  {
79  return false;
80  }
81  }
82 
87  public function saveToDb($original_id = "")
88  {
89  global $ilDB;
90 
92 
93  // save additional data
94  $affectedRows = $ilDB->manipulateF("DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
95  array("integer"),
96  array($this->getId())
97  );
98 
99  $affectedRows = $ilDB->manipulateF("INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, errortext, textsize, points_wrong) VALUES (%s, %s, %s, %s)",
100  array("integer", "text", "float", "float"),
101  array(
102  $this->getId(),
103  $this->getErrorText(),
104  $this->getTextSize(),
105  $this->getPointsWrong()
106  )
107  );
108 
109  $affectedRows = $ilDB->manipulateF("DELETE FROM qpl_a_errortext WHERE question_fi = %s",
110  array('integer'),
111  array($this->getId())
112  );
113 
114  $sequence = 0;
115  foreach ($this->errordata as $object)
116  {
117  $next_id = $ilDB->nextId('qpl_a_errortext');
118  $affectedRows = $ilDB->manipulateF("INSERT INTO qpl_a_errortext (answer_id, question_fi, text_wrong, text_correct, points, sequence) VALUES (%s, %s, %s, %s, %s, %s)",
119  array('integer','integer','text','text','float', 'integer'),
120  array(
121  $next_id,
122  $this->getId(),
123  $object->text_wrong,
124  $object->text_correct,
125  $object->points,
126  $sequence++
127  )
128  );
129  }
130 
132  }
133 
140  public function loadFromDb($question_id)
141  {
142  global $ilDB;
143 
144  $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",
145  array("integer"),
146  array($question_id)
147  );
148  if ($result->numRows() == 1)
149  {
150  $data = $ilDB->fetchAssoc($result);
151  $this->setId($question_id);
152  $this->setObjId($data["obj_fi"]);
153  $this->setTitle($data["title"]);
154  $this->setComment($data["description"]);
155  $this->setOriginalId($data["original_id"]);
156  $this->setNrOfTries($data['nr_of_tries']);
157  $this->setAuthor($data["author"]);
158  $this->setPoints($data["points"]);
159  $this->setOwner($data["owner"]);
160  include_once("./Services/RTE/classes/class.ilRTE.php");
161  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
162  $this->setErrorText($data["errortext"]);
163  $this->setTextSize($data["textsize"]);
164  $this->setPointsWrong($data["points_wrong"]);
165  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
166  }
167 
168  $result = $ilDB->queryF("SELECT * FROM qpl_a_errortext WHERE question_fi = %s ORDER BY sequence ASC",
169  array('integer'),
170  array($question_id)
171  );
172  include_once "./Modules/TestQuestionPool/classes/class.assAnswerErrorText.php";
173  if ($result->numRows() > 0)
174  {
175  while ($data = $ilDB->fetchAssoc($result))
176  {
177  array_push($this->errordata, new assAnswerErrorText($data["text_wrong"], $data["text_correct"], $data["points"]));
178  }
179  }
180 
181  parent::loadFromDb($question_id);
182  }
183 
187  public function duplicate($for_test = true, $title = "", $author = "", $owner = "")
188  {
189  if ($this->id <= 0)
190  {
191  // The question has not been saved. It cannot be duplicated
192  return;
193  }
194  // duplicate the question in database
195  $this_id = $this->getId();
196  $clone = $this;
197  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
199  $clone->id = -1;
200  if ($title)
201  {
202  $clone->setTitle($title);
203  }
204 
205  if ($author)
206  {
207  $clone->setAuthor($author);
208  }
209  if ($owner)
210  {
211  $clone->setOwner($owner);
212  }
213 
214  if ($for_test)
215  {
216  $clone->saveToDb($original_id);
217  }
218  else
219  {
220  $clone->saveToDb();
221  }
222  // copy question page content
223  $clone->copyPageOfQuestion($this_id);
224  // copy XHTML media objects
225  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
226  // duplicate the generic feedback
227  $clone->duplicateFeedbackGeneric($this_id);
228 
229  $clone->onDuplicate($this_id);
230  return $clone->id;
231  }
232 
236  public function copyObject($target_questionpool, $title = "")
237  {
238  if ($this->id <= 0)
239  {
240  // The question has not been saved. It cannot be duplicated
241  return;
242  }
243  // duplicate the question in database
244  $clone = $this;
245  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
247  $clone->id = -1;
248  $source_questionpool = $this->getObjId();
249  $clone->setObjId($target_questionpool);
250  if ($title)
251  {
252  $clone->setTitle($title);
253  }
254  $clone->saveToDb();
255 
256  // copy question page content
257  $clone->copyPageOfQuestion($original_id);
258  // copy XHTML media objects
259  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
260  // duplicate the generic feedback
261  $clone->duplicateFeedbackGeneric($original_id);
262 
263  $clone->onCopy($this->getObjId(), $this->getId());
264 
265  return $clone->id;
266  }
267 
273  public function getMaximumPoints()
274  {
275  $maxpoints = 0.0;
276  foreach ($this->errordata as $object)
277  {
278  if ($object->points > 0) $maxpoints += $object->points;
279  }
280  return $maxpoints;
281  }
282 
291  public function calculateReachedPoints($active_id, $pass = NULL)
292  {
293  global $ilDB;
294 
295  $found_values = array();
296  if (is_null($pass))
297  {
298  $pass = $this->getSolutionMaxPass($active_id);
299  }
300  $result = $ilDB->queryF("SELECT value1 FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
301  array('integer','integer','integer'),
302  array($active_id, $this->getId(), $pass)
303  );
304  while ($row = $ilDB->fetchAssoc($result))
305  {
306  array_push($found_values, $row['value1']);
307  }
308  $points = $this->getPointsForSelectedPositions($found_values);
309  $points = parent::calculateReachedPoints($active_id, $pass = NULL, $points);
310  return $points;
311  }
312 
313  /*
314  * Change the selection during a test when a user selects/deselects a word without using javascript
315  */
316  public function toggleSelection($position, $active_id, $pass = null)
317  {
318  global $ilDB;
319  global $ilUser;
320 
321  if (is_null($pass))
322  {
323  include_once "./Modules/Test/classes/class.ilObjTest.php";
324  $pass = ilObjTest::_getPass($active_id);
325  }
326 
327  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s AND value1 = %s",
328  array('integer','integer','integer', 'text'),
329  array($active_id, $this->getId(), $pass, $position)
330  );
331  if ($affectedRows == 0)
332  {
333  $next_id = $ilDB->nextId('tst_solutions');
334  $affectedRows = $ilDB->insert("tst_solutions", array(
335  "solution_id" => array("integer", $next_id),
336  "active_fi" => array("integer", $active_id),
337  "question_fi" => array("integer", $this->getId()),
338  "value1" => array("clob", $position),
339  "value2" => array("clob", null),
340  "pass" => array("integer", $pass),
341  "tstamp" => array("integer", time())
342  ));
343  }
344  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
346  {
347  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
348  }
349  $this->calculateReachedPoints($active_id, $pass);
350  }
351 
359  public function saveWorkingData($active_id, $pass = NULL)
360  {
361  global $ilDB;
362  global $ilUser;
363 
364  if (is_null($pass))
365  {
366  include_once "./Modules/Test/classes/class.ilObjTest.php";
367  $pass = ilObjTest::_getPass($active_id);
368  }
369 
370  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
371  array('integer','integer','integer'),
372  array($active_id, $this->getId(), $pass)
373  );
374 
375  $entered_values = false;
376  if (strlen($_POST["qst_" . $this->getId()]))
377  {
378  $selected = split(",", $_POST["qst_" . $this->getId()]);
379  foreach ($selected as $position)
380  {
381  $next_id = $ilDB->nextId('tst_solutions');
382  $affectedRows = $ilDB->insert("tst_solutions", array(
383  "solution_id" => array("integer", $next_id),
384  "active_fi" => array("integer", $active_id),
385  "question_fi" => array("integer", $this->getId()),
386  "value1" => array("clob", $position),
387  "value2" => array("clob", null),
388  "pass" => array("integer", $pass),
389  "tstamp" => array("integer", time())
390  ));
391  }
392  $entered_values = true;
393  }
394  if ($entered_values)
395  {
396  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
398  {
399  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
400  }
401  }
402  else
403  {
404  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
406  {
407  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
408  }
409  }
410  parent::saveWorkingData($active_id, $pass);
411  return true;
412  }
413 
419  public function getQuestionType()
420  {
421  return "assErrorText";
422  }
423 
429  public function getAdditionalTableName()
430  {
431  return "qpl_qst_errortext";
432  }
433 
439  public function getAnswerTableName()
440  {
441  return "";
442  }
443 
449  public function deleteAnswers($question_id)
450  {
451  }
452 
457  public function getRTETextWithMediaObjects()
458  {
460  return $text;
461  }
462 
471  public function setExportDetailsXLS(&$adapter, $startrow, $active_id, $pass)
472  {
473  $adapter->setCellValue($startrow, 0, $this->lng->txt($this->getQuestionType()), CELL_FORMAT_TITLE);
474  $adapter->setCellValue($startrow, 1, $this->getTitle(), CELL_FORMAT_TITLE);
475 // $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
476 // $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
477 
478  $i= 0;
479  $selections = array();
480  $solutions =& $this->getSolutionValues($active_id, $pass);
481  if (is_array($solutions))
482  {
483  foreach ($solutions as $solution)
484  {
485  array_push($selections, $solution['value1']);
486  }
487  $errortext_value = join(",", $selections);
488  }
489  $errortext = $this->createErrorTextExport($selections);
490  $errortext = preg_replace("/#HREF\d+/is", "javascript:void(0);", $errortext);
491  $i++;
492  $adapter->setCellValue($startrow+$i, 0, $errortext);
493 // $worksheet->writeString($startrow+$i, 0, ilExcelUtils::_convert_text($errortext));
494  $i++;
495  return $startrow + $i + 1;
496  }
497 
510  public function fromXML(&$item, &$questionpool_id, &$tst_id, &$tst_object, &$question_counter, &$import_mapping)
511  {
512  include_once "./Modules/TestQuestionPool/classes/import/qti12/class.assErrorTextImport.php";
513  $import = new assErrorTextImport($this);
514  $import->fromXML($item, $questionpool_id, $tst_id, $tst_object, $question_counter, $import_mapping);
515  }
516 
523  public function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
524  {
525  include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assErrorTextExport.php";
526  $export = new assErrorTextExport($this);
527  return $export->toXML($a_include_header, $a_include_binary, $a_shuffle, $test_output, $force_image_references);
528  }
529 
535  public function getBestSolution($active_id, $pass)
536  {
537  $user_solution = array();
538  return $user_solution;
539  }
540 
541  public function getErrorsFromText($a_text = "")
542  {
543  if (strlen($a_text) == 0) $a_text = $this->getErrorText();
544  preg_match_all("/#([^\s]+)/is", $a_text, $matches);
545  if (is_array($matches[1]))
546  {
547  return $matches[1];
548  }
549  else
550  {
551  return array();
552  }
553  }
554 
555  public function setErrorData($a_data)
556  {
557  include_once "./Modules/TestQuestionPool/classes/class.assAnswerErrorText.php";
558  $temp = $this->errordata;
559  $this->errordata = array();
560  foreach ($a_data as $error)
561  {
562  $text_correct = "";
563  $points = 0.0;
564  foreach ($temp as $object)
565  {
566  if (strcmp($object->text_wrong, $error) == 0)
567  {
568  $text_correct = $object->text_correct;
569  $points = $object->points;
570  continue;
571  }
572  }
573  array_push($this->errordata, new assAnswerErrorText($error, $text_correct, $points));
574  }
575  }
576 
577  public function createErrorTextOutput($selections = null, $graphicalOutput = false, $correct_solution = false)
578  {
579  $counter = 0;
580  $errorcounter = 0;
581  include_once "./Services/Utilities/classes/class.ilStr.php";
582  if (!is_array($selections)) $selections = array();
583  $textarray = preg_split("/[\n\r]+/", $this->getErrorText());
584  foreach ($textarray as $textidx => $text)
585  {
586  $items = preg_split("/\s+/", $text);
587  foreach ($items as $idx => $item)
588  {
589  if (strpos($item, '#') === 0)
590  {
591  $item = ilStr::substr($item, 1, ilStr::strlen($item));
592  if ($correct_solution)
593  {
594  $errorobject = $this->errordata[$errorcounter];
595  if (is_object($errorobject))
596  {
597  $item = $errorobject->text_correct;
598  }
599  $errorcounter++;
600  }
601  }
602  $class = '';
603  $img = '';
604  if (in_array($counter, $selections))
605  {
606  $class = ' class="sel"';
607  if ($graphicalOutput)
608  {
609  if ($this->getPointsForSelectedPositions(array($counter)) >= 0)
610  {
611  $img = ' <img src="' . ilUtil::getImagePath("icon_ok.gif") . '" alt="' . $this->lng->txt("answer_is_right") . '" title="' . $this->lng->txt("answer_is_right") . '" /> ';
612  }
613  else
614  {
615  $img = ' <img src="' . ilUtil::getImagePath("icon_not_ok.gif") . '" alt="' . $this->lng->txt("answer_is_wrong") . '" title="' . $this->lng->txt("answer_is_wrong") . '" /> ';
616  }
617  }
618  }
619  $items[$idx] = '<a' . $class . ' href="#HREF' . $idx . '" onclick="javascript: return false;">' . ilUtil::prepareFormOutput($item) . '</a>' . $img;
620  $counter++;
621  }
622  $textarray[$textidx] = '<p>' . join($items, " ") . '</p>';
623  }
624  return join($textarray, "\n");
625  }
626 
627  public function createErrorTextExport($selections = null)
628  {
629  $counter = 0;
630  $errorcounter = 0;
631  include_once "./Services/Utilities/classes/class.ilStr.php";
632  if (!is_array($selections)) $selections = array();
633  $textarray = preg_split("/[\n\r]+/", $this->getErrorText());
634  foreach ($textarray as $textidx => $text)
635  {
636  $items = preg_split("/\s+/", $text);
637  foreach ($items as $idx => $item)
638  {
639  if (strpos($item, '#') === 0)
640  {
641  $item = ilStr::substr($item, 1, ilStr::strlen($item));
642  if ($correct_solution)
643  {
644  $errorobject = $this->errordata[$errorcounter];
645  if (is_object($errorobject))
646  {
647  $item = $errorobject->text_correct;
648  }
649  $errorcounter++;
650  }
651  }
652  $word = "";
653  if (in_array($counter, $selections))
654  {
655  $word .= '#';
656  }
657  $word .= ilUtil::prepareFormOutput($item);
658  if (in_array($counter, $selections))
659  {
660  $word .= '#';
661  }
662  $items[$idx] = $word;
663  $counter++;
664  }
665  $textarray[$textidx] = join($items, " ");
666  }
667  return join($textarray, "\n");
668  }
669 
670  public function getBestSelection()
671  {
672  $words = array();
673  $counter = 0;
674  $errorcounter = 0;
675  $textarray = preg_split("/[\n\r]+/", $this->getErrorText());
676  foreach ($textarray as $textidx => $text)
677  {
678  $items = preg_split("/\s+/", $text);
679  foreach ($items as $word)
680  {
681  $points = $this->getPointsWrong();
682  if (strpos($word, '#') === 0)
683  {
684  $errorobject = $this->errordata[$errorcounter];
685  if (is_object($errorobject))
686  {
687  $points = $errorobject->points;
688  }
689  $errorcounter++;
690  }
691  $words[$counter] = array("word" => $word, "points" => $points);
692  $counter++;
693  }
694  }
695  $selections = array();
696  foreach ($words as $idx => $word)
697  {
698  if ($word['points'] > 0)
699  {
700  array_push($selections, $idx);
701  }
702  }
703  return $selections;
704  }
705 
706  protected function getPointsForSelectedPositions($positions)
707  {
708  $words = array();
709  $counter = 0;
710  $errorcounter = 0;
711  $textarray = preg_split("/[\n\r]+/", $this->getErrorText());
712  foreach ($textarray as $textidx => $text)
713  {
714  $items = preg_split("/\s+/", $text);
715  foreach ($items as $word)
716  {
717  $points = $this->getPointsWrong();
718  if (strpos($word, '#') === 0)
719  {
720  $errorobject = $this->errordata[$errorcounter];
721  if (is_object($errorobject))
722  {
723  $points = $errorobject->points;
724  }
725  $errorcounter++;
726  }
727  $words[$counter] = array("word" => $word, "points" => $points);
728  $counter++;
729  }
730  }
731  $total = 0;
732  foreach ($positions as $position)
733  {
734  $total += $words[$position]['points'];
735  }
736  return $total;
737  }
738 
742  public function flushErrorData()
743  {
744  $this->errordata = array();
745  }
746 
747  public function addErrorData($text_wrong, $text_correct, $points)
748  {
749  include_once "./Modules/TestQuestionPool/classes/class.assAnswerErrorText.php";
750  array_push($this->errordata, new assAnswerErrorText($text_wrong, $text_correct, $points));
751  }
752 
758  public function getErrorData()
759  {
760  return $this->errordata;
761  }
762 
768  public function getErrorText()
769  {
770  return $this->errortext;
771  }
772 
778  public function setErrorText($a_value)
779  {
780  $this->errortext = $a_value;
781  }
782 
788  public function getTextSize()
789  {
790  return $this->textsize;
791  }
792 
798  public function setTextSize($a_value)
799  {
800  $this->textsize = $a_value;
801  }
802 
808  public function getPointsWrong()
809  {
810  return $this->points_wrong;
811  }
812 
818  public function setPointsWrong($a_value)
819  {
820  $this->points_wrong = $a_value;
821  }
822 
826  public function __get($value)
827  {
828  switch ($value)
829  {
830  case "errortext":
831  return $this->getErrorText();
832  break;
833  case "textsize":
834  return $this->getTextSize();
835  break;
836  case "points_wrong":
837  return $this->getPointsWrong();
838  break;
839  default:
840  return parent::__get($value);
841  break;
842  }
843  }
844 
848  public function __set($key, $value)
849  {
850  switch ($key)
851  {
852  case "errortext":
853  $this->setErrorText($value);
854  break;
855  case "textsize":
856  $this->setTextSize($value);
857  break;
858  case "points_wrong":
859  $this->setPointsWrong($value);
860  break;
861  default:
862  parent::__set($key, $value);
863  break;
864  }
865  }
866 }
867 
868 ?>