ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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
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 $ilDB;
96
97 $result = $ilDB->queryF(
98 "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",
99 array("integer"),
100 array($question_id)
101 );
102 if ($result->numRows() == 1) {
103 $data = $ilDB->fetchAssoc($result);
104 $this->setId($question_id);
105 $this->setObjId($data["obj_fi"]);
106 $this->setTitle($data["title"]);
107 $this->setComment($data["description"]);
108 $this->setNrOfTries($data['nr_of_tries']);
109 $this->setOriginalId($data["original_id"]);
110 $this->setAuthor($data["author"]);
111 $this->setPoints($data["points"]);
112 $this->setOwner($data["owner"]);
113 require_once './Services/RTE/classes/class.ilRTE.php';
114 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc($data["question_text"], 1));
115 $this->setMaxChars($data["maxnumofchars"]);
116 $this->setEstimatedWorkingTime(substr($data["working_time"], 0, 2), substr($data["working_time"], 3, 2), substr($data["working_time"], 6, 2));
117
118 try {
119 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
120 } catch (ilTestQuestionPoolException $e) {
121 }
122 }
123
124 $result = $ilDB->queryF(
125 "SELECT * FROM qpl_num_range WHERE question_fi = %s ORDER BY aorder ASC",
126 array('integer'),
127 array($question_id)
128 );
129
130 require_once './Modules/TestQuestionPool/classes/class.assNumericRange.php';
131 if ($result->numRows() > 0) {
133 while ($data = $ilDB->fetchAssoc($result)) {
134 $this->setPoints($data['points']);
135 $this->setLowerLimit($data['lowerlimit']);
136 $this->setUpperLimit($data['upperlimit']);
137 }
138 }
139
140 parent::loadFromDb($question_id);
141 }
142
154 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
155 {
156 if ($this->id <= 0) {
157 // The question has not been saved. It cannot be duplicated
158 return;
159 }
160 // duplicate the question in database
161 $this_id = $this->getId();
162 $thisObjId = $this->getObjId();
163
164 $clone = $this;
165 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
167 $clone->id = -1;
168
169 if ((int) $testObjId > 0) {
170 $clone->setObjId($testObjId);
171 }
172
173 if ($title) {
174 $clone->setTitle($title);
175 }
176
177 if ($author) {
178 $clone->setAuthor($author);
179 }
180 if ($owner) {
181 $clone->setOwner($owner);
182 }
183
184 if ($for_test) {
185 $clone->saveToDb($original_id);
186 } else {
187 $clone->saveToDb();
188 }
189
190 // copy question page content
191 $clone->copyPageOfQuestion($this_id);
192 // copy XHTML media objects
193 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
194
195 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
196
197 return $clone->id;
198 }
199
208 public function copyObject($target_questionpool_id, $title = "")
209 {
210 if ($this->id <= 0) {
211 // The question has not been saved. It cannot be duplicated
212 return;
213 }
214 // duplicate the question in database
215 $clone = $this;
216 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
218 $clone->id = -1;
219 $source_questionpool_id = $this->getObjId();
220 $clone->setObjId($target_questionpool_id);
221 if ($title) {
222 $clone->setTitle($title);
223 }
224 $clone->saveToDb();
225
226 // copy question page content
227 $clone->copyPageOfQuestion($original_id);
228 // copy XHTML media objects
229 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
230
231 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
232
233 return $clone->id;
234 }
235
236 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
237 {
238 if ($this->id <= 0) {
239 // The question has not been saved. It cannot be duplicated
240 return;
241 }
242
243 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
244
245 $sourceQuestionId = $this->id;
246 $sourceParentId = $this->getObjId();
247
248 // duplicate the question in database
249 $clone = $this;
250 $clone->id = -1;
251
252 $clone->setObjId($targetParentId);
253
254 if ($targetQuestionTitle) {
255 $clone->setTitle($targetQuestionTitle);
256 }
257
258 $clone->saveToDb();
259 // copy question page content
260 $clone->copyPageOfQuestion($sourceQuestionId);
261 // copy XHTML media objects
262 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
263
264 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
265
266 return $clone->id;
267 }
268
269 public function getLowerLimit()
270 {
271 return $this->lower_limit;
272 }
273
274 public function getUpperLimit()
275 {
276 return $this->upper_limit;
277 }
278
279 public function setLowerLimit($a_limit)
280 {
281 $a_limit = str_replace(',', '.', $a_limit);
282 $this->lower_limit = $a_limit;
283 }
284
285 public function setUpperLimit($a_limit)
286 {
287 $a_limit = str_replace(',', '.', $a_limit);
288 $this->upper_limit = $a_limit;
289 }
290
296 public function getMaximumPoints()
297 {
298 return $this->getPoints();
299 }
300
302 {
303 $points = 0;
304 if ($this->contains($previewSession->getParticipantsSolution())) {
305 $points = $this->getPoints();
306 }
307
308 $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $points);
309
310 return $this->ensureNonNegativePoints($reachedPoints);
311 }
312
325 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
326 {
327 if ($returndetails) {
328 throw new ilTestException('return details not implemented for ' . __METHOD__);
329 }
330
332 global $ilDB;
333
334 $found_values = array();
335 if (is_null($pass)) {
336 $pass = $this->getSolutionMaxPass($active_id);
337 }
338 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
339 $data = $ilDB->fetchAssoc($result);
340
341 $enteredvalue = $data["value1"];
342
343 $points = 0;
344 if ($this->contains($enteredvalue)) {
345 $points = $this->getPoints();
346 }
347
348 return $points;
349 }
350
361 public function contains($value)
362 {
363 require_once './Services/Math/classes/class.EvalMath.php';
364 $eval = new EvalMath();
365 $eval->suppress_errors = true;
366 $result = $eval->e($value);
367 if (($result === false) || ($result === true)) {
368 return false;
369 }
370
371 if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit()))) {
372 return true;
373 }
374 return false;
375 }
376
377 protected function isValidNumericSubmitValue($submittedValue)
378 {
379 if (is_numeric($submittedValue)) {
380 return true;
381 }
382
383 if (preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue)) {
384 return true;
385 }
386
387 return false;
388 }
389
390 public function validateSolutionSubmit()
391 {
392 if (strlen($this->getSolutionSubmit()) && !$this->isValidNumericSubmitValue($this->getSolutionSubmit())) {
393 ilUtil::sendFailure($this->lng->txt("err_no_numeric_value"), true);
394 return false;
395 }
396
397 return true;
398 }
399
400 public function getSolutionSubmit()
401 {
402 return trim(str_replace(",", ".", $_POST["numeric_result"]));
403 }
404
405 public function isValidSolutionSubmit($numeric_solution)
406 {
407 require_once './Services/Math/classes/class.EvalMath.php';
408 $math = new EvalMath();
409 $math->suppress_errors = true;
410 $result = $math->evaluate($numeric_solution);
411
412 return !(
413 ($result === false || $result === true) && strlen($numeric_solution) > 0
414 );
415 }
416
425 public function saveWorkingData($active_id, $pass = null, $authorized = true)
426 {
428 global $ilDB;
429
430 if (is_null($pass)) {
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 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $numeric_result, $ilDB, $active_id, $pass, $authorized) {
442 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
443
444 $row = $ilDB->fetchAssoc($result);
445 $update = $row["solution_id"];
446 if ($update) {
447 if (strlen($numeric_result)) {
448 $this->updateCurrentSolution($update, trim($numeric_result), null, $authorized);
449 $entered_values++;
450 } else {
451 $this->removeSolutionRecordById($update);
452 }
453 } else {
454 if (strlen($numeric_result)) {
455 $this->saveCurrentSolution($active_id, $pass, trim($numeric_result), null, $authorized);
456 $entered_values++;
457 }
458 }
459 });
460
461 if ($entered_values) {
462 require_once './Modules/Test/classes/class.ilObjAssessmentFolder.php';
465 $this->lng->txtlng(
466 "assessment",
467 "log_user_entered_values",
469 ),
470 $active_id,
471 $this->getId()
472 );
473 }
474 } else {
475 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
478 $this->lng->txtlng(
479 "assessment",
480 "log_user_not_entered_values",
482 ),
483 $active_id,
484 $this->getId()
485 );
486 }
487 }
488
489 return $returnvalue;
490 }
491
492 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
493 {
494 $numericSolution = $this->getSolutionSubmit();
495 $previewSession->setParticipantsSolution($numericSolution);
496 }
497
498 public function saveAdditionalQuestionDataToDb()
499 {
501 global $ilDB;
502
503 // save additional data
504 $ilDB->manipulateF(
505 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
506 array( "integer" ),
507 array( $this->getId() )
508 );
509
510 $ilDB->manipulateF(
511 "INSERT INTO " . $this->getAdditionalTableName(
512 ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
513 array( "integer", "integer" ),
514 array(
515 $this->getId(),
516 ($this->getMaxChars()) ? $this->getMaxChars() : 0
517 )
518 );
519 }
520
521 public function saveAnswerSpecificDataToDb()
522 {
524 global $ilDB;
525
526 // Write range to the database
527 $ilDB->manipulateF(
528 "DELETE FROM qpl_num_range WHERE question_fi = %s",
529 array( 'integer' ),
530 array( $this->getId() )
531 );
532
533 $next_id = $ilDB->nextId('qpl_num_range');
534 $ilDB->manipulateF(
535 "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
536 VALUES (%s, %s, %s, %s, %s, %s, %s)",
537 array( 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ),
538 array( $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
539 ), $this->getPoints(), 0, time() )
540 );
541 }
542
546 protected function reworkWorkingData($active_id, $pass, $obligationsAnswered, $authorized)
547 {
548 // nothing to rework!
549 }
550
556 public function getQuestionType()
557 {
558 return "assNumeric";
559 }
560
566 public function getMaxChars()
567 {
568 return $this->maxchars;
569 }
570
576 public function setMaxChars($maxchars)
577 {
578 $this->maxchars = $maxchars;
579 }
580
586 public function getAdditionalTableName()
587 {
588 return "qpl_qst_numeric";
589 }
590
596 {
597 return parent::getRTETextWithMediaObjects();
598 }
599
603 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
604 {
605 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
606
607 $solutions = $this->getSolutionValues($active_id, $pass);
608
609 $i = 1;
610 $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
611 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
612
613 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
614 if (strlen($solutions[0]["value1"])) {
615 $worksheet->setCell($startrow + $i, 1, $solutions[0]["value1"]);
616 }
617 $i++;
618
619 return $startrow + $i + 1;
620 }
621
630 public function getOperators($expression)
631 {
632 require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
634 }
635
640 public function getExpressionTypes()
641 {
642 return array(
646 );
647 }
648
657 public function getUserQuestionResult($active_id, $pass)
658 {
660 global $ilDB;
661 $result = new ilUserQuestionResult($this, $active_id, $pass);
662
663 $maxStep = $this->lookupMaxStep($active_id, $pass);
664
665 if ($maxStep !== null) {
666 $data = $ilDB->queryF(
667 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
668 array("integer", "integer", "integer","integer"),
669 array($active_id, $pass, $this->getId(), $maxStep)
670 );
671 } else {
672 $data = $ilDB->queryF(
673 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
674 array("integer", "integer", "integer"),
675 array($active_id, $pass, $this->getId())
676 );
677 }
678
679 while ($row = $ilDB->fetchAssoc($data)) {
680 $result->addKeyValue(1, $row["value1"]);
681 }
682
683 $points = $this->calculateReachedPoints($active_id, $pass);
684 $max_points = $this->getMaximumPoints();
685
686 $result->setReachedPercentage(($points/$max_points) * 100);
687
688 return $result;
689 }
690
699 public function getAvailableAnswerOptions($index = null)
700 {
701 return array(
702 "lower" => $this->getLowerLimit(),
703 "upper" => $this->getUpperLimit()
704 );
705 }
706}
$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.
getSolutionValues($active_id, $pass=null, $authorized=true)
Loads solutions of a given user from the database an returns it.
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.
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)
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
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.
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.
$i
Definition: disco.tpl.php:19
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.
$index
Definition: metadata.php:60
global $ilDB