19 declare(strict_types=1);
39 $this->cache = $global_cache->get(
new BaseRequest(
'test_result'));
47 $result = $this->db->queryF(
48 'SELECT tst_result_cache.active_fi AS active_id, tst_active.user_fi AS user_id FROM tst_result_cache' . PHP_EOL
49 .
'INNER JOIN tst_active ON tst_active.active_id = tst_result_cache.active_fi' . PHP_EOL
50 .
'INNER JOIN tst_tests ON tst_tests.test_id = tst_active.test_fi' . PHP_EOL
51 .
'WHERE tst_tests.obj_fi = %s AND tst_result_cache.passed_once = 1' . PHP_EOL,
55 return $this->db->fetchAll($result);
75 $result = $this->db->queryF(
76 'SELECT tst_result_cache.*, tst_active.test_fi AS test_id FROM tst_result_cache' . PHP_EOL
77 .
'JOIN tst_active ON tst_result_cache.active_fi = tst_active.active_id' . PHP_EOL
78 .
'WHERE active_fi = %s',
90 if (!$attempt_result) {
95 $status = StatusOfAttempt::build(
97 $attempt_result[
'last_finished_pass'],
98 $attempt_result[
'finalized_by'],
102 $callback =
function () use ($result) {
117 [
'active_fi' => $result->getActiveId()],
122 if (is_object($process_locker)) {
123 $process_locker->executeUserTestResultUpdateLockOperation($callback);
129 $attempt_result[
'user_id'],
130 $attempt_result[
'test_obj_id'],
132 'passed' => $result->isPassed(),
133 'failed' => $result->isFailed(),
134 'finished' => $status->isFinished(),
143 $result = $this->db->queryF(
144 "SELECT * FROM tst_pass_result WHERE active_fi = %s",
155 ?
int $test_obj_id =
null,
156 bool $update_result_cache_table =
true 169 $callback =
function () use ($result_object, $attempt) {
189 if (is_object($process_locker)) {
190 $process_locker->executeUserPassResultUpdateLockOperation($callback);
195 if ($update_result_cache_table) {
199 return $result_object;
204 if (!$status_of_attempt->isFinished()) {
205 throw new \RuntimeException(
'Status of attempt must be finished to finalize test attempt result');
208 $this->db->manipulateF(
209 'UPDATE tst_pass_result SET tstamp = %s, finalized_by = %s WHERE active_fi = %s AND pass = %s',
210 [
'integer',
'text',
'integer',
'integer'],
211 [time(), $status_of_attempt->value, $active_id, $attempt]
217 return $this->db->fetchAssoc($this->db->queryF(
218 "SELECT tst_pass_result.*, tst_active.last_finished_pass, tst_active.user_fi AS user_id, tst_tests.test_id, 219 tst_tests.obj_fi AS test_obj_id, tst_pass_result.maxpoints AS max_points, points AS reached_points, 220 tst_result_cache.passed_once AS passed_once_before 222 INNER JOIN tst_active ON tst_pass_result.active_fi = tst_active.active_id 223 INNER JOIN tst_tests ON tst_tests.test_id = tst_active.test_fi 224 LEFT JOIN tst_result_cache ON tst_result_cache.active_fi = tst_active.active_id 225 WHERE tst_pass_result.active_fi = %s AND tst_pass_result.pass = %s",
227 [$active_id, $attempt]
235 $is_passed = $test_attempt_result->getAttempt() <= $test_attempt_result_array[
'last_finished_pass'] && $test_attempt_result->isPassed();
236 $passed_once_before = (bool) ($test_attempt_result_array[
'passed_once_before'] ??
false);
237 return $test_attempt_result->withPassedOnce($is_passed || $passed_once_before);
242 return $this->db->fetchAssoc($this->db->queryF(
243 'SELECT r.pass,' . PHP_EOL
244 .
'SUM(r.points) AS points,' . PHP_EOL
245 .
'COUNT(DISTINCT(r.question_fi)) answeredquestions,' . PHP_EOL
246 .
'pr.exam_id,' . PHP_EOL
247 .
'pr.finalized_by' . PHP_EOL
248 .
'FROM tst_test_result r' . PHP_EOL
249 .
'INNER JOIN tst_pass_result pr' . PHP_EOL
250 .
'ON r.active_fi = pr.active_fi AND r.pass = pr.pass' . PHP_EOL
251 .
'WHERE r.active_fi = %s AND r.pass = %s',
253 [$active_id, $attempt]
259 $test_result[
'active_fi'] = $active_id;
261 $additional_data = $this->
fetchAdditionalTestData($test_attempt_result->getActiveId(), $test_attempt_result->getAttempt());
263 return $test_attempt_result->withMaxPoints($additional_data[
'max_points'])
264 ->withQuestionCount($additional_data[
'question_count'])
266 $this->
fetchWorkingTime($test_attempt_result->getActiveId(), $test_attempt_result->getAttempt())
270 $test_attempt_result->getActiveId(),
271 $test_attempt_result->getAttempt(),
283 $result = $this->db->queryF(
284 "SELECT tst_tests.question_set_type FROM tst_active 285 INNER JOIN tst_tests ON tst_active.test_fi = tst_tests.test_id 286 WHERE tst_active.active_id = %s",
290 $question_set_type = $result->numRows() > 0 ? $this->db->fetchAssoc($result)[
'question_set_type'] :
'';
292 $result = match ($question_set_type) {
294 "SELECT tst_test_rnd_qst.pass, COUNT(tst_test_rnd_qst.question_fi) qcount, SUM(qpl_questions.points) qsum 295 FROM tst_test_rnd_qst, qpl_questions 296 WHERE tst_test_rnd_qst.question_fi = qpl_questions.question_id 297 AND tst_test_rnd_qst.active_fi = %s AND pass = %s 298 GROUP BY tst_test_rnd_qst.active_fi, tst_test_rnd_qst.pass",
300 [$active_id, $attempt]
303 "SELECT COUNT(tst_test_question.question_fi) qcount, SUM(qpl_questions.points) qsum 304 FROM tst_test_question, qpl_questions, tst_active 305 WHERE tst_test_question.question_fi = qpl_questions.question_id 306 AND tst_test_question.test_fi = tst_active.test_fi AND tst_active.active_id = %s 307 GROUP BY tst_test_question.test_fi",
311 default =>
throw new \ilTestException(
'not supported question set type: ' . $question_set_type),
314 $row = $this->db->fetchAssoc($result);
315 return is_array($row)
316 ? [
'question_count' => (
int) $row[
'qcount'],
'max_points' => (
float) $row[
'qsum']]
317 : [
'question_count' => 0,
'max_points' => 0.0];
322 $result = $this->db->queryF(
323 "SELECT started, finished FROM tst_times WHERE active_fi = %s AND pass = %s ORDER BY started",
325 [$active_id, $attempt]
329 while ($row = $this->db->fetchAssoc($result)) {
330 $time += (strtotime($row[
'finished']) - strtotime($row[
'started']));
339 $this->db->manipulate(
"DELETE FROM tst_test_result WHERE {$condition}");
340 $this->db->manipulate(
"DELETE FROM tst_pass_result WHERE {$condition}");
342 $user_ids = $this->db->fetchAll(
344 'SELECT user_fi FROM tst_active WHERE' . PHP_EOL
348 foreach ($user_ids as $row) {
349 $this->cache->delete($row[
'user_fi'] .
':' . $test_obj_id);
361 $reached_points = $this->
ensurePositive($row[
'reached_points'] ?? 0.0);
362 $percentage = ($max_points > 0 ? $reached_points / $max_points : 0.0) * 100;
364 $mark = $this->marks_repository->getMarkSchemaFor($row[
'test_id'])->getMatchingMark($percentage);
372 (
int) ($row[
'tstamp'] ?? -1),
373 (
bool) ($row[
'passed_once'] ??
false),
388 (
int) ($row[
'questioncount'] ?? 0),
389 (
int) ($row[
'answeredquestions'] ?? 0),
390 (
int) ($row[
'workingtime'] ?? 0),
391 (
int) ($row[
'tstamp'] ?? -1),
392 $row[
'exam_id'] ??
'',
393 $row[
'finalized_by'] ??
'',
399 return max(0.0, (
float) $value);
407 $this->cache->set($user_id .
':' . $test_obj_id, $status);
415 $cached_status = $this->cache->get($user_id .
':' . $test_obj_id, $this->
refinery->identity());
416 if ($cached_status !==
null) {
417 return $cached_status;
420 $status = $this->db->fetchAssoc($this->db->queryF(
421 "SELECT tst_result_cache.passed, tst_result_cache.failed, (tst_active.last_finished_pass IS NOT NULL) AS finished 422 FROM tst_result_cache 423 INNER JOIN tst_active ON tst_active.active_id = tst_result_cache.active_fi 424 INNER JOIN tst_tests ON tst_tests.test_id = tst_active.test_fi 425 WHERE tst_active.user_fi = %s AND tst_tests.obj_fi = %s",
427 [$user_id, $test_obj_id]
429 if ($status ===
null) {
getTestResult(int $active_id)
const QUESTION_SET_TYPE_RANDOM
buildTestAttemptResultObject(int $active_id, array $test_result, ?int $test_obj_id)
updateTestResultCache(int $active_id, ?\ilAssQuestionProcessLocker $process_locker=null)
fetchTestResult(int $active_id, int $attempt)
isPassed(int $user_id, int $test_obj_id)
isFailed(int $user_id, int $test_obj_id)
updateTestAttemptResult(int $active_id, int $attempt, ?\ilAssQuestionProcessLocker $process_locker=null, ?int $test_obj_id=null, bool $update_result_cache_table=true)
fetchWorkingTime(int $active_id, int $attempt)
buildTestResultObject(array $test_attempt_result_array)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
updateStatusCache(int $user_id, int $test_obj_id, array $status)
static _getResultPass($active_id)
Retrieves the pass number that should be counted for a given user.
fetchTestAttemptResult(int $active_id, int $attempt)
getPassedParticipants(int $test_obj_id)
Class ParticipantResult is a model representation of an entry in the test_result_cache table...
finalizeTestAttemptResult(int $active_id, int $attempt, StatusOfAttempt $status_of_attempt)
removeTestResults(array $active_ids, int $test_obj_id)
getTestAttemptResult(int $active_id)
toParticipantResult(?array $row)
__construct(private readonly \ilDBInterface $db, private readonly Refinery $refinery, private readonly MarksRepository $marks_repository, Services $global_cache)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
readOrQueryStatus(int $user_id, int $test_obj_id)
const QUESTION_SET_TYPE_FIXED
static buildExamId($active_id, $pass, $test_obj_id=null)
ensurePositive(mixed $value)
hasFinished(int $user_id, int $test_obj_id)
fetchAdditionalTestData(int $active_id, int $attempt)
toTestAttemptResult(?array $row)