ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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  public $maxchars;
33 
45  public function __construct(
46  $title = "",
47  $comment = "",
48  $author = "",
49  $owner = -1,
50  $question = ""
51  ) {
52  parent::__construct($title, $comment, $author, $owner, $question);
53  $this->maxchars = 6;
54  }
55 
61  public function isComplete()
62  {
63  if (
64  strlen($this->title)
65  && $this->author
66  && $this->question
67  && $this->getMaximumPoints() > 0
68  ) {
69  return true;
70  }
71  return false;
72  }
73 
79  public function saveToDb($original_id = "")
80  {
84  parent::saveToDb($original_id);
85  }
86 
92  public function loadFromDb($question_id)
93  {
95  global $DIC;
96  $ilDB = $DIC['ilDB'];
97 
98  $result = $ilDB->queryF(
99  "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  $data = $ilDB->fetchAssoc($result);
105  $this->setId($question_id);
106  $this->setObjId($data["obj_fi"]);
107  $this->setTitle($data["title"]);
108  $this->setComment($data["description"]);
109  $this->setNrOfTries($data['nr_of_tries']);
110  $this->setOriginalId($data["original_id"]);
111  $this->setAuthor($data["author"]);
112  $this->setPoints($data["points"]);
113  $this->setOwner($data["owner"]);
114  require_once './Services/RTE/classes/class.ilRTE.php';
115  $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
116  $this->setMaxChars($data["maxnumofchars"]);
117  $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
118 
119  try {
120  $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
121  } catch (ilTestQuestionPoolException $e) {
122  }
123  }
124 
125  $result = $ilDB->queryF(
126  "SELECT * FROM qpl_num_range WHERE question_fi = %s ORDER BY aorder ASC",
127  array('integer'),
128  array($question_id)
129  );
130 
131  require_once './Modules/TestQuestionPool/classes/class.assNumericRange.php';
132  if ($result->numRows() > 0) {
134  while ($data = $ilDB->fetchAssoc($result)) {
135  $this->setPoints($data['points']);
136  $this->setLowerLimit($data['lowerlimit']);
137  $this->setUpperLimit($data['upperlimit']);
138  }
139  }
140 
141  parent::loadFromDb($question_id);
142  }
143 
155  public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
156  {
157  if ($this->id <= 0) {
158  // The question has not been saved. It cannot be duplicated
159  return;
160  }
161  // duplicate the question in database
162  $this_id = $this->getId();
163  $thisObjId = $this->getObjId();
164 
165  $clone = $this;
166  require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
168  $clone->id = -1;
169 
170  if ((int) $testObjId > 0) {
171  $clone->setObjId($testObjId);
172  }
173 
174  if ($title) {
175  $clone->setTitle($title);
176  }
177 
178  if ($author) {
179  $clone->setAuthor($author);
180  }
181  if ($owner) {
182  $clone->setOwner($owner);
183  }
184 
185  if ($for_test) {
186  $clone->saveToDb($original_id);
187  } else {
188  $clone->saveToDb();
189  }
190 
191  // copy question page content
192  $clone->copyPageOfQuestion($this_id);
193  // copy XHTML media objects
194  $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
195 
196  $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
197 
198  return $clone->id;
199  }
200 
209  public function copyObject($target_questionpool_id, $title = "")
210  {
211  if ($this->id <= 0) {
212  // The question has not been saved. It cannot be duplicated
213  return;
214  }
215  // duplicate the question in database
216  $clone = $this;
217  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
219  $clone->id = -1;
220  $source_questionpool_id = $this->getObjId();
221  $clone->setObjId($target_questionpool_id);
222  if ($title) {
223  $clone->setTitle($title);
224  }
225  $clone->saveToDb();
226 
227  // copy question page content
228  $clone->copyPageOfQuestion($original_id);
229  // copy XHTML media objects
230  $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
231 
232  $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
233 
234  return $clone->id;
235  }
236 
237  public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
238  {
239  if ($this->id <= 0) {
240  // The question has not been saved. It cannot be duplicated
241  return;
242  }
243 
244  include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
245 
246  $sourceQuestionId = $this->id;
247  $sourceParentId = $this->getObjId();
248 
249  // duplicate the question in database
250  $clone = $this;
251  $clone->id = -1;
252 
253  $clone->setObjId($targetParentId);
254 
255  if ($targetQuestionTitle) {
256  $clone->setTitle($targetQuestionTitle);
257  }
258 
259  $clone->saveToDb();
260  // copy question page content
261  $clone->copyPageOfQuestion($sourceQuestionId);
262  // copy XHTML media objects
263  $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
264 
265  $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
266 
267  return $clone->id;
268  }
269 
270  public function getLowerLimit()
271  {
272  return $this->lower_limit;
273  }
274 
275  public function getUpperLimit()
276  {
277  return $this->upper_limit;
278  }
279 
280  public function setLowerLimit($a_limit)
281  {
282  $a_limit = str_replace(',', '.', $a_limit);
283  $this->lower_limit = $a_limit;
284  }
285 
286  public function setUpperLimit($a_limit)
287  {
288  $a_limit = str_replace(',', '.', $a_limit);
289  $this->upper_limit = $a_limit;
290  }
291 
297  public function getMaximumPoints()
298  {
299  return $this->getPoints();
300  }
301 
303  {
304  $points = 0;
305  if ($this->contains($previewSession->getParticipantsSolution())) {
306  $points = $this->getPoints();
307  }
308 
309  $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $points);
310 
311  return $this->ensureNonNegativePoints($reachedPoints);
312  }
313 
326  public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
327  {
328  if ($returndetails) {
329  throw new ilTestException('return details not implemented for ' . __METHOD__);
330  }
331 
333  global $DIC;
334  $ilDB = $DIC['ilDB'];
335 
336  $found_values = array();
337  if (is_null($pass)) {
338  $pass = $this->getSolutionMaxPass($active_id);
339  }
340  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
341  $data = $ilDB->fetchAssoc($result);
342 
343  $enteredvalue = $data["value1"];
344 
345  $points = 0;
346  if ($this->contains($enteredvalue)) {
347  $points = $this->getPoints();
348  }
349 
350  return $points;
351  }
352 
363  public function contains($value)
364  {
365  require_once './Services/Math/classes/class.EvalMath.php';
366  $eval = new EvalMath();
367  $eval->suppress_errors = true;
368  $result = $eval->e($value);
369  if (($result === false) || ($result === true)) {
370  return false;
371  }
372 
373  if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit()))) {
374  return true;
375  }
376  return false;
377  }
378 
379  protected function isValidNumericSubmitValue($submittedValue)
380  {
381  if (is_numeric($submittedValue)) {
382  return true;
383  }
384 
385  if (preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue)) {
386  return true;
387  }
388 
389  return false;
390  }
391 
392  public function validateSolutionSubmit()
393  {
394  if (strlen($this->getSolutionSubmit()) && !$this->isValidNumericSubmitValue($this->getSolutionSubmit())) {
395  ilUtil::sendFailure($this->lng->txt("err_no_numeric_value"), true);
396  return false;
397  }
398 
399  return true;
400  }
401 
402  public function getSolutionSubmit()
403  {
404  return trim(str_replace(",", ".", $_POST["numeric_result"]));
405  }
406 
407  public function isValidSolutionSubmit($numeric_solution)
408  {
409  require_once './Services/Math/classes/class.EvalMath.php';
410  $math = new EvalMath();
411  $math->suppress_errors = true;
412  $result = $math->evaluate($numeric_solution);
413 
414  return !(
415  ($result === false || $result === true) && strlen($numeric_solution) > 0
416  );
417  }
418 
427  public function saveWorkingData($active_id, $pass = null, $authorized = true)
428  {
430  global $DIC;
431  $ilDB = $DIC['ilDB'];
432 
433  if (is_null($pass)) {
434  require_once './Modules/Test/classes/class.ilObjTest.php';
435  $pass = ilObjTest::_getPass($active_id);
436  }
437 
438  $entered_values = 0;
439 
440  $returnvalue = true;
441 
442  $numeric_result = $this->getSolutionSubmit();
443 
444  $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $numeric_result, $ilDB, $active_id, $pass, $authorized) {
445  $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
446 
447  $row = $ilDB->fetchAssoc($result);
448  $update = $row["solution_id"];
449  if ($update) {
450  if (strlen($numeric_result)) {
451  $this->updateCurrentSolution($update, trim($numeric_result), null, $authorized);
452  $entered_values++;
453  } else {
454  $this->removeSolutionRecordById($update);
455  }
456  } else {
457  if (strlen($numeric_result)) {
458  $this->saveCurrentSolution($active_id, $pass, trim($numeric_result), null, $authorized);
459  $entered_values++;
460  }
461  }
462  });
463 
464  if ($entered_values) {
465  require_once './Modules/Test/classes/class.ilObjAssessmentFolder.php';
468  $this->lng->txtlng(
469  "assessment",
470  "log_user_entered_values",
472  ),
473  $active_id,
474  $this->getId()
475  );
476  }
477  } else {
478  include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
481  $this->lng->txtlng(
482  "assessment",
483  "log_user_not_entered_values",
485  ),
486  $active_id,
487  $this->getId()
488  );
489  }
490  }
491 
492  return $returnvalue;
493  }
494 
495  protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
496  {
497  $numericSolution = $this->getSolutionSubmit();
498  $previewSession->setParticipantsSolution($numericSolution);
499  }
500 
501  public function saveAdditionalQuestionDataToDb()
502  {
504  global $DIC;
505  $ilDB = $DIC['ilDB'];
506 
507  // save additional data
508  $ilDB->manipulateF(
509  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
510  array( "integer" ),
511  array( $this->getId() )
512  );
513 
514  $ilDB->manipulateF(
515  "INSERT INTO " . $this->getAdditionalTableName(
516  ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
517  array( "integer", "integer" ),
518  array(
519  $this->getId(),
520  ($this->getMaxChars()) ? $this->getMaxChars() : 0
521  )
522  );
523  }
524 
525  public function saveAnswerSpecificDataToDb()
526  {
528  global $DIC;
529  $ilDB = $DIC['ilDB'];
530 
531  // Write range to the database
532  $ilDB->manipulateF(
533  "DELETE FROM qpl_num_range WHERE question_fi = %s",
534  array( 'integer' ),
535  array( $this->getId() )
536  );
537 
538  $next_id = $ilDB->nextId('qpl_num_range');
539  $ilDB->manipulateF(
540  "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
541  VALUES (%s, %s, %s, %s, %s, %s, %s)",
542  array( 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ),
543  array( $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
544  ), $this->getPoints(), 0, time() )
545  );
546  }
547 
553  public function getQuestionType()
554  {
555  return "assNumeric";
556  }
557 
563  public function getMaxChars()
564  {
565  return $this->maxchars;
566  }
567 
573  public function setMaxChars($maxchars)
574  {
575  $this->maxchars = $maxchars;
576  }
577 
583  public function getAdditionalTableName()
584  {
585  return "qpl_qst_numeric";
586  }
587 
592  public function getRTETextWithMediaObjects()
593  {
594  return parent::getRTETextWithMediaObjects();
595  }
596 
600  public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
601  {
602  parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
603 
604  $solutions = $this->getSolutionValues($active_id, $pass);
605 
606  $i = 1;
607  $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
608  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
609 
610  $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
611  if (strlen($solutions[0]["value1"])) {
612  $worksheet->setCell($startrow + $i, 1, $solutions[0]["value1"]);
613  }
614  $i++;
615 
616  return $startrow + $i + 1;
617  }
618 
627  public function getOperators($expression)
628  {
629  require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
631  }
632 
637  public function getExpressionTypes()
638  {
639  return array(
643  );
644  }
645 
654  public function getUserQuestionResult($active_id, $pass)
655  {
657  global $DIC;
658  $ilDB = $DIC['ilDB'];
659  $result = new ilUserQuestionResult($this, $active_id, $pass);
660 
661  $maxStep = $this->lookupMaxStep($active_id, $pass);
662 
663  if ($maxStep !== null) {
664  $data = $ilDB->queryF(
665  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
666  array("integer", "integer", "integer","integer"),
667  array($active_id, $pass, $this->getId(), $maxStep)
668  );
669  } else {
670  $data = $ilDB->queryF(
671  "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
672  array("integer", "integer", "integer"),
673  array($active_id, $pass, $this->getId())
674  );
675  }
676 
677  while ($row = $ilDB->fetchAssoc($data)) {
678  $result->addKeyValue(1, $row["value1"]);
679  }
680 
681  $points = $this->calculateReachedPoints($active_id, $pass);
682  $max_points = $this->getMaximumPoints();
683 
684  $result->setReachedPercentage(($points / $max_points) * 100);
685 
686  return $result;
687  }
688 
697  public function getAvailableAnswerOptions($index = null)
698  {
699  return array(
700  "lower" => $this->getLowerLimit(),
701  "upper" => $this->getUpperLimit()
702  );
703  }
704 }
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.
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)
global $DIC
Definition: saml.php:7
Abstract basic class which is to be extended by the concrete assessment question type classes...
ensureNonNegativePoints($points)
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
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)
{}
$index
Definition: metadata.php:60
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.
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
removeSolutionRecordById($solutionId)
static _getLogLanguage()
retrieve the log language for assessment logging
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)
saveWorkingData($active_id, $pass=null, $authorized=true)
Saves the learners input of the question to the database.
Interface ilObjAnswerScoringAdjustable.
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)
setUpperLimit($a_limit)
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
$row
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...
$i
Definition: disco.tpl.php:19
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
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.
$data
Definition: bench.php:6