Passed
Push — 1.11.x ( 909037...ee7f72 )
by Julito
12:57
created

Tracking::get_sessions_coached_by_user()   F

Complexity

Conditions 19
Paths 336

Size

Total Lines 138
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 77
nc 336
nop 9
dl 0
loc 138
rs 1.9833
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CourseBundle\Entity\CLpCategory;
9
use Chamilo\UserBundle\Entity\User;
10
use ChamiloSession as Session;
11
use CpChart\Cache as pCache;
12
use CpChart\Data as pData;
13
use CpChart\Image as pImage;
14
use ExtraField as ExtraFieldModel;
15
16
/**
17
 *  Class Tracking.
18
 *
19
 *  @author  Julio Montoya <[email protected]>
20
 */
21
class Tracking
22
{
23
    /**
24
     * Get group reporting.
25
     *
26
     * @param int    $course_id
27
     * @param int    $sessionId
28
     * @param int    $group_id
29
     * @param string $type
30
     * @param int    $start
31
     * @param int    $limit
32
     * @param int    $sidx
33
     * @param string $sord
34
     * @param array  $where_condition
35
     *
36
     * @return array|null
37
     */
38
    public static function get_group_reporting(
39
        $course_id,
40
        $sessionId = 0,
41
        $group_id = 0,
42
        $type = 'all',
43
        $start = 0,
44
        $limit = 1000,
45
        $sidx = 1,
46
        $sord = 'desc',
47
        $where_condition = []
48
    ) {
49
        $course_id = (int) $course_id;
50
        $sessionId = (int) $sessionId;
51
52
        if (empty($course_id)) {
53
            return null;
54
        }
55
        $courseInfo = api_get_course_info_by_id($course_id);
56
        if ('count' == $type) {
57
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
58
        }
59
60
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
61
        $parsedResult = [];
62
        if (!empty($groupList)) {
63
            foreach ($groupList as $group) {
64
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
65
                $time = 0;
66
                $avg_student_score = 0;
67
                $avg_student_progress = 0;
68
                $work = 0;
69
                $messages = 0;
70
71
                foreach ($users as $user_data) {
72
                    $time += self::get_time_spent_on_the_course(
73
                        $user_data['user_id'],
74
                        $courseInfo['real_id'],
75
                        $sessionId
76
                    );
77
                    $average = self::get_avg_student_score(
78
                        $user_data['user_id'],
79
                        $courseInfo['code'],
80
                        [],
81
                        $sessionId
82
                    );
83
                    if (is_numeric($average)) {
84
                        $avg_student_score += $average;
85
                    }
86
                    $avg_student_progress += self::get_avg_student_progress(
87
                        $user_data['user_id'],
88
                        $courseInfo['code'],
89
                        [],
90
                        $sessionId
91
                    );
92
                    $work += self::count_student_assignments(
93
                        $user_data['user_id'],
94
                        $courseInfo['code'],
95
                        $sessionId
96
                    );
97
                    $messages += self::count_student_messages(
98
                        $user_data['user_id'],
99
                        $courseInfo['code'],
100
                        $sessionId
101
                    );
102
                }
103
104
                $countUsers = count($users);
105
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
106
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
107
108
                $groupItem = [
109
                    'id' => $group['id'],
110
                    'name' => $group['name'],
111
                    'time' => api_time_to_hms($time),
112
                    'progress' => $averageProgress,
113
                    'score' => $averageScore,
114
                    'works' => $work,
115
                    'messages' => $messages,
116
                ];
117
                $parsedResult[] = $groupItem;
118
            }
119
        }
120
121
        return $parsedResult;
122
    }
123
124
    /**
125
     * It gets table html of Lp stats used to export in pdf.
126
     *
127
     * @param $userId
128
     * @param $courseInfo
129
     * @param $sessionId
130
     * @param $lpId
131
     *
132
     * @return string
133
     */
134
    public static function getLpStatsContentToPdf(
135
        $userId,
136
        $courseInfo,
137
        $sessionId,
138
        $lpId,
139
        $lpName
140
    ) {
141
        $hideTime = api_get_configuration_value('hide_lp_time');
142
        $lpId = (int) $lpId;
143
        $userId = (int) $userId;
144
        $sessionId = (int) $sessionId;
145
        $courseId = $courseInfo['real_id'];
146
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
147
        $sessionCondition = api_get_session_condition($sessionId);
148
        $counter = 0;
149
        $totalTime = 0;
150
        $h = get_lang('h');
151
        $resultDisabledExtAll = true;
152
        $timeHeader = '<th>'.get_lang('ScormTime').'</th>';
153
        if ($hideTime) {
154
            $timeHeader = '';
155
        }
156
        $output = '<h2 class="clearfix text-center">'.$lpName.'</h2>';
157
        $output .= '<table class="table table-hover table-striped data_table">
158
            <thead>
159
                <tr>
160
                <th>'.get_lang('ScormLessonTitle').'</th>
161
                <th>'.get_lang('ScormStatus').'</th>
162
                <th>'.get_lang('ScormScore').'</th>
163
                '.$timeHeader.'
164
                </tr>
165
            </thead>
166
            <tbody>
167
        ';
168
169
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
170
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
171
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
172
        $tblQuizQuestions = Database::get_course_table(TABLE_QUIZ_QUESTION);
173
        $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
174
        $tblStatsExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
175
        $tblStatsAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
176
177
        // it gets the max view
178
        $sql = "SELECT max(view_count) FROM $tblLpView WHERE c_id = $courseId AND lp_id = $lpId AND user_id = $userId $sessionCondition";
179
        $res = Database::query($sql);
180
        $view = 0;
181
        $viewCondition = "";
182
        if (Database::num_rows($res) > 0) {
183
            $view = Database::result($res, 0, 0);
184
            $viewCondition = " AND v.view_count = ".(int) $view;
185
        }
186
187
        $chapterTypes = learnpath::getChapterTypes();
188
        $minimumAvailable = self::minimumTimeAvailable($sessionId, $courseId);
189
        $list = learnpath::get_flat_ordered_items_list($lpId, 0, $courseId);
190
        if (is_array($list) && count($list) > 0) {
191
            foreach ($list as $myItemId) {
192
                $sql = "SELECT
193
                    iv.status as mystatus,
194
                    v.view_count as mycount,
195
                    iv.score as myscore,
196
                    iv.total_time as mytime,
197
                    i.iid as myid,
198
                    i.lp_id as mylpid,
199
                    iv.lp_view_id as mylpviewid,
200
                    i.title as mytitle,
201
                    i.max_score as mymaxscore,
202
                    iv.max_score as myviewmaxscore,
203
                    i.item_type as item_type,
204
                    iv.view_count as iv_view_count,
205
                    iv.id as iv_id,
206
                    path
207
                FROM $tblLpItem as i
208
                INNER JOIN $tblLpItemView as iv
209
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
210
                INNER JOIN $tblLpView as v
211
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
212
                WHERE
213
                    v.c_id = $courseId AND
214
                    i.iid = $myItemId AND
215
                    i.lp_id = $lpId  AND
216
                    v.user_id = $userId AND
217
                    v.session_id = $sessionId
218
                    $viewCondition
219
                ORDER BY iv.view_count";
220
                $result = Database::query($sql);
221
                $num = Database::num_rows($result);
222
                $timeForTotal = 0;
223
224
                if ($num > 0) {
225
                    // Not extended.
226
                    $row = Database::fetch_array($result, 'ASSOC');
227
                    $myId = $row['myid'];
228
                    $myLpId = $row['mylpid'];
229
                    $myLpViewId = $row['mylpviewid'];
230
                    $lpItemPath = (int) $row['path'];
231
                    $resultDisabledExtAll = false;
232
                    if ($row['item_type'] === 'quiz') {
233
                        // Check results_disabled in quiz table.
234
                        $sql = "SELECT results_disabled
235
                                FROM $tblQuiz
236
                                WHERE iid = $lpItemPath";
237
                        $resResultDisabled = Database::query($sql);
238
                        $rowResultDisabled = Database::fetch_row($resResultDisabled);
239
                        if (Database::num_rows($resResultDisabled) > 0 && 1 === (int) $rowResultDisabled[0]) {
240
                            $resultDisabledExtAll = true;
241
                        }
242
                    }
243
244
                    // Check if there are interactions below
245
                    $extendThisAttempt = 0;
246
                    $interNum = learnpath::get_interactions_count_from_db($row['iv_id'], $courseId);
247
                    $objecNum = learnpath::get_objectives_count_from_db($row['iv_id'], $courseId);
248
                    if ($interNum > 0 || $objecNum > 0) {
249
                        $extendThisAttempt = 1;
250
                    }
251
                    $lesson_status = $row['mystatus'];
252
                    $score = $row['myscore'];
253
                    $subtotalTime = $row['mytime'];
254
                    while ($tmp_row = Database::fetch_array($result)) {
255
                        $subtotalTime += $tmp_row['mytime'];
256
                    }
257
258
                    $title = $row['mytitle'];
259
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
260
                    $sql = 'SELECT * FROM '.$tblStatsExercises.'
261
                            WHERE
262
                                exe_exo_id="'.$row['path'].'" AND
263
                                exe_user_id="'.$userId.'" AND
264
                                orig_lp_id = "'.$lpId.'" AND
265
                                orig_lp_item_id = "'.$row['myid'].'" AND
266
                                c_id = '.$courseId.' AND
267
                                status <> "incomplete" AND
268
                                session_id = '.$sessionId.'
269
                             ORDER BY exe_date DESC
270
                             LIMIT 1';
271
272
                    $resultLastAttempt = Database::query($sql);
273
                    $num = Database::num_rows($resultLastAttempt);
274
                    $idLastAttempt = null;
275
                    if ($num > 0) {
276
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
277
                            $idLastAttempt = $rowLA['exe_id'];
278
                        }
279
                    }
280
281
                    switch ($row['item_type']) {
282
                        case 'sco':
283
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
284
                                $maxscore = $row['myviewmaxscore'];
285
                            } elseif ($row['myviewmaxscore'] === '') {
286
                                $maxscore = 0;
287
                            } else {
288
                                $maxscore = $row['mymaxscore'];
289
                            }
290
                            break;
291
                        case 'quiz':
292
                            // Get score and total time from last attempt of a exercise en lp.
293
                            $sql = "SELECT iid, score
294
                                    FROM $tblLpItemView
295
                                    WHERE
296
                                        c_id = $courseId AND
297
                                        lp_item_id = '".(int) $myId."' AND
298
                                        lp_view_id = '".(int) $myLpViewId."'
299
                                    ORDER BY view_count DESC
300
                                    LIMIT 1";
301
                            $resScore = Database::query($sql);
302
                            $rowScore = Database::fetch_array($resScore);
303
304
                            $sql = "SELECT SUM(total_time) as total_time
305
                                    FROM $tblLpItemView
306
                                    WHERE
307
                                        c_id = $courseId AND
308
                                        lp_item_id = '".(int) $myId."' AND
309
                                        lp_view_id = '".(int) $myLpViewId."'";
310
                            $resTime = Database::query($sql);
311
                            $rowTime = Database::fetch_array($resTime);
312
313
                            $score = 0;
314
                            $subtotalTime = 0;
315
                            if (Database::num_rows($resScore) > 0 && Database::num_rows($resTime) > 0) {
316
                                $score = (float) $rowScore['score'];
317
                                $subtotalTime = (int) $rowTime['total_time'];
318
                            }
319
                            // Selecting the max score from an attempt.
320
                            $sql = "SELECT SUM(t.ponderation) as maxscore
321
                                    FROM (
322
                                        SELECT DISTINCT
323
                                            question_id, marks, ponderation
324
                                        FROM $tblStatsAttempts as at
325
                                        INNER JOIN $tblQuizQuestions as q
326
                                        ON q.iid = at.question_id
327
                                        WHERE exe_id ='$idLastAttempt'
328
                                    ) as t";
329
330
                            $result = Database::query($sql);
331
                            $rowMaxScore = Database::fetch_array($result);
332
                            $maxscore = $rowMaxScore['maxscore'];
333
334
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
335
                            $sql = 'SELECT SUM(exe_duration) exe_duration
336
                                    FROM '.$tblStatsExercises.'
337
                                    WHERE
338
                                        exe_exo_id="'.$row['path'].'" AND
339
                                        exe_user_id="'.$userId.'" AND
340
                                        orig_lp_id = "'.$lpId.'" AND
341
                                        orig_lp_item_id = "'.$row['myid'].'" AND
342
                                        c_id = '.$courseId.' AND
343
                                        status <> "incomplete" AND
344
                                        session_id = '.$sessionId.'
345
                                     ORDER BY exe_date DESC ';
346
                            $sumScoreResult = Database::query($sql);
347
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
348
                            if (!empty($durationRow['exe_duration'])) {
349
                                $exeDuration = $durationRow['exe_duration'];
350
                                if ($exeDuration != $subtotalTime && !empty($rowScore['iid']) && !empty($exeDuration)) {
351
                                    $subtotalTime = $exeDuration;
352
                                    // Update c_lp_item_view.total_time
353
                                    $sqlUpdate = "UPDATE $tblLpItemView SET total_time = '$exeDuration' WHERE iid = ".$rowScore['iid'];
354
                                    Database::query($sqlUpdate);
355
                                }
356
                            }
357
                            break;
358
                        default:
359
                            $maxscore = $row['mymaxscore'];
360
                            break;
361
                    }
362
363
                    $timeForTotal = $subtotalTime;
364
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotalTime);
365
                    if (empty($title)) {
366
                        $title = learnpath::rl_get_resource_name(
367
                            $courseInfo['code'],
368
                            $lpId,
369
                            $row['myid']
370
                        );
371
                    }
372
373
                    if (in_array($row['item_type'], $chapterTypes)) {
374
                        $title = Security::remove_XSS($title);
375
                        $output .= '<tr>
376
                                <td colspan="4"><h4>'.$title.'</h4></td>
377
                        </tr>';
378
                    } else {
379
                        if ('quiz' === $row['item_type']) {
380
                            $sql = 'SELECT * FROM '.$tblStatsExercises.'
381
                                     WHERE
382
                                        exe_exo_id="'.$row['path'].'" AND
383
                                        exe_user_id="'.$userId.'" AND
384
                                        orig_lp_id = "'.$lpId.'" AND
385
                                        orig_lp_item_id = "'.$row['myid'].'" AND
386
                                        c_id = '.$courseId.' AND
387
                                        status <> "incomplete" AND
388
                                        session_id = '.$sessionId.'
389
                                     ORDER BY exe_date DESC ';
390
                            $resultLastAttempt = Database::query($sql);
391
                            $num = Database::num_rows($resultLastAttempt);
392
                        }
393
394
                        $title = Security::remove_XSS($title);
395
                        if ($lpId == $myLpId && false) {
396
                            $output .= '<tr>
397
                                    <td>'.$title.'</td>
398
                                    <td>&nbsp;</td>
399
                                    <td>&nbsp;</td>
400
                                    <td>&nbsp;</td>
401
                            </tr>';
402
                        } else {
403
                            $output .= "<tr>";
404
                            $scoreItem = null;
405
                            if ($row['item_type'] === 'quiz') {
406
                                $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
407
                            } else {
408
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
409
                            }
410
                            $timeRow = '<td>'.$time.'</td>';
411
                            if ($hideTime) {
412
                                $timeRow = '';
413
                            }
414
                            $output .= '
415
                                <td>'.$title.'</td>
416
                                <td>'.learnpathitem::humanize_status($lesson_status).'</td>
417
                                <td>'.$scoreItem.'</td>
418
                                '.$timeRow.'
419
                             ';
420
                            $output .= '</tr>';
421
                        }
422
                    }
423
424
                    $counter++;
425
                    if ($extendThisAttempt) {
426
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
427
                        foreach ($list1 as $id => $interaction) {
428
                            $timeRow = '<td>'.$interaction['time'].'</td>';
429
                            if ($hideTime) {
430
                                $timeRow = '';
431
                            }
432
433
                            $output .= '<tr>
434
                                    <td>'.$interaction['order_id'].'</td>
435
                                    <td>'.$interaction['id'].'</td>
436
                                    <td>'.$interaction['type'].'</td>
437
                                    <td>'.urldecode($interaction['student_response']).'</td>
438
                                    <td>'.$interaction['result'].'</td>
439
                                    <td>'.$interaction['latency'].'</td>
440
                                    '.$timeRow.'
441
                               </tr>';
442
                            $counter++;
443
                        }
444
445
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
446
                        foreach ($list2 as $id => $interaction) {
447
                            $output .= '<tr>
448
                                    <td>'.$interaction['order_id'].'</td>
449
                                    <td>'.$interaction['objective_id'].'</td>
450
                                    <td>'.$interaction['status'].'</td>
451
                                    <td>'.$interaction['score_raw'].'</td>
452
                                    <td>'.$interaction['score_max'].'</td>
453
                                    <td>'.$interaction['score_min'].'</td>
454
                               </tr>';
455
                            $counter++;
456
                        }
457
                    }
458
459
                    // Attempts listing by exercise.
460
                    if ($lpId == $myLpId) {
461
                        // Get attempts of a exercise.
462
                        if (!empty($lpId) && 'quiz' === $row['item_type']) {
463
                            $sql = "SELECT path FROM $tblLpItem
464
                                    WHERE
465
                                        c_id = $courseId AND
466
                                        lp_id = '$lpId'";
467
                            $resPath = Database::query($sql);
468
                            $rowPath = Database::fetch_array($resPath);
469
470
                            if (Database::num_rows($resPath) > 0) {
471
                                $sql = 'SELECT * FROM '.$tblStatsExercises.'
472
                                        WHERE
473
                                            exe_exo_id="'.(int) $rowPath['path'].'" AND
474
                                            status <> "incomplete" AND
475
                                            exe_user_id="'.$userId.'" AND
476
                                            orig_lp_id = "'.$lpId.'" AND
477
                                            orig_lp_item_id = "'.$myItemId.'" AND
478
                                            c_id = '.$courseId.'  AND
479
                                            session_id = '.$sessionId.'
480
                                        ORDER BY exe_date';
481
                                $resAttempts = Database::query($sql);
482
                                if (Database::num_rows($resAttempts) > 0) {
483
                                    $n = 1;
484
                                    while ($rowAttempts = Database::fetch_array($resAttempts)) {
485
                                        $myScore = $rowAttempts['exe_result'];
486
                                        $myMaxscore = $rowAttempts['exe_weighting'];
487
                                        $mktimeStartDate = api_strtotime($rowAttempts['start_date'], 'UTC');
488
                                        $mktimeExeDate = api_strtotime($rowAttempts['exe_date'], 'UTC');
489
                                        $timeAttempt = ' - ';
490
                                        if ($mktimeStartDate && $mktimeExeDate) {
491
                                            $timeAttempt = api_format_time($rowAttempts['exe_duration'], 'js');
492
                                        }
493
                                        // Show only float when need it
494
                                        if ($myScore == 0) {
495
                                            $viewScore = ExerciseLib::show_score(
496
                                                0,
497
                                                $myMaxscore,
498
                                                false
499
                                            );
500
                                        } else {
501
                                            if ($myMaxscore == 0) {
502
                                                $viewScore = $myScore;
503
                                            } else {
504
                                                $viewScore = ExerciseLib::show_score(
505
                                                    $myScore,
506
                                                    $myMaxscore,
507
                                                    false
508
                                                );
509
                                            }
510
                                        }
511
                                        $myLessonStatus = $rowAttempts['status'];
512
                                        if ($myLessonStatus == '') {
513
                                            $myLessonStatus = learnpathitem::humanize_status('completed');
514
                                        } elseif ($myLessonStatus == 'incomplete') {
515
                                            $myLessonStatus = learnpathitem::humanize_status('incomplete');
516
                                        }
517
                                        $timeRow = '<td>'.$timeAttempt.'</td>';
518
                                        if ($hideTime) {
519
                                            $timeRow = '';
520
                                        }
521
                                        $output .= '<tr>
522
                                        <td><em>'.get_lang('Attempt').' '.$n.'</em></td>
523
                                        <td>'.$myLessonStatus.'</td>
524
                                        <td>'.$viewScore.'</td>
525
                                        '.$timeRow;
526
                                        $output .= '</tr>';
527
                                        $n++;
528
                                    }
529
                                }
530
                            }
531
                        }
532
                    }
533
                }
534
                $totalTime += $timeForTotal;
535
            }
536
        }
537
538
        $totalScore = self::get_avg_student_score(
539
            $userId,
540
            $courseId,
541
            [$lpId],
542
            $sessionId,
543
            false,
544
            false
545
        );
546
547
        $totalTime = learnpathItem::getScormTimeFromParameter('js', $totalTime);
548
        $totalTime = str_replace('NaN', '00'.$h.'00\'00"', $totalTime);
549
550
        if (!$isAllowedToEdit && $resultDisabledExtAll) {
551
            $finalScore = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
552
        } else {
553
            if (is_numeric($totalScore)) {
554
                $finalScore = $totalScore.'%';
555
            } else {
556
                $finalScore = $totalScore;
557
            }
558
        }
559
        $progress = learnpath::getProgress($lpId, $userId, $courseId, $sessionId);
560
        $timeTotal = '<th>'.$totalTime.'</th>';
561
        if ($hideTime) {
562
            $timeTotal = '';
563
        }
564
565
        $output .= '<tr>
566
                <th><i>'.get_lang('AccomplishedStepsTotal').'</i></th>
567
                <th>'.$progress.'%</th>
568
                <th>'.$finalScore.'</th>
569
                '.$timeTotal.'
570
           </tr></tbody></table>';
571
572
        return $output;
573
    }
574
575
    /**
576
     * @param int    $user_id
577
     * @param array  $courseInfo
578
     * @param int    $session_id
579
     * @param string $origin
580
     * @param bool   $export_csv
581
     * @param int    $lp_id
582
     * @param int    $lp_item_id
583
     * @param int    $extendId
584
     * @param int    $extendAttemptId
585
     * @param string $extendedAttempt
586
     * @param string $extendedAll
587
     * @param string $type            classic or simple
588
     * @param bool   $allowExtend     Optional. Allow or not extend te results
589
     *
590
     * @return string
591
     */
592
    public static function getLpStats(
593
        $user_id,
594
        $courseInfo,
595
        $session_id,
596
        $origin,
597
        $export_csv,
598
        $lp_id,
599
        $lp_item_id = null,
600
        $extendId = null,
601
        $extendAttemptId = null,
602
        $extendedAttempt = null,
603
        $extendedAll = null,
604
        $type = 'classic',
605
        $allowExtend = true
606
    ) {
607
        if (empty($courseInfo) || empty($lp_id)) {
608
            return '';
609
        }
610
611
        $hideTime = api_get_configuration_value('hide_lp_time');
612
        $lp_id = (int) $lp_id;
613
        $lp_item_id = (int) $lp_item_id;
614
        $user_id = (int) $user_id;
615
        $session_id = (int) $session_id;
616
        $origin = Security::remove_XSS($origin);
617
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
618
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
619
        $course_id = $courseInfo['real_id'];
620
        $courseCode = $courseInfo['code'];
621
        $session_condition = api_get_session_condition($session_id);
622
623
        // Extend all button
624
        $output = '';
625
626
        $extra = '<script>
627
        $(function() {
628
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
629
            $( "#dialog-confirm" ).dialog({
630
                autoOpen: false,
631
                show: "blind",
632
                resizable: false,
633
                height:300,
634
                modal: true
635
            });
636
637
            $(".export").click(function() {
638
                var targetUrl = $(this).attr("href");
639
                $( "#dialog-confirm" ).dialog({
640
                    width:400,
641
                    height:300,
642
                    buttons: {
643
                        "'.addslashes(get_lang('Download')).'": function() {
644
                            var option = $("input[name=add_logo]:checked").val();
645
                            location.href = targetUrl+"&add_logo="+option;
646
                            $(this).dialog("close");
647
                        }
648
                    }
649
                });
650
                $("#dialog-confirm").dialog("open");
651
652
                return false;
653
            });
654
        });
655
        </script>';
656
657
        $extra .= '<div id="dialog-confirm" title="'.get_lang('ConfirmYourChoice').'">';
658
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
659
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
660
        $extra .= $form->returnForm();
661
        $extra .= '</div>';
662
663
        $output .= $extra;
664
665
        $url_suffix = '&lp_id='.$lp_id;
666
        if ('tracking' === $origin) {
667
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
668
        }
669
670
        $extend_all = 0;
671
        if (!empty($extendedAll)) {
672
            $extend_all_link = Display::url(
673
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
674
                api_get_self().'?action=stats'.$url_suffix
675
            );
676
            $extend_all = 1;
677
        } else {
678
            $extend_all_link = Display::url(
679
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
680
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
681
            );
682
        }
683
684
        if ($origin != 'tracking') {
685
            $output .= '<div class="section-status">';
686
            $output .= Display::page_header(get_lang('ScormMystatus'));
687
            $output .= '</div>';
688
        }
689
690
        $actionColumn = null;
691
        if ($type === 'classic') {
692
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
693
        }
694
695
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
696
        if ($hideTime) {
697
            $timeHeader = '';
698
        }
699
        $output .= '<div class="table-responsive">';
700
        $output .= '<table id="lp_tracking" class="table tracking">
701
            <thead>
702
            <tr class="table-header">
703
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
704
                <th colspan="4">
705
                    '.get_lang('ScormLessonTitle').'
706
                </th>
707
                <th colspan="2">
708
                    '.get_lang('ScormStatus').'
709
                </th>
710
                <th colspan="2">
711
                    '.get_lang('ScormScore').'
712
                </th>
713
                '.$timeHeader.'
714
                '.$actionColumn.'
715
                </tr>
716
            </thead>
717
            <tbody>
718
        ';
719
720
        // Going through the items using the $items[] array instead of the database order ensures
721
        // we get them in the same order as in the imsmanifest file, which is rather random when using
722
        // the database table.
723
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
724
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
725
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
726
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
727
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
728
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
729
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
730
731
        $sql = "SELECT max(view_count)
732
                FROM $TBL_LP_VIEW
733
                WHERE
734
                    c_id = $course_id AND
735
                    lp_id = $lp_id AND
736
                    user_id = $user_id
737
                    $session_condition";
738
        $res = Database::query($sql);
739
        $view = 0;
740
        if (Database::num_rows($res) > 0) {
741
            $myrow = Database::fetch_array($res);
742
            $view = (int) $myrow[0];
743
        }
744
745
        $counter = 0;
746
        $total_time = 0;
747
        $h = get_lang('h');
748
749
        if (!empty($export_csv)) {
750
            $csvHeaders = [
751
                get_lang('ScormLessonTitle'),
752
                get_lang('ScormStatus'),
753
                get_lang('ScormScore'),
754
            ];
755
756
            if ($hideTime === false) {
757
                $csvHeaders[] = get_lang('ScormTime');
758
            }
759
760
            $csv_content[] = $csvHeaders;
761
        }
762
763
        $result_disabled_ext_all = true;
764
        $chapterTypes = learnpath::getChapterTypes();
765
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
766
767
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
768
        $timeCourse = [];
769
        if ($minimumAvailable) {
770
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
771
            Session::write('trackTimeCourse', $timeCourse);
772
        }
773
774
        // Show lp items
775
        if (is_array($list) && count($list) > 0) {
776
            foreach ($list as $my_item_id) {
777
                $extend_this = 0;
778
                $order = 'DESC';
779
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
780
                    $extend_this = 1;
781
                    $order = 'ASC';
782
                }
783
784
                // Prepare statement to go through each attempt.
785
                $viewCondition = null;
786
                if (!empty($view)) {
787
                    $viewCondition = " AND v.view_count = $view  ";
788
                }
789
790
                $sql = "SELECT
791
                    iv.status as mystatus,
792
                    v.view_count as mycount,
793
                    iv.score as myscore,
794
                    iv.total_time as mytime,
795
                    i.iid as myid,
796
                    i.lp_id as mylpid,
797
                    iv.lp_view_id as mylpviewid,
798
                    i.title as mytitle,
799
                    i.max_score as mymaxscore,
800
                    iv.max_score as myviewmaxscore,
801
                    i.item_type as item_type,
802
                    iv.view_count as iv_view_count,
803
                    iv.id as iv_id,
804
                    path
805
                FROM $TBL_LP_ITEM as i
806
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
807
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
808
                INNER JOIN $TBL_LP_VIEW as v
809
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
810
                WHERE
811
                    v.c_id = $course_id AND
812
                    i.iid = $my_item_id AND
813
                    i.lp_id = $lp_id  AND
814
                    v.user_id = $user_id AND
815
                    v.session_id = $session_id
816
                    $viewCondition
817
                ORDER BY iv.view_count $order ";
818
819
                $result = Database::query($sql);
820
                $num = Database::num_rows($result);
821
                $time_for_total = 0;
822
823
                // Extend all
824
                if (($extend_this || $extend_all) && $num > 0) {
825
                    $row = Database::fetch_array($result);
826
                    $result_disabled_ext_all = false;
827
                    if ('quiz' === $row['item_type']) {
828
                        // Check results_disabled in quiz table.
829
                        $lpItemPath = (int) $row['path'];
830
                        $sql = "SELECT results_disabled
831
                                FROM $TBL_QUIZ
832
                                WHERE
833
                                    iid = $lpItemPath";
834
                        $res_result_disabled = Database::query($sql);
835
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
836
837
                        if (Database::num_rows($res_result_disabled) > 0 &&
838
                            1 === (int) $row_result_disabled[0]
839
                        ) {
840
                            $result_disabled_ext_all = true;
841
                        }
842
                    }
843
844
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
845
                    $oddclass = 'row_even';
846
                    if (($counter % 2) === 0) {
847
                        $oddclass = 'row_odd';
848
                    }
849
                    $extend_link = '';
850
                    if (!empty($inter_num)) {
851
                        $extend_link = Display::url(
852
                            Display::return_icon(
853
                                'visible.png',
854
                                get_lang('HideAttemptView')
855
                            ),
856
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
857
                        );
858
                    }
859
                    $title = $row['mytitle'];
860
861
                    if (empty($title)) {
862
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
863
                    }
864
865
                    if (in_array($row['item_type'], $chapterTypes)) {
866
                        $title = "<h4> $title </h4>";
867
                    }
868
                    $lesson_status = $row['mystatus'];
869
                    $title = Security::remove_XSS($title);
870
                    $counter++;
871
872
                    $action = null;
873
                    if ('classic' === $type) {
874
                        $action = '<td></td>';
875
                    }
876
877
                    if (in_array($row['item_type'], $chapterTypes)) {
878
                        $output .= '<tr class="'.$oddclass.'">
879
                                <td>'.$extend_link.'</td>
880
                                <td colspan="4">
881
                                   '.$title.'
882
                                </td>
883
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
884
                                <td colspan="2"></td>
885
                                <td colspan="2"></td>
886
                                '.$action.'
887
                            </tr>';
888
                        continue;
889
                    } else {
890
                        $output .= '<tr class="'.$oddclass.'">
891
                                <td>'.$extend_link.'</td>
892
                                <td colspan="4">'.$title.'</td>
893
                                <td colspan="2"></td>
894
                                <td colspan="2"></td>
895
                                <td colspan="2"></td>
896
                                '.$action.'
897
                            </tr>';
898
                    }
899
900
                    $attemptCount = 1;
901
                    do {
902
                        // Check if there are interactions below.
903
                        $extend_attempt_link = '';
904
                        $extend_this_attempt = 0;
905
906
                        if ($timeCourse) {
907
                            //$attemptResult = 0;
908
                            if (isset($timeCourse['learnpath_detailed']) &&
909
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
910
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
911
                            ) {
912
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
913
                            }
914
                        }
915
                        if ((
916
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
917
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
918
                            ) &&
919
                            !$extend_all
920
                        ) {
921
                            if ($extendAttemptId == $row['iv_id']) {
922
                                // The extend button for this attempt has been clicked.
923
                                $extend_this_attempt = 1;
924
                                $extend_attempt_link = Display::url(
925
                                    Display::return_icon('visible.png', get_lang('HideAttemptView')),
926
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
927
                                );
928
                                if ($accessToPdfExport) {
929
                                    $extend_attempt_link .= '&nbsp;'.
930
                                        Display::url(
931
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
932
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
933
                                            ['class' => 'export']
934
                                        );
935
                                }
936
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
937
                                // The extend button for this attempt has not been clicked.
938
                                $extend_attempt_link = Display::url(
939
                                    Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
940
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
941
                                );
942
                                if ($accessToPdfExport) {
943
                                    $extend_attempt_link .= '&nbsp;'.
944
                                        Display::url(
945
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
946
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
947
                                            ['class' => 'export']
948
                                        );
949
                                }
950
                            }
951
                        }
952
953
                        $oddclass = 'row_even';
954
                        if (($counter % 2) == 0) {
955
                            $oddclass = 'row_odd';
956
                        }
957
958
                        $lesson_status = $row['mystatus'];
959
                        $score = $row['myscore'];
960
                        $time_for_total += $row['mytime'];
961
                        $attemptTime = $row['mytime'];
962
963
                        if ($minimumAvailable) {
964
                            /*$lp_time = $timeCourse[TOOL_LEARNPATH];
965
                            $lpTime = null;
966
                            if (isset($lp_time[$lp_id])) {
967
                                $lpTime = (int) $lp_time[$lp_id];
968
                            }
969
                            $time_for_total = $lpTime;*/
970
                            $time_for_total = (int) $attemptResult;
971
                            $attemptTime = (int) $attemptResult;
972
                        }
973
974
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
975
976
                        if ($score == 0) {
977
                            $maxscore = $row['mymaxscore'];
978
                        } else {
979
                            if ($row['item_type'] === 'sco') {
980
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
981
                                    $maxscore = $row['myviewmaxscore'];
982
                                } elseif ($row['myviewmaxscore'] === '') {
983
                                    $maxscore = 0;
984
                                } else {
985
                                    $maxscore = $row['mymaxscore'];
986
                                }
987
                            } else {
988
                                $maxscore = $row['mymaxscore'];
989
                            }
990
                        }
991
992
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
993
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
994
                        if ($row['item_type'] !== 'dir') {
995
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
996
                                $view_score = Display::return_icon(
997
                                    'invisible.png',
998
                                    get_lang('ResultsHiddenByExerciseSetting')
999
                                );
1000
                            } else {
1001
                                switch ($row['item_type']) {
1002
                                    case 'sco':
1003
                                        if ($maxscore == 0) {
1004
                                            $view_score = $score;
1005
                                        } else {
1006
                                            $view_score = ExerciseLib::show_score(
1007
                                                $score,
1008
                                                $maxscore,
1009
                                                false
1010
                                            );
1011
                                        }
1012
                                        break;
1013
                                    case 'document':
1014
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
1015
                                        break;
1016
                                    default:
1017
                                        $view_score = ExerciseLib::show_score(
1018
                                            $score,
1019
                                            $maxscore,
1020
                                            false
1021
                                        );
1022
                                        break;
1023
                                }
1024
                            }
1025
1026
                            $action = null;
1027
                            if ($type === 'classic') {
1028
                                $action = '<td></td>';
1029
                            }
1030
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1031
                            if ($hideTime) {
1032
                                $timeRow = '';
1033
                            }
1034
                            $output .= '<tr class="'.$oddclass.'">
1035
                                    <td></td>
1036
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
1037
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
1038
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
1039
                                    <td colspan="2">'.$view_score.'</td>
1040
                                    '.$timeRow.'
1041
                                    '.$action.'
1042
                                </tr>';
1043
                            $attemptCount++;
1044
                            if (!empty($export_csv)) {
1045
                                $temp = [];
1046
                                $temp[] = $title = Security::remove_XSS($title);
1047
                                $temp[] = Security::remove_XSS(
1048
                                    learnpathItem::humanize_status($lesson_status, false, $type)
1049
                                );
1050
1051
                                if ($row['item_type'] === 'quiz') {
1052
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1053
                                        $temp[] = '/';
1054
                                    } else {
1055
                                        $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1056
                                    }
1057
                                } else {
1058
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1059
                                }
1060
1061
                                if ($hideTime === false) {
1062
                                    $temp[] = $time;
1063
                                }
1064
                                $csv_content[] = $temp;
1065
                            }
1066
                        }
1067
1068
                        $counter++;
1069
                        $action = null;
1070
                        if ($type === 'classic') {
1071
                            $action = '<td></td>';
1072
                        }
1073
1074
                        if ($extend_this_attempt || $extend_all) {
1075
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1076
                            foreach ($list1 as $id => $interaction) {
1077
                                $oddclass = 'row_even';
1078
                                if (($counter % 2) == 0) {
1079
                                    $oddclass = 'row_odd';
1080
                                }
1081
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1082
                                if ($hideTime) {
1083
                                    $timeRow = '';
1084
                                }
1085
1086
                                $output .= '<tr class="'.$oddclass.'">
1087
                                        <td></td>
1088
                                        <td></td>
1089
                                        <td></td>
1090
                                        <td>'.$interaction['order_id'].'</td>
1091
                                        <td>'.$interaction['id'].'</td>';
1092
1093
                                $output .= '
1094
                                        <td colspan="2">'.$interaction['type'].'</td>
1095
                                        <td>'.$interaction['student_response_formatted'].'</td>
1096
                                        <td>'.$interaction['result'].'</td>
1097
                                        <td>'.$interaction['latency'].'</td>
1098
                                        '.$timeRow.'
1099
                                        '.$action.'
1100
                                    </tr>';
1101
                                $counter++;
1102
                            }
1103
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1104
                            foreach ($list2 as $id => $interaction) {
1105
                                $oddclass = 'row_even';
1106
                                if (($counter % 2) === 0) {
1107
                                    $oddclass = 'row_odd';
1108
                                }
1109
                                $output .= '<tr class="'.$oddclass.'">
1110
                                        <td></td>
1111
                                        <td></td>
1112
                                        <td></td>
1113
                                        <td>'.$interaction['order_id'].'</td>
1114
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
1115
                                        <td colspan="2">'.$interaction['status'].'</td>
1116
                                        <td>'.$interaction['score_raw'].'</td>
1117
                                        <td>'.$interaction['score_max'].'</td>
1118
                                        <td>'.$interaction['score_min'].'</td>
1119
                                        '.$action.'
1120
                                     </tr>';
1121
                                $counter++;
1122
                            }
1123
                        }
1124
                    } while ($row = Database::fetch_array($result));
