ILIAS  release_8 Revision v8.24
class.ilCourseObjectiveResult.php
Go to the documentation of this file.
1<?php
18declare(strict_types=0);
19
25{
26 public const IL_OBJECTIVE_STATUS_EMPTY = 'empty';
27 public const IL_OBJECTIVE_STATUS_PRETEST = 'pretest';
28 public const IL_OBJECTIVE_STATUS_FINAL = 'final';
29 public const IL_OBJECTIVE_STATUS_NONE = 'none';
30 public const IL_OBJECTIVE_STATUS_FINISHED = 'finished';
31 public const IL_OBJECTIVE_STATUS_PRETEST_NON_SUGGEST = 'pretest_non_suggest';
32
33 private int $user_id;
34
35 protected ilDBInterface $db;
36
37 public function __construct(int $a_usr_id)
38 {
39 global $DIC;
40
41 $this->db = $DIC->database();
42
43 $this->user_id = $a_usr_id;
44 }
45
46 public function getUserId(): int
47 {
48 return $this->user_id;
49 }
50
51 public function getAccomplished(int $a_crs_id): array
52 {
53 return ilCourseObjectiveResult::_getAccomplished($this->getUserId(), $a_crs_id);
54 }
55
56 public static function _getAccomplished(int $a_user_id, int $a_crs_id): array
57 {
58 global $DIC;
59
60 $ilDB = $DIC->database();
61
63 if (!is_array($objectives)) {
64 return array();
65 }
66 $query = "SELECT objective_id FROM crs_objective_status " .
67 "WHERE " . $ilDB->in('objective_id', $objectives, false, 'integer') . ' ' .
68 "AND user_id = " . $ilDB->quote($a_user_id, 'integer') . " ";
69 $res = $ilDB->query($query);
70 $accomplished = [];
71 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
72 $accomplished[] = (int) $row->objective_id;
73 }
74 return $accomplished;
75 }
76
77 public function getSuggested(int $a_crs_id, string $a_status = self::IL_OBJECTIVE_STATUS_FINAL): array
78 {
79 return ilCourseObjectiveResult::_getSuggested($this->getUserId(), $a_crs_id, $a_status);
80 }
81
82 public static function _getSuggested(
83 int $a_user_id,
84 int $a_crs_id,
85 string $a_status = self::IL_OBJECTIVE_STATUS_FINAL
86 ): array {
87 global $DIC;
88
89 $ilDB = $DIC->database();
90
92 $finished = $suggested = [];
93 if (
94 $a_status == self::IL_OBJECTIVE_STATUS_FINAL ||
95 $a_status == self::IL_OBJECTIVE_STATUS_FINISHED
96 ) {
97 // check finished
98 $query = "SELECT objective_id FROM crs_objective_status " .
99 "WHERE " . $ilDB->in('objective_id', $objectives, false, 'integer') . " " .
100 "AND user_id = " . $ilDB->quote($a_user_id, 'integer') . " ";
101 $res = $ilDB->query($query);
102 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
103 $finished[] = (int) $row->objective_id;
104 }
105 } else {
106 // Pretest
107 $query = "SELECT objective_id FROM crs_objective_status_p " .
108 "WHERE " . $ilDB->in('objective_id', $objectives, false, 'integer') . ' ' .
109 "AND user_id = " . $ilDB->quote($a_user_id, 'integer');
110 $res = $ilDB->query($query);
111 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
112 $finished[] = (int) $row->objective_id;
113 }
114 }
115 foreach ($objectives as $objective_id) {
116 if (!in_array($objective_id, $finished)) {
117 $suggested[] = $objective_id;
118 }
119 }
120 return $suggested;
121 }
122
123 public static function getSuggestedQuestions(int $a_usr_id, int $a_crs_id): array
124 {
125 $qsts = [];
126 foreach (self::_getSuggested($a_usr_id, $a_crs_id) as $objective_id) {
127 $obj = new ilCourseObjectiveQuestion($objective_id);
128 foreach ($obj->getFinalTestQuestions() as $qst) {
129 $qsts[] = $qst['question_id'];
130 }
131 }
132 return $qsts;
133 }
134
135 protected function resetTestForUser(ilObjTest $a_test, int $a_user_id): void
136 {
137 // #15038
138 $test_lp = ilTestLP::getInstance($a_test->getId());
139 $test_lp->resetLPDataForUserIds(array($a_user_id));
140
141 // #15205 - see ilObjTestGUI::confirmDeleteSelectedUserDataObject()
142 $active_id = $a_test->getActiveIdOfUser($a_user_id);
143 if ($active_id) {
144 $a_test->removeTestActives(array($active_id));
145 }
146 }
147
148 public function reset(int $a_course_id): void
149 {
150 $assignments = ilLOTestAssignments::getInstance($a_course_id);
151 foreach (array_merge(
152 $assignments->getAssignmentsByType(ilLOSettings::TYPE_TEST_INITIAL),
153 $assignments->getAssignmentsByType(ilLOSettings::TYPE_TEST_QUALIFIED)
154 )
155 as $assignment) {
156 $tst = ilObjectFactory::getInstanceByRefId($assignment->getTestRefId(), false);
157 if ($tst instanceof ilObjTest) {
158 global $DIC;
159
160 $lng = $DIC['lng'];
161
162 $participantData = new ilTestParticipantData($this->db, $lng);
163 $participantData->setUserIdsFilter(array($this->getUserId()));
164 $participantData->load($tst->getTestId());
165 $tst->removeTestResults($participantData);
166 }
167 }
168
169 $initial = ilLOSettings::getInstanceByObjId($a_course_id)->getInitialTest();
170 $initial_tst = ilObjectFactory::getInstanceByRefId($initial, false);
171 if ($initial_tst instanceof ilObjTest) {
172 $this->resetTestForUser($initial_tst, $this->getUserId());
173 }
174
175 $qualified = ilLOSettings::getInstanceByObjId($a_course_id)->getQualifiedTest();
176 $qualified_tst = ilObjectFactory::getInstanceByRefId($qualified, false);
177 if ($qualified_tst instanceof ilObjTest) {
178 $this->resetTestForUser($qualified_tst, $this->getUserId());
179 }
180
182
183 if ($objectives !== []) {
184 $query = "DELETE FROM crs_objective_status " .
185 "WHERE " . $this->db->in('objective_id', $objectives, false, 'integer') . ' ' .
186 "AND user_id = " . $this->db->quote($this->getUserId(), 'integer') . " ";
187 $res = $this->db->manipulate($query);
188
189 $query = "DELETE FROM crs_objective_status_p " .
190 "WHERE " . $this->db->in('objective_id', $objectives, false, 'integer') . ' ' .
191 "AND user_id = " . $this->db->quote($this->getUserId(), ilDBConstants::T_INTEGER) . "";
192 $res = $this->db->manipulate($query);
193
194 $query = "DELETE FROM loc_user_results " .
195 "WHERE " . $this->db->in('objective_id', $objectives, false, 'integer') . ' ' .
196 "AND user_id = " . $this->db->quote($this->getUserId(), ilDBConstants::T_INTEGER) . "";
197 }
198 // update/reset LP for course
199 ilLPStatusWrapper::_updateStatus($a_course_id, $this->getUserId());
200 }
201
202 public function getStatus(int $a_course_id): string
203 {
204 $objective_ids = ilCourseObjective::_getObjectiveIds($a_course_id, true);
206 $accomplished = $this->getAccomplished($a_course_id);
207 $suggested = $this->getSuggested($a_course_id);
208
209 if ($objective_ids === []) {
210 return self::IL_OBJECTIVE_STATUS_EMPTY;
211 }
212
213 if (count($accomplished) == count($objective_ids)) {
214 return self::IL_OBJECTIVE_STATUS_FINISHED;
215 }
216
217 $all_pretest_answered = false;
218 $all_final_answered = false;
219 foreach ($objectives as $data) {
220 if (assQuestion::_areAnswered($this->getUserId(), $data['questions'])) {
221 if ($data['tst_status']) {
222 $all_final_answered = true;
223 } else {
224 $all_pretest_answered = true;
225 }
226 }
227 }
228 if ($all_final_answered) {
229 return self::IL_OBJECTIVE_STATUS_FINAL;
230 }
231 if ($all_pretest_answered && $suggested === []) {
232 return self::IL_OBJECTIVE_STATUS_PRETEST_NON_SUGGEST;
233 } elseif ($all_pretest_answered) {
234 return self::IL_OBJECTIVE_STATUS_PRETEST;
235 }
236 return self::IL_OBJECTIVE_STATUS_NONE;
237 }
238
239 public function hasAccomplishedObjective(int $a_objective_id): bool
240 {
241 $query = "SELECT status FROM crs_objective_status " .
242 "WHERE objective_id = " . $this->db->quote($a_objective_id, 'integer') . " " .
243 "AND user_id = " . $this->db->quote($this->getUserId(), 'integer') . "";
244
245 $res = $this->db->query($query);
246 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
247 return true;
248 }
249 return false;
250 }
251
252 public function readStatus(int $a_crs_id): void
253 {
254 $objective_ids = ilCourseObjective::_getObjectiveIds($a_crs_id, true);
257 }
258
259 public static function _updateObjectiveResult(int $a_user_id, int $a_active_id, int $a_question_id): void
260 {
261 // find all objectives this question is assigned to
262 if (!$objectives = self::_readAssignedObjectivesOfQuestion($a_question_id)) {
263 // no objectives found. TODO user has passed a test. After that questions of that test are assigned to an objective.
264 // => User has not passed
265 return;
266 }
267 self::_updateObjectiveStatus($a_user_id, $objectives);
268 }
269
270 public static function _readAssignedObjectivesOfQuestion(int $a_question_id): array
271 {
272 global $DIC;
273
274 $ilDB = $DIC['ilDB'];
275
276 // get all objtives and questions this current question is assigned to
277 $query = "SELECT q2.question_id qid,q2.objective_id ob FROM crs_objective_qst q1, " .
278 "crs_objective_qst q2 " .
279 "WHERE q1.question_id = " . $ilDB->quote($a_question_id, 'integer') . " " .
280 "AND q1.objective_id = q2.objective_id ";
281
282 $res = $ilDB->query($query);
283 $objectives = array();
284 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
285 $objectives['all_objectives'][(int) $row->ob] = (int) $row->ob;
286 $objectives['all_questions'][(int) $row->qid] = (int) $row->qid;
287 }
288 if (count($objectives) === 0) {
289 return [];
290 }
291 $objectives['objectives'] = self::_readAssignedObjectives($objectives['all_objectives']);
292 return $objectives;
293 }
294
295 public static function _readAssignedObjectives(array $a_all_objectives): array
296 {
297 global $DIC;
298
299 $ilDB = $DIC['ilDB'];
300
301 // Read necessary points
302 $query = "SELECT t.objective_id obj,t.ref_id ref, question_id,tst_status,tst_limit " .
303 "FROM crs_objective_tst t JOIN crs_objective_qst q " .
304 "ON (t.objective_id = q.objective_id AND t.ref_id = q.ref_id) " .
305 "WHERE " . $ilDB->in('t.objective_id', $a_all_objectives, false, 'integer');
306
307 $res = $ilDB->query($query);
308 $objectives = array();
309 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
310 $objectives[$row->obj . "_" . $row->tst_status]['questions'][(int) $row->question_id] = (int) $row->question_id;
311 $objectives[$row->obj . "_" . $row->tst_status]['tst_status'] = (int) $row->tst_status;
312 $objectives[$row->obj . "_" . $row->tst_status]['tst_limit'] = (int) $row->tst_limit;
313 $objectives[$row->obj . "_" . $row->tst_status]['objective_id'] = (int) $row->obj;
314 }
315 return $objectives;
316 }
317
318 public static function _updateObjectiveStatus(int $a_user_id, array $objectives): bool
319 {
320 global $DIC;
321
322 $ilDB = $DIC['ilDB'];
323 $ilUser = $DIC['ilUser'];
324
325 if (
326 !count($objectives['all_questions']) ||
327 !count($objectives['all_objectives'])) {
328 return false;
329 }
330 // Read reachable points
331 $query = "SELECT question_id,points FROM qpl_questions " .
332 "WHERE " . $ilDB->in('question_id', (array) $objectives['all_questions'], false, 'integer');
333 $res = $ilDB->query($query);
334 while ($row = $ilDB->fetchAssoc($res)) {
335 $objectives['all_question_points'][(int) $row['question_id']]['max_points'] = (float) $row['points'];
336 }
337 // Read reached points
338 $query = "SELECT question_fi, MAX(points) as reached FROM tst_test_result " .
339 "JOIN tst_active ON (active_id = active_fi) " .
340 "WHERE user_fi = " . $ilDB->quote($a_user_id, 'integer') . " " .
341 "AND " . $ilDB->in('question_fi', (array) $objectives['all_questions'], false, 'integer') . " " .
342 "GROUP BY question_fi,user_fi";
343 $res = $ilDB->query($query);
344 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
345 $objectives['all_question_points'][$row->question_fi]['reached_points'] = (float) $row->reached;
346 }
347
348 // Check accomplished
349 $fullfilled = array();
350 $pretest = array();
351 foreach ($objectives['objectives'] as $data) {
352 // objective does not allow to change status
353 if (ilCourseObjectiveResult::__isFullfilled($objectives['all_question_points'], $data)) {
354 // Status 0 means pretest fullfilled, status 1 means final test fullfilled
355 if ($data['tst_status']) {
356 $fullfilled[] = array($data['objective_id'], $ilUser->getId(), $data['tst_status']);
357 } else {
358 $pretest[] = array($data['objective_id'], $ilUser->getId());
359 }
360 }
361 }
362 if ($fullfilled !== []) {
363 foreach ($fullfilled as $fullfilled_arr) {
364 $ilDB->replace(
365 'crs_objective_status',
366 array(
367 'objective_id' => array('integer', $fullfilled_arr[0]),
368 'user_id' => array('integer', $fullfilled_arr[1])
369 ),
370 array(
371 'status' => array('integer', $fullfilled_arr[2])
372 )
373 );
374 }
375 ilCourseObjectiveResult::__updatePassed($a_user_id, $objectives['all_objectives']);
376 }
377 if ($pretest !== []) {
378 foreach ($pretest as $pretest_arr) {
379 $ilDB->replace(
380 'crs_objective_status_p',
381 array(
382 'objective_id' => array('integer', $pretest_arr[0]),
383 'user_id' => array('integer', $pretest_arr[1])
384 ),
385 array()
386 );
387 }
388 }
389 return true;
390 }
391
392 public static function __isFullfilled(array $question_points, array $objective_data): bool
393 {
394 global $DIC;
395
396 if (!is_array($objective_data['questions'])) {
397 return false;
398 }
399 $max_points = 0;
400 $reached_points = 0;
401 foreach ($objective_data['questions'] as $question_id) {
402 if (array_key_exists($question_id, $question_points)) {
403 $max_points += $question_points[$question_id]['max_points'];
404 $reached_points += $question_points[$question_id]['reached_points'] ?? 0;
405 } else {
406 $DIC->logger()->crs()->warning('stale question in course objective assignment table id ' . $question_id);
407 }
408 }
409 if (!$max_points) {
410 return false;
411 }
412 return $reached_points >= $objective_data['tst_limit'];
413 }
414
415 protected static function __updatePassed(int $a_user_id, array $objective_ids): void
416 {
417 global $DIC;
418
419 $ilDB = $DIC['ilDB'];
420
421 $passed = array();
422
423 $query = "SELECT COUNT(t1.crs_id) num,t1.crs_id FROM crs_objectives t1 " .
424 "JOIN crs_objectives t2 WHERE t1.crs_id = t2.crs_id and " .
425 $ilDB->in('t1.objective_id', $objective_ids, false, 'integer') . " " .
426 "GROUP BY t1.crs_id";
427 $res = $ilDB->query($query);
428 $crs_ids = array();
429 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
430 $query = "SELECT COUNT(cs.objective_id) num_passed FROM crs_objective_status cs " .
431 "JOIN crs_objectives co ON cs.objective_id = co.objective_id " .
432 "WHERE crs_id = " . $ilDB->quote($row->crs_id, 'integer') . " " .
433 "AND user_id = " . $ilDB->quote($a_user_id, 'integer') . " ";
434
435 $user_res = $ilDB->query($query);
436 while ($user_row = $user_res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
437 if ((int) $user_row->num_passed === (int) $row->num) {
438 $passed[] = $row->crs_id;
439 }
440 }
441 $crs_ids[(int) $row->crs_id] = (int) $row->crs_id;
442 }
443 if ($passed !== []) {
444 foreach ($passed as $crs_id) {
446 $members->updatePassed($a_user_id, true);
447 }
448 }
449
450 // update tracking status
451 foreach ($crs_ids as $cid) {
452 ilLPStatusWrapper::_updateStatus($cid, $a_user_id);
453 }
454 }
455}
static _areAnswered(int $a_user_id, array $a_question_ids)
Checks if an array of question ids is answered by a user or not.
class ilcourseobjectiveQuestion
static __updatePassed(int $a_user_id, array $objective_ids)
static getSuggestedQuestions(int $a_usr_id, int $a_crs_id)
resetTestForUser(ilObjTest $a_test, int $a_user_id)
static _readAssignedObjectivesOfQuestion(int $a_question_id)
static _updateObjectiveStatus(int $a_user_id, array $objectives)
static _updateObjectiveResult(int $a_user_id, int $a_active_id, int $a_question_id)
static _getSuggested(int $a_user_id, int $a_crs_id, string $a_status=self::IL_OBJECTIVE_STATUS_FINAL)
static _getAccomplished(int $a_user_id, int $a_crs_id)
hasAccomplishedObjective(int $a_objective_id)
getSuggested(int $a_crs_id, string $a_status=self::IL_OBJECTIVE_STATUS_FINAL)
static _readAssignedObjectives(array $a_all_objectives)
static __isFullfilled(array $question_points, array $objective_data)
static _getObjectiveIds(int $course_id, bool $a_activated_only=false)
static _getInstanceByObjId(int $a_obj_id)
static getInstanceByObjId(int $a_obj_id)
static getInstance(int $a_container_id)
static _updateStatus(int $a_obj_id, int $a_usr_id, ?object $a_obj=null, bool $a_percentage=false, bool $a_force_raise=false)
removeTestActives($activeIds)
getActiveIdOfUser($user_id="", $anonymous_id="")
Gets the active id of a given user.
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
static getInstance(int $obj_id)
global $DIC
Definition: feed.php:28
$ilUser
Definition: imgupload.php:34
Interface ilDBInterface.
$res
Definition: ltiservices.php:69
$query
$lng
$objectives