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