1125
                } elseif ($num > 0) {
1126
                    // Not extended.
1127
                    $row = Database::fetch_array($result, 'ASSOC');
1128
                    $my_id = $row['myid'];
1129
                    $my_lp_id = $row['mylpid'];
1130
                    $my_lp_view_id = $row['mylpviewid'];
1131
                    $lpItemPath = (int) $row['path'];
1132
                    $result_disabled_ext_all = false;
1133
                    if ($row['item_type'] === 'quiz') {
1134
                        // Check results_disabled in quiz table.
1135
                        $sql = "SELECT results_disabled
1136
                                FROM $TBL_QUIZ
1137
                                WHERE iid = $lpItemPath";
1138
                        $res_result_disabled = Database::query($sql);
1139
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
1140
1141
                        if (Database::num_rows($res_result_disabled) > 0 &&
1142
                            (int) $row_result_disabled[0] === 1
1143
                        ) {
1144
                            $result_disabled_ext_all = true;
1145
                        }
1146
                    }
1147
1148
                    // Check if there are interactions below
1149
                    $extend_this_attempt = 0;
1150
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
1151
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
1152
                    $extend_attempt_link = '';
1153
                    if ($inter_num > 0 || $objec_num > 0) {
1154
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
1155
                            // The extend button for this attempt has been clicked.
1156
                            $extend_this_attempt = 1;
1157
                            $extend_attempt_link = Display::url(
1158
                                Display::return_icon('visible.png', get_lang('HideAttemptView')),
1159
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
1160
                            );
1161
                        } else {
1162
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
1163
                            // The extend button for this attempt has not been clicked.
1164
                            $extend_attempt_link = Display::url(
1165
                                Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1166
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1167
                            );
1168
                        }
1169
                    }
1170
1171
                    $oddclass = 'row_even';
1172
                    if (($counter % 2) == 0) {
1173
                        $oddclass = 'row_odd';
1174
                    }
1175
1176
                    $extend_link = '';
1177
                    if ($inter_num > 1) {
1178
                        $extend_link = Display::url(
1179
                            Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1180
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1181
                        );
1182
                    }
1183
1184
                    $lesson_status = $row['mystatus'];
1185
                    $score = $row['myscore'];
1186
                    $subtotal_time = $row['mytime'];
1187
                    while ($tmp_row = Database::fetch_array($result)) {
1188
                        $subtotal_time += $tmp_row['mytime'];
1189
                    }
1190
1191
                    $title = $row['mytitle'];
1192
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
1193
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1194
                            WHERE
1195
                                exe_exo_id="'.$row['path'].'" AND
1196
                                exe_user_id="'.$user_id.'" AND
1197
                                orig_lp_id = "'.$lp_id.'" AND
1198
                                orig_lp_item_id = "'.$row['myid'].'" AND
1199
                                c_id = '.$course_id.' AND
1200
                                status <> "incomplete" AND
1201
                                session_id = '.$session_id.'
1202
                             ORDER BY exe_date DESC
1203
                             LIMIT 1';
1204
1205
                    $resultLastAttempt = Database::query($sql);
1206
                    $num = Database::num_rows($resultLastAttempt);
1207
                    $id_last_attempt = null;
1208
                    if ($num > 0) {
1209
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
1210
                            $id_last_attempt = $rowLA['exe_id'];
1211
                        }
1212
                    }
1213
1214
                    switch ($row['item_type']) {
1215
                        case 'sco':
1216
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
1217
                                $maxscore = $row['myviewmaxscore'];
1218
                            } elseif ($row['myviewmaxscore'] === '') {
1219
                                $maxscore = 0;
1220
                            } else {
1221
                                $maxscore = $row['mymaxscore'];
1222
                            }
1223
                            break;
1224
                        case 'quiz':
1225
                            // Get score and total time from last attempt of a exercise en lp.
1226
                            $sql = "SELECT iid, score
1227
                                    FROM $TBL_LP_ITEM_VIEW
1228
                                    WHERE
1229
                                        c_id = $course_id AND
1230
                                        lp_item_id = '".(int) $my_id."' AND
1231
                                        lp_view_id = '".(int) $my_lp_view_id."'
1232
                                    ORDER BY view_count DESC
1233
                                    LIMIT 1";
1234
                            $res_score = Database::query($sql);
1235
                            $row_score = Database::fetch_array($res_score);
1236
1237
                            $sql = "SELECT SUM(total_time) as total_time
1238
                                    FROM $TBL_LP_ITEM_VIEW
1239
                                    WHERE
1240
                                        c_id = $course_id AND
1241
                                        lp_item_id = '".(int) $my_id."' AND
1242
                                        lp_view_id = '".(int) $my_lp_view_id."'";
1243
                            $res_time = Database::query($sql);
1244
                            $row_time = Database::fetch_array($res_time);
1245
1246
                            $score = 0;
1247
                            $subtotal_time = 0;
1248
                            if (Database::num_rows($res_score) > 0 &&
1249
                                Database::num_rows($res_time) > 0
1250
                            ) {
1251
                                $score = (float) $row_score['score'];
1252
                                $subtotal_time = (int) $row_time['total_time'];
1253
                            }
1254
                            // Selecting the max score from an attempt.
1255
                            $sql = "SELECT SUM(t.ponderation) as maxscore
1256
                                    FROM (
1257
                                        SELECT DISTINCT
1258
                                            question_id, marks, ponderation
1259
                                        FROM $tbl_stats_attempts as at
1260
                                        INNER JOIN $tbl_quiz_questions as q
1261
                                        ON q.iid = at.question_id
1262
                                        WHERE exe_id ='$id_last_attempt'
1263
                                    ) as t";
1264
1265
                            $result = Database::query($sql);
1266
                            $row_max_score = Database::fetch_array($result);
1267
                            $maxscore = $row_max_score['maxscore'];
1268
1269
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
1270
                            $sql = 'SELECT SUM(exe_duration) exe_duration
1271
                                    FROM '.$tbl_stats_exercices.'
1272
                                    WHERE
1273
                                        exe_exo_id="'.$row['path'].'" AND
1274
                                        exe_user_id="'.$user_id.'" AND
1275
                                        orig_lp_id = "'.$lp_id.'" AND
1276
                                        orig_lp_item_id = "'.$row['myid'].'" AND
1277
                                        c_id = '.$course_id.' AND
1278
                                        status <> "incomplete" AND
1279
                                        session_id = '.$session_id.'
1280
                                     ORDER BY exe_date DESC ';
1281
                            $sumScoreResult = Database::query($sql);
1282
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
1283
                            if (!empty($durationRow['exe_duration'])) {
1284
                                $exeDuration = $durationRow['exe_duration'];
1285
                                if ($exeDuration != $subtotal_time &&
1286
                                    !empty($row_score['iid']) &&
1287
                                    !empty($exeDuration)
1288
                                ) {
1289
                                    $subtotal_time = $exeDuration;
1290
                                    // Update c_lp_item_view.total_time
1291
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
1292
                                                  WHERE iid = ".$row_score['iid'];
1293
                                    Database::query($sqlUpdate);
1294
                                }
1295
                            }
1296
                            break;
1297
                        default:
1298
                            $maxscore = $row['mymaxscore'];
1299
                            break;
1300
                    }
1301
1302
                    $time_for_total = $subtotal_time;
1303
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
1304
                    if (empty($title)) {
1305
                        $title = learnpath::rl_get_resource_name(
1306
                            $courseInfo['code'],
1307
                            $lp_id,
1308
                            $row['myid']
1309
                        );
1310
                    }
1311
1312
                    $action = null;
1313
                    if ($type === 'classic') {
1314
                        $action = '<td></td>';
1315
                    }
1316
1317
                    if (in_array($row['item_type'], $chapterTypes)) {
1318
                        $title = Security::remove_XSS($title);
1319
                        $output .= '<tr class="'.$oddclass.'">
1320
                                <td>'.$extend_link.'</td>
1321
                                <td colspan="10">
1322
                                <h4>'.$title.'</h4>
1323
                                </td>
1324
                                '.$action.'
1325
                            </tr>';
1326
                    } else {
1327
                        $correct_test_link = '-';
1328
                        $showRowspan = false;
1329
                        if ($row['item_type'] === 'quiz') {
1330
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
1331
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1332
                                     WHERE
1333
                                        exe_exo_id="'.$row['path'].'" AND
1334
                                        exe_user_id="'.$user_id.'" AND
1335
                                        orig_lp_id = "'.$lp_id.'" AND
1336
                                        orig_lp_item_id = "'.$row['myid'].'" AND
1337
                                        c_id = '.$course_id.' AND
1338
                                        status <> "incomplete" AND
1339
                                        session_id = '.$session_id.'
1340
                                     ORDER BY exe_date DESC ';
1341
1342
                            $resultLastAttempt = Database::query($sql);
1343
                            $num = Database::num_rows($resultLastAttempt);
1344
                            $showRowspan = false;
1345
                            if ($num > 0) {
1346
                                $linkId = 'link_'.$my_id;
1347
                                if ($extendedAttempt == 1 &&
1348
                                    $lp_id == $my_lp_id &&
1349
                                    $lp_item_id == $my_id
1350
                                ) {
1351
                                    $showRowspan = true;
1352
                                    $correct_test_link = Display::url(
1353
                                        Display::return_icon(
1354
                                            'view_less_stats.gif',
1355
                                            get_lang('HideAllAttempts')
1356
                                        ),
1357
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1358
                                        ['id' => $linkId]
1359
                                    );
1360
                                } else {
1361
                                    $correct_test_link = Display::url(
1362
                                        Display::return_icon(
1363
                                            'view_more_stats.gif',
1364
                                            get_lang(
1365
                                                'ShowAllAttemptsByExercise'
1366
                                            )
1367
                                        ),
1368
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1369
                                        ['id' => $linkId]
1370
                                    );
1371
                                }
1372
                            }
1373
                        }
1374
1375
                        $title = Security::remove_XSS($title);
1376
                        $action = null;
1377
                        if ($type === 'classic') {
1378
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
1379
                        }
1380
1381
                        if ($lp_id == $my_lp_id && false) {
1382
                            $output .= '<tr class ='.$oddclass.'>
1383
                                    <td>'.$extend_link.'</td>
1384
                                    <td colspan="4">'.$title.'</td>
1385
                                    <td colspan="2">&nbsp;</td>
1386
                                    <td colspan="2">&nbsp;</td>
1387
                                    <td colspan="2">&nbsp;</td>
1388
                                    '.$action.'
1389
                                </tr>';
1390
                            $output .= '</tr>';
1391
                        } else {
1392
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
1393
                                $output .= "<tr class='$oddclass'>";
1394
                            } else {
1395
                                $output .= "<tr class='$oddclass'>";
1396
                            }
1397
1398
                            $scoreItem = null;
1399
                            if ($row['item_type'] === 'quiz') {
1400
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1401
                                    $scoreItem .= Display::return_icon(
1402
                                        'invisible.gif',
1403
                                        get_lang('ResultsHiddenByExerciseSetting')
1404
                                    );
1405
                                } else {
1406
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
1407
                                }
1408
                            } else {
1409
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
1410
                            }
1411
1412
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1413
                            if ($hideTime) {
1414
                                $timeRow = '';
1415
                            }
1416
1417
                            $output .= '
1418
                                <td>'.$extend_link.'</td>
1419
                                <td colspan="4">'.$title.'</td>
1420
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1421
                                <td colspan="2">'.$scoreItem.'</td>
1422
                                '.$timeRow.'
1423
                                '.$action.'
1424
                             ';
1425
                            $output .= '</tr>';
1426
                        }
1427
1428
                        if (!empty($export_csv)) {
1429
                            $temp = [];
1430
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1431
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1432
                            if ($row['item_type'] === 'quiz') {
1433
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1434
                                    $temp[] = '/';
1435
                                } else {
1436
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1437
                                }
1438
                            } else {
1439
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1440
                            }
1441
1442
                            if ($hideTime === false) {
1443
                                $temp[] = $time;
1444
                            }
1445
                            $csv_content[] = $temp;
1446
                        }
1447
                    }
1448
1449
                    $counter++;
1450
                    $action = null;
1451
                    if ($type === 'classic') {
1452
                        $action = '<td></td>';
1453
                    }
1454
1455
                    if ($extend_this_attempt || $extend_all) {
1456
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1457
                        foreach ($list1 as $id => $interaction) {
1458
                            $oddclass = 'row_even';
1459
                            if (($counter % 2) == 0) {
1460
                                $oddclass = 'row_odd';
1461
                            }
1462
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1463
                            if ($hideTime) {
1464
                                $timeRow = '';
1465
                            }
1466
1467
                            $output .= '<tr class="'.$oddclass.'">
1468
                                    <td></td>
1469
                                    <td></td>
1470
                                    <td></td>
1471
                                    <td>'.$interaction['order_id'].'</td>
1472
                                    <td>'.$interaction['id'].'</td>
1473
                                    <td colspan="2">'.$interaction['type'].'</td>
1474
                                    <td>'.urldecode($interaction['student_response']).'</td>
1475
                                    <td>'.$interaction['result'].'</td>
1476
                                    <td>'.$interaction['latency'].'</td>
1477
                                    '.$timeRow.'
1478
                                    '.$action.'
1479
                               </tr>';
1480
                            $counter++;
1481
                        }
1482
1483
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1484
                        foreach ($list2 as $id => $interaction) {
1485
                            $oddclass = 'row_even';
1486
                            if (($counter % 2) == 0) {
1487
                                $oddclass = 'row_odd';
1488
                            }
1489
                            $output .= '<tr class="'.$oddclass.'">
1490
                                    <td></td>
1491
                                    <td></td>
1492
                                    <td></td>
1493
                                    <td>'.$interaction['order_id'].'</td>
1494
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1495
                                    <td colspan="2">'.$interaction['status'].'</td>
1496
                                    <td>'.$interaction['score_raw'].'</td>
1497
                                    <td>'.$interaction['score_max'].'</td>
1498
                                    <td>'.$interaction['score_min'].'</td>
1499
                                    '.$action.'
1500
                               </tr>';
1501
                            $counter++;
1502
                        }
1503
                    }
1504
1505
                    // Attempts listing by exercise.
1506
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1507
                        // Get attempts of a exercise.
1508
                        if (!empty($lp_id) &&
1509
                            !empty($lp_item_id) &&
1510
                            'quiz' === $row['item_type']
1511
                        ) {
1512
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1513
                                    WHERE
1514
                                        c_id = $course_id AND
1515
                                        iid = '$lp_item_id' AND
1516
                                        lp_id = '$lp_id'";
1517
                            $res_path = Database::query($sql);
1518
                            $row_path = Database::fetch_array($res_path);
1519
1520
                            if (Database::num_rows($res_path) > 0) {
1521
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1522
                                        WHERE
1523
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1524
                                            status <> "incomplete" AND
1525
                                            exe_user_id="'.$user_id.'" AND
1526
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1527
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1528
                                            c_id = '.$course_id.'  AND
1529
                                            session_id = '.$session_id.'
1530
                                        ORDER BY exe_date';
1531
                                $res_attempts = Database::query($sql);
1532
                                $num_attempts = Database::num_rows($res_attempts);
1533
                                if ($num_attempts > 0) {
1534
                                    $n = 1;
1535
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1536
                                        $my_score = $row_attempts['exe_result'];
1537
                                        $my_maxscore = $row_attempts['exe_weighting'];
1538
                                        $my_exe_id = $row_attempts['exe_id'];
1539
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1540
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1541
                                        $time_attemp = ' - ';
1542
                                        if ($mktime_start_date && $mktime_exe_date) {
1543
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1544
                                        }
1545
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1546
                                            $view_score = Display::return_icon(
1547
                                                'invisible.png',
1548
                                                get_lang(
1549
                                                    'ResultsHiddenByExerciseSetting'
1550
                                                )
1551
                                            );
1552
                                        } else {
1553
                                            // Show only float when need it
1554
                                            if ($my_score == 0) {
1555
                                                $view_score = ExerciseLib::show_score(
1556
                                                    0,
1557
                                                    $my_maxscore,
1558
                                                    false
1559
                                                );
1560
                                            } else {
1561
                                                if ($my_maxscore == 0) {
1562
                                                    $view_score = $my_score;
1563
                                                } else {
1564
                                                    $view_score = ExerciseLib::show_score(
1565
                                                        $my_score,
1566
                                                        $my_maxscore,
1567
                                                        false
1568
                                                    );
1569
                                                }
1570
                                            }
1571
                                        }
1572
                                        $my_lesson_status = $row_attempts['status'];
1573
                                        if ($my_lesson_status == '') {
1574
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1575
                                        } elseif ($my_lesson_status == 'incomplete') {
1576
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1577
                                        }
1578
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1579
                                        if ($hideTime) {
1580
                                            $timeRow = '';
1581
                                        }
1582
1583
                                        $output .= '<tr class="'.$oddclass.'" >
1584
                                        <td></td>
1585
                                        <td>'.$extend_attempt_link.'</td>
1586
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1587
                                        <td colspan="2">'.$my_lesson_status.'</td>
1588
                                        <td colspan="2">'.$view_score.'</td>
1589
                                        '.$timeRow;
1590
1591
                                        if ($action == 'classic') {
1592
                                            if ($origin != 'tracking') {
1593
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1594
                                                    $output .= '<td>
1595
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1596
                                                            </td>';
1597
                                                } else {
1598
                                                    $output .= '<td>
1599
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1600
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1601
                                                            </a></td>';
1602
                                                }
1603
                                            } else {
1604
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1605
                                                    $output .= '<td>
1606
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
1607
                                                } else {
1608
                                                    $output .= '<td>
1609
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1610
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
1611
                                                }
1612
                                            }
1613
                                        }
1614
                                        $output .= '</tr>';
1615
                                        $n++;
1616
                                    }
1617
                                }
1618
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1619
                            }
1620
                        }
1621
                    }
1622
                }
1623
1624
                $total_time += $time_for_total;
1625
                // QUIZZ IN LP
1626
                $a_my_id = [];
1627
                if (!empty($my_lp_id)) {
1628
                    $a_my_id[] = $my_lp_id;
1629
                }
1630
            }
1631
        }
1632
1633
        // NOT Extend all "left green cross"
1634
        if (!empty($a_my_id)) {
1635
            if ($extendedAttempt) {
1636
                // "Right green cross" extended
1637
                $total_score = self::get_avg_student_score(
1638
                    $user_id,
1639
                    $course_id,
1640
                    $a_my_id,
1641
                    $session_id,
1642
                    false,
1643
                    false
1644
                );
1645
            } else {
1646
                // "Left green cross" extended
1647
                $total_score = self::get_avg_student_score(
1648
                    $user_id,
1649
                    $course_id,
1650
                    $a_my_id,
1651
                    $session_id,
1652
                    false,
1653
                    true
1654
                );
1655
            }
1656
        } else {
1657
            // Extend all "left green cross"
1658
            $total_score = self::get_avg_student_score(
1659
                $user_id,
1660
                $course_id,
1661
                [$lp_id],
1662
                $session_id,
1663
                false,
1664
                false
1665
            );
1666
        }
1667
1668
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1669
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1670
1671
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1672
            $final_score = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
1673
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
1674
        } else {
1675
            if (is_numeric($total_score)) {
1676
                $final_score = $total_score.'%';
1677
            } else {
1678
                $final_score = $total_score;
1679
            }
1680
            $finalScoreToCsv = $final_score;
1681
        }
1682
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1683
1684
        $oddclass = 'row_even';
1685
        if (($counter % 2) == 0) {
1686
            $oddclass = 'row_odd';
1687
        }
1688
1689
        $action = null;
1690
        if ('classic' === $type) {
1691
            $action = '<td></td>';
1692
        }
1693
1694
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1695
        if ($hideTime) {
1696
            $timeTotal = '';
1697
        }
1698
1699
        $output .= '<tr class="'.$oddclass.'">
1700
                <td></td>
1701
                <td colspan="4">
1702
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
1703
                </td>
1704
                <td colspan="2">'.$progress.'%</td>
1705
                <td colspan="2">'.$final_score.'</td>
1706
                '.$timeTotal.'
1707
                '.$action.'
1708
           </tr>';
1709
1710
        $output .= '
1711
                    </tbody>
1712
                </table>
1713
            </div>
1714
        ';
1715
1716
        if (!empty($export_csv)) {
1717
            $temp = [
1718
                '',
1719
                '',
1720
                '',
1721
                '',
1722
            ];
1723
            $csv_content[] = $temp;
1724
            $temp = [
1725
                get_lang('AccomplishedStepsTotal'),
1726
                '',
1727
                $finalScoreToCsv,
1728
            ];
1729
1730
            if ($hideTime === false) {
1731
                $temp[] = $total_time;
1732
            }
1733
1734
            $csv_content[] = $temp;
1735
            ob_end_clean();
1736
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1737
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1738
        }
1739
1740
        return $output;
1741
    }
1742
1743
    /**
1744
     * @param int  $userId
1745
     * @param bool $getCount
1746
     *
1747
     * @return array
1748
     */
1749
    public static function getStats($userId, $getCount = false)
1750
    {
1751
        $courses = [];
1752
        $assignedCourses = [];
1753
        $drhCount = 0;
1754
        $teachersCount = 0;
1755
        $studentBossCount = 0;
1756
        $courseCount = 0;
1757
        $assignedCourseCount = 0;
1758
        $checkSessionVisibility = api_get_configuration_value('show_users_in_active_sessions_in_tracking');
1759
1760
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1761
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1762
                'drh_all',
1763
                $userId,
1764
                false,
1765
                null,
1766
                null,
1767
                null,
1768
                null,
1769
                null,
1770
                null,
1771
                null,
1772
                [],
1773
                [],
1774
                STUDENT
1775
            );
1776
1777
            $students = [];
1778
            if (is_array($studentList)) {
1779
                foreach ($studentList as $studentData) {
1780
                    $students[] = $studentData['user_id'];
1781
                }
1782
            }
1783
1784
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1785
                'drh_all',
1786
                $userId,
1787
                $getCount,
1788
                null,
1789
                null,
1790
                null,
1791
                null,
1792
                null,
1793
                null,
1794
                null,
1795
                [],
1796
                [],
1797
                STUDENT_BOSS
1798
            );
1799
1800
            if ($getCount) {
1801
                $studentBossCount = $studentBossesList;
1802
            } else {
1803
                $studentBosses = [];
1804
                if (is_array($studentBossesList)) {
1805
                    foreach ($studentBossesList as $studentBossData) {
1806
                        $studentBosses[] = $studentBossData['user_id'];
1807
                    }
1808
                }
1809
            }
1810
1811
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1812
                'drh_all',
1813
                $userId,
1814
                $getCount,
1815
                null,
1816
                null,
1817
                null,
1818
                null,
1819
                null,
1820
                null,
1821
                null,
1822
                [],
1823
                [],
1824
                COURSEMANAGER
1825
            );
1826
1827
            if ($getCount) {
1828
                $teachersCount = $teacherList;
1829
            } else {
1830
                $teachers = [];
1831
                foreach ($teacherList as $teacherData) {
1832
                    $teachers[] = $teacherData['user_id'];
1833
                }
1834
            }
1835
1836
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1837
                'drh_all',
1838
                $userId,
1839
                $getCount,
1840
                null,
1841
                null,
1842
                null,
1843
                null,
1844
                null,
1845
                null,
1846
                null,
1847
                [],
1848
                [],
1849
                DRH
1850
            );
1851
1852
            if ($getCount) {
1853
                $drhCount = $humanResources;
1854
            } else {
1855
                $humanResourcesList = [];
1856
                if (is_array($humanResources)) {
1857
                    foreach ($humanResources as $item) {
1858
                        $humanResourcesList[] = $item['user_id'];
1859
                    }
1860
                }
1861
            }
1862
1863
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1864
                $userId,
1865
                null,
1866
                null,
1867
                null,
1868
                null,
1869
                null,
1870
                $getCount
1871
            );
1872
1873
            if ($getCount) {
1874
                $courseCount = $platformCourses;
1875
            } else {
1876
                foreach ($platformCourses as $course) {
1877
                    $courses[$course['code']] = $course['code'];
1878
                }
1879
            }
1880
1881
            $sessions = SessionManager::get_sessions_followed_by_drh(
1882
                $userId,
1883
                null,
1884
                null,
1885
                false
1886
            );
1887
        } else {
1888
            $studentList = UserManager::getUsersFollowedByUser(
1889
                $userId,
1890
                STUDENT,
1891
                false,
1892
                false,
1893
                false,
1894
                null,
1895
                null,
1896
                null,
1897
                null,
1898
                null,
1899
                null,
1900
                COURSEMANAGER,
1901
                null,
1902
                $checkSessionVisibility
1903
            );
1904
1905
            $students = [];
1906
            if (is_array($studentList)) {
1907
                foreach ($studentList as $studentData) {
1908
                    $students[] = $studentData['user_id'];
1909
                }
1910
            }
1911
1912
            $studentBossesList = UserManager::getUsersFollowedByUser(
1913
                $userId,
1914
                STUDENT_BOSS,
1915
                false,
1916
                false,
1917
                $getCount,
1918
                null,
1919
                null,
1920
                null,
1921
                null,
1922
                null,
1923
                null,
1924
                COURSEMANAGER,
1925
                null,
1926
                $checkSessionVisibility
1927
            );
1928
1929
            if ($getCount) {
1930
                $studentBossCount = $studentBossesList;
1931
            } else {
1932
                $studentBosses = [];
1933
                if (is_array($studentBossesList)) {
1934
                    foreach ($studentBossesList as $studentBossData) {
1935
                        $studentBosses[] = $studentBossData['user_id'];
1936
                    }
1937
                }
1938
            }
1939
1940
            $teacherList = UserManager::getUsersFollowedByUser(
1941
                $userId,
1942
                COURSEMANAGER,
1943
                false,
1944
                false,
1945
                $getCount,
1946
                null,
1947
                null,
1948
                null,
1949
                null,
1950
                null,
1951
                null,
1952
                COURSEMANAGER,
1953
                null,
1954
                $checkSessionVisibility
1955
            );
1956
1957
            if ($getCount) {
1958
                $teachersCount = $teacherList;
1959
            } else {
1960
                $teachers = [];
1961
                foreach ($teacherList as $teacherData) {
1962
                    $teachers[] = $teacherData['user_id'];
1963
                }
1964
            }
1965
1966
            $humanResources = UserManager::getUsersFollowedByUser(
1967
                $userId,
1968
                DRH,
1969
                false,
1970
                false,
1971
                $getCount,
1972
                null,
1973
                null,
1974
                null,
1975
                null,
1976
                null,
1977
                null,
1978
                COURSEMANAGER,
1979
                null,
1980
                $checkSessionVisibility
1981
            );
1982
1983
            if ($getCount) {
1984
                $drhCount = $humanResources;
1985
            } else {
1986
                $humanResourcesList = [];
1987
                foreach ($humanResources as $item) {
1988
                    $humanResourcesList[] = $item['user_id'];
1989
                }
1990
            }
1991
1992
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1993
                $userId,
1994
                COURSEMANAGER,
1995
                null,
1996
                null,
1997
                null,
1998
                null,
1999
                $getCount,
2000
                null,
2001
                null,
2002
                true
2003
            );
2004
2005
            if ($getCount) {
2006
                $assignedCourseCount = $platformCourses;
2007
            } else {
2008
                foreach ($platformCourses as $course) {
2009
                    $assignedCourses[$course['code']] = $course['code'];
2010
                }
2011
            }
2012
2013
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2014
                $userId,
2015
                COURSEMANAGER,
2016
                null,
2017
                null,
2018
                null,
2019
                null,
2020
                $getCount
2021
            );
2022
2023
            if ($getCount) {
2024
                $courseCount = $platformCourses;
2025
            } else {
2026
                foreach ($platformCourses as $course) {
2027
                    $courses[$course['code']] = $course['code'];
2028
                }
2029
            }
2030
2031
            $sessions = SessionManager::getSessionsFollowedByUser(
2032
                $userId,
2033
                COURSEMANAGER,
2034
                null,
2035
                null,
2036
                false
2037
            );
2038
        }
2039
2040
        if ($getCount) {
2041
            return [
2042
                'drh' => $drhCount,
2043
                'teachers' => $teachersCount,
2044
                'student_count' => count($students),
2045
                'student_list' => $students,
2046
                'student_bosses' => $studentBossCount,
2047
                'courses' => $courseCount,
2048
                'session_count' => count($sessions),
2049
                'session_list' => $sessions,
2050
                'assigned_courses' => $assignedCourseCount,
2051
            ];
2052
        }
2053
2054
        return [
2055
            'drh' => $humanResourcesList,
2056
            'teachers' => $teachers,
2057
            'student_list' => $students,
2058
            'student_bosses' => $studentBosses,
2059
            'courses' => $courses,
2060
            'sessions' => $sessions,
2061
            'assigned_courses' => $assignedCourses,
2062
        ];
2063
    }
2064
2065
    /**
2066
     * Calculates the time spent on the platform by a user.
2067
     *
2068
     * @param int|array $userId
2069
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
2070
     * @param string    $start_date       start date date('Y-m-d H:i:s')
2071
     * @param string    $end_date         end date date('Y-m-d H:i:s')
2072
     * @param bool      $returnAllRecords
2073
     *
2074
     * @return int
2075
     */
2076
    public static function get_time_spent_on_the_platform(
2077
        $userId,
2078
        $timeFilter = 'last_7_days',
2079
        $start_date = null,
2080
        $end_date = null,
2081
        $returnAllRecords = false
2082
    ) {
2083
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2084
        $condition_time = '';
2085
2086
        if (is_array($userId)) {
2087
            $userList = array_map('intval', $userId);
2088
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
2089
        } else {
2090
            $userId = (int) $userId;
2091
            $userCondition = " login_user_id = $userId ";
2092
        }
2093
2094
        $url_condition = null;
2095
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2096
        $url_table = null;
2097
        if (api_is_multiple_url_enabled()) {
2098
            $access_url_id = api_get_current_access_url_id();
2099
            $url_table = ", $tbl_url_rel_user as url_users";
2100
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2101
        }
2102
2103
        if (empty($timeFilter)) {
2104
            $timeFilter = 'last_week';
2105
        }
2106
2107
        $today = new DateTime('now', new DateTimeZone('UTC'));
2108
2109
        switch ($timeFilter) {
2110
            case 'last_7_days':
2111
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
2112
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2113
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2114
                break;
2115
            case 'last_30_days':
2116
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
2117
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2118
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2119
                break;
2120
            case 'wide':
2121
                if (!empty($start_date) && !empty($end_date)) {
2122
                    $start_date = Database::escape_string($start_date);
2123
                    $end_date = Database::escape_string($end_date);
2124
                    $condition_time = ' AND (
2125
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
2126
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
2127
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
2128
                    ) ';
2129
                }
2130
                break;
2131
            case 'custom':
2132
                if (!empty($start_date) && !empty($end_date)) {
2133
                    $start_date = Database::escape_string($start_date);
2134
                    $end_date = Database::escape_string($end_date);
2135
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
2136
                }
2137
                break;
2138
        }
2139
2140
        if ($returnAllRecords) {
2141
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
2142
                    FROM $tbl_track_login u $url_table
2143
                    WHERE $userCondition $condition_time $url_condition
2144
                    ORDER BY login_date";
2145
            $rs = Database::query($sql);
2146
2147
            return Database::store_result($rs, 'ASSOC');
2148
        }
2149
2150
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2151
    	        FROM $tbl_track_login u $url_table
2152
                WHERE $userCondition $condition_time $url_condition";
2153
        $rs = Database::query($sql);
2154
        $row = Database::fetch_array($rs, 'ASSOC');
2155
        $diff = $row['diff'];
2156
2157
        if ($diff >= 0) {
2158
            return $diff;
2159
        }
2160
2161
        return -1;
2162
    }
2163
2164
    /**
2165
     * @param string $startDate
2166
     * @param string $endDate
2167
     *
2168
     * @return int
2169
     */
2170
    public static function getTotalTimeSpentOnThePlatform(
2171
        $startDate = '',
2172
        $endDate = ''
2173
    ) {
2174
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2175
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2176
2177
        $url_table = null;
2178
        $url_condition = null;
2179
        if (api_is_multiple_url_enabled()) {
2180
            $access_url_id = api_get_current_access_url_id();
2181
            $url_table = ", ".$tbl_url_rel_user." as url_users";
2182
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2183
        }
2184
2185
        if (!empty($startDate) && !empty($endDate)) {
2186
            $startDate = Database::escape_string($startDate);
2187
            $endDate = Database::escape_string($endDate);
2188
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
2189
        }
2190
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2191
    	        FROM $tbl_track_login u $url_table
2192
                WHERE $condition_time $url_condition";
2193
        $rs = Database::query($sql);
2194
        $row = Database::fetch_array($rs, 'ASSOC');
2195
        $diff = $row['diff'];
2196
2197
        if ($diff >= 0) {
2198
            return $diff;
2199
        }
2200
2201
        return -1;
2202
    }
2203
2204
    /**
2205
     * Checks if the "lp_minimum_time" feature is available for the course.
2206
     *
2207
     * @param int $sessionId
2208
     * @param int $courseId
2209
     *
2210
     * @return bool
2211
     */
2212
    public static function minimumTimeAvailable($sessionId, $courseId)
2213
    {
2214
        if (!api_get_configuration_value('lp_minimum_time')) {
2215
            return false;
2216
        }
2217
2218
        if (!empty($sessionId)) {
2219
            $extraFieldValue = new ExtraFieldValue('session');
2220
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
2221
2222
            if ($value && isset($value['value']) && 1 == $value['value']) {
2223
                return true;
2224
            }
2225
        } else {
2226
            if ($courseId) {
2227
                $extraFieldValue = new ExtraFieldValue('course');
2228
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
2229
                if ($value && isset($value['value']) && 1 == $value['value']) {
2230
                    return true;
2231
                }
2232
            }
2233
        }
2234
2235
        return false;
2236
    }
2237
2238
    /**
2239
     * Calculates the time spent on the course.
2240
     *
2241
     * @param int $user_id
2242
     * @param int $courseId
2243
     * @param int $session_id
2244
     *
2245
     * @return int Time in seconds
2246
     */
2247
    public static function get_time_spent_on_the_course(
2248
        $user_id,
2249
        $courseId,
2250
        $session_id = 0
2251
    ) {
2252
        $courseId = (int) $courseId;
2253
2254
        if (empty($courseId) || empty($user_id)) {
2255
            return 0;
2256
        }
2257
2258
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2259
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
2260
2261
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
2262
        }
2263
2264
        $conditionUser = '';
2265
        $session_id = (int) $session_id;
2266
        if (is_array($user_id)) {
2267
            $user_id = array_map('intval', $user_id);
2268
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
2269
        } else {
2270
            if (!empty($user_id)) {
2271
                $user_id = (int) $user_id;
2272
                $conditionUser = " AND user_id = $user_id ";
2273
            }
2274
        }
2275
2276
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2277
        $sql = "SELECT
2278
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
2279
                FROM $table
2280
                WHERE
2281
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
2282
                    c_id = '$courseId' ";
2283
2284
        if (-1 != $session_id) {
2285
            $sql .= "AND session_id = '$session_id' ";
2286
        }
2287
2288
        $sql .= $conditionUser;
2289
2290
        $rs = Database::query($sql);
2291
        $row = Database::fetch_array($rs);
2292
2293
        return $row['nb_seconds'];
2294
    }
2295
2296
    /**
2297
     * Get first connection date for a student.
2298
     *
2299
     * @param int $student_id
2300
     *
2301
     * @return string|bool Date format long without day or false if there are no connections
2302
     */
2303
    public static function get_first_connection_date($student_id)
2304
    {
2305
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2306
        $sql = 'SELECT login_date
2307
                FROM '.$table.'
2308
                WHERE login_user_id = '.intval($student_id).'
2309
                ORDER BY login_date ASC
2310
                LIMIT 0,1';
2311
2312
        $rs = Database::query($sql);
2313
        if (Database::num_rows($rs) > 0) {
2314
            if ($first_login_date = Database::result($rs, 0, 0)) {
2315
                return api_convert_and_format_date(
2316
                    $first_login_date,
2317
                    DATE_FORMAT_SHORT
2318
                );
2319
            }
2320
        }
2321
2322
        return false;
2323
    }
2324
2325
    /**
2326
     * Get las connection date for a student.
2327
     *
2328
     * @param int  $student_id
2329
     * @param bool $warning_message  Show a warning message (optional)
2330
     * @param bool $return_timestamp True for returning results in timestamp (optional)
2331
     *
2332
     * @return string|int|bool Date format long without day, false if there are no connections or
2333
     *                         timestamp if parameter $return_timestamp is true
2334
     */
2335
    public static function get_last_connection_date(
2336
        $student_id,
2337
        $warning_message = false,
2338
        $return_timestamp = false
2339
    ) {
2340
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2341
        $sql = 'SELECT login_date
2342
                FROM '.$table.'
2343
                WHERE login_user_id = '.intval($student_id).'
2344
                ORDER BY login_date
2345
                DESC LIMIT 0,1';
2346
2347
        $rs = Database::query($sql);
2348
        if (Database::num_rows($rs) > 0) {
2349
            if ($last_login_date = Database::result($rs, 0, 0)) {
2350
                $last_login_date = api_get_local_time($last_login_date);
2351
                if ($return_timestamp) {
2352
                    return api_strtotime($last_login_date, 'UTC');
2353
                } else {
2354
                    if (!$warning_message) {
2355
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
2356
                    } else {
2357
                        $timestamp = api_strtotime($last_login_date, 'UTC');
2358
                        $currentTimestamp = time();
2359
2360
                        //If the last connection is > than 7 days, the text is red
2361
                        //345600 = 7 days in seconds
2362
                        if ($currentTimestamp - $timestamp > 604800) {
2363
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</span>';
2364
                        } else {
2365
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
2366
                        }
2367
                    }
2368
                }
2369
            }
2370
        }
2371
2372
        return false;
2373
    }
2374
2375
    /**
2376
     * Get first user's connection date on the course.
2377
     *
2378
     * @param int User id
2379
     * @param int $courseId
2380
     * @param int Session id (optional, default=0)
2381
     * @param bool $convert_date
2382
     *
2383
     * @return string|bool Date with format long without day or false if there is no date
2384
     */
2385
    public static function get_first_connection_date_on_the_course(
2386
        $student_id,
2387
        $courseId,
2388
        $session_id = 0,
2389
        $convert_date = true
2390
    ) {
2391
        $student_id = (int) $student_id;
2392
        $courseId = (int) $courseId;
2393
        $session_id = (int) $session_id;
2394
2395
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2396
        $sql = 'SELECT login_course_date
2397
                FROM '.$table.'
2398
                WHERE
2399
                    user_id = '.$student_id.' AND
2400
                    c_id = '.$courseId.' AND
2401
                    session_id = '.$session_id.'
2402
                ORDER BY login_course_date ASC
2403
                LIMIT 0,1';
2404
        $rs = Database::query($sql);
2405
        if (Database::num_rows($rs) > 0) {
2406
            if ($first_login_date = Database::result($rs, 0, 0)) {
2407
                if (empty($first_login_date)) {
2408
                    return false;
2409
                }
2410
2411
                if ($convert_date) {
2412
                    return api_convert_and_format_date(
2413
                        $first_login_date,
2414
                        DATE_FORMAT_SHORT
2415
                    );
2416
                }
2417
2418
                return $first_login_date;
2419
            }
2420
        }
2421
2422
        return false;
2423
    }
