ILIAS  release_7 Revision v7.30-3-g800a261c036
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 ) {
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 {
123 }
124
125 try {
126 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
128 }
129 }
130
131 $result = $ilDB->queryF(
132 "SELECT * FROM qpl_num_range WHERE question_fi = %s ORDER BY aorder ASC",
133 array('integer'),
134 array($question_id)
135 );
136
137 require_once './Modules/TestQuestionPool/classes/class.assNumericRange.php';
138 if ($result->numRows() > 0) {
140 while ($data = $ilDB->fetchAssoc($result)) {
141 $this->setPoints($data['points']);
142 $this->setLowerLimit($data['lowerlimit']);
143 $this->setUpperLimit($data['upperlimit']);
144 }
145 }
146
147 parent::loadFromDb($question_id);
148 }
149
161 public function duplicate($for_test = true, $title = "", $author = "", $owner = "", $testObjId = null)
162 {
163 if ($this->id <= 0) {
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 $clone->setObjId($testObjId);
178 }
179
180 if ($title) {
181 $clone->setTitle($title);
182 }
183
184 if ($author) {
185 $clone->setAuthor($author);
186 }
187 if ($owner) {
188 $clone->setOwner($owner);
189 }
190
191 if ($for_test) {
192 $clone->saveToDb($original_id);
193 } else {
194 $clone->saveToDb();
195 }
196
197 // copy question page content
198 $clone->copyPageOfQuestion($this_id);
199 // copy XHTML media objects
200 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
201
202 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
203
204 return $clone->id;
205 }
206
215 public function copyObject($target_questionpool_id, $title = "")
216 {
217 if ($this->id <= 0) {
218 // The question has not been saved. It cannot be duplicated
219 return;
220 }
221 // duplicate the question in database
222 $clone = $this;
223 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
225 $clone->id = -1;
226 $source_questionpool_id = $this->getObjId();
227 $clone->setObjId($target_questionpool_id);
228 if ($title) {
229 $clone->setTitle($title);
230 }
231 $clone->saveToDb();
232
233 // copy question page content
234 $clone->copyPageOfQuestion($original_id);
235 // copy XHTML media objects
236 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
237
238 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
239
240 return $clone->id;
241 }
242
243 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = "")
244 {
245 if ($this->id <= 0) {
246 // The question has not been saved. It cannot be duplicated
247 return;
248 }
249
250 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
251
252 $sourceQuestionId = $this->id;
253 $sourceParentId = $this->getObjId();
254
255 // duplicate the question in database
256 $clone = $this;
257 $clone->id = -1;
258
259 $clone->setObjId($targetParentId);
260
261 if ($targetQuestionTitle) {
262 $clone->setTitle($targetQuestionTitle);
263 }
264
265 $clone->saveToDb();
266 // copy question page content
267 $clone->copyPageOfQuestion($sourceQuestionId);
268 // copy XHTML media objects
269 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
270
271 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
272
273 return $clone->id;
274 }
275
276 public function getLowerLimit()
277 {
278 return $this->lower_limit;
279 }
280
281 public function getUpperLimit()
282 {
283 return $this->upper_limit;
284 }
285
286 public function setLowerLimit($a_limit)
287 {
288 $a_limit = str_replace(',', '.', $a_limit);
289 $this->lower_limit = $a_limit;
290 }
291
292 public function setUpperLimit($a_limit)
293 {
294 $a_limit = str_replace(',', '.', $a_limit);
295 $this->upper_limit = $a_limit;
296 }
297
303 public function getMaximumPoints()
304 {
305 return $this->getPoints();
306 }
307
309 {
310 $points = 0;
311 if ($this->contains($previewSession->getParticipantsSolution())) {
312 $points = $this->getPoints();
313 }
314
315 $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $points);
316
317 return $this->ensureNonNegativePoints($reachedPoints);
318 }
319
332 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
333 {
334 if ($returndetails) {
335 throw new ilTestException('return details not implemented for ' . __METHOD__);
336 }
337
339 global $DIC;
340 $ilDB = $DIC['ilDB'];
341
342 $found_values = array();
343 if (is_null($pass)) {
344 $pass = $this->getSolutionMaxPass($active_id);
345 }
346 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
347 $data = $ilDB->fetchAssoc($result);
348
349 $enteredvalue = $data["value1"];
350
351 $points = 0;
352 if ($this->contains($enteredvalue)) {
353 $points = $this->getPoints();
354 }
355
356 return $points;
357 }
358
369 public function contains($value)
370 {
371 require_once './Services/Math/classes/class.EvalMath.php';
372 $eval = new EvalMath();
373 $eval->suppress_errors = true;
374 $result = $eval->e($value);
375 if (($result === false) || ($result === true)) {
376 return false;
377 }
378
379 if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit()))) {
380 return true;
381 }
382 return false;
383 }
384
385 protected function isValidNumericSubmitValue($submittedValue)
386 {
387 if (is_numeric($submittedValue)) {
388 return true;
389 }
390
391 if (preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue)) {
392 return true;
393 }
394
395 return false;
396 }
397
398 public function validateSolutionSubmit()
399 {
400 if (strlen($this->getSolutionSubmit()) && !$this->isValidNumericSubmitValue($this->getSolutionSubmit())) {
401 ilUtil::sendFailure($this->lng->txt("err_no_numeric_value"), true);
402 return false;
403 }
404
405 return true;
406 }
407
408 public function getSolutionSubmit()
409 {
410 return trim(str_replace(",", ".", $_POST["numeric_result"]));
411 }
412
413 public function isValidSolutionSubmit($numeric_solution)
414 {
415 require_once './Services/Math/classes/class.EvalMath.php';
416 $math = new EvalMath();
417 $math->suppress_errors = true;
418 $result = $math->evaluate($numeric_solution);
419
420 return !(
421 ($result === false || $result === true) && strlen($numeric_solution) > 0
422 );
423 }
424
433 public function saveWorkingData($active_id, $pass = null, $authorized = true)
434 {
436 global $DIC;
437 $ilDB = $DIC['ilDB'];
438
439 if (is_null($pass)) {
440 require_once './Modules/Test/classes/class.ilObjTest.php';
441 $pass = ilObjTest::_getPass($active_id);
442 }
443
444 $entered_values = 0;
445
446 $returnvalue = true;
447
448 $numeric_result = $this->getSolutionSubmit();
449
450 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $numeric_result, $ilDB, $active_id, $pass, $authorized) {
451 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
452
453 $row = $ilDB->fetchAssoc($result);
454 $update = $row["solution_id"];
455 if ($update) {
456 if (strlen($numeric_result)) {
457 $this->updateCurrentSolution($update, trim($numeric_result), null, $authorized);
458 $entered_values++;
459 } else {
460 $this->removeSolutionRecordById($update);
461 }
462 } else {
463 if (strlen($numeric_result)) {
464 $this->saveCurrentSolution($active_id, $pass, trim($numeric_result), null, $authorized);
465 $entered_values++;
466 }
467 }
468 });
469
470 if ($entered_values) {
471 require_once './Modules/Test/classes/class.ilObjAssessmentFolder.php';
474 $this->lng->txtlng(
475 "assessment",
476 "log_user_entered_values",
478 ),
479 $active_id,
480 $this->getId()
481 );
482 }
483 } else {
484 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
487 $this->lng->txtlng(
488 "assessment",
489 "log_user_not_entered_values",
491 ),
492 $active_id,
493 $this->getId()
494 );
495 }
496 }
497
498 return $returnvalue;
499 }
500
501 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession)
502 {
503 $numericSolution = $this->getSolutionSubmit();
504 $previewSession->setParticipantsSolution($numericSolution);
505 }
506
507 public function saveAdditionalQuestionDataToDb()
508 {
510 global $DIC;
511 $ilDB = $DIC['ilDB'];
512
513 // save additional data
514 $ilDB->manipulateF(
515 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
516 array( "integer" ),
517 array( $this->getId() )
518 );
519
520 $ilDB->manipulateF(
521 "INSERT INTO " . $this->getAdditionalTableName(
522 ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
523 array( "integer", "integer" ),
524 array(
525 $this->getId(),
526 ($this->getMaxChars()) ? $this->getMaxChars() : 0
527 )
528 );
529 }
530
531 public function saveAnswerSpecificDataToDb()
532 {
534 global $DIC;
535 $ilDB = $DIC['ilDB'];
536
537 // Write range to the database
538 $ilDB->manipulateF(
539 "DELETE FROM qpl_num_range WHERE question_fi = %s",
540 array( 'integer' ),
541 array( $this->getId() )
542 );
543
544 $next_id = $ilDB->nextId('qpl_num_range');
545 $ilDB->manipulateF(
546 "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
547 VALUES (%s, %s, %s, %s, %s, %s, %s)",
548 array( 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ),
549 array( $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
550 ), $this->getPoints(), 0, time() )
551 );
552 }
553
559 public function getQuestionType()
560 {
561 return "assNumeric";
562 }
563
569 public function getMaxChars()
570 {
571 return $this->maxchars;
572 }
573
579 public function setMaxChars($maxchars)
580 {
581 $this->maxchars = $maxchars;
582 }
583
589 public function getAdditionalTableName()
590 {
591 return "qpl_qst_numeric";
592 }
593
599 {
600 return parent::getRTETextWithMediaObjects();
601 }
602
606 public function setExportDetailsXLS($worksheet, $startrow, $active_id, $pass)
607 {
608 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
609
610 $solutions = $this->getSolutionValues($active_id, $pass);
611
612 $i = 1;
613 $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
614 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
615
616 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
617 if (strlen($solutions[0]["value1"])) {
618 $worksheet->setCell($startrow + $i, 2, $solutions[0]["value1"]);
619 }
620 $i++;
621
622 return $startrow + $i + 1;
623 }
624
633 public function getOperators($expression)
634 {
635 require_once "./Modules/TestQuestionPool/classes/class.ilOperatorsExpressionMapping.php";
637 }
638
643 public function getExpressionTypes()
644 {
645 return array(
649 );
650 }
651
660 public function getUserQuestionResult($active_id, $pass)
661 {
663 global $DIC;
664 $ilDB = $DIC['ilDB'];
665 $result = new ilUserQuestionResult($this, $active_id, $pass);
666
667 $maxStep = $this->lookupMaxStep($active_id, $pass);
668
669 if ($maxStep !== null) {
670 $data = $ilDB->queryF(
671 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
672 array("integer", "integer", "integer","integer"),
673 array($active_id, $pass, $this->getId(), $maxStep)
674 );
675 } else {
676 $data = $ilDB->queryF(
677 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
678 array("integer", "integer", "integer"),
679 array($active_id, $pass, $this->getId())
680 );
681 }
682
683 while ($row = $ilDB->fetchAssoc($data)) {
684 $result->addKeyValue(1, $row["value1"]);
685 }
686
687 $points = $this->calculateReachedPoints($active_id, $pass);
688 $max_points = $this->getMaximumPoints();
689
690 $result->setReachedPercentage(($points / $max_points) * 100);
691
692 return $result;
693 }
694
703 public function getAvailableAnswerOptions($index = null)
704 {
705 return array(
706 "lower" => $this->getLowerLimit(),
707 "upper" => $this->getUpperLimit()
708 );
709 }
710}
$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.
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.
setLifecycle(ilAssQuestionLifecycle $lifecycle)
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.
global $DIC
Definition: goto.php:24
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:128
$i
Definition: metadata.php:24
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
global $ilDB
$data
Definition: storeScorm.php:23