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