2424
2425
    /**
2426
     * Get last user's connection date on the course.
2427
     *
2428
     * @param int         User id
2429
     * @param array $courseInfo real_id and code are used
2430
     * @param int            Session id (optional, default=0)
2431
     * @param bool $convert_date
2432
     *
2433
     * @return string|bool Date with format long without day or false if there is no date
2434
     */
2435
    public static function get_last_connection_date_on_the_course(
2436
        $student_id,
2437
        $courseInfo,
2438
        $session_id = 0,
2439
        $convert_date = true
2440
    ) {
2441
        // protect data
2442
        $student_id = (int) $student_id;
2443
        $session_id = (int) $session_id;
2444
2445
        if (empty($courseInfo) || empty($student_id)) {
2446
            return false;
2447
        }
2448
2449
        $courseId = $courseInfo['real_id'];
2450
2451
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2452
2453
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2454
            // Show the last date on which the user acceed the session when it was active
2455
            $where_condition = '';
2456
            $userInfo = api_get_user_info($student_id);
2457
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
2458
                // fin de acceso a la sesión
2459
                $sessionInfo = SessionManager::fetch($session_id);
2460
                $last_access = $sessionInfo['access_end_date'];
2461
                if (!empty($last_access)) {
2462
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
2463
                }
2464
            }
2465
            $sql = "SELECT logout_course_date
2466
                    FROM $table
2467
                    WHERE   user_id = $student_id AND
2468
                            c_id = $courseId AND
2469
                            session_id = $session_id $where_condition
2470
                    ORDER BY logout_course_date DESC
2471
                    LIMIT 0,1";
2472
2473
            $rs = Database::query($sql);
2474
            if (Database::num_rows($rs) > 0) {
2475
                if ($last_login_date = Database::result($rs, 0, 0)) {
2476
                    if (empty($last_login_date)) {
2477
                        return false;
2478
                    }
2479
                    if ($convert_date) {
2480
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2481
                    }
2482
2483
                    return $last_login_date;
2484
                }
2485
            }
2486
        } else {
2487
            $sql = "SELECT logout_course_date
2488
                    FROM $table
2489
                    WHERE   user_id = $student_id AND
2490
                            c_id = $courseId AND
2491
                            session_id = $session_id
2492
                    ORDER BY logout_course_date DESC
2493
                    LIMIT 0,1";
2494
2495
            $rs = Database::query($sql);
2496
            if (Database::num_rows($rs) > 0) {
2497
                if ($last_login_date = Database::result($rs, 0, 0)) {
2498
                    if (empty($last_login_date)) {
2499
                        return false;
2500
                    }
2501
                    //see #5736
2502
                    $last_login_date_timestamp = api_strtotime($last_login_date);
2503
                    $now = time();
2504
                    //If the last connection is > than 7 days, the text is red
2505
                    //345600 = 7 days in seconds
2506
                    if ($now - $last_login_date_timestamp > 604800) {
2507
                        if ($convert_date) {
2508
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2509
                            $icon = null;
2510
                            if (api_is_allowed_to_edit()) {
2511
                                $url = api_get_path(WEB_CODE_PATH).
2512
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
2513
                                $icon = '<a href="'.$url.'" title="'.get_lang('RemindInactiveUser').'">
2514
                                  '.Display::return_icon('messagebox_warning.gif').'
2515
                                 </a>';
2516
                            }
2517
2518
                            return $icon.Display::label($last_login_date, 'warning');
2519
                        }
2520
2521
                        return $last_login_date;
2522
                    } else {
2523
                        if ($convert_date) {
2524
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2525
                        }
2526
2527
                        return $last_login_date;
2528
                    }
2529
                }
2530
            }
2531
        }
2532
2533
        return false;
2534
    }
2535
2536
    public static function getLastConnectionInAnyCourse($studentId)
2537
    {
2538
        $studentId = (int) $studentId;
2539
2540
        if (empty($studentId)) {
2541
            return false;
2542
        }
2543
2544
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2545
        $sql = "SELECT logout_course_date
2546
                FROM $table
2547
                WHERE user_id = $studentId
2548
                ORDER BY logout_course_date DESC
2549
                LIMIT 1";
2550
        $result = Database::query($sql);
2551
        if (Database::num_rows($result)) {
2552
            $row = Database::fetch_array($result);
2553
2554
            return $row['logout_course_date'];
2555
        }
2556
2557
        return false;
2558
    }
2559
2560
    /**
2561
     * Get last course access by course/session.
2562
     */
2563
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
2564
    {
2565
        $courseId = (int) $courseId;
2566
        $sessionId = (int) $sessionId;
2567
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2568
2569
        $sql = "SELECT logout_course_date
2570
                FROM $table
2571
                WHERE
2572
                        c_id = $courseId AND
2573
                        session_id = $sessionId
2574
                ORDER BY logout_course_date DESC
2575
                LIMIT 0,1";
2576
2577
        $result = Database::query($sql);
2578
        if (Database::num_rows($result)) {
2579
            $row = Database::fetch_array($result);
2580
            if ($row) {
2581
                return $row['logout_course_date'];
2582
            }
2583
        }
2584
2585
        return '';
2586
    }
2587
2588
    /**
2589
     * Get count of the connections to the course during a specified period.
2590
     *
2591
     * @param int $courseId
2592
     * @param   int     Session id (optional)
2593
     * @param   int     Datetime from which to collect data (defaults to 0)
2594
     * @param   int     Datetime to which to collect data (defaults to now)
2595
     *
2596
     * @return int count connections
2597
     */
2598
    public static function get_course_connections_count(
2599
        $courseId,
2600
        $session_id = 0,
2601
        $start = 0,
2602
        $stop = null
2603
    ) {
2604
        if ($start < 0) {
2605
            $start = 0;
2606
        }
2607
        if (!isset($stop) || $stop < 0) {
2608
            $stop = api_get_utc_datetime();
2609
        }
2610
2611
        // Given we're storing in cache, round the start and end times
2612
        // to the lower minute
2613
        $roundedStart = substr($start, 0, -2).'00';
2614
        $roundedStop = substr($stop, 0, -2).'00';
2615
        $roundedStart = Database::escape_string($roundedStart);
2616
        $roundedStop = Database::escape_string($roundedStop);
2617
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
2618
        $courseId = (int) $courseId;
2619
        $session_id = (int) $session_id;
2620
        $count = 0;
2621
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2622
        $sql = "SELECT count(*) as count_connections
2623
                FROM $table
2624
                WHERE
2625
                    c_id = $courseId AND
2626
                    session_id = $session_id
2627
                    $month_filter";
2628
2629
        //This query can be very slow (several seconds on an indexed table
2630
        // with 14M rows). As such, we'll try to use APCu if it is
2631
        // available to store the resulting value for a few seconds
2632
        $cacheAvailable = api_get_configuration_value('apc');
2633
        if ($cacheAvailable === true) {
2634
            $apc = apcu_cache_info(true);
2635
            $apc_end = $apc['start_time'] + $apc['ttl'];
2636
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
2637
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
2638
                apcu_fetch($apc_var) > 0
2639
            ) {
2640
                $count = apcu_fetch($apc_var);
2641
            } else {
2642
                $rs = Database::query($sql);
2643
                if (Database::num_rows($rs) > 0) {
2644
                    $row = Database::fetch_object($rs);
2645
                    $count = $row->count_connections;
2646
                }
2647
                apcu_clear_cache();
2648
                apcu_store($apc_var, $count, 60);
2649
            }
2650
        } else {
2651
            $rs = Database::query($sql);
2652
            if (Database::num_rows($rs) > 0) {
2653
                $row = Database::fetch_object($rs);
2654
                $count = $row->count_connections;
2655
            }
2656
        }
2657
2658
        return $count;
2659
    }
2660
2661
    /**
2662
     * Get count courses per student.
2663
     *
2664
     * @param int  $user_id          Student id
2665
     * @param bool $include_sessions Include sessions (optional)
2666
     *
2667
     * @return int count courses
2668
     */
2669
    public static function count_course_per_student($user_id, $include_sessions = true)
2670
    {
2671
        $user_id = (int) $user_id;
2672
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2673
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2674
2675
        $sql = 'SELECT DISTINCT c_id
2676
                FROM '.$tbl_course_rel_user.'
2677
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
2678
        $rs = Database::query($sql);
2679
        $nb_courses = Database::num_rows($rs);
2680
2681
        if ($include_sessions) {
2682
            $sql = 'SELECT DISTINCT c_id
2683
                    FROM '.$tbl_session_course_rel_user.'
2684
                    WHERE user_id = '.$user_id;
2685
            $rs = Database::query($sql);
2686
            $nb_courses += Database::num_rows($rs);
2687
        }
2688
2689
        return $nb_courses;
2690
    }
2691
2692
    /**
2693
     * Gets the score average from all tests in a course by student.
2694
     *
2695
     * @param $student_id
2696
     * @param $course_code
2697
     * @param int  $exercise_id
2698
     * @param null $session_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $session_id is correct as it would always require null to be passed?
Loading history...
2699
     * @param int  $active_filter 2 for consider all tests
2700
     *                            1 for active <> -1
2701
     *                            0 for active <> 0
2702
     * @param int  $into_lp       1 for all exercises
2703
     *                            0 for without LP
2704
     * @param mixed id
2705
     * @param string code
2706
     * @param int id (optional), filtered by exercise
2707
     * @param int id (optional), if param $session_id is null
2708
     *                                                it'll return results including sessions, 0 = session is not
2709
     *                                                filtered
2710
     *
2711
     * @return string value (number %) Which represents a round integer about the score average
2712
     */
2713
    public static function get_avg_student_exercise_score(
2714
        $student_id,
2715
        $course_code,
2716
        $exercise_id = 0,
2717
        $session_id = null,
2718
        $active_filter = 1,
2719
        $into_lp = 0
2720
    ) {
2721
        $course_code = Database::escape_string($course_code);
2722
        $course_info = api_get_course_info($course_code);
2723
        if (!empty($course_info)) {
2724
            // table definition
2725
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2726
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2727
2728
            // Compose a filter based on optional exercise given
2729
            $condition_quiz = "";
2730
            if (!empty($exercise_id)) {
2731
                $exercise_id = intval($exercise_id);
2732
                $condition_quiz = " AND id = $exercise_id ";
2733
            }
2734
2735
            // Compose a filter based on optional session id given
2736
            $condition_session = '';
2737
            if (isset($session_id)) {
2738
                $session_id = intval($session_id);
2739
                $condition_session = " AND session_id = $session_id ";
2740
            }
2741
            if ($active_filter == 1) {
2742
                $condition_active = 'AND active <> -1';
2743
            } elseif ($active_filter == 0) {
2744
                $condition_active = 'AND active <> 0';
2745
            } else {
2746
                $condition_active = '';
2747
            }
2748
            $condition_into_lp = '';
2749
            $select_lp_id = '';
2750
            if ($into_lp == 0) {
2751
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2752
            } else {
2753
                $select_lp_id = ', orig_lp_id as lp_id ';
2754
            }
2755
2756
            $sql = "SELECT count(iid)
2757
    		        FROM $tbl_course_quiz
2758
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2759
            $count_quiz = 0;
2760
            $countQuizResult = Database::query($sql);
2761
            if (!empty($countQuizResult)) {
2762
                $count_quiz = Database::fetch_row($countQuizResult);
2763
            }
2764
2765
            if (!empty($count_quiz[0]) && !empty($student_id)) {
2766
                if (is_array($student_id)) {
2767
                    $student_id = array_map('intval', $student_id);
2768
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2769
                } else {
2770
                    $student_id = intval($student_id);
2771
                    $condition_user = " AND exe_user_id = '$student_id' ";
2772
                }
2773
2774
                if (empty($exercise_id)) {
2775
                    $sql = "SELECT iid FROM $tbl_course_quiz
2776
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2777
                    $result = Database::query($sql);
2778
                    $exercise_list = [];
2779
                    $exercise_id = null;
2780
                    if (!empty($result) && Database::num_rows($result)) {
2781
                        while ($row = Database::fetch_array($result)) {
2782
                            $exercise_list[] = $row['iid'];
2783
                        }
2784
                    }
2785
                    if (!empty($exercise_list)) {
2786
                        $exercise_id = implode("','", $exercise_list);
2787
                    }
2788
                }
2789
2790
                $count_quiz = Database::fetch_row(Database::query($sql));
2791
                $sql = "SELECT
2792
                        SUM(exe_result/exe_weighting*100) as avg_score,
2793
                        COUNT(*) as num_attempts
2794
                        $select_lp_id
2795
                        FROM $tbl_stats_exercise
2796
                        WHERE
2797
                            exe_exo_id IN ('".$exercise_id."')
2798
                            $condition_user AND
2799
                            status = '' AND
2800
                            c_id = {$course_info['real_id']}
2801
                            $condition_session
2802
                            $condition_into_lp
2803
                        ORDER BY exe_date DESC";
2804
2805
                $res = Database::query($sql);
2806
                $row = Database::fetch_array($res);
2807
                $quiz_avg_score = null;
2808
2809
                if (!empty($row['avg_score'])) {
2810
                    $quiz_avg_score = round($row['avg_score'], 2);
2811
                }
2812
2813
                if (!empty($row['num_attempts'])) {
2814
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2815
                }
2816
                if (is_array($student_id)) {
2817
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2818
                }
2819
                if ($into_lp == 0) {
2820
                    return $quiz_avg_score;
2821
                } else {
2822
                    if (!empty($row['lp_id'])) {
2823
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2824
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2825
                        $sql = "SELECT lp.name
2826
                                FROM $tbl_lp as lp, $tbl_course as c
2827
                                WHERE
2828
                                    c.code = '$course_code' AND
2829
                                    lp.id = ".$row['lp_id']." AND
2830
                                    lp.c_id = c.id
2831
                                LIMIT 1;
2832
                        ";
2833
                        $result = Database::query($sql);
2834
                        $row_lp = Database::fetch_row($result);
2835
                        $lp_name = null;
2836
                        if ($row_lp && isset($row_lp[0])) {
2837
                            $lp_name = $row_lp[0];
2838
                        }
2839
2840
                        return [$quiz_avg_score, $lp_name];
2841
                    } else {
2842
                        return [$quiz_avg_score, null];
2843
                    }
2844
                }
2845
            }
2846
        }
2847
2848
        return null;
2849
    }
2850
2851
    /**
2852
     * Get count student's exercise COMPLETED attempts.
2853
     *
2854
     * @param int $student_id
2855
     * @param int $courseId
2856
     * @param int $exercise_id
2857
     * @param int $lp_id
2858
     * @param int $lp_item_id
2859
     * @param int $session_id
2860
     * @param int $find_all_lp 0 = just LP specified
2861
     *                         1 = LP specified or whitout LP,
2862
     *                         2 = all rows
2863
     *
2864
     * @internal param \Student $int id
2865
     * @internal param \Course $string code
2866
     * @internal param \Exercise $int id
2867
     * @internal param \Learning $int path id (optional),
2868
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2869
     * @internal param \Learning $int path item id (optional),
2870
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2871
     *
2872
     * @return int count of attempts
2873
     */
2874
    public static function count_student_exercise_attempts(
2875
        $student_id,
2876
        $courseId,
2877
        $exercise_id,
2878
        $lp_id = 0,
2879
        $lp_item_id = 0,
2880
        $session_id = 0,
2881
        $find_all_lp = 0
2882
    ) {
2883
        $courseId = intval($courseId);
2884
        $student_id = intval($student_id);
2885
        $exercise_id = intval($exercise_id);
2886
        $session_id = intval($session_id);
2887
2888
        $lp_id = intval($lp_id);
2889
        $lp_item_id = intval($lp_item_id);
2890
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2891
2892
        $sql = "SELECT COUNT(ex.exe_id) as essais
2893
                FROM $tbl_stats_exercises AS ex
2894
                WHERE
2895
                    ex.c_id = $courseId AND
2896
                    ex.exe_exo_id = $exercise_id AND
2897
                    status = '' AND
2898
                    exe_user_id= $student_id AND
2899
                    session_id = $session_id ";
2900
2901
        if ($find_all_lp == 1) {
2902
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2903
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2904
        } elseif ($find_all_lp == 0) {
2905
            $sql .= "AND orig_lp_id = $lp_id
2906
                AND orig_lp_item_id = $lp_item_id";
2907
        }
2908
2909
        $rs = Database::query($sql);
2910
        $row = Database::fetch_row($rs);
2911
        $count_attempts = $row[0];
2912
2913
        return $count_attempts;
2914
    }
2915
2916
    /**
2917
     * Get count student's exercise progress.
2918
     *
2919
     * @param array $exercise_list
2920
     * @param int   $user_id
2921
     * @param int   $courseId
2922
     * @param int   $session_id
2923
     *
2924
     * @return string
2925
     */
2926
    public static function get_exercise_student_progress(
2927
        $exercise_list,
2928
        $user_id,
2929
        $courseId,
2930
        $session_id
2931
    ) {
2932
        $courseId = (int) $courseId;
2933
        $user_id = (int) $user_id;
2934
        $session_id = (int) $session_id;
2935
2936
        if (empty($exercise_list)) {
2937
            return '0%';
2938
        }
2939
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2940
        $exercise_list = array_keys($exercise_list);
2941
        $exercise_list = array_map('intval', $exercise_list);
2942
2943
        $exercise_list_imploded = implode("' ,'", $exercise_list);
2944
2945
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
2946
                FROM $tbl_stats_exercises AS ex
2947
                WHERE
2948
                    ex.c_id = $courseId AND
2949
                    ex.session_id  = $session_id AND
2950
                    ex.exe_user_id = $user_id AND
2951
                    ex.status = '' AND
2952
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
2953
2954
        $rs = Database::query($sql);
2955
        $count = 0;
2956
        if ($rs) {
2957
            $row = Database::fetch_row($rs);
2958
            $count = $row[0];
2959
        }
2960
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
2961
2962
        return $count;
2963
    }
2964
2965
    /**
2966
     * @param array $exercise_list
2967
     * @param int   $user_id
2968
     * @param int   $courseId
2969
     * @param int   $session_id
2970
     *
2971
     * @return string
2972
     */
2973
    public static function get_exercise_student_average_best_attempt(
2974
        $exercise_list,
2975
        $user_id,
2976
        $courseId,
2977
        $session_id
2978
    ) {
2979
        $result = 0;
2980
        if (!empty($exercise_list)) {
2981
            foreach ($exercise_list as $exercise_data) {
2982
                $exercise_id = $exercise_data['iid'];
2983
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2984
                    $user_id,
2985
                    $exercise_id,
2986
                    $courseId,
2987
                    $session_id
2988
                );
2989
2990
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
2991
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
2992
                }
2993
            }
2994
            $result = $result / count($exercise_list);
2995
            $result = round($result, 2) * 100;
2996
        }
2997
2998
        return $result.'%';
2999
    }
3000
3001
    /**
3002
     * Returns the average student progress in the learning paths of the given
3003
     * course, it will take into account the progress that were not started.
3004
     *
3005
     * @param int|array $studentId
3006
     * @param string    $courseCode
3007
     * @param array     $lpIdList        Limit average to listed lp ids
3008
     * @param int       $sessionId       Session id (optional),
3009
     *                                   if parameter $session_id is null(default) it'll return results including
3010
     *                                   sessions, 0 = session is not filtered
3011
     * @param bool      $returnArray     Will return an array of the type:
3012
     *                                   [sum_of_progresses, number] if it is set to true
3013
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
3014
     *
3015
     * @return float Average progress of the user in this course from 0 to 100
3016
     */
3017
    public static function get_avg_student_progress(
3018
        $studentId,
3019
        $courseCode = null,
3020
        $lpIdList = [],
3021
        $sessionId = null,
3022
        $returnArray = false,
3023
        $onlySeriousGame = false
3024
    ) {
3025
        // If there is at least one learning path and one student.
3026
        if (empty($studentId)) {
3027
            return false;
3028
        }
3029
3030
        $sessionId = (int) $sessionId;
3031
        $courseInfo = api_get_course_info($courseCode);
3032
3033
        if (empty($courseInfo)) {
3034
            return false;
3035
        }
3036
3037
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
3038
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3039
        $lpConditions = [];
3040
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
3041
3042
        if ($sessionId > 0) {
3043
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
3044
        } else {
3045
            $lpConditions['AND session_id = ?'] = $sessionId;
3046
        }
3047
3048
        if (is_array($lpIdList) && count($lpIdList) > 0) {
3049
            $placeHolders = [];
3050
            for ($i = 0; $i < count($lpIdList); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3051
                $placeHolders[] = '?';
3052
            }
3053
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
3054
        }
3055
3056
        if ($onlySeriousGame) {
3057
            $lpConditions['AND seriousgame_mode = ? '] = true;
3058
        }
3059
3060
        $resultLP = Database::select(
3061
            'id',
3062
            $lPTable,
3063
            ['where' => $lpConditions]
3064
        );
3065
        $filteredLP = array_keys($resultLP);
3066
3067
        if (empty($filteredLP)) {
3068
            return false;
3069
        }
3070
3071
        $conditions = [
3072
            " c_id = {$courseInfo['real_id']} ",
3073
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
3074
        ];
3075
3076
        $groupBy = 'GROUP BY lp_id';
3077
3078
        if (is_array($studentId)) {
3079
            $studentId = array_map('intval', $studentId);
3080
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
3081
        } else {
3082
            $studentId = (int) $studentId;
3083
            $conditions[] = " lp_view.user_id = '$studentId' ";
3084
3085
            if (empty($lpIdList)) {
3086
                $lpList = new LearnpathList(
3087
                    $studentId,
3088
                    $courseInfo,
3089
                    $sessionId,
3090
                    null,
3091
                    false,
3092
                    null,
3093
                    true
3094
                );
3095
                $lpList = $lpList->get_flat_list();
3096
                if (!empty($lpList)) {
3097
                    /** @var $lp */
3098
                    foreach ($lpList as $lpId => $lp) {
3099
                        $lpIdList[] = $lp['lp_old_id'];
3100
                    }
3101
                }
3102
            }
3103
        }
3104
3105
        if (!empty($sessionId)) {
3106
            $conditions[] = " session_id = $sessionId ";
3107
        } else {
3108
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
3109
        }
3110
3111
        $conditionToString = implode('AND', $conditions);
3112
        $sql = "SELECT lp_id, view_count, progress
3113
                FROM $lpViewTable lp_view
3114
                WHERE
3115
                    $conditionToString
3116
                    $groupBy
3117
                ORDER BY view_count DESC";
3118
3119
        $result = Database::query($sql);
3120
3121
        $progress = [];
3122
        $viewCount = [];
3123
        while ($row = Database::fetch_array($result, 'ASSOC')) {
3124
            if (!isset($viewCount[$row['lp_id']])) {
3125
                $progress[$row['lp_id']] = $row['progress'];
3126
            }
3127
            $viewCount[$row['lp_id']] = $row['view_count'];
3128
        }
3129
3130
        // Fill with lp ids
3131
        $newProgress = [];
3132
        if (!empty($lpIdList)) {
3133
            foreach ($lpIdList as $lpId) {
3134
                if (isset($progress[$lpId])) {
3135
                    $newProgress[] = $progress[$lpId];
3136
                }
3137
            }
3138
            $total = count($lpIdList);
3139
        } else {
3140
            $newProgress = $progress;
3141
            $total = count($newProgress);
3142
        }
3143
3144
        $average = 0;
3145
        $sum = 0;
3146
        if (!empty($newProgress)) {
3147
            $sum = array_sum($newProgress);
3148
            $average = $sum / $total;
3149
        }
3150
3151
        if ($returnArray) {
3152
            return [
3153
                $sum,
3154
                $total,
3155
            ];
3156
        }
3157
3158
        return round($average, 1);
3159
    }
3160
3161
    /**
3162
     * This function gets:
3163
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3164
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3165
     * 3. And finally it will return the average between 1. and 2.
3166
     *
3167
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
3168
     * This function does not take the results of a Test out of a LP
3169
     *
3170
     * @param mixed  $student_id                      Array of user ids or an user id
3171
     * @param string $course_code
3172
     * @param array  $lp_ids                          List of LP ids
3173
     * @param int    $session_id                      Session id (optional),
3174
     *                                                if param $session_id is null(default) it'll return results
3175
     *                                                including sessions, 0 = session is not filtered
3176
     * @param bool   $return_array                    Returns an array of the
3177
     *                                                type [sum_score, num_score] if set to true
3178
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
3179
     * @param bool   $getOnlyBestAttempt
3180
     *
3181
     * @return string value (number %) Which represents a round integer explain in got in 3
3182
     */
3183
    public static function get_avg_student_score(
3184
        $student_id,
3185
        $course_code,
3186
        $lp_ids = [],
3187
        $session_id = null,
3188
        $return_array = false,
3189
        $get_only_latest_attempt_results = false,
3190
        $getOnlyBestAttempt = false
3191
    ) {
3192
        $debug = false;
3193
        if ($debug) {
3194
            echo '<h1>Tracking::get_avg_student_score</h1>';
3195
        }
3196
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3197
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3198
        $course = api_get_course_info($course_code);
3199
3200
        if (empty($course)) {
3201
            return null;
3202
        }
3203
3204
        // Get course tables names
3205
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
3206
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3207
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3208
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3209
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3210
        $course_id = $course['real_id'];
3211
3212
        // Compose a filter based on optional learning paths list given
3213
        $condition_lp = '';
3214
        if (count($lp_ids) > 0) {
3215
            $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3216
        }
3217
3218
        // Compose a filter based on optional session id
3219
        $session_id = (int) $session_id;
3220
        if (count($lp_ids) > 0) {
3221
            $condition_session = " AND session_id = $session_id ";
3222
        } else {
3223
            $condition_session = " WHERE session_id = $session_id ";
3224
        }
3225
3226
        // Check the real number of LPs corresponding to the filter in the
3227
        // database (and if no list was given, get them all)
3228
        if (empty($session_id)) {
3229
            $sql = "SELECT DISTINCT(id), use_max_score
3230
                    FROM $lp_table
3231
                    WHERE
3232
                        c_id = $course_id AND
3233
                        (session_id = 0 OR session_id IS NULL) $condition_lp ";
3234
        } else {
3235
            $sql = "SELECT DISTINCT(id), use_max_score
3236
                    FROM $lp_table
3237
                    WHERE c_id = $course_id $condition_lp ";
3238
        }
3239
3240
        $res_row_lp = Database::query($sql);
3241
        $count_row_lp = Database::num_rows($res_row_lp);
3242
3243
        $lp_list = $use_max_score = [];
3244
        while ($row_lp = Database::fetch_array($res_row_lp)) {
3245
            $lp_list[] = $row_lp['id'];
3246
            $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
3247
        }
3248
3249
        // prepare filter on users
3250
        if (is_array($student_id)) {
3251
            array_walk($student_id, 'intval');
3252
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
3253
        } else {
3254
            $condition_user1 = " AND user_id = $student_id ";
3255
        }
3256
3257
        if (empty($count_row_lp) || empty($student_id)) {
3258
            return null;
3259
        }
3260
3261
        // Getting latest LP result for a student
3262
        //@todo problem when a  course have more than 1500 users
3263
        $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
3264
                FROM $lp_view_table
3265
                WHERE
3266
                    c_id = $course_id AND
3267
                    lp_id IN (".implode(',', $lp_list).")
3268
                    $condition_user1 AND
3269
                    session_id = $session_id
3270
                GROUP BY lp_id, user_id";
3271
3272
        $rs_last_lp_view_id = Database::query($sql);
3273
        $global_result = 0;
3274
3275
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
3276
            // Cycle through each line of the results (grouped by lp_id, user_id)
3277
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
3278
                $count_items = 0;
3279
                $lpPartialTotal = 0;
3280
                $list = [];
3281
                $lp_view_id = $row_lp_view['id'];
3282
                $lp_id = $row_lp_view['lp_id'];
3283
                $user_id = $row_lp_view['user_id'];
3284
3285
                if ($debug) {
3286
                    echo '<h2>LP id '.$lp_id.'</h2>';
3287
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
3288
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
3289
                }
3290
3291
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
3292
                    // Getting lp_items done by the user
3293
                    $sql = "SELECT DISTINCT lp_item_id
3294
                            FROM $lp_item_view_table
3295
                            WHERE
3296
                                c_id = $course_id AND
3297
                                lp_view_id = $lp_view_id
3298
                            ORDER BY lp_item_id";
3299
                    $res_lp_item = Database::query($sql);
3300
3301
                    while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
3302
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
3303
                        $order = ' view_count DESC';
3304
                        if ($getOnlyBestAttempt) {
3305
                            $order = ' lp_iv.score DESC';
3306
                        }
3307
3308
                        // Getting the most recent attempt
3309
                        $sql = "SELECT
3310
                                    lp_iv.id as lp_item_view_id,
3311
                                    lp_iv.score as score,
3312
                                    lp_i.max_score,
3313
                                    lp_iv.max_score as max_score_item_view,
3314
                                    lp_i.path,
3315
                                    lp_i.item_type,
3316
                                    lp_i.id as iid
3317
                                FROM $lp_item_view_table as lp_iv
3318
                                INNER JOIN $lp_item_table as lp_i
3319
                                ON (
3320
                                    lp_i.id = lp_iv.lp_item_id AND
3321
                                    lp_iv.c_id = lp_i.c_id
3322
                                )
3323
                                WHERE
3324
                                    lp_iv.c_id = $course_id AND
3325
                                    lp_i.c_id  = $course_id AND
3326
                                    lp_item_id = $my_lp_item_id AND
3327
                                    lp_view_id = $lp_view_id AND
3328
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
3329
                                ORDER BY $order
3330
                                LIMIT 1";
3331
3332
                        $res_lp_item_result = Database::query($sql);
3333
                        while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
3334
                            $list[] = $row_max_score;
3335
                        }
3336
                    }
3337
                } else {
3338
                    // For the currently analysed view, get the score and
3339
                    // max_score of each item if it is a sco or a TOOL_QUIZ
3340
                    $sql = "SELECT
3341
                                lp_iv.id as lp_item_view_id,
3342
                                lp_iv.score as score,
3343
                                lp_i.max_score,
3344
                                lp_iv.max_score as max_score_item_view,
3345
                                lp_i.path,
3346
                                lp_i.item_type,
3347
                                lp_i.id as iid
3348
                              FROM $lp_item_view_table as lp_iv
3349
                              INNER JOIN $lp_item_table as lp_i
3350
                              ON lp_i.id = lp_iv.lp_item_id AND
3351
                                 lp_iv.c_id = lp_i.c_id
3352
                              WHERE
3353
                                lp_iv.c_id = $course_id AND
3354
                                lp_i.c_id  = $course_id AND
3355
                                lp_view_id = $lp_view_id AND
3356
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
3357
                            ";
3358
                    $res_max_score = Database::query($sql);
3359
                    while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
3360
                        $list[] = $row_max_score;
3361
                    }
3362
                }
3363
3364
                // Go through each scorable element of this view
3365
                $score_of_scorm_calculate = 0;
3366
                foreach ($list as $row_max_score) {
3367
                    // Came from the original lp_item
3368
                    $max_score = $row_max_score['max_score'];
3369
                    // Came from the lp_item_view
3370
                    $max_score_item_view = $row_max_score['max_score_item_view'];
3371
                    $score = $row_max_score['score'];
3372
                    if ($debug) {
3373
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
3374
                    }
3375
3376
                    if ($row_max_score['item_type'] === 'sco') {
3377
                        /* Check if it is sco (easier to get max_score)
3378
                           when there's no max score, we assume 100 as the max score,
3379
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
3380
                        */
3381
                        if ($max_score == 0 || is_null($max_score) || $max_score == '') {
3382
                            // Chamilo style
3383
                            if ($use_max_score[$lp_id]) {
3384
                                $max_score = 100;
3385
                            } else {
3386
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
3387
                                $max_score = $max_score_item_view;
3388
                            }
3389
                        }
3390
                        // Avoid division by zero errors
3391
                        if (!empty($max_score)) {
3392
                            $lpPartialTotal += $score / $max_score;
3393
                        }
3394
                        if ($debug) {
3395
                            var_dump("lpPartialTotal: $lpPartialTotal");
0 ignored issues
show
Security Debugging Code introduced by
var_dump('lpPartialTotal: '.$lpPartialTotal) looks like debug code. Are you sure you do not want to remove it?
Loading history...
3396
                            var_dump("score: $score");
3397
                            var_dump("max_score: $max_score");
3398
                        }
3399
                    } else {
3400
                        // Case of a TOOL_QUIZ element
3401
                        $item_id = $row_max_score['iid'];
3402
                        $item_path = $row_max_score['path'];
3403
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
3404
3405
                        if (empty($lp_item_view_id)) {
3406
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
3407
                        } else {
3408
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
3409
                        }
3410
3411
                        // Get last attempt to this exercise through
3412
                        // the current lp for the current user
3413
                        $order = 'exe_date DESC';
3414
                        if ($getOnlyBestAttempt) {
3415
                            $order = 'exe_result DESC';
3416
                        }
3417
                        $sql = "SELECT exe_id, exe_result
3418
                                FROM $tbl_stats_exercices
3419
                                WHERE
3420
                                    exe_exo_id = '$item_path' AND
3421
                                    exe_user_id = $user_id AND
3422
                                    orig_lp_item_id = $item_id AND
3423
                                    $lpItemCondition AND
3424
                                    c_id = $course_id AND
3425
                                    session_id = $session_id AND
3426
                                    status = ''
3427
                                ORDER BY $order
3428
                                LIMIT 1";
3429
3430
                        $result_last_attempt = Database::query($sql);
3431
                        $num = Database::num_rows($result_last_attempt);
3432
                        if ($num > 0) {
3433
                            $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
3434
                            $id_last_attempt = (int) $attemptResult['exe_id'];
3435
                            // We overwrite the score with the best one not the one saved in the LP (latest)
3436
                            if ($getOnlyBestAttempt && $get_only_latest_attempt_results == false) {
3437
                                if ($debug) {
3438
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
3439
                                }
3440
                                $score = $attemptResult['exe_result'];
3441
                            }
3442
3443
                            if ($debug) {
3444
                                echo "Attempt id: $id_last_attempt with score $score<br />";
3445
                            }
3446
                            // Within the last attempt number tracking, get the sum of
3447
                            // the max_scores of all questions that it was
3448
                            // made of (we need to make this call dynamic because of random questions selection)
3449
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
3450
                                    (
3451
                                        SELECT DISTINCT
3452
                                            question_id,
3453
                                            marks,
3454
                                            ponderation
3455
                                        FROM $tbl_stats_attempts AS at
3456
                                        INNER JOIN $tbl_quiz_questions AS q
3457
                                        ON q.iid = at.question_id
3458
                                        WHERE
3459
                                            exe_id = $id_last_attempt AND
3460
                                            at.c_id = $course_id
3461
                                    )
3462
                                    AS t";
3463
3464
                            $res_max_score_bis = Database::query($sql);
3465
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
3466
3467
                            if (!empty($row_max_score_bis['maxscore'])) {
3468
                                $max_score = $row_max_score_bis['maxscore'];
3469
                            }
3470
                            if (!empty($max_score) && floatval($max_score) > 0) {
3471
                                $lpPartialTotal += $score / $max_score;
3472
                            }
3473
                            if ($debug) {
3474
                                var_dump("score: $score");
3475
                                var_dump("max_score: $max_score");
3476
                                var_dump("lpPartialTotal: $lpPartialTotal");
3477
                            }
3478
                        }
3479
                    }
3480
3481
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
3482
                        // Normal way
3483
                        if ($use_max_score[$lp_id]) {
3484
                            $count_items++;
3485
                        } else {
3486
                            if ($max_score != '') {
3487
                                $count_items++;
3488
                            }
3489
                        }
3490
                        if ($debug) {
3491
                            echo '$count_items: '.$count_items;
3492
                        }
3493
                    }
3494
                } //end for
3495
3496
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
3497
                $global_result += $score_of_scorm_calculate;
3498
3499
                if ($debug) {
3500
                    var_dump("count_items: $count_items");
3501
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
3502
                    var_dump("global_result: $global_result");
3503
                }
3504
            } // end while
3505
        }
3506
3507
        $lp_with_quiz = 0;
3508
        foreach ($lp_list as $lp_id) {
3509
            // Check if LP have a score we assume that all SCO have an score
3510
            $sql = "SELECT count(id) as count
3511
                    FROM $lp_item_table
3512
                    WHERE
3513
                        c_id = $course_id AND
3514
                        (item_type = 'quiz' OR item_type = 'sco') AND
3515
                        lp_id = ".$lp_id;
3516
            $result_have_quiz = Database::query($sql);
3517
            if (Database::num_rows($result_have_quiz) > 0) {
3518
                $row = Database::fetch_array($result_have_quiz, 'ASSOC');
3519
                if (is_numeric($row['count']) && $row['count'] != 0) {
3520
                    $lp_with_quiz++;
3521
                }
3522
            }
3523
        }
3524
3525
        if ($debug) {
3526
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
3527
        }
3528
        if ($debug) {
3529
            echo '<h3>Final return</h3>';
3530
        }
3531
3532
        if ($lp_with_quiz != 0) {
3533
            if (!$return_array) {
3534
                $score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
3535
                if ($debug) {
3536
                    var_dump($score_of_scorm_calculate);
3537
                }
3538
                if (empty($lp_ids)) {
3539
                    if ($debug) {
3540
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
3541
                    }
3542
                }
3543
3544
                return $score_of_scorm_calculate;
3545
            }
3546
3547
            if ($debug) {
3548
                var_dump($global_result, $lp_with_quiz);
3549
            }
3550
3551
            return [$global_result, $lp_with_quiz];
3552
        }
3553
3554
        return '-';
3555
    }
3556
3557
    /**
3558
     * This function gets:
3559
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3560
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3561
     * 3. And finally it will return the average between 1. and 2.
3562
     * This function does not take the results of a Test out of a LP.
3563
     *
3564
     * @param int|array $student_id  Array of user ids or an user id
3565
     * @param string    $course_code Course code
3566
     * @param array     $lp_ids      List of LP ids
3567
     * @param int       $session_id  Session id (optional), if param $session_id is 0(default)
3568
     *                               it'll return results including sessions, 0 = session is not filtered
3569
     *
3570
     * @return string value (number %) Which represents a round integer explain in got in 3
3571
     */
