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
4require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
5require_once './Modules/Test/classes/inc.AssessmentConstants.php';
6require_once './Modules/TestQuestionPool/interfaces/interface.ilObjQuestionScoringAdjustable.php';
7require_once './Modules/TestQuestionPool/interfaces/interface.ilObjAnswerScoringAdjustable.php';
8require_once './Modules/TestQuestionPool/interfaces/interface.iQuestionCondition.php';
9require_once './Modules/TestQuestionPool/classes/class.ilUserQuestionResult.php';
10
27{
28 protected $lower_limit;
29 protected $upper_limit;
30
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}
$worksheet
$result
$_POST["username"]
An exception for terminatinating execution or to throw for unit testing.
Class for numeric questions.
duplicate($for_test=true, $title="", $author="", $owner="", $testObjId=null)
Duplicates an assNumericQuestion.
reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
{Reworks the allready saved working data if neccessary.}
getExpressionTypes()
Get all available expression types for a specific question.
copyObject($target_questionpool_id, $title="")
Copies an assNumeric object.
contains($value)
Checks for a given value within the range.
getMaximumPoints()
Returns the maximum points, a learner can reach answering the question.
setMaxChars($maxchars)
Sets the maximum number of characters for the numeric input field.
setLowerLimit($a_limit)
isValidNumericSubmitValue($submittedValue)
setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
{Creates an Excel worksheet for the detailed cumulated results of this question.object}
saveToDb($original_id="")
Saves a assNumeric object to a database.
setUpperLimit($a_limit)
getQuestionType()
Returns the question type of the question.
__construct( $title="", $comment="", $author="", $owner=-1, $question="")
assNumeric constructor
savePreviewData(ilAssQuestionPreviewSession $previewSession)
calculateReachedPointsFromPreviewSession(ilAssQuestionPreviewSession $previewSession)
getAdditionalTableName()
Returns the name of the additional question data table in the database.
getMaxChars()
Returns the maximum number of characters for the numeric input field.
getRTETextWithMediaObjects()
Collects all text in the question which could contain media objects which were created with the Rich ...
getOperators($expression)
Get all available operations for a specific question.
isValidSolutionSubmit($numeric_solution)
getAvailableAnswerOptions($index=null)
If index is null, the function returns an array with all anwser options Else it returns the specific ...
isComplete()
Returns true, if a numeric question is complete for use.
createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle="")
Abstract basic class which is to be extended by the concrete assessment question type classes.
getCurrentSolutionResultSet($active_id, $pass, $authorized=true)
Get a restulset for the current user solution for a this question by active_id and pass.
static _getOriginalId($question_id)
Returns the original id of a question.
setId($id=-1)
Sets the id of the assQuestion object.
setOriginalId($original_id)
setObjId($obj_id=0)
Set the object id of the container object.
getSolutionMaxPass($active_id)
Returns the maximum pass a users question solution.
saveQuestionDataToDb($original_id="")
getId()
Gets the id of the assQuestion object.
saveCurrentSolution($active_id, $pass, $value1, $value2, $authorized=true, $tstamp=null)
getObjId()
Get the object id of the container object.
setTitle($title="")
Sets the title string of the assQuestion object.
calculateReachedPoints($active_id, $pass=NULL, $authorizedSolution=true, $returndetails=FALSE)
Returns the points, a learner has reached answering the question.
setOwner($owner="")
Sets the creator/owner ID of the assQuestion object.
setEstimatedWorkingTime($hour=0, $min=0, $sec=0)
Sets the estimated working time of a question from given hour, minute and second.
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
static logAction($logtext="", $active_id="", $question_id="")
Logs an action into the Test&Assessment log.
updateCurrentSolution($solutionId, $value1, $value2, $authorized=true)
removeSolutionRecordById($solutionId)
setAuthor($author="")
Sets the authors name of the assQuestion object.
getPoints()
Returns the maximum available points for the question.
saveWorkingData($active_id, $pass=NULL, $authorized=true)
Saves the learners input of the question to the database.
getSolutionValues($active_id, $pass=NULL, $authorized=true)
Loads solutions of a given user from the database an returns it.
setPoints($a_points)
Sets the maximum available points for the question.
setComment($comment="")
Sets the comment string of the assQuestion object.
setNrOfTries($a_nr_of_tries)
setAdditionalContentEditingMode($additinalContentEditingMode)
setter for additional content editing mode for this question
setQuestion($question="")
Sets the question string of the question object.
loadFromDb($question_id)
Loads the question from the database.
ensureNonNegativePoints($points)
static _getLogLanguage()
retrieve the log language for assessment logging
static _enabledAssessmentLogging()
check wether assessment logging is enabled or not
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
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...
Base Exception for all Exceptions relating to Modules/Test.
Class ilUserQuestionResult.
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
e($cmd)
Definition: flush.php:14
Class iQuestionCondition.
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
Interface ilObjAnswerScoringAdjustable.
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
Interface ilObjQuestionScoringAdjustable.
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
global $ilDB