ILIAS  trunk Revision v12.0_alpha-1540-g00f839d5fa1
class.assNumeric.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
23
40{
41 protected $lower_limit;
42 protected $upper_limit;
43 public $maxchars = 6;
44
45 public function isComplete(): bool
46 {
47 if (
48 strlen($this->title)
49 && $this->author
50 && $this->question
51 && $this->getMaximumPoints() > 0
52 ) {
53 return true;
54 }
55 return false;
56 }
57
58 public function saveToDb(?int $original_id = null): void
59 {
63 parent::saveToDb($original_id);
64 }
65
66 public function loadFromDb(int $question_id): void
67 {
68 $result = $this->db->queryF(
69 "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",
70 ["integer"],
71 [$question_id]
72 );
73 if ($result->numRows() == 1) {
74 $data = $this->db->fetchAssoc($result);
75 $this->setId($question_id);
76 $this->setObjId($data["obj_fi"]);
77 $this->setTitle((string) $data["title"]);
78 $this->setComment((string) $data["description"]);
79 $this->setNrOfTries($data['nr_of_tries']);
80 $this->setOriginalId($data["original_id"]);
81 $this->setAuthor($data["author"]);
82 $this->setPoints($data["points"]);
83 $this->setOwner($data["owner"]);
84 $this->setQuestion(ilRTE::_replaceMediaObjectImageSrc((string) $data["question_text"], 1));
85 $this->setMaxChars($data["maxnumofchars"]);
86
87 try {
88 $this->setLifecycle(ilAssQuestionLifecycle::getInstance($data['lifecycle']));
91 }
92
93 try {
94 $this->setAdditionalContentEditingMode($data['add_cont_edit_mode']);
96 }
97 }
98
99 $result = $this->db->queryF(
100 "SELECT * FROM qpl_num_range WHERE question_fi = %s ORDER BY aorder ASC",
101 ['integer'],
102 [$question_id]
103 );
104
105 if ($result->numRows() > 0) {
107 while ($data = $this->db->fetchAssoc($result)) {
108 $this->setPoints($data['points']);
109 $this->setLowerLimit($data['lowerlimit']);
110 $this->setUpperLimit($data['upperlimit']);
111 }
112 }
113
114 parent::loadFromDb($question_id);
115 }
116
117 public function getLowerLimit()
118 {
119 return $this->lower_limit;
120 }
121
122 public function getUpperLimit()
123 {
124 return $this->upper_limit;
125 }
126
127 public function setLowerLimit(string $limit): void
128 {
129 $this->lower_limit = str_replace(',', '.', $limit);
130 }
131
132 public function setUpperLimit(string $limit): void
133 {
134 $this->upper_limit = str_replace(',', '.', $limit);
135 }
136
137 public function getMaximumPoints(): float
138 {
139 return $this->getPoints();
140 }
141
143 {
144 $points = 0;
145 if ($this->contains($previewSession->getParticipantsSolution())) {
146 $points = $this->getPoints();
147 }
148
149 return $this->ensureNonNegativePoints($points);
150 }
151
152 public function calculateReachedPoints(
153 int $active_id,
154 ?int $pass = null,
155 bool $authorized_solution = true
156 ): float {
157 if ($pass === null) {
158 $pass = $this->getSolutionMaxPass($active_id);
159 }
160 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized_solution);
161 $data = $this->db->fetchAssoc($result);
162 $enteredvalue = '';
163 if (is_array($data) && array_key_exists('value1', $data)) {
164 $enteredvalue = $data["value1"];
165 }
166
167 $points = 0.0;
168 if ($this->contains($enteredvalue)) {
169 $points = $this->getPoints();
170 }
171
172 return $points;
173 }
174
185 public function contains($value): bool
186 {
187 $eval = new EvalMath();
188 $eval->suppress_errors = true;
189 $result = $eval->e((string) $value);
190 if (($result === false) || ($result === true)) {
191 return false;
192 }
193
194 if (($result >= $eval->e($this->getLowerLimit())) && ($result <= $eval->e($this->getUpperLimit()))) {
195 return true;
196 }
197 return false;
198 }
199
200 public function validateSolutionSubmit(): bool
201 {
202 if ($this->getSolutionSubmit() === null) {
203 $this->tpl->setOnScreenMessage('failure', $this->lng->txt('err_no_numeric_value'), true);
204 return false;
205 }
206
207 return true;
208 }
209
210 protected function getSolutionSubmit(): ?float
211 {
212 return $this->questionpool_request->getNumericQuestionSolutionSubmit();
213 }
214
215 public function isValidSolutionSubmit($numeric_solution): bool
216 {
217 $math = new EvalMath();
218 $math->suppress_errors = true;
219 $result = $math->evaluate($numeric_solution);
220
221 return !(
222 ($result === false || $result === true) && strlen($numeric_solution) > 0
223 );
224 }
225
226 public function saveWorkingData(
227 int $active_id,
228 ?int $pass = null,
229 bool $authorized = true
230 ): bool {
231 $pass ??= ilObjTest::_getPass($active_id);
232
233 $answer = $this->getSolutionSubmit();
234 $this->getProcessLocker()->executeUserSolutionUpdateLockOperation(
235 function () use ($answer, $active_id, $pass, $authorized): void {
236 $result = $this->getCurrentSolutionResultSet($active_id, $pass, $authorized);
237 $update = -1;
238 if ($this->db->numRows($result) !== 0) {
239 $update = $this->db->fetchAssoc($result)['solution_id'];
240 }
241
242 if ($update === -1 && $answer === null) {
243 return;
244 }
245
246 if ($update === -1) {
247 $this->saveCurrentSolution(
248 $active_id,
249 $pass,
250 $answer,
251 null,
252 $authorized
253 );
254 return;
255 }
256
257 if ($answer === null) {
258 $this->removeSolutionRecordById($update);
259 return;
260 }
261
262 $this->updateCurrentSolution($update, $answer, null, $authorized);
263 }
264 );
265
266 return true;
267 }
268
269 protected function savePreviewData(ilAssQuestionPreviewSession $previewSession): void
270 {
271 $numericSolution = $this->getSolutionSubmit();
272 $previewSession->setParticipantsSolution($numericSolution);
273 }
274
276 {
277 // save additional data
278 $this->db->manipulateF(
279 "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
280 [ "integer" ],
281 [ $this->getId() ]
282 );
283
284 $this->db->manipulateF(
285 "INSERT INTO " . $this->getAdditionalTableName(
286 ) . " (question_fi, maxnumofchars) VALUES (%s, %s)",
287 [ "integer", "integer" ],
288 [
289 $this->getId(),
290 ($this->getMaxChars()) ? $this->getMaxChars() : 0
291 ]
292 );
293 }
294
296 {
297 // Write range to the database
298 $this->db->manipulateF(
299 "DELETE FROM qpl_num_range WHERE question_fi = %s",
300 [ 'integer' ],
301 [ $this->getId() ]
302 );
303
304 $next_id = $this->db->nextId('qpl_num_range');
305 $this->db->manipulateF(
306 "INSERT INTO qpl_num_range (range_id, question_fi, lowerlimit, upperlimit, points, aorder, tstamp)
307 VALUES (%s, %s, %s, %s, %s, %s, %s)",
308 [ 'integer', 'integer', 'text', 'text', 'float', 'integer', 'integer' ],
309 [ $next_id, $this->id, $this->getLowerLimit(), $this->getUpperLimit(
310 ), $this->getPoints(), 0, time() ]
311 );
312 }
313
319 public function getQuestionType(): string
320 {
321 return "assNumeric";
322 }
323
329 public function getMaxChars()
330 {
331 return $this->maxchars;
332 }
333
339 public function setMaxChars($maxchars): void
340 {
341 $this->maxchars = $maxchars;
342 }
343
349 public function getAdditionalTableName(): string
350 {
351 return "qpl_qst_numeric";
352 }
353
358 public function getRTETextWithMediaObjects(): string
359 {
360 return parent::getRTETextWithMediaObjects();
361 }
362
363 public function getOperators(string $expression): array
364 {
366 }
367
368 public function getExpressionTypes(): array
369 {
370 return [
374 ];
375 }
376
377 public function getUserQuestionResult(
378 int $active_id,
379 int $pass
381 $result = new ilUserQuestionResult($this, $active_id, $pass);
382
383 $maxStep = $this->lookupMaxStep($active_id, $pass);
384 if ($maxStep > 0) {
385 $data = $this->db->queryF(
386 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s AND step = %s",
387 ["integer", "integer", "integer","integer"],
388 [$active_id, $pass, $this->getId(), $maxStep]
389 );
390 } else {
391 $data = $this->db->queryF(
392 "SELECT value1 FROM tst_solutions WHERE active_fi = %s AND pass = %s AND question_fi = %s",
393 ["integer", "integer", "integer"],
394 [$active_id, $pass, $this->getId()]
395 );
396 }
397
398 while ($row = $this->db->fetchAssoc($data)) {
399 $result->addKeyValue(1, $row["value1"]);
400 }
401
402 $points = $this->calculateReachedPoints($active_id, $pass);
403 $max_points = $this->getMaximumPoints();
404
405 $result->setReachedPercentage(($points / $max_points) * 100);
406
407 return $result;
408 }
409
410 public function getAvailableAnswerOptions(?int $index = null): array
411 {
412 return [
413 "lower" => $this->getLowerLimit(),
414 "upper" => $this->getUpperLimit()
415 ];
416 }
417
418 public function getAnswerTableName(): string
419 {
420 return '';
421 }
422
423 public function toLog(AdditionalInformationGenerator $additional_info): array
424 {
425 return [
426 AdditionalInformationGenerator::KEY_QUESTION_TYPE => (string) $this->getQuestionType(),
427 AdditionalInformationGenerator::KEY_QUESTION_TITLE => $this->getTitleForHTMLOutput(),
428 AdditionalInformationGenerator::KEY_QUESTION_TEXT => $this->formatSAQuestion($this->getQuestion()),
429 AdditionalInformationGenerator::KEY_QUESTION_SHUFFLE_ANSWER_OPTIONS => $additional_info
430 ->getTrueFalseTagForBool($this->getShuffle()),
431 AdditionalInformationGenerator::KEY_QUESTION_MAXCHARS => $this->getMaxChars(),
432 AdditionalInformationGenerator::KEY_QUESTION_REACHABLE_POINTS => $this->getPoints(),
433 AdditionalInformationGenerator::KEY_QUESTION_LOWER_LIMIT => $this->getLowerLimit(),
434 AdditionalInformationGenerator::KEY_QUESTION_UPPER_LIMIT => $this->getUpperLimit(),
435 AdditionalInformationGenerator::KEY_FEEDBACK => [
436 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_INCOMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), false)),
437 AdditionalInformationGenerator::KEY_QUESTION_FEEDBACK_ON_COMPLETE => $this->formatSAQuestion($this->feedbackOBJ->getGenericFeedbackTestPresentation($this->getId(), true))
438 ]
439 ];
440 }
441
442 protected function solutionValuesToLog(
443 AdditionalInformationGenerator $additional_info,
444 array $solution_values
445 ): string {
446 if (!array_key_exists(0, $solution_values) ||
447 !array_key_exists('value1', $solution_values[0])) {
448 return '';
449 }
450 return $solution_values[0]['value1'];
451 }
452
453 public function solutionValuesToText(array $solution_values): string
454 {
455 if (!array_key_exists(0, $solution_values) ||
456 !array_key_exists('value1', $solution_values[0])) {
457 return '';
458 }
459 return $solution_values[0]['value1'];
460 }
461
462 public function getCorrectSolutionForTextOutput(int $active_id, int $pass): string
463 {
464 return "{$this->getLowerLimit()}-{$this->getUpperLimit()}";
465 }
466}
Class for numeric questions.
getExpressionTypes()
Get all available expression types for a specific question.
contains($value)
Checks for a given value within the range.
solutionValuesToText(array $solution_values)
MUST convert the given solution values into text.
setMaxChars($maxchars)
Sets the maximum number of characters for the numeric input field.
getOperators(string $expression)
Get all available operations for a specific question.
toLog(AdditionalInformationGenerator $additional_info)
MUST return an array of the question settings that can be stored in the log.
getUserQuestionResult(int $active_id, int $pass)
Get the user solution for a question by active_id and the test pass.
getQuestionType()
Returns the question type of the question.
getCorrectSolutionForTextOutput(int $active_id, int $pass)
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 ...
saveAnswerSpecificDataToDb()
Saves the answer specific records into a question types answer table.
setLowerLimit(string $limit)
isValidSolutionSubmit($numeric_solution)
saveAdditionalQuestionDataToDb()
Saves a record to the question types additional data table.
setUpperLimit(string $limit)
calculateReachedPoints(int $active_id, ?int $pass=null, bool $authorized_solution=true)
getAvailableAnswerOptions(?int $index=null)
If index is null, the function returns an array with all anwser options else it returns the specific ...
saveToDb(?int $original_id=null)
loadFromDb(int $question_id)
saveWorkingData(int $active_id, ?int $pass=null, bool $authorized=true)
solutionValuesToLog(AdditionalInformationGenerator $additional_info, array $solution_values)
MUST convert the given solution values into an array or a string that can be stored in the log.
setOriginalId(?int $original_id)
ensureNonNegativePoints(float $points)
setId(int $id=-1)
setAdditionalContentEditingMode(?string $additionalContentEditingMode)
setQuestion(string $question="")
getCurrentSolutionResultSet(int $active_id, int $pass, bool $authorized=true)
setAuthor(string $author="")
setComment(string $comment="")
setObjId(int $obj_id=0)
getSolutionMaxPass(int $active_id)
setOwner(int $owner=-1)
setNrOfTries(int $a_nr_of_tries)
setLifecycle(ilAssQuestionLifecycle $lifecycle)
setTitle(string $title="")
saveQuestionDataToDb(?int $original_id=null)
setPoints(float $points)
static getOperatorsByExpression(string $expression)
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...
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...
if(!file_exists('../ilias.ini.php'))