3572
    public static function getAverageStudentScore(
3573
        $student_id,
3574
        $course_code = '',
3575
        $lp_ids = [],
3576
        $session_id = 0
3577
    ) {
3578
        if (empty($student_id)) {
3579
            return 0;
3580
        }
3581
3582
        $conditions = [];
3583
        if (!empty($course_code)) {
3584
            $course = api_get_course_info($course_code);
3585
            $courseId = $course['real_id'];
3586
            $conditions[] = " lp.c_id = $courseId";
3587
        }
3588
3589
        // Get course tables names
3590
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3591
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3592
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3593
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3594
3595
        // Compose a filter based on optional learning paths list given
3596
        if (!empty($lp_ids) && count($lp_ids) > 0) {
3597
            $conditions[] = ' lp.id IN ('.implode(',', $lp_ids).') ';
3598
        }
3599
3600
        // Compose a filter based on optional session id
3601
        $session_id = (int) $session_id;
3602
        if (!empty($session_id)) {
3603
            $conditions[] = " lp_view.session_id = $session_id ";
3604
        }
3605
3606
        if (is_array($student_id)) {
3607
            array_walk($student_id, 'intval');
3608
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
3609
        } else {
3610
            $student_id = (int) $student_id;
3611
            $conditions[] = " lp_view.user_id = $student_id ";
3612
        }
3613
3614
        $conditionsToString = implode(' AND ', $conditions);
3615
        $sql = "SELECT
3616
                    SUM(lp_iv.score) sum_score,
3617
                    SUM(lp_i.max_score) sum_max_score
3618
                FROM $lp_table as lp
3619
                INNER JOIN $lp_item_table as lp_i
3620
                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
3621
                INNER JOIN $lp_view_table as lp_view
3622
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
3623
                INNER JOIN $lp_item_view_table as lp_iv
3624
                ON lp_i.iid = lp_iv.lp_item_id AND lp_view.c_id = lp_iv.c_id AND lp_iv.lp_view_id = lp_view.iid
3625
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
3626
                $conditionsToString
3627
        ";
3628
        $result = Database::query($sql);
3629
        $row = Database::fetch_array($result, 'ASSOC');
3630
3631
        if (empty($row['sum_max_score'])) {
3632
            return 0;
3633
        }
3634
3635
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
3636
    }
3637
3638
    /**
3639
     * This function gets time spent in learning path for a student inside a course.
3640
     *
3641
     * @param int|array $student_id  Student id(s)
3642
     * @param string    $course_code Course code
3643
     * @param array     $lp_ids      Limit average to listed lp ids
3644
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
3645
     *                               it'll return results including sessions, 0 = session is not filtered
3646
     *
3647
     * @return int Total time in seconds
3648
     */
3649
    public static function get_time_spent_in_lp(
3650
        $student_id,
3651
        $course_code,
3652
        $lp_ids = [],
3653
        $session_id = 0
3654
    ) {
3655
        $course = api_get_course_info($course_code);
3656
        $student_id = (int) $student_id;
3657
        $session_id = (int) $session_id;
3658
        $total_time = 0;
3659
3660
        if (!empty($course)) {
3661
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
3662
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
3663
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3664
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3665
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3666
            $course_id = $course['real_id'];
3667
3668
            // Compose a filter based on optional learning paths list given
3669
            $condition_lp = '';
3670
            if (count($lp_ids) > 0) {
3671
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3672
            }
3673
3674
            // Check the real number of LPs corresponding to the filter in the
3675
            // database (and if no list was given, get them all)
3676
            $sql = "SELECT DISTINCT(id) FROM $lpTable
3677
                    WHERE c_id = $course_id $condition_lp";
3678
            $result = Database::query($sql);
3679
            $session_condition = api_get_session_condition($session_id);
3680
3681
            // calculates time
3682
            if (Database::num_rows($result) > 0) {
3683
                while ($row = Database::fetch_array($result)) {
3684
                    $lp_id = (int) $row['id'];
3685
3686
                    // Start Exercise in LP total_time
3687
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
3688
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
3689
                    foreach ($list as $itemId) {
3690
                        $sql = "SELECT max(view_count)
3691
                                FROM $lpViewTable
3692
                                WHERE
3693
                                    c_id = $course_id AND
3694
                                    lp_id = $lp_id AND
3695
                                    user_id = $student_id
3696
                                    $session_condition";
3697
                        $res = Database::query($sql);
3698
                        $view = '';
3699
                        if (Database::num_rows($res) > 0) {
3700
                            $myrow = Database::fetch_array($res);
3701
                            $view = $myrow[0];
3702
                        }
3703
                        $viewCondition = null;
3704
                        if (!empty($view)) {
3705
                            $viewCondition = " AND v.view_count = $view  ";
3706
                        }
3707
                        $sql = "SELECT
3708
                            iv.iid,
3709
                            iv.total_time as mytime,
3710
                            i.id as myid,
3711
                            iv.view_count as iv_view_count,
3712
                            path
3713
                        FROM $lpItemTable as i
3714
                        INNER JOIN $lpItemViewTable as iv
3715
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
3716
                        INNER JOIN $lpViewTable as v
3717
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
3718
                        WHERE
3719
                            v.c_id = $course_id AND
3720
                            i.id = $itemId AND
3721
                            i.lp_id = $lp_id  AND
3722
                            v.user_id = $student_id AND
3723
                            item_type = 'quiz' AND
3724
                            path <> '' AND
3725
                            v.session_id = $session_id
3726
                            $viewCondition
3727
                        ORDER BY iv.view_count DESC ";
3728
3729
                        $resultRow = Database::query($sql);
3730
                        if (Database::num_rows($resultRow)) {
3731
                            $row = Database::fetch_array($resultRow);
3732
                            $totalTimeInLpItemView = $row['mytime'];
3733
                            $lpItemViewId = $row['iid'];
3734
3735
                            $sql = 'SELECT SUM(exe_duration) exe_duration
3736
                                    FROM '.$trackExercises.'
3737
                                    WHERE
3738
                                        exe_exo_id="'.$row['path'].'" AND
3739
                                        exe_user_id="'.$student_id.'" AND
3740
                                        orig_lp_id = "'.$lp_id.'" AND
3741
                                        orig_lp_item_id = "'.$row['myid'].'" AND
3742
                                        c_id = '.$course_id.' AND
3743
                                        status <> "incomplete" AND
3744
                                        session_id = '.$session_id.'
3745
                                     ORDER BY exe_date DESC ';
3746
3747
                            $sumScoreResult = Database::query($sql);
3748
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
3749
                            if (!empty($durationRow['exe_duration'])) {
3750
                                $exeDuration = $durationRow['exe_duration'];
3751
                                if ($exeDuration != $totalTimeInLpItemView &&
3752
                                    !empty($lpItemViewId) &&
3753
                                    !empty($exeDuration)
3754
                                ) {
3755
                                    // Update c_lp_item_view.total_time
3756
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration'
3757
                                                  WHERE iid = ".$lpItemViewId;
3758
                                    Database::query($sqlUpdate);
3759
                                }
3760
                            }
3761
                        }
3762
                    }
3763
3764
                    // End total_time fix
3765
3766
                    // Calculate total time
3767
                    $sql = "SELECT SUM(total_time)
3768
                            FROM $lpItemViewTable AS item_view
3769
                            INNER JOIN $lpViewTable AS view
3770
                            ON (
3771
                                item_view.lp_view_id = view.id AND
3772
                                item_view.c_id = view.c_id
3773
                            )
3774
                            WHERE
3775
                                item_view.c_id = $course_id AND
3776
                                view.c_id = $course_id AND
3777
                                view.lp_id = $lp_id AND
3778
                                view.user_id = $student_id AND
3779
                                session_id = $session_id";
3780
3781
                    $rs = Database::query($sql);
3782
                    if (Database::num_rows($rs) > 0) {
3783
                        $total_time += Database::result($rs, 0, 0);
3784
                    }
3785
                }
3786
            }
3787
        }
3788
3789
        return $total_time;
3790
    }
3791
3792
    /**
3793
     * This function gets last connection time to one learning path.
3794
     *
3795
     * @param int|array $student_id  Student id(s)
3796
     * @param string    $course_code Course code
3797
     * @param int       $lp_id       Learning path id
3798
     * @param int       $session_id
3799
     *
3800
     * @return int last connection timestamp
3801
     */
3802
    public static function get_last_connection_time_in_lp(
3803
        $student_id,
3804
        $course_code,
3805
        $lp_id,
3806
        $session_id = 0
3807
    ) {
3808
        $course = api_get_course_info($course_code);
3809
3810
        if (empty($course)) {
3811
            return 0;
3812
        }
3813
3814
        $course_id = $course['real_id'];
3815
        $student_id = (int) $student_id;
3816
        $lp_id = (int) $lp_id;
3817
        $session_id = (int) $session_id;
3818
        $lastTime = 0;
3819
3820
        // Use new system
3821
        if (self::minimumTimeAvailable($session_id, $course_id)) {
3822
            $sql = "SELECT MAX(date_reg) max
3823
                    FROM track_e_access_complete
3824
                    WHERE
3825
                        user_id = $student_id AND
3826
                        c_id = $course_id AND
3827
                        session_id = $session_id AND
3828
                        tool = 'learnpath' AND
3829
                        tool_id = $lp_id AND
3830
                        action = 'view' AND
3831
                        login_as = 0
3832
                    ORDER BY date_reg ASC
3833
                    LIMIT 1";
3834
            $rs = Database::query($sql);
3835
3836
            $lastConnection = 0;
3837
            if (Database::num_rows($rs) > 0) {
3838
                $value = Database::fetch_array($rs);
3839
                if (isset($value['max']) && !empty($value['max'])) {
3840
                    $lastConnection = api_strtotime($value['max'], 'UTC');
3841
                }
3842
            }
3843
3844
            if (!empty($lastConnection)) {
3845
                return $lastConnection;
3846
            }
3847
        }
3848
3849
        if (!empty($course)) {
3850
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3851
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3852
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3853
3854
            // Check the real number of LPs corresponding to the filter in the
3855
            // database (and if no list was given, get them all)
3856
            $sql = "SELECT id FROM $lp_table
3857
                    WHERE c_id = $course_id AND id = $lp_id ";
3858
            $row = Database::query($sql);
3859
            $count = Database::num_rows($row);
3860
3861
            // calculates last connection time
3862
            if ($count > 0) {
3863
                $sql = 'SELECT MAX(start_time)
3864
                        FROM '.$t_lpiv.' AS item_view
3865
                        INNER JOIN '.$t_lpv.' AS view
3866
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3867
                        WHERE
3868
                            item_view.c_id = '.$course_id.' AND
3869
                            view.c_id = '.$course_id.' AND
3870
                            view.lp_id = '.$lp_id.' AND
3871
                            view.user_id = '.$student_id.' AND
3872
                            view.session_id = '.$session_id;
3873
                $rs = Database::query($sql);
3874
                if (Database::num_rows($rs) > 0) {
3875
                    $lastTime = Database::result($rs, 0, 0);
3876
                }
3877
            }
3878
        }
3879
3880
        return $lastTime;
3881
    }
3882
3883
    public static function getFirstConnectionTimeInLp(
3884
        $student_id,
3885
        $course_code,
3886
        $lp_id,
3887
        $session_id = 0
3888
    ) {
3889
        $course = api_get_course_info($course_code);
3890
        $student_id = (int) $student_id;
3891
        $lp_id = (int) $lp_id;
3892
        $session_id = (int) $session_id;
3893
        $time = 0;
3894
3895
        if (!empty($course)) {
3896
            $course_id = $course['real_id'];
3897
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3898
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3899
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3900
3901
            // Check the real number of LPs corresponding to the filter in the
3902
            // database (and if no list was given, get them all)
3903
            $sql = "SELECT id FROM $lp_table
3904
                    WHERE c_id = $course_id AND id = $lp_id ";
3905
            $row = Database::query($sql);
3906
            $count = Database::num_rows($row);
3907
3908
            // calculates first connection time
3909
            if ($count > 0) {
3910
                $sql = 'SELECT MIN(start_time)
3911
                        FROM '.$t_lpiv.' AS item_view
3912
                        INNER JOIN '.$t_lpv.' AS view
3913
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3914
                        WHERE
3915
                            status != "not attempted" AND
3916
                            item_view.c_id = '.$course_id.' AND
3917
                            view.c_id = '.$course_id.' AND
3918
                            view.lp_id = '.$lp_id.' AND
3919
                            view.user_id = '.$student_id.' AND
3920
                            view.session_id = '.$session_id;
3921
                $rs = Database::query($sql);
3922
                if (Database::num_rows($rs) > 0) {
3923
                    $time = Database::result($rs, 0, 0);
3924
                }
3925
            }
3926
        }
3927
3928
        return $time;
3929
    }
3930
3931
    /**
3932
     * gets the list of students followed by coach.
3933
     *
3934
     * @param int $coach_id Coach id
3935
     *
3936
     * @return array List of students
3937
     */
3938
    public static function get_student_followed_by_coach($coach_id)
3939
    {
3940
        $coach_id = intval($coach_id);
3941
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3942
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3943
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3944
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3945
3946
        $students = [];
3947
        // At first, courses where $coach_id is coach of the course //
3948
        $sql = 'SELECT session_id, c_id
3949
                FROM '.$tbl_session_course_user.'
3950
                WHERE user_id='.$coach_id.' AND status=2';
3951
3952
        if (api_is_multiple_url_enabled()) {
3953
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3954
            $access_url_id = api_get_current_access_url_id();
3955
            if (-1 != $access_url_id) {
3956
                $sql = 'SELECT scu.session_id, scu.c_id
3957
                        FROM '.$tbl_session_course_user.' scu
3958
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
3959
                        ON (scu.session_id=sru.session_id)
3960
                        WHERE
3961
                            scu.user_id='.$coach_id.' AND
3962
                            scu.status=2 AND
3963
                            sru.access_url_id = '.$access_url_id;
3964
            }
3965
        }
3966
3967
        $result = Database::query($sql);
3968
3969
        while ($a_courses = Database::fetch_array($result)) {
3970
            $courseId = $a_courses['c_id'];
3971
            $id_session = $a_courses['session_id'];
3972
3973
            $sql = "SELECT DISTINCT srcru.user_id
3974
                    FROM $tbl_session_course_user AS srcru
3975
                    INNER JOIN $tbl_session_user sru
3976
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
3977
                    WHERE
3978
                        sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
3979
                        srcru.c_id = '$courseId' AND
3980
                        srcru.session_id = '$id_session'";
3981
3982
            $rs = Database::query($sql);
3983
            while ($row = Database::fetch_array($rs)) {
3984
                $students[$row['user_id']] = $row['user_id'];
3985
            }
3986
        }
3987
3988
        // Then, courses where $coach_id is coach of the session
3989
        $sql = 'SELECT session_course_user.user_id
3990
                FROM '.$tbl_session_course_user.' as session_course_user
3991
                INNER JOIN '.$tbl_session_user.' sru
3992
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
3993
                INNER JOIN '.$tbl_session_course.' as session_course
3994
                ON session_course.c_id = session_course_user.c_id
3995
                AND session_course_user.session_id = session_course.session_id
3996
                INNER JOIN '.$tbl_session.' as session
3997
                ON session.id = session_course.session_id
3998
                AND session.id_coach = '.$coach_id;
3999
        if (api_is_multiple_url_enabled()) {
4000
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4001
            $access_url_id = api_get_current_access_url_id();
4002
            if (-1 != $access_url_id) {
4003
                $sql = 'SELECT session_course_user.user_id
4004
                        FROM '.$tbl_session_course_user.' as session_course_user
4005
                        INNER JOIN '.$tbl_session_user.' sru
4006
                        ON session_course_user.user_id = sru.user_id AND
4007
                           session_course_user.session_id = sru.session_id
4008
                        INNER JOIN '.$tbl_session_course.' as session_course
4009
                        ON session_course.c_id = session_course_user.c_id AND
4010
                        session_course_user.session_id = session_course.session_id
4011
                        INNER JOIN '.$tbl_session.' as session
4012
                        ON session.id = session_course.session_id AND
4013
                        session.id_coach = '.$coach_id.'
4014
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
4015
                        ON session.id = session_rel_url.session_id
4016
                        WHERE access_url_id = '.$access_url_id;
4017
            }
4018
        }
4019
4020
        $result = Database::query($sql);
4021
        while ($row = Database::fetch_array($result)) {
4022
            $students[$row['user_id']] = $row['user_id'];
4023
        }
4024
4025
        return $students;
4026
    }
4027
4028
    /**
4029
     * Check if a coach is allowed to follow a student.
4030
     *
4031
     * @param    int        Coach id
4032
     * @param    int        Student id
4033
     *
4034
     * @return bool
4035
     */
4036
    public static function is_allowed_to_coach_student($coach_id, $student_id)
4037
    {
4038
        $coach_id = intval($coach_id);
4039
        $student_id = intval($student_id);
4040
4041
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4042
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4043
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4044
4045
        // At first, courses where $coach_id is coach of the course
4046
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
4047
                WHERE user_id='.$coach_id.' AND status=2';
4048
        $result = Database::query($sql);
4049
        if (Database::num_rows($result) > 0) {
4050
            return true;
4051
        }
4052
4053
        // Then, courses where $coach_id is coach of the session
4054
        $sql = 'SELECT session_course_user.user_id
4055
                FROM '.$tbl_session_course_user.' as session_course_user
4056
                INNER JOIN '.$tbl_session_course.' as session_course
4057
                ON session_course.c_id = session_course_user.c_id
4058
                INNER JOIN '.$tbl_session.' as session
4059
                ON session.id = session_course.session_id
4060
                AND session.id_coach = '.$coach_id.'
4061
                WHERE user_id = '.$student_id;
4062
        $result = Database::query($sql);
4063
        if (Database::num_rows($result) > 0) {
4064
            return true;
4065
        }
4066
4067
        return false;
4068
    }
4069
4070
    /**
4071
     * Get courses followed by coach.
4072
     *
4073
     * @param     int        Coach id
4074
     * @param    int        Session id (optional)
4075
     *
4076
     * @return array Courses list
4077
     */
4078
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
4079
    {
4080
        $coach_id = intval($coach_id);
4081
        if (!empty($id_session)) {
4082
            $id_session = intval($id_session);
4083
        }
4084
4085
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4086
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4087
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4088
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4089
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4090
4091
        // At first, courses where $coach_id is coach of the course.
4092
        $sql = 'SELECT DISTINCT c.code
4093
                FROM '.$tbl_session_course_user.' sc
4094
                INNER JOIN '.$tbl_course.' c
4095
                ON (c.id = sc.c_id)
4096
                WHERE user_id = '.$coach_id.' AND status = 2';
4097
4098
        if (api_is_multiple_url_enabled()) {
4099
            $access_url_id = api_get_current_access_url_id();
4100
            if (-1 != $access_url_id) {
4101
                $sql = 'SELECT DISTINCT c.code
4102
                        FROM '.$tbl_session_course_user.' scu
4103
                        INNER JOIN '.$tbl_course.' c
4104
                        ON (c.code = scu.c_id)
4105
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
4106
                        ON (c.id = cru.c_id)
4107
                        WHERE
4108
                            scu.user_id='.$coach_id.' AND
4109
                            scu.status=2 AND
4110
                            cru.access_url_id = '.$access_url_id;
4111
            }
4112
        }
4113
4114
        if (!empty($id_session)) {
4115
            $sql .= ' AND session_id='.$id_session;
4116
        }
4117
4118
        $courseList = [];
4119
        $result = Database::query($sql);
4120
        while ($row = Database::fetch_array($result)) {
4121
            $courseList[$row['code']] = $row['code'];
4122
        }
4123
4124
        // Then, courses where $coach_id is coach of the session
4125
        $sql = 'SELECT DISTINCT course.code
4126
                FROM '.$tbl_session_course.' as session_course
4127
                INNER JOIN '.$tbl_session.' as session
4128
                    ON session.id = session_course.session_id
4129
                    AND session.id_coach = '.$coach_id.'
4130
                INNER JOIN '.$tbl_course.' as course
4131
                    ON course.id = session_course.c_id';
4132
4133
        if (api_is_multiple_url_enabled()) {
4134
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4135
            $access_url_id = api_get_current_access_url_id();
4136
            if (-1 != $access_url_id) {
4137
                $sql = 'SELECT DISTINCT c.code
4138
                    FROM '.$tbl_session_course.' as session_course
4139
                    INNER JOIN '.$tbl_course.' c
4140
                    ON (c.id = session_course.c_id)
4141
                    INNER JOIN '.$tbl_session.' as session
4142
                    ON session.id = session_course.session_id
4143
                        AND session.id_coach = '.$coach_id.'
4144
                    INNER JOIN '.$tbl_course.' as course
4145
                        ON course.id = session_course.c_id
4146
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
4147
                    ON (course_rel_url.c_id = c.id)';
4148
            }
4149
        }
4150
4151
        if (!empty($id_session)) {
4152
            $sql .= ' WHERE session_course.session_id='.$id_session;
4153
            if (api_is_multiple_url_enabled()) {
4154
                $sql .= ' AND access_url_id = '.$access_url_id;
4155
            }
4156
        } else {
4157
            if (api_is_multiple_url_enabled()) {
4158
                $sql .= ' WHERE access_url_id = '.$access_url_id;
4159
            }
4160
        }
4161
4162
        $result = Database::query($sql);
4163
        while ($row = Database::fetch_array($result)) {
4164
            $courseList[$row['code']] = $row['code'];
4165
        }
4166
4167
        return $courseList;
4168
    }
4169
4170
    /**
4171
     * Get sessions coached by user.
4172
     *
4173
     * @param int    $coach_id
4174
     * @param int    $start
4175
     * @param int    $limit
4176
     * @param bool   $getCount
4177
     * @param string $keyword
4178
     * @param string $description
4179
     * @param string $orderByName
4180
     * @param string $orderByDirection
4181
     * @param array  $options
4182
     *
4183
     * @return mixed
4184
     */
4185
    public static function get_sessions_coached_by_user(
4186
        $coach_id,
4187
        $start = 0,
4188
        $limit = 0,
4189
        $getCount = false,
4190
        $keyword = '',
4191
        $description = '',
4192
        $orderByName = '',
4193
        $orderByDirection = '',
4194
        $options = []
4195
    ) {
4196
        // table definition
4197
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4198
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4199
        $coach_id = (int) $coach_id;
4200
4201
        $select = ' SELECT * FROM ';
4202
        if ($getCount) {
4203
            $select = ' SELECT count(DISTINCT id) as count FROM ';
4204
        }
4205
4206
        $limitCondition = null;
4207
        if (!empty($start) && !empty($limit)) {
4208
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
4209
        }
4210
4211
        $keywordCondition = null;
4212
        if (!empty($keyword)) {
4213
            $keyword = Database::escape_string($keyword);
4214
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
4215
4216
            if (!empty($description)) {
4217
                $description = Database::escape_string($description);
4218
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
4219
            }
4220
        }
4221
4222
        $extraFieldModel = new ExtraFieldModel('session');
4223
        $conditions = $extraFieldModel->parseConditions($options);
4224
        $sqlInjectJoins = $conditions['inject_joins'];
4225
        $extraFieldsConditions = $conditions['where'];
4226
        $sqlInjectWhere = $conditions['inject_where'];
4227
        $injectExtraFields = $conditions['inject_extra_fields'];
4228
4229
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4230
        $access_url_id = api_get_current_access_url_id();
4231
4232
        $orderBy = '';
4233
        if (!empty($orderByName)) {
4234
            if (in_array($orderByName, ['name', 'access_start_date'])) {
4235
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
4236
                $orderByName = Database::escape_string($orderByName);
4237
                $orderBy .= " ORDER BY `$orderByName` $orderByDirection";
4238
            }
4239
        }
4240
4241
        $sql = "
4242
            $select
4243
            (
4244
                SELECT DISTINCT
4245
                    s.id,
4246
                    name,
4247
                    $injectExtraFields
4248
                    access_start_date,
4249
                    access_end_date
4250
                FROM $tbl_session s
4251
                INNER JOIN $tbl_session_rel_access_url session_rel_url
4252
                ON (s.id = session_rel_url.session_id)
4253
                $sqlInjectJoins
4254
                WHERE
4255
                    id_coach = $coach_id AND
4256
                    access_url_id = $access_url_id
4257
                    $keywordCondition
4258
                    $extraFieldsConditions
4259
                    $sqlInjectWhere
4260
            UNION
4261
                SELECT DISTINCT
4262
                    s.id,
4263
                    s.name,
4264
                    $injectExtraFields
4265
                    s.access_start_date,
4266
                    s.access_end_date
4267
                FROM $tbl_session as s
4268
                INNER JOIN $tbl_session_course_user as session_course_user
4269
                ON
4270
                    s.id = session_course_user.session_id AND
4271
                    session_course_user.user_id = $coach_id AND
4272
                    session_course_user.status = 2
4273
                INNER JOIN $tbl_session_rel_access_url session_rel_url
4274
                ON (s.id = session_rel_url.session_id)
4275
                $sqlInjectJoins
4276
                WHERE
4277
                    access_url_id = $access_url_id
4278
                    $keywordCondition
4279
                    $extraFieldsConditions
4280
                    $sqlInjectWhere
4281
            ) as sessions $limitCondition $orderBy
4282
            ";
4283
4284
        $rs = Database::query($sql);
4285
        if ($getCount) {
4286
            $row = Database::fetch_array($rs);
4287
4288
            return $row['count'];
4289
        }
4290
4291
        $sessions = [];
4292
        while ($row = Database::fetch_array($rs)) {
4293
            if ($row['access_start_date'] === '0000-00-00 00:00:00') {
4294
                $row['access_start_date'] = null;
4295
            }
4296
4297
            $sessions[$row['id']] = $row;
4298
        }
4299
4300
        if (!empty($sessions)) {
4301
            foreach ($sessions as &$session) {
4302
                if (empty($session['access_start_date'])) {
4303
                    $session['status'] = get_lang('SessionActive');
4304
                } else {
4305
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
4306
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
4307
                    if ($time_start < time() && time() < $time_end) {
4308
                        $session['status'] = get_lang('SessionActive');
4309
                    } else {
4310
                        if (time() < $time_start) {
4311
                            $session['status'] = get_lang('SessionFuture');
4312
                        } else {
4313
                            if (time() > $time_end) {
4314
                                $session['status'] = get_lang('SessionPast');
4315
                            }
4316
                        }
4317
                    }
4318
                }
4319
            }
4320
        }
4321
4322
        return $sessions;
4323
    }
4324
4325
    /**
4326
     * Get courses list from a session.
4327
     *
4328
     * @param    int        Session id
4329
     *
4330
     * @return array Courses list
4331
     */
4332
    public static function get_courses_list_from_session($session_id)
4333
    {
4334
        $session_id = (int) $session_id;
4335
4336
        // table definition
4337
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4338
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4339
4340
        $sql = "SELECT DISTINCT code, c_id
4341
                FROM $tbl_session_course sc
4342
                INNER JOIN $courseTable c
4343
                ON sc.c_id = c.id
4344
                WHERE session_id= $session_id";
4345
4346
        $result = Database::query($sql);
4347
4348
        $courses = [];
4349
        while ($row = Database::fetch_array($result)) {
4350
            $courses[$row['code']] = $row;
4351
        }
4352
4353
        return $courses;
4354
    }
4355
4356
    /**
4357
     * Count the number of documents that an user has uploaded to a course.
4358
     *
4359
     * @param    int|array   Student id(s)
4360
     * @param    string      Course code
4361
     * @param    int         Session id (optional),
4362
     * if param $session_id is null(default)
4363
     * return count of assignments including sessions, 0 = session is not filtered
4364
     *
4365
     * @return int Number of documents
4366
     */
4367
    public static function count_student_uploaded_documents(
4368
        $student_id,
4369
        $course_code,
4370
        $session_id = null
4371
    ) {
4372
        // get the information of the course
4373
        $a_course = api_get_course_info($course_code);
4374
        if (!empty($a_course)) {
4375
            // table definition
4376
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
4377
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
4378
            $course_id = $a_course['real_id'];
4379
            if (is_array($student_id)) {
4380
                $studentList = array_map('intval', $student_id);
4381
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
4382
            } else {
4383
                $student_id = (int) $student_id;
4384
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
4385
            }
4386
4387
            $condition_session = null;
4388
            if (isset($session_id)) {
4389
                $session_id = (int) $session_id;
4390
                $condition_session = " AND pub.session_id = $session_id ";
4391
            }
4392
4393
            $sql = "SELECT count(ip.tool) AS count
4394
                    FROM $tbl_item_property ip
4395
                    INNER JOIN $tbl_document pub
4396
                    ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
4397
                    WHERE
4398
                        ip.c_id  = $course_id AND
4399
                        pub.c_id  = $course_id AND
4400
                        pub.filetype ='file' AND
4401
                        ip.tool = 'document'
4402
                        $condition_user $condition_session ";
4403
            $rs = Database::query($sql);
4404
            $row = Database::fetch_array($rs, 'ASSOC');
4405
4406
            return $row['count'];
4407
        }
4408
4409
        return null;
4410
    }
4411
4412
    /**
4413
     * Count assignments per student.
4414
     *
4415
     * @param array|int $student_id
4416
     * @param string    $course_code
4417
     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
4418
     *                               0 = session is not filtered
4419
     *
4420
     * @return int Count of assignments
4421
     */
4422
    public static function count_student_assignments(
4423
        $student_id,
4424
        $course_code = null,
4425
        $session_id = null
4426
    ) {
4427
        if (empty($student_id)) {
4428
            return 0;
4429
        }
4430
4431
        $conditions = [];
4432
4433
        // Get the information of the course
4434
        $a_course = api_get_course_info($course_code);
4435
        if (!empty($a_course)) {
4436
            $course_id = $a_course['real_id'];
4437
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
4438
        }
4439
4440
        // table definition
4441
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
4442
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
4443
4444
        if (is_array($student_id)) {
4445
            $studentList = array_map('intval', $student_id);
4446
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
4447
        } else {
4448
            $student_id = (int) $student_id;
4449
            $conditions[] = " ip.insert_user_id = '$student_id' ";
4450
        }
4451
4452
        $conditions[] = ' pub.active <> 2 ';
4453
        $conditionToString = implode(' AND ', $conditions);
4454
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
4455
        $conditionToString .= $sessionCondition;
4456
4457
        $sql = "SELECT count(ip.tool) as count
4458
                FROM $tbl_item_property ip
4459
                INNER JOIN $tbl_student_publication pub
4460
                ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
4461
                WHERE
4462
                    ip.tool='work' AND
4463
                    $conditionToString";
4464
        $rs = Database::query($sql);
4465
        $row = Database::fetch_array($rs, 'ASSOC');
4466
4467
        return $row['count'];
4468
    }
4469
4470
    /**
4471
     * Count messages per student inside forum tool.
4472
     *
4473
     * @param int|array  Student id
4474
     * @param string     Course code
4475
     * @param int        Session id if null(default) return count of messages including sessions, 0 = session is not
4476
     *                           filtered
4477
     *
4478
     * @return int Count of messages
4479
     */
4480
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
4481
    {
4482
        if (empty($student_id)) {
4483
            return 0;
4484
        }
4485
4486
        // Table definition.
4487
        $tbl_forum_post = Database::get_course_table(TABLE_FORUM_POST);
4488
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
4489
4490
        $conditions = [];
4491
        if (is_array($student_id)) {
4492
            $studentList = array_map('intval', $student_id);
4493
            $conditions[] = " post.poster_id IN ('".implode("','", $studentList)."') ";
4494
        } else {
4495
            $student_id = (int) $student_id;
4496
            $conditions[] = " post.poster_id = '$student_id' ";
4497
        }
4498
4499
        $conditionsToString = implode('AND ', $conditions);
4500
4501
        if (empty($courseCode)) {
4502
            $sql = "SELECT count(poster_id) as count
4503
                    FROM $tbl_forum_post post
4504
                    INNER JOIN $tbl_forum forum
4505
                    ON (forum.forum_id = post.forum_id AND forum.c_id = post.c_id)
4506
                    WHERE $conditionsToString";
4507
4508
            $rs = Database::query($sql);
4509
            $row = Database::fetch_array($rs, 'ASSOC');
4510
4511
            return $row['count'];
4512
        }
4513
4514
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
4515
4516
        $courseInfo = api_get_course_info($courseCode);
4517
4518
        $forums = [];
4519
        if (!empty($courseInfo)) {
4520
            $forums = get_forums('', $courseCode, true, $session_id);
4521
            $course_id = $courseInfo['real_id'];
4522
            $conditions[] = " post.c_id  = $course_id ";
4523
        }
4524
4525
        if (!empty($forums)) {
4526
            $idList = array_column($forums, 'forum_id');
4527
            $idListToString = implode("', '", $idList);
4528
            $conditions[] = " post.forum_id  IN ('$idListToString')";
4529
        }
4530
4531
        $conditionsToString = implode('AND ', $conditions);
4532
        $sql = "SELECT count(poster_id) as count
4533
                FROM $tbl_forum_post post
4534
                WHERE $conditionsToString";
4535
4536
        $rs = Database::query($sql);
4537
        $row = Database::fetch_array($rs, 'ASSOC');
4538
        $count = $row['count'];
4539
4540
        return $count;
4541
    }
4542
4543
    /**
4544
     * This function counts the number of post by course.
4545
     *
4546
     * @param string $course_code
4547
     * @param int    $session_id  (optional), if is null(default) it'll return results including sessions,
4548
     *                            0 = session is not filtered
4549
     * @param int    $groupId
4550
     *
4551
     * @return int The number of post by course
4552
     */
4553
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
4554
    {
4555
        $courseInfo = api_get_course_info($course_code);
4556
        if (!empty($courseInfo)) {
4557
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
4558
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
4559
4560
            $condition_session = '';
4561
            if (isset($session_id)) {
4562
                $session_id = (int) $session_id;
4563
                $condition_session = api_get_session_condition(
4564
                    $session_id,
4565
                    true,
4566
                    false,
4567
                    'f.session_id'
4568
                );
4569
            }
4570
4571
            $course_id = $courseInfo['real_id'];
4572
            $groupId = (int) $groupId;
4573
            if (!empty($groupId)) {
4574
                $groupCondition = " i.to_group_id = $groupId ";
4575
            } else {
4576
                $groupCondition = ' (i.to_group_id = 0 OR i.to_group_id IS NULL) ';
4577
            }
4578
4579
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4580
            $sql = "SELECT count(*) FROM $tbl_posts p
4581
                    INNER JOIN $tbl_forums f
4582
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
4583
                    INNER JOIN $item i
4584
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
4585
                    WHERE
4586
                        p.c_id = $course_id AND
4587
                        f.c_id = $course_id AND
4588
                        $groupCondition
4589
                        $condition_session
4590
                    ";
4591
            $result = Database::query($sql);
4592
            $row = Database::fetch_row($result);
4593
            $count = $row[0];
4594
4595
            return $count;
4596
        }
4597
4598
        return 0;
4599
    }
4600
4601
    /**
4602
     * This function counts the number of threads by course.
4603
     *
4604
     * @param      string     Course code
4605
     * @param    int        Session id (optional),
4606
     * if param $session_id is null(default) it'll return results including
4607
     * sessions, 0 = session is not filtered
4608
     * @param int $groupId
4609
     *
4610
     * @return int The number of threads by course
4611
     */
4612
    public static function count_number_of_threads_by_course(
4613
        $course_code,
4614
        $session_id = null,
4615
        $groupId = 0
4616
    ) {
4617
        $course_info = api_get_course_info($course_code);
4618
        if (empty($course_info)) {
4619
            return null;
4620
        }
4621
4622
        $course_id = $course_info['real_id'];
4623
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
4624
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4625
4626
        $condition_session = '';
4627
        if (isset($session_id)) {
4628
            $session_id = (int) $session_id;
4629
            $condition_session = ' AND f.session_id = '.$session_id;
4630
        }
4631
4632
        $groupId = (int) $groupId;
4633
4634
        if (!empty($groupId)) {
4635
            $groupCondition = " i.to_group_id = $groupId ";
4636
        } else {
4637
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4638
        }
4639
4640
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4641
        $sql = "SELECT count(*)
4642
                FROM $tbl_threads t
4643
                INNER JOIN $tbl_forums f
4644
                ON f.iid = t.forum_id AND f.c_id = t.c_id
4645
                INNER JOIN $item i
4646
                ON (
4647
                    tool = '".TOOL_FORUM_THREAD."' AND
4648
                    f.c_id = i.c_id AND
4649
                    t.iid = i.ref
4650
                )
4651
                WHERE
4652
                    t.c_id = $course_id AND
4653
                    f.c_id = $course_id AND
4654
                    $groupCondition
4655
                    $condition_session
4656
                ";
4657
4658
        $result = Database::query($sql);
4659
        if (Database::num_rows($result)) {
4660
            $row = Database::fetch_row($result);
4661
            $count = $row[0];
4662
4663
            return $count;
4664
        }
4665
4666
        return 0;
4667
    }
4668
4669
    /**
4670
     * This function counts the number of forums by course.
4671
     *
4672
     * @param      string     Course code
4673
     * @param    int        Session id (optional),
4674
     * if param $session_id is null(default) it'll return results
4675
     * including sessions, 0 = session is not filtered
4676
     * @param int $groupId
4677
     *
4678
     * @return int The number of forums by course
4679
     */
4680
    public static function count_number_of_forums_by_course(
4681
        $course_code,
4682
        $session_id = null,
4683
        $groupId = 0
4684
    ) {
4685
        $course_info = api_get_course_info($course_code);
4686
        if (empty($course_info)) {
4687
            return null;
4688
        }
4689
        $course_id = $course_info['real_id'];
4690
4691
        $condition_session = '';
4692
        if (isset($session_id)) {
4693
            $session_id = (int) $session_id;
4694
            $condition_session = ' AND f.session_id = '.$session_id;
4695
        }
4696
4697
        $groupId = (int) $groupId;
4698
        if (!empty($groupId)) {
4699
            $groupCondition = " i.to_group_id = $groupId ";
4700
        } else {
4701
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4702
        }
4703
4704
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4705
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4706
4707
        $sql = "SELECT count(*)
4708
                FROM $tbl_forums f
4709
                INNER JOIN $item i
4710
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
4711
                WHERE
4712
                    f.c_id = $course_id AND
4713
                    $groupCondition
4714
                    $condition_session
4715
                ";
4716
        $result = Database::query($sql);
4717
        if (Database::num_rows($result)) {
4718
            $row = Database::fetch_row($result);
4719
            $count = $row[0];
4720
4721
            return $count;
4722
        }
4723
4724
        return 0;
4725
    }
