ILIAS  release_8 Revision v8.24
class.assNumeric.php
Go to the documentation of this file.
1<?php
2
19require_once './Modules/Test/classes/inc.AssessmentConstants.php';
20
37{
38 protected $lower_limit;
39 protected $upper_limit;
40
42 public $maxchars;
43
55 public function __construct(
56 $title = "",
57 $comment = "",
58 $author = "",
59 $owner = -1,
60 $question = ""
61 ) {
63 $this->maxchars = 6;
64 }
65
71 public function isComplete(): bool
72 {
73 if (
74 strlen($this->title)
75 && $this->author
76 && $this->question
77 && $this->getMaximumPoints() > 0
78 ) {
79 return true;
80 }
81 return false;
82 }
83
89 public function saveToDb($original_id = ""): void
90 {
91 if ($original_id == "") {
92 $this->saveQuestionDataToDb();
93 } else {
95 }
96
99 parent::saveToDb($original_id);
100 }
101
107 public function loadFromDb($question_id): void
108 {
110 global $DIC;
111 $ilDB = $DIC['ilDB'];
112
113 $result = $ilDB->queryF(
114 "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",
115 array("integer"),
116 array($question_id)
117 );
118 if ($result->numRows() == 1) {
119 $data = $ilDB->fetchAssoc($result);
120 $this->setId($question_id);
121 $this->setObjId($data["obj_fi"]);
122 $this->setTitle((string) $data["title"]);
123 $this->setComment((string) $data["description"]);
124 $this->setNrOfTries($data['nr_of_tries']);
125 $this->setOriginalId($data["original_id"]);
126 $this->setAuthor($data["author"]);
127 $this->setPoints($data["points"]);
128 $this->setOwner($data["owner"]);
129 require_once './Services/RTE/classes/class.ilRTE.php';
130 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
131 $this->setMaxChars($data["maxnumofchars"]);
132
133 try {
137 }
138
139 try {
140 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
142 }
143 }
144
145 $result = $ilDB->queryF(
146 "SELECT * FROM qpl_num_range WHERE question_fi = %s ORDER BY aorder ASC",
147 array('integer'),
148 array($question_id)
149 );
150
151 require_once './Modules/TestQuestionPool/classes/class.assNumericRange.php';
152 if ($result->numRows() > 0) {
154 while ($data = $ilDB->fetchAssoc($result)) {
155 $this->setPoints($data['points']);
156 $this->setLowerLimit($data['lowerlimit']);
157 $this->setUpperLimit($data['upperlimit']);
158 }
159 }
160
161 parent::loadFromDb($question_id);
162 }
163
175 public function duplicate(bool $for_test = true, string $title = "", string $author = "", string $owner = "", $testObjId = null): int
176 {
177 if ($this->id <= 0) {
178 // The question has not been saved. It cannot be duplicated
179 return -1;
180 }
181 // duplicate the question in database
182 $this_id = $this->getId();
183 $thisObjId = $this->getObjId();
184
185 $clone = $this;
186 require_once './Modules/TestQuestionPool/classes/class.assQuestion.php';
188 $clone->id = -1;
189
190 if ((int) $testObjId > 0) {
191 $clone->setObjId($testObjId);
192 }
193
194 if ($title) {
195 $clone->setTitle($title);
196 }
197
198 if ($author) {
199 $clone->setAuthor($author);
200 }
201 if ($owner) {
202 $clone->setOwner($owner);
203 }
204
205 if ($for_test) {
206 $clone->saveToDb($original_id);
207 } else {
208 $clone->saveToDb();
209 }
210
211 // copy question page content
212 $clone->copyPageOfQuestion($this_id);
213 // copy XHTML media objects
214 $clone->copyXHTMLMediaObjectsOfQuestion($this_id);
215
216 $clone->onDuplicate($thisObjId, $this_id, $clone->getObjId(), $clone->getId());
217
218 return $clone->id;
219 }
220
229 public function copyObject($target_questionpool_id, $title = "")
230 {
231 if ($this->id <= 0) {
232 // The question has not been saved. It cannot be duplicated
233 return;
234 }
235 // duplicate the question in database
236 $clone = $this;
237 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
239 $clone->id = -1;
240 $source_questionpool_id = $this->getObjId();
241 $clone->setObjId($target_questionpool_id);
242 if ($title) {
243 $clone->setTitle($title);
244 }
245 $clone->saveToDb();
246
247 // copy question page content
248 $clone->copyPageOfQuestion($original_id);
249 // copy XHTML media objects
250 $clone->copyXHTMLMediaObjectsOfQuestion($original_id);
251
252 $clone->onCopy($source_questionpool_id, $original_id, $clone->getObjId(), $clone->getId());
253
254 return $clone->id;
255 }
256
257 public function createNewOriginalFromThisDuplicate($targetParentId, $targetQuestionTitle = ""): int
258 {
259 if ($this->getId() <= 0) {
260 throw new RuntimeException('The question has not been saved. It cannot be duplicated');
261 }
262
263 include_once("./Modules/TestQuestionPool/classes/class.assQuestion.php");
264
265 $sourceQuestionId = $this->id;
266 $sourceParentId = $this->getObjId();
267
268 // duplicate the question in database
269 $clone = $this;
270 $clone->id = -1;
271
272 $clone->setObjId($targetParentId);
273
274 if ($targetQuestionTitle) {
275 $clone->setTitle($targetQuestionTitle);
276 }
277
278 $clone->saveToDb();
279 // copy question page content
280 $clone->copyPageOfQuestion($sourceQuestionId);
281 // copy XHTML media objects
282 $clone->copyXHTMLMediaObjectsOfQuestion($sourceQuestionId);
283
284 $clone->onCopy($sourceParentId, $sourceQuestionId, $clone->getObjId(), $clone->getId());
285
286 return $clone->id;
287 }
288
289 public function getLowerLimit()
290 {
291 return $this->lower_limit;
292 }
293
294 public function getUpperLimit()
295 {
296 return $this->upper_limit;
297 }
298
299 public function setLowerLimit($a_limit): void
300 {
301 $a_limit = str_replace(',', '.', $a_limit);
302 $this->lower_limit = $a_limit;
303 }
304
305 public function setUpperLimit($a_limit): void
306 {
307 $a_limit = str_replace(',', '.', $a_limit);
308 $this->upper_limit = $a_limit;
309 }
310
316 public function getMaximumPoints(): float
317 {
318 return $this->getPoints();
319 }
320
322 {
323 $points = 0;
324 if ($this->contains($previewSession->getParticipantsSolution())) {
325 $points = $this->getPoints();
326 }
327
328 $reachedPoints = $this->deductHintPointsFromReachedPoints($previewSession, $points);
329
330 return $this->ensureNonNegativePoints($reachedPoints);
331 }
332
345 public function calculateReachedPoints($active_id, $pass = null, $authorizedSolution = true, $returndetails = false)
346 {
347 if ($returndetails) {
348 throw new ilTestException('return details not implemented for ' . __METHOD__);
349 }
350
352 global $DIC;
353 $ilDB = $DIC['ilDB'];
354
355 $found_values = array();
356 if (is_null($pass)) {
357 $pass = $this->getSolutionMaxPass($active_id);
358 }
359 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorizedSolution);
360 $data = $ilDB->fetchAssoc($result);
361 $enteredvalue = '';
362 if (is_array($data) && array_key_exists('value1', $data)) {
363 $enteredvalue = $data["value1"];
364 }
365
366 $points = 0;
367 if ($this->contains($enteredvalue)) {
368 $points = $this->getPoints();
369 }
370
371 return $points;
372 }
373
384 public function contains($value): bool
385 {
386 require_once './Services/Math/classes/class.EvalMath.php';
387 $eval = new EvalMath();
388 $eval->suppress_errors = true;
389 $result = $eval->e($value);
390 if (($result === false) || ($result === true)) {
391 return false;
392 }
393
394 if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit()))) {
395 return true;
396 }
397 return false;
398 }
399
400 protected function isValidNumericSubmitValue($submittedValue): bool
401 {
402 if (is_numeric($submittedValue)) {
403 return true;
404 }
405
406 if (preg_match('/^[-+]{0,1}\d+\/\d+$/', $submittedValue)) {
407 return true;
408 }
409
410 return false;
411 }
412
413 public function validateSolutionSubmit(): bool
414 {
415 if (strlen($this->getSolutionSubmit()) && !$this->isValidNumericSubmitValue($this->getSolutionSubmit())) {
416 $this->tpl->setOnScreenMessage('failure', $this->lng->txt("err_no_numeric_value"), true);
417 return false;
418 }
419
420 return true;
421 }
422
423 public function getSolutionSubmit(): string
424 {
425 return trim(str_replace(",", ".", $_POST["numeric_result"]));
426 }
427
428 public function isValidSolutionSubmit($numeric_solution): bool
429 {
430 require_once './Services/Math/classes/class.EvalMath.php';
431 $math = new EvalMath();
432 $math->suppress_errors = true;
433 $result = $math->evaluate($numeric_solution);
434
435 return !(
436 ($result === false || $result === true) && strlen($numeric_solution) > 0
437 );
438 }
439
448 public function saveWorkingData($active_id, $pass = null, $authorized = true): bool
449 {
451 global $DIC;
452 $ilDB = $DIC['ilDB'];
453
454 if (is_null($pass)) {
455 require_once './Modules/Test/classes/class.ilObjTest.php';
456 $pass = ilObjTest::_getPass($active_id);
457 }
458
459 $entered_values = 0;
460
461 $returnvalue = true;
462
463 $numeric_result = $this->getSolutionSubmit();
464
465 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(function () use (&$entered_values, $numeric_result, $ilDB, $active_id, $pass, $authorized) {
466 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
467
468 $update = -1;
469 if ($ilDB->numRows($result) != 0) {
470 $row = $ilDB->fetchAssoc($result);
471 $update = $row["solution_id"];
472 }
473
474 if ($update != -1) {
475 if (strlen($numeric_result)) {
476 $this->updateCurrentSolution($update, trim($numeric_result), null, $authorized);
477 $entered_values++;
478 } else {
480 }
481 } else {
482 if (strlen($numeric_result)) {
483 $this->saveCurrentSolution($active_id, $pass, trim($numeric_result), null, $authorized);
484 $entered_values++;
485 }
486 }
487 });
488
489 if ($entered_values) {
490 require_once './Modules/Test/classes/class.ilObjAssessmentFolder.php';
493 $this->lng->txtlng(
494 "assessment",
495 "log_user_entered_values",
497 ),
498 $active_id,
499 $this->getId()
500 );
501 }
502 } else {
503 include_once("./Modules/Test/classes/class.ilObjAssessmentFolder.php");
506 $this->lng->txtlng(
507 "assessment",
508 "log_user_not_entered_values",
510 ),
511 $active_id,
512 $this->getId()
513 );
514 }
515 }
516
517 return $returnvalue;
518 }
519
520 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void
521 {
522 $numericSolution = $this->getSolutionSubmit();
523 $previewSession->setParticipantsSolution($numericSolution);
524 }
525
526 public function saveAdditionalQuestionDataToDb()
527 {
529 global $DIC;
530 $ilDB = $DIC['ilDB'];
531
532 // save additional data
533 $ilDB->manipulateF(
534 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
535 array( "integer" ),
536 array( $this->getId() )
537 );
538
539 $ilDB->manipulateF(
540 "INSERT INTO " . $this->getAdditionalTableName(
541 ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
542 array( "integer", "integer" ),
543 array(
544 $this->getId(),
545 ($this->getMaxChars()) ? $this->getMaxChars() : 0
546 )
547 );
548 }
549
550 public function saveAnswerSpecificDataToDb()
551 {
553 global $DIC;
554 $ilDB = $DIC['ilDB'];
555
556 // Write range to the database
557 $ilDB->manipulateF(
558 "DELETE FROM qpl_num_range WHERE question_fi = %s",
559 array( 'integer' ),
560 array( $this->getId() )
561 );
562
563 $next_id = $ilDB->nextId('qpl_num_range');
564 $ilDB->manipulateF(
565 "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
566 VALUES (%s, %s, %s, %s, %s, %s, %s)",
567 array( 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ),
568 array( $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
569 ), $this->getPoints(), 0, time() )
570 );
571 }
572
578 public function getQuestionType(): string
579 {
580 return "assNumeric";
581 }
582
588 public function getMaxChars()
589 {
590 return $this->maxchars;
591 }
592
598 public function setMaxChars($maxchars): void
599 {
600 $this->maxchars = $maxchars;
601 }
602
608 public function getAdditionalTableName(): string
609 {
610 return "qpl_qst_numeric";
611 }
612
617 public function getRTETextWithMediaObjects(): string
618 {
619 return parent::getRTETextWithMediaObjects();
620 }
621
625 public function setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass): int
626 {
627 parent::setExportDetailsXLS($worksheet, $startrow, $active_id, $pass);
628
629 $solutions = $this->getSolutionValues($active_id, $pass);
630
631 $i = 1;
632 $worksheet->setCell($startrow + $i, 0, $this->lng->txt("result"));
633 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
634
635 $worksheet->setBold($worksheet->getColumnCoord(0) . ($startrow + $i));
636 if (array_key_exists(0, $solutions) &&
637 array_key_exists('value1', $solutions[0]) &&
638 strlen($solutions[0]["value1"])) {
639 $worksheet->setCell($startrow + $i, 2, $solutions[0]["value1"]);
640 }
641 $i++;
642
643 return $startrow + $i + 1;
644 }
645
654 public function getOperators($expression): array
655 {
657 }
658
663 public function getExpressionTypes(): array
664 {
665 return array(
669 );
670 }
671
680 public function getUserQuestionResult($active_id, $pass): ilUserQuestionResult
681 {
683 global $DIC;
684 $ilDB = $DIC['ilDB'];
685 $result = new ilUserQuestionResult($this, $active_id, $pass);
686
687 $maxStep = $this->lookupMaxStep($active_id, $pass);
688
689 if ($maxStep !== null) {
690 $data = $ilDB->queryF(
691 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
692 array("integer", "integer", "integer","integer"),
693 array($active_id, $pass, $this->getId(), $maxStep)
694 );
695 } else {
696 $data = $ilDB->queryF(
697 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
698 array("integer", "integer", "integer"),
699 array($active_id, $pass, $this->getId())
700 );
701 }
702
703 while ($row = $ilDB->fetchAssoc($data)) {
704 $result->addKeyValue(1, $row["value1"]);
705 }
706
707 $points = $this->calculateReachedPoints($active_id, $pass);
708 $max_points = $this->getMaximumPoints();
709
710 $result->setReachedPercentage(($points / $max_points) * 100);
711
712 return $result;
713 }
714
723 public function getAvailableAnswerOptions($index = null)
724 {
725 return array(
726 "lower" => $this->getLowerLimit(),
727 "upper" => $this->getUpperLimit()
728 );
729 }
730}
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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)
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)
duplicate(bool $for_test=true, string $title="", string $author="", string $owner="", $testObjId=null)
Duplicates an assNumericQuestion.
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)
setExportDetailsXLS(ilAssExcelFormatHelper $worksheet, int $startrow, int $active_id, int $pass)
{}
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.
loadFromDb(int $question_id)
float $points
The maximum available points for the question.
setOriginalId(?int $original_id)
string $question
The question text.
static logAction(string $logtext, int $active_id, int $question_id)
setId(int $id=-1)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
saveCurrentSolution(int $active_id, int $pass, $value1, $value2, bool $authorized=true, $tstamp=0)
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
updateCurrentSolution(int $solutionId, $value1, $value2, bool $authorized=true)
deductHintPointsFromReachedPoints(ilAssQuestionPreviewSession $previewSession, $reachedPoints)
setQuestion(string $question="")
calculateReachedPoints($active_id, $pass=null, $authorizedSolution=true, $returndetails=false)
Returns the points, a learner has reached answering the question.
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
static _getOriginalId(int $question_id)
saveQuestionDataToDb(int $original_id=-1)
setAuthor(string $author="")
setComment(string $comment="")
setObjId(int $obj_id=0)
removeSolutionRecordById(int $solutionId)
getSolutionMaxPass(int $active_id)
setOwner(int $owner=-1)
setNrOfTries(int $a_nr_of_tries)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
saveWorkingData(int $active_id, int $pass, bool $authorized=true)
Saves the learners input of the question to the database.
setTitle(string $title="")
lookupMaxStep(int $active_id, int $pass)
setPoints(float $points)
ensureNonNegativePoints($points)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setCell($a_row, $a_col, $a_value, $datatype=null)
setBold(string $a_coords)
Set cell(s) to bold.
getColumnCoord(int $a_col)
Get column "name" from number.
static _getPass($active_id)
Retrieves the actual pass of a given user for a given test.
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
global $DIC
Definition: feed.php:28
$update
Definition: imgupload.php:92
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getUserQuestionResult($active_id, $pass)
Get the user solution for a question by active_id and the test pass.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
$index
Definition: metadata.php:145
$i
Definition: metadata.php:41
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc