ILIAS  Release_5_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.assNumeric.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 require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
9 require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
10 
27 {
28  protected $lower_limit;
29  protected $upper_limit;
30 
32  var $maxchars;
33 
45  function __construct(
46  $title = "",
47  $comment = "",
48  $author = "",
49  $owner = -1,
50  $question = ""
51  )
52  {
54  $this->maxchars = 6;
55  }
56 
62  public function isComplete()
63  {
64  if (
65  strlen($this->title)
66  && $this->author
67  && $this->question
68  && $this->getMaximumPoints() > 0
69  )
70  {
71  return true;
72  }
73  return false;
74  }
75 
81  public function saveToDb($original_id = "")
82  {
87  }
88 
94  public function loadFromDb($question_id)
95  {
97  global $ilDB;
98 
99  $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",
100  array("integer"),
101  array($question_id)
102  );
103  if ($result->numRows() == 1)
104  {
105  $data = $ilDB->fetchAssoc($result);
106  $this->setId($question_id);
107  $this->setObjId($data["obj_fi"]);
108  $this->setTitle($data["title"]);
109  $this->setComment($data["description"]);
110  $this->setNrOfTries($data['nr_of_tries']);
111  $this->setOriginalId($data["original_id"]);
112  $this->setAuthor($data["author"]);
113  $this->setPoints($data["points"]);
114  $this->setOwner($data["owner"]);
115  require_once './Services/RTE/classes/class.ilRTE.php';
116  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
117  $this->setMaxChars($data["maxnumofchars"]);
118  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
119 
120  try
121  {
122  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
123  }
125  {
126  }
127  }
128 
129  $result = $ilDB->queryF("SELECT * FROM qpl_num_range WHERE question_fi = %s ORDER BY aorder ASC",
130  array('integer'),
131  array($question_id)
132  );
133 
134  require_once './Modules/TestQuestionPool/classes/class.assNumericRange.php';
135  if ($result->numRows() > 0)
136  {
138  while ($data = $ilDB->fetchAssoc($result))
139  {
140  $this->setPoints($data['points']);
141  $this->setLowerLimit($data['lowerlimit']);
142  $this->setUpperLimit($data['upperlimit']);
143  }
144  }
145 
146  parent::loadFromDb($question_id);
147  }
148 
160  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
161  {
162  if ($this->id <= 0)
163  {
164  // The question has not been saved. It cannot be duplicated
165  return;
166  }
167  // duplicate the question in database
168  $this_id = $this->getId();
169  $thisObjId = $this->getObjId();
170 
171  $clone = $this;
172  require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
174  $clone->id = -1;
175 
176  if( (int)$testObjId > 0 )
177  {
178  $clone->setObjId($testObjId);
179  }
180 
181  if ($title)
182  {
183  $clone->setTitle($title);
184  }
185 
186  if ($author)
187  {
188  $clone->setAuthor($author);
189  }
190  if ($owner)
191  {
192  $clone->setOwner($owner);
193  }
194 
195  if ($for_test)
196  {
197  $clone->saveToDb($original_id);
198  }
199  else
200  {
201  $clone->saveToDb();
202  }
203 
204  // copy question page content
205  $clone->copyPageOfQuestion($this_id);
206  // copy XHTML media objects
207  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
208 
209  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
210 
211  return $clone->id;
212  }
213 
222  public function copyObject($target_questionpool_id, $title = "")
223  {
224  if ($this->id <= 0)
225  {
226  // The question has not been saved. It cannot be duplicated
227  return;
228  }
229  // duplicate the question in database
230  $clone = $this;
231  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
233  $clone->id = -1;
234  $source_questionpool_id = $this->getObjId();
235  $clone->setObjId($target_questionpool_id);
236  if ($title)
237  {
238  $clone->setTitle($title);
239  }
240  $clone->saveToDb();
241 
242  // copy question page content
243  $clone->copyPageOfQuestion($original_id);
244  // copy XHTML media objects
245  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
246 
247  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
248 
249  return $clone->id;
250  }
251 
252  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
253  {
254  if ($this->id <= 0)
255  {
256  // The question has not been saved. It cannot be duplicated
257  return;
258  }
259 
260  include_once ("./Modules/TestQuestionPool/classes/class.assQuestion.php");
261 
262  $sourceQuestionId = $this->id;
263  $sourceParentId = $this->getObjId();
264 
265  // duplicate the question in database
266  $clone = $this;
267  $clone->id = -1;
268 
269  $clone->setObjId($targetParentId);
270 
271  if ($targetQuestionTitle)
272  {
273  $clone->setTitle($targetQuestionTitle);
274  }
275 
276  $clone->saveToDb();
277  // copy question page content
278  $clone->copyPageOfQuestion($sourceQuestionId);
279  // copy XHTML media objects
280  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
281 
282  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
283 
284  return $clone->id;
285  }
286 
287  public function getLowerLimit()
288  {
289  return $this->lower_limit;
290  }
291 
292  public function getUpperLimit()
293  {
294  return $this->upper_limit;
295  }
296 
297  public function setLowerLimit($a_limit)
298  {
299  $a_limit = str_replace(',', '.', $a_limit);
300  $this->lower_limit = $a_limit;
301  }
302 
303  public function setUpperLimit($a_limit)
304  {
305  $a_limit = str_replace(',', '.', $a_limit);
306  $this->upper_limit = $a_limit;
307  }
308 
314  public function getMaximumPoints()
315  {
316  return $this->getPoints();
317  }
318 
331  public function calculateReachedPoints($active_id, $pass = NULL, $returndetails = FALSE)
332  {
333  if( $returndetails )
334  {
335  throw new ilTestException('return details not implemented for '.__METHOD__);
336  }
337 
339  global $ilDB;
340 
341  $found_values = array();
342  if (is_null($pass))
343  {
344  $pass = $this->getSolutionMaxPass($active_id);
345  }
346  $result = $this->getCurrentSolutionResultSet($active_id, $pass);
347  $data = $ilDB->fetchAssoc($result);
348 
349  $enteredvalue = $data["value1"];
350 
351  $points = 0;
352  if ($this->contains($enteredvalue))
353  {
354  $points = $this->getPoints();
355  }
356 
357  return $points;
358  }
359 
361  {
362  $points = 0;
363  if ($this->contains($previewSession->getParticipantsSolution()))
364  {
365  $points = $this->getPoints();
366  }
367 
368  return $points;
369  }
370 
381  public function contains($value)
382  {
383  require_once './Services/Math/classes/class.EvalMath.php';
384  $eval = new EvalMath();
385  $eval->suppress_errors = TRUE;
386  $result = $eval->e($value);
387  if (($result === FALSE) || ($result === TRUE))
388  {
389  return FALSE;
390  }
391 
392  if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit())))
393  {
394  return TRUE;
395  }
396  return FALSE;
397  }
398 
399  public function getSolutionSubmit()
400  {
401  return trim(str_replace(",",".",$_POST["numeric_result"]));
402  }
403 
404  public function isValidSolutionSubmit($numeric_solution)
405  {
406  require_once './Services/Math/classes/class.EvalMath.php';
407  $math = new EvalMath();
408  $math->suppress_errors = TRUE;
409  $result = $math->evaluate($numeric_solution);
410 
411  return !(
412  ($result === FALSE || $result === TRUE) && strlen($numeric_solution) > 0
413  );
414  }
415 
424  public function saveWorkingData($active_id, $pass = NULL)
425  {
427  global $ilDB;
428 
429  if (is_null($pass))
430  {
431  require_once './Modules/Test/classes/class.ilObjTest.php';
432  $pass = ilObjTest::_getPass($active_id);
433  }
434 
435  $entered_values = 0;
436 
437  $returnvalue = true;
438 
439  $numeric_result = $this->getSolutionSubmit();
440 
441  if( !$this->isValidSolutionSubmit($numeric_result) )
442  {
443  ilUtil::sendInfo($this->lng->txt("err_no_numeric_value"), true);
444  $returnvalue = false;
445  }
446 
447  $this->getProcessLocker()->requestUserSolutionUpdateLock();
448 
449  $result = $this->getCurrentSolutionResultSet($active_id, $pass);
450 
451  $row = $ilDB->fetchAssoc($result);
452  $update = $row["solution_id"];
453  if ($update)
454  {
455  if (strlen($numeric_result))
456  {
457  $ilDB->update("tst_solutions", array(
458  "value1" => array("clob", trim($numeric_result)),
459  "tstamp" => array("integer", time())
460  ),
461  array(
462  "solution_id" => array("integer", $update)
463  )
464  );
465 
466  $entered_values++;
467  }
468  else
469  {
470  $ilDB->manipulateF("DELETE FROM tst_solutions WHERE solution_id = %s",
471  array('integer'),
472  array($update)
473  );
474  }
475  }
476  else
477  {
478  if (strlen($numeric_result))
479  {
480  $this->saveCurrentSolution($active_id, $pass, trim($numeric_result), null);
481  $entered_values++;
482  }
483  }
484 
485  $this->getProcessLocker()->releaseUserSolutionUpdateLock();
486 
487  if ($entered_values)
488  {
489  require_once './Modules/Test/classes/class.ilObjAssessmentFolder.php';
491  {
492  $this->logAction($this->lng->txtlng(
493  "assessment",
494  "log_user_entered_values",
496  ),
497  $active_id,
498  $this->getId()
499  );
500  }
501  }
502  else
503  {
504  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
506  {
507  $this->logAction($this->lng->txtlng(
508  "assessment",
509  "log_user_not_entered_values",
511  ),
512  $active_id,
513  $this->getId()
514  );
515  }
516  }
517 
518  return $returnvalue;
519  }
520 
521  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
522  {
523  $numericSolution = $this->getSolutionSubmit();
524 
525  if( !$this->isValidSolutionSubmit($numericSolution) )
526  {
527  ilUtil::sendInfo($this->lng->txt("err_no_numeric_value"), true);
528  }
529 
530  $previewSession->setParticipantsSolution($numericSolution);
531  }
532 
533  public function saveAdditionalQuestionDataToDb()
534  {
536  global $ilDB;
537 
538  // save additional data
539  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
540  array( "integer" ),
541  array( $this->getId() )
542  );
543 
544  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName(
545  ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
546  array( "integer", "integer" ),
547  array(
548  $this->getId(),
549  ($this->getMaxChars()) ? $this->getMaxChars() : 0
550  )
551  );
552  }
553 
554  public function saveAnswerSpecificDataToDb()
555  {
557  global $ilDB;
558 
559  // Write range to the database
560  $ilDB->manipulateF( "DELETE FROM qpl_num_range WHERE question_fi = %s",
561  array( 'integer' ),
562  array( $this->getId() )
563  );
564 
565  $next_id = $ilDB->nextId( 'qpl_num_range' );
566  $ilDB->manipulateF( "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
567  VALUES (%s, %s, %s, %s, %s, %s, %s)",
568  array( 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ),
569  array( $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
570  ), $this->getPoints(), 0, time() )
571  );
572  }
573 
581  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered)
582  {
583  // nothing to rework!
584  }
585 
591  public function getQuestionType()
592  {
593  return "assNumeric";
594  }
595 
601  public function getMaxChars()
602  {
603  return $this->maxchars;
604  }
605 
611  public function setMaxChars($maxchars)
612  {
613  $this->maxchars = $maxchars;
614  }
615 
622  {
623  return "qpl_qst_numeric";
624  }
625 
631  {
633  }
634 
647  public function setExportDetailsXLS(&$worksheet, $startrow, $active_id, $pass, &$format_title, &$format_bold)
648  {
649  include_once ("./Services/Excel/classes/class.ilExcelUtils.php");
650  $solutions = $this->getSolutionValues($active_id, $pass);
651  $worksheet->writeString($startrow, 0, ilExcelUtils::_convert_text($this->lng->txt($this->getQuestionType())), $format_title);
652  $worksheet->writeString($startrow, 1, ilExcelUtils::_convert_text($this->getTitle()), $format_title);
653  $i = 1;
654  $worksheet->writeString($startrow + $i, 0, ilExcelUtils::_convert_text($this->lng->txt("result")), $format_bold);
655  if (strlen($solutions[0]["value1"]))
656  {
657  $worksheet->write($startrow + $i, 1, ilExcelUtils::_convert_text($solutions[0]["value1"]));
658  }
659  $i++;
660  return $startrow + $i + 1;
661  }
662 
671  public function getOperators($expression)
672  {
673  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
675  }
676 
681  public function getExpressionTypes()
682  {
683  return array(
687  );
688  }
689 
698  public function getUserQuestionResult($active_id, $pass)
699  {
701  global $ilDB;
702  $result = new ilUserQuestionResult($this, $active_id, $pass);
703 
704  $data = $ilDB->queryF(
705  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = (
706  SELECT MAX(step) FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s
707  )",
708  array("integer", "integer", "integer","integer", "integer", "integer"),
709  array($active_id, $pass, $this->getId(), $active_id, $pass, $this->getId())
710  );
711 
712  while($row = $ilDB->fetchAssoc($data))
713  {
714  $result->addKeyValue(1, $row["value1"]);
715  }
716 
717  $points = $this->calculateReachedPoints($active_id, $pass);
718  $max_points = $this->getMaximumPoints();
719 
720  $result->setReachedPercentage(($points/$max_points) * 100);
721 
722  return $result;
723  }
724 
733  public function getAvailableAnswerOptions($index = null)
734  {
735  return array(
736  "lower" => $this->getLowerLimit(),
737  "upper" => $this->getUpperLimit()
738  );
739  }
740 }