4726
4727
    /**
4728
     * This function counts the chat last connections by course in x days.
4729
     *
4730
     * @param      string     Course code
4731
     * @param      int     Last x days
4732
     * @param    int        Session id (optional)
4733
     *
4734
     * @return int Chat last connections by course in x days
4735
     */
4736
    public static function chat_connections_during_last_x_days_by_course(
4737
        $course_code,
4738
        $last_days,
4739
        $session_id = 0
4740
    ) {
4741
        $course_info = api_get_course_info($course_code);
4742
        if (empty($course_info)) {
4743
            return null;
4744
        }
4745
        $course_id = $course_info['real_id'];
4746
4747
        // Protect data
4748
        $last_days = (int) $last_days;
4749
        $session_id = (int) $session_id;
4750
4751
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4752
        $now = api_get_utc_datetime();
4753
4754
        $sql = "SELECT count(*) FROM $tbl_stats_access
4755
                WHERE
4756
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
4757
                    c_id = '$course_id' AND
4758
                    access_tool='".TOOL_CHAT."' AND
4759
                    access_session_id = '$session_id' ";
4760
        $result = Database::query($sql);
4761
        if (Database::num_rows($result)) {
4762
            $row = Database::fetch_row($result);
4763
            $count = $row[0];
4764
4765
            return $count;
4766
        }
4767
4768
        return 0;
4769
    }
4770
4771
    /**
4772
     * This function gets the last student's connection in chat.
4773
     *
4774
     * @param      int     Student id
4775
     * @param      string     Course code
4776
     * @param    int        Session id (optional)
4777
     *
4778
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
4779
     */
4780
    public static function chat_last_connection(
4781
        $student_id,
4782
        $courseId,
4783
        $session_id = 0
4784
    ) {
4785
        $student_id = (int) $student_id;
4786
        $courseId = (int) $courseId;
4787
        $session_id = (int) $session_id;
4788
        $date_time = '';
4789
4790
        // table definition
4791
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4792
        $sql = "SELECT access_date
4793
                FROM $tbl_stats_access
4794
                WHERE
4795
                     access_tool='".TOOL_CHAT."' AND
4796
                     access_user_id='$student_id' AND
4797
                     c_id = $courseId AND
4798
                     access_session_id = '$session_id'
4799
                ORDER BY access_date DESC limit 1";
4800
        $rs = Database::query($sql);
4801
        if (Database::num_rows($rs) > 0) {
4802
            $row = Database::fetch_array($rs);
4803
            $date_time = api_convert_and_format_date(
4804
                $row['access_date'],
4805
                null,
4806
                date_default_timezone_get()
4807
            );
4808
        }
4809
4810
        return $date_time;
4811
    }
4812
4813
    /**
4814
     * Get count student's visited links.
4815
     *
4816
     * @param int $student_id Student id
4817
     * @param int $courseId
4818
     * @param int $session_id Session id (optional)
4819
     *
4820
     * @return int count of visited links
4821
     */
4822
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
4823
    {
4824
        $student_id = (int) $student_id;
4825
        $courseId = (int) $courseId;
4826
        $session_id = (int) $session_id;
4827
4828
        // table definition
4829
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4830
4831
        $sql = 'SELECT 1
4832
                FROM '.$table.'
4833
                WHERE
4834
                    links_user_id= '.$student_id.' AND
4835
                    c_id = "'.$courseId.'" AND
4836
                    links_session_id = '.$session_id.' ';
4837
4838
        $rs = Database::query($sql);
4839
4840
        return Database::num_rows($rs);
4841
    }
4842
4843
    /**
4844
     * Get count student downloaded documents.
4845
     *
4846
     * @param    int        Student id
4847
     * @param int $courseId
4848
     * @param    int        Session id (optional)
4849
     *
4850
     * @return int Count downloaded documents
4851
     */
4852
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
4853
    {
4854
        $student_id = (int) $student_id;
4855
        $courseId = (int) $courseId;
4856
        $session_id = (int) $session_id;
4857
4858
        // table definition
4859
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4860
4861
        $sql = 'SELECT 1
4862
                FROM '.$table.'
4863
                WHERE down_user_id = '.$student_id.'
4864
                AND c_id  = "'.$courseId.'"
4865
                AND down_session_id = '.$session_id.' ';
4866
        $rs = Database::query($sql);
4867
4868
        return Database::num_rows($rs);
4869
    }
4870
4871
    /**
4872
     * Get course list inside a session from a student.
4873
     *
4874
     * @param int $user_id    Student id
4875
     * @param int $id_session Session id (optional)
4876
     *
4877
     * @return array Courses list
4878
     */
4879
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
4880
    {
4881
        $user_id = intval($user_id);
4882
        $id_session = intval($id_session);
4883
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4884
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4885
4886
        $sql = "SELECT c.code
4887
                FROM $tbl_session_course_user sc
4888
                INNER JOIN $courseTable c
4889
                WHERE
4890
                    user_id= $user_id  AND
4891
                    session_id = $id_session";
4892
        $result = Database::query($sql);
4893
        $courses = [];
4894
        while ($row = Database::fetch_array($result)) {
4895
            $courses[$row['code']] = $row['code'];
4896
        }
4897
4898
        return $courses;
4899
    }
4900
4901
    /**
4902
     * Get inactive students in course.
4903
     *
4904
     * @param int        $courseId
4905
     * @param string|int $since      Since login course date (optional, default = 'never')
4906
     * @param int        $session_id (optional)
4907
     *
4908
     * @return array Inactive users
4909
     */
4910
    public static function getInactiveStudentsInCourse(
4911
        $courseId,
4912
        $since = 'never',
4913
        $session_id = 0
4914
    ) {
4915
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4916
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4917
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4918
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4919
        $now = api_get_utc_datetime();
4920
        $courseId = (int) $courseId;
4921
        $session_id = (int) $session_id;
4922
4923
        if (empty($courseId)) {
4924
            return false;
4925
        }
4926
4927
        if ($since === 'never') {
4928
            if (empty($session_id)) {
4929
                $sql = 'SELECT course_user.user_id
4930
                        FROM '.$table_course_rel_user.' course_user
4931
                        LEFT JOIN '.$tbl_track_login.' stats_login
4932
                        ON course_user.user_id = stats_login.user_id AND
4933
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
4934
                        INNER JOIN '.$tableCourse.' c
4935
                        ON (c.id = course_user.c_id)
4936
                        WHERE
4937
                            course_user.c_id = '.$courseId.' AND
4938
                            stats_login.login_course_date IS NULL
4939
                        GROUP BY course_user.user_id';
4940
            } else {
4941
                $sql = 'SELECT session_course_user.user_id
4942
                        FROM '.$tbl_session_course_user.' session_course_user
4943
                        LEFT JOIN '.$tbl_track_login.' stats_login
4944
                        ON session_course_user.user_id = stats_login.user_id
4945
                        INNER JOIN '.$tableCourse.' c
4946
                        ON (c.id = session_course_user.c_id)
4947
                        WHERE
4948
                            session_course_user.c_id = '.$courseId.' AND
4949
                            stats_login.login_course_date IS NULL
4950
                        GROUP BY session_course_user.user_id';
4951
            }
4952
        } else {
4953
            $since = (int) $since;
4954
            if (empty($session_id)) {
4955
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
4956
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
4957
            } else {
4958
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
4959
                          ON
4960
                            c.id = session_course_user.c_id AND
4961
                            session_course_user.session_id = '.$session_id.' AND
4962
                            session_course_user.user_id = stats_login.user_id ';
4963
            }
4964
4965
            $sql = 'SELECT
4966
                    stats_login.user_id,
4967
                    MAX(login_course_date) max_date
4968
                FROM '.$tbl_track_login.' stats_login
4969
                INNER JOIN '.$tableCourse.' c
4970
                ON (c.id = stats_login.c_id)
4971
                '.$inner.'
4972
                WHERE c.id = '.$courseId.'
4973
                GROUP BY stats_login.user_id
4974
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
4975
        }
4976
4977
        $rs = Database::query($sql);
4978
4979
        $allow = api_get_plugin_setting('pausetraining', 'tool_enable') === 'true';
4980
        $allowPauseFormation = api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') === 'true';
4981
4982
        $extraFieldValue = new ExtraFieldValue('user');
4983
        $users = [];
4984
        while ($user = Database::fetch_array($rs)) {
4985
            $userId = $user['user_id'];
4986
4987
            if ($allow && $allowPauseFormation) {
4988
                $pause = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'pause_formation');
4989
                if (!empty($pause) && isset($pause['value']) && 1 == $pause['value']) {
4990
                    // Skip user because he paused his formation.
4991
                    continue;
4992
                }
4993
            }
4994
4995
            $users[] = $userId;
4996
        }
4997
4998
        return $users;
4999
    }
5000
5001
    /**
5002
     * get count clicks about tools most used by course.
5003
     *
5004
     * @param int $courseId
5005
     * @param    int        Session id (optional),
5006
     * if param $session_id is null(default) it'll return results
5007
     * including sessions, 0 = session is not filtered
5008
     *
5009
     * @return array tools data
5010
     */
5011
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
5012
    {
5013
        $courseId = (int) $courseId;
5014
        $data = [];
5015
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
5016
        $condition_session = '';
5017
        if (isset($session_id)) {
5018
            $session_id = (int) $session_id;
5019
            $condition_session = ' AND access_session_id = '.$session_id;
5020
        }
5021
        $sql = "SELECT
5022
                    access_tool,
5023
                    COUNT(DISTINCT access_user_id),
5024
                    count(access_tool) as count_access_tool
5025
                FROM $TABLETRACK_ACCESS
5026
                WHERE
5027
                    access_tool IS NOT NULL AND
5028
                    access_tool != '' AND
5029
                    c_id = '$courseId'
5030
                    $condition_session
5031
                GROUP BY access_tool
5032
                ORDER BY count_access_tool DESC
5033
                LIMIT 0, 3";
5034
        $rs = Database::query($sql);
5035
        if (Database::num_rows($rs) > 0) {
5036
            while ($row = Database::fetch_array($rs)) {
5037
                $data[] = $row;
5038
            }
5039
        }
5040
5041
        return $data;
5042
    }
5043
5044
    /**
5045
     * get documents most downloaded by course.
5046
     *
5047
     * @param      string     Course code
5048
     * @param    int        Session id (optional),
5049
     * if param $session_id is null(default) it'll return results including
5050
     * sessions, 0 = session is not filtered
5051
     * @param    int        Limit (optional, default = 0, 0 = without limit)
5052
     *
5053
     * @return array documents downloaded
5054
     */
5055
    public static function get_documents_most_downloaded_by_course(
5056
        $course_code,
5057
        $session_id = 0,
5058
        $limit = 0
5059
    ) {
5060
        $courseId = api_get_course_int_id($course_code);
5061
        $data = [];
5062
5063
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
5064
        $condition_session = '';
5065
        $session_id = intval($session_id);
5066
        if (!empty($session_id)) {
5067
            $condition_session = ' AND down_session_id = '.$session_id;
5068
        }
5069
        $sql = "SELECT
5070
                    down_doc_path,
5071
                    COUNT(DISTINCT down_user_id),
5072
                    COUNT(down_doc_path) as count_down
5073
                FROM $TABLETRACK_DOWNLOADS
5074
                WHERE c_id = $courseId
5075
                    $condition_session
5076
                GROUP BY down_doc_path
5077
                ORDER BY count_down DESC
5078
                LIMIT 0,  $limit";
5079
        $rs = Database::query($sql);
5080
5081
        if (Database::num_rows($rs) > 0) {
5082
            while ($row = Database::fetch_array($rs)) {
5083
                $data[] = $row;
5084
            }
5085
        }
5086
5087
        return $data;
5088
    }
5089
5090
    /**
5091
     * get links most visited by course.
5092
     *
5093
     * @param      string     Course code
5094
     * @param    int        Session id (optional),
5095
     * if param $session_id is null(default) it'll
5096
     * return results including sessions, 0 = session is not filtered
5097
     *
5098
     * @return array links most visited
5099
     */
5100
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
5101
    {
5102
        $course_code = Database::escape_string($course_code);
5103
        $course_info = api_get_course_info($course_code);
5104
        $course_id = $course_info['real_id'];
5105
        $data = [];
5106
5107
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
5108
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
5109
5110
        $condition_session = '';
5111
        if (isset($session_id)) {
5112
            $session_id = intval($session_id);
5113
            $condition_session = ' AND cl.session_id = '.$session_id;
5114
        }
5115
5116
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
5117
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
5118
                WHERE
5119
                    cl.c_id = $course_id AND
5120
                    sl.links_link_id = cl.id AND
5121
                    sl.c_id = $course_id
5122
                    $condition_session
5123
                GROUP BY cl.title, cl.url
5124
                ORDER BY count_visits DESC
5125
                LIMIT 0, 3";
5126
        $rs = Database::query($sql);
5127
        if (Database::num_rows($rs) > 0) {
5128
            while ($row = Database::fetch_array($rs)) {
5129
                $data[] = $row;
5130
            }
5131
        }
5132
5133
        return $data;
5134
    }
5135
5136
    /**
5137
     * Shows the user progress (when clicking in the Progress tab).
5138
     *
5139
     * @param int    $user_id
5140
     * @param int    $session_id
5141
     * @param string $extra_params
5142
     * @param bool   $show_courses
5143
     * @param bool   $showAllSessions
5144
     * @param bool   $returnArray
5145
     *
5146
     * @return string|array
5147
     */
5148
    public static function showUserProgress(
5149
        $user_id,
5150
        $session_id = 0,
5151
        $extra_params = '',
5152
        $show_courses = true,
5153
        $showAllSessions = true,
5154
        $returnArray = false,
5155
        $showGraph = true
5156
    ) {
5157
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
5158
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5159
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5160
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5161
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5162
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5163
5164
        $trackingColumns = [
5165
            'course_session' => [
5166
                'course_title' => true,
5167
                'published_exercises' => true,
5168
                'new_exercises' => true,
5169
                'my_average' => true,
5170
                'average_exercise_result' => true,
5171
                'time_spent' => true,
5172
                'lp_progress' => true,
5173
                'score' => true,
5174
                'best_score' => true,
5175
                'last_connection' => true,
5176
                'details' => true,
5177
            ],
5178
        ];
5179
5180
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
5181
        if (!empty($trackingColumnsConfig)) {
5182
            $trackingColumns = $trackingColumnsConfig;
5183
        }
5184
5185
        $user_id = (int) $user_id;
5186
        $session_id = (int) $session_id;
5187
        $urlId = api_get_current_access_url_id();
5188
5189
        if (api_is_multiple_url_enabled()) {
5190
            $sql = "SELECT c.id, c.code, title
5191
                    FROM $tbl_course_user cu
5192
                    INNER JOIN $tbl_course c
5193
                    ON (cu.c_id = c.id)
5194
                    INNER JOIN $tbl_access_rel_course a
5195
                    ON (a.c_id = c.id)
5196
                    WHERE
5197
                        cu.user_id = $user_id AND
5198
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
5199
                        access_url_id = $urlId
5200
                    ORDER BY title";
5201
        } else {
5202
            $sql = "SELECT c.id, c.code, title
5203
                    FROM $tbl_course_user u
5204
                    INNER JOIN $tbl_course c ON (c_id = c.id)
5205
                    WHERE
5206
                        u.user_id= $user_id AND
5207
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
5208
                    ORDER BY title";
5209
        }
5210
5211
        $rs = Database::query($sql);
5212
        $courses = $course_in_session = $temp_course_in_session = [];
5213
        $courseIdList = [];
5214
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
5215
            $courses[$row['code']] = $row['title'];
5216
            $courseIdList[] = $row['id'];
5217
        }
5218
5219
        $orderBy = ' ORDER BY display_end_date DESC, name ';
5220
        $extraInnerJoin = null;
5221
5222
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
5223
            $orderBy = ' ORDER BY s.id, src.position ';
5224
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
5225
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
5226
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
5227
        }
5228
5229
        $sessionCondition = '';
5230
        if (!empty($session_id)) {
5231
            $sessionCondition = " AND s.id = $session_id";
5232
        }
5233
5234
        // Get the list of sessions where the user is subscribed as student
5235
        if (api_is_multiple_url_enabled()) {
5236
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
5237
                    FROM $tbl_session_course_user cu
5238
                    INNER JOIN $tbl_access_rel_session a
5239
                    ON (a.session_id = cu.session_id)
5240
                    INNER JOIN $tbl_session s
5241
                    ON (s.id = a.session_id)
5242
                    INNER JOIN $tbl_course c
5243
                    ON (c.id = cu.c_id)
5244
                    $extraInnerJoin
5245
                    WHERE
5246
                        cu.user_id = $user_id AND
5247
                        access_url_id = ".$urlId."
5248
                        $sessionCondition
5249
                    $orderBy ";
5250
        } else {
5251
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
5252
                    FROM $tbl_session_course_user cu
5253
                    INNER JOIN $tbl_session s
5254
                    ON (s.id = cu.session_id)
5255
                    INNER JOIN $tbl_course c
5256
                    ON (c.id = cu.c_id)
5257
                    $extraInnerJoin
5258
                    WHERE
5259
                        cu.user_id = $user_id
5260
                        $sessionCondition
5261
                    $orderBy ";
5262
        }
5263
5264
        $rs = Database::query($sql);
5265
        $simple_session_array = [];
5266
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
5267
            $course_info = api_get_course_info($row['code']);
5268
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
5269
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
5270
            $simple_session_array[$row['session_id']] = $row['name'];
5271
        }
5272
5273
        foreach ($simple_session_array as $my_session_id => $session_name) {
5274
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
5275
            $my_course_data = [];
5276
            foreach ($course_list as $courseId => $course_data) {
5277
                $my_course_data[$courseId] = $course_data['title'];
5278
            }
5279
5280
            if (empty($session_id)) {
5281
                $my_course_data = utf8_sort($my_course_data);
5282
            }
5283
5284
            $final_course_data = [];
5285
            foreach ($my_course_data as $course_id => $value) {
5286
                if (isset($course_list[$course_id])) {
5287
                    $final_course_data[$course_id] = $course_list[$course_id];
5288
                }
5289
            }
5290
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
5291
            $course_in_session[$my_session_id]['name'] = $session_name;
5292
        }
5293
5294
        if ($returnArray) {
5295
            $course_in_session[0] = $courseIdList;
5296
5297
            return $course_in_session;
5298
        }
5299
5300
        $html = '';
5301
        // Course list.
5302
        if ($show_courses) {
5303
            if (!empty($courses)) {
5304
                $html .= Display::page_header(
5305
                    Display::return_icon('course.png', get_lang('MyCourses')).PHP_EOL.get_lang('MyCourses')
5306
                );
5307
5308
                $columns = [
5309
                    'course_title' => get_lang('Course'),
5310
                    'time_spent' => get_lang('TimeSpentInTheCourse'),
5311
                    'progress' => get_lang('Progress'),
5312
                    'best_score_in_lp' => get_lang('BestScoreInLearningPath'),
5313
                    'best_score_not_in_lp' => get_lang('BestScoreNotInLearningPath'),
5314
                    'latest_login' => get_lang('LastConnexion'),
5315
                    'details' => get_lang('Details'),
5316
                ];
5317
                $availableColumns = [];
5318
                if (isset($trackingColumns['my_progress_courses'])) {
5319
                    $availableColumns = $trackingColumns['my_progress_courses'];
5320
                }
5321
5322
                $columns = array_filter(
5323
                    $columns,
5324
                    function ($column, $columnKey) use ($availableColumns) {
5325
                        if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
5326
                            return false;
5327
                        }
5328
5329
                        return true;
5330
                    },
5331
                    ARRAY_FILTER_USE_BOTH
5332
                );
5333
5334
                $coursesTable = new SortableTableFromArray([], 0, 0, 'courses');
5335
                $coursesTable->setHeaders($columns);
5336
5337
                foreach ($courses as $course_code => $course_title) {
5338
                    $courseInfo = api_get_course_info($course_code);
5339
                    $courseId = $courseInfo['real_id'];
5340
5341
                    $total_time_login = self::get_time_spent_on_the_course(
5342
                        $user_id,
5343
                        $courseId
5344
                    );
5345
                    $time = api_time_to_hms($total_time_login);
5346
                    $progress = self::get_avg_student_progress(
5347
                        $user_id,
5348
                        $course_code
5349
                    );
5350
                    $bestScore = self::get_avg_student_score(
5351
                        $user_id,
5352
                        $course_code,
5353
                        [],
5354
                        null,
5355
                        false,
5356
                        false,
5357
                        true
5358
                    );
5359
5360
                    $exerciseList = ExerciseLib::get_all_exercises(
5361
                        $courseInfo,
5362
                        0,
5363
                        false,
5364
                        null,
5365
                        false,
5366
                        1
5367
                    );
5368
5369
                    $bestScoreAverageNotInLP = 0;
5370
                    if (!empty($exerciseList)) {
5371
                        foreach ($exerciseList as $exerciseData) {
5372
                            $results = Event::get_best_exercise_results_by_user(
5373
                                $exerciseData['iid'],
5374
                                $courseInfo['real_id'],
5375
                                0,
5376
                                $user_id
5377
                            );
5378
                            $best = 0;
5379
                            if (!empty($results)) {
5380
                                foreach ($results as $result) {
5381
                                    if (!empty($result['exe_weighting'])) {
5382
                                        $score = $result['exe_result'] / $result['exe_weighting'];
5383
                                        if ($score > $best) {
5384
                                            $best = $score;
5385
                                        }
5386
                                    }
5387
                                }
5388
                            }
5389
                            $bestScoreAverageNotInLP += $best;
5390
                        }
5391
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
5392
                    }
5393
5394
                    $last_connection = self::get_last_connection_date_on_the_course($user_id, $courseInfo);
5395
5396
                    if (is_null($progress) || empty($progress)) {
5397
                        $progress = '0%';
5398
                    } else {
5399
                        $progress = $progress.'%';
5400
                    }
5401
5402
                    $filterByCourse = isset($_GET['course']) && $course_code == $_GET['course'] &&
5403
                        empty($_GET['session_id']);
5404
5405
                    $url = api_get_course_url($course_code, $session_id);
5406
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
5407
                    $bestScoreResult = empty($bestScore) ? '-' : sprintf(get_lang('XPercent'), $bestScore);
5408
                    $bestScoreNotInLP = empty($bestScoreAverageNotInLP)
5409
                        ? '-'
5410
                        : sprintf(get_lang('XPercent'), $bestScoreAverageNotInLP);
5411
5412
                    $detailsLink = '';
5413
                    if ($filterByCourse) {
5414
                        $detailsLink .= '<a href="#course_session_data">';
5415
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
5416
                        $detailsLink .= '</a>';
5417
                    } else {
5418
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_data">';
5419
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
5420
                        $detailsLink .= '</a>';
5421
                    }
5422
5423
                    $result = array_filter(
5424
                        [
5425
                            'course_title' => $course_url,
5426
                            'time_spent' => $time,
5427
                            'progress' => $progress,
5428
                            'best_score_in_lp' => $bestScoreResult,
5429
                            'best_score_not_in_lp' => $bestScoreNotInLP,
5430
                            'latest_login' => $last_connection,
5431
                            'details' => $detailsLink,
5432
                        ],
5433
                        function ($data, $columnKey) {
5434
                            if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
5435
                                return false;
5436
                            }
5437
5438
                            return true;
5439
                        },
5440
                        ARRAY_FILTER_USE_BOTH
5441
                    );
5442
5443
                    $coursesTable->addRow(
5444
                        array_values($result),
5445
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
5446
                    );
5447
                }
5448
5449
                $html .= Display::div($coursesTable->toHtml(), ['class' => 'table-responsive']);
5450
            }
5451
        }
5452
5453
        $allowCareerUser = api_get_configuration_value('allow_career_users');
5454
5455
        // Session list.
5456
        $visibleSessions = [];
5457
        if (!empty($course_in_session)) {
5458
            $main_session_graph = '';
5459
            // Load graphics only when calling to an specific session
5460
            $all_exercise_graph_name_list = [];
5461
            $my_results = [];
5462
            $all_exercise_graph_list = [];
5463
            $all_exercise_start_time = [];
5464
            foreach ($course_in_session as $my_session_id => $session_data) {
5465
                $course_list = $session_data['course_list'];
5466
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
5467
                $exercise_graph_name_list = [];
5468
                $exercise_graph_list = [];
5469
5470
                $visibility = api_get_session_visibility($my_session_id, null, false, $user_id);
5471
5472
                if (SESSION_AVAILABLE === $visibility) {
5473
                    $visibleSessions[] = $my_session_id;
5474
                }
5475
5476
                foreach ($course_list as $course_data) {
5477
                    $exercise_list = ExerciseLib::get_all_exercises(
5478
                        $course_data,
5479
                        $my_session_id,
5480
                        false,
5481
                        null,
5482
                        false,
5483
                        1
5484
                    );
5485
5486
                    foreach ($exercise_list as $exercise_data) {
5487
                        $exercise_obj = new Exercise($course_data['real_id']);
5488
                        $exercise_obj->read($exercise_data['iid']);
5489
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
5490
                        //$visible_return = $exercise_obj->is_visible();
5491
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
5492
                            $best_average = (int)
5493
                                ExerciseLib::get_best_average_score_by_exercise(
5494
                                    $exercise_data['iid'],
5495
                                    $course_data['real_id'],
5496
                                    $my_session_id,
5497
                                    $user_count
5498
                                )
5499
                            ;
5500
5501
                            $exercise_graph_list[] = $best_average;
5502
                            $all_exercise_graph_list[] = $best_average;
5503
5504
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
5505
                                api_get_user_id(),
5506
                                $exercise_data['iid'],
5507
                                $course_data['real_id'],
5508
                                $my_session_id
5509
                            );
5510
5511
                            $score = 0;
5512
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
5513
                                $score = intval($user_result_data['exe_result'] / $user_result_data['exe_weighting'] * 100);
5514
                            }
5515
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
5516
                            $all_exercise_start_time[] = $time;
5517
                            $my_results[] = $score;
5518
                            if (count($exercise_list) <= 10) {
5519
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
5520
                                $exercise_graph_name_list[] = $title;
5521
                                $all_exercise_graph_name_list[] = $title;
5522
                            } else {
5523
                                // if there are more than 10 results, space becomes difficult to find,
5524
                                // so only show the title of the exercise, not the tool
5525
                                $title = cut($exercise_data['title'], 30);
5526
                                $exercise_graph_name_list[] = $title;
5527
                                $all_exercise_graph_name_list[] = $title;
5528
                            }
5529
                        }
5530
                    }
5531
                }
5532
            }
5533
5534
            // Complete graph
5535
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
5536
                asort($all_exercise_start_time);
5537
5538
                //Fix exams order
5539
                $final_all_exercise_graph_name_list = [];
5540
                $my_results_final = [];
5541
                $final_all_exercise_graph_list = [];
5542
5543
                foreach ($all_exercise_start_time as $key => $time) {
5544
                    $label_time = '';
5545
                    if (!empty($time)) {
5546
                        $label_time = date('d-m-y', $time);
5547
                    }
5548
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
5549
                    $my_results_final[] = $my_results[$key];
5550
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
5551
                }
5552
                if ($showGraph) {
5553
                    $main_session_graph = '<div class="row"><div class="col-md-10 col-md-offset-1">'
5554
                        .self::generate_session_exercise_graph(
5555
                            $final_all_exercise_graph_name_list,
5556
                            $my_results_final,
5557
                            $final_all_exercise_graph_list
5558
                        )
5559
                        .'</div></div>';
5560
                }
5561
            }
5562
5563
            $sessionIcon = Display::return_icon('session.png', get_lang('Sessions'));
5564
5565
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5566
            $html .= $anchor.Display::page_header(
5567
                $sessionIcon.PHP_EOL.get_lang('Sessions')
5568
            );
5569
5570
            $sessionsTable = new SortableTableFromArray([], 0, 0, 'sessions');
5571
            $sessionsTable->setHeaders(
5572
                [
5573
                    get_lang('Session'),
5574
                    get_lang('PublishedExercises'),
5575
                    get_lang('NewExercises'),
5576
                    get_lang('AverageExerciseResult'),
5577
                    get_lang('Details'),
5578
                ]
5579
            );
5580
5581
            foreach ($course_in_session as $my_session_id => $session_data) {
5582
                $course_list = $session_data['course_list'];
5583
                $session_name = $session_data['name'];
5584
                if ($showAllSessions == false) {
5585
                    if (isset($session_id) && !empty($session_id)) {
5586
                        if ($session_id != $my_session_id) {
5587
                            continue;
5588
                        }
5589
                    }
5590
                }
5591
5592
                $all_exercises = 0;
5593
                $all_unanswered_exercises_by_user = 0;
5594
                $all_average = 0;
5595
                $stats_array = [];
5596
5597
                foreach ($course_list as $course_data) {
5598
                    // All exercises in the course @todo change for a real count
5599
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5600
                    $count_exercises = 0;
5601
                    if (is_array($exercises) && !empty($exercises)) {
5602
                        $count_exercises = count($exercises);
5603
                    }
5604
5605
                    // Count of user results
5606
                    $done_exercises = null;
5607
                    $courseInfo = api_get_course_info($course_data['code']);
5608
5609
                    $answered_exercises = 0;
5610
                    if (!empty($exercises)) {
5611
                        foreach ($exercises as $exercise_item) {
5612
                            $attempts = Event::count_exercise_attempts_by_user(
5613
                                api_get_user_id(),
5614
                                $exercise_item['iid'],
5615
                                $courseInfo['real_id'],
5616
                                $my_session_id
5617
                            );
5618
                            if ($attempts > 1) {
5619
                                $answered_exercises++;
5620
                            }
5621
                        }
5622
                    }
5623
5624
                    // Average
5625
                    $average = ExerciseLib::get_average_score_by_course(
5626
                        $courseInfo['real_id'],
5627
                        $my_session_id
5628
                    );
5629
                    $all_exercises += $count_exercises;
5630
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5631
                    $all_average += $average;
5632
                }
5633
5634
                if (!empty($course_list)) {
5635
                    $all_average = $all_average / count($course_list);
5636
                }
5637
5638
                $filterBySession = isset($_GET['session_id']) && $my_session_id == $_GET['session_id'];
5639
5640
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5641
5642
                $sessionsTable->addRow(
5643
                    [
5644
                        Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]),
5645
                        $all_exercises,
5646
                        $all_unanswered_exercises_by_user,
5647
                        ExerciseLib::convert_to_percentage($all_average),
5648
                        Display::url(
5649
                            Display::return_icon(
5650
                                $filterBySession ? '2rightarrow_na.png' : '2rightarrow.png',
5651
                                get_lang('Details')
5652
                            ),
5653
                            api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5654
                        ),
5655
                    ],
5656
                    ['style' => $filterBySession ? 'background-color: #FBF09D;' : '']
5657
                );
5658
            }
5659
5660
            if ($allowCareerUser) {
5661
                $diagrams = '';
5662
                if (!empty($visibleSessions)) {
5663
                    $diagrams .= SessionManager::getCareerDiagramPerSessionList($visibleSessions, $user_id);
5664
                }
5665
                $html .= $diagrams.MyStudents::userCareersTable($user_id);
5666
            }
5667
5668
            $html .= Display::div($sessionsTable->toHtml(), ['class' => 'table-responsive']);
5669
5670
            if ($showGraph) {
5671
                $html .= Display::div(
5672
                    $main_session_graph,
5673
                    [
5674
                        'id' => 'session_graph',
5675
                        'class' => 'chart-session',
5676
                    ]
5677
                );
5678
            }
5679
5680
            // Checking selected session.
5681
            if (isset($_GET['session_id'])) {
5682
                $session_id_from_get = (int) $_GET['session_id'];
5683
                $session_data = $course_in_session[$session_id_from_get];
5684
                $course_list = $session_data['course_list'];
5685
5686
                $html .= '<a name="course_session_list"></a>';
5687
                $html .= Display::page_subheader($session_data['name'], get_lang('CourseList'));
5688
5689
                $columnHeaders = array_filter(
5690
                    [
5691
                        'course_title' => get_lang('Course'),
5692
                        'published_exercises' => get_lang('PublishedExercises'),
5693
                        'new_exercises' => get_lang('NewExercises'),
5694
                        'my_average' => get_lang('MyAverage'),
5695
                        'average_exercise_result' => get_lang('AverageExerciseResult'),
5696
                        'time_spent' => get_lang('TimeSpentInTheCourse'),
5697
                        'lp_progress' => get_lang('LPProgress'),
5698
                        'score' => get_lang('Score')
5699
                            .Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage')),
5700
                        'best_score' => get_lang('BestScore'),
5701
                        'last_connection' => get_lang('LastConnexion'),
5702
                        'details' => get_lang('Details'),
5703
                    ],
5704
                    function ($column, $key) use ($trackingColumns) {
5705
                        if (isset($trackingColumns['course_session']) &&
5706
                            in_array($key, $trackingColumns['course_session']) &&
5707
                            $trackingColumns['course_session'][$key]
5708
                        ) {
5709
                            return true;
5710
                        }
5711
5712
                        return false;
5713
                    },
5714
                    ARRAY_FILTER_USE_BOTH
5715
                );
5716
5717
                $sessionCoursesTable = new SortableTableFromArray([], 0, 0, 'session_courses');
5718
                $sessionCoursesTable->setHeaders($columnHeaders);
5719
5720
                foreach ($course_list as $course_data) {
5721
                    $course_code = $course_data['code'];
5722
                    $course_title = $course_data['title'];
5723
                    $courseId = $course_data['real_id'];
5724
5725
                    // All exercises in the course @todo change for a real count
5726
                    $exercises = ExerciseLib::get_all_exercises(
5727
                        $course_data,
5728
                        $session_id_from_get
5729
                    );
5730
                    $count_exercises = 0;
5731
                    if (!empty($exercises)) {
5732
                        $count_exercises = count($exercises);
5733
                    }
5734
                    $answered_exercises = 0;
5735
                    foreach ($exercises as $exercise_item) {
5736
                        $attempts = Event::count_exercise_attempts_by_user(
5737
                            api_get_user_id(),
5738
                            $exercise_item['iid'],
5739
                            $courseId,
5740
                            $session_id_from_get
5741
                        );
5742
                        if ($attempts > 1) {
5743
                            $answered_exercises++;
5744
                        }
5745
                    }
5746
5747
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5748
5749
                    // Average
5750
                    $average = ExerciseLib::get_average_score_by_course(
5751
                        $courseId,
5752
                        $session_id_from_get
5753
                    );
5754
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5755
                        api_get_user_id(),
5756
                        $courseId,
5757
                        $session_id_from_get
5758
                    );
5759
5760
                    $bestScore = self::get_avg_student_score(
5761
                        $user_id,
5762
                        $course_code,
5763
                        [],
5764
                        $session_id_from_get,
5765
                        false,
5766
                        false,
5767
                        true
5768
                    );
5769
5770
                    $stats_array[$course_code] = [
5771
                        'exercises' => $count_exercises,
5772
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5773
                        'done_exercises' => $done_exercises,
5774
                        'average' => $average,
5775
                        'my_average' => $my_average,
5776
                        'best_score' => $bestScore,
5777
                    ];
5778
5779
                    $last_connection = self::get_last_connection_date_on_the_course(
5780
                        $user_id,
5781
                        $course_data,
5782
                        $session_id_from_get
5783
                    );
5784
5785
                    $progress = self::get_avg_student_progress(
5786
                        $user_id,
5787
                        $course_code,
5788
                        [],
5789
                        $session_id_from_get
5790
                    );
5791
5792
                    $total_time_login = self::get_time_spent_on_the_course(
5793
                        $user_id,
5794
                        $courseId,
5795
                        $session_id_from_get
5796
                    );
5797
                    $time = api_time_to_hms($total_time_login);
5798
5799
                    $percentage_score = self::get_avg_student_score(
5800
                        $user_id,
5801
                        $course_code,
5802
                        [],
5803
                        $session_id_from_get
5804
                    );
5805
                    $courseCodeFromGet = $_GET['course'] ?? null;
5806
5807
                    $filterByCourse = $course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get;
5808
5809
                    $url = api_get_course_url($course_code, $session_id_from_get);
5810
                    $course_url = Display::url(
5811
                        $course_title,
5812
                        $url,
5813
                        ['target' => SESSION_LINK_TARGET]
5814
                    );
5815
5816
                    if (is_numeric($progress)) {
5817
                        $progress = $progress.'%';
5818
                    } else {
5819
                        $progress = '0%';
5820
                    }
5821
                    if (is_numeric($percentage_score)) {
5822
                        $percentage_score = $percentage_score.'%';
5823
                    } else {
5824
                        $percentage_score = '0%';
5825
                    }
5826
5827
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5828
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5829
                    } else {
5830
                        $bestScore = '-';
5831
                    }
5832
5833
                    if (empty($last_connection) || is_bool($last_connection)) {
5834
                        $last_connection = '';
5835
                    }
5836
5837
                    if ($course_code == $courseCodeFromGet &&
5838
                        $_GET['session_id'] == $session_id_from_get
5839
                    ) {
5840
                        $details = Display::url(
5841
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
5842
                        '#course_session_data'
5843
                        );
5844
                    } else {
5845
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5846
                        $details = Display::url(
5847
                            Display::return_icon(
5848
                                '2rightarrow.png',
5849
                                get_lang('Details')
5850
                            ),
5851
                            $url
5852
                        );
5853
                    }
5854
5855
                    $data = array_filter(
5856
                        [
5857
                            'course_title' => $course_url,
5858
                            'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5859
                            'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5860
                            'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5861
                            'average_exercise_result' => $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5862
                            'time_spent' => $time,
5863
                            'lp_progress' => $progress,
5864
                            'score' => $percentage_score,
5865
                            'best_score' => $bestScore,
5866
                            'last_connection' => $last_connection,
5867
                            'details' => $details,
5868
                        ],
5869
                        function ($value, $key) use ($trackingColumns) {
5870
                            if (in_array($key, $trackingColumns['course_session']) && $trackingColumns['course_session'][$key]) {
5871
                                return true;
5872
                            }
5873
5874
                            return false;
5875
                        },
5876
                        ARRAY_FILTER_USE_BOTH
5877
                    );
5878
5879
                    $sessionCoursesTable->addRow(
5880
                        array_values($data),
5881
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
5882
                    );
5883
                }
5884
5885
                $html .= Display::div($sessionCoursesTable->toHtml(), ['class' => 'table-responsive']);
5886
            }
5887
        }
5888
5889
        $pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true';
5890
        if ($pluginCalendar) {
5891
            $course_in_session[0] = $courseIdList;
5892
            $plugin = LearningCalendarPlugin::create();
5893
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
5894
        }
