ILIAS  release_8 Revision v8.24
class.ilLOUserResults.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=0);
26{
27 protected int $course_obj_id;
28 protected int $user_id;
29
30 public const TYPE_INITIAL = 1;
31 public const TYPE_QUALIFIED = 2;
32
33 public const STATUS_COMPLETED = 1;
34 public const STATUS_FAILED = 2;
35
36 protected ilDBInterface $db;
37
38 public function __construct(int $a_course_obj_id, int $a_user_id)
39 {
40 global $DIC;
41
42 $this->course_obj_id = $a_course_obj_id;
43 $this->user_id = $a_user_id;
44
45 $this->db = $DIC->database();
46 }
47
48 public static function updateResultLimit(int $a_objective_id, int $a_test_type, int $a_limit) : void
49 {
50 global $DIC;
51
52 $db = $DIC->database();
53 $query = 'UPDATE loc_user_results ' .
54 'SET limit_perc = ' . $db->quote($a_limit, ilDBConstants::T_INTEGER) . ' ' .
55 'WHERE objective_id = ' . $db->quote($a_objective_id, ilDBConstants::T_INTEGER) . ' ' .
56 'AND type = ' . $db->quote($a_test_type, ilDBConstants::T_INTEGER);
58 }
59
60 public static function lookupResult(
61 int $a_course_obj_id,
62 int $a_user_id,
63 int $a_objective_id,
64 int $a_tst_type
65 ): array {
66 global $DIC;
67
68 $ilDB = $DIC->database();
69 $query = 'SELECT * FROM loc_user_results ' .
70 'WHERE user_id = ' . $ilDB->quote($a_user_id, 'integer') . ' ' .
71 'AND course_id = ' . $ilDB->quote($a_course_obj_id, 'integer') . ' ' .
72 'AND objective_id = ' . $ilDB->quote($a_objective_id, 'integer') . ' ' .
73 'AND type = ' . $ilDB->quote($a_tst_type, 'integer');
74 $res = $ilDB->query($query);
75 $ur = array(
76 'status' => self::STATUS_FAILED,
77 'result_perc' => 0,
78 'limit_perc' => 0,
79 'tries' => 0,
80 'is_final' => 0,
81 'has_result' => false
82 );
83 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
84 $ur['status'] = $row->status;
85 $ur['result_perc'] = $row->result_perc;
86 $ur['limit_perc'] = $row->limit_perc;
87 $ur['tries'] = $row->tries;
88 $ur['is_final'] = $row->is_final;
89 $ur['has_result'] = true;
90 }
91 return $ur;
92 }
93
94 public static function resetFinalByObjective(int $a_objective_id): void
95 {
96 global $DIC;
97
98 $db = $DIC->database();
99 $query = 'UPDATE loc_user_results ' .
100 'SET is_final = ' . $db->quote(0, 'integer') . ' ' .
101 'WHERE objective_id = ' . $db->quote($a_objective_id, 'integer');
102 $db->manipulate($query);
103 }
104
105 protected static function isValidType(int $a_type): bool
106 {
107 return in_array($a_type, array(self::TYPE_INITIAL, self::TYPE_QUALIFIED));
108 }
109
110 protected static function isValidStatus(int $a_status): bool
111 {
112 return in_array($a_status, array(self::STATUS_COMPLETED, self::STATUS_FAILED));
113 }
114
115 public static function deleteResultsForUser(int $a_user_id): bool
116 {
117 global $DIC;
118
119 $ilDB = $DIC->database();
120 if (!$a_user_id) {
121 return false;
122 }
123
124 $ilDB->manipulate("DELETE FROM loc_user_results" .
125 " WHERE user_id = " . $ilDB->quote($a_user_id, "integer"));
126 return true;
127 }
128
129 public static function deleteResultsForCourse(int $a_course_id): bool
130 {
131 global $DIC;
132
133 $ilDB = $DIC->database();
134 if (!$a_course_id) {
135 return false;
136 }
137 $ilDB->manipulate("DELETE FROM loc_user_results" .
138 " WHERE course_id = " . $ilDB->quote($a_course_id, "integer"));
139 return true;
140 }
141
142 public function delete(): void
143 {
144 $query = 'DELETE FROM loc_user_results ' .
145 'WHERE course_id = ' . $this->db->quote($this->course_obj_id, ilDBConstants::T_INTEGER) . ' ' .
146 'AND user_id = ' . $this->db->quote($this->user_id, ilDBConstants::T_INTEGER);
147 $this->db->manipulate($query);
148 }
149
150 public static function deleteResultsFromLP(
151 int $a_course_id,
152 array $a_user_ids,
153 bool $a_remove_initial,
154 bool $a_remove_qualified,
155 array $a_objective_ids
156 ): bool {
157 global $DIC;
158
159 $ilDB = $DIC->database();
160 if (!$a_course_id ||
161 $a_user_ids === []) {
162 return false;
163 }
164
165 $base_sql = "DELETE FROM loc_user_results" .
166 " WHERE course_id = " . $ilDB->quote($a_course_id, "integer") .
167 " AND " . $ilDB->in("user_id", $a_user_ids, false, "integer");
168
169 if ($a_objective_ids !== []) {
170 $base_sql .= ' AND ' . $ilDB->in('objective_id', $a_objective_ids, false, "integer");
171 }
172
173 $sql = '';
174 if ($a_remove_initial) {
175 $sql = $base_sql .
176 " AND type = " . $ilDB->quote(self::TYPE_INITIAL, "integer");
177 $ilDB->manipulate($sql);
178 }
179
180 if ($a_remove_qualified) {
181 $sql = $base_sql .
182 " AND type = " . $ilDB->quote(self::TYPE_QUALIFIED, "integer");
183 $ilDB->manipulate($sql);
184 }
185
186 if ($a_objective_ids === []) {
187 $ilDB->manipulate($base_sql);
188 }
189 $ilDB->manipulate($sql);
190 return true;
191 }
192
193 public function saveObjectiveResult(
194 int $a_objective_id,
195 int $a_type,
196 int $a_status,
197 int $a_result_percentage,
198 int $a_limit_percentage,
199 int $a_tries,
200 bool $a_is_final
201 ): bool {
202 if (!self::isValidType($a_type) ||
203 !self::isValidStatus($a_status)) {
204 return false;
205 }
206 $this->db->replace(
207 "loc_user_results",
208 array(
209 "course_id" => array("integer", $this->course_obj_id),
210 "user_id" => array("integer", $this->user_id),
211 "objective_id" => array("integer", $a_objective_id),
212 "type" => array("integer", $a_type)
213 ),
214 array(
215 "status" => array("integer", $a_status),
216 "result_perc" => array("integer", $a_result_percentage),
217 "limit_perc" => array("integer", $a_limit_percentage),
218 "tries" => array("integer", $a_tries),
219 "is_final" => array("integer", $a_is_final),
220 "tstamp" => array("integer", time()),
221 )
222 );
223 return true;
224 }
225
226 protected function findObjectiveIds(int $a_type = 0, int $a_status = 0, ?bool $a_is_final = null): array
227 {
228 $res = array();
229 $sql = "SELECT objective_id" .
230 " FROM loc_user_results" .
231 " WHERE course_id = " . $this->db->quote($this->course_obj_id, "integer") .
232 " AND user_id = " . $this->db->quote($this->user_id, "integer");
233
234 if ($this->isValidType($a_type)) {
235 $sql .= " AND type = " . $this->db->quote($a_type, "integer");
236 }
237 if ($this->isValidStatus($a_status)) {
238 $sql .= " AND status = " . $this->db->quote($a_status, "integer");
239 }
240 if ($a_is_final !== null) {
241 $sql .= " AND is_final = " . $this->db->quote($a_is_final, "integer");
242 }
243
244 $set = $this->db->query($sql);
245 while ($row = $this->db->fetchAssoc($set)) {
246 $res[] = $row["objective_id"];
247 }
248
249 return $res;
250 }
251
252 public function getCompletedObjectiveIdsByType(int $a_type): array
253 {
254 return $this->findObjectiveIds($a_type, self::STATUS_COMPLETED);
255 }
256
260 public function getSuggestedObjectiveIds(): array
261 {
262 return $this->findObjectiveIds(self::TYPE_INITIAL, self::STATUS_FAILED);
263 }
264
268 public function getCompletedObjectiveIds(): array
269 {
270 $settings = ilLOSettings::getInstanceByObjId($this->course_obj_id);
271
272 if (!$settings->isInitialTestQualifying() || !$settings->worksWithInitialTest()) {
273 return $this->findObjectiveIds(self::TYPE_QUALIFIED, self::STATUS_COMPLETED);
274 }
275
276 // status of final final test overwrites initial qualified.
277 $completed = [];
278 if (
279 $settings->isInitialTestQualifying() &&
280 $settings->worksWithInitialTest()
281 ) {
282 $completed_candidates = array_unique(
283 array_merge(
284 $this->findObjectiveIds(self::TYPE_INITIAL, self::STATUS_COMPLETED),
285 $this->findObjectiveIds(self::TYPE_QUALIFIED, self::STATUS_COMPLETED)
286 )
287 );
288 $failed_final = $this->findObjectiveIds(self::TYPE_QUALIFIED, self::STATUS_FAILED);
289
290 foreach ($completed_candidates as $objective_completed) {
291 if (!in_array($objective_completed, $failed_final)) {
292 $completed[] = $objective_completed;
293 }
294 }
295 return $completed;
296 }
297 return [];
298 }
299
300 public function getFailedObjectiveIds(bool $a_is_final = true): array
301 {
302 return $this->findObjectiveIds(self::TYPE_QUALIFIED, self::STATUS_FAILED, $a_is_final);
303 }
304
305 public function getCourseResultsForUserPresentation(): array
306 {
307 $res = [];
308 $settings = ilLOSettings::getInstanceByObjId($this->course_obj_id);
309
310 $set = $this->db->query("SELECT *" .
311 " FROM loc_user_results" .
312 " WHERE course_id = " . $this->db->quote($this->course_obj_id, "integer") .
313 " AND user_id = " . $this->db->quote($this->user_id, "integer"));
314 while ($row = $this->db->fetchAssoc($set)) {
315 // do not read initial test results, if disabled.
316 if (
317 $row['type'] == self::TYPE_INITIAL &&
318 !$settings->worksWithInitialTest()
319 ) {
320 continue;
321 }
322
323 $objective_id = (int) $row["objective_id"];
324 $type = (int) $row["type"];
325 unset($row["objective_id"]);
326 unset($row["type"]);
327 $res[$objective_id][$type] = $row;
328 }
329 return $res;
330 }
331
335 public static function getObjectiveStatusForLP(int $a_user_id, int $a_obj_id, array $a_objective_ids): array
336 {
337 global $DIC;
338
339 $ilDB = $DIC->database();
340
341 // are initital test(s) qualifying?
342 $lo_set = ilLOSettings::getInstanceByObjId($a_obj_id);
343 $initial_qualifying = $lo_set->isInitialTestQualifying();
344
345 // this method returns LP status codes!
346
347 $res = array();
348
349 $sql = "SELECT lor.objective_id, lor.user_id, lor.status, lor.is_final" .
350 " FROM loc_user_results lor" .
351 " JOIN crs_objectives cobj ON (cobj.objective_id = lor.objective_id)" .
352 " WHERE " . $ilDB->in("lor.objective_id", $a_objective_ids, false, "integer");
353 if (!$initial_qualifying) {
354 $sql .= " AND lor.type = " . $ilDB->quote(self::TYPE_QUALIFIED, "integer");
355 }
356 $sql .= " AND lor.user_id = " . $ilDB->quote($a_user_id, "integer") .
357 " AND cobj.active = " . $ilDB->quote(1, "integer") .
358 " ORDER BY lor.type"; // qualified must come last!
359 $set = $ilDB->query($sql);
360 while ($row = $ilDB->fetchAssoc($set)) {
361 switch ($row["status"]) {
362 case self::STATUS_FAILED:
363 if ($row["is_final"]) {
365 } else {
366 // #15379
368 }
369 break;
370
371 case self::STATUS_COMPLETED:
373 break;
374
375 default:
376 continue 2;
377 }
378
379 // if both initial and qualified, qualified will overwrite initial
380 $res[(int) $row["objective_id"]] = $status;
381 }
382 return $res;
383 }
384
388 public static function getSummarizedObjectiveStatusForLP(
389 int $a_obj_id,
390 array $a_objective_ids,
391 int $a_user_id = 0
392 ) {
393 global $DIC;
394
395 $ilDB = $DIC->database();
396 // change event is NOT parsed here!
397 // are initital test(s) qualifying?
398 $lo_set = ilLOSettings::getInstanceByObjId($a_obj_id);
399 $initial_qualifying = $lo_set->isInitialTestQualifying();
400
401 // this method returns LP status codes!
402
403 $res = $tmp_completed = array();
404
405 $sql = "SELECT lor.objective_id, lor.user_id, lor.status, lor.type, lor.is_final" .
406 " FROM loc_user_results lor" .
407 " JOIN crs_objectives cobj ON (cobj.objective_id = lor.objective_id)" .
408 " WHERE " . $ilDB->in("lor.objective_id", $a_objective_ids, false, "integer") .
409 " AND cobj.active = " . $ilDB->quote(1, "integer");
410 if (!$initial_qualifying) {
411 $sql .= " AND lor.type = " . $ilDB->quote(self::TYPE_QUALIFIED, "integer");
412 }
413 if ($a_user_id) {
414 $sql .= " AND lor.user_id = " . $ilDB->quote($a_user_id, "integer");
415 }
416 $sql .= " ORDER BY lor.type DESC"; // qualified must come first!
417 $set = $ilDB->query($sql);
418
419 $has_final_result = array();
420 while ($row = $ilDB->fetchAssoc($set)) {
421 if ($row['type'] == self::TYPE_QUALIFIED) {
422 $has_final_result[$row['objective_id']] = $row['user_id'];
423 }
424
425 $user_id = (int) $row["user_id"];
426 $status = (int) $row["status"];
427
428 // initial tests only count if no qualified test
429 if (
430 $row["type"] == self::TYPE_INITIAL &&
431 in_array($row['user_id'], (array) ($has_final_result[(int) $row['objective_id']] ?? []))
432 ) {
433 continue;
434 }
435
436 // user did do something
438
439 switch ($status) {
440 case self::STATUS_COMPLETED:
441 $tmp_completed[$user_id] = ($tmp_completed[$user_id] ?? 0) + 1;
442 break;
443
444 case self::STATUS_FAILED:
445 if ($row["is_final"]) {
446 // object is failed when at least 1 objective is failed without any tries left
448 }
449 break;
450 }
451 }
452
453 $all_nr = count($a_objective_ids);
454 foreach ($tmp_completed as $user_id => $counter) {
455 // if used as precondition object should be completed ASAP, status can be lost on subsequent tries
456 if ($counter == $all_nr) {
458 }
459 }
460
461 if ($a_user_id) {
462 return isset($res[$a_user_id]) ? (int) $res[$a_user_id] : null;
463 } else {
464 return $res;
465 }
466 }
467
468 public static function hasResults(int $a_container_id, int $a_user_id): bool
469 {
470 global $DIC;
471
472 $ilDB = $DIC->database();
473 $query = 'SELECT objective_id FROM loc_user_results ' .
474 'WHERE course_id = ' . $ilDB->quote($a_container_id, 'integer') . ' ' .
475 'AND user_id = ' . $ilDB->quote($a_user_id, 'integer');
476
477 $res = $ilDB->query($query);
478 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
479 return true;
480 }
481 return false;
482 }
483
487 public static function getCompletionsOfUser(int $a_user_id, int $a_from_ts, int $a_to_ts): array
488 {
489 global $DIC;
490
491 $ilDB = $DIC->database();
492 $res = [];
493 $sql = "SELECT lor.objective_id, lor.user_id, lor.status, lor.is_final, lor.tstamp, lor.course_id, cobj.title" .
494 " FROM loc_user_results lor" .
495 " JOIN crs_objectives cobj ON (cobj.objective_id = lor.objective_id)" .
496 " WHERE lor.user_id = " . $ilDB->quote($a_user_id, "integer") .
497 " AND lor.type = " . $ilDB->quote(self::TYPE_QUALIFIED, "integer") .
498 " AND lor.tstamp >= " . $ilDB->quote($a_from_ts, "integer") .
499 " AND lor.tstamp <= " . $ilDB->quote($a_to_ts, "integer") .
500 " AND lor.status = " . $ilDB->quote(self::STATUS_COMPLETED, "integer");
501
502 $set = $ilDB->query($sql);
503 while ($row = $ilDB->fetchAssoc($set)) {
504 $res[(int) $row["objective_id"]] = $row;
505 }
506 return $res;
507 }
508}
static getInstanceByObjId(int $a_obj_id)
static resetFinalByObjective(int $a_objective_id)
getSuggestedObjectiveIds()
Get all objectives where the user failed the initial test.
static updateResultLimit(int $a_objective_id, int $a_test_type, int $a_limit)
findObjectiveIds(int $a_type=0, int $a_status=0, ?bool $a_is_final=null)
static deleteResultsFromLP(int $a_course_id, array $a_user_ids, bool $a_remove_initial, bool $a_remove_qualified, array $a_objective_ids)
static getCompletionsOfUser(int $a_user_id, int $a_from_ts, int $a_to_ts)
Get completed learning objectives for user and time frame.
static deleteResultsForUser(int $a_user_id)
saveObjectiveResult(int $a_objective_id, int $a_type, int $a_status, int $a_result_percentage, int $a_limit_percentage, int $a_tries, bool $a_is_final)
getCompletedObjectiveIds()
Get all objectives where the user completed the qualified test.
getCompletedObjectiveIdsByType(int $a_type)
static getSummarizedObjectiveStatusForLP(int $a_obj_id, array $a_objective_ids, int $a_user_id=0)
static isValidStatus(int $a_status)
static hasResults(int $a_container_id, int $a_user_id)
static lookupResult(int $a_course_obj_id, int $a_user_id, int $a_objective_id, int $a_tst_type)
getFailedObjectiveIds(bool $a_is_final=true)
static isValidType(int $a_type)
static getObjectiveStatusForLP(int $a_user_id, int $a_obj_id, array $a_objective_ids)
__construct(int $a_course_obj_id, int $a_user_id)
static deleteResultsForCourse(int $a_course_id)
const LP_STATUS_COMPLETED_NUM
const LP_STATUS_IN_PROGRESS_NUM
const LP_STATUS_FAILED_NUM
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
global $DIC
Definition: feed.php:28
Interface ilDBInterface.
quote($value, string $type)
manipulate(string $query)
Run a (write) Query on the database.
$res
Definition: ltiservices.php:69
array $settings
Setting values (LTI parameters, custom parameters and local parameters).
Definition: System.php:200
$query
$type