ILIAS  Release_4_2_x_branch Revision 61807
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assTextQuestion.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 
36 {
45 
54  var $keywords;
55 
62 
63  /* method for automatic string matching */
64  private $matchcondition;
65 
79  function __construct(
80  $title = "",
81  $comment = "",
82  $author = "",
83  $owner = -1,
84  $question = ""
85  )
86  {
88  $this->maxNumOfChars = 0;
89  $this->points = 0;
90  $this->keywords = "";
91  $this->matchcondition = 0;
92  }
93 
100  function isComplete()
101  {
102  if (strlen($this->title) and ($this->author) and ($this->question) and ($this->getMaximumPoints() > 0))
103  {
104  return true;
105  }
106  else
107  {
108  return false;
109  }
110  }
111 
118  function saveToDb($original_id = "")
119  {
120  global $ilDB;
121 
123 
124  // save additional data
125  $affectedRows = $ilDB->manipulateF("DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
126  array("integer"),
127  array($this->getId())
128  );
129 
130  $affectedRows = $ilDB->manipulateF("INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, maxnumofchars, keywords, textgap_rating, matchcondition) VALUES (%s, %s, %s, %s, %s)",
131  array("integer", "integer", "text", "text", 'integer'),
132  array(
133  $this->getId(),
134  $this->getMaxNumOfChars(),
135  (strlen($this->getKeywords())) ? $this->getKeywords() : NULL,
136  $this->getTextRating(),
137  $this->matchcondition
138  )
139  );
140 
142  }
143 
151  function loadFromDb($question_id)
152  {
153  global $ilDB;
154 
155  $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",
156  array("integer"),
157  array($question_id)
158  );
159  if ($result->numRows() == 1)
160  {
161  $data = $ilDB->fetchAssoc($result);
162  $this->setId($question_id);
163  $this->setObjId($data["obj_fi"]);
164  $this->setTitle($data["title"]);
165  $this->setComment($data["description"]);
166  $this->setOriginalId($data["original_id"]);
167  $this->setNrOfTries($data['nr_of_tries']);
168  $this->setAuthor($data["author"]);
169  $this->setPoints($data["points"]);
170  $this->setOwner($data["owner"]);
171  include_once("./Services/RTE/classes/class.ilRTE.php");
172  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
173  $this->setShuffle($data["shuffle"]);
174  $this->setMaxNumOfChars($data["maxnumofchars"]);
175  $this->setKeywords($data["keywords"]);
176  $this->setTextRating($data["textgap_rating"]);
177  $this->matchcondition = (strlen($data['matchcondition'])) ? $data['matchcondition'] : 0;
178  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
179  }
180  parent::loadFromDb($question_id);
181  }
182 
188  function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
189  {
190  if ($this->id <= 0)
191  {
192  // The question has not been saved. It cannot be duplicated
193  return;
194  }
195  // duplicate the question in database
196  $this_id = $this->getId();
197 
198  if( (int)$testObjId > 0 )
199  {
200  $thisObjId = $this->getObjId();
201  }
202 
203  $clone = $this;
204  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
206  $clone->id = -1;
207 
208  if( (int)$testObjId > 0 )
209  {
210  $clone->setObjId($testObjId);
211  }
212 
213  if ($title)
214  {
215  $clone->setTitle($title);
216  }
217 
218  if ($author)
219  {
220  $clone->setAuthor($author);
221  }
222  if ($owner)
223  {
224  $clone->setOwner($owner);
225  }
226 
227  if ($for_test)
228  {
229  $clone->saveToDb($original_id);
230  }
231  else
232  {
233  $clone->saveToDb();
234  }
235 
236  // copy question page content
237  $clone->copyPageOfQuestion($this_id);
238  // copy XHTML media objects
239  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
240  // duplicate the generic feedback
241  $clone->duplicateFeedbackGeneric($this_id);
242 
243  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
244 
245  return $clone->id;
246  }
247 
253  function copyObject($target_questionpool, $title = "")
254  {
255  if ($this->id <= 0)
256  {
257  // The question has not been saved. It cannot be duplicated
258  return;
259  }
260  // duplicate the question in database
261  $clone = $this;
262  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
264  $clone->id = -1;
265  $source_questionpool = $this->getObjId();
266  $clone->setObjId($target_questionpool);
267  if ($title)
268  {
269  $clone->setTitle($title);
270  }
271  $clone->saveToDb();
272 
273  // copy question page content
274  $clone->copyPageOfQuestion($original_id);
275  // copy XHTML media objects
276  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
277  // duplicate the generic feedback
278  $clone->duplicateFeedbackGeneric($original_id);
279  $clone->onCopy($this->getObjId(), $this->getId());
280 
281  return $clone->id;
282  }
283 
291  function getMaxNumOfChars()
292  {
293  if (strcmp($this->maxNumOfChars, "") == 0)
294  {
295  return 0;
296  }
297  else
298  {
299  return $this->maxNumOfChars;
300  }
301  }
302 
310  function setMaxNumOfChars($maxchars = 0)
311  {
312  $this->maxNumOfChars = $maxchars;
313  }
314 
321  function getMaximumPoints()
322  {
323  return $this->points;
324  }
325 
335  function setReachedPoints($active_id, $points, $pass = NULL)
336  {
337  global $ilDB;
338 
339  if (($points > 0) && ($points <= $this->getPoints()))
340  {
341  if (is_null($pass))
342  {
343  $pass = $this->getSolutionMaxPass($active_id);
344  }
345  $affectedRows = $ilDB->manipulateF("UPDATE tst_test_result SET points = %s WHERE active_fi = %s AND question_fi = %s AND pass = %s",
346  array('float','integer','integer','integer'),
347  array($points, $active_id, $this->getId(), $pass)
348  );
349  $this->_updateTestPassResults($active_id, $pass);
350  return TRUE;
351  }
352  else
353  {
354  return TRUE;
355  }
356  }
357 
366  function isKeywordMatching($answertext, $a_keyword)
367  {
368  $result = FALSE;
369  $textrating = $this->getTextRating();
370  include_once "./Services/Utilities/classes/class.ilStr.php";
371  switch ($textrating)
372  {
374  if (ilStr::strPos(ilStr::strToLower($answertext), ilStr::strToLower($a_keyword)) !== false) return TRUE;
375  break;
377  if (ilStr::strPos($answertext, $a_keyword) !== false) return TRUE;
378  break;
379  }
380  $answerwords = array();
381  if (preg_match_all("/([^\s.]+)/", $answertext, $matches))
382  {
383  foreach ($matches[1] as $answerword)
384  {
385  array_push($answerwords, trim($answerword));
386  }
387  }
388  foreach ($answerwords as $a_original)
389  {
390  switch ($textrating)
391  {
393  if (levenshtein($a_original, $a_keyword) <= 1) return TRUE;
394  break;
396  if (levenshtein($a_original, $a_keyword) <= 2) return TRUE;
397  break;
399  if (levenshtein($a_original, $a_keyword) <= 3) return TRUE;
400  break;
402  if (levenshtein($a_original, $a_keyword) <= 4) return TRUE;
403  break;
405  if (levenshtein($a_original, $a_keyword) <= 5) return TRUE;
406  break;
407  }
408  }
409  return $result;
410  }
411 
421  function calculateReachedPoints($active_id, $pass = NULL)
422  {
423  global $ilDB;
424 
425  $points = 0;
426  if (is_null($pass))
427  {
428  $pass = $this->getSolutionMaxPass($active_id);
429  }
430  $result = $ilDB->queryF("SELECT * FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
431  array('integer','integer','integer'),
432  array($active_id, $this->getId(), $pass)
433  );
434  if ($result->numRows() == 1)
435  {
436  $row = $ilDB->fetchAssoc($result);
437  if ($row["points"])
438  {
439  $points = $row["points"];
440  }
441  else
442  {
443  $keywords =& $this->getKeywordList();
444  if (count($keywords))
445  {
446  if ($this->matchcondition == 0)
447  {
448  $foundkeyword = false;
449  }
450  else
451  {
452  $foundkeyword = true;
453  }
454  foreach ($keywords as $keyword)
455  {
456  if ($this->matchcondition == 0)
457  {
458  // OR: only one keyword needs to match
459  $foundkeyword = $foundkeyword | $this->isKeywordMatching($row["value1"], $keyword);
460  }
461  else
462  {
463  // AND: all keywords needs to match
464  $foundkeyword = $foundkeyword & $this->isKeywordMatching($row["value1"], $keyword);
465  }
466  }
467  if ($foundkeyword) $points = $this->getMaximumPoints();
468  }
469  }
470  }
471 
472  $points = parent::calculateReachedPoints($active_id, $pass = NULL, $points);
473  return $points;
474  }
475 
484  function saveWorkingData($active_id, $pass = NULL)
485  {
486  global $ilDB;
487  global $ilUser;
488 
489  include_once "./Services/Utilities/classes/class.ilStr.php";
490  if (is_null($pass))
491  {
492  include_once "./Modules/Test/classes/class.ilObjTest.php";
493  $pass = ilObjTest::_getPass($active_id);
494  }
495  $affectedRows = $ilDB->manipulateF("DELETE FROM tst_solutions WHERE active_fi = %s AND question_fi = %s AND pass = %s",
496  array('integer','integer','integer'),
497  array($active_id, $this->getId(), $pass)
498  );
499  $text = ilUtil::stripSlashes($_POST["TEXT"], FALSE);
500  if ($this->getMaxNumOfChars())
501  {
502  include_once "./Services/Utilities/classes/class.ilStr.php";
503  $text_without_tags = preg_replace("/<[^>*?]>/is", "", $text);
504  $len_with_tags = ilStr::strLen($text);
505  $len_without_tags = ilStr::strLen($text_without_tags);
506  if ($this->getMaxNumOfChars() < $len_without_tags)
507  {
508  if (!$this->isHTML($text))
509  {
510  $text = ilStr::subStr($text, 0, $this->getMaxNumOfChars());
511  }
512  }
513  }
514  if ($this->isHTML($text))
515  {
516  $text = preg_replace("/<[^>]*$/ims", "", $text);
517  }
518  else
519  {
520  //$text = htmlentities($text, ENT_QUOTES, "UTF-8");
521  }
522  $entered_values = 0;
523  if (strlen($text))
524  {
525  $next_id = $ilDB->nextId('tst_solutions');
526  $affectedRows = $ilDB->insert("tst_solutions", array(
527  "solution_id" => array("integer", $next_id),
528  "active_fi" => array("integer", $active_id),
529  "question_fi" => array("integer", $this->getId()),
530  "value1" => array("clob", trim($text)),
531  "value2" => array("clob", null),
532  "pass" => array("integer", $pass),
533  "tstamp" => array("integer", time())
534  ));
535  $entered_values++;
536  }
537  if ($entered_values)
538  {
539  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
541  {
542  $this->logAction($this->lng->txtlng("assessment", "log_user_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
543  }
544  }
545  else
546  {
547  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
549  {
550  $this->logAction($this->lng->txtlng("assessment", "log_user_not_entered_values", ilObjAssessmentFolder::_getLogLanguage()), $active_id, $this->getId());
551  }
552  }
553  parent::saveWorkingData($active_id, $pass);
554  return true;
555  }
556 
557  function createRandomSolution($test_id, $user_id)
558  {
559  }
560 
567  function getQuestionType()
568  {
569  return "assTextQuestion";
570  }
571 
578  function getKeywords()
579  {
580  return $this->keywords;
581  }
582 
589  function setKeywords($a_keywords)
590  {
591  $this->keywords = $a_keywords;
592  }
593 
600  function &getKeywordList()
601  {
602  $keywords = array();
603  if (preg_match_all("/([^\s]+)/", $this->keywords, $matches))
604  {
605  foreach ($matches[1] as $keyword)
606  {
607  array_push($keywords, trim($keyword));
608  }
609  }
610  return $keywords;
611  }
612 
620  function getTextRating()
621  {
622  return $this->text_rating;
623  }
624 
632  function setTextRating($a_text_rating)
633  {
634  switch ($a_text_rating)
635  {
643  $this->text_rating = $a_text_rating;
644  break;
645  default:
646  $this->text_rating = TEXTGAP_RATING_CASEINSENSITIVE;
647  break;
648  }
649  }
650 
658  {
659  return "qpl_qst_essay";
660  }
661 
667  {
669  }
670 
683  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
684  {
685  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
686  $solutions = $this->getSolutionValues($active_id, $pass);
687  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
688  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
689  $i = 1;
690  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($this->lng->txt("result")), $format_bold);
691  if (strlen($solutions[0]["value1"]))
692  {
693  $worksheet->write($startrow + $i, 1, ilExcelUtils::_convert_text($solutions[0]["value1"]));
694  }
695  $i++;
696  return $startrow + $i + 1;
697  }
698 
702  public function toJSON()
703  {
704  include_once("./Services/RTE/classes/class.ilRTE.php");
705  $result = array();
706  $result['id'] = (int) $this->getId();
707  $result['type'] = (string) $this->getQuestionType();
708  $result['title'] = (string) $this->getTitle();
709  $result['question'] = $this->formatSAQuestion($this->getQuestion());
710  $result['nr_of_tries'] = (int) $this->getNrOfTries();
711  $result['shuffle'] = (bool) $this->getShuffle();
712  $result['maxlength'] = (int) $this->getMaxNumOfChars();
713  return json_encode($result);
714  }
715 
716 }
717 
718 ?>