5895
5896
        return $html;
5897
    }
5898
5899
    /**
5900
     * Shows the user detail progress (when clicking in the details link).
5901
     *
5902
     * @param int    $user_id
5903
     * @param string $course_code
5904
     * @param int    $session_id
5905
     */
5906
    public static function show_course_detail($user_id, $course_code, $session_id, $isAllowedToEdit = true): string
5907
    {
5908
        if (empty($user_id) || empty($course_code)) {
5909
            return '';
5910
        }
5911
5912
        $course_info = api_get_course_info($course_code);
5913
        if (empty($course_info)) {
5914
            return '';
5915
        }
5916
5917
        $user_id = (int) $user_id;
5918
        $user = api_get_user_entity($user_id);
5919
        $session_id = (int) $session_id;
5920
5921
        $html = '<a name="course_session_data"></a>';
5922
        $html .= Display::page_subheader2($course_info['title']);
5923
        // Show exercise results of invisible exercises? see BT#4091
5924
        $quizzesHtml = self::generateQuizzesTable($course_info, $session_id);
5925
        // LP table results
5926
        $learningPathsHtml = self::generateLearningPathsTable($user, $course_info, $session_id, $isAllowedToEdit);
5927
        $skillsHtml = self::displayUserSkills($user_id, $course_info['id'], $session_id);
5928
5929
        $toolsHtml = [
5930
            'quizzes' => $quizzesHtml,
5931
            'learning_paths' => $learningPathsHtml,
5932
            'skills' => $skillsHtml,
5933
        ];
5934
5935
        $toolsOrder = api_get_configuration_value('my_progress_course_tools_order');
5936
5937
        if (empty($toolsOrder)) {
5938
            $html .= implode(PHP_EOL, $toolsHtml);
5939
        } else {
5940
            foreach ($toolsOrder['order'] as $tool) {
5941
                $html .= $toolsHtml[$tool].PHP_EOL;
5942
            }
5943
        }
5944
5945
        return $html;
5946
    }
5947
5948
    /**
5949
     * Generates an histogram.
5950
     *
5951
     * @param array $names      list of exercise names
5952
     * @param array $my_results my results 0 to 100
5953
     * @param array $average    average scores 0-100
5954
     *
5955
     * @return string
5956
     */
5957
    public static function generate_session_exercise_graph($names, $my_results, $average)
5958
    {
5959
        $html = api_get_js('chartjs/Chart.js');
5960
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
5961
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
5962
        $jsStr = " var data = {
5963
                       labels:".json_encode($names).",
5964
                       datasets: [
5965
                       {
5966
                         label: '".get_lang('MyResults')."',
5967
                         backgroundColor: 'rgb(255, 99, 132)',
5968
                         stack: 'Stack1',
5969
                         data: ".json_encode($my_results).",
5970
                        },
5971
                        {
5972
                         label: '".get_lang('AverageScore')."',
5973
                         backgroundColor: 'rgb(75, 192, 192)',
5974
                         stack: 'Stack2',
5975
                         data: ".json_encode($average).",
5976
                        },
5977
                        ],
5978
                    };
5979
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
5980
                    var myBarChart = new Chart(ctx, {
5981
                    type: 'bar',
5982
                    data: data,
5983
                    options: {
5984
                            title: {
5985
                                    display: true,
5986
                                    text: '".get_lang('ExercisesInTimeProgressChart')."'
5987
                            },
5988
                            tooltips: {
5989
                                    mode: 'index',
5990
                                    intersect: false
5991
                            },
5992
                            responsive: true,
5993
                            scales: {
5994
                                yAxes: [{
5995
                                    ticks: {
5996
                                        // Include a dollar sign in the ticks
5997
                                        callback: function(value, index, values) {
5998
                                            return value + '%';
5999
                                        }
6000
                                    }
6001
                                }]
6002
                            }
6003
                    }
6004
                });";
6005
        $html .= Display::tag('script', $jsStr);
6006
6007
        return $html;
6008
    }
6009
6010
    /**
6011
     * Returns a thumbnail of the function generate_exercise_result_graph.
6012
     *
6013
     * @param array $attempts
6014
     */
6015
    public static function generate_exercise_result_thumbnail_graph($attempts)
6016
    {
6017
        //$exercise_title = $attempts['title'];
6018
        $attempts = $attempts['data'];
6019
        $my_exercise_result_array = $exercise_result = [];
6020
        if (empty($attempts)) {
6021
            return null;
6022
        }
6023
6024
        foreach ($attempts as $attempt) {
6025
            if (api_get_user_id() == $attempt['exe_user_id']) {
6026
                if ($attempt['exe_weighting'] != 0) {
6027
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6028
                }
6029
            } else {
6030
                if ($attempt['exe_weighting'] != 0) {
6031
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6032
                }
6033
            }
6034
        }
6035
6036
        // Getting best result
6037
        rsort($my_exercise_result_array);
6038
        $my_exercise_result = 0;
6039
        if (isset($my_exercise_result_array[0])) {
6040
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6041
        }
6042
6043
        $max = 100;
6044
        $pieces = 5;
6045
        $part = round($max / $pieces);
6046
        $x_axis = [];
6047
        $final_array = [];
6048
        $my_final_array = [];
6049
6050
        for ($i = 1; $i <= $pieces; $i++) {
6051
            $sum = 1;
6052
            if ($i == 1) {
6053
                $sum = 0;
6054
            }
6055
            $min = ($i - 1) * $part + $sum;
6056
            $max = ($i) * $part;
6057
            $x_axis[] = $min." - ".$max;
6058
            $count = 0;
6059
            foreach ($exercise_result as $result) {
6060
                $percentage = $result * 100;
6061
                if ($percentage >= $min && $percentage <= $max) {
6062
                    //echo ' is > ';
6063
                    $count++;
6064
                }
6065
            }
6066
            //echo '<br />';
6067
            $final_array[] = $count;
6068
6069
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6070
                $my_final_array[] = 1;
6071
            } else {
6072
                $my_final_array[] = 0;
6073
            }
6074
        }
6075
6076
        // Fix to remove the data of the user with my data
6077
        for ($i = 0; $i <= count($my_final_array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
6078
            if (!empty($my_final_array[$i])) {
6079
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6080
                $final_array[$i] = 0;
6081
            }
6082
        }
6083
6084
        // Dataset definition
6085
        $dataSet = new pData();
6086
        $dataSet->addPoints($final_array, 'Serie1');
6087
        $dataSet->addPoints($my_final_array, 'Serie2');
6088
        $dataSet->normalize(100, "%");
6089
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6090
6091
        // Cache definition
6092
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6093
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6094
        $chartHash = $myCache->getHash($dataSet);
6095
        if ($myCache->isInCache($chartHash)) {
6096
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6097
            $myCache->saveFromCache($chartHash, $imgPath);
6098
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6099
        } else {
6100
            /* Create the pChart object */
6101
            $widthSize = 80;
6102
            $heightSize = 35;
6103
            $fontSize = 2;
6104
6105
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6106
6107
            /* Turn of Antialiasing */
6108
            $myPicture->Antialias = false;
6109
6110
            /* Add a border to the picture */
6111
            $myPicture->drawRectangle(
6112
                0,
6113
                0,
6114
                $widthSize - 1,
6115
                $heightSize - 1,
6116
                ['R' => 0, 'G' => 0, 'B' => 0]
6117
            );
6118
6119
            /* Set the default font */
6120
            $myPicture->setFontProperties(
6121
                [
6122
                    'FontName' => api_get_path(
6123
                            SYS_FONTS_PATH
6124
                        ).'opensans/OpenSans-Regular.ttf',
6125
                    'FontSize' => $fontSize,
6126
                ]
6127
            );
6128
6129
            /* Do not write the chart title */
6130
            /* Define the chart area */
6131
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
6132
6133
            /* Draw the scale */
6134
            $scaleSettings = [
6135
                'GridR' => 200,
6136
                'GridG' => 200,
6137
                'GridB' => 200,
6138
                'DrawSubTicks' => true,
6139
                'CycleBackground' => true,
6140
                'Mode' => SCALE_MODE_MANUAL,
6141
                'ManualScale' => [
6142
                    '0' => [
6143
                        'Min' => 0,
6144
                        'Max' => 100,
6145
                    ],
6146
                ],
6147
            ];
6148
            $myPicture->drawScale($scaleSettings);
6149
6150
            /* Turn on shadow computing */
6151
            $myPicture->setShadow(
6152
                true,
6153
                [
6154
                    'X' => 1,
6155
                    'Y' => 1,
6156
                    'R' => 0,
6157
                    'G' => 0,
6158
                    'B' => 0,
6159
                    'Alpha' => 10,
6160
                ]
6161
            );
6162
6163
            /* Draw the chart */
6164
            $myPicture->setShadow(
6165
                true,
6166
                [
6167
                    'X' => 1,
6168
                    'Y' => 1,
6169
                    'R' => 0,
6170
                    'G' => 0,
6171
                    'B' => 0,
6172
                    'Alpha' => 10,
6173
                ]
6174
            );
6175
            $settings = [
6176
                'DisplayValues' => true,
6177
                'DisplaySize' => $fontSize,
6178
                'DisplayR' => 0,
6179
                'DisplayG' => 0,
6180
                'DisplayB' => 0,
6181
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6182
                'Gradient' => false,
6183
                'Surrounding' => 5,
6184
                'InnerSurrounding' => 5,
6185
            ];
6186
            $myPicture->drawStackedBarChart($settings);
6187
6188
            /* Save and write in cache */
6189
            $myCache->writeToCache($chartHash, $myPicture);
6190
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6191
            $myCache->saveFromCache($chartHash, $imgPath);
6192
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6193
        }
6194
6195
        return $imgPath;
6196
    }
6197
6198
    /**
6199
     * Generates a big graph with the number of best results.
6200
     *
6201
     * @param	array
6202
     */
6203
    public static function generate_exercise_result_graph($attempts)
6204
    {
6205
        $exercise_title = strip_tags($attempts['title']);
6206
        $attempts = $attempts['data'];
6207
        $my_exercise_result_array = $exercise_result = [];
6208
        if (empty($attempts)) {
6209
            return null;
6210
        }
6211
        foreach ($attempts as $attempt) {
6212
            if (api_get_user_id() == $attempt['exe_user_id']) {
6213
                if ($attempt['exe_weighting'] != 0) {
6214
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6215
                }
6216
            } else {
6217
                if ($attempt['exe_weighting'] != 0) {
6218
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6219
                }
6220
            }
6221
        }
6222
6223
        //Getting best result
6224
        rsort($my_exercise_result_array);
6225
        $my_exercise_result = 0;
6226
        if (isset($my_exercise_result_array[0])) {
6227
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6228
        }
6229
6230
        $max = 100;
6231
        $pieces = 5;
6232
        $part = round($max / $pieces);
6233
        $x_axis = [];
6234
        $final_array = [];
6235
        $my_final_array = [];
6236
6237
        for ($i = 1; $i <= $pieces; $i++) {
6238
            $sum = 1;
6239
            if ($i == 1) {
6240
                $sum = 0;
6241
            }
6242
            $min = ($i - 1) * $part + $sum;
6243
            $max = ($i) * $part;
6244
            $x_axis[] = $min." - ".$max;
6245
            $count = 0;
6246
            foreach ($exercise_result as $result) {
6247
                $percentage = $result * 100;
6248
                if ($percentage >= $min && $percentage <= $max) {
6249
                    $count++;
6250
                }
6251
            }
6252
            $final_array[] = $count;
6253
6254
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6255
                $my_final_array[] = 1;
6256
            } else {
6257
                $my_final_array[] = 0;
6258
            }
6259
        }
6260
6261
        //Fix to remove the data of the user with my data
6262
6263
        for ($i = 0; $i <= count($my_final_array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
6264
            if (!empty($my_final_array[$i])) {
6265
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6266
                $final_array[$i] = 0;
6267
            }
6268
        }
6269
6270
        // Dataset definition
6271
        $dataSet = new pData();
6272
        $dataSet->addPoints($final_array, 'Serie1');
6273
        $dataSet->addPoints($my_final_array, 'Serie2');
6274
        $dataSet->addPoints($x_axis, 'Serie3');
6275
6276
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
6277
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
6278
        $dataSet->setAbscissa('Serie3');
6279
6280
        $dataSet->setXAxisName(get_lang('Score'));
6281
        $dataSet->normalize(100, "%");
6282
6283
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6284
6285
        // Cache definition
6286
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6287
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6288
        $chartHash = $myCache->getHash($dataSet);
6289
6290
        if ($myCache->isInCache($chartHash)) {
6291
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6292
            $myCache->saveFromCache($chartHash, $imgPath);
6293
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6294
        } else {
6295
            /* Create the pChart object */
6296
            $widthSize = 480;
6297
            $heightSize = 250;
6298
            $fontSize = 8;
6299
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6300
6301
            /* Turn of Antialiasing */
6302
            $myPicture->Antialias = false;
6303
6304
            /* Add a border to the picture */
6305
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
6306
6307
            /* Set the default font */
6308
            $myPicture->setFontProperties(
6309
                [
6310
                    'FontName' => api_get_path(
6311
                            SYS_FONTS_PATH
6312
                        ).'opensans/OpenSans-Regular.ttf',
6313
                    'FontSize' => 10,
6314
                ]
6315
            );
6316
6317
            /* Write the chart title */
6318
            $myPicture->drawText(
6319
                250,
6320
                20,
6321
                $exercise_title,
6322
                [
6323
                    'FontSize' => 12,
6324
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
6325
                ]
6326
            );
6327
6328
            /* Define the chart area */
6329
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
6330
6331
            /* Draw the scale */
6332
            $scaleSettings = [
6333
                'GridR' => 200,
6334
                'GridG' => 200,
6335
                'GridB' => 200,
6336
                'DrawSubTicks' => true,
6337
                'CycleBackground' => true,
6338
                'Mode' => SCALE_MODE_MANUAL,
6339
                'ManualScale' => [
6340
                    '0' => [
6341
                        'Min' => 0,
6342
                        'Max' => 100,
6343
                    ],
6344
                ],
6345
            ];
6346
            $myPicture->drawScale($scaleSettings);
6347
6348
            /* Turn on shadow computing */
6349
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6350
6351
            /* Draw the chart */
6352
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6353
            $settings = [
6354
                'DisplayValues' => true,
6355
                'DisplaySize' => $fontSize,
6356
                'DisplayR' => 0,
6357
                'DisplayG' => 0,
6358
                'DisplayB' => 0,
6359
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6360
                'Gradient' => false,
6361
                'Surrounding' => 30,
6362
                'InnerSurrounding' => 25,
6363
            ];
6364
            $myPicture->drawStackedBarChart($settings);
6365
6366
            $legendSettings = [
6367
                'Mode' => LEGEND_HORIZONTAL,
6368
                'Style' => LEGEND_NOBORDER,
6369
            ];
6370
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
6371
6372
            /* Write and save into cache */
6373
            $myCache->writeToCache($chartHash, $myPicture);
6374
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6375
            $myCache->saveFromCache($chartHash, $imgPath);
6376
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6377
        }
6378
6379
        return $imgPath;
6380
    }
6381
6382
    /**
6383
     * @param FormValidator $form
6384
     *
6385
     * @return mixed
6386
     */
6387
    public static function setUserSearchForm($form)
6388
    {
6389
        global $_configuration;
6390
        $form->addElement('text', 'keyword', get_lang('Keyword'));
6391
        $form->addElement(
6392
            'select',
6393
            'active',
6394
            get_lang('Status'),
6395
            [1 => get_lang('Active'), 0 => get_lang('Inactive')]
6396
        );
6397
6398
        $form->addElement(
6399
            'select',
6400
            'sleeping_days',
6401
            get_lang('InactiveDays'),
6402
            [
6403
                '',
6404
                1 => 1,
6405
                5 => 5,
6406
                15 => 15,
6407
                30 => 30,
6408
                60 => 60,
6409
                90 => 90,
6410
                120 => 120,
6411
            ]
6412
        );
6413
6414
        $form->addButtonSearch(get_lang('Search'));
6415
6416
        return $form;
6417
    }
6418
6419
    /**
6420
     * Get the progress of a exercise.
6421
     *
6422
     * @param int    $sessionId  The session ID (session.id)
6423
     * @param int    $courseId   The course ID (course.id)
6424
     * @param int    $exerciseId The quiz ID (c_quiz.id)
6425
     * @param string $date_from
6426
     * @param string $date_to
6427
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
6428
     *
6429
     * @return array An array with the data of exercise(s) progress
6430
     */
6431
    public static function get_exercise_progress(
6432
        $sessionId = 0,
6433
        $courseId = 0,
6434
        $exerciseId = 0,
6435
        $date_from = null,
6436
        $date_to = null,
6437
        $options = []
6438
    ) {
6439
        $sessionId = intval($sessionId);
6440
        $courseId = intval($courseId);
6441
        $exerciseId = intval($exerciseId);
6442
        $date_from = Database::escape_string($date_from);
6443
        $date_to = Database::escape_string($date_to);
6444
        /*
6445
         * This method gets the data by blocks, as previous attempts at one single
6446
         * query made it take ages. The logic of query division is described below
6447
         */
6448
        // Get tables names
6449
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6450
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6451
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6452
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6453
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6454
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6455
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6456
6457
        $sessions = [];
6458
        $courses = [];
6459
        // if session ID is defined but course ID is empty, get all the courses
6460
        // from that session
6461
        if (!empty($sessionId) && empty($courseId)) {
6462
            // $courses is an array of course int id as index and course details hash as value
6463
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6464
            $sessions[$sessionId] = api_get_session_info($sessionId);
6465
        } elseif (empty($sessionId) && !empty($courseId)) {
6466
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6467
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
6468
            $course = api_get_course_info_by_id($courseId);
6469
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6470
            $courses[$courseId] = $course;
6471
            foreach ($sessionsTemp as $sessionItem) {
6472
                $sessions[$sessionItem['id']] = $sessionItem;
6473
            }
6474
        } elseif (!empty($courseId) && !empty($sessionId)) {
6475
            //none is empty
6476
            $course = api_get_course_info_by_id($courseId);
6477
            $courses[$courseId] = [$course['code']];
6478
            $courses[$courseId]['code'] = $course['code'];
6479
            $sessions[$sessionId] = api_get_session_info($sessionId);
6480
        } else {
6481
            //both are empty, not enough data, return an empty array
6482
            return [];
6483
        }
6484
        // Now we have two arrays of courses and sessions with enough data to proceed
6485
        // If no course could be found, we shouldn't return anything.
6486
        // Sessions can be empty (then we only return the pure-course-context results)
6487
        if (count($courses) < 1) {
6488
            return [];
6489
        }
6490
6491
        $data = [];
6492
        // The following loop is less expensive than what it seems:
6493
        // - if a course was defined, then we only loop through sessions
6494
        // - if a session was defined, then we only loop through courses
6495
        // - if a session and a course were defined, then we only loop once
6496
        foreach ($courses as $courseIdx => $courseData) {
6497
            $where = '';
6498
            $whereParams = [];
6499
            $whereSessionParams = '';
6500
            if (count($sessions > 0)) {
6501
                foreach ($sessions as $sessionIdx => $sessionData) {
6502
                    if (!empty($sessionIdx)) {
6503
                        $whereSessionParams .= $sessionIdx.',';
6504
                    }
6505
                }
6506
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6507
            }
6508
6509
            if (!empty($exerciseId)) {
6510
                $exerciseId = intval($exerciseId);
6511
                $where .= ' AND q.id = %d ';
6512
                $whereParams[] = $exerciseId;
6513
            }
6514
6515
            /*
6516
             * This feature has been disabled for now, to avoid having to
6517
             * join two very large tables
6518
            //2 = show all questions (wrong and correct answered)
6519
            if ($answer != 2) {
6520
                $answer = intval($answer);
6521
                //$where .= ' AND qa.correct = %d';
6522
                //$whereParams[] = $answer;
6523
            }
6524
            */
6525
6526
            $limit = '';
6527
            if (!empty($options['limit'])) {
6528
                $limit = " LIMIT ".$options['limit'];
6529
            }
6530
6531
            if (!empty($options['where'])) {
6532
                $where .= ' AND '.Database::escape_string($options['where']);
6533
            }
6534
6535
            $order = '';
6536
            if (!empty($options['order'])) {
6537
                $order = " ORDER BY ".$options['order'];
6538
            }
6539
6540
            if (!empty($date_to) && !empty($date_from)) {
6541
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6542
            }
6543
6544
            $sql = "SELECT
6545
                te.session_id,
6546
                ta.id as attempt_id,
6547
                te.exe_user_id as user_id,
6548
                te.exe_id as exercise_attempt_id,
6549
                ta.question_id,
6550
                ta.answer as answer_id,
6551
                ta.tms as time,
6552
                te.exe_exo_id as quiz_id,
6553
                CONCAT ('c', q.c_id, '_e', q.iid) as exercise_id,
6554
                q.title as quiz_title,
6555
                qq.description as description
6556
                FROM $ttrack_exercises te
6557
                INNER JOIN $ttrack_attempt ta
6558
                ON ta.exe_id = te.exe_id
6559
                INNER JOIN $tquiz q
6560
                ON q.iid = te.exe_exo_id
6561
                INNER JOIN $tquiz_rel_question rq
6562
                ON rq.exercice_id = q.iid AND rq.c_id = q.c_id
6563
                INNER JOIN $tquiz_question qq
6564
                ON
6565
                    qq.iid = rq.question_id AND
6566
                    qq.position = rq.question_order AND
6567
                    ta.question_id = rq.question_id
6568
                WHERE
6569
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6570
                    AND q.c_id = $courseIdx
6571
                    $where $order $limit";
6572
            $sql_query = vsprintf($sql, $whereParams);
6573
6574
            // Now browse through the results and get the data
6575
            $rs = Database::query($sql_query);
6576
            $userIds = [];
6577
            $questionIds = [];
6578
            $answerIds = [];
6579
            while ($row = Database::fetch_array($rs)) {
6580
                //only show if exercise is visible
6581
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6582
                    $userIds[$row['user_id']] = $row['user_id'];
6583
                    $questionIds[$row['question_id']] = $row['question_id'];
6584
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6585
                    $row['session'] = $sessions[$row['session_id']];
6586
                    $data[] = $row;
6587
                }
6588
            }
6589
            // Now fill questions data. Query all questions and answers for this test to avoid
6590
            $sqlQuestions = "SELECT tq.c_id, tq.iid as question_id, tq.question, tqa.id_auto,
6591
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
6592
                            FROM $tquiz_question tq, $tquiz_answer tqa
6593
                            WHERE
6594
                                tqa.question_id = tq.iid AND
6595
                                tq.c_id = $courseIdx AND
6596
                                tq.id IN (".implode(',', $questionIds).")";
6597
6598
            $resQuestions = Database::query($sqlQuestions);
6599
            $answer = [];
6600
            $question = [];
6601
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6602
                $questionId = $rowQuestion['question_id'];
6603
                $answerId = $rowQuestion['answer_id'];
6604
                $answer[$questionId][$answerId] = [
6605
                    'position' => $rowQuestion['position'],
6606
                    'question' => $rowQuestion['question'],
6607
                    'answer' => $rowQuestion['answer'],
6608
                    'correct' => $rowQuestion['correct'],
6609
                ];
6610
                $question[$questionId]['question'] = $rowQuestion['question'];
6611
            }
6612
6613
            // Now fill users data
6614
            $sqlUsers = "SELECT user_id, username, lastname, firstname
6615
                         FROM $tuser
6616
                         WHERE user_id IN (".implode(',', $userIds).")";
6617
            $resUsers = Database::query($sqlUsers);
6618
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6619
                $users[$rowUser['user_id']] = $rowUser;
6620
            }
6621
6622
            foreach ($data as $id => $row) {
6623
                $rowQuestId = $row['question_id'];
6624
                $rowAnsId = $row['answer_id'];
6625
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
6626
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
6627
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6628
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6629
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6630
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
6631
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6632
                $data[$id]['question_id'] = $rowQuestId;
6633
                $data[$id]['description'] = $row['description'];
6634
            }
6635
6636
            /*
6637
            The minimum expected array structure at the end is:
6638
            attempt_id,
6639
            session name,
6640
            exercise_id,
6641
            quiz_title,
6642
            username,
6643
            lastname,
6644
            firstname,
6645
            time,
6646
            question_id,
6647
            question,
6648
            answer,
6649
            */
6650
        }
6651
6652
        return $data;
6653
    }
6654
6655
    /**
6656
     * @param string              $tool
6657
     * @param sessionEntity |null $session Optional
6658
     *
6659
     * @throws \Doctrine\ORM\NonUniqueResultException
6660
     *
6661
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
6662
     */
6663
    public static function getLastStudentPublication(
6664
        User $user,
6665
        $tool,
6666
        Course $course,
6667
        SessionEntity $session = null
6668
    ) {
6669
        return Database::getManager()
6670
            ->createQuery("
6671
                SELECT csp
6672
                FROM ChamiloCourseBundle:CStudentPublication csp
6673
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6674
                    WITH (
6675
                        csp.iid = cip.ref AND
6676
                        csp.session = cip.session AND
6677
                        csp.cId = cip.course AND
6678
                        csp.userId = cip.lasteditUserId
6679
                    )
6680
                WHERE
6681
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6682
                ORDER BY csp.iid DESC
6683
            ")
6684
            ->setMaxResults(1)
6685
            ->setParameters([
6686
                'tool' => $tool,
6687
                'session' => $session,
6688
                'course' => $course,
6689
                'user' => $user,
6690
            ])
6691
            ->getOneOrNullResult();
6692
    }
6693
6694
    /**
6695
     * Get the HTML code for show a block with the achieved user skill on course/session.
6696
     *
6697
     * @param int  $userId
6698
     * @param int  $courseId
6699
     * @param int  $sessionId
6700
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6701
     *
6702
     * @return string
6703
     */
6704
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6705
    {
6706
        if (Skill::isAllowed($userId, false) === false && $forceView == false) {
6707
            return '';
6708
        }
6709
        $skillManager = new Skill();
6710
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6711
6712
        return $html;
6713
    }
6714
6715
    /**
6716
     * @param int $userId
6717
     * @param int $courseId
6718
     * @param int $sessionId
6719
     *
6720
     * @return array
6721
     */
6722
    public static function getCalculateTime($userId, $courseId, $sessionId)
6723
    {
6724
        $userId = (int) $userId;
6725
        $courseId = (int) $courseId;
6726
        $sessionId = (int) $sessionId;
6727
6728
        if (empty($userId) || empty($courseId)) {
6729
            return [];
6730
        }
6731
6732
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6733
                FROM track_e_access_complete
6734
                WHERE
6735
                    user_id = $userId AND
6736
                    c_id = $courseId AND
6737
                    session_id = $sessionId AND
6738
                    login_as = 0
6739
                ORDER BY date_reg ASC
6740
                LIMIT 1";
6741
        $rs = Database::query($sql);
6742
6743
        $firstConnection = '';
6744
        $lastConnection = '';
6745
        if (Database::num_rows($rs) > 0) {
6746
            $value = Database::fetch_array($rs);
6747
            $firstConnection = $value['min'];
6748
            $lastConnection = $value['max'];
6749
        }
6750
6751
        $sql = "SELECT * FROM track_e_access_complete
6752
                WHERE
6753
                    user_id = $userId AND
6754
                    c_id = $courseId AND
6755
                    session_id = $sessionId AND
6756
                    login_as = 0 AND current_id <> 0";
6757
6758
        $res = Database::query($sql);
6759
        $reg = [];
6760
        while ($row = Database::fetch_assoc($res)) {
6761
            $reg[$row['id']] = $row;
6762
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6763
        }
6764
6765
        $sessions = [];
6766
        foreach ($reg as $key => $value) {
6767
            $sessions[$value['current_id']][$value['tool']][] = $value;
6768
        }
6769
6770
        $quizTime = 0;
6771
        $result = [];
6772
        $totalTime = 0;
6773
        $lpTime = [];
6774
        $lpDetailTime = [];
6775
        foreach ($sessions as $listPerTool) {
6776
            $min = 0;
6777
            $max = 0;
6778
            $sessionDiff = 0;
6779
            foreach ($listPerTool as $tool => $results) {
6780
                $beforeItem = [];
6781
                foreach ($results as $item) {
6782
                    if (empty($beforeItem)) {
6783
                        $beforeItem = $item;
6784
                        if (empty($min)) {
6785
                            $min = $item['date_reg'];
6786
                        }
6787
6788
                        if (empty($max)) {
6789
                            $max = $item['date_reg'];
6790
                        }
6791
                        continue;
6792
                    }
6793
6794
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6795
                    if ($item['date_reg'] > $max) {
6796
                        $max = $item['date_reg'];
6797
                    }
6798
6799
                    if (empty($min)) {
6800
                        $min = $item['date_reg'];
6801
                    }
6802
6803
                    if ($item['date_reg'] < $min) {
6804
                        $min = $item['date_reg'];
6805
                    }
6806
6807
                    switch ($tool) {
6808
                        case TOOL_AGENDA:
6809
                        case TOOL_FORUM:
6810
                        case TOOL_ANNOUNCEMENT:
6811
                        case TOOL_COURSE_DESCRIPTION:
6812
                        case TOOL_SURVEY:
6813
                        case TOOL_NOTEBOOK:
6814
                        case TOOL_GRADEBOOK:
6815
                        case TOOL_DROPBOX:
6816
                        case 'Reports':
6817
                        case 'Videoconference':
6818
                        case TOOL_LINK:
6819
                        case TOOL_CHAT:
6820
                        case 'course-main':
6821
                            if (!isset($result[$tool])) {
6822
                                $result[$tool] = 0;
6823
                            }
6824
                            $result[$tool] += $partialTime;
6825
                            break;
6826
                        case TOOL_LEARNPATH:
6827
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6828
                                break;
6829
                            }
6830
                            if (!isset($lpTime[$item['tool_id']])) {
6831
                                $lpTime[$item['tool_id']] = 0;
6832
                            }
6833
6834
                            // Saving the attempt id "action_details"
6835
                            if (!empty($item['tool_id'])) {
6836
                                if (!empty($item['tool_id_detail'])) {
6837
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6838
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6839
                                    }
6840
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6841
                                }
6842
                                $lpTime[$item['tool_id']] += $partialTime;
6843
                            }
6844
                            break;
6845
                        case TOOL_QUIZ:
6846
                            if (!isset($lpTime[$item['action_details']])) {
6847
                                $lpTime[$item['action_details']] = 0;
6848
                            }
6849
                            if ($beforeItem['action'] === 'learnpath_id') {
6850
                                $lpTime[$item['action_details']] += $partialTime;
6851
                            } else {
6852
                                $quizTime += $partialTime;
6853
                            }
6854
                            break;
6855
                    }
6856
                    $beforeItem = $item;
6857
                }
6858
            }
6859
6860
            $sessionDiff += $max - $min;
6861
            if ($sessionDiff > 0) {
6862
                $totalTime += $sessionDiff;
6863
            }
6864
        }
6865
6866
        $totalLp = 0;
6867
        foreach ($lpTime as $value) {
6868
            $totalLp += $value;
6869
        }
6870
6871
        $result['learnpath_detailed'] = $lpDetailTime;
6872
        $result[TOOL_LEARNPATH] = $lpTime;
6873
        $result[TOOL_QUIZ] = $quizTime;
6874
        $result['total_learnpath'] = $totalLp;
6875
        $result['total_time'] = $totalTime;
6876
        $result['number_connections'] = count($sessions);
6877
        $result['first'] = $firstConnection;
6878
        $result['last'] = $lastConnection;
6879
6880
        return $result;
6881
    }
6882
6883
    /**
6884
     * Gets the IP of a given user, using the last login before the given date.
6885
     *
6886
     * @param int User ID
6887
     * @param string Datetime
6888
     * @param bool Whether to return the IP as a link or just as an IP
6889
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6890
     *
6891
     * @return string IP address (or false on error)
6892
     * @assert (0,0) === false
6893
     */
6894
    public static function get_ip_from_user_event(
6895
        $user_id,
6896
        $event_date,
6897
        $return_as_link = false,
6898
        $body_replace = null
6899
    ) {
6900
        if (empty($user_id) || empty($event_date)) {
6901
            return false;
6902
        }
6903
        $user_id = intval($user_id);
6904
        $event_date = Database::escape_string($event_date);
6905
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6906
        $sql_ip = "SELECT login_date, user_ip
6907
                   FROM $table_login
6908
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6909
                   ORDER BY login_date DESC LIMIT 1";
6910
        $ip = '';
6911
        $res_ip = Database::query($sql_ip);
6912
        if ($res_ip !== false && Database::num_rows($res_ip) > 0) {
6913
            $row_ip = Database::fetch_row($res_ip);
6914
            if ($return_as_link) {
6915
                $ip = Display::url(
6916
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6917
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6918
                    ['title' => get_lang('TraceIP'), 'target' => '_blank']
6919
                );
6920
            } else {
6921
                $ip = $row_ip[1];
6922
            }
6923
        }
6924
6925
        return $ip;
6926
    }
6927
6928
    /**
6929
     * @param int   $userId
6930
     * @param array $courseInfo
6931
     * @param int   $sessionId
6932
     *
6933
     * @return array
6934
     */
6935
    public static function getToolInformation(
6936
        $userId,
6937
        $courseInfo,
6938
        $sessionId = 0
6939
    ) {
6940
        $csvContent = [];
6941
        $courseToolInformation = '';
6942
        $headerTool = [
6943
            [get_lang('Title')],
6944
            [get_lang('CreatedAt')],
6945
            [get_lang('UpdatedAt')],
6946
        ];
6947
6948
        $headerListForCSV = [];
6949
        foreach ($headerTool as $item) {
6950
            $headerListForCSV[] = $item[0];
6951
        }
6952
6953
        $courseForumInformationArray = getForumCreatedByUser(
6954
            $userId,
6955
            $courseInfo,
6956
            $sessionId
6957
        );
6958
6959
        if (!empty($courseForumInformationArray)) {
6960
            $csvContent[] = [];
6961
            $csvContent[] = [get_lang('Forums')];
6962
            $csvContent[] = $headerListForCSV;
6963
            foreach ($courseForumInformationArray as $row) {
6964
                $csvContent[] = $row;
6965
            }
6966
6967
            $courseToolInformation .= Display::page_subheader2(
6968
                get_lang('Forums')
6969
            );
6970
            $courseToolInformation .= Display::return_sortable_table(
6971
                $headerTool,
6972
                $courseForumInformationArray
6973
            );
6974
        }
6975
6976
        $courseWorkInformationArray = getWorkCreatedByUser(
6977
            $userId,
6978
            $courseInfo['real_id'],
6979
            $sessionId
6980
        );
6981
6982
        if (!empty($courseWorkInformationArray)) {
6983
            $csvContent[] = null;
6984
            $csvContent[] = [get_lang('Works')];
6985
            $csvContent[] = $headerListForCSV;
6986
6987
            foreach ($courseWorkInformationArray as $row) {
6988
                $csvContent[] = $row;
6989
            }
6990
            $csvContent[] = null;
6991
6992
            $courseToolInformation .= Display::page_subheader2(
6993
                get_lang('Works')
6994
            );
6995
            $courseToolInformation .= Display::return_sortable_table(
6996
                $headerTool,
6997
                $courseWorkInformationArray
6998
            );
6999
        }
7000
7001
        $courseToolInformationTotal = null;
7002
        if (!empty($courseToolInformation)) {
7003
            $sessionTitle = null;
7004
            if (!empty($sessionId)) {
7005
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
7006
            }
7007
7008
            $courseToolInformationTotal .= Display::page_subheader(
7009
                $courseInfo['title'].$sessionTitle
7010
            );
7011
            $courseToolInformationTotal .= $courseToolInformation;
7012
        }
7013
7014
        return [
7015
            'array' => $csvContent,
7016
            'html' => $courseToolInformationTotal,
7017
        ];
7018
    }
7019
7020
    /**
7021
     * @param int $sessionId
7022
     *
7023
     * @return bool
7024
     */
7025
    public static function isAllowToTrack($sessionId)
7026
    {
7027
        return
7028
            api_is_platform_admin(true, true) ||
7029
            SessionManager::user_is_general_coach(api_get_user_id(), $sessionId) ||
7030
            api_is_allowed_to_create_course() ||
7031
            api_is_course_tutor() ||
7032
            api_is_course_admin();
7033
    }
7034
7035
    public static function getCourseLpProgress($userId, $sessionId)
7036
    {
7037
        $controller = new IndexManager(get_lang('MyCourses'));
7038
        $data = $controller->returnCoursesAndSessions($userId);
7039
        $courseList = $data['courses'];
7040
        $result = [];
7041
        if ($courseList) {
7042
            //$counter = 1;
7043
            foreach ($courseList as $course) {
7044
                $courseId = $course['course_id'];
7045
                $courseInfo = api_get_course_info_by_id($courseId);
7046
                if (empty($courseInfo)) {
7047
                    continue;
7048
                }
7049
                $courseCode = $courseInfo['code'];
7050
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
7051
7052
                // total progress
7053
                $list = new LearnpathList(
7054
                    $userId,
7055
                     $courseInfo,
7056
                    0,
7057
                    'lp.publicatedOn ASC',
7058
                    true,
7059
                    null,
7060
                    true
7061
                );
7062
7063
                $list = $list->get_flat_list();
7064
                $totalProgress = 0;
7065
                $totalTime = 0;
7066
                if (!empty($list)) {
7067
                    foreach ($list as $lp_id => $learnpath) {
7068
                        if (!$learnpath['lp_visibility']) {
7069
                            continue;
7070
                        }
7071
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
7072
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
7073
                        if ($lpProgress == 100) {
7074
                            if (!empty($time)) {
7075
                                $timeInMinutes = $time / 60;
7076
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
7077
                                if ($timeInMinutes >= $min) {
7078
                                    $totalProgress++;
7079
                                }
7080
                            }
7081
                        }
7082
                        $totalTime += $time;
7083
                    }
7084
7085
                    if (!empty($totalProgress)) {
7086
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
7087
                    }
7088
                }
7089
7090
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
7091
7092
                $result[] = [
7093
                    'module' => $courseInfo['name'],
7094
                    'progress' => $progress,
7095
                    'qualification' => $totalProgress,
7096
                    'activeTime' => $totalTime,
7097
                ];
7098
            }
7099
        }
7100
7101
        return $result;
7102
    }
7103
7104
    /**
7105
     * @param int $userId
7106
     * @param int $courseId
7107
     * @param int $sessionId
7108
     *
7109
     * @return int
7110
     */
