ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
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  {
53  parent::__construct($title, $comment, $author, $owner, $question);
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  {
86  parent::saveToDb($original_id);
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 
320  {
321  $points = 0;
322  if ($this->contains($previewSession->getParticipantsSolution()))
323  {
324  $points = $this->getPoints();
325  }
326 
327  $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $points);
328 
329  return $this->ensureNonNegativePoints($reachedPoints);
330  }
331 
344  public function calculateReachedPoints($active_id, $pass = NULL, $authorizedSolution = true, $returndetails = FALSE)
345  {
346  if( $returndetails )
347  {
348  throw new ilTestException('return details not implemented for '.__METHOD__);
349  }
350 
352  global $ilDB;
353 
354  $found_values = array();
355  if (is_null($pass))
356  {
357  $pass = $this->getSolutionMaxPass($active_id);
358  }
359  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
360  $data = $ilDB->fetchAssoc($result);
361 
362  $enteredvalue = $data["value1"];
363 
364  $points = 0;
365  if ($this->contains($enteredvalue))
366  {
367  $points = $this->getPoints();
368  }
369 
370  return $points;
371  }
372 
383  public function contains($value)
384  {
385  require_once './Services/Math/classes/class.EvalMath.php';
386  $eval = new EvalMath();
387  $eval->suppress_errors = TRUE;
388  $result = $eval->e($value);
389  if (($result === FALSE) || ($result === TRUE))
390  {
391  return FALSE;
392  }
393 
394  if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit())))
395  {
396  return TRUE;
397  }
398  return FALSE;
399  }
400 
401  protected function isValidNumericSubmitValue($submittedValue)
402  {
403  if( is_numeric($submittedValue) )
404  {
405  return true;
406  }
407 
408  if( preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue) )
409  {
410  return true;
411  }
412 
413  return false;
414  }
415 
416  public function validateSolutionSubmit()
417  {
418  if( strlen($this->getSolutionSubmit()) && !$this->isValidNumericSubmitValue($this->getSolutionSubmit()) )
419  {
420  ilUtil::sendFailure($this->lng->txt("err_no_numeric_value"), true);
421  return false;
422  }
423 
424  return true;
425  }
426 
427  public function getSolutionSubmit()
428  {
429  return trim(str_replace(",",".",$_POST["numeric_result"]));
430  }
431 
432  public function isValidSolutionSubmit($numeric_solution)
433  {
434  require_once './Services/Math/classes/class.EvalMath.php';
435  $math = new EvalMath();
436  $math->suppress_errors = TRUE;
437  $result = $math->evaluate($numeric_solution);
438 
439  return !(
440  ($result === FALSE || $result === TRUE) && strlen($numeric_solution) > 0
441  );
442  }
443 
452  public function saveWorkingData($active_id, $pass = NULL, $authorized = true)
453  {
455  global $ilDB;
456 
457  if (is_null($pass))
458  {
459  require_once './Modules/Test/classes/class.ilObjTest.php';
460  $pass = ilObjTest::_getPass($active_id);
461  }
462 
463  $entered_values = 0;
464 
465  $returnvalue = true;
466 
467  $numeric_result = $this->getSolutionSubmit();
468 
469  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function() use (&$entered_values, $numeric_result, $ilDB, $active_id, $pass, $authorized) {
470 
471  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
472 
473  $row = $ilDB->fetchAssoc($result);
474  $update = $row["solution_id"];
475  if($update)
476  {
477  if(strlen($numeric_result))
478  {
479  $this->updateCurrentSolution($update, trim($numeric_result), null, $authorized);
480  $entered_values++;
481  }
482  else
483  {
484  $this->removeSolutionRecordById($update);
485  }
486  }
487  else
488  {
489  if(strlen($numeric_result))
490  {
491  $this->saveCurrentSolution($active_id, $pass, trim($numeric_result), null, $authorized);
492  $entered_values++;
493  }
494  }
495 
496  });
497 
498  if ($entered_values)
499  {
500  require_once './Modules/Test/classes/class.ilObjAssessmentFolder.php';
502  {
503  assQuestion::logAction($this->lng->txtlng(
504  "assessment",
505  "log_user_entered_values",
507  ),
508  $active_id,
509  $this->getId()
510  );
511  }
512  }
513  else
514  {
515  include_once ("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
517  {
518  assQuestion::logAction($this->lng->txtlng(
519  "assessment",
520  "log_user_not_entered_values",
522  ),
523  $active_id,
524  $this->getId()
525  );
526  }
527  }
528 
529  return $returnvalue;
530  }
531 
532  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
533  {
534  $numericSolution = $this->getSolutionSubmit();
535  $previewSession->setParticipantsSolution($numericSolution);
536  }
537 
538  public function saveAdditionalQuestionDataToDb()
539  {
541  global $ilDB;
542 
543  // save additional data
544  $ilDB->manipulateF( "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
545  array( "integer" ),
546  array( $this->getId() )
547  );
548 
549  $ilDB->manipulateF( "INSERT INTO " . $this->getAdditionalTableName(
550  ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
551  array( "integer", "integer" ),
552  array(
553  $this->getId(),
554  ($this->getMaxChars()) ? $this->getMaxChars() : 0
555  )
556  );
557  }
558 
559  public function saveAnswerSpecificDataToDb()
560  {
562  global $ilDB;
563 
564  // Write range to the database
565  $ilDB->manipulateF( "DELETE FROM qpl_num_range WHERE question_fi = %s",
566  array( 'integer' ),
567  array( $this->getId() )
568  );
569 
570  $next_id = $ilDB->nextId( 'qpl_num_range' );
571  $ilDB->manipulateF( "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
572  VALUES (%s, %s, %s, %s, %s, %s, %s)",
573  array( 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ),
574  array( $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
575  ), $this->getPoints(), 0, time() )
576  );
577  }
578 
582  protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
583  {
584  // nothing to rework!
585  }
586 
592  public function getQuestionType()
593  {
594  return "assNumeric";
595  }
596 
602  public function getMaxChars()
603  {
604  return $this->maxchars;
605  }
606 
612  public function setMaxChars($maxchars)
613  {
614  $this->maxchars = $maxchars;
615  }
616 
623  {
624  return "qpl_qst_numeric";
625  }
626 
632  {
633  return parent::getRTETextWithMediaObjects();
634  }
635 
639  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
640  {
641  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
642 
643  $solutions = $this->getSolutionValues($active_id, $pass);
644 
645  $i = 1;
646  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
647  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
648 
649  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
650  if (strlen($solutions[0]["value1"]))
651  {
652  $worksheet->setCell($startrow + $i, 1, $solutions[0]["value1"]);
653  }
654  $i++;
655 
656  return $startrow + $i + 1;
657  }
658 
667  public function getOperators($expression)
668  {
669  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
671  }
672 
677  public function getExpressionTypes()
678  {
679  return array(
683  );
684  }
685 
694  public function getUserQuestionResult($active_id, $pass)
695  {
697  global $ilDB;
698  $result = new ilUserQuestionResult($this, $active_id, $pass);
699 
700  $maxStep = $this->lookupMaxStep($active_id, $pass);
701 
702  if( $maxStep !== null )
703  {
704  $data = $ilDB->queryF(
705  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
706  array("integer", "integer", "integer","integer"),
707  array($active_id, $pass, $this->getId(), $maxStep)
708  );
709  }
710  else
711  {
712  $data = $ilDB->queryF(
713  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
714  array("integer", "integer", "integer"),
715  array($active_id, $pass, $this->getId())
716  );
717  }
718 
719  while($row = $ilDB->fetchAssoc($data))
720  {
721  $result->addKeyValue(1, $row["value1"]);
722  }
723 
724  $points = $this->calculateReachedPoints($active_id, $pass);
725  $max_points = $this->getMaximumPoints();
726 
727  $result->setReachedPercentage(($points/$max_points) * 100);
728 
729  return $result;
730  }
731 
740  public function getAvailableAnswerOptions($index = null)
741  {
742  return array(
743  "lower" => $this->getLowerLimit(),
744  "upper" => $this->getUpperLimit()
745  );
746  }
747 }
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
getId()
Gets the id of the assQuestion object.
static _getOriginalId($question_id)
Returns the original id of a question.
$worksheet
Class iQuestionCondition.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assNumeric constructor
getExpressionTypes()
Get all available expression types for a specific question.
$result
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
getPoints()
Returns the maximum available points for the question.
getMaxChars()
Returns the maximum number of characters for the numeric input field.
setLowerLimit($a_limit)
Abstract basic class which is to be extended by the concrete assessment question type classes...
ensureNonNegativePoints($points)
setId($id=-1)
Sets the id of the assQuestion object.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second. ...
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{}
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
setNrOfTries($a_nr_of_tries)
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
getQuestionType()
Returns the question type of the question.
loadFromDb($question_id)
Loads the question from the database.
static _replaceMediaObjectImageSrc($a_text, $a_direction=0, $nic=IL_INST_ID)
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
getObjId()
Get the object id of the container object.
isValidNumericSubmitValue($submittedValue)
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
Base Exception for all Exceptions relating to Modules/Test.
setMaxChars($maxchars)
Sets the maximum number of characters for the numeric input field.
removeSolutionRecordById($solutionId)
static _getLogLanguage()
retrieve the log language for assessment logging
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{}
setAuthor($author="")
Sets the authors name of the assQuestion object.
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
Class ilUserQuestionResult.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
Interface ilObjAnswerScoringAdjustable.
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assNumericQuestion.
saveToDb($original_id="")
Saves a assNumeric object to a database.
getAdditionalTableName()
Returns the name of the additional question data table in the database.
getOperators($expression)
Get all available operations for a specific question.
updateCurrentSolution($solutionId, $value1, $value2, $authorized=true)
Create styles array
The data for the language used.
setUpperLimit($a_limit)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
copyObject($target_questionpool_id, $title="")
Copies an assNumeric object.
setPoints($a_points)
Sets the maximum available points for the question.
saveQuestionDataToDb($original_id="")
isValidSolutionSubmit($numeric_solution)
isComplete()
Returns true, if a numeric question is complete for use.
Class for numeric questions.
setQuestion($question="")
Sets the question string of the question object.
Interface ilObjQuestionScoringAdjustable.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
global $ilDB
setOriginalId($original_id)
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass...
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
setTitle($title="")
Sets the title string of the assQuestion object.
setObjId($obj_id=0)
Set the object id of the container object.
savePreviewData(ilAssQuestionPreviewSession $previewSession)
setComment($comment="")
Sets the comment string of the assQuestion object.
$_POST["username"]
contains($value)
Checks for a given value within the range.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.