7111
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
7112
    {
7113
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7114
        $sessionCondition = api_get_session_condition($sessionId);
7115
        $courseId = (int) $courseId;
7116
        $userId = (int) $userId;
7117
7118
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
7119
                FROM $tblTrackCourseAccess
7120
                WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
7121
7122
        $result = Database::fetch_assoc(Database::query($sql));
7123
7124
        return (int) $result['c'];
7125
    }
7126
7127
    public static function processUserDataMove(
7128
        $user_id,
7129
        $course_info,
7130
        $origin_session_id,
7131
        $new_session_id,
7132
        $update_database,
7133
        $debug = false
7134
    ) {
7135
        // Begin with the import process
7136
        $origin_course_code = $course_info['code'];
7137
        $course_id = $course_info['real_id'];
7138
        $user_id = (int) $user_id;
7139
        $origin_session_id = (int) $origin_session_id;
7140
        $new_session_id = (int) $new_session_id;
7141
        $session = api_get_session_entity($new_session_id);
7142
        $em = Database::getManager();
7143
7144
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7145
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7146
        $attemptRecording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
7147
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7148
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
7149
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
7150
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
7151
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
7152
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
7153
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
7154
7155
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
7156
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
7157
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
7158
7159
        //1. track_e_exercises
7160
        //ORIGINAL COURSE
7161
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7162
                WHERE c_id = $course_id AND  session_id = $origin_session_id AND exe_user_id = $user_id ";
7163
        $res = Database::query($sql);
7164
        $list = [];
7165
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7166
            $list[$row['exe_id']] = $row;
7167
        }
7168
7169
        $result_message = [];
7170
        $result_message_compare = [];
7171
        if (!empty($list)) {
7172
            foreach ($list as $exe_id => $data) {
7173
                if ($update_database) {
7174
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7175
                    Database::query($sql);
7176
7177
                    $sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7178
                    Database::query($sql);
7179
7180
                    $sql = "UPDATE $attemptRecording SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7181
                    Database::query($sql);
7182
7183
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
7184
                        $result_message[$TABLETRACK_EXERCICES] = 0;
7185
                    }
7186
                    $result_message[$TABLETRACK_EXERCICES]++;
7187
                } else {
7188
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7189
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
7190
                    } else {
7191
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7192
                    }
7193
                }
7194
            }
7195
        }
7196
7197
        // DESTINY COURSE
7198
        if (!$update_database) {
7199
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7200
                    WHERE
7201
                        c_id = $course_id AND
7202
                        session_id = $new_session_id AND
7203
                        exe_user_id = $user_id ";
7204
            $res = Database::query($sql);
7205
            $list = [];
7206
            while ($row = Database::fetch_array($res, 'ASSOC')) {
7207
                $list[$row['exe_id']] = $row;
7208
            }
7209
7210
            if (!empty($list)) {
7211
                foreach ($list as $exe_id => $data) {
7212
                    if ($update_database) {
7213
                        $sql = "UPDATE $TABLETRACK_EXERCICES
7214
                                SET session_id = '$new_session_id'
7215
                                WHERE exe_id = $exe_id";
7216
                        Database::query($sql);
7217
                        $result_message[$TABLETRACK_EXERCICES]++;
7218
                    } else {
7219
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7220
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
7221
                        } else {
7222
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7223
                        }
7224
                    }
7225
                }
7226
            }
7227
        }
7228
7229
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
7230
        // Nothing to do because there are not relationship with a session
7231
        // 3. track_e_course_access
7232
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
7233
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
7234
        $res = Database::query($sql);
7235
        $list = [];
7236
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7237
            $list[$row['course_access_id']] = $row;
7238
        }
7239
7240
        if (!empty($list)) {
7241
            foreach ($list as $id => $data) {
7242
                if ($update_database) {
7243
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
7244
                            SET session_id = $new_session_id
7245
                            WHERE course_access_id = $id";
7246
                    if ($debug) {
7247
                        echo $sql;
7248
                    }
7249
                    Database::query($sql);
7250
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
7251
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
7252
                    }
7253
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
7254
                }
7255
            }
7256
        }
7257
7258
        // 4. track_e_lastaccess
7259
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
7260
                WHERE
7261
                    c_id = $course_id AND
7262
                    access_session_id = $origin_session_id AND
7263
                    access_user_id = $user_id ";
7264
        $res = Database::query($sql);
7265
        $list = [];
7266
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7267
            $list[] = $row['access_id'];
7268
        }
7269
7270
        if (!empty($list)) {
7271
            foreach ($list as $id) {
7272
                if ($update_database) {
7273
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
7274
                            SET access_session_id = $new_session_id
7275
                            WHERE access_id = $id";
7276
                    if ($debug) {
7277
                        echo $sql;
7278
                    }
7279
                    Database::query($sql);
7280
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
7281
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
7282
                    }
7283
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
7284
                }
7285
            }
7286
        }
7287
7288
        // 5. lp_item_view
7289
        // CHECK ORIGIN
7290
        $sql = "SELECT * FROM $TBL_LP_VIEW
7291
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
7292
        $res = Database::query($sql);
7293
7294
        // Getting the list of LPs in the new session
7295
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
7296
        $flat_list = $lp_list->get_flat_list();
7297
        $list = [];
7298
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7299
            // Checking if the LP exist in the new session
7300
            //if (in_array($row['lp_id'], array_keys($flat_list))) {
7301
            $list[$row['id']] = $row;
7302
            //}
7303
        }
7304
7305
        if (!empty($list)) {
7306
            foreach ($list as $id => $data) {
7307
                if ($update_database) {
7308
                    $sql = "UPDATE $TBL_LP_VIEW
7309
                            SET session_id = $new_session_id
7310
                            WHERE c_id = $course_id AND id = $id ";
7311
                    if ($debug) {
7312
                        var_dump($sql);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($sql) looks like debug code. Are you sure you do not want to remove it?
Loading history...
7313
                    }
7314
                    $res = Database::query($sql);
7315
                    if ($debug) {
7316
                        var_dump($res);
7317
                    }
7318
                    if (!isset($result_message[$TBL_LP_VIEW])) {
7319
                        $result_message[$TBL_LP_VIEW] = 0;
7320
                    }
7321
                    $result_message[$TBL_LP_VIEW]++;
7322
                } else {
7323
                    // Getting all information of that lp_item_id
7324
                    $score = self::get_avg_student_score(
7325
                        $user_id,
7326
                        $origin_course_code,
7327
                        [$data['lp_id']],
7328
                        $origin_session_id
7329
                    );
7330
                    $progress = self::get_avg_student_progress(
7331
                        $user_id,
7332
                        $origin_course_code,
7333
                        [$data['lp_id']],
7334
                        $origin_session_id
7335
                    );
7336
                    $result_message['LP_VIEW'][$data['lp_id']] = [
7337
                        'score' => $score,
7338
                        'progress' => $progress,
7339
                    ];
7340
                }
7341
            }
7342
        }
7343
7344
        // Check destination.
7345
        if (!$update_database) {
7346
            $sql = "SELECT * FROM $TBL_LP_VIEW
7347
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
7348
            $res = Database::query($sql);
7349
7350
            // Getting the list of LPs in the new session
7351
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
7352
            $flat_list = $lp_list->get_flat_list();
7353
7354
            $list = [];
7355
            while ($row = Database::fetch_array($res, 'ASSOC')) {
7356
                //Checking if the LP exist in the new session
7357
                //if (in_array($row['lp_id'], array_keys($flat_list))) {
7358
                $list[$row['id']] = $row;
7359
                //}
7360
            }
7361
7362
            if (!empty($list)) {
7363
                foreach ($list as $id => $data) {
7364
                    // Getting all information of that lp_item_id
7365
                    $score = self::get_avg_student_score(
7366
                        $user_id,
7367
                        $origin_course_code,
7368
                        [$data['lp_id']],
7369
                        $new_session_id
7370
                    );
7371
                    $progress = self::get_avg_student_progress(
7372
                        $user_id,
7373
                        $origin_course_code,
7374
                        [$data['lp_id']],
7375
                        $new_session_id
7376
                    );
7377
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
7378
                        'score' => $score,
7379
                        'progress' => $progress,
7380
                    ];
7381
                }
7382
            }
7383
        }
7384
7385
        // 6. Agenda
7386
        // calendar_event_attachment no problems no session_id
7387
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
7388
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
7389
        $res = Database::query($sql);
7390
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7391
            $id = $row['ref'];
7392
            if ($update_database) {
7393
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id ";
7394
                if ($debug) {
7395
                    var_dump($sql);
7396
                }
7397
                $res_update = Database::query($sql);
7398
                if ($debug) {
7399
                    var_dump($res_update);
7400
                }
7401
                if (!isset($result_message['agenda'])) {
7402
                    $result_message['agenda'] = 0;
7403
                }
7404
                $result_message['agenda']++;
7405
            }
7406
        }
7407
7408
        // 7. Forum ?? So much problems when trying to import data
7409
        // 8. Student publication - Works
7410
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
7411
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
7412
        if ($debug) {
7413
            echo $sql;
7414
        }
7415
        $res = Database::query($sql);
7416
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7417
            $id = $row['ref'];
7418
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7419
                    WHERE id = $id AND session_id = $origin_session_id AND c_id = $course_id";
7420
            if ($debug) {
7421
                var_dump($sql);
7422
            }
7423
            $sub_res = Database::query($sql);
7424
            if (Database::num_rows($sub_res) > 0) {
7425
                $data = Database::fetch_array($sub_res, 'ASSOC');
7426
                if ($debug) {
7427
                    var_dump($data);
7428
                }
7429
                $parent_id = $data['parent_id'];
7430
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
7431
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7432
                            WHERE id = $parent_id AND c_id = $course_id";
7433
                    $select_res = Database::query($sql);
7434
                    $parent_data = Database::fetch_array($select_res, 'ASSOC');
7435
                    if ($debug) {
7436
                        var_dump($parent_data);
7437
                    }
7438
7439
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
7440
                    $course_dir = $sys_course_path.$course_info['path'];
7441
                    $base_work_dir = $course_dir.'/work';
7442
7443
                    // Creating the parent folder in the session if does not exists already
7444
                    //@todo ugly fix
7445
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
7446
                    $search_this2 = $parent_data['url'];
7447
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7448
                            WHERE description like '%$search_this%' AND
7449
                                  url LIKE '%$search_this2%' AND
7450
                                  session_id = $new_session_id AND
7451
                                  c_id = $course_id
7452
                            ORDER BY id desc  LIMIT 1";
7453
                    if ($debug) {
7454
                        echo $sql;
7455
                    }
7456
                    $sub_res = Database::query($sql);
7457
                    $num_rows = Database::num_rows($sub_res);
7458
                    $new_parent_id = 0;
7459
                    if ($num_rows > 0) {
7460
                        $new_result = Database::fetch_array($sub_res, 'ASSOC');
7461
                        $created_dir = $new_result['url'];
7462
                        $new_parent_id = $new_result['id'];
7463
                    } else {
7464
                        if ($update_database) {
7465
                            $dir_name = substr($parent_data['url'], 1);
7466
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
7467
                            $created_dir = '/'.$created_dir;
7468
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
7469
                            // Creating directory
7470
                            $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
7471
                            $publication
7472
                                ->setUrl($created_dir)
7473
                                ->setCId($course_id)
7474
                                ->setTitle($parent_data['title'])
7475
                                ->setDescription(
7476
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
7477
                                )
7478
                                ->setActive(false)
7479
                                ->setAccepted(true)
7480
                                ->setPostGroupId(0)
7481
                                ->setHasProperties($parent_data['has_properties'])
7482
                                ->setWeight($parent_data['weight'])
7483
                                ->setContainsFile($parent_data['contains_file'])
7484
                                ->setFiletype('folder')
7485
                                ->setSentDate($now)
7486
                                ->setQualification($parent_data['qualification'])
7487
                                ->setParentId(0)
7488
                                ->setQualificatorId(0)
7489
                                ->setUserId($parent_data['user_id'])
7490
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
7491
                                ->setSession($session);
7492
7493
                            $publication->setDocumentId($parent_data['document_id']);
7494
7495
                            Database::getManager()->persist($publication);
7496
                            Database::getManager()->flush();
7497
                            $id = $publication->getIid();
7498
                            // Folder created
7499
                            api_item_property_update(
7500
                                $course_info,
7501
                                'work',
7502
                                $id,
7503
                                'DirectoryCreated',
7504
                                api_get_user_id(),
7505
                                null,
7506
                                null,
7507
                                null,
7508
                                null,
7509
                                $new_session_id
7510
                            );
7511
                            $new_parent_id = $id;
7512
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
7513
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
7514
                            }
7515
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
7516
                        }
7517
                    }
7518
7519
                    //Creating student_publication_assignment if exists
7520
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
7521
                            WHERE publication_id = $parent_id AND c_id = $course_id";
7522
                    if ($debug) {
7523
                        var_dump($sql);
7524
                    }
7525
                    $rest_select = Database::query($sql);
7526
                    if (Database::num_rows($rest_select) > 0) {
7527
                        if ($update_database && $new_parent_id) {
7528
                            $assignment_data = Database::fetch_array($rest_select, 'ASSOC');
7529
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
7530
                                c_id = '$course_id',
7531
                                expires_on = '".$assignment_data['expires_on']."',
7532
                                ends_on = '".$assignment_data['ends_on']."',
7533
                                add_to_calendar = '".$assignment_data['add_to_calendar']."',
7534
                                enable_qualification = '".$assignment_data['enable_qualification']."',
7535
                                publication_id = '".$new_parent_id."'";
7536
                            if ($debug) {
7537
                                echo $sql_add_publication;
7538
                            }
7539
                            Database::query($sql_add_publication);
7540
                            $id = (int) Database::insert_id();
7541
                            if ($id) {
7542
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
7543
                                    SET  has_properties = '".$id."',
7544
                                        view_properties = '1'
7545
                                    WHERE id = ".$new_parent_id;
7546
                                if ($debug) {
7547
                                    echo $sql_update;
7548
                                }
7549
                                Database::query($sql_update);
7550
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
7551
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
7552
                                }
7553
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
7554
                            }
7555
                        }
7556
                    }
7557
7558
                    $doc_url = $data['url'];
7559
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
7560
7561
                    if ($update_database) {
7562
                        // Creating a new work
7563
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
7564
                        $data['post_group_id'] = (int) $data['post_group_id'];
7565
                        $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
7566
                        $publication
7567
                            ->setUrl($new_url)
7568
                            ->setCId($course_id)
7569
                            ->setTitle($data['title'])
7570
                            ->setDescription($data['description'].' file moved')
7571
                            ->setActive($data['active'])
7572
                            ->setAccepted($data['accepted'])
7573
                            ->setPostGroupId($data['post_group_id'])
7574
                            ->setSentDate($data['sent_date'])
7575
                            ->setParentId($new_parent_id)
7576
                            ->setWeight($data['weight'])
7577
                            ->setHasProperties(0)
7578
                            ->setWeight($data['weight'])
7579
                            ->setContainsFile($data['contains_file'])
7580
                            ->setSession($session)
7581
                            ->setUserId($data['user_id'])
7582
                            ->setFiletype('file')
7583
                            ->setDocumentId(0)
7584
                        ;
7585
7586
                        $em->persist($publication);
7587
                        $em->flush();
7588
7589
                        $id = $publication->getIid();
7590
                        api_item_property_update(
7591
                            $course_info,
7592
                            'work',
7593
                            $id,
7594
                            'DocumentAdded',
7595
                            $user_id,
7596
                            null,
7597
                            null,
7598
                            null,
7599
                            null,
7600
                            $new_session_id
7601
                        );
7602
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
7603
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
7604
                        }
7605
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
7606
                        $full_file_name = $course_dir.'/'.$doc_url;
7607
                        $new_file = $course_dir.'/'.$new_url;
7608
7609
                        if (file_exists($full_file_name)) {
7610
                            // deleting old assignment
7611
                            $result = copy($full_file_name, $new_file);
7612
                            if ($result) {
7613
                                unlink($full_file_name);
7614
                                if (isset($data['id'])) {
7615
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
7616
                                    if ($debug) {
7617
                                        var_dump($sql);
7618
                                    }
7619
                                    Database::query($sql);
7620
                                }
7621
                                api_item_property_update(
7622
                                    $course_info,
7623
                                    'work',
7624
                                    $data['id'],
7625
                                    'DocumentDeleted',
7626
                                    api_get_user_id()
7627
                                );
7628
                            }
7629
                        }
7630
                    }
7631
                }
7632
            }
7633
        }
7634
7635
        //9. Survey   Pending
7636
        //10. Dropbox - not neccesary to move categories (no presence of session_id)
7637
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
7638
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
7639
        if ($debug) {
7640
            var_dump($sql);
7641
        }
7642
        $res = Database::query($sql);
7643
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7644
            $id = (int) $row['id'];
7645
            if ($update_database) {
7646
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id";
7647
                if ($debug) {
7648
                    var_dump($sql);
7649
                }
7650
                Database::query($sql);
7651
                if ($debug) {
7652
                    var_dump($res);
7653
                }
7654
7655
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
7656
                if ($debug) {
7657
                    var_dump($sql);
7658
                }
7659
                Database::query($sql);
7660
                if ($debug) {
7661
                    var_dump($res);
7662
                }
7663
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
7664
                    $result_message[$TBL_DROPBOX_FILE] = 0;
7665
                }
7666
                $result_message[$TBL_DROPBOX_FILE]++;
7667
            }
7668
        }
7669
7670
        // 11. Notebook
7671
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
7672
                WHERE
7673
                    user_id = $user_id AND
7674
                    session_id = $origin_session_id AND
7675
                    course = '$origin_course_code' AND
7676
                    c_id = $course_id";
7677
        if ($debug) {
7678
            var_dump($sql);
7679
        }
7680
        $res = Database::query($sql);
7681
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7682
            $id = $row['notebook_id'];
7683
            if ($update_database) {
7684
                $sql = "UPDATE $TBL_NOTEBOOK
7685
                        SET session_id = $new_session_id
7686
                        WHERE c_id = $course_id AND notebook_id = $id";
7687
                if ($debug) {
7688
                    var_dump($sql);
7689
                }
7690
                $res = Database::query($sql);
7691
                if ($debug) {
7692
                    var_dump($res);
7693
                }
7694
            }
7695
        }*/
7696
7697
        if ($update_database) {
7698
            echo Display::return_message(get_lang('StatsMoved'));
7699
            if (is_array($result_message)) {
7700
                foreach ($result_message as $table => $times) {
7701
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
7702
                }
7703
            }
7704
        } else {
7705
            echo '<h4>'.get_lang('UserInformationOfThisCourse').'</h4>';
7706
            echo '<br />';
7707
            echo '<table class="table" width="100%">';
7708
            echo '<tr>';
7709
            echo '<td width="50%" valign="top">';
7710
            if ($origin_session_id == 0) {
7711
                echo '<h5>'.get_lang('OriginCourse').'</h5>';
7712
            } else {
7713
                echo '<h5>'.get_lang('OriginSession').' #'.$origin_session_id.'</h5>';
7714
            }
7715
            self::compareUserData($result_message);
7716
            echo '</td>';
7717
            echo '<td width="50%" valign="top">';
7718
            if ($new_session_id == 0) {
7719
                echo '<h5>'.get_lang('DestinyCourse').'</h5>';
7720
            } else {
7721
                echo '<h5>'.get_lang('DestinySession').' #'.$new_session_id.'</h5>';
7722
            }
7723
            self::compareUserData($result_message_compare);
7724
            echo '</td>';
7725
            echo '</tr>';
7726
            echo '</table>';
7727
        }
7728
    }
7729
7730
    public static function compareUserData($result_message)
7731
    {
7732
        foreach ($result_message as $table => $data) {
7733
            $title = $table;
7734
            if ($table === 'TRACK_E_EXERCISES') {
7735
                $title = get_lang('Exercises');
7736
            } elseif ($table === 'TRACK_E_EXERCISES_IN_LP') {
7737
                $title = get_lang('ExercisesInLp');
7738
            } elseif ($table === 'LP_VIEW') {
7739
                $title = get_lang('LearningPaths');
7740
            }
7741
            echo '<br / ><h3>'.get_lang($title).' </h3><hr />';
7742
7743
            if (is_array($data)) {
7744
                foreach ($data as $id => $item) {
7745
                    if ($table === 'TRACK_E_EXERCISES' || $table === 'TRACK_E_EXERCISES_IN_LP') {
7746
                        echo "<br /><h3>".get_lang('Attempt')." #$id</h3>";
7747
                        echo '<h3>';
7748
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
7749
                        echo '</h3>';
7750
                        if (!empty($item['orig_lp_id'])) {
7751
                            echo '<h3>';
7752
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
7753
                            echo '</h3>';
7754
                        }
7755
                        // Process data.
7756
                        $array = [
7757
                            'exe_date' => get_lang('Date'),
7758
                            'exe_result' => get_lang('Score'),
7759
                            'exe_weighting' => get_lang('Weighting'),
7760
                        ];
7761
                        foreach ($item as $key => $value) {
7762
                            if (in_array($key, array_keys($array))) {
7763
                                $key = $array[$key];
7764
                                echo "$key =  $value <br />";
7765
                            }
7766
                        }
7767
                    } else {
7768
                        echo "<br /><h3>".get_lang('Id')." #$id</h3>";
7769
                        // process data
7770
                        foreach ($item as $key => $value) {
7771
                            echo "$key =  $value <br />";
7772
                        }
7773
                    }
7774
                }
7775
            } else {
7776
                echo get_lang('NoResults');
7777
            }
7778
        }
7779
    }
7780
7781
    public static function updateUserLastLogin($userId)
7782
    {
7783
        $tblTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7784
        $sql = "SELECT login_id, login_date
7785
            FROM $tblTrackLogin
7786
            WHERE
7787
                login_user_id='".$userId."'
7788
            ORDER BY login_date DESC
7789
            LIMIT 0,1";
7790
7791
        $qLastConnection = Database::query($sql);
7792
        if (Database::num_rows($qLastConnection) > 0) {
7793
            $now = api_get_utc_datetime();
7794
            $iIdLastConnection = Database::result($qLastConnection, 0, 'login_id');
7795
7796
            // is the latest logout_date still relevant?
7797
            $sql = "SELECT logout_date FROM $tblTrackLogin
7798
                WHERE login_id = $iIdLastConnection";
7799
            $qLogoutDate = Database::query($sql);
7800
            $resLogoutDate = convert_sql_date(Database::result($qLogoutDate, 0, 'logout_date'));
7801
            $lifeTime = api_get_configuration_value('session_lifetime');
7802
7803
            if ($resLogoutDate < time() - $lifeTime) {
7804
                // it isn't, we should create a fresh entry
7805
                Event::eventLogin($userId);
7806
            // now that it's created, we can get its ID and carry on
7807
            } else {
7808
                $sql = "UPDATE $tblTrackLogin SET logout_date = '$now'
7809
                    WHERE login_id = '$iIdLastConnection'";
7810
                Database::query($sql);
7811
            }
7812
7813
            $tableUser = Database::get_main_table(TABLE_MAIN_USER);
7814
            $sql = "UPDATE $tableUser SET last_login = '$now'
7815
                WHERE user_id = ".$userId;
7816
            Database::query($sql);
7817
        }
7818
    }
7819
7820
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
7821
    {
7822
        if (empty($sessionId)) {
7823
            $userList = CourseManager::get_user_list_from_course_code(
7824
                $courseInfo['code'],
7825
                $sessionId,
7826
                null,
7827
                null,
7828
                STUDENT
7829
            );
7830
        } else {
7831
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
7832
        }
7833
7834
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null);
7835
7836
        if (empty($exerciseList)) {
7837
            return Display::return_message(get_lang('NoEx'));
7838
        }
7839
7840
        $toGraphExerciseResult = [];
7841
7842
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
7843
        $quizzesTable->setHeaders(
7844
            [
7845
                get_lang('Title'),
7846
                get_lang('Attempts'),
7847
                get_lang('BestAttempt'),
7848
                get_lang('Ranking'),
7849
                get_lang('BestResultInCourse'),
7850
                get_lang('Statistics').Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent')),
7851
            ]
7852
        );
7853
7854
        $webCodePath = api_get_path(WEB_CODE_PATH);
7855
7856
        foreach ($exerciseList as $exercices) {
7857
            $objExercise = new Exercise($courseInfo['real_id']);
7858
            $objExercise->read($exercices['iid']);
7859
            $visibleReturn = $objExercise->is_visible();
7860
7861
            // Getting count of attempts by user
7862
            $attempts = Event::count_exercise_attempts_by_user(
7863
                api_get_user_id(),
7864
                $exercices['iid'],
7865
                $courseInfo['real_id'],
7866
                $sessionId
7867
            );
7868
7869
            $url = $webCodePath.'exercise/overview.php?'
7870
                .http_build_query(
7871
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['iid']]
7872
                );
7873
7874
            if ($visibleReturn['value'] == true) {
7875
                $exercices['title'] = Display::url(
7876
                    $exercices['title'],
7877
                    $url,
7878
                    ['target' => SESSION_LINK_TARGET]
7879
                );
7880
            } elseif ($exercices['active'] == -1) {
7881
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
7882
            }
7883
7884
            $quizData = [
7885
                $exercices['title'],
7886
                $attempts,
7887
                '-',
7888
                '-',
7889
                '-',
7890
                '-',
7891
            ];
7892
7893
            // Exercise configuration show results or show only score
7894
            if (!in_array($exercices['results_disabled'], [0, 2])
7895
                || empty($attempts)
7896
            ) {
7897
                $quizzesTable->addRow($quizData);
7898
7899
                continue;
7900
            }
7901
7902
            //For graphics
7903
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
7904
                $exercices['iid'],
7905
                $courseInfo['real_id'],
7906
                $sessionId
7907
            );
7908
7909
            $toGraphExerciseResult[$exercices['iid']] = [
7910
                'title' => $exercices['title'],
7911
                'data' => $bestExerciseAttempts,
7912
            ];
7913
7914
            // Getting best results
7915
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
7916
                $exercices['iid'],
7917
                $courseInfo['real_id'],
7918
                $sessionId
7919
            );
7920
7921
            if (!empty($bestScoreData)) {
7922
                $quizData[5] = ExerciseLib::show_score(
7923
                    $bestScoreData['exe_result'],
7924
                    $bestScoreData['exe_weighting']
7925
                );
7926
            }
7927
7928
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
7929
                api_get_user_id(),
7930
                $exercices['iid'],
7931
                $courseInfo['real_id'],
7932
                $sessionId
7933
            );
7934
7935
            if (!empty($exerciseAttempt)) {
7936
                // Always getting the BEST attempt
7937
                $score = $exerciseAttempt['exe_result'];
7938
                $weighting = $exerciseAttempt['exe_weighting'];
7939
                $exeId = $exerciseAttempt['exe_id'];
7940
7941
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
7942
                    .http_build_query(
7943
                        [
7944
                            'id' => $exeId,
7945
                            'cidReq' => $courseInfo['code'],
7946
                            'show_headers' => 1,
7947
                            'id_session' => $sessionId,
7948
                        ]
7949
                    );
7950
7951
                $quizData[3] = Display::url(
7952
                    ExerciseLib::show_score($score, $weighting),
7953
                    $latestAttemptUrl
7954
                );
7955
7956
                $myScore = !empty($weighting) && intval($weighting) != 0 ? $score / $weighting : 0;
7957
7958
                //@todo this function slows the page
7959
                if (is_int($userList)) {
7960
                    $userList = [$userList];
7961
                }
7962
7963
                $quizData[4] = ExerciseLib::get_exercise_result_ranking(
7964
                    $myScore,
7965
                    $exeId,
7966
                    $exercices['iid'],
7967
                    $courseInfo['code'],
7968
                    $sessionId,
7969
                    $userList
7970
                );
7971
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['iid']]);
7972
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['iid']]);
7973
7974
                $quizData[6] = Display::url(
7975
                    Display::img($graph, '', [], false),
7976
                    $normalGraph,
7977
                    ['id' => $exercices['iid'], 'class' => 'expand-image']
7978
                );
7979
            }
7980
7981
            $quizzesTable->addRow($quizData);
7982
        }
7983
7984
        return Display::page_subheader2(get_lang('Exercises'))
7985
            .Display::div(
7986
                $quizzesTable->toHtml(),
7987
                ['class' => 'table-responsive']
7988
            );
7989
    }
7990
7991
    private static function generateLearningPathsTable(
7992
        User $user,
7993
        array $courseInfo,
7994
        int $sessionId = 0,
7995
        bool $isAllowedToEdit = true
7996
    ): string {
7997
        $html = [];
7998
7999
        $columnHeaders = [
8000
            'lp' => get_lang('LearningPath'),
8001
            'time' => get_lang('LatencyTimeSpent'),
8002
            'progress' => get_lang('Progress'),
8003
            'score' => get_lang('Score'),
8004
            'best_score' => get_lang('BestScore'),
8005
            'last_connection' => get_lang('LastConnexion'),
8006
        ];
8007
8008
        $trackingColumns = api_get_configuration_value('tracking_columns');
8009
8010
        if (isset($trackingColumns['my_progress_lp'])) {
8011
            $columnHeaders = array_filter(
8012
                $columnHeaders,
8013
                function ($columHeader, $key) use ($trackingColumns) {
8014
                    if (!isset($trackingColumns['my_progress_lp'][$key])
8015
                        || $trackingColumns['my_progress_lp'][$key] == false
8016
                    ) {
8017
                        return false;
8018
                    }
8019
8020
                    return true;
8021
                },
8022
                ARRAY_FILTER_USE_BOTH
8023
            );
8024
        }
8025
8026
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
8027
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
8028
        }
8029
8030
        if (true === api_get_configuration_value('student_follow_page_add_LP_acquisition_info')) {
8031
            $columnHeaders['student_follow_page_add_LP_acquisition_info'] = get_lang('Acquisition');
8032
        }
8033
8034
        $addLpInvisibleCheckbox = api_get_configuration_value('student_follow_page_add_LP_invisible_checkbox');
8035
        $includeNotsubscribedLp = api_get_configuration_value('student_follow_page_include_not_subscribed_lp_students');
8036
8037
        $columnHeadersKeys = array_keys($columnHeaders);
8038
8039
        $categories = learnpath::getCategories($courseInfo['real_id'], true);
8040
        $countCategories = count($categories);
8041
8042
        $webCodePath = api_get_path(WEB_CODE_PATH);
8043
8044
        /** @var CLpCategory $category */
8045
        foreach ($categories as $category) {
8046
            // LP table results
8047
            $objLearnpathList = new LearnpathList(
8048
                $user->getId(),
8049
                $courseInfo,
8050
                $sessionId,
8051
                'lp.publicatedOn ASC',
8052
                true,
8053
                $category->getId(),
8054
                false,
8055
                false,
8056
                $includeNotsubscribedLp === false
8057
            );
8058
            $lpList = $objLearnpathList->get_flat_list();
8059
8060
            $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
8061
            $learningpathsTable->setHeaders($columnHeaders);
8062
8063
            foreach ($lpList as $lpId => $learnpath) {
8064
                $learningpathData = [];
8065
8066
                if (!$learnpath['lp_visibility']) {
8067
                    continue;
8068
                }
8069
8070
                if ($addLpInvisibleCheckbox) {
8071
                    if (!StudentFollowPage::isViewVisible($lpId, $user->getId(), $courseInfo['real_id'], $sessionId)) {
8072
                        continue;
8073
                    }
8074
                }
8075
8076
                $url = $webCodePath.'lp/lp_controller.php?'
8077
                    .http_build_query(
8078
                        ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
8079
                    );
8080
8081
                if (in_array('lp', $columnHeadersKeys)) {
8082
                    if ($learnpath['lp_visibility'] == 0) {
8083
                        $learningpathData[] = $learnpath['lp_name'];
8084
                    } else {
8085
                        $learningpathData[] = Display::url(
8086
                            $learnpath['lp_name'],
8087
                            $url,
8088
                            ['target' => SESSION_LINK_TARGET]
8089
                        );
8090
                    }
8091
                }
8092
8093
                if (in_array('time', $columnHeadersKeys)) {
8094
                    $time_spent_in_lp = self::get_time_spent_in_lp(
8095
                        $user->getId(),
8096
                        $courseInfo['code'],
8097
                        [$lpId],
8098
                        $sessionId
8099
                    );
8100
8101
                    $learningpathData[] = api_time_to_hms($time_spent_in_lp);
8102
                }
8103
8104
                if (in_array('progress', $columnHeadersKeys)) {
8105
                    $progress = self::get_avg_student_progress(
8106
                        $user->getId(),
8107
                        $courseInfo['code'],
8108
                        [$lpId],
8109
                        $sessionId
8110
                    );
8111
8112
                    if (is_numeric($progress)) {
8113
                        $progress = sprintf(get_lang('XPercent'), $progress);
8114
                    }
8115
8116
                    $learningpathData[] = $progress;
8117
                }
8118
8119
                if (in_array('score', $columnHeadersKeys)) {
8120
                    $percentage_score = self::get_avg_student_score(
8121
                        $user->getId(),
8122
                        $courseInfo['code'],
8123
                        [$lpId],
8124
                        $sessionId
8125
                    );
8126
8127
                    if (is_numeric($percentage_score)) {
8128
                        $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
8129
                    } else {
8130
                        $percentage_score = sprintf(get_lang('XPercent'), 0);
8131
                    }
8132
8133
                    $learningpathData[] = $percentage_score;
8134
                }
8135
8136
                if (in_array('best_score', $columnHeadersKeys)) {
8137
                    $bestScore = self::get_avg_student_score(
8138
                        $user->getId(),
8139
                        $courseInfo['code'],
8140
                        [$lpId],
8141
                        $sessionId,
8142
                        false,
8143
                        false,
8144
                        true
8145
                    );
8146
8147
                    if (is_numeric($bestScore)) {
8148
                        $bestScore = sprintf(get_lang('XPercent'), $bestScore);
8149
                    } else {
8150
                        $bestScore = '-';
8151
                    }
8152
8153
                    $learningpathData[] = $bestScore;
8154
                }
8155
8156
                if (in_array('last_connection', $columnHeadersKeys)) {
8157
                    $lastConnectionInLp = self::get_last_connection_time_in_lp(
8158
                        $user->getId(),
8159
                        $courseInfo['code'],
8160
                        $lpId,
8161
                        $sessionId
8162
                    );
8163
8164
                    $lastConnection = '-';
8165
8166
                    if (!empty($lastConnectionInLp)) {
8167
                        $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
8168
                    }
8169
8170
                    $learningpathData[] = $lastConnection;
8171
                }
8172
8173
                if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
8174
                    $learningpathData[] = StudentFollowPage::getLpSubscription(
8175
                        $learnpath,
8176
                        $user->getId(),
8177
                        $courseInfo['real_id'],
8178
                        $sessionId,
8179
                        $isAllowedToEdit
8180
                    );
8181
                }
8182
8183
                if (in_array('student_follow_page_add_LP_acquisition_info', $columnHeadersKeys)) {
8184
                    $learningpathData[] = StudentFollowPage::getLpAcquisition(
8185
                        $learnpath,
8186
                        $user->getId(),
8187
                        $courseInfo['real_id'],
8188
                        $sessionId
8189
                    );
8190
                }
8191
8192
                $learningpathsTable->addRow($learningpathData);
8193
            }
8194
8195
            if ($learningpathsTable->getRowCount() < 2) {
8196
                continue;
8197
            }
8198
8199
            if ($countCategories > 1) {
8200
                $html[] = Display::tag('h5', $category->getName());
8201
            }
8202
8203
            $html[] = Display::div(
8204
                $learningpathsTable->toHtml(),
8205
                ['class' => 'table-responsive']
8206
            );
8207
        }
8208
8209
        return implode(PHP_EOL, $html);
8210
    }
8211
}
8212
8213
/**
8214
 * @todo move into a proper file
8215
 */
8216
class TrackingCourseLog
8217
{
8218
    /**
8219
     * @return mixed
8220
     */
8221
    public static function count_item_resources()
8222
    {
8223
        $session_id = api_get_session_id();
8224
        $course_id = api_get_course_int_id();
8225
8226
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
8227
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8228
8229
        $sql = "SELECT count(tool) AS total_number_of_items
8230
                FROM $table_item_property track_resource, $table_user user
8231
                WHERE
8232
                    track_resource.c_id = $course_id AND
8233
                    track_resource.insert_user_id = user.user_id AND
8234
                    session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
8235
8236
        if (isset($_GET['keyword'])) {
8237
            $keyword = Database::escape_string(trim($_GET['keyword']));
8238
            $sql .= " AND (
8239
                        user.username LIKE '%".$keyword."%' OR
8240
                        lastedit_type LIKE '%".$keyword."%' OR
8241
                        tool LIKE '%".$keyword."%'
8242
                    )";
8243
        }
8244
8245
        $sql .= " AND tool IN (
8246
                    'document',
8247
                    'learnpath',
8248
                    'quiz',
8249
                    'glossary',
8250
                    'link',
8251
                    'course_description',
8252
                    'announcement',
8253
                    'thematic',
8254
                    'thematic_advance',
8255
                    'thematic_plan'
8256
                )";
8257
        $res = Database::query($sql);
8258
        $obj = Database::fetch_object($res);
8259
8260
        return $obj->total_number_of_items;
8261
    }
8262
8263
    /**
8264
     * @param $from
8265
     * @param $number_of_items
8266
     * @param $column
8267
     * @param $direction
8268
     *
8269
     * @return array
8270
     */
8271
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
8272
    {
8273
        $session_id = api_get_session_id();
8274
        $course_id = api_get_course_int_id();
8275
8276
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
8277
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8278
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
8279
        $column = (int) $column;
8280
        $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
8281
8282
        $sql = "SELECT
8283
                    tool as col0,
8284
                    lastedit_type as col1,
8285
                    ref as ref,
8286
                    user.username as col3,
8287
                    insert_date as col6,
8288
                    visibility as col7,
8289
                    user.user_id as user_id
8290
                FROM $table_item_property track_resource, $table_user user
8291
                WHERE
8292
                  track_resource.c_id = $course_id AND
8293
                  track_resource.insert_user_id = user.user_id AND
8294
                  session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
8295
8296
        if (isset($_GET['keyword'])) {
8297
            $keyword = Database::escape_string(trim($_GET['keyword']));
8298
            $sql .= " AND (
8299
                        user.username LIKE '%".$keyword."%' OR
8300
                        lastedit_type LIKE '%".$keyword."%' OR
8301
                        tool LIKE '%".$keyword."%'
8302
                     ) ";
8303
        }
8304
8305
        $sql .= " AND tool IN (
8306
                    'document',
8307
                    'learnpath',
8308
                    'quiz',
8309
                    'glossary',
8310
                    'link',
8311
                    'course_description',
8312
                    'announcement',
8313
                    'thematic',
8314
                    'thematic_advance',
8315
                    'thematic_plan'
8316
                )";
8317
8318
        if ($column == 0) {
8319
            $column = '0';
8320
        }
8321
        if ($column != '' && $direction != '') {
8322
            if ($column != 2 && $column != 4) {
8323
                $sql .= " ORDER BY col$column $direction";
8324
            }
8325
        } else {
8326
            $sql .= " ORDER BY col6 DESC ";
8327
        }
8328
8329
        $from = intval($from);
8330
        if ($from) {
8331
            $number_of_items = intval($number_of_items);
8332
            $sql .= " LIMIT $from, $number_of_items ";
8333
        }
8334
8335
        $res = Database::query($sql);
8336
        $resources = [];
8337
        $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan'];
8338
        while ($row = Database::fetch_array($res)) {
8339
            $ref = $row['ref'];
8340
            $table_name = self::get_tool_name_table($row['col0']);
8341
            $table_tool = Database::get_course_table($table_name['table_name']);
8342
8343
            $id = $table_name['id_tool'];
8344
            $recorset = false;
8345
8346
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
8347
                $tbl_thematic = Database::get_course_table(TABLE_THEMATIC);
8348
                $sql = "SELECT thematic_id FROM $table_tool
8349
                        WHERE c_id = $course_id AND id = $ref";
8350
                $rs_thematic = Database::query($sql);
8351
                if (Database::num_rows($rs_thematic)) {
8352
                    $row_thematic = Database::fetch_array($rs_thematic);
8353
                    $thematic_id = $row_thematic['thematic_id'];
8354
8355
                    $sql = "SELECT session.id, session.name, user.username
8356
                            FROM $tbl_thematic t, $table_session session, $table_user user
8357
                            WHERE
8358
                              t.c_id = $course_id AND
8359
                              t.session_id = session.id AND
8360
                              session.id_coach = user.user_id AND
8361
                              t.id = $thematic_id";
8362
                    $recorset = Database::query($sql);
8363
                }
8364
            } else {
8365
                $sql = "SELECT session.id, session.name, user.username
8366
                          FROM $table_tool tool, $table_session session, $table_user user
8367
                          WHERE
8368
                              tool.c_id = $course_id AND
8369
                              tool.session_id = session.id AND
8370
                              session.id_coach = user.user_id AND
8371
                              tool.$id = $ref";
8372
                $recorset = Database::query($sql);
8373
            }
8374
8375
            if (!empty($recorset)) {
8376
                $obj = Database::fetch_object($recorset);
8377
8378
                $name_session = '';
8379
                $coach_name = '';
8380
                if (!empty($obj)) {
8381
                    $name_session = $obj->name;
8382
                    $coach_name = $obj->username;
8383
                }
8384
8385
                $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
8386
                $row[0] = '';
8387
                if ($row['col6'] != 2) {
8388
                    if (in_array($row['col0'], $thematic_tools)) {
8389
                        $exp_thematic_tool = explode('_', $row['col0']);
8390
                        $thematic_tool_title = '';
8391
                        if (is_array($exp_thematic_tool)) {
8392
                            foreach ($exp_thematic_tool as $exp) {
8393
                                $thematic_tool_title .= api_ucfirst($exp);
8394
                            }
8395
                        } else {
8396
                            $thematic_tool_title = api_ucfirst($row['col0']);
8397
                        }
8398
8399
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
8400
                    } else {
8401
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
8402
                    }
8403
                } else {
8404
                    $row[0] = api_ucfirst($row['col0']);
8405
                }
8406
                $row[1] = get_lang($row[1]);
8407
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
8408
                $row[5] = '';
8409
                //@todo Improve this code please
8410
                switch ($table_name['table_name']) {
8411
                    case 'document':
8412
                        $sql = "SELECT tool.title as title FROM $table_tool tool
8413
                                WHERE c_id = $course_id AND id = $ref";
8414
                        $rs_document = Database::query($sql);
8415
                        $obj_document = Database::fetch_object($rs_document);
8416
                        if ($obj_document) {
8417
                            $row[5] = $obj_document->title;
8418
                        }
8419
                        break;
8420
                    case 'announcement':
8421
                        $sql = "SELECT title FROM $table_tool
8422
                                WHERE c_id = $course_id AND id = $ref";
8423
                        $rs_document = Database::query($sql);
8424
                        $obj_document = Database::fetch_object($rs_document);
8425
                        if ($obj_document) {
8426
                            $row[5] = $obj_document->title;
8427
                        }
8428
                        break;
8429
                    case 'glossary':
8430
                        $sql = "SELECT name FROM $table_tool
8431
                                WHERE c_id = $course_id AND glossary_id = $ref";
8432
                        $rs_document = Database::query($sql);
8433
                        $obj_document = Database::fetch_object($rs_document);
8434
                        if ($obj_document) {
8435
                            $row[5] = $obj_document->name;
8436
                        }
8437
                        break;
8438
                    case 'lp':
8439
                        $sql = "SELECT name
8440
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
8441
                        $rs_document = Database::query($sql);
8442
                        $obj_document = Database::fetch_object($rs_document);
8443
                        $row[5] = $obj_document->name;
8444
                        break;
8445
                    case 'quiz':
8446
                        $sql = "SELECT title FROM $table_tool
8447
                                WHERE c_id = $course_id AND id = $ref";
8448
                        $rs_document = Database::query($sql);
8449
                        $obj_document = Database::fetch_object($rs_document);
8450
                        if ($obj_document) {
8451
                            $row[5] = $obj_document->title;
8452
                        }
8453
                        break;
8454
                    case 'course_description':
8455
                        $sql = "SELECT title FROM $table_tool
8456
                                WHERE c_id = $course_id AND id = $ref";
8457
                        $rs_document = Database::query($sql);
8458
                        $obj_document = Database::fetch_object($rs_document);
8459
                        if ($obj_document) {
8460
                            $row[5] = $obj_document->title;
8461
                        }
8462
                        break;
8463
                    case 'thematic':
8464
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
8465
                        if (Database::num_rows($rs) > 0) {
8466
                            $obj = Database::fetch_object($rs);
8467
                            if ($obj) {
8468
                                $row[5] = $obj->title;
8469
                            }
8470
                        }
8471
                        break;
8472
                    case 'thematic_advance':
8473
                        $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
8474
                        if (Database::num_rows($rs) > 0) {
8475
                            $obj = Database::fetch_object($rs);
8476
                            if ($obj) {
8477
                                $row[5] = $obj->content;
8478
                            }
8479
                        }
8480
                        break;
8481
                    case 'thematic_plan':
8482
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
8483
                        if (Database::num_rows($rs) > 0) {
8484
                            $obj = Database::fetch_object($rs);
8485
                            if ($obj) {
8486
                                $row[5] = $obj->title;
8487
                            }
8488
                        }
8489
                        break;
8490
                    default:
8491
                        break;
8492
                }
8493
8494
                $row2 = $name_session;
8495
                if (!empty($coach_name)) {
8496
                    $row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
8497
                }
8498
                $row[2] = $row2;
8499
                if (!empty($row['col3'])) {
8500
                    $userInfo = api_get_user_info($row['user_id']);
8501
                    $row['col3'] = Display::url(
8502
                        $row['col3'],
8503
                        $userInfo['profile_url']
8504
                    );
8505
                    $row[3] = $row['col3'];
8506
8507
                    $ip = Tracking::get_ip_from_user_event(
8508
                        $row['user_id'],
8509
                        $row['col6'],
8510
                        true
8511
                    );
8512
                    if (empty($ip)) {
8513
                        $ip = get_lang('Unknown');
8514
                    }
8515
                    $row[4] = $ip;
8516
                }
8517
8518
                $resources[] = $row;
8519
            }
8520
        }
8521
8522
        return $resources;
8523
    }
8524
8525
    /**
8526
     * @param string $tool
8527
     *
8528
     * @return array
8529
     */
8530
    public static function get_tool_name_table($tool)
8531
    {
8532
        switch ($tool) {
8533
            case 'document':
8534
                $table_name = TABLE_DOCUMENT;
8535
                $link_tool = 'document/document.php';
8536
                $id_tool = 'id';
8537
                break;
8538
            case 'learnpath':
8539
                $table_name = TABLE_LP_MAIN;
8540
                $link_tool = 'lp/lp_controller.php';
8541
                $id_tool = 'id';
8542
                break;
8543
            case 'quiz':
8544
                $table_name = TABLE_QUIZ_TEST;
8545
                $link_tool = 'exercise/exercise.php';
8546
                $id_tool = 'iid';
8547
                break;
8548
            case 'glossary':
8549
                $table_name = TABLE_GLOSSARY;
8550
                $link_tool = 'glossary/index.php';
8551
                $id_tool = 'glossary_id';
8552
                break;
8553
            case 'link':
8554
                $table_name = TABLE_LINK;
8555
                $link_tool = 'link/link.php';
8556
                $id_tool = 'id';
8557
                break;
8558
            case 'course_description':
8559
                $table_name = TABLE_COURSE_DESCRIPTION;
8560
                $link_tool = 'course_description/';
8561
                $id_tool = 'id';
8562
                break;
8563
            case 'announcement':
8564
                $table_name = TABLE_ANNOUNCEMENT;
8565
                $link_tool = 'announcements/announcements.php';
8566
                $id_tool = 'id';
8567
                break;
8568
            case 'thematic':
8569
                $table_name = TABLE_THEMATIC;
8570
                $link_tool = 'course_progress/index.php';
8571
                $id_tool = 'id';
8572
                break;
8573
            case 'thematic_advance':
8574
                $table_name = TABLE_THEMATIC_ADVANCE;
8575
                $link_tool = 'course_progress/index.php';
8576
                $id_tool = 'id';
8577
                break;
8578
            case 'thematic_plan':
8579
                $table_name = TABLE_THEMATIC_PLAN;
8580
                $link_tool = 'course_progress/index.php';
8581
                $id_tool = 'id';
8582
                break;
8583
            default:
8584
                $table_name = $tool;
8585
            break;
8586
        }
8587
8588
        return [
8589
            'table_name' => $table_name,
8590
            'link_tool' => $link_tool,
8591
            'id_tool' => $id_tool,
8592
        ];
8593
    }
8594
8595
    /**
8596
     * @param array $exclude Extra fields to be skipped, by ID
8597
     *
8598
     * @return string
8599
     */
8600
    public static function display_additional_profile_fields($exclude = [])
8601
    {
8602
        // getting all the extra profile fields that are defined by the platform administrator
8603
        $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
8604
8605
        // creating the form
8606
        $return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
8607
8608
        // the select field with the additional user profile fields (= this is where we select the field of which we want to see
8609
        // the information the users have entered or selected.
8610
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
8611
        $return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
8612
        $extra_fields_to_show = 0;
8613
        foreach ($extra_fields as $key => $field) {
8614
            // exclude extra profile fields by id
8615
            if (in_array($field[3], $exclude)) {
8616
                continue;
8617
            }
8618
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
8619
            if ($field[6] == 1 && $field[8] == 1) {
8620
                if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field']) {
8621
                    $selected = 'selected="selected"';
8622
                } else {
8623
                    $selected = '';
8624
                }
8625
                $extra_fields_to_show++;
8626
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
8627
            }
8628
        }
8629
        $return .= '</select>';
8630
8631
        // the form elements for the $_GET parameters (because the form is passed through GET
8632
        foreach ($_GET as $key => $value) {
8633
            if ($key != 'additional_profile_field') {
8634
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
8635
            }
8636
        }
8637
        // the submit button
8638
        $return .= '<button class="save" type="submit">'.get_lang('AddAdditionalProfileField').'</button>';
8639
        $return .= '</form>';
8640
        if ($extra_fields_to_show > 0) {
8641
            return $return;
8642
        } else {
8643
            return '';
8644
        }
8645
    }
8646
8647
    /**
8648
     * This function gets all the information of a certrain ($field_id)
8649
     * additional profile field for a specific list of users is more efficent
8650
     * than get_addtional_profile_information_of_field() function
8651
     * It gets the information of all the users so that it can be displayed
8652
     * in the sortable table or in the csv or xls export.
8653
     *
8654
     * @author    Julio Montoya <[email protected]>
8655
     *
8656
     * @param    int field id
8657
     * @param    array list of user ids
8658
     *
8659
     * @return array
8660
     *
8661
     * @since    Nov 2009
8662
     *
8663
     * @version    1.8.6.2
8664
     */
8665
    public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users)
8666
    {
8667
        // Database table definition
8668
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8669
        $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
8670
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
8671
        $result_extra_field = UserManager::get_extra_field_information($field_id);
8672
        $return = [];
8673
        if (!empty($users)) {
8674
            if ($result_extra_field['field_type'] == UserManager::USER_FIELD_TYPE_TAG) {
8675
                foreach ($users as $user_id) {
8676
                    $user_result = UserManager::get_user_tags($user_id, $field_id);
8677
                    $tag_list = [];
8678
                    foreach ($user_result as $item) {
8679
                        $tag_list[] = $item['tag'];
8680
                    }
8681
                    $return[$user_id][] = implode(', ', $tag_list);
8682
                }
8683
            } else {
8684
                $new_user_array = [];
8685
                foreach ($users as $user_id) {
8686
                    $new_user_array[] = "'".$user_id."'";
8687
                }
8688
                $users = implode(',', $new_user_array);
8689
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
8690
                // Selecting only the necessary information NOT ALL the user list
8691
                $sql = "SELECT user.user_id, v.value
8692
                        FROM $table_user user
8693
                        INNER JOIN $table_user_field_values v
8694
                        ON (user.user_id = v.item_id)
8695
                        INNER JOIN $extraField f
8696
                        ON (f.id = v.field_id)
8697
                        WHERE
8698
                            f.extra_field_type = $extraFieldType AND
8699
                            v.field_id=".intval($field_id)." AND
8700
                            user.user_id IN ($users)";
8701
8702
                $result = Database::query($sql);
8703
                while ($row = Database::fetch_array($result)) {
8704
                    // get option value for field type double select by id
8705
                    if (!empty($row['value'])) {
8706
                        if ($result_extra_field['field_type'] ==
8707
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
8708
                        ) {
8709
                            $id_double_select = explode(';', $row['value']);
8710
                            if (is_array($id_double_select)) {
8711
                                $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
8712
                                $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
8713
                                $row['value'] = ($value1.';'.$value2);
8714
                            }
8715
                        }
8716
8717
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) {
8718
                            $parsedValue = explode('::', $row['value']);
8719
8720
                            if ($parsedValue) {
8721
                                $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text'];
8722
                                $value2 = $parsedValue[1];
8723
8724
                                $row['value'] = "$value1: $value2";
8725
                            }
8726
                        }
8727
8728
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) {
8729
                            [$level1, $level2, $level3] = explode(';', $row['value']);
8730
8731
                            $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / ';
8732
                            $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / ';
8733
                            $row['value'] .= $result_extra_field['options'][$level3]['display_text'];
8734
                        }
8735
                    }
8736
                    // get other value from extra field
8737
                    $return[$row['user_id']][] = $row['value'];
8738
                }
8739
            }
8740
        }
8741
8742
        return $return;
8743
    }
8744
8745
    /**
8746
     * count the number of students in this course (used for SortableTable)
8747
     * Deprecated.
8748
     */
8749
    public function count_student_in_course()
8750
    {
8751
        global $nbStudents;
8752
8753
        return $nbStudents;
8754
    }
8755
8756
    public function sort_users($a, $b)
8757
    {
8758
        $tracking = Session::read('tracking_column');
8759
8760
        return strcmp(
8761
            trim(api_strtolower($a[$tracking])),
8762
            trim(api_strtolower($b[$tracking]))
8763
        );
8764
    }
8765
8766
    public function sort_users_desc($a, $b)
8767
    {
8768
        $tracking = Session::read('tracking_column');
8769
8770
        return strcmp(
8771
            trim(api_strtolower($b[$tracking])),
8772
            trim(api_strtolower($a[$tracking]))
8773
        );
8774
    }
8775
8776
    /**
8777
     * Get number of users for sortable with pagination.
8778
     *
8779
     * @return int
8780
     */
8781
    public static function get_number_of_users($conditions)
8782
    {
8783
        $conditions['get_count'] = true;
8784
8785
        return self::get_user_data(null, null, null, null, $conditions);
8786
    }
8787
8788
    /**
8789
     * Get data for users list in sortable with pagination.
8790
     *
8791
     * @param int $from
8792
     * @param int $number_of_items
8793
     * @param $column
8794
     * @param $direction
8795
     * @param $conditions
8796
     *
8797
     * @return array
8798
     */
8799
    public static function get_user_data(
8800
        $from,
8801
        $number_of_items,
8802
        $column,
8803
        $direction,
8804
        $conditions = []
8805
    ) {
8806
        global $user_ids, $course_code, $export_csv, $session_id;
8807
        $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users
8808
        $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false;
8809
8810
        $csv_content = [];
8811
        $course_code = $course_code ? Database::escape_string($course_code) : api_get_course_id();
8812
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
8813
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8814
        $access_url_id = api_get_current_access_url_id();
8815
8816
        // get all users data from a course for sortable with limit
8817
        if (is_array($user_ids)) {
8818
            $user_ids = array_map('intval', $user_ids);
8819
            $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") ";
8820
        } else {
8821
            $user_ids = (int) $user_ids;
8822
            $condition_user = " WHERE user.id = $user_ids ";
8823
        }
8824
8825
        if (!empty($_GET['user_keyword'])) {
8826
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
8827
            $condition_user .= " AND (
8828
                user.firstname LIKE '%".$keyword."%' OR
8829
                user.lastname LIKE '%".$keyword."%'  OR
8830
                user.username LIKE '%".$keyword."%'  OR
8831
                user.email LIKE '%".$keyword."%'
8832
             ) ";
8833
        }
8834
8835
        $url_table = '';
8836
        $url_condition = '';
8837
        if (api_is_multiple_url_enabled()) {
8838
            $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)";
8839
            $url_condition = " AND access_url_id = '$access_url_id'";
8840
        }
8841
8842
        $invitedUsersCondition = '';
8843
        if (!$includeInvitedUsers) {
8844
            $invitedUsersCondition = " AND user.status != ".INVITEE;
8845
        }
8846
8847
        $select = '
8848
                SELECT user.id as user_id,
8849
                    user.official_code  as col0,
8850
                    user.lastname       as col1,
8851
                    user.firstname      as col2,
8852
                    user.username       as col3,
8853
                    user.email          as col4';
8854
        if ($getCount) {
8855
            $select = ' SELECT COUNT(distinct(user.id)) as count ';
8856
        }
8857
8858
        $sqlInjectJoins = '';
8859
        $where = 'AND 1 = 1 ';
8860
        $sqlInjectWhere = '';
8861
        if (!empty($conditions)) {
8862
            if (isset($conditions['inject_joins'])) {
8863
                $sqlInjectJoins = $conditions['inject_joins'];
8864
            }
8865
            if (isset($conditions['where'])) {
8866
                $where = $conditions['where'];
8867
            }
8868
            if (isset($conditions['inject_where'])) {
8869
                $sqlInjectWhere = $conditions['inject_where'];
8870
            }
8871
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
8872
            $injectExtraFields = rtrim($injectExtraFields, ', ');
8873
            if (false === $getCount) {
8874
                $select .= " , $injectExtraFields";
8875
            }
8876
        }
8877
8878
        $sql = "$select
8879
                FROM $tbl_user as user
8880
                $url_table
8881
                $sqlInjectJoins
8882
                $condition_user
8883
                $url_condition
8884
                $invitedUsersCondition
8885
                $where
8886
                $sqlInjectWhere
8887
                ";
8888
8889
        if (!in_array($direction, ['ASC', 'DESC'])) {
8890
            $direction = 'ASC';
8891
        }
8892
8893
        $column = (int) $column;
8894
        $from = (int) $from;
8895
        $number_of_items = (int) $number_of_items;
8896
8897
        if ($getCount) {
8898
            $res = Database::query($sql);
8899
            $row = Database::fetch_array($res);
8900
8901
            return $row['count'];
8902
        }
8903
8904
        $sql .= " ORDER BY col$column $direction ";
8905
        $sql .= " LIMIT $from, $number_of_items";
8906
8907
        $res = Database::query($sql);
8908
        $users = [];
8909
8910
        $courseInfo = api_get_course_info($course_code);
8911
        $courseId = $courseInfo['real_id'];
8912
        $courseCode = $courseInfo['code'];
8913
8914
        $total_surveys = 0;
8915
        $total_exercises = ExerciseLib::get_all_exercises(
8916
            $courseInfo,
8917
            $session_id,
8918
            false,
8919
            null,
8920
            false,
8921
            3
8922
        );
8923
8924
        if (empty($session_id)) {
8925
            $survey_user_list = [];
8926
            $surveyList = SurveyManager::get_surveys($course_code, $session_id);
8927
            if ($surveyList) {
8928
                $total_surveys = count($surveyList);
8929
                foreach ($surveyList as $survey) {
8930
                    $user_list = SurveyManager::get_people_who_filled_survey(
8931
                        $survey['survey_id'],
8932
                        false,
8933
                        $courseId
8934
                    );
8935
8936
                    foreach ($user_list as $user_id) {
8937
                        isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
8938
                    }
8939
                }
8940
            }
8941
        }
8942
8943
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode.
8944
            '&course='.$course_code.'&origin=tracking_course&id_session='.$session_id;
8945
8946
        $sortByFirstName = api_sort_by_first_name();
8947
        Session::write('user_id_list', []);
8948
        $userIdList = [];
8949
8950
        $addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report');
8951
        $exerciseResultsToCheck = [];
8952
        if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) &&
8953
            isset($addExerciseOption['courses'][$courseCode])
8954
        ) {
8955
            foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) {
8956
                $exercise = new Exercise();
8957
                $exercise->read($exerciseId);
8958
                if ($exercise->iId) {
8959
                    $exerciseResultsToCheck[] = $exercise;
8960
                }
8961
            }
8962
        }
8963
8964
        while ($user = Database::fetch_array($res, 'ASSOC')) {
8965
            $userIdList[] = $user['user_id'];
8966
            $user['official_code'] = $user['col0'];
8967
            $user['username'] = $user['col3'];
8968
            $user['time'] = api_time_to_hms(
8969
                Tracking::get_time_spent_on_the_course(
8970
                    $user['user_id'],
8971
                    $courseId,
8972
                    $session_id
8973
                )
8974
            );
8975
8976
            $avg_student_score = Tracking::get_avg_student_score(
8977
                $user['user_id'],
8978
                $course_code,
8979
                [],
8980
                $session_id
8981
            );
8982
8983
            $averageBestScore = Tracking::get_avg_student_score(
8984
                $user['user_id'],
8985
                $course_code,
8986
                [],
8987
                $session_id,
8988
                false,
8989
                false,
8990
                true
8991
            );
8992
8993
            $avg_student_progress = Tracking::get_avg_student_progress(
8994
                $user['user_id'],
8995
                $course_code,
8996
                [],
8997
                $session_id
8998
            );
8999
9000
            if (empty($avg_student_progress)) {
9001
                $avg_student_progress = 0;
9002
            }
9003
            $user['average_progress'] = $avg_student_progress.'%';
9004
9005
            $total_user_exercise = Tracking::get_exercise_student_progress(
9006
                $total_exercises,
9007
                $user['user_id'],
9008
                $courseId,
9009
                $session_id
9010
            );
9011
9012
            $user['exercise_progress'] = $total_user_exercise;
9013
9014
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
9015
                $total_exercises,
9016
                $user['user_id'],
9017
                $courseId,
9018
                $session_id
9019
            );
9020
9021
            $user['exercise_average_best_attempt'] = $total_user_exercise;
9022
9023
            if (is_numeric($avg_student_score)) {
9024
                $user['student_score'] = $avg_student_score.'%';
9025
            } else {
9026
                $user['student_score'] = $avg_student_score;
9027
            }
9028
9029
            if (is_numeric($averageBestScore)) {
9030
                $user['student_score_best'] = $averageBestScore.'%';
9031
            } else {
9032
                $user['student_score_best'] = $averageBestScore;
9033
            }
9034
9035
            $exerciseResults = [];
9036
            if (!empty($exerciseResultsToCheck)) {
9037
                foreach ($exerciseResultsToCheck as $exercise) {
9038
                    $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user(
9039
                        $user['user_id'],
9040
                        $exercise->iId,
9041
                        $courseId,
9042
                        $session_id,
9043
                        false
9044
                    );
9045
9046
                    $best = null;
9047
                    if ($bestExerciseResult) {
9048
                        $best = $bestExerciseResult['exe_result'] / $bestExerciseResult['exe_weighting'];
9049
                        $best = round($best, 2) * 100;
9050
                        $best .= '%';
9051
                    }
9052
                    $exerciseResults['exercise_'.$exercise->iId] = $best;
9053
                }
9054
            }
9055
9056
            $user['count_assignments'] = Tracking::count_student_assignments(
9057
                $user['user_id'],
9058
                $course_code,
9059
                $session_id
9060
            );
9061
            $user['count_messages'] = Tracking::count_student_messages(
9062
                $user['user_id'],
9063
                $course_code,
9064
                $session_id
9065
            );
9066
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
9067
                $user['user_id'],
9068
                $courseId,
9069
                $session_id,
9070
                false === $export_csv
9071
            );
9072
9073
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
9074
                $user['user_id'],
9075
                $courseInfo,
9076
                $session_id,
9077
                false === $export_csv
9078
            );
9079
9080
            if ($export_csv) {
9081
                if (!empty($user['first_connection'])) {
9082
                    $user['first_connection'] = api_get_local_time($user['first_connection']);
9083
                } else {
9084
                    $user['first_connection'] = '-';
9085
                }
9086
                if (!empty($user['last_connection'])) {
9087
                    $user['last_connection'] = api_get_local_time($user['last_connection']);
9088
                } else {
9089
                    $user['last_connection'] = '-';
9090
                }
9091
            }
9092
9093
            if (empty($session_id)) {
9094
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys;
9095
            }
9096
9097
            $url = $urlBase.'&student='.$user['user_id'];
9098
9099
            $user['link'] = '<center><a href="'.$url.'">
9100
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
9101
                             </a></center>';
9102
9103
            // store columns in array $users
9104
            $user_row = [];
9105
            $user_row['official_code'] = $user['official_code']; //0
9106
            if ($sortByFirstName) {
9107
                $user_row['firstname'] = $user['col2'];
9108
                $user_row['lastname'] = $user['col1'];
9109
            } else {
9110
                $user_row['lastname'] = $user['col1'];
9111
                $user_row['firstname'] = $user['col2'];
9112
            }
9113
            $user_row['username'] = $user['username'];
9114
            $user_row['time'] = $user['time'];
9115
            $user_row['average_progress'] = $user['average_progress'];
9116
            $user_row['exercise_progress'] = $user['exercise_progress'];
9117
            $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
9118
            $user_row['student_score'] = $user['student_score'];
9119
            $user_row['student_score_best'] = $user['student_score_best'];
9120
            if (!empty($exerciseResults)) {
9121
                foreach ($exerciseResults as $exerciseId => $bestResult) {
9122
                    $user_row[$exerciseId] = $bestResult;
9123
                }
9124
            }
9125
9126
            $user_row['count_assignments'] = $user['count_assignments'];
9127
            $user_row['count_messages'] = $user['count_messages'];
9128
9129
            $userGroupManager = new UserGroup();
9130
            if ($export_csv) {
9131
                $user_row['classes'] = implode(
9132
                    ',',
9133
                    $userGroupManager->getNameListByUser($user['user_id'], UserGroup::NORMAL_CLASS)
9134
                );
9135
            } else {
9136
                $user_row['classes'] = $userGroupManager->getLabelsFromNameList(
9137
                    $user['user_id'],
9138
                    UserGroup::NORMAL_CLASS
9139
                );
9140
            }
9141
9142
            if (empty($session_id)) {
9143
                $user_row['survey'] = $user['survey'];
9144
            } else {
9145
                $userSession = SessionManager::getUserSession($user['user_id'], $session_id);
9146
                $user_row['registered_at'] = '';
9147
                if ($userSession) {
9148
                    $user_row['registered_at'] = api_get_local_time($userSession['registered_at']);
9149
                }
9150
            }
9151
9152
            $user_row['first_connection'] = $user['first_connection'];
9153
            $user_row['last_connection'] = $user['last_connection'];
9154
9155
            // we need to display an additional profile field
9156
            if (isset($_GET['additional_profile_field'])) {
9157
                $data = Session::read('additional_user_profile_info');
9158
9159
                $extraFieldInfo = Session::read('extra_field_info');
9160
                foreach ($_GET['additional_profile_field'] as $fieldId) {
9161
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
9162
                        if (is_array($data[$fieldId][$user['user_id']])) {
9163
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = implode(
9164
                                ', ',
9165
                                $data[$fieldId][$user['user_id']]
9166
                            );
9167
                        } else {
9168
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
9169
                        }
9170
                    } else {
9171
                        $user_row[$extraFieldInfo[$fieldId]['variable']] = '';
9172
                    }
9173
                }
9174
            }
9175
9176
            $data = Session::read('default_additional_user_profile_info');
9177
            $defaultExtraFieldInfo = Session::read('default_extra_field_info');
9178
            if (isset($defaultExtraFieldInfo) && isset($data)) {
9179
                foreach ($data as $key => $val) {
9180
                    if (isset($val[$user['user_id']])) {
9181
                        if (is_array($val[$user['user_id']])) {
9182
                            $user_row[$defaultExtraFieldInfo[$key]['variable']] = implode(
9183
                                ', ',
9184
                                $val[$user['user_id']]
9185
                            );
9186
                        } else {
9187
                            $user_row[$defaultExtraFieldInfo[$key]['variable']] = $val[$user['user_id']];
9188
                        }
9189
                    } else {
9190
                        $user_row[$defaultExtraFieldInfo[$key]['variable']] = '';
9191
                    }
9192
                }
9193
            }
9194
9195
            if (api_get_setting('show_email_addresses') === 'true') {
9196
                $user_row['email'] = $user['col4'];
9197
            }
9198
9199
            $user_row['link'] = $user['link'];
9200
9201
            if ($export_csv) {
9202
                if (empty($session_id)) {
9203
                    //unset($user_row['classes']);
9204
                    unset($user_row['link']);
9205
                } else {
9206
                    //unset($user_row['classes']);
9207
                    unset($user_row['link']);
9208
                }
9209
                $csv_content[] = $user_row;
9210
            }
9211
            $users[] = array_values($user_row);
9212
        }
9213
9214
        if ($export_csv) {
9215
            Session::write('csv_content', $csv_content);
9216
        }
9217
9218
        Session::erase('additional_user_profile_info');
9219
        Session::erase('extra_field_info');
9220
        Session::erase('default_additional_user_profile_info');
9221
        Session::erase('default_extra_field_info');
9222
        Session::write('user_id_list', $userIdList);
9223
9224
        return $users;
9225
    }
9226
9227
    /**
9228
     * Get data for users list in sortable with pagination.
9229
     *
9230
     * @param $from
9231
     * @param $number_of_items
9232
     * @param $column
9233
     * @param $direction
9234
     * @param $includeInvitedUsers boolean Whether include the invited users
9235
     *
9236
     * @return array
9237
     */
9238
    public static function getTotalTimeReport(
9239
        $from,
9240
        $number_of_items,
9241
        $column,
9242
        $direction,
9243
        $includeInvitedUsers = false
9244
    ) {
9245
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
9246
9247
        $course_code = Database::escape_string($course_code);
9248
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
9249
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
9250
        $access_url_id = api_get_current_access_url_id();
9251
9252
        // get all users data from a course for sortable with limit
9253
        if (is_array($user_ids)) {
9254
            $user_ids = array_map('intval', $user_ids);
9255
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
9256
        } else {
9257
            $user_ids = intval($user_ids);
9258
            $condition_user = " WHERE user.user_id = $user_ids ";
9259
        }
9260
9261
        $url_table = null;
9262
        $url_condition = null;
9263
        if (api_is_multiple_url_enabled()) {
9264
            $url_table = ", ".$tbl_url_rel_user." as url_users";
9265
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
9266
        }
9267
9268
        $invitedUsersCondition = '';
9269
        if (!$includeInvitedUsers) {
9270
            $invitedUsersCondition = " AND user.status != ".INVITEE;
9271
        }
9272
9273
        $sql = "SELECT  user.user_id as user_id,
9274
                    user.official_code  as col0,
9275
                    user.lastname       as col1,
9276
                    user.firstname      as col2,
9277
                    user.username       as col3
9278
                FROM $tbl_user as user $url_table
9279
                $condition_user $url_condition $invitedUsersCondition";
9280
9281
        if (!in_array($direction, ['ASC', 'DESC'])) {
9282
            $direction = 'ASC';
9283
        }
9284
9285
        $column = (int) $column;
9286
        $from = (int) $from;
9287
        $number_of_items = (int) $number_of_items;
9288
9289
        $sql .= " ORDER BY col$column $direction ";
9290
        $sql .= " LIMIT $from,$number_of_items";
9291
9292
        $res = Database::query($sql);
9293
        $users = [];
9294
9295
        $sortByFirstName = api_sort_by_first_name();
9296
        $courseInfo = api_get_course_info($course_code);
9297
        $courseId = $courseInfo['real_id'];
9298
9299
        while ($user = Database::fetch_array($res, 'ASSOC')) {
9300
            $user['official_code'] = $user['col0'];
9301
            $user['lastname'] = $user['col1'];
9302
            $user['firstname'] = $user['col2'];
9303
            $user['username'] = $user['col3'];
9304
9305
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
9306
                $user['user_id'],
9307
                $courseId,
9308
                $session_id
9309
            );
9310
9311
            $user['time'] = api_time_to_hms($totalCourseTime);
9312
            $totalLpTime = Tracking::get_time_spent_in_lp(
9313
                $user['user_id'],
9314
                $course_code,
9315
                [],
9316
                $session_id
9317
            );
9318
9319
            $user['total_lp_time'] = $totalLpTime;
9320
            $warning = '';
9321
            if ($totalLpTime > $totalCourseTime) {
9322
                $warning = '&nbsp;'.Display::label(get_lang('TimeDifference'), 'danger');
9323
            }
9324
9325
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
9326
9327
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
9328
                $user['user_id'],
9329
                $courseId,
9330
                $session_id
9331
            );
9332
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
9333
                $user['user_id'],
9334
                $courseInfo,
9335
                $session_id,
9336
                $export_csv === false
9337
            );
9338
9339
            $user['link'] = '<center>
9340
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
9341
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
9342
                             </a>
9343
                         </center>';
9344
9345
            // store columns in array $users
9346
            $user_row = [];
9347
            $user_row['official_code'] = $user['official_code']; //0
9348
            if ($sortByFirstName) {
9349
                $user_row['firstname'] = $user['firstname'];
9350
                $user_row['lastname'] = $user['lastname'];
9351
            } else {
9352
                $user_row['lastname'] = $user['lastname'];
9353
                $user_row['firstname'] = $user['firstname'];
9354
            }
9355
            $user_row['username'] = $user['username'];
9356
            $user_row['time'] = $user['time'];
9357
            $user_row['total_lp_time'] = $user['total_lp_time'];
9358
            $user_row['first_connection'] = $user['first_connection'];
9359
            $user_row['last_connection'] = $user['last_connection'];
9360
9361
            $user_row['link'] = $user['link'];
9362
            $users[] = array_values($user_row);
9363
        }
9364
9365
        return $users;
9366
    }
9367
9368
    /**
9369
     * @param string $current
9370
     */
9371
    public static function actionsLeft($current, $sessionId = 0)
9372
    {
9373
        $usersLink = Display::url(
9374
            Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM),
9375
            'courseLog.php?'.api_get_cidreq(true, false)
9376
        );
9377
9378
        $groupsLink = Display::url(
9379
            Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
9380
            'course_log_groups.php?'.api_get_cidreq()
9381
        );
9382
9383
        $resourcesLink = Display::url(
9384
            Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM),
9385
            'course_log_resources.php?'.api_get_cidreq(true, false)
9386
        );
9387
9388
        $courseLink = Display::url(
9389
            Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
9390
            'course_log_tools.php?'.api_get_cidreq(true, false)
9391
        );
9392
9393
        $examLink = Display::url(
9394
            Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
9395
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
9396
        );
9397
9398
        $eventsLink = Display::url(
9399
            Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
9400
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
9401
        );
9402
9403
        $lpLink = Display::url(
9404
            Display::return_icon('scorms.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
9405
            api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq()
9406
        );
9407
9408
        $attendanceLink = '';
9409
        if (!empty($sessionId)) {
9410
            $attendanceLink = Display::url(
9411
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
9412
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
9413
            );
9414
        }
9415
9416
        switch ($current) {
9417
            case 'users':
9418
                $usersLink = Display::url(
9419
                        Display::return_icon(
9420
                        'user_na.png',
9421
                        get_lang('StudentsTracking'),
9422
                        [],
9423
                        ICON_SIZE_MEDIUM
9424
                    ),
9425
                    '#'
9426
                );
9427
                break;
9428
            case 'groups':
9429
                $groupsLink = Display::url(
9430
                    Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
9431
                    '#'
9432
                );
9433
                break;
9434
            case 'courses':
9435
                $courseLink = Display::url(
9436
                    Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
9437
                    '#'
9438
                );
9439
                break;
9440
            case 'resources':
9441
                $resourcesLink = Display::url(
9442
                    Display::return_icon(
9443
                    'tools_na.png',
9444
                    get_lang('ResourcesTracking'),
9445
                    [],
9446
                    ICON_SIZE_MEDIUM
9447
                    ),
9448
                    '#'
9449
                );
9450
                break;
9451
            case 'exams':
9452
                $examLink = Display::url(
9453
                    Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
9454
                    '#'
9455
                );
9456
                break;
9457
            case 'logs':
9458
                $eventsLink = Display::url(
9459
                    Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
9460
                    '#'
9461
                );
9462
                break;
9463
            case 'attendance':
9464
                if (!empty($sessionId)) {
9465
                    $attendanceLink = Display::url(
9466
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
9467
                        '#'
9468
                    );
9469
                }
9470
                break;
9471
            case 'lp':
9472
                $lpLink = Display::url(
9473
                    Display::return_icon('scorms_na.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
9474
                    '#'
9475
                );
9476
                break;
9477
        }
9478
9479
        $items = [
9480
            $usersLink,
9481
            $groupsLink,
9482
            $courseLink,
9483
            $resourcesLink,
9484
            $examLink,
9485
            $eventsLink,
9486
            $lpLink,
9487
            $attendanceLink,
9488
        ];
9489
9490
        return implode('', $items).'&nbsp;';
9491
    }
9492
}
9493