Passed
Push — master ( 0085e5...a02707 )
by Julito
10:26
created

Tracking::get_last_connection_time_in_lp()   B

Complexity

Conditions 10
Paths 20

Size

Total Lines 77
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 43
nc 20
nop 4
dl 0
loc 77
rs 7.6666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

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\CoreBundle\Entity\User;
9
use Chamilo\CoreBundle\Framework\Container;
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
     * @param int    $user_id
126
     * @param array  $courseInfo
127
     * @param int    $session_id
128
     * @param string $origin
129
     * @param bool   $export_csv
130
     * @param int    $lp_id
131
     * @param int    $lp_item_id
132
     * @param int    $extendId
133
     * @param int    $extendAttemptId
134
     * @param string $extendedAttempt
135
     * @param string $extendedAll
136
     * @param string $type            classic or simple
137
     * @param bool   $allowExtend     Optional. Allow or not extend te results
138
     *
139
     * @return string
140
     */
141
    public static function getLpStats(
142
        $user_id,
143
        $courseInfo,
144
        $session_id,
145
        $origin,
146
        $export_csv,
147
        $lp_id,
148
        $lp_item_id = null,
149
        $extendId = null,
150
        $extendAttemptId = null,
151
        $extendedAttempt = null,
152
        $extendedAll = null,
153
        $type = 'classic',
154
        $allowExtend = true
155
    ) {
156
        if (empty($courseInfo) || empty($lp_id)) {
157
            return '';
158
        }
159
160
        $hideTime = api_get_configuration_value('hide_lp_time');
161
        $allowNewTracking = api_get_configuration_value('use_new_tracking_in_lp_item');
162
        $lp_id = (int) $lp_id;
163
164
        if ($allowNewTracking) {
165
            $extraField = new ExtraFieldValue('lp');
166
            $result = $extraField->get_values_by_handler_and_field_variable($lp_id, 'track_lp_item');
167
            if (empty($result)) {
168
                $allowNewTracking = false;
169
            } else {
170
                if (isset($result['value']) && 1 == $result['value']) {
171
                    $allowNewTracking = true;
172
                }
173
            }
174
        }
175
176
        $lp_item_id = (int) $lp_item_id;
177
        $user_id = (int) $user_id;
178
        $session_id = (int) $session_id;
179
        $origin = Security::remove_XSS($origin);
180
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
181
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
182
        $course_id = $courseInfo['real_id'];
183
        $courseCode = $courseInfo['code'];
184
        $session_condition = api_get_session_condition($session_id);
185
186
        // Extend all button
187
        $output = '';
188
189
        $extra = '<script>
190
        $(function() {
191
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
192
            $( "#dialog-confirm" ).dialog({
193
                autoOpen: false,
194
                show: "blind",
195
                resizable: false,
196
                height:300,
197
                modal: true
198
            });
199
200
            $(".export").click(function() {
201
                var targetUrl = $(this).attr("href");
202
                $( "#dialog-confirm" ).dialog({
203
                    width:400,
204
                    height:300,
205
                    buttons: {
206
                        "'.addslashes(get_lang('Download')).'": function() {
207
                            var option = $("input[name=add_logo]:checked").val();
208
                            location.href = targetUrl+"&add_logo="+option;
209
                            $(this).dialog("close");
210
                        }
211
                    }
212
                });
213
                $("#dialog-confirm").dialog("open");
214
215
                return false;
216
            });
217
        });
218
        </script>';
219
220
        $extra .= '<div id="dialog-confirm" title="'.get_lang('Please confirm your choice').'">';
221
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
222
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
223
        $extra .= $form->returnForm();
224
        $extra .= '</div>';
225
226
        $output .= $extra;
227
228
        $url_suffix = '&lp_id='.$lp_id;
229
        if ('tracking' === $origin) {
230
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
231
        }
232
233
        $extend_all = 0;
234
        if (!empty($extendedAll)) {
235
            $extend_all_link = Display::url(
236
                Display::return_icon('view_less_stats.gif', get_lang('Hide all attempts')),
237
                api_get_self().'?action=stats'.$url_suffix
238
            );
239
            $extend_all = 1;
240
        } else {
241
            $extend_all_link = Display::url(
242
                Display::return_icon('view_more_stats.gif', get_lang('Show all attempts')),
243
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
244
            );
245
        }
246
247
        if ('tracking' != $origin) {
248
            $output .= '<div class="section-status">';
249
            $output .= Display::page_header(get_lang('My progress'));
250
            $output .= '</div>';
251
        }
252
253
        $actionColumn = null;
254
        if ('classic' === $type) {
255
            $actionColumn = ' <th>'.get_lang('Detail').'</th>';
256
        }
257
258
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('Time').'</th>';
259
        if ($hideTime) {
260
            $timeHeader = '';
261
        }
262
        $output .= '<div class="table-responsive">';
263
        $output .= '<table id="lp_tracking" class="table tracking">
264
            <thead>
265
            <tr class="table-header">
266
                <th width="16">'.(true == $allowExtend ? $extend_all_link : '&nbsp;').'</th>
267
                <th colspan="4">
268
                    '.get_lang('Learning object name').'
269
                </th>
270
                <th colspan="2">
271
                    '.get_lang('Status').'
272
                </th>
273
                <th colspan="2">
274
                    '.get_lang('Score').'
275
                </th>
276
                '.$timeHeader.'
277
                '.$actionColumn.'
278
                </tr>
279
            </thead>
280
            <tbody>
281
        ';
282
283
        // Going through the items using the $items[] array instead of the database order ensures
284
        // we get them in the same order as in the imsmanifest file, which is rather random when using
285
        // the database table.
286
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
287
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
288
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
289
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
290
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
291
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
292
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
293
294
        $sql = "SELECT max(view_count)
295
                FROM $TBL_LP_VIEW
296
                WHERE
297
                    c_id = $course_id AND
298
                    lp_id = $lp_id AND
299
                    user_id = $user_id
300
                    $session_condition";
301
        $res = Database::query($sql);
302
        $view = 0;
303
        if (Database::num_rows($res) > 0) {
304
            $myrow = Database::fetch_array($res);
305
            $view = (int) $myrow[0];
306
        }
307
308
        $counter = 0;
309
        $total_time = 0;
310
        $h = get_lang('h');
311
312
        if (!empty($export_csv)) {
313
            $csvHeaders = [
314
                get_lang('Learning object name'),
315
                get_lang('Status'),
316
                get_lang('Score'),
317
            ];
318
319
            if (false === $hideTime) {
320
                $csvHeaders[] = get_lang('Time');
321
            }
322
323
            $csv_content[] = $csvHeaders;
324
        }
325
326
        $result_disabled_ext_all = true;
327
        $chapterTypes = learnpath::getChapterTypes();
328
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
329
330
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
331
        $timeCourse = [];
332
        if ($minimumAvailable) {
333
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
334
            Session::write('trackTimeCourse', $timeCourse);
335
        }
336
337
        // Show lp items
338
        if (is_array($list) && count($list) > 0) {
339
            foreach ($list as $my_item_id) {
340
                $extend_this = 0;
341
                $order = 'DESC';
342
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
343
                    $extend_this = 1;
344
                    $order = 'ASC';
345
                }
346
347
                // Prepare statement to go through each attempt.
348
                $viewCondition = null;
349
                if (!empty($view)) {
350
                    $viewCondition = " AND v.view_count = $view  ";
351
                }
352
353
                $sql = "SELECT
354
                    iv.status as mystatus,
355
                    v.view_count as mycount,
356
                    iv.score as myscore,
357
                    iv.total_time as mytime,
358
                    i.iid as myid,
359
                    i.lp_id as mylpid,
360
                    iv.lp_view_id as mylpviewid,
361
                    i.title as mytitle,
362
                    i.max_score as mymaxscore,
363
                    iv.max_score as myviewmaxscore,
364
                    i.item_type as item_type,
365
                    iv.view_count as iv_view_count,
366
                    iv.id as iv_id,
367
                    path
368
                FROM $TBL_LP_ITEM as i
369
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
370
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
371
                INNER JOIN $TBL_LP_VIEW as v
372
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
373
                WHERE
374
                    v.c_id = $course_id AND
375
                    i.iid = $my_item_id AND
376
                    i.lp_id = $lp_id  AND
377
                    v.user_id = $user_id AND
378
                    v.session_id = $session_id
379
                    $viewCondition
380
                ORDER BY iv.view_count $order ";
381
382
                $result = Database::query($sql);
383
                $num = Database::num_rows($result);
384
                $time_for_total = 0;
385
                $attemptResult = 0;
386
387
                if ($allowNewTracking && $timeCourse) {
388
                    if (isset($timeCourse['learnpath_detailed']) &&
389
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
390
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
391
                    ) {
392
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
393
                    }
394
                }
395
396
                // Extend all
397
                if (($extend_this || $extend_all) && $num > 0) {
398
                    $row = Database::fetch_array($result);
399
                    $result_disabled_ext_all = false;
400
                    if ('quiz' === $row['item_type']) {
401
                        // Check results_disabled in quiz table.
402
                        $my_path = Database::escape_string($row['path']);
403
                        $sql = "SELECT results_disabled
404
                                FROM $TBL_QUIZ
405
                                WHERE
406
                                    c_id = $course_id AND
407
                                    id ='".$my_path."'";
408
                        $res_result_disabled = Database::query($sql);
409
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
410
411
                        if (Database::num_rows($res_result_disabled) > 0 &&
412
                            1 === (int) $row_result_disabled[0]
413
                        ) {
414
                            $result_disabled_ext_all = true;
415
                        }
416
                    }
417
418
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
419
                    $oddclass = 'row_even';
420
                    if (0 === ($counter % 2)) {
421
                        $oddclass = 'row_odd';
422
                    }
423
                    $extend_link = '';
424
                    if (!empty($inter_num)) {
425
                        $extend_link = Display::url(
426
                            Display::return_icon(
427
                                'visible.png',
428
                                get_lang('Hide attempt view')
429
                            ),
430
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
431
                        );
432
                    }
433
                    $title = $row['mytitle'];
434
435
                    if (empty($title)) {
436
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
437
                    }
438
439
                    if (in_array($row['item_type'], $chapterTypes)) {
440
                        $title = "<h4> $title </h4>";
441
                    }
442
                    $lesson_status = $row['mystatus'];
443
                    $title = Security::remove_XSS($title);
444
                    $counter++;
445
446
                    $action = null;
447
                    if ('classic' === $type) {
448
                        $action = '<td></td>';
449
                    }
450
451
                    if (in_array($row['item_type'], $chapterTypes)) {
452
                        $output .= '<tr class="'.$oddclass.'">
453
                                <td>'.$extend_link.'</td>
454
                                <td colspan="4">
455
                                   '.$title.'
456
                                </td>
457
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
458
                                <td colspan="2"></td>
459
                                <td colspan="2"></td>
460
                                '.$action.'
461
                            </tr>';
462
                        continue;
463
                    } else {
464
                        $output .= '<tr class="'.$oddclass.'">
465
                                <td>'.$extend_link.'</td>
466
                                <td colspan="4">'.$title.'</td>
467
                                <td colspan="2"></td>
468
                                <td colspan="2"></td>
469
                                <td colspan="2"></td>
470
                                '.$action.'
471
                            </tr>';
472
                    }
473
474
                    $attemptCount = 1;
475
                    do {
476
                        // Check if there are interactions below.
477
                        $extend_attempt_link = '';
478
                        $extend_this_attempt = 0;
479
480
                        if ($allowNewTracking && $timeCourse) {
481
                            //$attemptResult = 0;
482
                            if (isset($timeCourse['learnpath_detailed']) &&
483
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
484
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
485
                            ) {
486
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
487
                            }
488
                        }
489
                        if ((
490
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
491
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
492
                            ) &&
493
                            !$extend_all
494
                        ) {
495
                            if ($extendAttemptId == $row['iv_id']) {
496
                                // The extend button for this attempt has been clicked.
497
                                $extend_this_attempt = 1;
498
                                $extend_attempt_link = Display::url(
499
                                    Display::return_icon('visible.png', get_lang('Hide attempt view')),
500
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
501
                                );
502
                                if ($accessToPdfExport) {
503
                                    $extend_attempt_link .= '&nbsp;'.
504
                                        Display::url(
505
                                            Display::return_icon('pdf.png', get_lang('Export to PDF')),
506
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
507
                                            ['class' => 'export']
508
                                        );
509
                                }
510
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
511
                                // The extend button for this attempt has not been clicked.
512
                                $extend_attempt_link = Display::url(
513
                                    Display::return_icon('invisible.png', get_lang('Extend attempt view')),
514
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
515
                                );
516
                                if ($accessToPdfExport) {
517
                                    $extend_attempt_link .= '&nbsp;'.
518
                                        Display::url(
519
                                            Display::return_icon('pdf.png', get_lang('Export to PDF')),
520
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
521
                                            ['class' => 'export']
522
                                        );
523
                                }
524
                            }
525
                        }
526
527
                        $oddclass = 'row_even';
528
                        if (0 == ($counter % 2)) {
529
                            $oddclass = 'row_odd';
530
                        }
531
532
                        $lesson_status = $row['mystatus'];
533
                        $score = $row['myscore'];
534
                        $time_for_total += $row['mytime'];
535
                        $attemptTime = $row['mytime'];
536
537
                        if ($minimumAvailable) {
538
                            $lp_time = $timeCourse[TOOL_LEARNPATH];
539
                            $lpTime = null;
540
                            if (isset($lp_time[$lp_id])) {
541
                                $lpTime = (int) $lp_time[$lp_id];
542
                            }
543
                            $time_for_total = $lpTime;
544
545
                            if ($allowNewTracking) {
546
                                $time_for_total = (int) $attemptResult;
547
                                $attemptTime = (int) $attemptResult;
548
                            }
549
                        }
550
551
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
552
553
                        if (0 == $score) {
554
                            $maxscore = $row['mymaxscore'];
555
                        } else {
556
                            if ('sco' === $row['item_type']) {
557
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
558
                                    $maxscore = $row['myviewmaxscore'];
559
                                } elseif ('' === $row['myviewmaxscore']) {
560
                                    $maxscore = 0;
561
                                } else {
562
                                    $maxscore = $row['mymaxscore'];
563
                                }
564
                            } else {
565
                                $maxscore = $row['mymaxscore'];
566
                            }
567
                        }
568
569
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
570
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
571
572
                        if ('dir' !== $row['item_type']) {
573
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
574
                                $view_score = Display::return_icon(
575
                                    'invisible.png',
576
                                    get_lang('Results hidden by the exercise setting')
577
                                );
578
                            } else {
579
                                switch ($row['item_type']) {
580
                                    case 'sco':
581
                                        if (0 == $maxscore) {
582
                                            $view_score = $score;
583
                                        } else {
584
                                            $view_score = ExerciseLib::show_score(
585
                                                $score,
586
                                                $maxscore,
587
                                                false
588
                                            );
589
                                        }
590
                                        break;
591
                                    case 'document':
592
                                        $view_score = (0 == $score ? '/' : ExerciseLib::show_score($score, $maxscore, false));
593
                                        break;
594
                                    default:
595
                                        $view_score = ExerciseLib::show_score(
596
                                            $score,
597
                                            $maxscore,
598
                                            false
599
                                        );
600
                                        break;
601
                                }
602
                            }
603
604
                            $action = null;
605
                            if ('classic' == $type) {
606
                                $action = '<td></td>';
607
                            }
608
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
609
                            if ($hideTime) {
610
                                $timeRow = '';
611
                            }
612
                            $output .= '<tr class="'.$oddclass.'">
613
                                    <td></td>
614
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
615
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
616
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
617
                                    <td colspan="2">'.$view_score.'</td>
618
                                    '.$timeRow.'
619
                                    '.$action.'
620
                                </tr>';
621
                            $attemptCount++;
622
                            if (!empty($export_csv)) {
623
                                $temp = [];
624
                                $temp[] = $title = Security::remove_XSS($title);
625
                                $temp[] = Security::remove_XSS(
626
                                    learnpathItem::humanize_status($lesson_status, false, $type)
627
                                );
628
629
                                if ('quiz' === $row['item_type']) {
630
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
631
                                        $temp[] = '/';
632
                                    } else {
633
                                        $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
634
                                    }
635
                                } else {
636
                                    $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
637
                                }
638
639
                                if (false === $hideTime) {
640
                                    $temp[] = $time;
641
                                }
642
                                $csv_content[] = $temp;
643
                            }
644
                        }
645
646
                        $counter++;
647
                        $action = null;
648
                        if ('classic' === $type) {
649
                            $action = '<td></td>';
650
                        }
651
652
                        if ($extend_this_attempt || $extend_all) {
653
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
654
                            foreach ($list1 as $id => $interaction) {
655
                                $oddclass = 'row_even';
656
                                if (0 == ($counter % 2)) {
657
                                    $oddclass = 'row_odd';
658
                                }
659
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
660
                                if ($hideTime) {
661
                                    $timeRow = '';
662
                                }
663
664
                                $output .= '<tr class="'.$oddclass.'">
665
                                        <td></td>
666
                                        <td></td>
667
                                        <td></td>
668
                                        <td>'.$interaction['order_id'].'</td>
669
                                        <td>'.$interaction['id'].'</td>';
670
671
                                $output .= '
672
                                        <td colspan="2">'.$interaction['type'].'</td>
673
                                        <td>'.$interaction['student_response_formatted'].'</td>
674
                                        <td>'.$interaction['result'].'</td>
675
                                        <td>'.$interaction['latency'].'</td>
676
                                        '.$timeRow.'
677
                                        '.$action.'
678
                                    </tr>';
679
                                $counter++;
680
                            }
681
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
682
                            foreach ($list2 as $id => $interaction) {
683
                                $oddclass = 'row_even';
684
                                if (0 === ($counter % 2)) {
685
                                    $oddclass = 'row_odd';
686
                                }
687
                                $output .= '<tr class="'.$oddclass.'">
688
                                        <td></td>
689
                                        <td></td>
690
                                        <td></td>
691
                                        <td>'.$interaction['order_id'].'</td>
692
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
693
                                        <td colspan="2">'.$interaction['status'].'</td>
694
                                        <td>'.$interaction['score_raw'].'</td>
695
                                        <td>'.$interaction['score_max'].'</td>
696
                                        <td>'.$interaction['score_min'].'</td>
697
                                        '.$action.'
698
                                     </tr>';
699
                                $counter++;
700
                            }
701
                        }
702
                    } while ($row = Database::fetch_array($result));
703
                } elseif ($num > 0) {
704
                    // Not extended.
705
                    $row = Database::fetch_array($result, 'ASSOC');
706
                    $my_id = $row['myid'];
707
                    $my_lp_id = $row['mylpid'];
708
                    $my_lp_view_id = $row['mylpviewid'];
709
                    $my_path = $row['path'];
710
                    $result_disabled_ext_all = false;
711
                    if ('quiz' === $row['item_type']) {
712
                        // Check results_disabled in quiz table.
713
                        $my_path = Database::escape_string($my_path);
714
                        $sql = "SELECT results_disabled
715
                                FROM $TBL_QUIZ
716
                                WHERE c_id = $course_id AND id = '$my_path' ";
717
                        $res_result_disabled = Database::query($sql);
718
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
719
720
                        if (Database::num_rows($res_result_disabled) > 0 &&
721
                            1 === (int) $row_result_disabled[0]
722
                        ) {
723
                            $result_disabled_ext_all = true;
724
                        }
725
                    }
726
727
                    // Check if there are interactions below
728
                    $extend_this_attempt = 0;
729
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
730
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
731
                    $extend_attempt_link = '';
732
                    if ($inter_num > 0 || $objec_num > 0) {
733
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
734
                            // The extend button for this attempt has been clicked.
735
                            $extend_this_attempt = 1;
736
                            $extend_attempt_link = Display::url(
737
                                Display::return_icon('visible.png', get_lang('Hide attempt view')),
738
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
739
                            );
740
                        } else {
741
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
742
                            // The extend button for this attempt has not been clicked.
743
                            $extend_attempt_link = Display::url(
744
                                Display::return_icon('invisible.png', get_lang('Extend attempt view')),
745
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
746
                            );
747
                        }
748
                    }
749
750
                    $oddclass = 'row_even';
751
                    if (0 == ($counter % 2)) {
752
                        $oddclass = 'row_odd';
753
                    }
754
755
                    $extend_link = '';
756
                    if ($inter_num > 1) {
757
                        $extend_link = Display::url(
758
                            Display::return_icon('invisible.png', get_lang('Extend attempt view')),
759
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
760
                        );
761
                    }
762
763
                    $lesson_status = $row['mystatus'];
764
                    $score = $row['myscore'];
765
                    $subtotal_time = $row['mytime'];
766
                    while ($tmp_row = Database::fetch_array($result)) {
767
                        $subtotal_time += $tmp_row['mytime'];
768
                    }
769
770
                    if ($allowNewTracking) {
771
                        $subtotal_time = $attemptResult;
772
                    }
773
774
                    $title = $row['mytitle'];
775
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
776
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
777
                            WHERE
778
                                exe_exo_id="'.$row['path'].'" AND
779
                                exe_user_id="'.$user_id.'" AND
780
                                orig_lp_id = "'.$lp_id.'" AND
781
                                orig_lp_item_id = "'.$row['myid'].'" AND
782
                                c_id = '.$course_id.' AND
783
                                status <> "incomplete" AND
784
                                session_id = '.$session_id.'
785
                             ORDER BY exe_date DESC
786
                             LIMIT 1';
787
788
                    $resultLastAttempt = Database::query($sql);
789
                    $num = Database::num_rows($resultLastAttempt);
790
                    $id_last_attempt = null;
791
                    if ($num > 0) {
792
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
793
                            $id_last_attempt = $rowLA['exe_id'];
794
                        }
795
                    }
796
797
                    switch ($row['item_type']) {
798
                        case 'sco':
799
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
800
                                $maxscore = $row['myviewmaxscore'];
801
                            } elseif ('' === $row['myviewmaxscore']) {
802
                                $maxscore = 0;
803
                            } else {
804
                                $maxscore = $row['mymaxscore'];
805
                            }
806
                            break;
807
                        case 'quiz':
808
                            // Get score and total time from last attempt of a exercise en lp.
809
                            $sql = "SELECT iid, score
810
                                    FROM $TBL_LP_ITEM_VIEW
811
                                    WHERE
812
                                        c_id = $course_id AND
813
                                        lp_item_id = '".(int) $my_id."' AND
814
                                        lp_view_id = '".(int) $my_lp_view_id."'
815
                                    ORDER BY view_count DESC
816
                                    LIMIT 1";
817
                            $res_score = Database::query($sql);
818
                            $row_score = Database::fetch_array($res_score);
819
820
                            $sql = "SELECT SUM(total_time) as total_time
821
                                    FROM $TBL_LP_ITEM_VIEW
822
                                    WHERE
823
                                        c_id = $course_id AND
824
                                        lp_item_id = '".(int) $my_id."' AND
825
                                        lp_view_id = '".(int) $my_lp_view_id."'";
826
                            $res_time = Database::query($sql);
827
                            $row_time = Database::fetch_array($res_time);
828
829
                            $score = 0;
830
                            $subtotal_time = 0;
831
                            if (Database::num_rows($res_score) > 0 &&
832
                                Database::num_rows($res_time) > 0
833
                            ) {
834
                                $score = (float) $row_score['score'];
835
                                $subtotal_time = (int) $row_time['total_time'];
836
                            }
837
                            // Selecting the max score from an attempt.
838
                            $sql = "SELECT SUM(t.ponderation) as maxscore
839
                                    FROM (
840
                                        SELECT DISTINCT
841
                                            question_id, marks, ponderation
842
                                        FROM $tbl_stats_attempts as at
843
                                        INNER JOIN $tbl_quiz_questions as q
844
                                        ON (q.id = at.question_id AND q.c_id = $course_id)
845
                                        WHERE exe_id ='$id_last_attempt'
846
                                    ) as t";
847
848
                            $result = Database::query($sql);
849
                            $row_max_score = Database::fetch_array($result);
850
                            $maxscore = $row_max_score['maxscore'];
851
852
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
853
                            $sql = 'SELECT SUM(exe_duration) exe_duration
854
                                    FROM '.$tbl_stats_exercices.'
855
                                    WHERE
856
                                        exe_exo_id="'.$row['path'].'" AND
857
                                        exe_user_id="'.$user_id.'" AND
858
                                        orig_lp_id = "'.$lp_id.'" AND
859
                                        orig_lp_item_id = "'.$row['myid'].'" AND
860
                                        c_id = '.$course_id.' AND
861
                                        status <> "incomplete" AND
862
                                        session_id = '.$session_id.'
863
                                     ORDER BY exe_date DESC ';
864
                            $sumScoreResult = Database::query($sql);
865
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
866
                            if (!empty($durationRow['exe_duration'])) {
867
                                $exeDuration = $durationRow['exe_duration'];
868
                                if ($exeDuration != $subtotal_time &&
869
                                    !empty($row_score['iid']) &&
870
                                    !empty($exeDuration)
871
                                ) {
872
                                    $subtotal_time = $exeDuration;
873
                                    // Update c_lp_item_view.total_time
874
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
875
                                                  WHERE iid = ".$row_score['iid'];
876
                                    Database::query($sqlUpdate);
877
                                }
878
                            }
879
                            break;
880
                        default:
881
                            $maxscore = $row['mymaxscore'];
882
                            break;
883
                    }
884
885
                    $time_for_total = $subtotal_time;
886
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
887
                    if (empty($title)) {
888
                        $title = learnpath::rl_get_resource_name(
889
                            $courseInfo['code'],
890
                            $lp_id,
891
                            $row['myid']
892
                        );
893
                    }
894
895
                    $action = null;
896
                    if ('classic' == $type) {
897
                        $action = '<td></td>';
898
                    }
899
900
                    if (in_array($row['item_type'], $chapterTypes)) {
901
                        $title = Security::remove_XSS($title);
902
                        $output .= '<tr class="'.$oddclass.'">
903
                                <td>'.$extend_link.'</td>
904
                                <td colspan="10">
905
                                <h4>'.$title.'</h4>
906
                                </td>
907
                                '.$action.'
908
                            </tr>';
909
                    } else {
910
                        $correct_test_link = '-';
911
                        $showRowspan = false;
912
                        if ('quiz' === $row['item_type']) {
913
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
914
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
915
                                     WHERE
916
                                        exe_exo_id="'.$row['path'].'" AND
917
                                        exe_user_id="'.$user_id.'" AND
918
                                        orig_lp_id = "'.$lp_id.'" AND
919
                                        orig_lp_item_id = "'.$row['myid'].'" AND
920
                                        c_id = '.$course_id.' AND
921
                                        status <> "incomplete" AND
922
                                        session_id = '.$session_id.'
923
                                     ORDER BY exe_date DESC ';
924
925
                            $resultLastAttempt = Database::query($sql);
926
                            $num = Database::num_rows($resultLastAttempt);
927
                            $showRowspan = false;
928
                            if ($num > 0) {
929
                                $linkId = 'link_'.$my_id;
930
                                if (1 == $extendedAttempt &&
931
                                    $lp_id == $my_lp_id &&
932
                                    $lp_item_id == $my_id
933
                                ) {
934
                                    $showRowspan = true;
935
                                    $correct_test_link = Display::url(
936
                                        Display::return_icon(
937
                                            'view_less_stats.gif',
938
                                            get_lang('Hide all attempts')
939
                                        ),
940
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
941
                                        ['id' => $linkId]
942
                                    );
943
                                } else {
944
                                    $correct_test_link = Display::url(
945
                                        Display::return_icon(
946
                                            'view_more_stats.gif',
947
                                            get_lang(
948
                                                'Show all attemptsByExercise'
949
                                            )
950
                                        ),
951
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
952
                                        ['id' => $linkId]
953
                                    );
954
                                }
955
                            }
956
                        }
957
958
                        $title = Security::remove_XSS($title);
959
                        $action = null;
960
                        if ('classic' === $type) {
961
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
962
                        }
963
964
                        if ($lp_id == $my_lp_id && false) {
965
                            $output .= '<tr class ='.$oddclass.'>
966
                                    <td>'.$extend_link.'</td>
967
                                    <td colspan="4">'.$title.'</td>
968
                                    <td colspan="2">&nbsp;</td>
969
                                    <td colspan="2">&nbsp;</td>
970
                                    <td colspan="2">&nbsp;</td>
971
                                    '.$action.'
972
                                </tr>';
973
                            $output .= '</tr>';
974
                        } else {
975
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
976
                                $output .= "<tr class='$oddclass'>";
977
                            } else {
978
                                $output .= "<tr class='$oddclass'>";
979
                            }
980
981
                            $scoreItem = null;
982
                            if ('quiz' == $row['item_type']) {
983
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
984
                                    $scoreItem .= Display::return_icon(
985
                                        'invisible.png',
986
                                        get_lang('Results hidden by the exercise setting')
987
                                    );
988
                                } else {
989
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
990
                                }
991
                            } else {
992
                                $scoreItem .= 0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.$maxscore);
993
                            }
994
995
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
996
                            if ($hideTime) {
997
                                $timeRow = '';
998
                            }
999
1000
                            $output .= '
1001
                                <td>'.$extend_link.'</td>
1002
                                <td colspan="4">'.$title.'</td>
1003
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1004
                                <td colspan="2">'.$scoreItem.'</td>
1005
                                '.$timeRow.'
1006
                                '.$action.'
1007
                             ';
1008
                            $output .= '</tr>';
1009
                        }
1010
1011
                        if (!empty($export_csv)) {
1012
                            $temp = [];
1013
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1014
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1015
                            if ('quiz' === $row['item_type']) {
1016
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1017
                                    $temp[] = '/';
1018
                                } else {
1019
                                    $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1020
                                }
1021
                            } else {
1022
                                $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1023
                            }
1024
1025
                            if (false === $hideTime) {
1026
                                $temp[] = $time;
1027
                            }
1028
                            $csv_content[] = $temp;
1029
                        }
1030
                    }
1031
1032
                    $counter++;
1033
                    $action = null;
1034
                    if ('classic' === $type) {
1035
                        $action = '<td></td>';
1036
                    }
1037
1038
                    if ($extend_this_attempt || $extend_all) {
1039
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1040
                        foreach ($list1 as $id => $interaction) {
1041
                            $oddclass = 'row_even';
1042
                            if (0 == ($counter % 2)) {
1043
                                $oddclass = 'row_odd';
1044
                            }
1045
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1046
                            if ($hideTime) {
1047
                                $timeRow = '';
1048
                            }
1049
1050
                            $output .= '<tr class="'.$oddclass.'">
1051
                                    <td></td>
1052
                                    <td></td>
1053
                                    <td></td>
1054
                                    <td>'.$interaction['order_id'].'</td>
1055
                                    <td>'.$interaction['id'].'</td>
1056
                                    <td colspan="2">'.$interaction['type'].'</td>
1057
                                    <td>'.urldecode($interaction['student_response']).'</td>
1058
                                    <td>'.$interaction['result'].'</td>
1059
                                    <td>'.$interaction['latency'].'</td>
1060
                                    '.$timeRow.'
1061
                                    '.$action.'
1062
                               </tr>';
1063
                            $counter++;
1064
                        }
1065
1066
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1067
                        foreach ($list2 as $id => $interaction) {
1068
                            $oddclass = 'row_even';
1069
                            if (0 == ($counter % 2)) {
1070
                                $oddclass = 'row_odd';
1071
                            }
1072
                            $output .= '<tr class="'.$oddclass.'">
1073
                                    <td></td>
1074
                                    <td></td>
1075
                                    <td></td>
1076
                                    <td>'.$interaction['order_id'].'</td>
1077
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1078
                                    <td colspan="2">'.$interaction['status'].'</td>
1079
                                    <td>'.$interaction['score_raw'].'</td>
1080
                                    <td>'.$interaction['score_max'].'</td>
1081
                                    <td>'.$interaction['score_min'].'</td>
1082
                                    '.$action.'
1083
                               </tr>';
1084
                            $counter++;
1085
                        }
1086
                    }
1087
1088
                    // Attempts listing by exercise.
1089
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1090
                        // Get attempts of a exercise.
1091
                        if (!empty($lp_id) &&
1092
                            !empty($lp_item_id) &&
1093
                            'quiz' === $row['item_type']
1094
                        ) {
1095
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1096
                                    WHERE
1097
                                        c_id = $course_id AND
1098
                                        iid = '$lp_item_id' AND
1099
                                        lp_id = '$lp_id'";
1100
                            $res_path = Database::query($sql);
1101
                            $row_path = Database::fetch_array($res_path);
1102
1103
                            if (Database::num_rows($res_path) > 0) {
1104
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1105
                                        WHERE
1106
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1107
                                            status <> "incomplete" AND
1108
                                            exe_user_id="'.$user_id.'" AND
1109
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1110
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1111
                                            c_id = '.$course_id.'  AND
1112
                                            session_id = '.$session_id.'
1113
                                        ORDER BY exe_date';
1114
                                $res_attempts = Database::query($sql);
1115
                                $num_attempts = Database::num_rows($res_attempts);
1116
                                if ($num_attempts > 0) {
1117
                                    $n = 1;
1118
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1119
                                        $my_score = $row_attempts['score'];
1120
                                        $my_maxscore = $row_attempts['max_score'];
1121
                                        $my_exe_id = $row_attempts['exe_id'];
1122
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1123
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1124
                                        $time_attemp = ' - ';
1125
                                        if ($mktime_start_date && $mktime_exe_date) {
1126
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1127
                                        }
1128
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1129
                                            $view_score = Display::return_icon(
1130
                                                'invisible.png',
1131
                                                get_lang(
1132
                                                    'Results hidden by the exercise setting'
1133
                                                )
1134
                                            );
1135
                                        } else {
1136
                                            // Show only float when need it
1137
                                            if (0 == $my_score) {
1138
                                                $view_score = ExerciseLib::show_score(
1139
                                                    0,
1140
                                                    $my_maxscore,
1141
                                                    false
1142
                                                );
1143
                                            } else {
1144
                                                if (0 == $my_maxscore) {
1145
                                                    $view_score = $my_score;
1146
                                                } else {
1147
                                                    $view_score = ExerciseLib::show_score(
1148
                                                        $my_score,
1149
                                                        $my_maxscore,
1150
                                                        false
1151
                                                    );
1152
                                                }
1153
                                            }
1154
                                        }
1155
                                        $my_lesson_status = $row_attempts['status'];
1156
                                        if ('' == $my_lesson_status) {
1157
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1158
                                        } elseif ('incomplete' == $my_lesson_status) {
1159
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1160
                                        }
1161
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1162
                                        if ($hideTime) {
1163
                                            $timeRow = '';
1164
                                        }
1165
1166
                                        $output .= '<tr class="'.$oddclass.'" >
1167
                                        <td></td>
1168
                                        <td>'.$extend_attempt_link.'</td>
1169
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1170
                                        <td colspan="2">'.$my_lesson_status.'</td>
1171
                                        <td colspan="2">'.$view_score.'</td>
1172
                                        '.$timeRow;
1173
1174
                                        if ('classic' == $action) {
1175
                                            if ('tracking' != $origin) {
1176
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1177
                                                    $output .= '<td>
1178
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('Show attempt').'" title="'.get_lang('Show attempt').'">
1179
                                                            </td>';
1180
                                                } else {
1181
                                                    $output .= '<td>
1182
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1183
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('Show attempt').'" title="'.get_lang('Show attempt').'">
1184
                                                            </a></td>';
1185
                                                }
1186
                                            } else {
1187
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1188
                                                    $output .= '<td>
1189
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('Show and grade attempt').'" title="'.get_lang('Show and grade attempt').'"></td>';
1190
                                                } else {
1191
                                                    $output .= '<td>
1192
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1193
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('Show and grade attempt').'" title="'.get_lang('Show and grade attempt').'"></a></td>';
1194
                                                }
1195
                                            }
1196
                                        }
1197
                                        $output .= '</tr>';
1198
                                        $n++;
1199
                                    }
1200
                                }
1201
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1202
                            }
1203
                        }
1204
                    }
1205
                }
1206
1207
                $total_time += $time_for_total;
1208
                // QUIZZ IN LP
1209
                $a_my_id = [];
1210
                if (!empty($my_lp_id)) {
1211
                    $a_my_id[] = $my_lp_id;
1212
                }
1213
            }
1214
        }
1215
1216
        // NOT Extend all "left green cross"
1217
        if (!empty($a_my_id)) {
1218
            if ($extendedAttempt) {
1219
                // "Right green cross" extended
1220
                $total_score = self::get_avg_student_score(
1221
                    $user_id,
1222
                    $course_id,
1223
                    $a_my_id,
1224
                    $session_id,
1225
                    false,
1226
                    false
1227
                );
1228
            } else {
1229
                // "Left green cross" extended
1230
                $total_score = self::get_avg_student_score(
1231
                    $user_id,
1232
                    $course_id,
1233
                    $a_my_id,
1234
                    $session_id,
1235
                    false,
1236
                    true
1237
                );
1238
            }
1239
        } else {
1240
            // Extend all "left green cross"
1241
            $total_score = self::get_avg_student_score(
1242
                $user_id,
1243
                $course_id,
1244
                [$lp_id],
1245
                $session_id,
1246
                false,
1247
                false
1248
            );
1249
        }
1250
1251
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1252
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1253
1254
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1255
            $final_score = Display::return_icon('invisible.png', get_lang('Results hidden by the exercise setting'));
1256
            $finalScoreToCsv = get_lang('Results hidden by the exercise setting');
1257
        } else {
1258
            if (is_numeric($total_score)) {
1259
                $final_score = $total_score.'%';
1260
            } else {
1261
                $final_score = $total_score;
1262
            }
1263
            $finalScoreToCsv = $final_score;
1264
        }
1265
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1266
1267
        $oddclass = 'row_even';
1268
        if (0 == ($counter % 2)) {
1269
            $oddclass = 'row_odd';
1270
        }
1271
1272
        $action = null;
1273
        if ('classic' === $type) {
1274
            $action = '<td></td>';
1275
        }
1276
1277
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1278
        if ($hideTime) {
1279
            $timeTotal = '';
1280
        }
1281
1282
        $output .= '<tr class="'.$oddclass.'">
1283
                <td></td>
1284
                <td colspan="4">
1285
                    <i>'.get_lang('Total of completed learning objects').'</i>
1286
                </td>
1287
                <td colspan="2">'.$progress.'%</td>
1288
                <td colspan="2">'.$final_score.'</td>
1289
                '.$timeTotal.'
1290
                '.$action.'
1291
           </tr>';
1292
1293
        $output .= '
1294
                    </tbody>
1295
                </table>
1296
            </div>
1297
        ';
1298
1299
        if (!empty($export_csv)) {
1300
            $temp = [
1301
                '',
1302
                '',
1303
                '',
1304
                '',
1305
            ];
1306
            $csv_content[] = $temp;
1307
            $temp = [
1308
                get_lang('Total of completed learning objects'),
1309
                '',
1310
                $finalScoreToCsv,
1311
            ];
1312
1313
            if (false === $hideTime) {
1314
                $temp[] = $total_time;
1315
            }
1316
1317
            $csv_content[] = $temp;
1318
            ob_end_clean();
1319
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1320
            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...
1321
        }
1322
1323
        return $output;
1324
    }
1325
1326
    /**
1327
     * @param int  $userId
1328
     * @param bool $getCount
1329
     *
1330
     * @return array
1331
     */
1332
    public static function getStats($userId, $getCount = false)
1333
    {
1334
        $courses = [];
1335
        $assignedCourses = [];
1336
        $drhCount = 0;
1337
        $teachersCount = 0;
1338
        $studentsCount = 0;
1339
        $studentBossCount = 0;
1340
        $courseCount = 0;
1341
        $sessionCount = 0;
1342
        $assignedCourseCount = 0;
1343
1344
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1345
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1346
                'drh_all',
1347
                $userId,
1348
                false,
1349
                null,
1350
                null,
1351
                null,
1352
                null,
1353
                null,
1354
                null,
1355
                null,
1356
                [],
1357
                [],
1358
                STUDENT
1359
            );
1360
1361
            $students = [];
1362
            if (is_array($studentList)) {
1363
                foreach ($studentList as $studentData) {
1364
                    $students[] = $studentData['user_id'];
1365
                }
1366
            }
1367
1368
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1369
                'drh_all',
1370
                $userId,
1371
                $getCount,
1372
                null,
1373
                null,
1374
                null,
1375
                null,
1376
                null,
1377
                null,
1378
                null,
1379
                [],
1380
                [],
1381
                STUDENT_BOSS
1382
            );
1383
1384
            if ($getCount) {
1385
                $studentBossCount = $studentBossesList;
1386
            } else {
1387
                $studentBosses = [];
1388
                if (is_array($studentBossesList)) {
1389
                    foreach ($studentBossesList as $studentBossData) {
1390
                        $studentBosses[] = $studentBossData['user_id'];
1391
                    }
1392
                }
1393
            }
1394
1395
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1396
                'drh_all',
1397
                $userId,
1398
                $getCount,
1399
                null,
1400
                null,
1401
                null,
1402
                null,
1403
                null,
1404
                null,
1405
                null,
1406
                [],
1407
                [],
1408
                COURSEMANAGER
1409
            );
1410
1411
            if ($getCount) {
1412
                $teachersCount = $teacherList;
1413
            } else {
1414
                $teachers = [];
1415
                foreach ($teacherList as $teacherData) {
1416
                    $teachers[] = $teacherData['user_id'];
1417
                }
1418
            }
1419
1420
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1421
                'drh_all',
1422
                $userId,
1423
                $getCount,
1424
                null,
1425
                null,
1426
                null,
1427
                null,
1428
                null,
1429
                null,
1430
                null,
1431
                [],
1432
                [],
1433
                DRH
1434
            );
1435
1436
            if ($getCount) {
1437
                $drhCount = $humanResources;
1438
            } else {
1439
                $humanResourcesList = [];
1440
                if (is_array($humanResources)) {
1441
                    foreach ($humanResources as $item) {
1442
                        $humanResourcesList[] = $item['user_id'];
1443
                    }
1444
                }
1445
            }
1446
1447
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1448
                $userId,
1449
                null,
1450
                null,
1451
                null,
1452
                null,
1453
                null,
1454
                $getCount
1455
            );
1456
1457
            if ($getCount) {
1458
                $courseCount = $platformCourses;
1459
            } else {
1460
                foreach ($platformCourses as $course) {
1461
                    $courses[$course['code']] = $course['code'];
1462
                }
1463
            }
1464
1465
            $sessions = SessionManager::get_sessions_followed_by_drh(
1466
                $userId,
1467
                null,
1468
                null,
1469
                false
1470
            );
1471
        } else {
1472
            $studentList = UserManager::getUsersFollowedByUser(
1473
                $userId,
1474
                STUDENT,
1475
                false,
1476
                false,
1477
                false,
1478
                null,
1479
                null,
1480
                null,
1481
                null,
1482
                null,
1483
                null,
1484
                COURSEMANAGER
1485
            );
1486
1487
            $students = [];
1488
            if (is_array($studentList)) {
1489
                foreach ($studentList as $studentData) {
1490
                    $students[] = $studentData['user_id'];
1491
                }
1492
            }
1493
1494
            $studentBossesList = UserManager::getUsersFollowedByUser(
1495
                $userId,
1496
                STUDENT_BOSS,
1497
                false,
1498
                false,
1499
                $getCount,
1500
                null,
1501
                null,
1502
                null,
1503
                null,
1504
                null,
1505
                null,
1506
                COURSEMANAGER
1507
            );
1508
1509
            if ($getCount) {
1510
                $studentBossCount = $studentBossesList;
1511
            } else {
1512
                $studentBosses = [];
1513
                if (is_array($studentBossesList)) {
1514
                    foreach ($studentBossesList as $studentBossData) {
1515
                        $studentBosses[] = $studentBossData['user_id'];
1516
                    }
1517
                }
1518
            }
1519
1520
            $teacherList = UserManager::getUsersFollowedByUser(
1521
                $userId,
1522
                COURSEMANAGER,
1523
                false,
1524
                false,
1525
                $getCount,
1526
                null,
1527
                null,
1528
                null,
1529
                null,
1530
                null,
1531
                null,
1532
                COURSEMANAGER
1533
            );
1534
1535
            if ($getCount) {
1536
                $teachersCount = $teacherList;
1537
            } else {
1538
                $teachers = [];
1539
                foreach ($teacherList as $teacherData) {
1540
                    $teachers[] = $teacherData['user_id'];
1541
                }
1542
            }
1543
1544
            $humanResources = UserManager::getUsersFollowedByUser(
1545
                $userId,
1546
                DRH,
1547
                false,
1548
                false,
1549
                $getCount,
1550
                null,
1551
                null,
1552
                null,
1553
                null,
1554
                null,
1555
                null,
1556
                COURSEMANAGER
1557
            );
1558
1559
            if ($getCount) {
1560
                $drhCount = $humanResources;
1561
            } else {
1562
                $humanResourcesList = [];
1563
                foreach ($humanResources as $item) {
1564
                    $humanResourcesList[] = $item['user_id'];
1565
                }
1566
            }
1567
1568
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1569
                $userId,
1570
                COURSEMANAGER,
1571
                null,
1572
                null,
1573
                null,
1574
                null,
1575
                $getCount,
1576
                null,
1577
                null,
1578
                true
1579
            );
1580
1581
            if ($getCount) {
1582
                $assignedCourseCount = $platformCourses;
1583
            } else {
1584
                foreach ($platformCourses as $course) {
1585
                    $assignedCourses[$course['code']] = $course['code'];
1586
                }
1587
            }
1588
1589
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1590
                $userId,
1591
                COURSEMANAGER,
1592
                null,
1593
                null,
1594
                null,
1595
                null,
1596
                $getCount
1597
            );
1598
1599
            if ($getCount) {
1600
                $courseCount = $platformCourses;
1601
            } else {
1602
                foreach ($platformCourses as $course) {
1603
                    $courses[$course['code']] = $course['code'];
1604
                }
1605
            }
1606
1607
            $sessions = SessionManager::getSessionsFollowedByUser(
1608
                $userId,
1609
                COURSEMANAGER,
1610
                null,
1611
                null,
1612
                false
1613
            );
1614
        }
1615
1616
        if ($getCount) {
1617
            return [
1618
                'drh' => $drhCount,
1619
                'teachers' => $teachersCount,
1620
                'student_count' => count($students),
1621
                'student_list' => $students,
1622
                'student_bosses' => $studentBossCount,
1623
                'courses' => $courseCount,
1624
                'session_count' => count($sessions),
1625
                'session_list' => $sessions,
1626
                'assigned_courses' => $assignedCourseCount,
1627
            ];
1628
        }
1629
1630
        return [
1631
            'drh' => $humanResourcesList,
1632
            'teachers' => $teachers,
1633
            'student_list' => $students,
1634
            'student_bosses' => $studentBosses,
1635
            'courses' => $courses,
1636
            'sessions' => $sessions,
1637
            'assigned_courses' => $assignedCourses,
1638
        ];
1639
    }
1640
1641
    /**
1642
     * Calculates the time spent on the platform by a user.
1643
     *
1644
     * @param int|array $userId
1645
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
1646
     * @param string    $start_date       start date date('Y-m-d H:i:s')
1647
     * @param string    $end_date         end date date('Y-m-d H:i:s')
1648
     * @param bool      $returnAllRecords
1649
     *
1650
     * @return int
1651
     */
1652
    public static function get_time_spent_on_the_platform(
1653
        $userId,
1654
        $timeFilter = 'last_7_days',
1655
        $start_date = null,
1656
        $end_date = null,
1657
        $returnAllRecords = false
1658
    ) {
1659
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1660
        $condition_time = '';
1661
1662
        if (is_array($userId)) {
1663
            $userList = array_map('intval', $userId);
1664
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1665
        } else {
1666
            $userId = (int) $userId;
1667
            $userCondition = " login_user_id = $userId ";
1668
        }
1669
1670
        $url_condition = null;
1671
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1672
        $url_table = null;
1673
        if (api_is_multiple_url_enabled()) {
1674
            $access_url_id = api_get_current_access_url_id();
1675
            $url_table = ", $tbl_url_rel_user as url_users";
1676
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1677
        }
1678
1679
        if (empty($timeFilter)) {
1680
            $timeFilter = 'last_week';
1681
        }
1682
1683
        $today = new DateTime('now', new DateTimeZone('UTC'));
1684
1685
        switch ($timeFilter) {
1686
            case 'last_7_days':
1687
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1688
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1689
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1690
                break;
1691
            case 'last_30_days':
1692
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1693
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1694
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1695
                break;
1696
            case 'wide':
1697
                if (!empty($start_date) && !empty($end_date)) {
1698
                    $start_date = Database::escape_string($start_date);
1699
                    $end_date = Database::escape_string($end_date);
1700
                    $condition_time = ' AND (
1701
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
1702
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
1703
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
1704
                    ) ';
1705
                }
1706
                break;
1707
            case 'custom':
1708
                if (!empty($start_date) && !empty($end_date)) {
1709
                    $start_date = Database::escape_string($start_date);
1710
                    $end_date = Database::escape_string($end_date);
1711
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1712
                }
1713
                break;
1714
        }
1715
1716
        if ($returnAllRecords) {
1717
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
1718
                    FROM $tbl_track_login u $url_table
1719
                    WHERE $userCondition $condition_time $url_condition
1720
                    ORDER BY login_date";
1721
            $rs = Database::query($sql);
1722
1723
            return Database::store_result($rs, 'ASSOC');
1724
        }
1725
1726
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1727
    	        FROM $tbl_track_login u $url_table
1728
                WHERE $userCondition $condition_time $url_condition";
1729
        $rs = Database::query($sql);
1730
        $row = Database::fetch_array($rs, 'ASSOC');
1731
        $diff = $row['diff'];
1732
1733
        if ($diff >= 0) {
1734
            return $diff;
1735
        }
1736
1737
        return -1;
1738
    }
1739
1740
    /**
1741
     * @param string $startDate
1742
     * @param string $endDate
1743
     *
1744
     * @return int
1745
     */
1746
    public static function getTotalTimeSpentOnThePlatform(
1747
        $startDate = '',
1748
        $endDate = ''
1749
    ) {
1750
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1751
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1752
1753
        $url_table = null;
1754
        $url_condition = null;
1755
        if (api_is_multiple_url_enabled()) {
1756
            $access_url_id = api_get_current_access_url_id();
1757
            $url_table = ", ".$tbl_url_rel_user." as url_users";
1758
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1759
        }
1760
1761
        if (!empty($startDate) && !empty($endDate)) {
1762
            $startDate = Database::escape_string($startDate);
1763
            $endDate = Database::escape_string($endDate);
1764
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
1765
        }
1766
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1767
    	        FROM $tbl_track_login u $url_table
1768
                WHERE $condition_time $url_condition";
1769
        $rs = Database::query($sql);
1770
        $row = Database::fetch_array($rs, 'ASSOC');
1771
        $diff = $row['diff'];
1772
1773
        if ($diff >= 0) {
1774
            return $diff;
1775
        }
1776
1777
        return -1;
1778
    }
1779
1780
    /**
1781
     * Checks if the "lp_minimum_time" feature is available for the course.
1782
     *
1783
     * @param int $sessionId
1784
     * @param int $courseId
1785
     *
1786
     * @return bool
1787
     */
1788
    public static function minimumTimeAvailable($sessionId, $courseId)
1789
    {
1790
        if (!api_get_configuration_value('lp_minimum_time')) {
1791
            return false;
1792
        }
1793
1794
        if (!empty($sessionId)) {
1795
            $extraFieldValue = new ExtraFieldValue('session');
1796
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
1797
1798
            if ($value && isset($value['value']) && 1 == $value['value']) {
1799
                return true;
1800
            }
1801
        } else {
1802
            if ($courseId) {
1803
                $extraFieldValue = new ExtraFieldValue('course');
1804
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
1805
                if ($value && isset($value['value']) && 1 == $value['value']) {
1806
                    return true;
1807
                }
1808
            }
1809
        }
1810
1811
        return false;
1812
    }
1813
1814
    /**
1815
     * Calculates the time spent on the course.
1816
     *
1817
     * @param int $user_id
1818
     * @param int $courseId
1819
     * @param int $session_id
1820
     *
1821
     * @return int Time in seconds
1822
     */
1823
    public static function get_time_spent_on_the_course(
1824
        $user_id,
1825
        $courseId,
1826
        $session_id = 0
1827
    ) {
1828
        $courseId = (int) $courseId;
1829
1830
        if (empty($courseId) || empty($user_id)) {
1831
            return 0;
1832
        }
1833
1834
        if (self::minimumTimeAvailable($session_id, $courseId)) {
1835
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
1836
1837
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
1838
        }
1839
1840
        $conditionUser = '';
1841
        $session_id = (int) $session_id;
1842
        if (is_array($user_id)) {
1843
            $user_id = array_map('intval', $user_id);
1844
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
1845
        } else {
1846
            if (!empty($user_id)) {
1847
                $user_id = (int) $user_id;
1848
                $conditionUser = " AND user_id = $user_id ";
1849
            }
1850
        }
1851
1852
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1853
        $sql = "SELECT
1854
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1855
                FROM $table
1856
                WHERE
1857
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
1858
                    c_id = '$courseId' ";
1859
1860
        if (-1 != $session_id) {
1861
            $sql .= "AND session_id = '$session_id' ";
1862
        }
1863
1864
        $sql .= $conditionUser;
1865
1866
        $rs = Database::query($sql);
1867
        $row = Database::fetch_array($rs);
1868
1869
        return $row['nb_seconds'];
1870
    }
1871
1872
    /**
1873
     * Get first connection date for a student.
1874
     *
1875
     * @param int $student_id
1876
     *
1877
     * @return string|bool Date format long without day or false if there are no connections
1878
     */
1879
    public static function get_first_connection_date($student_id)
1880
    {
1881
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1882
        $sql = 'SELECT login_date
1883
                FROM '.$table.'
1884
                WHERE login_user_id = '.intval($student_id).'
1885
                ORDER BY login_date ASC
1886
                LIMIT 0,1';
1887
1888
        $rs = Database::query($sql);
1889
        if (Database::num_rows($rs) > 0) {
1890
            if ($first_login_date = Database::result($rs, 0, 0)) {
1891
                return api_convert_and_format_date(
1892
                    $first_login_date,
1893
                    DATE_FORMAT_SHORT
1894
                );
1895
            }
1896
        }
1897
1898
        return false;
1899
    }
1900
1901
    /**
1902
     * Get las connection date for a student.
1903
     *
1904
     * @param int  $student_id
1905
     * @param bool $warning_message  Show a warning message (optional)
1906
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1907
     *
1908
     * @return string|int|bool Date format long without day, false if there are no connections or
1909
     *                         timestamp if parameter $return_timestamp is true
1910
     */
1911
    public static function get_last_connection_date(
1912
        $student_id,
1913
        $warning_message = false,
1914
        $return_timestamp = false
1915
    ) {
1916
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1917
        $sql = 'SELECT login_date
1918
                FROM '.$table.'
1919
                WHERE login_user_id = '.intval($student_id).'
1920
                ORDER BY login_date
1921
                DESC LIMIT 0,1';
1922
1923
        $rs = Database::query($sql);
1924
        if (Database::num_rows($rs) > 0) {
1925
            if ($last_login_date = Database::result($rs, 0, 0)) {
1926
                $last_login_date = api_get_local_time($last_login_date);
1927
                if ($return_timestamp) {
1928
                    return api_strtotime($last_login_date, 'UTC');
1929
                } else {
1930
                    if (!$warning_message) {
1931
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1932
                    } else {
1933
                        $timestamp = api_strtotime($last_login_date, 'UTC');
1934
                        $currentTimestamp = time();
1935
1936
                        //If the last connection is > than 7 days, the text is red
1937
                        //345600 = 7 days in seconds
1938
                        if ($currentTimestamp - $timestamp > 604800) {
1939
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</span>';
1940
                        } else {
1941
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1942
                        }
1943
                    }
1944
                }
1945
            }
1946
        }
1947
1948
        return false;
1949
    }
1950
1951
    /**
1952
     * Get first user's connection date on the course.
1953
     *
1954
     * @param int User id
1955
     * @param int $courseId
1956
     * @param int Session id (optional, default=0)
1957
     * @param bool $convert_date
1958
     *
1959
     * @return string|bool Date with format long without day or false if there is no date
1960
     */
1961
    public static function get_first_connection_date_on_the_course(
1962
        $student_id,
1963
        $courseId,
1964
        $session_id = 0,
1965
        $convert_date = true
1966
    ) {
1967
        $student_id = (int) $student_id;
1968
        $courseId = (int) $courseId;
1969
        $session_id = (int) $session_id;
1970
1971
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1972
        $sql = 'SELECT login_course_date
1973
                FROM '.$table.'
1974
                WHERE
1975
                    user_id = '.$student_id.' AND
1976
                    c_id = '.$courseId.' AND
1977
                    session_id = '.$session_id.'
1978
                ORDER BY login_course_date ASC
1979
                LIMIT 0,1';
1980
        $rs = Database::query($sql);
1981
        if (Database::num_rows($rs) > 0) {
1982
            if ($first_login_date = Database::result($rs, 0, 0)) {
1983
                if (empty($first_login_date)) {
1984
                    return false;
1985
                }
1986
1987
                if ($convert_date) {
1988
                    return api_convert_and_format_date(
1989
                        $first_login_date,
1990
                        DATE_FORMAT_SHORT
1991
                    );
1992
                }
1993
1994
                return $first_login_date;
1995
            }
1996
        }
1997
1998
        return false;
1999
    }
2000
2001
    /**
2002
     * Get last user's connection date on the course.
2003
     *
2004
     * @param     int         User id
2005
     * @param array $courseInfo real_id and code are used
2006
     * @param    int            Session id (optional, default=0)
2007
     * @param bool $convert_date
2008
     *
2009
     * @return string|bool Date with format long without day or false if there is no date
2010
     */
2011
    public static function get_last_connection_date_on_the_course(
2012
        $student_id,
2013
        $courseInfo,
2014
        $session_id = 0,
2015
        $convert_date = true
2016
    ) {
2017
        // protect data
2018
        $student_id = (int) $student_id;
2019
        $session_id = (int) $session_id;
2020
2021
        if (empty($courseInfo) || empty($student_id)) {
2022
            return false;
2023
        }
2024
2025
        $courseId = $courseInfo['real_id'];
2026
2027
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2028
2029
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2030
            // Show the last date on which the user acceed the session when it was active
2031
            $where_condition = '';
2032
            $userInfo = api_get_user_info($student_id);
2033
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
2034
                // fin de acceso a la sesión
2035
                $sessionInfo = SessionManager::fetch($session_id);
2036
                $last_access = $sessionInfo['access_end_date'];
2037
                if (!empty($last_access)) {
2038
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
2039
                }
2040
            }
2041
            $sql = "SELECT logout_course_date
2042
                    FROM $table
2043
                    WHERE   user_id = $student_id AND
2044
                            c_id = $courseId AND
2045
                            session_id = $session_id $where_condition
2046
                    ORDER BY logout_course_date DESC
2047
                    LIMIT 0,1";
2048
2049
            $rs = Database::query($sql);
2050
            if (Database::num_rows($rs) > 0) {
2051
                if ($last_login_date = Database::result($rs, 0, 0)) {
2052
                    if (empty($last_login_date)) {
2053
                        return false;
2054
                    }
2055
                    if ($convert_date) {
2056
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2057
                    }
2058
2059
                    return $last_login_date;
2060
                }
2061
            }
2062
        } else {
2063
            $sql = "SELECT logout_course_date
2064
                    FROM $table
2065
                    WHERE   user_id = $student_id AND
2066
                            c_id = $courseId AND
2067
                            session_id = $session_id
2068
                    ORDER BY logout_course_date DESC
2069
                    LIMIT 0,1";
2070
2071
            $rs = Database::query($sql);
2072
            if (Database::num_rows($rs) > 0) {
2073
                if ($last_login_date = Database::result($rs, 0, 0)) {
2074
                    if (empty($last_login_date)) {
2075
                        return false;
2076
                    }
2077
                    //see #5736
2078
                    $last_login_date_timestamp = api_strtotime($last_login_date);
2079
                    $now = time();
2080
                    //If the last connection is > than 7 days, the text is red
2081
                    //345600 = 7 days in seconds
2082
                    if ($now - $last_login_date_timestamp > 604800) {
2083
                        if ($convert_date) {
2084
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2085
                            $icon = null;
2086
                            if (api_is_allowed_to_edit()) {
2087
                                $url = api_get_path(WEB_CODE_PATH).
2088
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
2089
                                $icon = '<a href="'.$url.'" title="'.get_lang('Remind inactive user').'">
2090
                                  '.Display::return_icon('messagebox_warning.gif').'
2091
                                 </a>';
2092
                            }
2093
2094
                            return $icon.Display::label($last_login_date, 'warning');
2095
                        }
2096
2097
                        return $last_login_date;
2098
                    } else {
2099
                        if ($convert_date) {
2100
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2101
                        }
2102
2103
                        return $last_login_date;
2104
                    }
2105
                }
2106
            }
2107
        }
2108
2109
        return false;
2110
    }
2111
2112
    /**
2113
     * Get last course access by course/session.
2114
     */
2115
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
2116
    {
2117
        $courseId = (int) $courseId;
2118
        $sessionId = (int) $sessionId;
2119
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2120
2121
        $sql = "SELECT logout_course_date
2122
                FROM $table
2123
                WHERE
2124
                        c_id = $courseId AND
2125
                        session_id = $sessionId
2126
                ORDER BY logout_course_date DESC
2127
                LIMIT 0,1";
2128
2129
        $result = Database::query($sql);
2130
        if (Database::num_rows($result)) {
2131
            $row = Database::fetch_array($result);
2132
            if ($row) {
2133
                return $row['logout_course_date'];
2134
            }
2135
        }
2136
2137
        return '';
2138
    }
2139
2140
    /**
2141
     * Get count of the connections to the course during a specified period.
2142
     *
2143
     * @param int $courseId
2144
     * @param   int     Session id (optional)
2145
     * @param   int     Datetime from which to collect data (defaults to 0)
2146
     * @param   int     Datetime to which to collect data (defaults to now)
2147
     *
2148
     * @return int count connections
2149
     */
2150
    public static function get_course_connections_count(
2151
        $courseId,
2152
        $session_id = 0,
2153
        $start = 0,
2154
        $stop = null
2155
    ) {
2156
        if ($start < 0) {
2157
            $start = 0;
2158
        }
2159
        if (!isset($stop) || $stop < 0) {
2160
            $stop = api_get_utc_datetime();
2161
        }
2162
2163
        // Given we're storing in cache, round the start and end times
2164
        // to the lower minute
2165
        $roundedStart = substr($start, 0, -2).'00';
2166
        $roundedStop = substr($stop, 0, -2).'00';
2167
        $roundedStart = Database::escape_string($roundedStart);
2168
        $roundedStop = Database::escape_string($roundedStop);
2169
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
2170
        $courseId = (int) $courseId;
2171
        $session_id = (int) $session_id;
2172
        $count = 0;
2173
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2174
        $sql = "SELECT count(*) as count_connections
2175
                FROM $table
2176
                WHERE
2177
                    c_id = $courseId AND
2178
                    session_id = $session_id
2179
                    $month_filter";
2180
2181
        //This query can be very slow (several seconds on an indexed table
2182
        // with 14M rows). As such, we'll try to use APCu if it is
2183
        // available to store the resulting value for a few seconds
2184
        $cacheAvailable = api_get_configuration_value('apc');
2185
        if (true === $cacheAvailable) {
2186
            $apc = apcu_cache_info(true);
2187
            $apc_end = $apc['start_time'] + $apc['ttl'];
2188
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
2189
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
2190
                apcu_fetch($apc_var) > 0
2191
            ) {
2192
                $count = apcu_fetch($apc_var);
2193
            } else {
2194
                $rs = Database::query($sql);
2195
                if (Database::num_rows($rs) > 0) {
2196
                    $row = Database::fetch_object($rs);
2197
                    $count = $row->count_connections;
2198
                }
2199
                apcu_clear_cache();
2200
                apcu_store($apc_var, $count, 60);
2201
            }
2202
        } else {
2203
            $rs = Database::query($sql);
2204
            if (Database::num_rows($rs) > 0) {
2205
                $row = Database::fetch_object($rs);
2206
                $count = $row->count_connections;
2207
            }
2208
        }
2209
2210
        return $count;
2211
    }
2212
2213
    /**
2214
     * Get count courses per student.
2215
     *
2216
     * @param int  $user_id          Student id
2217
     * @param bool $include_sessions Include sessions (optional)
2218
     *
2219
     * @return int count courses
2220
     */
2221
    public static function count_course_per_student($user_id, $include_sessions = true)
2222
    {
2223
        $user_id = (int) $user_id;
2224
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2225
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2226
2227
        $sql = 'SELECT DISTINCT c_id
2228
                FROM '.$tbl_course_rel_user.'
2229
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
2230
        $rs = Database::query($sql);
2231
        $nb_courses = Database::num_rows($rs);
2232
2233
        if ($include_sessions) {
2234
            $sql = 'SELECT DISTINCT c_id
2235
                    FROM '.$tbl_session_course_rel_user.'
2236
                    WHERE user_id = '.$user_id;
2237
            $rs = Database::query($sql);
2238
            $nb_courses += Database::num_rows($rs);
2239
        }
2240
2241
        return $nb_courses;
2242
    }
2243
2244
    /**
2245
     * Gets the score average from all tests in a course by student.
2246
     *
2247
     * @param $student_id
2248
     * @param $course_code
2249
     * @param int  $exercise_id
2250
     * @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...
2251
     * @param int  $active_filter 2 for consider all tests
2252
     *                            1 for active <> -1
2253
     *                            0 for active <> 0
2254
     * @param int  $into_lp       1 for all exercises
2255
     *                            0 for without LP
2256
     * @param mixed id
2257
     * @param string code
2258
     * @param int id (optional), filtered by exercise
2259
     * @param int id (optional), if param $session_id is null
2260
     *                                                it'll return results including sessions, 0 = session is not filtered
2261
     *
2262
     * @return string value (number %) Which represents a round integer about the score average
2263
     */
2264
    public static function get_avg_student_exercise_score(
2265
        $student_id,
2266
        $course_code,
2267
        $exercise_id = 0,
2268
        $session_id = null,
2269
        $active_filter = 1,
2270
        $into_lp = 0
2271
    ) {
2272
        $course_code = Database::escape_string($course_code);
2273
        $course_info = api_get_course_info($course_code);
2274
        if (!empty($course_info)) {
2275
            // table definition
2276
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2277
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2278
2279
            // Compose a filter based on optional exercise given
2280
            $condition_quiz = '';
2281
            if (!empty($exercise_id)) {
2282
                $exercise_id = (int) $exercise_id;
2283
                $condition_quiz = " AND id = $exercise_id ";
2284
            }
2285
2286
            // Compose a filter based on optional session id given
2287
            $condition_session = '';
2288
            if (isset($session_id)) {
2289
                $session_id = (int) $session_id;
2290
                $condition_session = " AND session_id = $session_id ";
2291
            }
2292
            if (1 == $active_filter) {
2293
                $condition_active = 'AND active <> -1';
2294
            } elseif (0 == $active_filter) {
2295
                $condition_active = 'AND active <> 0';
2296
            } else {
2297
                $condition_active = '';
2298
            }
2299
            $condition_into_lp = '';
2300
            $select_lp_id = '';
2301
            if (0 == $into_lp) {
2302
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2303
            } else {
2304
                $select_lp_id = ', orig_lp_id as lp_id ';
2305
            }
2306
2307
            $sql = "SELECT count(id)
2308
    		        FROM $tbl_course_quiz
2309
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2310
            $count_quiz = 0;
2311
2312
            $countQuizResult = Database::query($sql);
2313
            if (!empty($countQuizResult)) {
2314
                $count_quiz = Database::fetch_row($countQuizResult);
2315
            }
2316
2317
            if (!empty($count_quiz[0]) && !empty($student_id)) {
2318
                if (is_array($student_id)) {
2319
                    $student_id = array_map('intval', $student_id);
2320
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2321
                } else {
2322
                    $student_id = (int) $student_id;
2323
                    $condition_user = " AND exe_user_id = '$student_id' ";
2324
                }
2325
2326
                if (empty($exercise_id)) {
2327
                    $sql = "SELECT id FROM $tbl_course_quiz
2328
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2329
                    $result = Database::query($sql);
2330
                    $exercise_list = [];
2331
                    $exercise_id = null;
2332
                    if (!empty($result) && Database::num_rows($result)) {
2333
                        while ($row = Database::fetch_array($result)) {
2334
                            $exercise_list[] = $row['id'];
2335
                        }
2336
                    }
2337
                    if (!empty($exercise_list)) {
2338
                        $exercise_id = implode("','", $exercise_list);
2339
                    }
2340
                }
2341
2342
                $count_quiz = Database::fetch_row(Database::query($sql));
2343
                $sql = "SELECT
2344
                        SUM(score/max_score*100) as avg_score,
2345
                        COUNT(*) as num_attempts
2346
                        $select_lp_id
2347
                        FROM $tbl_stats_exercise
2348
                        WHERE
2349
                            exe_exo_id IN ('".$exercise_id."')
2350
                            $condition_user AND
2351
                            status = '' AND
2352
                            c_id = {$course_info['real_id']}
2353
                            $condition_session
2354
                            $condition_into_lp
2355
                        ORDER BY exe_date DESC";
2356
2357
                $res = Database::query($sql);
2358
                $row = Database::fetch_array($res);
2359
                $quiz_avg_score = null;
2360
2361
                if (!empty($row['avg_score'])) {
2362
                    $quiz_avg_score = round($row['avg_score'], 2);
2363
                }
2364
2365
                if (!empty($row['num_attempts'])) {
2366
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2367
                }
2368
                if (is_array($student_id)) {
2369
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2370
                }
2371
                if (0 == $into_lp) {
2372
                    return $quiz_avg_score;
2373
                } else {
2374
                    if (!empty($row['lp_id'])) {
2375
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2376
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2377
                        $sql = "SELECT lp.name
2378
                                FROM $tbl_lp as lp, $tbl_course as c
2379
                                WHERE
2380
                                    c.code = '$course_code' AND
2381
                                    lp.id = ".$row['lp_id']." AND
2382
                                    lp.c_id = c.id
2383
                                LIMIT 1;
2384
                        ";
2385
                        $result = Database::query($sql);
2386
                        $row_lp = Database::fetch_row($result);
2387
                        $lp_name = $row_lp[0];
2388
2389
                        return [$quiz_avg_score, $lp_name];
2390
                    }
2391
2392
                    return [$quiz_avg_score, null];
2393
                }
2394
            }
2395
        }
2396
2397
        return null;
2398
    }
2399
2400
    /**
2401
     * Get count student's exercise COMPLETED attempts.
2402
     *
2403
     * @param int $student_id
2404
     * @param int $courseId
2405
     * @param int $exercise_id
2406
     * @param int $lp_id
2407
     * @param int $lp_item_id
2408
     * @param int $session_id
2409
     * @param int $find_all_lp 0 = just LP specified
2410
     *                         1 = LP specified or whitout LP,
2411
     *                         2 = all rows
2412
     *
2413
     * @internal param \Student $int id
2414
     * @internal param \Course $string code
2415
     * @internal param \Exercise $int id
2416
     * @internal param \Learning $int path id (optional),
2417
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2418
     * @internal param \Learning $int path item id (optional),
2419
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2420
     *
2421
     * @return int count of attempts
2422
     */
2423
    public static function count_student_exercise_attempts(
2424
        $student_id,
2425
        $courseId,
2426
        $exercise_id,
2427
        $lp_id = 0,
2428
        $lp_item_id = 0,
2429
        $session_id = 0,
2430
        $find_all_lp = 0
2431
    ) {
2432
        $courseId = intval($courseId);
2433
        $student_id = intval($student_id);
2434
        $exercise_id = intval($exercise_id);
2435
        $session_id = intval($session_id);
2436
2437
        $lp_id = intval($lp_id);
2438
        $lp_item_id = intval($lp_item_id);
2439
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2440
2441
        $sql = "SELECT COUNT(ex.exe_id) as essais
2442
                FROM $tbl_stats_exercises AS ex
2443
                WHERE
2444
                    ex.c_id = $courseId AND
2445
                    ex.exe_exo_id = $exercise_id AND
2446
                    status = '' AND
2447
                    exe_user_id= $student_id AND
2448
                    session_id = $session_id ";
2449
2450
        if (1 == $find_all_lp) {
2451
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2452
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2453
        } elseif (0 == $find_all_lp) {
2454
            $sql .= "AND orig_lp_id = $lp_id
2455
                AND orig_lp_item_id = $lp_item_id";
2456
        }
2457
2458
        $rs = Database::query($sql);
2459
        $row = Database::fetch_row($rs);
2460
        $count_attempts = $row[0];
2461
2462
        return $count_attempts;
2463
    }
2464
2465
    /**
2466
     * Get count student's exercise progress.
2467
     *
2468
     * @param array $exercise_list
2469
     * @param int   $user_id
2470
     * @param int   $courseId
2471
     * @param int   $session_id
2472
     *
2473
     * @return string
2474
     */
2475
    public static function get_exercise_student_progress(
2476
        $exercise_list,
2477
        $user_id,
2478
        $courseId,
2479
        $session_id
2480
    ) {
2481
        $courseId = (int) $courseId;
2482
        $user_id = (int) $user_id;
2483
        $session_id = (int) $session_id;
2484
2485
        if (empty($exercise_list)) {
2486
            return '0%';
2487
        }
2488
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2489
        $exercise_list = array_keys($exercise_list);
2490
        $exercise_list = array_map('intval', $exercise_list);
2491
2492
        $exercise_list_imploded = implode("' ,'", $exercise_list);
2493
2494
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
2495
                FROM $tbl_stats_exercises AS ex
2496
                WHERE
2497
                    ex.c_id = $courseId AND
2498
                    ex.session_id  = $session_id AND
2499
                    ex.exe_user_id = $user_id AND
2500
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
2501
2502
        $rs = Database::query($sql);
2503
        $count = 0;
2504
        if ($rs) {
2505
            $row = Database::fetch_row($rs);
2506
            $count = $row[0];
2507
        }
2508
        $count = (0 != $count) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
2509
2510
        return $count;
2511
    }
2512
2513
    /**
2514
     * @param array $exercise_list
2515
     * @param int   $user_id
2516
     * @param int   $courseId
2517
     * @param int   $session_id
2518
     *
2519
     * @return string
2520
     */
2521
    public static function get_exercise_student_average_best_attempt(
2522
        $exercise_list,
2523
        $user_id,
2524
        $courseId,
2525
        $session_id
2526
    ) {
2527
        $result = 0;
2528
        if (!empty($exercise_list)) {
2529
            foreach ($exercise_list as $exercise_data) {
2530
                $exercise_id = $exercise_data['id'];
2531
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2532
                    $user_id,
2533
                    $exercise_id,
2534
                    $courseId,
2535
                    $session_id
2536
                );
2537
2538
                if (!empty($best_attempt) && !empty($best_attempt['max_score'])) {
2539
                    $result += $best_attempt['score'] / $best_attempt['max_score'];
2540
                }
2541
            }
2542
            $result = $result / count($exercise_list);
2543
            $result = round($result, 2) * 100;
2544
        }
2545
2546
        return $result.'%';
2547
    }
2548
2549
    /**
2550
     * Returns the average student progress in the learning paths of the given
2551
     * course, it will take into account the progress that were not started.
2552
     *
2553
     * @param int|array $studentId
2554
     * @param string    $courseCode
2555
     * @param array     $lpIdList        Limit average to listed lp ids
2556
     * @param int       $sessionId       Session id (optional),
2557
     *                                   if parameter $session_id is null(default) it'll return results including
2558
     *                                   sessions, 0 = session is not filtered
2559
     * @param bool      $returnArray     Will return an array of the type:
2560
     *                                   [sum_of_progresses, number] if it is set to true
2561
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2562
     *
2563
     * @return float Average progress of the user in this course from 0 to 100
2564
     */
2565
    public static function get_avg_student_progress(
2566
        $studentId,
2567
        $courseCode = null,
2568
        $lpIdList = [],
2569
        $sessionId = null,
2570
        $returnArray = false,
2571
        $onlySeriousGame = false
2572
    ) {
2573
        // If there is at least one learning path and one student.
2574
        if (empty($studentId)) {
2575
            return false;
2576
        }
2577
2578
        $sessionId = (int) $sessionId;
2579
        $courseInfo = api_get_course_info($courseCode);
2580
2581
        if (empty($courseInfo)) {
2582
            return false;
2583
        }
2584
2585
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2586
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2587
        $lpConditions = [];
2588
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2589
2590
        if ($sessionId > 0) {
2591
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2592
        } else {
2593
            $lpConditions['AND session_id = ?'] = $sessionId;
2594
        }
2595
2596
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2597
            $placeHolders = [];
2598
            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...
2599
                $placeHolders[] = '?';
2600
            }
2601
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
2602
        }
2603
2604
        if ($onlySeriousGame) {
2605
            $lpConditions['AND seriousgame_mode = ? '] = true;
2606
        }
2607
2608
        $resultLP = Database::select(
2609
            'iid',
2610
            $lPTable,
2611
            ['where' => $lpConditions]
2612
        );
2613
        $filteredLP = array_keys($resultLP);
2614
2615
        if (empty($filteredLP)) {
2616
            return false;
2617
        }
2618
2619
        $conditions = [
2620
            " c_id = {$courseInfo['real_id']} ",
2621
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
2622
        ];
2623
2624
        $groupBy = 'GROUP BY lp_id';
2625
2626
        if (is_array($studentId)) {
2627
            $studentId = array_map('intval', $studentId);
2628
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
2629
        } else {
2630
            $studentId = (int) $studentId;
2631
            $conditions[] = " lp_view.user_id = '$studentId' ";
2632
2633
            if (empty($lpIdList)) {
2634
                $lpList = new LearnpathList(
2635
                    $studentId,
2636
                    $courseInfo,
2637
                    $sessionId,
2638
                    null,
2639
                    false,
2640
                    null,
2641
                    true
2642
                );
2643
                $lpList = $lpList->get_flat_list();
2644
                if (!empty($lpList)) {
2645
                    /** @var $lp */
2646
                    foreach ($lpList as $lpId => $lp) {
2647
                        $lpIdList[] = $lp['lp_old_id'];
2648
                    }
2649
                }
2650
            }
2651
        }
2652
2653
        if (!empty($sessionId)) {
2654
            $conditions[] = " session_id = $sessionId ";
2655
        } else {
2656
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
2657
        }
2658
2659
        $conditionToString = implode('AND', $conditions);
2660
        $sql = "SELECT lp_id, view_count, progress
2661
                FROM $lpViewTable lp_view
2662
                WHERE
2663
                    $conditionToString
2664
                    $groupBy
2665
                ORDER BY view_count DESC";
2666
2667
        $result = Database::query($sql);
2668
2669
        $progress = [];
2670
        $viewCount = [];
2671
        while ($row = Database::fetch_array($result, 'ASSOC')) {
2672
            if (!isset($viewCount[$row['lp_id']])) {
2673
                $progress[$row['lp_id']] = $row['progress'];
2674
            }
2675
            $viewCount[$row['lp_id']] = $row['view_count'];
2676
        }
2677
2678
        // Fill with lp ids
2679
        $newProgress = [];
2680
        if (!empty($lpIdList)) {
2681
            foreach ($lpIdList as $lpId) {
2682
                if (isset($progress[$lpId])) {
2683
                    $newProgress[] = $progress[$lpId];
2684
                }
2685
            }
2686
            $total = count($lpIdList);
2687
        } else {
2688
            $newProgress = $progress;
2689
            $total = count($newProgress);
2690
        }
2691
2692
        $average = 0;
2693
        $sum = 0;
2694
        if (!empty($newProgress)) {
2695
            $sum = array_sum($newProgress);
2696
            $average = $sum / $total;
2697
        }
2698
2699
        if ($returnArray) {
2700
            return [
2701
                $sum,
2702
                $total,
2703
            ];
2704
        }
2705
2706
        return round($average, 1);
2707
    }
2708
2709
    /**
2710
     * This function gets:
2711
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2712
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2713
     * 3. And finally it will return the average between 1. and 2.
2714
     *
2715
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2716
     * This function does not take the results of a Test out of a LP
2717
     *
2718
     * @param mixed  $student_id                      Array of user ids or an user id
2719
     * @param string $course_code
2720
     * @param array  $lp_ids                          List of LP ids
2721
     * @param int    $session_id                      Session id (optional),
2722
     *                                                if param $session_id is null(default) it'll return results
2723
     *                                                including sessions, 0 = session is not filtered
2724
     * @param bool   $return_array                    Returns an array of the
2725
     *                                                type [sum_score, num_score] if set to true
2726
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2727
     * @param bool   $getOnlyBestAttempt
2728
     *
2729
     * @return string value (number %) Which represents a round integer explain in got in 3
2730
     */
2731
    public static function get_avg_student_score(
2732
        $student_id,
2733
        $course_code,
2734
        $lp_ids = [],
2735
        $session_id = null,
2736
        $return_array = false,
2737
        $get_only_latest_attempt_results = false,
2738
        $getOnlyBestAttempt = false
2739
    ) {
2740
        $debug = false;
2741
        if ($debug) {
2742
            echo '<h1>Tracking::get_avg_student_score</h1>';
2743
        }
2744
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2745
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2746
        $course = api_get_course_info($course_code);
2747
2748
        if (empty($course)) {
2749
            return null;
2750
        }
2751
2752
        // Get course tables names
2753
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
2754
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
2755
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
2756
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
2757
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2758
        $course_id = $course['real_id'];
2759
2760
        // Compose a filter based on optional learning paths list given
2761
        $condition_lp = '';
2762
        if (count($lp_ids) > 0) {
2763
            $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
2764
        }
2765
2766
        // Compose a filter based on optional session id
2767
        $session_id = intval($session_id);
2768
        if (count($lp_ids) > 0) {
2769
            $condition_session = " AND session_id = $session_id ";
2770
        } else {
2771
            $condition_session = " WHERE session_id = $session_id ";
2772
        }
2773
2774
        // Check the real number of LPs corresponding to the filter in the
2775
        // database (and if no list was given, get them all)
2776
        if (empty($session_id)) {
2777
            $sql = "SELECT DISTINCT(iid), use_max_score
2778
                    FROM $lp_table
2779
                    WHERE
2780
                        c_id = $course_id AND
2781
                        (session_id = 0 OR session_id IS NULL) $condition_lp ";
2782
        } else {
2783
            $sql = "SELECT DISTINCT(iid), use_max_score
2784
                    FROM $lp_table
2785
                    WHERE c_id = $course_id $condition_lp ";
2786
        }
2787
2788
        $res_row_lp = Database::query($sql);
2789
        $count_row_lp = Database::num_rows($res_row_lp);
2790
2791
        $lp_list = $use_max_score = [];
2792
        while ($row_lp = Database::fetch_array($res_row_lp)) {
2793
            $lp_list[] = $row_lp['id'];
2794
            $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
2795
        }
2796
2797
        // prepare filter on users
2798
        if (is_array($student_id)) {
2799
            array_walk($student_id, 'intval');
2800
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
2801
        } else {
2802
            $condition_user1 = " AND user_id = $student_id ";
2803
        }
2804
2805
        if (empty($count_row_lp) || empty($student_id)) {
2806
            return null;
2807
        }
2808
2809
        // Getting latest LP result for a student
2810
        //@todo problem when a  course have more than 1500 users
2811
        $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
2812
                FROM $lp_view_table
2813
                WHERE
2814
                    c_id = $course_id AND
2815
                    lp_id IN (".implode(',', $lp_list).")
2816
                    $condition_user1 AND
2817
                    session_id = $session_id
2818
                GROUP BY lp_id, user_id";
2819
2820
        $rs_last_lp_view_id = Database::query($sql);
2821
        $global_result = 0;
2822
2823
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
2824
            // Cycle through each line of the results (grouped by lp_id, user_id)
2825
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2826
                $count_items = 0;
2827
                $lpPartialTotal = 0;
2828
                $list = [];
2829
                $lp_view_id = $row_lp_view['id'];
2830
                $lp_id = $row_lp_view['lp_id'];
2831
                $user_id = $row_lp_view['user_id'];
2832
2833
                if ($debug) {
2834
                    echo '<h2>LP id '.$lp_id.'</h2>';
2835
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
2836
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
2837
                }
2838
2839
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
2840
                    // Getting lp_items done by the user
2841
                    $sql = "SELECT DISTINCT lp_item_id
2842
                            FROM $lp_item_view_table
2843
                            WHERE
2844
                                c_id = $course_id AND
2845
                                lp_view_id = $lp_view_id
2846
                            ORDER BY lp_item_id";
2847
                    $res_lp_item = Database::query($sql);
2848
2849
                    while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
2850
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
2851
                        $order = ' view_count DESC';
2852
                        if ($getOnlyBestAttempt) {
2853
                            $order = ' lp_iv.score DESC';
2854
                        }
2855
2856
                        // Getting the most recent attempt
2857
                        $sql = "SELECT
2858
                                    lp_iv.id as lp_item_view_id,
2859
                                    lp_iv.score as score,
2860
                                    lp_i.max_score,
2861
                                    lp_iv.max_score as max_score_item_view,
2862
                                    lp_i.path,
2863
                                    lp_i.item_type,
2864
                                    lp_i.id as iid
2865
                                FROM $lp_item_view_table as lp_iv
2866
                                INNER JOIN $lp_item_table as lp_i
2867
                                ON (
2868
                                    lp_i.id = lp_iv.lp_item_id AND
2869
                                    lp_iv.c_id = lp_i.c_id
2870
                                )
2871
                                WHERE
2872
                                    lp_iv.c_id = $course_id AND
2873
                                    lp_i.c_id  = $course_id AND
2874
                                    lp_item_id = $my_lp_item_id AND
2875
                                    lp_view_id = $lp_view_id AND
2876
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2877
                                ORDER BY $order
2878
                                LIMIT 1";
2879
2880
                        $res_lp_item_result = Database::query($sql);
2881
                        while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
2882
                            $list[] = $row_max_score;
2883
                        }
2884
                    }
2885
                } else {
2886
                    // For the currently analysed view, get the score and
2887
                    // max_score of each item if it is a sco or a TOOL_QUIZ
2888
                    $sql = "SELECT
2889
                                lp_iv.id as lp_item_view_id,
2890
                                lp_iv.score as score,
2891
                                lp_i.max_score,
2892
                                lp_iv.max_score as max_score_item_view,
2893
                                lp_i.path,
2894
                                lp_i.item_type,
2895
                                lp_i.id as iid
2896
                              FROM $lp_item_view_table as lp_iv
2897
                              INNER JOIN $lp_item_table as lp_i
2898
                              ON lp_i.id = lp_iv.lp_item_id AND
2899
                                 lp_iv.c_id = lp_i.c_id
2900
                              WHERE
2901
                                lp_iv.c_id = $course_id AND
2902
                                lp_i.c_id  = $course_id AND
2903
                                lp_view_id = $lp_view_id AND
2904
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2905
                            ";
2906
                    $res_max_score = Database::query($sql);
2907
                    while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
2908
                        $list[] = $row_max_score;
2909
                    }
2910
                }
2911
2912
                // Go through each scorable element of this view
2913
                $score_of_scorm_calculate = 0;
2914
                foreach ($list as $row_max_score) {
2915
                    // Came from the original lp_item
2916
                    $max_score = $row_max_score['max_score'];
2917
                    // Came from the lp_item_view
2918
                    $max_score_item_view = $row_max_score['max_score_item_view'];
2919
                    $score = $row_max_score['score'];
2920
                    if ($debug) {
2921
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
2922
                    }
2923
2924
                    if ('sco' == $row_max_score['item_type']) {
2925
                        /* Check if it is sco (easier to get max_score)
2926
                           when there's no max score, we assume 100 as the max score,
2927
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
2928
                        */
2929
                        if (0 == $max_score || is_null($max_score) || '' == $max_score) {
2930
                            // Chamilo style
2931
                            if ($use_max_score[$lp_id]) {
2932
                                $max_score = 100;
2933
                            } else {
2934
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
2935
                                $max_score = $max_score_item_view;
2936
                            }
2937
                        }
2938
                        // Avoid division by zero errors
2939
                        if (!empty($max_score)) {
2940
                            $lpPartialTotal += $score / $max_score;
2941
                        }
2942
                        if ($debug) {
2943
                            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...
2944
                            var_dump("score: $score");
2945
                            var_dump("max_score: $max_score");
2946
                        }
2947
                    } else {
2948
                        // Case of a TOOL_QUIZ element
2949
                        $item_id = $row_max_score['iid'];
2950
                        $item_path = $row_max_score['path'];
2951
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
2952
2953
                        $lpItemCondition = '';
2954
                        if (empty($lp_item_view_id)) {
2955
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
2956
                        } else {
2957
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
2958
                        }
2959
2960
                        // Get last attempt to this exercise through
2961
                        // the current lp for the current user
2962
                        $order = 'exe_date DESC';
2963
                        if ($getOnlyBestAttempt) {
2964
                            $order = 'score DESC';
2965
                        }
2966
                        $sql = "SELECT exe_id, score
2967
                                FROM $tbl_stats_exercices
2968
                                WHERE
2969
                                    exe_exo_id = '$item_path' AND
2970
                                    exe_user_id = $user_id AND
2971
                                    orig_lp_item_id = $item_id AND
2972
                                    $lpItemCondition AND
2973
                                    c_id = $course_id AND
2974
                                    session_id = $session_id AND
2975
                                    status = ''
2976
                                ORDER BY $order
2977
                                LIMIT 1";
2978
2979
                        $result_last_attempt = Database::query($sql);
2980
                        $num = Database::num_rows($result_last_attempt);
2981
                        if ($num > 0) {
2982
                            $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
2983
                            $id_last_attempt = $attemptResult['exe_id'];
2984
                            // We overwrite the score with the best one not the one saved in the LP (latest)
2985
                            if ($getOnlyBestAttempt && false == $get_only_latest_attempt_results) {
2986
                                if ($debug) {
2987
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
2988
                                }
2989
                                $score = $attemptResult['score'];
2990
                            }
2991
2992
                            if ($debug) {
2993
                                echo "Attempt id: $id_last_attempt with score $score<br />";
2994
                            }
2995
                            // Within the last attempt number tracking, get the sum of
2996
                            // the max_scores of all questions that it was
2997
                            // made of (we need to make this call dynamic because of random questions selection)
2998
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
2999
                                            (
3000
                                                SELECT DISTINCT
3001
                                                    question_id,
3002
                                                    marks,
3003
                                                    ponderation
3004
                                                FROM $tbl_stats_attempts AS at
3005
                                                INNER JOIN $tbl_quiz_questions AS q
3006
                                                ON (q.id = at.question_id AND q.c_id = q.c_id)
3007
                                                WHERE
3008
                                                    exe_id ='$id_last_attempt' AND
3009
                                                    q.c_id = $course_id
3010
                                            )
3011
                                            AS t";
3012
3013
                            $res_max_score_bis = Database::query($sql);
3014
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
3015
3016
                            if (!empty($row_max_score_bis['maxscore'])) {
3017
                                $max_score = $row_max_score_bis['maxscore'];
3018
                            }
3019
                            if (!empty($max_score) && floatval($max_score) > 0) {
3020
                                $lpPartialTotal += $score / $max_score;
3021
                            }
3022
                            if ($debug) {
3023
                                var_dump("score: $score");
3024
                                var_dump("max_score: $max_score");
3025
                                var_dump("lpPartialTotal: $lpPartialTotal");
3026
                            }
3027
                        }
3028
                    }
3029
3030
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
3031
                        // Normal way
3032
                        if ($use_max_score[$lp_id]) {
3033
                            $count_items++;
3034
                        } else {
3035
                            if ('' != $max_score) {
3036
                                $count_items++;
3037
                            }
3038
                        }
3039
                        if ($debug) {
3040
                            echo '$count_items: '.$count_items;
3041
                        }
3042
                    }
3043
                } //end for
3044
3045
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
3046
                $global_result += $score_of_scorm_calculate;
3047
3048
                if ($debug) {
3049
                    var_dump("count_items: $count_items");
3050
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
3051
                    var_dump("global_result: $global_result");
3052
                }
3053
            } // end while
3054
        }
3055
3056
        $lp_with_quiz = 0;
3057
        foreach ($lp_list as $lp_id) {
3058
            // Check if LP have a score we assume that all SCO have an score
3059
            $sql = "SELECT count(id) as count
3060
                    FROM $lp_item_table
3061
                    WHERE
3062
                        c_id = $course_id AND
3063
                        (item_type = 'quiz' OR item_type = 'sco') AND
3064
                        lp_id = ".$lp_id;
3065
            $result_have_quiz = Database::query($sql);
3066
            if (Database::num_rows($result_have_quiz) > 0) {
3067
                $row = Database::fetch_array($result_have_quiz, 'ASSOC');
3068
                if (is_numeric($row['count']) && 0 != $row['count']) {
3069
                    $lp_with_quiz++;
3070
                }
3071
            }
3072
        }
3073
3074
        if ($debug) {
3075
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
3076
        }
3077
        if ($debug) {
3078
            echo '<h3>Final return</h3>';
3079
        }
3080
3081
        if (0 != $lp_with_quiz) {
3082
            if (!$return_array) {
3083
                $score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
3084
                if ($debug) {
3085
                    var_dump($score_of_scorm_calculate);
3086
                }
3087
                if (empty($lp_ids)) {
3088
                    if ($debug) {
3089
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
3090
                    }
3091
                }
3092
3093
                return $score_of_scorm_calculate;
3094
            }
3095
3096
            if ($debug) {
3097
                var_dump($global_result, $lp_with_quiz);
3098
            }
3099
3100
            return [$global_result, $lp_with_quiz];
3101
        }
3102
3103
        return '-';
3104
    }
3105
3106
    /**
3107
     * This function gets:
3108
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3109
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3110
     * 3. And finally it will return the average between 1. and 2.
3111
     * This function does not take the results of a Test out of a LP.
3112
     *
3113
     * @param int|array $student_id  Array of user ids or an user id
3114
     * @param string    $course_code Course code
3115
     * @param array     $lp_ids      List of LP ids
3116
     * @param int       $session_id  Session id (optional), if param $session_id is 0(default)
3117
     *                               it'll return results including sessions, 0 = session is not filtered
3118
     *
3119
     * @return string value (number %) Which represents a round integer explain in got in 3
3120
     */
3121
    public static function getAverageStudentScore(
3122
        $student_id,
3123
        $course_code = '',
3124
        $lp_ids = [],
3125
        $session_id = 0
3126
    ) {
3127
        if (empty($student_id)) {
3128
            return 0;
3129
        }
3130
3131
        $conditions = [];
3132
        if (!empty($course_code)) {
3133
            $course = api_get_course_info($course_code);
3134
            $courseId = $course['real_id'];
3135
            $conditions[] = " lp.c_id = $courseId";
3136
        }
3137
3138
        // Get course tables names
3139
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3140
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3141
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3142
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3143
3144
        // Compose a filter based on optional learning paths list given
3145
        if (!empty($lp_ids) && count($lp_ids) > 0) {
3146
            $conditions[] = ' lp.id IN ('.implode(',', $lp_ids).') ';
3147
        }
3148
3149
        // Compose a filter based on optional session id
3150
        $session_id = (int) $session_id;
3151
        if (!empty($session_id)) {
3152
            $conditions[] = " lp_view.session_id = $session_id ";
3153
        }
3154
3155
        if (is_array($student_id)) {
3156
            array_walk($student_id, 'intval');
3157
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
3158
        } else {
3159
            $student_id = (int) $student_id;
3160
            $conditions[] = " lp_view.user_id = $student_id ";
3161
        }
3162
3163
        $conditionsToString = implode('AND ', $conditions);
3164
        $sql = "SELECT
3165
                    SUM(lp_iv.score) sum_score,
3166
                    SUM(lp_i.max_score) sum_max_score
3167
                FROM $lp_table as lp
3168
                INNER JOIN $lp_item_table as lp_i
3169
                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
3170
                INNER JOIN $lp_view_table as lp_view
3171
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
3172
                INNER JOIN $lp_item_view_table as lp_iv
3173
                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
3174
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
3175
                $conditionsToString
3176
        ";
3177
        $result = Database::query($sql);
3178
        $row = Database::fetch_array($result, 'ASSOC');
3179
3180
        if (empty($row['sum_max_score'])) {
3181
            return 0;
3182
        }
3183
3184
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
3185
    }
3186
3187
    /**
3188
     * This function gets time spent in learning path for a student inside a course.
3189
     *
3190
     * @param int|array $student_id  Student id(s)
3191
     * @param string    $course_code Course code
3192
     * @param array     $lp_ids      Limit average to listed lp ids
3193
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
3194
     *                               it'll return results including sessions, 0 = session is not filtered
3195
     *
3196
     * @return int Total time in seconds
3197
     */
3198
    public static function get_time_spent_in_lp(
3199
        $student_id,
3200
        $course_code,
3201
        $lp_ids = [],
3202
        $session_id = 0
3203
    ) {
3204
        $course = api_get_course_info($course_code);
3205
        $student_id = (int) $student_id;
3206
        $session_id = (int) $session_id;
3207
        $total_time = 0;
3208
3209
        if (!empty($course)) {
3210
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
3211
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
3212
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3213
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3214
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3215
            $course_id = $course['real_id'];
3216
3217
            // Compose a filter based on optional learning paths list given
3218
            $condition_lp = '';
3219
            if (count($lp_ids) > 0) {
3220
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3221
            }
3222
3223
            // Check the real number of LPs corresponding to the filter in the
3224
            // database (and if no list was given, get them all)
3225
            $sql = "SELECT DISTINCT(id) FROM $lpTable
3226
                    WHERE c_id = $course_id $condition_lp";
3227
            $result = Database::query($sql);
3228
            $session_condition = api_get_session_condition($session_id);
3229
3230
            // calculates time
3231
            if (Database::num_rows($result) > 0) {
3232
                while ($row = Database::fetch_array($result)) {
3233
                    $lp_id = (int) $row['id'];
3234
3235
                    // Start Exercise in LP total_time
3236
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
3237
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
3238
                    foreach ($list as $itemId) {
3239
                        $sql = "SELECT max(view_count)
3240
                                FROM $lpViewTable
3241
                                WHERE
3242
                                    c_id = $course_id AND
3243
                                    lp_id = $lp_id AND
3244
                                    user_id = $student_id
3245
                                    $session_condition";
3246
                        $res = Database::query($sql);
3247
                        $view = '';
3248
                        if (Database::num_rows($res) > 0) {
3249
                            $myrow = Database::fetch_array($res);
3250
                            $view = $myrow[0];
3251
                        }
3252
                        $viewCondition = null;
3253
                        if (!empty($view)) {
3254
                            $viewCondition = " AND v.view_count = $view  ";
3255
                        }
3256
                        $sql = "SELECT
3257
                            iv.iid,
3258
                            iv.total_time as mytime,
3259
                            i.id as myid,
3260
                            iv.view_count as iv_view_count,
3261
                            path
3262
                        FROM $lpItemTable as i
3263
                        INNER JOIN $lpItemViewTable as iv
3264
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
3265
                        INNER JOIN $lpViewTable as v
3266
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
3267
                        WHERE
3268
                            v.c_id = $course_id AND
3269
                            i.id = $itemId AND
3270
                            i.lp_id = $lp_id  AND
3271
                            v.user_id = $student_id AND
3272
                            item_type = 'quiz' AND
3273
                            path <> '' AND
3274
                            v.session_id = $session_id
3275
                            $viewCondition
3276
                        ORDER BY iv.view_count DESC ";
3277
3278
                        $resultRow = Database::query($sql);
3279
                        if (Database::num_rows($resultRow)) {
3280
                            $row = Database::fetch_array($resultRow);
3281
                            $totalTimeInLpItemView = $row['mytime'];
3282
                            $lpItemViewId = $row['iid'];
3283
3284
                            $sql = 'SELECT SUM(exe_duration) exe_duration
3285
                                    FROM '.$trackExercises.'
3286
                                    WHERE
3287
                                        exe_exo_id="'.$row['path'].'" AND
3288
                                        exe_user_id="'.$student_id.'" AND
3289
                                        orig_lp_id = "'.$lp_id.'" AND
3290
                                        orig_lp_item_id = "'.$row['myid'].'" AND
3291
                                        c_id = '.$course_id.' AND
3292
                                        status <> "incomplete" AND
3293
                                        session_id = '.$session_id.'
3294
                                     ORDER BY exe_date DESC ';
3295
3296
                            $sumScoreResult = Database::query($sql);
3297
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
3298
                            if (!empty($durationRow['exe_duration'])) {
3299
                                $exeDuration = $durationRow['exe_duration'];
3300
                                if ($exeDuration != $totalTimeInLpItemView &&
3301
                                    !empty($lpItemViewId) &&
3302
                                    !empty($exeDuration)
3303
                                ) {
3304
                                    // Update c_lp_item_view.total_time
3305
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration'
3306
                                                  WHERE iid = ".$lpItemViewId;
3307
                                    Database::query($sqlUpdate);
3308
                                }
3309
                            }
3310
                        }
3311
                    }
3312
3313
                    // End total_time fix
3314
3315
                    // Calculate total time
3316
                    $sql = "SELECT SUM(total_time)
3317
                            FROM $lpItemViewTable AS item_view
3318
                            INNER JOIN $lpViewTable AS view
3319
                            ON (
3320
                                item_view.lp_view_id = view.id AND
3321
                                item_view.c_id = view.c_id
3322
                            )
3323
                            WHERE
3324
                                item_view.c_id = $course_id AND
3325
                                view.c_id = $course_id AND
3326
                                view.lp_id = $lp_id AND
3327
                                view.user_id = $student_id AND
3328
                                session_id = $session_id";
3329
3330
                    $rs = Database::query($sql);
3331
                    if (Database::num_rows($rs) > 0) {
3332
                        $total_time += Database::result($rs, 0, 0);
3333
                    }
3334
                }
3335
            }
3336
        }
3337
3338
        return $total_time;
3339
    }
3340
3341
    /**
3342
     * This function gets last connection time to one learning path.
3343
     *
3344
     * @param int|array $student_id  Student id(s)
3345
     * @param string    $course_code Course code
3346
     * @param int       $lp_id       Learning path id
3347
     * @param int       $session_id
3348
     *
3349
     * @return int last connection timestamp
3350
     */
3351
    public static function get_last_connection_time_in_lp(
3352
        $student_id,
3353
        $course_code,
3354
        $lp_id,
3355
        $session_id = 0
3356
    ) {
3357
        $course = api_get_course_info($course_code);
3358
        if (empty($course)) {
3359
            return 0;
3360
        }
3361
3362
        $course_id = $course['real_id'];
3363
        $student_id = (int) $student_id;
3364
        $lp_id = (int) $lp_id;
3365
        $session_id = (int) $session_id;
3366
        $lastTime = 0;
3367
3368
        if (self::minimumTimeAvailable($session_id, $course_id)) {
3369
            $sql = "SELECT MAX(date_reg) max
3370
                    FROM track_e_access_complete
3371
                    WHERE
3372
                        user_id = $student_id AND
3373
                        c_id = $course_id AND
3374
                        session_id = $session_id AND
3375
                        tool = 'learnpath' AND
3376
                        tool_id = $lp_id AND
3377
                        action = 'view' AND
3378
                        login_as = 0
3379
                    ORDER BY date_reg ASC
3380
                    LIMIT 1";
3381
            $rs = Database::query($sql);
3382
3383
            $lastConnection = 0;
3384
            if (Database::num_rows($rs) > 0) {
3385
                $value = Database::fetch_array($rs);
3386
                if (isset($value['max']) && !empty($value['max'])) {
3387
                    $lastConnection = api_strtotime($value['max'], 'UTC');
3388
                }
3389
            }
3390
3391
            if (!empty($lastConnection)) {
3392
                return $lastConnection;
3393
            }
3394
        }
3395
        if (!empty($course)) {
3396
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3397
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3398
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3399
3400
            // Check the real number of LPs corresponding to the filter in the
3401
            // database (and if no list was given, get them all)
3402
            $sql = "SELECT id FROM $lp_table
3403
                    WHERE c_id = $course_id AND id = $lp_id ";
3404
            $row = Database::query($sql);
3405
            $count = Database::num_rows($row);
3406
3407
            // calculates last connection time
3408
            if ($count > 0) {
3409
                $sql = 'SELECT MAX(start_time)
3410
                        FROM '.$t_lpiv.' AS item_view
3411
                        INNER JOIN '.$t_lpv.' AS view
3412
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3413
                        WHERE
3414
                            status != "not attempted" AND
3415
                            item_view.c_id = '.$course_id.' AND
3416
                            view.c_id = '.$course_id.' AND
3417
                            view.lp_id = '.$lp_id.' AND
3418
                            view.user_id = '.$student_id.' AND
3419
                            view.session_id = '.$session_id;
3420
                $rs = Database::query($sql);
3421
                if (Database::num_rows($rs) > 0) {
3422
                    $lastTime = Database::result($rs, 0, 0);
3423
                }
3424
            }
3425
        }
3426
3427
        return $lastTime;
3428
    }
3429
3430
    public static function getFirstConnectionTimeInLp(
3431
        $student_id,
3432
        $course_code,
3433
        $lp_id,
3434
        $session_id = 0
3435
    ) {
3436
        $course = api_get_course_info($course_code);
3437
        $student_id = (int) $student_id;
3438
        $lp_id = (int) $lp_id;
3439
        $session_id = (int) $session_id;
3440
        $time = 0;
3441
3442
        if (!empty($course)) {
3443
            $course_id = $course['real_id'];
3444
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3445
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3446
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3447
3448
            // Check the real number of LPs corresponding to the filter in the
3449
            // database (and if no list was given, get them all)
3450
            $sql = "SELECT id FROM $lp_table
3451
                    WHERE c_id = $course_id AND id = $lp_id ";
3452
            $row = Database::query($sql);
3453
            $count = Database::num_rows($row);
3454
3455
            // calculates first connection time
3456
            if ($count > 0) {
3457
                $sql = 'SELECT MIN(start_time)
3458
                        FROM '.$t_lpiv.' AS item_view
3459
                        INNER JOIN '.$t_lpv.' AS view
3460
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3461
                        WHERE
3462
                            status != "not attempted" AND
3463
                            item_view.c_id = '.$course_id.' AND
3464
                            view.c_id = '.$course_id.' AND
3465
                            view.lp_id = '.$lp_id.' AND
3466
                            view.user_id = '.$student_id.' AND
3467
                            view.session_id = '.$session_id;
3468
                $rs = Database::query($sql);
3469
                if (Database::num_rows($rs) > 0) {
3470
                    $time = Database::result($rs, 0, 0);
3471
                }
3472
            }
3473
        }
3474
3475
        return $time;
3476
    }
3477
3478
    /**
3479
     * gets the list of students followed by coach.
3480
     *
3481
     * @param int $coach_id Coach id
3482
     *
3483
     * @return array List of students
3484
     */
3485
    public static function get_student_followed_by_coach($coach_id)
3486
    {
3487
        $coach_id = intval($coach_id);
3488
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3489
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3490
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3491
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3492
3493
        $students = [];
3494
        // At first, courses where $coach_id is coach of the course //
3495
        $sql = 'SELECT session_id, c_id
3496
                FROM '.$tbl_session_course_user.'
3497
                WHERE user_id='.$coach_id.' AND status=2';
3498
3499
        if (api_is_multiple_url_enabled()) {
3500
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3501
            $access_url_id = api_get_current_access_url_id();
3502
            if (-1 != $access_url_id) {
3503
                $sql = 'SELECT scu.session_id, scu.c_id
3504
                        FROM '.$tbl_session_course_user.' scu
3505
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
3506
                        ON (scu.session_id=sru.session_id)
3507
                        WHERE
3508
                            scu.user_id='.$coach_id.' AND
3509
                            scu.status=2 AND
3510
                            sru.access_url_id = '.$access_url_id;
3511
            }
3512
        }
3513
3514
        $result = Database::query($sql);
3515
3516
        while ($a_courses = Database::fetch_array($result)) {
3517
            $courseId = $a_courses['c_id'];
3518
            $id_session = $a_courses['session_id'];
3519
3520
            $sql = "SELECT DISTINCT srcru.user_id
3521
                    FROM $tbl_session_course_user AS srcru
3522
                    INNER JOIN $tbl_session_user sru
3523
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
3524
                    WHERE
3525
                        sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
3526
                        srcru.c_id = '$courseId' AND
3527
                        srcru.session_id = '$id_session'";
3528
3529
            $rs = Database::query($sql);
3530
            while ($row = Database::fetch_array($rs)) {
3531
                $students[$row['user_id']] = $row['user_id'];
3532
            }
3533
        }
3534
3535
        // Then, courses where $coach_id is coach of the session
3536
        $sql = 'SELECT session_course_user.user_id
3537
                FROM '.$tbl_session_course_user.' as session_course_user
3538
                INNER JOIN '.$tbl_session_user.' sru
3539
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
3540
                INNER JOIN '.$tbl_session_course.' as session_course
3541
                ON session_course.c_id = session_course_user.c_id
3542
                AND session_course_user.session_id = session_course.session_id
3543
                INNER JOIN '.$tbl_session.' as session
3544
                ON session.id = session_course.session_id
3545
                AND session.id_coach = '.$coach_id;
3546
        if (api_is_multiple_url_enabled()) {
3547
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3548
            $access_url_id = api_get_current_access_url_id();
3549
            if (-1 != $access_url_id) {
3550
                $sql = 'SELECT session_course_user.user_id
3551
                        FROM '.$tbl_session_course_user.' as session_course_user
3552
                        INNER JOIN '.$tbl_session_user.' sru
3553
                        ON session_course_user.user_id = sru.user_id AND
3554
                           session_course_user.session_id = sru.session_id
3555
                        INNER JOIN '.$tbl_session_course.' as session_course
3556
                        ON session_course.c_id = session_course_user.c_id AND
3557
                        session_course_user.session_id = session_course.session_id
3558
                        INNER JOIN '.$tbl_session.' as session
3559
                        ON session.id = session_course.session_id AND
3560
                        session.id_coach = '.$coach_id.'
3561
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
3562
                        ON session.id = session_rel_url.session_id
3563
                        WHERE access_url_id = '.$access_url_id;
3564
            }
3565
        }
3566
3567
        $result = Database::query($sql);
3568
        while ($row = Database::fetch_array($result)) {
3569
            $students[$row['user_id']] = $row['user_id'];
3570
        }
3571
3572
        return $students;
3573
    }
3574
3575
    /**
3576
     * Check if a coach is allowed to follow a student.
3577
     *
3578
     * @param    int        Coach id
3579
     * @param    int        Student id
3580
     *
3581
     * @return bool
3582
     */
3583
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3584
    {
3585
        $coach_id = intval($coach_id);
3586
        $student_id = intval($student_id);
3587
3588
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3589
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3590
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3591
3592
        // At first, courses where $coach_id is coach of the course
3593
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
3594
                WHERE user_id='.$coach_id.' AND status=2';
3595
        $result = Database::query($sql);
3596
        if (Database::num_rows($result) > 0) {
3597
            return true;
3598
        }
3599
3600
        // Then, courses where $coach_id is coach of the session
3601
        $sql = 'SELECT session_course_user.user_id
3602
                FROM '.$tbl_session_course_user.' as session_course_user
3603
                INNER JOIN '.$tbl_session_course.' as session_course
3604
                ON session_course.c_id = session_course_user.c_id
3605
                INNER JOIN '.$tbl_session.' as session
3606
                ON session.id = session_course.session_id
3607
                AND session.id_coach = '.$coach_id.'
3608
                WHERE user_id = '.$student_id;
3609
        $result = Database::query($sql);
3610
        if (Database::num_rows($result) > 0) {
3611
            return true;
3612
        }
3613
3614
        return false;
3615
    }
3616
3617
    /**
3618
     * Get courses followed by coach.
3619
     *
3620
     * @param     int        Coach id
3621
     * @param    int        Session id (optional)
3622
     *
3623
     * @return array Courses list
3624
     */
3625
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
3626
    {
3627
        $coach_id = intval($coach_id);
3628
        if (!empty($id_session)) {
3629
            $id_session = intval($id_session);
3630
        }
3631
3632
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3633
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3634
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3635
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3636
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3637
3638
        // At first, courses where $coach_id is coach of the course.
3639
        $sql = 'SELECT DISTINCT c.code
3640
                FROM '.$tbl_session_course_user.' sc
3641
                INNER JOIN '.$tbl_course.' c
3642
                ON (c.id = sc.c_id)
3643
                WHERE user_id = '.$coach_id.' AND status = 2';
3644
3645
        if (api_is_multiple_url_enabled()) {
3646
            $access_url_id = api_get_current_access_url_id();
3647
            if (-1 != $access_url_id) {
3648
                $sql = 'SELECT DISTINCT c.code
3649
                        FROM '.$tbl_session_course_user.' scu
3650
                        INNER JOIN '.$tbl_course.' c
3651
                        ON (c.code = scu.c_id)
3652
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3653
                        ON (c.id = cru.c_id)
3654
                        WHERE
3655
                            scu.user_id='.$coach_id.' AND
3656
                            scu.status=2 AND
3657
                            cru.access_url_id = '.$access_url_id;
3658
            }
3659
        }
3660
3661
        if (!empty($id_session)) {
3662
            $sql .= ' AND session_id='.$id_session;
3663
        }
3664
3665
        $courseList = [];
3666
        $result = Database::query($sql);
3667
        while ($row = Database::fetch_array($result)) {
3668
            $courseList[$row['code']] = $row['code'];
3669
        }
3670
3671
        // Then, courses where $coach_id is coach of the session
3672
        $sql = 'SELECT DISTINCT course.code
3673
                FROM '.$tbl_session_course.' as session_course
3674
                INNER JOIN '.$tbl_session.' as session
3675
                    ON session.id = session_course.session_id
3676
                    AND session.id_coach = '.$coach_id.'
3677
                INNER JOIN '.$tbl_course.' as course
3678
                    ON course.id = session_course.c_id';
3679
3680
        if (api_is_multiple_url_enabled()) {
3681
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3682
            $access_url_id = api_get_current_access_url_id();
3683
            if (-1 != $access_url_id) {
3684
                $sql = 'SELECT DISTINCT c.code
3685
                    FROM '.$tbl_session_course.' as session_course
3686
                    INNER JOIN '.$tbl_course.' c
3687
                    ON (c.id = session_course.c_id)
3688
                    INNER JOIN '.$tbl_session.' as session
3689
                    ON session.id = session_course.session_id
3690
                        AND session.id_coach = '.$coach_id.'
3691
                    INNER JOIN '.$tbl_course.' as course
3692
                        ON course.id = session_course.c_id
3693
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
3694
                    ON (course_rel_url.c_id = c.id)';
3695
            }
3696
        }
3697
3698
        if (!empty($id_session)) {
3699
            $sql .= ' WHERE session_course.session_id='.$id_session;
3700
            if (api_is_multiple_url_enabled()) {
3701
                $sql .= ' AND access_url_id = '.$access_url_id;
3702
            }
3703
        } else {
3704
            if (api_is_multiple_url_enabled()) {
3705
                $sql .= ' WHERE access_url_id = '.$access_url_id;
3706
            }
3707
        }
3708
3709
        $result = Database::query($sql);
3710
        while ($row = Database::fetch_array($result)) {
3711
            $courseList[$row['code']] = $row['code'];
3712
        }
3713
3714
        return $courseList;
3715
    }
3716
3717
    /**
3718
     * Get sessions coached by user.
3719
     *
3720
     * @param int    $coach_id
3721
     * @param int    $start
3722
     * @param int    $limit
3723
     * @param bool   $getCount
3724
     * @param string $keyword
3725
     * @param string $description
3726
     * @param string $orderByName
3727
     * @param string $orderByDirection
3728
     * @param array  $options
3729
     *
3730
     * @return mixed
3731
     */
3732
    public static function get_sessions_coached_by_user(
3733
        $coach_id,
3734
        $start = 0,
3735
        $limit = 0,
3736
        $getCount = false,
3737
        $keyword = '',
3738
        $description = '',
3739
        $orderByName = '',
3740
        $orderByDirection = '',
3741
        $options = []
3742
    ) {
3743
        // table definition
3744
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3745
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3746
        $coach_id = (int) $coach_id;
3747
3748
        $select = ' SELECT * FROM ';
3749
        if ($getCount) {
3750
            $select = ' SELECT count(DISTINCT id) as count FROM ';
3751
        }
3752
3753
        $limitCondition = null;
3754
        if (!empty($start) && !empty($limit)) {
3755
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3756
        }
3757
3758
        $keywordCondition = null;
3759
        if (!empty($keyword)) {
3760
            $keyword = Database::escape_string($keyword);
3761
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
3762
3763
            if (!empty($description)) {
3764
                $description = Database::escape_string($description);
3765
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3766
            }
3767
        }
3768
3769
        $extraFieldModel = new ExtraFieldModel('session');
3770
        $conditions = $extraFieldModel->parseConditions($options);
3771
        $sqlInjectJoins = $conditions['inject_joins'];
3772
        $extraFieldsConditions = $conditions['where'];
3773
        $sqlInjectWhere = $conditions['inject_where'];
3774
        $injectExtraFields = $conditions['inject_extra_fields'];
3775
3776
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3777
        $access_url_id = api_get_current_access_url_id();
3778
3779
        $orderBy = '';
3780
        if (!empty($orderByName)) {
3781
            if (in_array($orderByName, ['name', 'access_start_date'])) {
3782
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
3783
                $orderByName = Database::escape_string($orderByName);
3784
                $orderBy .= " ORDER BY $orderByName $orderByDirection";
3785
            }
3786
        }
3787
3788
        $sql = "
3789
            $select
3790
            (
3791
                SELECT DISTINCT
3792
                    s.id,
3793
                    name,
3794
                    $injectExtraFields
3795
                    access_start_date,
3796
                    access_end_date
3797
                FROM $tbl_session s
3798
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3799
                ON (s.id = session_rel_url.session_id)
3800
                $sqlInjectJoins
3801
                WHERE
3802
                    id_coach = $coach_id AND
3803
                    access_url_id = $access_url_id
3804
                    $keywordCondition
3805
                    $extraFieldsConditions
3806
                    $sqlInjectWhere
3807
            UNION
3808
                SELECT DISTINCT
3809
                    s.id,
3810
                    s.name,
3811
                    $injectExtraFields
3812
                    s.access_start_date,
3813
                    s.access_end_date
3814
                FROM $tbl_session as s
3815
                INNER JOIN $tbl_session_course_user as session_course_user
3816
                ON
3817
                    s.id = session_course_user.session_id AND
3818
                    session_course_user.user_id = $coach_id AND
3819
                    session_course_user.status = 2
3820
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3821
                ON (s.id = session_rel_url.session_id)
3822
                $sqlInjectJoins
3823
                WHERE
3824
                    access_url_id = $access_url_id
3825
                    $keywordCondition
3826
                    $extraFieldsConditions
3827
                    $sqlInjectWhere
3828
            ) as sessions $limitCondition $orderBy
3829
            ";
3830
3831
        $rs = Database::query($sql);
3832
        if ($getCount) {
3833
            $row = Database::fetch_array($rs);
3834
3835
            return $row['count'];
3836
        }
3837
3838
        $sessions = [];
3839
        while ($row = Database::fetch_array($rs)) {
3840
            if ('0000-00-00 00:00:00' === $row['access_start_date']) {
3841
                $row['access_start_date'] = null;
3842
            }
3843
3844
            $sessions[$row['id']] = $row;
3845
        }
3846
3847
        if (!empty($sessions)) {
3848
            foreach ($sessions as &$session) {
3849
                if (empty($session['access_start_date'])) {
3850
                    $session['status'] = get_lang('active');
3851
                } else {
3852
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3853
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3854
                    if ($time_start < time() && time() < $time_end) {
3855
                        $session['status'] = get_lang('active');
3856
                    } else {
3857
                        if (time() < $time_start) {
3858
                            $session['status'] = get_lang('Not yet begun');
3859
                        } else {
3860
                            if (time() > $time_end) {
3861
                                $session['status'] = get_lang('Past');
3862
                            }
3863
                        }
3864
                    }
3865
                }
3866
            }
3867
        }
3868
3869
        return $sessions;
3870
    }
3871
3872
    /**
3873
     * Get courses list from a session.
3874
     *
3875
     * @param    int        Session id
3876
     *
3877
     * @return array Courses list
3878
     */
3879
    public static function get_courses_list_from_session($session_id)
3880
    {
3881
        $session_id = (int) $session_id;
3882
3883
        // table definition
3884
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3885
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3886
3887
        $sql = "SELECT DISTINCT code, c_id
3888
                FROM $tbl_session_course sc
3889
                INNER JOIN $courseTable c
3890
                ON sc.c_id = c.id
3891
                WHERE session_id= $session_id";
3892
3893
        $result = Database::query($sql);
3894
3895
        $courses = [];
3896
        while ($row = Database::fetch_array($result)) {
3897
            $courses[$row['code']] = $row;
3898
        }
3899
3900
        return $courses;
3901
    }
3902
3903
    /**
3904
     * Count the number of documents that an user has uploaded to a course.
3905
     *
3906
     * @param    int|array   Student id(s)
3907
     * @param    string      Course code
3908
     * @param    int         Session id (optional),
3909
     * if param $session_id is null(default)
3910
     * return count of assignments including sessions, 0 = session is not filtered
3911
     *
3912
     * @return int Number of documents
3913
     */
3914
    public static function count_student_uploaded_documents(
3915
        $student_id,
3916
        $course_code,
3917
        $session_id = null
3918
    ) {
3919
        $a_course = api_get_course_info($course_code);
3920
        $repo = Container::getDocumentRepository();
3921
3922
        $user = api_get_user_entity($student_id);
3923
        $course = api_get_course_entity($a_course['real_id']);
3924
        $session = api_get_session_entity($session_id);
3925
        //$group = api_get_group_entity(api_get_group_id());
3926
3927
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
3928
3929
        $qb->select('count(resource)');
3930
        $count = $qb->getQuery()->getSingleScalarResult();
3931
3932
        return $count;
3933
3934
        // get the information of the course
3935
        $a_course = api_get_course_info($course_code);
0 ignored issues
show
Unused Code introduced by
$a_course = api_get_course_info($course_code) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
3936
        if (!empty($a_course)) {
3937
            // table definition
3938
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3939
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
3940
            $course_id = $a_course['real_id'];
3941
            if (is_array($student_id)) {
3942
                $studentList = array_map('intval', $student_id);
3943
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
3944
            } else {
3945
                $student_id = (int) $student_id;
3946
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
3947
            }
3948
3949
            $condition_session = null;
3950
            if (isset($session_id)) {
3951
                $session_id = (int) $session_id;
3952
                $condition_session = " AND pub.session_id = $session_id ";
3953
            }
3954
3955
            $sql = "SELECT count(ip.tool) AS count
3956
                    FROM $tbl_item_property ip
3957
                    INNER JOIN $tbl_document pub
3958
                    ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
3959
                    WHERE
3960
                        ip.c_id  = $course_id AND
3961
                        pub.c_id  = $course_id AND
3962
                        pub.filetype ='file' AND
3963
                        ip.tool = 'document'
3964
                        $condition_user $condition_session ";
3965
            $rs = Database::query($sql);
3966
            $row = Database::fetch_array($rs, 'ASSOC');
3967
3968
            return $row['count'];
3969
        }
3970
3971
        return null;
3972
    }
3973
3974
    /**
3975
     * Count assignments per student.
3976
     *
3977
     * @param array|int $student_id
3978
     * @param string    $course_code
3979
     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
3980
     *                               0 = session is not filtered
3981
     *
3982
     * @return int Count of assignments
3983
     */
3984
    public static function count_student_assignments(
3985
        $student_id,
3986
        $course_code = null,
3987
        $session_id = null
3988
    ) {
3989
        if (empty($student_id)) {
3990
            return 0;
3991
        }
3992
3993
        $a_course = api_get_course_info($course_code);
3994
        $repo = Container::getStudentPublicationRepository();
3995
3996
        $user = api_get_user_entity($student_id);
3997
        $course = api_get_course_entity($a_course['real_id']);
3998
        $session = api_get_session_entity($session_id);
3999
        //$group = api_get_group_entity(api_get_group_id());
4000
4001
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
4002
4003
        $qb->select('count(resource)');
4004
        $count = $qb->getQuery()->getSingleScalarResult();
4005
4006
        return $count;
4007
4008
        $conditions = [];
0 ignored issues
show
Unused Code introduced by
$conditions = array() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
4009
4010
        // Get the information of the course
4011
        $a_course = api_get_course_info($course_code);
4012
        if (!empty($a_course)) {
4013
            $course_id = $a_course['real_id'];
4014
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
4015
        }
4016
4017
        // table definition
4018
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
4019
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
4020
4021
        if (is_array($student_id)) {
4022
            $studentList = array_map('intval', $student_id);
4023
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
4024
        } else {
4025
            $student_id = (int) $student_id;
4026
            $conditions[] = " ip.insert_user_id = '$student_id' ";
4027
        }
4028
4029
        $conditions[] = ' pub.active <> 2 ';
4030
        $conditionToString = implode(' AND ', $conditions);
4031
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
4032
        $conditionToString .= $sessionCondition;
4033
4034
        $sql = "SELECT count(ip.tool) as count
4035
                FROM $tbl_item_property ip
4036
                INNER JOIN $tbl_student_publication pub
4037
                ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
4038
                WHERE
4039
                    ip.tool='work' AND
4040
                    $conditionToString";
4041
        $rs = Database::query($sql);
4042
        $row = Database::fetch_array($rs, 'ASSOC');
4043
4044
        return $row['count'];
4045
    }
4046
4047
    /**
4048
     * Count messages per student inside forum tool.
4049
     *
4050
     * @param int|array  Student id
4051
     * @param string     Course code
4052
     * @param int        Session id if null(default) return count of messages including sessions, 0 = session is not filtered
4053
     *
4054
     * @return int Count of messages
4055
     */
4056
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
4057
    {
4058
        if (empty($student_id)) {
4059
            return 0;
4060
        }
4061
4062
        $a_course = api_get_course_info($courseCode);
4063
        $repo = Container::getForumPostRepository();
4064
4065
        $user = api_get_user_entity($student_id);
4066
        $course = api_get_course_entity($a_course['real_id']);
4067
        $session = api_get_session_entity($session_id);
4068
        //$group = api_get_group_entity(api_get_group_id());
4069
4070
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
4071
4072
        $qb->select('count(resource)');
4073
4074
        return $qb->getQuery()->getSingleScalarResult();
4075
    }
4076
4077
    /**
4078
     * This function counts the number of post by course.
4079
     *
4080
     * @param string $course_code
4081
     * @param int    $session_id  (optional), if is null(default) it'll return results including sessions,
4082
     *                            0 = session is not filtered
4083
     * @param int    $groupId
4084
     *
4085
     * @return int The number of post by course
4086
     */
4087
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
4088
    {
4089
        $courseInfo = api_get_course_info($course_code);
4090
        if (!empty($courseInfo)) {
4091
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
4092
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
4093
4094
            $condition_session = '';
4095
            if (isset($session_id)) {
4096
                $session_id = (int) $session_id;
4097
                $condition_session = api_get_session_condition(
4098
                    $session_id,
4099
                    true,
4100
                    false,
4101
                    'f.session_id'
4102
                );
4103
            }
4104
4105
            $course_id = $courseInfo['real_id'];
4106
            $groupId = (int) $groupId;
4107
            if (!empty($groupId)) {
4108
                $groupCondition = " i.to_group_id = $groupId ";
4109
            } else {
4110
                $groupCondition = ' (i.to_group_id = 0 OR i.to_group_id IS NULL) ';
4111
            }
4112
4113
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4114
            $sql = "SELECT count(*) FROM $tbl_posts p
4115
                    INNER JOIN $tbl_forums f
4116
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
4117
                    INNER JOIN $item i
4118
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
4119
                    WHERE
4120
                        p.c_id = $course_id AND
4121
                        f.c_id = $course_id AND
4122
                        $groupCondition
4123
                        $condition_session
4124
                    ";
4125
            $result = Database::query($sql);
4126
            $row = Database::fetch_row($result);
4127
            $count = $row[0];
4128
4129
            return $count;
4130
        }
4131
4132
        return 0;
4133
    }
4134
4135
    /**
4136
     * This function counts the number of threads by course.
4137
     *
4138
     * @param      string     Course code
4139
     * @param    int        Session id (optional),
4140
     * if param $session_id is null(default) it'll return results including
4141
     * sessions, 0 = session is not filtered
4142
     * @param int $groupId
4143
     *
4144
     * @return int The number of threads by course
4145
     */
4146
    public static function count_number_of_threads_by_course(
4147
        $course_code,
4148
        $session_id = null,
4149
        $groupId = 0
4150
    ) {
4151
        $course_info = api_get_course_info($course_code);
4152
        if (empty($course_info)) {
4153
            return null;
4154
        }
4155
4156
        $course_id = $course_info['real_id'];
4157
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
4158
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4159
4160
        $condition_session = '';
4161
        if (isset($session_id)) {
4162
            $session_id = (int) $session_id;
4163
            $condition_session = ' AND f.session_id = '.$session_id;
4164
        }
4165
4166
        $groupId = (int) $groupId;
4167
4168
        if (!empty($groupId)) {
4169
            $groupCondition = " i.to_group_id = $groupId ";
4170
        } else {
4171
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4172
        }
4173
4174
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4175
        $sql = "SELECT count(*)
4176
                FROM $tbl_threads t
4177
                INNER JOIN $tbl_forums f
4178
                ON f.iid = t.forum_id AND f.c_id = t.c_id
4179
                INNER JOIN $item i
4180
                ON (
4181
                    tool = '".TOOL_FORUM_THREAD."' AND
4182
                    f.c_id = i.c_id AND
4183
                    t.iid = i.ref
4184
                )
4185
                WHERE
4186
                    t.c_id = $course_id AND
4187
                    f.c_id = $course_id AND
4188
                    $groupCondition
4189
                    $condition_session
4190
                ";
4191
4192
        $result = Database::query($sql);
4193
        if (Database::num_rows($result)) {
4194
            $row = Database::fetch_row($result);
4195
            $count = $row[0];
4196
4197
            return $count;
4198
        }
4199
4200
        return 0;
4201
    }
4202
4203
    /**
4204
     * This function counts the number of forums by course.
4205
     *
4206
     * @param      string     Course code
4207
     * @param    int        Session id (optional),
4208
     * if param $session_id is null(default) it'll return results
4209
     * including sessions, 0 = session is not filtered
4210
     * @param int $groupId
4211
     *
4212
     * @return int The number of forums by course
4213
     */
4214
    public static function count_number_of_forums_by_course(
4215
        $course_code,
4216
        $session_id = null,
4217
        $groupId = 0
4218
    ) {
4219
        $course_info = api_get_course_info($course_code);
4220
        if (empty($course_info)) {
4221
            return null;
4222
        }
4223
        $course_id = $course_info['real_id'];
4224
4225
        $condition_session = '';
4226
        if (isset($session_id)) {
4227
            $session_id = (int) $session_id;
4228
            $condition_session = ' AND f.session_id = '.$session_id;
4229
        }
4230
4231
        $groupId = (int) $groupId;
4232
        if (!empty($groupId)) {
4233
            $groupCondition = " i.to_group_id = $groupId ";
4234
        } else {
4235
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4236
        }
4237
4238
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4239
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4240
4241
        $sql = "SELECT count(*)
4242
                FROM $tbl_forums f
4243
                INNER JOIN $item i
4244
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
4245
                WHERE
4246
                    f.c_id = $course_id AND
4247
                    $groupCondition
4248
                    $condition_session
4249
                ";
4250
        $result = Database::query($sql);
4251
        if (Database::num_rows($result)) {
4252
            $row = Database::fetch_row($result);
4253
            $count = $row[0];
4254
4255
            return $count;
4256
        }
4257
4258
        return 0;
4259
    }
4260
4261
    /**
4262
     * This function counts the chat last connections by course in x days.
4263
     *
4264
     * @param      string     Course code
4265
     * @param      int     Last x days
4266
     * @param    int        Session id (optional)
4267
     *
4268
     * @return int Chat last connections by course in x days
4269
     */
4270
    public static function chat_connections_during_last_x_days_by_course(
4271
        $course_code,
4272
        $last_days,
4273
        $session_id = 0
4274
    ) {
4275
        $course_info = api_get_course_info($course_code);
4276
        if (empty($course_info)) {
4277
            return null;
4278
        }
4279
        $course_id = $course_info['real_id'];
4280
4281
        // Protect data
4282
        $last_days = (int) $last_days;
4283
        $session_id = (int) $session_id;
4284
4285
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4286
        $now = api_get_utc_datetime();
4287
4288
        $sql = "SELECT count(*) FROM $tbl_stats_access
4289
                WHERE
4290
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
4291
                    c_id = '$course_id' AND
4292
                    access_tool='".TOOL_CHAT."' AND
4293
                    access_session_id = '$session_id' ";
4294
        $result = Database::query($sql);
4295
        if (Database::num_rows($result)) {
4296
            $row = Database::fetch_row($result);
4297
            $count = $row[0];
4298
4299
            return $count;
4300
        }
4301
4302
        return 0;
4303
    }
4304
4305
    /**
4306
     * This function gets the last student's connection in chat.
4307
     *
4308
     * @param      int     Student id
4309
     * @param      string     Course code
4310
     * @param    int        Session id (optional)
4311
     *
4312
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
4313
     */
4314
    public static function chat_last_connection(
4315
        $student_id,
4316
        $courseId,
4317
        $session_id = 0
4318
    ) {
4319
        $student_id = (int) $student_id;
4320
        $courseId = (int) $courseId;
4321
        $session_id = (int) $session_id;
4322
        $date_time = '';
4323
4324
        // table definition
4325
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4326
        $sql = "SELECT access_date
4327
                FROM $tbl_stats_access
4328
                WHERE
4329
                     access_tool='".TOOL_CHAT."' AND
4330
                     access_user_id='$student_id' AND
4331
                     c_id = $courseId AND
4332
                     access_session_id = '$session_id'
4333
                ORDER BY access_date DESC limit 1";
4334
        $rs = Database::query($sql);
4335
        if (Database::num_rows($rs) > 0) {
4336
            $row = Database::fetch_array($rs);
4337
            $date_time = api_convert_and_format_date(
4338
                $row['access_date'],
4339
                null,
4340
                date_default_timezone_get()
4341
            );
4342
        }
4343
4344
        return $date_time;
4345
    }
4346
4347
    /**
4348
     * Get count student's visited links.
4349
     *
4350
     * @param int $student_id Student id
4351
     * @param int $courseId
4352
     * @param int $session_id Session id (optional)
4353
     *
4354
     * @return int count of visited links
4355
     */
4356
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
4357
    {
4358
        $student_id = (int) $student_id;
4359
        $courseId = (int) $courseId;
4360
        $session_id = (int) $session_id;
4361
4362
        // table definition
4363
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4364
4365
        $sql = 'SELECT 1
4366
                FROM '.$table.'
4367
                WHERE
4368
                    links_user_id= '.$student_id.' AND
4369
                    c_id = "'.$courseId.'" AND
4370
                    links_session_id = '.$session_id.' ';
4371
4372
        $rs = Database::query($sql);
4373
4374
        return Database::num_rows($rs);
4375
    }
4376
4377
    /**
4378
     * Get count student downloaded documents.
4379
     *
4380
     * @param    int        Student id
4381
     * @param int $courseId
4382
     * @param    int        Session id (optional)
4383
     *
4384
     * @return int Count downloaded documents
4385
     */
4386
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
4387
    {
4388
        $student_id = (int) $student_id;
4389
        $courseId = (int) $courseId;
4390
        $session_id = (int) $session_id;
4391
4392
        // table definition
4393
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4394
4395
        $sql = 'SELECT 1
4396
                FROM '.$table.'
4397
                WHERE down_user_id = '.$student_id.'
4398
                AND c_id  = "'.$courseId.'"
4399
                AND down_session_id = '.$session_id.' ';
4400
        $rs = Database::query($sql);
4401
4402
        return Database::num_rows($rs);
4403
    }
4404
4405
    /**
4406
     * Get course list inside a session from a student.
4407
     *
4408
     * @param int $user_id    Student id
4409
     * @param int $id_session Session id (optional)
4410
     *
4411
     * @return array Courses list
4412
     */
4413
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
4414
    {
4415
        $user_id = intval($user_id);
4416
        $id_session = intval($id_session);
4417
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4418
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4419
4420
        $sql = "SELECT c.code
4421
                FROM $tbl_session_course_user sc
4422
                INNER JOIN $courseTable c
4423
                WHERE
4424
                    user_id= $user_id  AND
4425
                    session_id = $id_session";
4426
        $result = Database::query($sql);
4427
        $courses = [];
4428
        while ($row = Database::fetch_array($result)) {
4429
            $courses[$row['code']] = $row['code'];
4430
        }
4431
4432
        return $courses;
4433
    }
4434
4435
    /**
4436
     * Get inactive students in course.
4437
     *
4438
     * @param int        $courseId
4439
     * @param string|int $since      Since login course date (optional, default = 'never')
4440
     * @param int        $session_id (optional)
4441
     *
4442
     * @return array Inactive users
4443
     */
4444
    public static function getInactiveStudentsInCourse(
4445
        $courseId,
4446
        $since = 'never',
4447
        $session_id = 0
4448
    ) {
4449
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4450
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4451
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4452
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4453
        $now = api_get_utc_datetime();
4454
        $courseId = (int) $courseId;
4455
        $session_id = (int) $session_id;
4456
4457
        if (empty($courseId)) {
4458
            return false;
4459
        }
4460
4461
        if ('never' === $since) {
4462
            if (empty($session_id)) {
4463
                $sql = 'SELECT course_user.user_id
4464
                        FROM '.$table_course_rel_user.' course_user
4465
                        LEFT JOIN '.$tbl_track_login.' stats_login
4466
                        ON course_user.user_id = stats_login.user_id AND
4467
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
4468
                        INNER JOIN '.$tableCourse.' c
4469
                        ON (c.id = course_user.c_id)
4470
                        WHERE
4471
                            course_user.c_id = '.$courseId.' AND
4472
                            stats_login.login_course_date IS NULL
4473
                        GROUP BY course_user.user_id';
4474
            } else {
4475
                $sql = 'SELECT session_course_user.user_id
4476
                        FROM '.$tbl_session_course_user.' session_course_user
4477
                        LEFT JOIN '.$tbl_track_login.' stats_login
4478
                        ON session_course_user.user_id = stats_login.user_id
4479
                        INNER JOIN '.$tableCourse.' c
4480
                        ON (c.id = session_course_user.c_id)
4481
                        WHERE
4482
                            session_course_user.c_id = '.$courseId.' AND
4483
                            stats_login.login_course_date IS NULL
4484
                        GROUP BY session_course_user.user_id';
4485
            }
4486
        } else {
4487
            $since = (int) $since;
4488
            if (empty($session_id)) {
4489
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
4490
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
4491
            } else {
4492
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
4493
                          ON
4494
                            c.id = session_course_user.c_id AND
4495
                            session_course_user.session_id = '.$session_id.' AND
4496
                            session_course_user.user_id = stats_login.user_id ';
4497
            }
4498
4499
            $sql = 'SELECT
4500
                    stats_login.user_id,
4501
                    MAX(login_course_date) max_date
4502
                FROM '.$tbl_track_login.' stats_login
4503
                INNER JOIN '.$tableCourse.' c
4504
                ON (c.id = stats_login.c_id)
4505
                '.$inner.'
4506
                WHERE c.id = '.$courseId.'
4507
                GROUP BY stats_login.user_id
4508
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
4509
        }
4510
4511
        $rs = Database::query($sql);
4512
4513
        $allow = 'true' === api_get_plugin_setting('pausetraining', 'tool_enable');
4514
        $allowPauseFormation = 'true' === api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation');
4515
4516
        $extraFieldValue = new ExtraFieldValue('user');
4517
        $users = [];
4518
        while ($user = Database::fetch_array($rs)) {
4519
            $userId = $user['user_id'];
4520
4521
            if ($allow && $allowPauseFormation) {
4522
                $pause = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'pause_formation');
4523
                if (!empty($pause) && isset($pause['value']) && 1 == $pause['value']) {
4524
                    // Skip user because he paused his formation.
4525
                    continue;
4526
                }
4527
            }
4528
4529
            $users[] = $userId;
4530
        }
4531
4532
        return $users;
4533
    }
4534
4535
    /**
4536
     * get count clicks about tools most used by course.
4537
     *
4538
     * @param int $courseId
4539
     * @param    int        Session id (optional),
4540
     * if param $session_id is null(default) it'll return results
4541
     * including sessions, 0 = session is not filtered
4542
     *
4543
     * @return array tools data
4544
     */
4545
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
4546
    {
4547
        $courseId = (int) $courseId;
4548
        $data = [];
4549
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4550
        $condition_session = '';
4551
        if (isset($session_id)) {
4552
            $session_id = (int) $session_id;
4553
            $condition_session = ' AND access_session_id = '.$session_id;
4554
        }
4555
        $sql = "SELECT
4556
                    access_tool,
4557
                    COUNT(DISTINCT access_user_id),
4558
                    count(access_tool) as count_access_tool
4559
                FROM $TABLETRACK_ACCESS
4560
                WHERE
4561
                    access_tool IS NOT NULL AND
4562
                    access_tool != '' AND
4563
                    c_id = '$courseId'
4564
                    $condition_session
4565
                GROUP BY access_tool
4566
                ORDER BY count_access_tool DESC
4567
                LIMIT 0, 3";
4568
        $rs = Database::query($sql);
4569
        if (Database::num_rows($rs) > 0) {
4570
            while ($row = Database::fetch_array($rs)) {
4571
                $data[] = $row;
4572
            }
4573
        }
4574
4575
        return $data;
4576
    }
4577
4578
    /**
4579
     * get documents most downloaded by course.
4580
     *
4581
     * @param      string     Course code
4582
     * @param    int        Session id (optional),
4583
     * if param $session_id is null(default) it'll return results including
4584
     * sessions, 0 = session is not filtered
4585
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4586
     *
4587
     * @return array documents downloaded
4588
     */
4589
    public static function get_documents_most_downloaded_by_course(
4590
        $course_code,
4591
        $session_id = 0,
4592
        $limit = 0
4593
    ) {
4594
        $courseId = api_get_course_int_id($course_code);
4595
        $data = [];
4596
4597
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4598
        $condition_session = '';
4599
        $session_id = intval($session_id);
4600
        if (!empty($session_id)) {
4601
            $condition_session = ' AND down_session_id = '.$session_id;
4602
        }
4603
        $sql = "SELECT
4604
                    down_doc_path,
4605
                    COUNT(DISTINCT down_user_id),
4606
                    COUNT(down_doc_path) as count_down
4607
                FROM $TABLETRACK_DOWNLOADS
4608
                WHERE c_id = $courseId
4609
                    $condition_session
4610
                GROUP BY down_doc_path
4611
                ORDER BY count_down DESC
4612
                LIMIT 0,  $limit";
4613
        $rs = Database::query($sql);
4614
4615
        if (Database::num_rows($rs) > 0) {
4616
            while ($row = Database::fetch_array($rs)) {
4617
                $data[] = $row;
4618
            }
4619
        }
4620
4621
        return $data;
4622
    }
4623
4624
    /**
4625
     * get links most visited by course.
4626
     *
4627
     * @param      string     Course code
4628
     * @param    int        Session id (optional),
4629
     * if param $session_id is null(default) it'll
4630
     * return results including sessions, 0 = session is not filtered
4631
     *
4632
     * @return array links most visited
4633
     */
4634
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4635
    {
4636
        $course_code = Database::escape_string($course_code);
4637
        $course_info = api_get_course_info($course_code);
4638
        $course_id = $course_info['real_id'];
4639
        $data = [];
4640
4641
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4642
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4643
4644
        $condition_session = '';
4645
        if (isset($session_id)) {
4646
            $session_id = intval($session_id);
4647
            $condition_session = ' AND cl.session_id = '.$session_id;
4648
        }
4649
4650
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4651
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4652
                WHERE
4653
                    cl.c_id = $course_id AND
4654
                    sl.links_link_id = cl.id AND
4655
                    sl.c_id = $course_id
4656
                    $condition_session
4657
                GROUP BY cl.title, cl.url
4658
                ORDER BY count_visits DESC
4659
                LIMIT 0, 3";
4660
        $rs = Database::query($sql);
4661
        if (Database::num_rows($rs) > 0) {
4662
            while ($row = Database::fetch_array($rs)) {
4663
                $data[] = $row;
4664
            }
4665
        }
4666
4667
        return $data;
4668
    }
4669
4670
    /**
4671
     * Shows the user progress (when clicking in the Progress tab).
4672
     *
4673
     * @param int    $user_id
4674
     * @param int    $session_id
4675
     * @param string $extra_params
4676
     * @param bool   $show_courses
4677
     * @param bool   $showAllSessions
4678
     * @param bool   $returnArray
4679
     *
4680
     * @return string|array
4681
     */
4682
    public static function show_user_progress(
4683
        $user_id,
4684
        $session_id = 0,
4685
        $extra_params = '',
4686
        $show_courses = true,
4687
        $showAllSessions = true,
4688
        $returnArray = false
4689
    ) {
4690
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4691
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4692
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4693
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4694
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4695
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4696
4697
        $trackingColumns = [
4698
            'course_session' => [
4699
                'course_title' => true,
4700
                'published_exercises' => true,
4701
                'new_exercises' => true,
4702
                'my_average' => true,
4703
                'average_exercise_result' => true,
4704
                'time_spent' => true,
4705
                'lp_progress' => true,
4706
                'score' => true,
4707
                'best_score' => true,
4708
                'last_connection' => true,
4709
                'details' => true,
4710
            ],
4711
        ];
4712
4713
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
4714
        if (!empty($trackingColumnsConfig)) {
4715
            $trackingColumns = $trackingColumnsConfig;
4716
        }
4717
4718
        $user_id = (int) $user_id;
4719
        $session_id = (int) $session_id;
4720
        $urlId = api_get_current_access_url_id();
4721
4722
        if (api_is_multiple_url_enabled()) {
4723
            $sql = "SELECT c.id, c.code, title
4724
                    FROM $tbl_course_user cu
4725
                    INNER JOIN $tbl_course c
4726
                    ON (cu.c_id = c.id)
4727
                    INNER JOIN $tbl_access_rel_course a
4728
                    ON (a.c_id = c.id)
4729
                    WHERE
4730
                        cu.user_id = $user_id AND
4731
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4732
                        access_url_id = $urlId
4733
                    ORDER BY title";
4734
        } else {
4735
            $sql = "SELECT c.id, c.code, title
4736
                    FROM $tbl_course_user u
4737
                    INNER JOIN $tbl_course c ON (c_id = c.id)
4738
                    WHERE
4739
                        u.user_id= $user_id AND
4740
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
4741
                    ORDER BY title";
4742
        }
4743
4744
        $rs = Database::query($sql);
4745
        $courses = $course_in_session = $temp_course_in_session = [];
4746
        $courseIdList = [];
4747
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4748
            $courses[$row['code']] = $row['title'];
4749
            $courseIdList[] = $row['id'];
4750
        }
4751
4752
        $orderBy = ' ORDER BY name ';
4753
        $extraInnerJoin = null;
4754
4755
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4756
            $orderBy = ' ORDER BY s.id, src.position ';
4757
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4758
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4759
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4760
        }
4761
4762
        $sessionCondition = '';
4763
        if (!empty($session_id)) {
4764
            $sessionCondition = " AND s.id = $session_id";
4765
        }
4766
4767
        // Get the list of sessions where the user is subscribed as student
4768
        if (api_is_multiple_url_enabled()) {
4769
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4770
                    FROM $tbl_session_course_user cu
4771
                    INNER JOIN $tbl_access_rel_session a
4772
                    ON (a.session_id = cu.session_id)
4773
                    INNER JOIN $tbl_session s
4774
                    ON (s.id = a.session_id)
4775
                    INNER JOIN $tbl_course c
4776
                    ON (c.id = cu.c_id)
4777
                    $extraInnerJoin
4778
                    WHERE
4779
                        cu.user_id = $user_id AND
4780
                        access_url_id = ".$urlId."
4781
                        $sessionCondition
4782
                    $orderBy ";
4783
        } else {
4784
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4785
                    FROM $tbl_session_course_user cu
4786
                    INNER JOIN $tbl_session s
4787
                    ON (s.id = cu.session_id)
4788
                    INNER JOIN $tbl_course c
4789
                    ON (c.id = cu.c_id)
4790
                    $extraInnerJoin
4791
                    WHERE
4792
                        cu.user_id = $user_id
4793
                        $sessionCondition
4794
                    $orderBy ";
4795
        }
4796
4797
        $rs = Database::query($sql);
4798
        $simple_session_array = [];
4799
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4800
            $course_info = api_get_course_info($row['code']);
4801
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4802
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
4803
            $simple_session_array[$row['session_id']] = $row['name'];
4804
        }
4805
4806
        foreach ($simple_session_array as $my_session_id => $session_name) {
4807
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4808
            $my_course_data = [];
4809
            foreach ($course_list as $courseId => $course_data) {
4810
                $my_course_data[$courseId] = $course_data['title'];
4811
            }
4812
4813
            if (empty($session_id)) {
4814
                $my_course_data = utf8_sort($my_course_data);
4815
            }
4816
4817
            $final_course_data = [];
4818
            foreach ($my_course_data as $course_id => $value) {
4819
                if (isset($course_list[$course_id])) {
4820
                    $final_course_data[$course_id] = $course_list[$course_id];
4821
                }
4822
            }
4823
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4824
            $course_in_session[$my_session_id]['name'] = $session_name;
4825
        }
4826
4827
        if ($returnArray) {
4828
            $course_in_session[0] = $courseIdList;
4829
4830
            return $course_in_session;
4831
        }
4832
4833
        $html = '';
4834
        // Course list
4835
        if ($show_courses) {
4836
            if (!empty($courses)) {
4837
                $html .= Display::page_subheader(
4838
                    Display::return_icon(
4839
                        'course.png',
4840
                        get_lang('My courses'),
4841
                        [],
4842
                        ICON_SIZE_SMALL
4843
                    ).' '.get_lang('My courses')
4844
                );
4845
4846
                $columns = [
4847
                    'course_title' => get_lang('Course'),
4848
                    'time_spent' => get_lang('Time spent in the course'),
4849
                    'progress' => get_lang('Progress'),
4850
                    'best_score_in_lp' => get_lang('Best score in learning path'),
4851
                    'best_score_not_in_lp' => get_lang('Best score not in learning path'),
4852
                    'latest_login' => get_lang('Latest login'),
4853
                    'details' => get_lang('Details'),
4854
                ];
4855
                $availableColumns = [];
4856
                if (isset($trackingColumns['my_progress_courses'])) {
4857
                    $availableColumns = $trackingColumns['my_progress_courses'];
4858
                }
4859
                $html .= '<div class="table-responsive">';
4860
                $html .= '<table class="table table-striped table-hover">';
4861
                $html .= '<thead><tr>';
4862
                foreach ($columns as $columnKey => $name) {
4863
                    if (!empty($availableColumns)) {
4864
                        if (isset($availableColumns[$columnKey]) && false == $availableColumns[$columnKey]) {
4865
                            continue;
4866
                        }
4867
                    }
4868
                    $html .= Display::tag('th', $name);
4869
                }
4870
                $html .= '</tr></thead><tbody>';
4871
4872
                foreach ($courses as $course_code => $course_title) {
4873
                    $courseInfo = api_get_course_info($course_code);
4874
                    $courseId = $courseInfo['real_id'];
4875
4876
                    $total_time_login = self::get_time_spent_on_the_course(
4877
                        $user_id,
4878
                        $courseId
4879
                    );
4880
                    $time = api_time_to_hms($total_time_login);
4881
                    $progress = self::get_avg_student_progress(
4882
                        $user_id,
4883
                        $course_code
4884
                    );
4885
                    $bestScore = self::get_avg_student_score(
4886
                        $user_id,
4887
                        $course_code,
4888
                        [],
4889
                        null,
4890
                        false,
4891
                        false,
4892
                        true
4893
                    );
4894
4895
                    $exerciseList = ExerciseLib::get_all_exercises(
4896
                        $courseInfo,
4897
                        0,
4898
                        false,
4899
                        null,
4900
                        false,
4901
                        1
4902
                    );
4903
4904
                    $bestScoreAverageNotInLP = 0;
4905
                    if (!empty($exerciseList)) {
4906
                        foreach ($exerciseList as $exerciseData) {
4907
                            $results = Event::get_best_exercise_results_by_user(
4908
                                $exerciseData['id'],
4909
                                $courseInfo['real_id'],
4910
                                0,
4911
                                $user_id
4912
                            );
4913
                            $best = 0;
4914
                            if (!empty($results)) {
4915
                                foreach ($results as $result) {
4916
                                    if (!empty($result['max_score'])) {
4917
                                        $score = $result['score'] / $result['max_score'];
4918
                                        if ($score > $best) {
4919
                                            $best = $score;
4920
                                        }
4921
                                    }
4922
                                }
4923
                            }
4924
                            $bestScoreAverageNotInLP += $best;
4925
                        }
4926
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
4927
                    }
4928
4929
                    $last_connection = self::get_last_connection_date_on_the_course(
4930
                        $user_id,
4931
                        $courseInfo
4932
                    );
4933
4934
                    if (is_null($progress) || empty($progress)) {
4935
                        $progress = '0%';
4936
                    } else {
4937
                        $progress = $progress.'%';
4938
                    }
4939
4940
                    if (isset($_GET['course']) &&
4941
                        $course_code == $_GET['course'] &&
4942
                        empty($_GET['session_id'])
4943
                    ) {
4944
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D">';
4945
                    } else {
4946
                        $html .= '<tr class="row_even">';
4947
                    }
4948
                    $url = api_get_course_url($course_code, $session_id);
4949
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
4950
                    $bestScoreResult = '';
4951
                    if (empty($bestScore)) {
4952
                        $bestScoreResult = '-';
4953
                    } else {
4954
                        $bestScoreResult = $bestScore.'%';
4955
                    }
4956
                    $bestScoreNotInLP = '';
4957
                    if (empty($bestScoreAverageNotInLP)) {
4958
                        $bestScoreNotInLP = '-';
4959
                    } else {
4960
                        $bestScoreNotInLP = $bestScoreAverageNotInLP.'%';
4961
                    }
4962
4963
                    $detailsLink = '';
4964
                    if (isset($_GET['course']) &&
4965
                        $course_code == $_GET['course'] &&
4966
                        empty($_GET['session_id'])
4967
                    ) {
4968
                        $detailsLink .= '<a href="#course_session_header">';
4969
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4970
                        $detailsLink .= '</a>';
4971
                    } else {
4972
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_header">';
4973
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4974
                        $detailsLink .= '</a>';
4975
                    }
4976
4977
                    $result = [
4978
                        'course_title' => $course_url,
4979
                        'time_spent' => $time,
4980
                        'progress' => $progress,
4981
                        'best_score_in_lp' => $bestScoreResult,
4982
                        'best_score_not_in_lp' => $bestScoreNotInLP,
4983
                        'latest_login' => $last_connection,
4984
                        'details' => $detailsLink,
4985
                    ];
4986
4987
                    foreach ($result as $columnKey => $data) {
4988
                        if (!empty($availableColumns)) {
4989
                            if (isset($availableColumns[$columnKey]) && false == $availableColumns[$columnKey]) {
4990
                                continue;
4991
                            }
4992
                        }
4993
                        $html .= '<td>'.$data.'</td>';
4994
                    }
4995
4996
                    $html .= '</tr>';
4997
                }
4998
                $html .= '</tbody></table>';
4999
                $html .= '</div>';
5000
            }
5001
        }
5002
5003
        // Session list
5004
        if (!empty($course_in_session)) {
5005
            $main_session_graph = '';
5006
            // Load graphics only when calling to an specific session
5007
            $all_exercise_graph_name_list = [];
5008
            $my_results = [];
5009
            $all_exercise_graph_list = [];
5010
            $all_exercise_start_time = [];
5011
            foreach ($course_in_session as $my_session_id => $session_data) {
5012
                $course_list = $session_data['course_list'];
5013
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
5014
                $exercise_graph_name_list = [];
5015
                $exercise_graph_list = [];
5016
5017
                foreach ($course_list as $course_data) {
5018
                    $exercise_list = ExerciseLib::get_all_exercises(
5019
                        $course_data,
5020
                        $my_session_id,
5021
                        false,
5022
                        null,
5023
                        false,
5024
                        1
5025
                    );
5026
5027
                    foreach ($exercise_list as $exercise_data) {
5028
                        $exercise_obj = new Exercise($course_data['real_id']);
5029
                        $exercise_obj->read($exercise_data['id']);
5030
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
5031
                        //$visible_return = $exercise_obj->is_visible();
5032
                        if (0 == $exercise_data['results_disabled'] || 2 == $exercise_data['results_disabled']) {
5033
                            $best_average = (int)
5034
                                ExerciseLib::get_best_average_score_by_exercise(
5035
                                    $exercise_data['id'],
5036
                                    $course_data['real_id'],
5037
                                    $my_session_id,
5038
                                    $user_count
5039
                                )
5040
                            ;
5041
5042
                            $exercise_graph_list[] = $best_average;
5043
                            $all_exercise_graph_list[] = $best_average;
5044
5045
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
5046
                                api_get_user_id(),
5047
                                $exercise_data['id'],
5048
                                $course_data['real_id'],
5049
                                $my_session_id
5050
                            );
5051
5052
                            $score = 0;
5053
                            if (!empty($user_result_data['max_score']) && 0 != intval($user_result_data['max_score'])) {
5054
                                $score = intval($user_result_data['score'] / $user_result_data['max_score'] * 100);
5055
                            }
5056
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
5057
                            $all_exercise_start_time[] = $time;
5058
                            $my_results[] = $score;
5059
                            if (count($exercise_list) <= 10) {
5060
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
5061
                                $exercise_graph_name_list[] = $title;
5062
                                $all_exercise_graph_name_list[] = $title;
5063
                            } else {
5064
                                // if there are more than 10 results, space becomes difficult to find,
5065
                                // so only show the title of the exercise, not the tool
5066
                                $title = cut($exercise_data['title'], 30);
5067
                                $exercise_graph_name_list[] = $title;
5068
                                $all_exercise_graph_name_list[] = $title;
5069
                            }
5070
                        }
5071
                    }
5072
                }
5073
            }
5074
5075
            // Complete graph
5076
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
5077
                asort($all_exercise_start_time);
5078
5079
                //Fix exams order
5080
                $final_all_exercise_graph_name_list = [];
5081
                $my_results_final = [];
5082
                $final_all_exercise_graph_list = [];
5083
5084
                foreach ($all_exercise_start_time as $key => $time) {
5085
                    $label_time = '';
5086
                    if (!empty($time)) {
5087
                        $label_time = date('d-m-y', $time);
5088
                    }
5089
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
5090
                    $my_results_final[] = $my_results[$key];
5091
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
5092
                }
5093
                $main_session_graph = self::generate_session_exercise_graph(
5094
                    $final_all_exercise_graph_name_list,
5095
                    $my_results_final,
5096
                    $final_all_exercise_graph_list
5097
                );
5098
            }
5099
5100
            $sessionIcon = Display::return_icon(
5101
                'session.png',
5102
                get_lang('Course sessions'),
5103
                [],
5104
                ICON_SIZE_SMALL
5105
            );
5106
5107
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5108
            $html .= $anchor.Display::page_subheader(
5109
                $sessionIcon.' '.get_lang('Course sessions')
5110
            );
5111
5112
            $html .= '<div class="table-responsive">';
5113
            $html .= '<table class="table table-striped table-hover">';
5114
            $html .= '<thead>';
5115
            $html .= '<tr>
5116
                  '.Display::tag('th', get_lang('Session'), ['width' => '300px']).'
5117
                  '.Display::tag('th', get_lang('Tests available'), ['width' => '300px']).'
5118
                  '.Display::tag('th', get_lang('New exercises')).'
5119
                  '.Display::tag('th', get_lang('Average exercise result')).'
5120
                  '.Display::tag('th', get_lang('Details')).'
5121
                  </tr>';
5122
            $html .= '</thead>';
5123
            $html .= '<tbody>';
5124
5125
            foreach ($course_in_session as $my_session_id => $session_data) {
5126
                $course_list = $session_data['course_list'];
5127
                $session_name = $session_data['name'];
5128
                if (false == $showAllSessions) {
5129
                    if (isset($session_id) && !empty($session_id)) {
5130
                        if ($session_id != $my_session_id) {
5131
                            continue;
5132
                        }
5133
                    }
5134
                }
5135
5136
                $all_exercises = 0;
5137
                $all_unanswered_exercises_by_user = 0;
5138
                $all_average = 0;
5139
                $stats_array = [];
5140
5141
                foreach ($course_list as $course_data) {
5142
                    // All exercises in the course @todo change for a real count
5143
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5144
                    $count_exercises = 0;
5145
                    if (is_array($exercises) && !empty($exercises)) {
5146
                        $count_exercises = count($exercises);
5147
                    }
5148
5149
                    // Count of user results
5150
                    $done_exercises = null;
5151
                    $courseInfo = api_get_course_info($course_data['code']);
5152
5153
                    $answered_exercises = 0;
5154
                    if (!empty($exercises)) {
5155
                        foreach ($exercises as $exercise_item) {
5156
                            $attempts = Event::count_exercise_attempts_by_user(
5157
                                api_get_user_id(),
5158
                                $exercise_item['id'],
5159
                                $courseInfo['real_id'],
5160
                                $my_session_id
5161
                            );
5162
                            if ($attempts > 1) {
5163
                                $answered_exercises++;
5164
                            }
5165
                        }
5166
                    }
5167
5168
                    // Average
5169
                    $average = ExerciseLib::get_average_score_by_course(
5170
                        $courseInfo['real_id'],
5171
                        $my_session_id
5172
                    );
5173
                    $all_exercises += $count_exercises;
5174
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5175
                    $all_average += $average;
5176
                }
5177
5178
                if (!empty($course_list)) {
5179
                    $all_average = $all_average / count($course_list);
5180
                }
5181
5182
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5183
                    $html .= '<tr style="background-color:#FBF09D">';
5184
                } else {
5185
                    $html .= '<tr>';
5186
                }
5187
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5188
5189
                $html .= Display::tag('td', Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]));
5190
                $html .= Display::tag('td', $all_exercises);
5191
                $html .= Display::tag('td', $all_unanswered_exercises_by_user);
5192
                $html .= Display::tag('td', ExerciseLib::convert_to_percentage($all_average));
5193
5194
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5195
                    $icon = Display::url(
5196
                        Display::return_icon(
5197
                            '2rightarrow_na.png',
5198
                            get_lang('Details')
5199
                        ),
5200
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5201
                    );
5202
                } else {
5203
                    $icon = Display::url(
5204
                        Display::return_icon(
5205
                            '2rightarrow.png',
5206
                            get_lang('Details')
5207
                        ),
5208
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5209
                    );
5210
                }
5211
                $html .= Display::tag('td', $icon);
5212
                $html .= '</tr>';
5213
            }
5214
            $html .= '</tbody>';
5215
            $html .= '</table></div><br />';
5216
            $html .= Display::div(
5217
                $main_session_graph,
5218
                [
5219
                    'id' => 'session_graph',
5220
                    'class' => 'chart-session',
5221
                    'style' => 'position:relative; text-align: center;',
5222
                ]
5223
            );
5224
5225
            // Checking selected session.
5226
            if (isset($_GET['session_id'])) {
5227
                $session_id_from_get = (int) $_GET['session_id'];
5228
                $session_data = $course_in_session[$session_id_from_get];
5229
                $course_list = $session_data['course_list'];
5230
5231
                $html .= '<a name= "course_session_list"></a>';
5232
                $html .= Display::tag('h3', $session_data['name'].' - '.get_lang('Course list'));
5233
5234
                $html .= '<div class="table-responsive">';
5235
                $html .= '<table class="table table-hover table-striped">';
5236
5237
                $columnHeaders = [
5238
                    'course_title' => [
5239
                        get_lang('Course'),
5240
                        ['width' => '300px'],
5241
                    ],
5242
                    'published_exercises' => [
5243
                        get_lang('Tests available'),
5244
                    ],
5245
                    'new_exercises' => [
5246
                        get_lang('New exercises'),
5247
                    ],
5248
                    'my_average' => [
5249
                        get_lang('My average'),
5250
                    ],
5251
                    'average_exercise_result' => [
5252
                        get_lang('Average exercise result'),
5253
                    ],
5254
                    'time_spent' => [
5255
                        get_lang('Time spent in the course'),
5256
                    ],
5257
                    'lp_progress' => [
5258
                        get_lang('Learning path progress'),
5259
                    ],
5260
                    'score' => [
5261
                        get_lang('Score').
5262
                        Display::return_icon(
5263
                            'info3.gif',
5264
                            get_lang('Average of tests in Learning Paths'),
5265
                            ['align' => 'absmiddle', 'hspace' => '3px']
5266
                        ),
5267
                    ],
5268
                    'best_score' => [
5269
                        get_lang('Best score'),
5270
                    ],
5271
                    'last_connection' => [
5272
                        get_lang('Latest login'),
5273
                    ],
5274
                    'details' => [
5275
                        get_lang('Details'),
5276
                    ],
5277
                ];
5278
5279
                $html .= '<thead><tr>';
5280
                foreach ($columnHeaders as $key => $columnSetting) {
5281
                    if (isset($trackingColumns['course_session']) &&
5282
                        in_array($key, $trackingColumns['course_session']) &&
5283
                        $trackingColumns['course_session'][$key]
5284
                    ) {
5285
                        $settings = isset($columnSetting[1]) ? $columnSetting[1] : [];
5286
                        $html .= Display::tag(
5287
                             'th',
5288
                             $columnSetting[0],
5289
                             $settings
5290
                         );
5291
                    }
5292
                }
5293
5294
                $html .= '</tr>
5295
                    </thead>
5296
                    <tbody>';
5297
5298
                foreach ($course_list as $course_data) {
5299
                    $course_code = $course_data['code'];
5300
                    $course_title = $course_data['title'];
5301
                    $courseId = $course_data['real_id'];
5302
5303
                    // All exercises in the course @todo change for a real count
5304
                    $exercises = ExerciseLib::get_all_exercises(
5305
                        $course_data,
5306
                        $session_id_from_get
5307
                    );
5308
                    $count_exercises = 0;
5309
                    if (!empty($exercises)) {
5310
                        $count_exercises = count($exercises);
5311
                    }
5312
                    $answered_exercises = 0;
5313
                    foreach ($exercises as $exercise_item) {
5314
                        $attempts = Event::count_exercise_attempts_by_user(
5315
                            api_get_user_id(),
5316
                            $exercise_item['id'],
5317
                            $courseId,
5318
                            $session_id_from_get
5319
                        );
5320
                        if ($attempts > 1) {
5321
                            $answered_exercises++;
5322
                        }
5323
                    }
5324
5325
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5326
5327
                    // Average
5328
                    $average = ExerciseLib::get_average_score_by_course(
5329
                        $courseId,
5330
                        $session_id_from_get
5331
                    );
5332
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5333
                        api_get_user_id(),
5334
                        $courseId,
5335
                        $session_id_from_get
5336
                    );
5337
5338
                    $bestScore = self::get_avg_student_score(
5339
                        $user_id,
5340
                        $course_code,
5341
                        [],
5342
                        $session_id_from_get,
5343
                        false,
5344
                        false,
5345
                        true
5346
                    );
5347
5348
                    $stats_array[$course_code] = [
5349
                        'exercises' => $count_exercises,
5350
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5351
                        'done_exercises' => $done_exercises,
5352
                        'average' => $average,
5353
                        'my_average' => $my_average,
5354
                        'best_score' => $bestScore,
5355
                    ];
5356
5357
                    $last_connection = self::get_last_connection_date_on_the_course(
5358
                        $user_id,
5359
                        $course_data,
5360
                        $session_id_from_get
5361
                    );
5362
5363
                    $progress = self::get_avg_student_progress(
5364
                        $user_id,
5365
                        $course_code,
5366
                        [],
5367
                        $session_id_from_get
5368
                    );
5369
5370
                    $total_time_login = self::get_time_spent_on_the_course(
5371
                        $user_id,
5372
                        $courseId,
5373
                        $session_id_from_get
5374
                    );
5375
                    $time = api_time_to_hms($total_time_login);
5376
5377
                    $percentage_score = self::get_avg_student_score(
5378
                        $user_id,
5379
                        $course_code,
5380
                        [],
5381
                        $session_id_from_get
5382
                    );
5383
                    $courseCodeFromGet = isset($_GET['course']) ? $_GET['course'] : null;
5384
5385
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
5386
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D" >';
5387
                    } else {
5388
                        $html .= '<tr class="row_even">';
5389
                    }
5390
5391
                    $url = api_get_course_url($course_code, $session_id_from_get);
5392
                    $course_url = Display::url(
5393
                        $course_title,
5394
                        $url,
5395
                        ['target' => SESSION_LINK_TARGET]
5396
                    );
5397
5398
                    if (is_numeric($progress)) {
5399
                        $progress = $progress.'%';
5400
                    } else {
5401
                        $progress = '0%';
5402
                    }
5403
                    if (is_numeric($percentage_score)) {
5404
                        $percentage_score = $percentage_score.'%';
5405
                    } else {
5406
                        $percentage_score = '0%';
5407
                    }
5408
5409
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5410
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5411
                    } else {
5412
                        $bestScore = '-';
5413
                    }
5414
5415
                    if (empty($last_connection) || is_bool($last_connection)) {
5416
                        $last_connection = '';
5417
                    }
5418
5419
                    if ($course_code == $courseCodeFromGet &&
5420
                        $_GET['session_id'] == $session_id_from_get
5421
                    ) {
5422
                        $details = Display::url(
5423
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
5424
                        '#course_session_data'
5425
                        );
5426
                    } else {
5427
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5428
                        $details = Display::url(
5429
                            Display::return_icon(
5430
                                '2rightarrow.png',
5431
                                get_lang('Details')
5432
                            ),
5433
                            $url
5434
                        );
5435
                    }
5436
                    $details .= '</a>';
5437
5438
                    $data = [
5439
                        'course_title' => $course_url,
5440
                        'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5441
                        'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5442
                        'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5443
                        'average_exercise_result' => 0 == $stats_array[$course_code]['average'] ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5444
                        'time_spent' => $time,
5445
                        'lp_progress' => $progress,
5446
                        'score' => $percentage_score,
5447
                        'best_score' => $bestScore,
5448
                        'last_connection' => $last_connection,
5449
                        'details' => $details,
5450
                    ];
5451
5452
                    foreach ($data as $key => $value) {
5453
                        if (in_array($key, $trackingColumns['course_session'])
5454
                            && $trackingColumns['course_session'][$key]
5455
                        ) {
5456
                            $html .= Display::tag('td', $value);
5457
                        }
5458
                    }
5459
                    $html .= '</tr>';
5460
                }
5461
                $html .= '</tbody></table></div>';
5462
            }
5463
        }
5464
5465
        $pluginCalendar = 'true' === api_get_plugin_setting('learning_calendar', 'enabled');
5466
        if ($pluginCalendar) {
5467
            $course_in_session[0] = $courseIdList;
5468
            $plugin = LearningCalendarPlugin::create();
5469
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
5470
        }
5471
5472
        return $html;
5473
    }
5474
5475
    /**
5476
     * Shows the user detail progress (when clicking in the details link).
5477
     *
5478
     * @param int    $user_id
5479
     * @param string $course_code
5480
     * @param int    $session_id
5481
     *
5482
     * @return string html code
5483
     */
5484
    public static function show_course_detail($user_id, $course_code, $session_id)
5485
    {
5486
        $html = '';
5487
        if (isset($course_code)) {
5488
            $user_id = (int) $user_id;
5489
            $session_id = (int) $session_id;
5490
            $course = Database::escape_string($course_code);
5491
            $course_info = api_get_course_info($course);
5492
            if (empty($course_info)) {
5493
                return '';
5494
            }
5495
5496
            $html .= '<a name="course_session_data"></a>';
5497
            $html .= Display::page_subheader($course_info['title']);
5498
            $html .= '<div class="table-responsive">';
5499
            $html .= '<table class="table table-striped table-hover">';
5500
5501
            // Course details
5502
            $html .= '
5503
                <thead>
5504
                <tr>
5505
                <th>'.get_lang('Tests').'</th>
5506
                <th>'.get_lang('Attempts').'</th>
5507
                <th>'.get_lang('Best attempt').'</th>
5508
                <th>'.get_lang('Ranking').'</th>
5509
                <th>'.get_lang('Best result in course').'</th>
5510
                <th>'.get_lang('Statistics').' '.Display::return_icon('info3.gif', get_lang('In case of multiple attempts, only shows the best result of each learner'), ['align' => 'absmiddle', 'hspace' => '3px']).'</th>
5511
                </tr>
5512
                </thead>
5513
                <tbody>';
5514
5515
            if (empty($session_id)) {
5516
                $user_list = CourseManager::get_user_list_from_course_code(
5517
                    $course,
5518
                    $session_id,
5519
                    null,
5520
                    null,
5521
                    STUDENT
5522
                );
5523
            } else {
5524
                $user_list = CourseManager::get_user_list_from_course_code(
5525
                    $course,
5526
                    $session_id,
5527
                    null,
5528
                    null,
5529
                    0
5530
                );
5531
            }
5532
5533
            // Show exercise results of invisible exercises? see BT#4091
5534
            $exercise_list = ExerciseLib::get_all_exercises(
5535
                $course_info,
5536
                $session_id,
5537
                false,
5538
                null,
5539
                false,
5540
                2
5541
            );
5542
5543
            $to_graph_exercise_result = [];
5544
            if (!empty($exercise_list)) {
5545
                $score = $weighting = $exe_id = 0;
5546
                foreach ($exercise_list as $exercices) {
5547
                    $exercise_obj = new Exercise($course_info['real_id']);
5548
                    $exercise_obj->read($exercices['id']);
5549
                    $visible_return = $exercise_obj->is_visible();
5550
                    $score = $weighting = $attempts = 0;
5551
5552
                    // Getting count of attempts by user
5553
                    $attempts = Event::count_exercise_attempts_by_user(
5554
                        api_get_user_id(),
5555
                        $exercices['id'],
5556
                        $course_info['real_id'],
5557
                        $session_id
5558
                    );
5559
5560
                    $html .= '<tr class="row_even">';
5561
                    $url = api_get_path(WEB_CODE_PATH)."exercise/overview.php?cidReq={$course_info['code']}&id_session=$session_id&exerciseId={$exercices['id']}";
5562
5563
                    if (true == $visible_return['value']) {
5564
                        $exercices['title'] = Display::url(
5565
                            $exercices['title'],
5566
                            $url,
5567
                            ['target' => SESSION_LINK_TARGET]
5568
                        );
5569
                    } elseif (-1 == $exercices['active']) {
5570
                        $exercices['title'] = sprintf(get_lang('%s (deleted)'), $exercices['title']);
5571
                    }
5572
5573
                    $html .= Display::tag('td', $exercices['title']);
5574
5575
                    // Exercise configuration show results or show only score
5576
                    if (0 == $exercices['results_disabled'] || 2 == $exercices['results_disabled']) {
5577
                        //For graphics
5578
                        $best_exercise_stats = Event::get_best_exercise_results_by_user(
5579
                            $exercices['id'],
5580
                            $course_info['real_id'],
5581
                            $session_id
5582
                        );
5583
5584
                        $to_graph_exercise_result[$exercices['id']] = [
5585
                            'title' => $exercices['title'],
5586
                            'data' => $best_exercise_stats,
5587
                        ];
5588
5589
                        $latest_attempt_url = '';
5590
                        $best_score = $position = $percentage_score_result = '-';
5591
                        $graph = $normal_graph = null;
5592
5593
                        // Getting best results
5594
                        $best_score_data = ExerciseLib::get_best_attempt_in_course(
5595
                            $exercices['id'],
5596
                            $course_info['real_id'],
5597
                            $session_id
5598
                        );
5599
5600
                        $best_score = '';
5601
                        if (!empty($best_score_data)) {
5602
                            $best_score = ExerciseLib::show_score(
5603
                                $best_score_data['score'],
5604
                                $best_score_data['max_score']
5605
                            );
5606
                        }
5607
5608
                        if ($attempts > 0) {
5609
                            $exercise_stat = ExerciseLib::get_best_attempt_by_user(
5610
                                api_get_user_id(),
5611
                                $exercices['id'],
5612
                                $course_info['real_id'],
5613
                                $session_id
5614
                            );
5615
                            if (!empty($exercise_stat)) {
5616
                                // Always getting the BEST attempt
5617
                                $score = $exercise_stat['score'];
5618
                                $weighting = $exercise_stat['max_score'];
5619
                                $exe_id = $exercise_stat['exe_id'];
5620
5621
                                $latest_attempt_url .= api_get_path(WEB_CODE_PATH).'exercise/result.php?id='.$exe_id.'&cidReq='.$course_info['code'].'&show_headers=1&id_session='.$session_id;
5622
                                $percentage_score_result = Display::url(
5623
                                    ExerciseLib::show_score($score, $weighting),
5624
                                    $latest_attempt_url
5625
                                );
5626
                                $my_score = 0;
5627
                                if (!empty($weighting) && 0 != intval($weighting)) {
5628
                                    $my_score = $score / $weighting;
5629
                                }
5630
                                //@todo this function slows the page
5631
                                if (is_int($user_list)) {
5632
                                    $user_list = [$user_list];
5633
                                }
5634
                                $position = ExerciseLib::get_exercise_result_ranking(
5635
                                    $my_score,
5636
                                    $exe_id,
5637
                                    $exercices['id'],
5638
                                    $course_info['code'],
5639
                                    $session_id,
5640
                                    $user_list
5641
                                );
5642
5643
                                $graph = self::generate_exercise_result_thumbnail_graph(
5644
                                    $to_graph_exercise_result[$exercices['id']]
5645
                                );
5646
                                $normal_graph = self::generate_exercise_result_graph(
5647
                                    $to_graph_exercise_result[$exercices['id']]
5648
                                );
5649
                            }
5650
                        }
5651
                        $html .= Display::div(
5652
                            $normal_graph,
5653
                            [
5654
                                'id' => 'main_graph_'.$exercices['id'],
5655
                                'class' => 'dialog',
5656
                                'style' => 'display:none',
5657
                            ]
5658
                        );
5659
5660
                        if (empty($graph)) {
5661
                            $graph = '-';
5662
                        } else {
5663
                            $graph = Display::url(
5664
                                '<img src="'.$graph.'" >',
5665
                                $normal_graph,
5666
                                [
5667
                                    'id' => $exercices['id'],
5668
                                    'class' => 'expand-image',
5669
                                ]
5670
                            );
5671
                        }
5672
5673
                        $html .= Display::tag('td', $attempts);
5674
                        $html .= Display::tag('td', $percentage_score_result);
5675
                        $html .= Display::tag('td', $position);
5676
                        $html .= Display::tag('td', $best_score);
5677
                        $html .= Display::tag('td', $graph);
5678
                    } else {
5679
                        // Exercise configuration NO results
5680
                        $html .= Display::tag('td', $attempts);
5681
                        $html .= Display::tag('td', '-');
5682
                        $html .= Display::tag('td', '-');
5683
                        $html .= Display::tag('td', '-');
5684
                        $html .= Display::tag('td', '-');
5685
                    }
5686
                    $html .= '</tr>';
5687
                }
5688
            } else {
5689
                $html .= '<tr><td colspan="5">'.get_lang('There is no test for the moment').'</td></tr>';
5690
            }
5691
            $html .= '</tbody></table></div>';
5692
5693
            $columnHeaders = [
5694
                'lp' => get_lang('Learning paths'),
5695
                'time' => get_lang('Time spent'),
5696
                'progress' => get_lang('Progress'),
5697
                'score' => get_lang('Score'),
5698
                'best_score' => get_lang('Best score'),
5699
                'last_connection' => get_lang('Latest login'),
5700
            ];
5701
5702
            $headers = '';
5703
            $trackingColumns = api_get_configuration_value('tracking_columns');
5704
            if (isset($trackingColumns['my_progress_lp'])) {
5705
                foreach ($columnHeaders as $key => $value) {
5706
                    if (!isset($trackingColumns['my_progress_lp'][$key]) ||
5707
                        false == $trackingColumns['my_progress_lp'][$key]
5708
                    ) {
5709
                        unset($columnHeaders[$key]);
5710
                    }
5711
                }
5712
            }
5713
5714
            $columnHeadersKeys = array_keys($columnHeaders);
5715
            foreach ($columnHeaders as $key => $columnName) {
5716
                $headers .= Display::tag(
5717
                    'th',
5718
                    $columnName
5719
                );
5720
            }
5721
5722
            // LP table results
5723
            $html .= '<div class="table-responsive">';
5724
            $html .= '<table class="table table-striped table-hover">';
5725
            $html .= '<thead><tr>';
5726
            $html .= $headers;
5727
            $html .= '</tr></thead><tbody>';
5728
5729
            $list = new LearnpathList(
5730
                api_get_user_id(),
5731
                $course_info,
5732
                $session_id,
5733
                'lp.publicatedOn ASC',
5734
                true,
5735
                null,
5736
                true
5737
            );
5738
5739
            $lp_list = $list->get_flat_list();
5740
5741
            if (!empty($lp_list)) {
5742
                foreach ($lp_list as $lp_id => $learnpath) {
5743
                    if (!$learnpath['lp_visibility']) {
5744
                        continue;
5745
                    }
5746
5747
                    $progress = self::get_avg_student_progress(
5748
                        $user_id,
5749
                        $course,
5750
                        [$lp_id],
5751
                        $session_id
5752
                    );
5753
                    $last_connection_in_lp = self::get_last_connection_time_in_lp(
5754
                        $user_id,
5755
                        $course,
5756
                        $lp_id,
5757
                        $session_id
5758
                    );
5759
5760
                    $time_spent_in_lp = self::get_time_spent_in_lp(
5761
                        $user_id,
5762
                        $course,
5763
                        [$lp_id],
5764
                        $session_id
5765
                    );
5766
                    $percentage_score = self::get_avg_student_score(
5767
                        $user_id,
5768
                        $course,
5769
                        [$lp_id],
5770
                        $session_id
5771
                    );
5772
5773
                    $bestScore = self::get_avg_student_score(
5774
                        $user_id,
5775
                        $course,
5776
                        [$lp_id],
5777
                        $session_id,
5778
                        false,
5779
                        false,
5780
                        true
5781
                    );
5782
5783
                    if (is_numeric($progress)) {
5784
                        $progress = $progress.'%';
5785
                    }
5786
                    if (is_numeric($percentage_score)) {
5787
                        $percentage_score = $percentage_score.'%';
5788
                    } else {
5789
                        $percentage_score = '0%';
5790
                    }
5791
5792
                    if (is_numeric($bestScore)) {
5793
                        $bestScore = $bestScore.'%';
5794
                    } else {
5795
                        $bestScore = '-';
5796
                    }
5797
5798
                    $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
5799
                    $last_connection = '-';
5800
                    if (!empty($last_connection_in_lp)) {
5801
                        $last_connection = api_convert_and_format_date(
5802
                            $last_connection_in_lp,
5803
                            DATE_TIME_FORMAT_LONG
5804
                        );
5805
                    }
5806
5807
                    $url = api_get_path(WEB_CODE_PATH)."lp/lp_controller.php?cidReq={$course_code}&id_session=$session_id&lp_id=$lp_id&action=view";
5808
                    $html .= '<tr class="row_even">';
5809
5810
                    if (in_array('lp', $columnHeadersKeys)) {
5811
                        if (0 == $learnpath['lp_visibility']) {
5812
                            $html .= Display::tag('td', $learnpath['lp_name']);
5813
                        } else {
5814
                            $html .= Display::tag(
5815
                                'td',
5816
                                Display::url(
5817
                                    $learnpath['lp_name'],
5818
                                    $url,
5819
                                    ['target' => SESSION_LINK_TARGET]
5820
                                )
5821
                            );
5822
                        }
5823
                    }
5824
5825
                    if (in_array('time', $columnHeadersKeys)) {
5826
                        $html .= Display::tag(
5827
                            'td',
5828
                            $time_spent_in_lp
5829
                        );
5830
                    }
5831
5832
                    if (in_array('progress', $columnHeadersKeys)) {
5833
                        $html .= Display::tag(
5834
                            'td',
5835
                            $progress
5836
                        );
5837
                    }
5838
5839
                    if (in_array('score', $columnHeadersKeys)) {
5840
                        $html .= Display::tag('td', $percentage_score);
5841
                    }
5842
                    if (in_array('best_score', $columnHeadersKeys)) {
5843
                        $html .= Display::tag('td', $bestScore);
5844
                    }
5845
5846
                    if (in_array('last_connection', $columnHeadersKeys)) {
5847
                        $html .= Display::tag('td', $last_connection, ['width' => '180px']);
5848
                    }
5849
                    $html .= '</tr>';
5850
                }
5851
            } else {
5852
                $html .= '<tr>
5853
                        <td colspan="4" align="center">
5854
                            '.get_lang('No learning path').'
5855
                        </td>
5856
                      </tr>';
5857
            }
5858
            $html .= '</tbody></table></div>';
5859
5860
            $html .= self::displayUserSkills($user_id, $course_info['id'], $session_id);
5861
        }
5862
5863
        return $html;
5864
    }
5865
5866
    /**
5867
     * Generates an histogram.
5868
     *
5869
     * @param array $names      list of exercise names
5870
     * @param array $my_results my results 0 to 100
5871
     * @param array $average    average scores 0-100
5872
     *
5873
     * @return string
5874
     */
5875
    public static function generate_session_exercise_graph($names, $my_results, $average)
5876
    {
5877
        $html = api_get_js('chartjs/Chart.js');
5878
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
5879
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
5880
        $jsStr = " var data = {
5881
                       labels:".json_encode($names).",
5882
                       datasets: [
5883
                       {
5884
                         label: '".get_lang('My results')."',
5885
                         backgroundColor: 'rgb(255, 99, 132)',
5886
                         stack: 'Stack1',
5887
                         data: ".json_encode($my_results).",
5888
                        },
5889
                        {
5890
                         label: '".get_lang('Average score')."',
5891
                         backgroundColor: 'rgb(75, 192, 192)',
5892
                         stack: 'Stack2',
5893
                         data: ".json_encode($average).",
5894
                        },
5895
                        ],
5896
                    };
5897
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
5898
                    var myBarChart = new Chart(ctx, {
5899
                    type: 'bar',
5900
                    data: data,
5901
                    options: {
5902
                            title: {
5903
                                    display: true,
5904
                                    text: '".get_lang('TestsInTimeProgressChart')."'
5905
                            },
5906
                            tooltips: {
5907
                                    mode: 'index',
5908
                                    intersect: false
5909
                            },
5910
                            responsive: true,
5911
                            scales: {
5912
                                yAxes: [{
5913
                                    ticks: {
5914
                                        // Include a dollar sign in the ticks
5915
                                        callback: function(value, index, values) {
5916
                                            return value + '%';
5917
                                        }
5918
                                    }
5919
                                }]
5920
                            }
5921
                    }
5922
                });";
5923
        $html .= Display::tag('script', $jsStr);
5924
5925
        return $html;
5926
    }
5927
5928
    /**
5929
     * Returns a thumbnail of the function generate_exercise_result_graph.
5930
     *
5931
     * @param array $attempts
5932
     */
5933
    public static function generate_exercise_result_thumbnail_graph($attempts)
5934
    {
5935
        //$exercise_title = $attempts['title'];
5936
        $attempts = $attempts['data'];
5937
        $my_exercise_result_array = $exercise_result = [];
5938
        if (empty($attempts)) {
5939
            return null;
5940
        }
5941
5942
        foreach ($attempts as $attempt) {
5943
            if (api_get_user_id() == $attempt['exe_user_id']) {
5944
                if (0 != $attempt['max_score']) {
5945
                    $my_exercise_result_array[] = $attempt['score'] / $attempt['max_score'];
5946
                }
5947
            } else {
5948
                if (0 != $attempt['max_score']) {
5949
                    $exercise_result[] = $attempt['score'] / $attempt['max_score'];
5950
                }
5951
            }
5952
        }
5953
5954
        // Getting best result
5955
        rsort($my_exercise_result_array);
5956
        $my_exercise_result = 0;
5957
        if (isset($my_exercise_result_array[0])) {
5958
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5959
        }
5960
5961
        $max = 100;
5962
        $pieces = 5;
5963
        $part = round($max / $pieces);
5964
        $x_axis = [];
5965
        $final_array = [];
5966
        $my_final_array = [];
5967
5968
        for ($i = 1; $i <= $pieces; $i++) {
5969
            $sum = 1;
5970
            if (1 == $i) {
5971
                $sum = 0;
5972
            }
5973
            $min = ($i - 1) * $part + $sum;
5974
            $max = ($i) * $part;
5975
            $x_axis[] = $min." - ".$max;
5976
            $count = 0;
5977
            foreach ($exercise_result as $result) {
5978
                $percentage = $result * 100;
5979
                if ($percentage >= $min && $percentage <= $max) {
5980
                    //echo ' is > ';
5981
                    $count++;
5982
                }
5983
            }
5984
            //echo '<br />';
5985
            $final_array[] = $count;
5986
5987
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5988
                $my_final_array[] = 1;
5989
            } else {
5990
                $my_final_array[] = 0;
5991
            }
5992
        }
5993
5994
        // Fix to remove the data of the user with my data
5995
        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...
5996
            if (!empty($my_final_array[$i])) {
5997
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5998
                $final_array[$i] = 0;
5999
            }
6000
        }
6001
6002
        // Dataset definition
6003
        $dataSet = new pData();
6004
        $dataSet->addPoints($final_array, 'Serie1');
6005
        $dataSet->addPoints($my_final_array, 'Serie2');
6006
        $dataSet->normalize(100, "%");
6007
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6008
6009
        // Cache definition
6010
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6011
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6012
        $chartHash = $myCache->getHash($dataSet);
6013
        if ($myCache->isInCache($chartHash)) {
6014
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6015
            $myCache->saveFromCache($chartHash, $imgPath);
6016
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6017
        } else {
6018
            /* Create the pChart object */
6019
            $widthSize = 80;
6020
            $heightSize = 35;
6021
            $fontSize = 2;
6022
6023
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6024
6025
            /* Turn of Antialiasing */
6026
            $myPicture->Antialias = false;
6027
6028
            /* Add a border to the picture */
6029
            $myPicture->drawRectangle(
6030
                0,
6031
                0,
6032
                $widthSize - 1,
6033
                $heightSize - 1,
6034
                ['R' => 0, 'G' => 0, 'B' => 0]
6035
            );
6036
6037
            /* Set the default font */
6038
            $myPicture->setFontProperties(
6039
                [
6040
                    'FontName' => api_get_path(
6041
                            SYS_FONTS_PATH
6042
                        ).'opensans/OpenSans-Regular.ttf',
6043
                    'FontSize' => $fontSize,
6044
                ]
6045
            );
6046
6047
            /* Do not write the chart title */
6048
            /* Define the chart area */
6049
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
6050
6051
            /* Draw the scale */
6052
            $scaleSettings = [
6053
                'GridR' => 200,
6054
                'GridG' => 200,
6055
                'GridB' => 200,
6056
                'DrawSubTicks' => true,
6057
                'CycleBackground' => true,
6058
                'Mode' => SCALE_MODE_MANUAL,
6059
                'ManualScale' => [
6060
                    '0' => [
6061
                        'Min' => 0,
6062
                        'Max' => 100,
6063
                    ],
6064
                ],
6065
            ];
6066
            $myPicture->drawScale($scaleSettings);
6067
6068
            /* Turn on shadow computing */
6069
            $myPicture->setShadow(
6070
                true,
6071
                [
6072
                    'X' => 1,
6073
                    'Y' => 1,
6074
                    'R' => 0,
6075
                    'G' => 0,
6076
                    'B' => 0,
6077
                    'Alpha' => 10,
6078
                ]
6079
            );
6080
6081
            /* Draw the chart */
6082
            $myPicture->setShadow(
6083
                true,
6084
                [
6085
                    'X' => 1,
6086
                    'Y' => 1,
6087
                    'R' => 0,
6088
                    'G' => 0,
6089
                    'B' => 0,
6090
                    'Alpha' => 10,
6091
                ]
6092
            );
6093
            $settings = [
6094
                'DisplayValues' => true,
6095
                'DisplaySize' => $fontSize,
6096
                'DisplayR' => 0,
6097
                'DisplayG' => 0,
6098
                'DisplayB' => 0,
6099
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6100
                'Gradient' => false,
6101
                'Surrounding' => 5,
6102
                'InnerSurrounding' => 5,
6103
            ];
6104
            $myPicture->drawStackedBarChart($settings);
6105
6106
            /* Save and write in cache */
6107
            $myCache->writeToCache($chartHash, $myPicture);
6108
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6109
            $myCache->saveFromCache($chartHash, $imgPath);
6110
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6111
        }
6112
6113
        return $imgPath;
6114
    }
6115
6116
    /**
6117
     * Generates a big graph with the number of best results.
6118
     *
6119
     * @param	array
6120
     */
6121
    public static function generate_exercise_result_graph($attempts)
6122
    {
6123
        $exercise_title = strip_tags($attempts['title']);
6124
        $attempts = $attempts['data'];
6125
        $my_exercise_result_array = $exercise_result = [];
6126
        if (empty($attempts)) {
6127
            return null;
6128
        }
6129
        foreach ($attempts as $attempt) {
6130
            if (api_get_user_id() == $attempt['exe_user_id']) {
6131
                if (0 != $attempt['max_score']) {
6132
                    $my_exercise_result_array[] = $attempt['score'] / $attempt['max_score'];
6133
                }
6134
            } else {
6135
                if (0 != $attempt['max_score']) {
6136
                    $exercise_result[] = $attempt['score'] / $attempt['max_score'];
6137
                }
6138
            }
6139
        }
6140
6141
        //Getting best result
6142
        rsort($my_exercise_result_array);
6143
        $my_exercise_result = 0;
6144
        if (isset($my_exercise_result_array[0])) {
6145
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6146
        }
6147
6148
        $max = 100;
6149
        $pieces = 5;
6150
        $part = round($max / $pieces);
6151
        $x_axis = [];
6152
        $final_array = [];
6153
        $my_final_array = [];
6154
6155
        for ($i = 1; $i <= $pieces; $i++) {
6156
            $sum = 1;
6157
            if (1 == $i) {
6158
                $sum = 0;
6159
            }
6160
            $min = ($i - 1) * $part + $sum;
6161
            $max = ($i) * $part;
6162
            $x_axis[] = $min." - ".$max;
6163
            $count = 0;
6164
            foreach ($exercise_result as $result) {
6165
                $percentage = $result * 100;
6166
                if ($percentage >= $min && $percentage <= $max) {
6167
                    $count++;
6168
                }
6169
            }
6170
            $final_array[] = $count;
6171
6172
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6173
                $my_final_array[] = 1;
6174
            } else {
6175
                $my_final_array[] = 0;
6176
            }
6177
        }
6178
6179
        //Fix to remove the data of the user with my data
6180
6181
        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...
6182
            if (!empty($my_final_array[$i])) {
6183
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6184
                $final_array[$i] = 0;
6185
            }
6186
        }
6187
6188
        // Dataset definition
6189
        $dataSet = new pData();
6190
        $dataSet->addPoints($final_array, 'Serie1');
6191
        $dataSet->addPoints($my_final_array, 'Serie2');
6192
        $dataSet->addPoints($x_axis, 'Serie3');
6193
6194
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
6195
        $dataSet->setSerieDescription('Serie2', get_lang('My results'));
6196
        $dataSet->setAbscissa('Serie3');
6197
6198
        $dataSet->setXAxisName(get_lang('Score'));
6199
        $dataSet->normalize(100, "%");
6200
6201
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6202
6203
        // Cache definition
6204
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6205
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6206
        $chartHash = $myCache->getHash($dataSet);
6207
6208
        if ($myCache->isInCache($chartHash)) {
6209
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6210
            $myCache->saveFromCache($chartHash, $imgPath);
6211
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6212
        } else {
6213
            /* Create the pChart object */
6214
            $widthSize = 480;
6215
            $heightSize = 250;
6216
            $fontSize = 8;
6217
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6218
6219
            /* Turn of Antialiasing */
6220
            $myPicture->Antialias = false;
6221
6222
            /* Add a border to the picture */
6223
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
6224
6225
            /* Set the default font */
6226
            $myPicture->setFontProperties(
6227
                [
6228
                    'FontName' => api_get_path(
6229
                            SYS_FONTS_PATH
6230
                        ).'opensans/OpenSans-Regular.ttf',
6231
                    'FontSize' => 10,
6232
                ]
6233
            );
6234
6235
            /* Write the chart title */
6236
            $myPicture->drawText(
6237
                250,
6238
                20,
6239
                $exercise_title,
6240
                [
6241
                    'FontSize' => 12,
6242
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
6243
                ]
6244
            );
6245
6246
            /* Define the chart area */
6247
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
6248
6249
            /* Draw the scale */
6250
            $scaleSettings = [
6251
                'GridR' => 200,
6252
                'GridG' => 200,
6253
                'GridB' => 200,
6254
                'DrawSubTicks' => true,
6255
                'CycleBackground' => true,
6256
                'Mode' => SCALE_MODE_MANUAL,
6257
                'ManualScale' => [
6258
                    '0' => [
6259
                        'Min' => 0,
6260
                        'Max' => 100,
6261
                    ],
6262
                ],
6263
            ];
6264
            $myPicture->drawScale($scaleSettings);
6265
6266
            /* Turn on shadow computing */
6267
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6268
6269
            /* Draw the chart */
6270
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6271
            $settings = [
6272
                'DisplayValues' => true,
6273
                'DisplaySize' => $fontSize,
6274
                'DisplayR' => 0,
6275
                'DisplayG' => 0,
6276
                'DisplayB' => 0,
6277
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6278
                'Gradient' => false,
6279
                'Surrounding' => 30,
6280
                'InnerSurrounding' => 25,
6281
            ];
6282
            $myPicture->drawStackedBarChart($settings);
6283
6284
            $legendSettings = [
6285
                'Mode' => LEGEND_HORIZONTAL,
6286
                'Style' => LEGEND_NOBORDER,
6287
            ];
6288
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
6289
6290
            /* Write and save into cache */
6291
            $myCache->writeToCache($chartHash, $myPicture);
6292
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6293
            $myCache->saveFromCache($chartHash, $imgPath);
6294
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6295
        }
6296
6297
        return $imgPath;
6298
    }
6299
6300
    /**
6301
     * @param FormValidator $form
6302
     *
6303
     * @return mixed
6304
     */
6305
    public static function setUserSearchForm($form)
6306
    {
6307
        $form->addElement('text', 'keyword', get_lang('Keyword'));
6308
        $form->addElement(
6309
            'select',
6310
            'active',
6311
            get_lang('Status'),
6312
            [1 => get_lang('active'), 0 => get_lang('inactive')]
6313
        );
6314
6315
        $form->addElement(
6316
            'select',
6317
            'sleeping_days',
6318
            get_lang('Inactive days'),
6319
            [
6320
                '',
6321
                1 => 1,
6322
                5 => 5,
6323
                15 => 15,
6324
                30 => 30,
6325
                60 => 60,
6326
                90 => 90,
6327
                120 => 120,
6328
            ]
6329
        );
6330
6331
        $form->addButtonSearch(get_lang('Search'));
6332
6333
        return $form;
6334
    }
6335
6336
    /**
6337
     * Get the progress of a exercise.
6338
     *
6339
     * @param int    $sessionId  The session ID (session.id)
6340
     * @param int    $courseId   The course ID (course.id)
6341
     * @param int    $exerciseId The quiz ID (c_quiz.id)
6342
     * @param string $date_from
6343
     * @param string $date_to
6344
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
6345
     *
6346
     * @return array An array with the data of exercise(s) progress
6347
     */
6348
    public static function get_exercise_progress(
6349
        $sessionId = 0,
6350
        $courseId = 0,
6351
        $exerciseId = 0,
6352
        $date_from = null,
6353
        $date_to = null,
6354
        $options = []
6355
    ) {
6356
        $sessionId = intval($sessionId);
6357
        $courseId = intval($courseId);
6358
        $exerciseId = intval($exerciseId);
6359
        $date_from = Database::escape_string($date_from);
6360
        $date_to = Database::escape_string($date_to);
6361
        /*
6362
         * This method gets the data by blocks, as previous attempts at one single
6363
         * query made it take ages. The logic of query division is described below
6364
         */
6365
        // Get tables names
6366
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6367
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6368
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6369
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6370
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6371
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6372
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6373
6374
        $sessions = [];
6375
        $courses = [];
6376
        // if session ID is defined but course ID is empty, get all the courses
6377
        // from that session
6378
        if (!empty($sessionId) && empty($courseId)) {
6379
            // $courses is an array of course int id as index and course details hash as value
6380
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6381
            $sessions[$sessionId] = api_get_session_info($sessionId);
6382
        } elseif (empty($sessionId) && !empty($courseId)) {
6383
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6384
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
6385
            $course = api_get_course_info_by_id($courseId);
6386
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6387
            $courses[$courseId] = $course;
6388
            foreach ($sessionsTemp as $sessionItem) {
6389
                $sessions[$sessionItem['id']] = $sessionItem;
6390
            }
6391
        } elseif (!empty($courseId) && !empty($sessionId)) {
6392
            //none is empty
6393
            $course = api_get_course_info_by_id($courseId);
6394
            $courses[$courseId] = [$course['code']];
6395
            $courses[$courseId]['code'] = $course['code'];
6396
            $sessions[$sessionId] = api_get_session_info($sessionId);
6397
        } else {
6398
            //both are empty, not enough data, return an empty array
6399
            return [];
6400
        }
6401
        // Now we have two arrays of courses and sessions with enough data to proceed
6402
        // If no course could be found, we shouldn't return anything.
6403
        // Course sessions can be empty (then we only return the pure-course-context results)
6404
        if (count($courses) < 1) {
6405
            return [];
6406
        }
6407
6408
        $data = [];
6409
        // The following loop is less expensive than what it seems:
6410
        // - if a course was defined, then we only loop through sessions
6411
        // - if a session was defined, then we only loop through courses
6412
        // - if a session and a course were defined, then we only loop once
6413
        foreach ($courses as $courseIdx => $courseData) {
6414
            $where = '';
6415
            $whereParams = [];
6416
            $whereSessionParams = '';
6417
            if (count($sessions > 0)) {
6418
                foreach ($sessions as $sessionIdx => $sessionData) {
6419
                    if (!empty($sessionIdx)) {
6420
                        $whereSessionParams .= $sessionIdx.',';
6421
                    }
6422
                }
6423
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6424
            }
6425
6426
            if (!empty($exerciseId)) {
6427
                $exerciseId = intval($exerciseId);
6428
                $where .= ' AND q.id = %d ';
6429
                $whereParams[] = $exerciseId;
6430
            }
6431
6432
            /*
6433
             * This feature has been disabled for now, to avoid having to
6434
             * join two very large tables
6435
            //2 = show all questions (wrong and correct answered)
6436
            if ($answer != 2) {
6437
                $answer = intval($answer);
6438
                //$where .= ' AND qa.correct = %d';
6439
                //$whereParams[] = $answer;
6440
            }
6441
            */
6442
6443
            $limit = '';
6444
            if (!empty($options['limit'])) {
6445
                $limit = " LIMIT ".$options['limit'];
6446
            }
6447
6448
            if (!empty($options['where'])) {
6449
                $where .= ' AND '.Database::escape_string($options['where']);
6450
            }
6451
6452
            $order = '';
6453
            if (!empty($options['order'])) {
6454
                $order = " ORDER BY ".$options['order'];
6455
            }
6456
6457
            if (!empty($date_to) && !empty($date_from)) {
6458
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6459
            }
6460
6461
            $sql = "SELECT
6462
                te.session_id,
6463
                ta.id as attempt_id,
6464
                te.exe_user_id as user_id,
6465
                te.exe_id as exercise_attempt_id,
6466
                ta.question_id,
6467
                ta.answer as answer_id,
6468
                ta.tms as time,
6469
                te.exe_exo_id as quiz_id,
6470
                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
6471
                q.title as quiz_title,
6472
                qq.description as description
6473
                FROM $ttrack_exercises te
6474
                INNER JOIN $ttrack_attempt ta
6475
                ON ta.exe_id = te.exe_id
6476
                INNER JOIN $tquiz q
6477
                ON q.id = te.exe_exo_id
6478
                INNER JOIN $tquiz_rel_question rq
6479
                ON rq.exercice_id = q.id AND rq.c_id = q.c_id
6480
                INNER JOIN $tquiz_question qq
6481
                ON
6482
                    qq.id = rq.question_id AND
6483
                    qq.c_id = rq.c_id AND
6484
                    qq.position = rq.question_order AND
6485
                    ta.question_id = rq.question_id
6486
                WHERE
6487
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6488
                    AND q.c_id = $courseIdx
6489
                    $where $order $limit";
6490
            $sql_query = vsprintf($sql, $whereParams);
6491
6492
            // Now browse through the results and get the data
6493
            $rs = Database::query($sql_query);
6494
            $userIds = [];
6495
            $questionIds = [];
6496
            $answerIds = [];
6497
            while ($row = Database::fetch_array($rs)) {
6498
                //only show if exercise is visible
6499
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6500
                    $userIds[$row['user_id']] = $row['user_id'];
6501
                    $questionIds[$row['question_id']] = $row['question_id'];
6502
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6503
                    $row['session'] = $sessions[$row['session_id']];
6504
                    $data[] = $row;
6505
                }
6506
            }
6507
            // Now fill questions data. Query all questions and answers for this test to avoid
6508
            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
6509
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
6510
                            FROM $tquiz_question tq, $tquiz_answer tqa
6511
                            WHERE
6512
                                tqa.question_id = tq.id AND
6513
                                tqa.c_id = tq.c_id AND
6514
                                tq.c_id = $courseIdx AND
6515
                                tq.id IN (".implode(',', $questionIds).")";
6516
6517
            $resQuestions = Database::query($sqlQuestions);
6518
            $answer = [];
6519
            $question = [];
6520
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6521
                $questionId = $rowQuestion['question_id'];
6522
                $answerId = $rowQuestion['answer_id'];
6523
                $answer[$questionId][$answerId] = [
6524
                    'position' => $rowQuestion['position'],
6525
                    'question' => $rowQuestion['question'],
6526
                    'answer' => $rowQuestion['answer'],
6527
                    'correct' => $rowQuestion['correct'],
6528
                ];
6529
                $question[$questionId]['question'] = $rowQuestion['question'];
6530
            }
6531
6532
            // Now fill users data
6533
            $sqlUsers = "SELECT id as user_id, username, lastname, firstname
6534
                         FROM $tuser
6535
                         WHERE id IN (".implode(',', $userIds).")";
6536
            $resUsers = Database::query($sqlUsers);
6537
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6538
                $users[$rowUser['user_id']] = $rowUser;
6539
            }
6540
6541
            foreach ($data as $id => $row) {
6542
                $rowQuestId = $row['question_id'];
6543
                $rowAnsId = $row['answer_id'];
6544
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
6545
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
6546
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6547
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6548
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6549
                $data[$id]['correct'] = (0 == $answer[$rowQuestId][$rowAnsId]['correct'] ? get_lang('No') : get_lang('Yes'));
6550
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6551
                $data[$id]['question_id'] = $rowQuestId;
6552
                $data[$id]['description'] = $row['description'];
6553
            }
6554
6555
            /*
6556
            The minimum expected array structure at the end is:
6557
            attempt_id,
6558
            session name,
6559
            exercise_id,
6560
            quiz_title,
6561
            username,
6562
            lastname,
6563
            firstname,
6564
            time,
6565
            question_id,
6566
            question,
6567
            answer,
6568
            */
6569
        }
6570
6571
        return $data;
6572
    }
6573
6574
    /**
6575
     * @param string              $tool
6576
     * @param sessionEntity |null $session Optional
6577
     *
6578
     * @throws \Doctrine\ORM\NonUniqueResultException
6579
     *
6580
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
6581
     */
6582
    public static function getLastStudentPublication(
6583
        User $user,
6584
        $tool,
6585
        Course $course,
6586
        SessionEntity $session = null
6587
    ) {
6588
        return Database::getManager()
6589
            ->createQuery("
6590
                SELECT csp
6591
                FROM ChamiloCourseBundle:CStudentPublication csp
6592
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6593
                    WITH (
6594
                        csp.iid = cip.ref AND
6595
                        csp.session = cip.session AND
6596
                        csp.cId = cip.course AND
6597
                        csp.userId = cip.lasteditUserId
6598
                    )
6599
                WHERE
6600
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6601
                ORDER BY csp.iid DESC
6602
            ")
6603
            ->setMaxResults(1)
6604
            ->setParameters([
6605
                'tool' => $tool,
6606
                'session' => $session,
6607
                'course' => $course,
6608
                'user' => $user,
6609
            ])
6610
            ->getOneOrNullResult();
6611
    }
6612
6613
    /**
6614
     * Get the HTML code for show a block with the achieved user skill on course/session.
6615
     *
6616
     * @param int  $userId
6617
     * @param int  $courseId
6618
     * @param int  $sessionId
6619
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6620
     *
6621
     * @return string
6622
     */
6623
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6624
    {
6625
        if (false === Skill::isAllowed($userId, false) && false == $forceView) {
6626
            return '';
6627
        }
6628
        $skillManager = new Skill();
6629
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6630
6631
        return $html;
6632
    }
6633
6634
    /**
6635
     * @param int $userId
6636
     * @param int $courseId
6637
     * @param int $sessionId
6638
     *
6639
     * @return array
6640
     */
6641
    public static function getCalculateTime($userId, $courseId, $sessionId)
6642
    {
6643
        $userId = (int) $userId;
6644
        $courseId = (int) $courseId;
6645
        $sessionId = (int) $sessionId;
6646
6647
        if (empty($userId) || empty($courseId)) {
6648
            return [];
6649
        }
6650
6651
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6652
                FROM track_e_access_complete
6653
                WHERE
6654
                    user_id = $userId AND
6655
                    c_id = $courseId AND
6656
                    session_id = $sessionId AND
6657
                    login_as = 0
6658
                ORDER BY date_reg ASC
6659
                LIMIT 1";
6660
        $rs = Database::query($sql);
6661
6662
        $firstConnection = '';
6663
        $lastConnection = '';
6664
        if (Database::num_rows($rs) > 0) {
6665
            $value = Database::fetch_array($rs);
6666
            $firstConnection = $value['min'];
6667
            $lastConnection = $value['max'];
6668
        }
6669
6670
        $sql = "SELECT * FROM track_e_access_complete
6671
                WHERE
6672
                    user_id = $userId AND
6673
                    c_id = $courseId AND
6674
                    session_id = $sessionId AND
6675
                    login_as = 0 AND current_id <> 0";
6676
6677
        $res = Database::query($sql);
6678
        $reg = [];
6679
        while ($row = Database::fetch_assoc($res)) {
6680
            $reg[$row['id']] = $row;
6681
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6682
        }
6683
6684
        $sessions = [];
6685
        foreach ($reg as $key => $value) {
6686
            $sessions[$value['current_id']][$value['tool']][] = $value;
6687
        }
6688
6689
        $quizTime = 0;
6690
        $result = [];
6691
        $totalTime = 0;
6692
        $lpTime = [];
6693
        $lpDetailTime = [];
6694
        foreach ($sessions as $listPerTool) {
6695
            $min = 0;
6696
            $max = 0;
6697
            $sessionDiff = 0;
6698
            foreach ($listPerTool as $tool => $results) {
6699
                $beforeItem = [];
6700
                foreach ($results as $item) {
6701
                    if (empty($beforeItem)) {
6702
                        $beforeItem = $item;
6703
                        if (empty($min)) {
6704
                            $min = $item['date_reg'];
6705
                        }
6706
6707
                        if (empty($max)) {
6708
                            $max = $item['date_reg'];
6709
                        }
6710
                        continue;
6711
                    }
6712
6713
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6714
                    if ($item['date_reg'] > $max) {
6715
                        $max = $item['date_reg'];
6716
                    }
6717
6718
                    if (empty($min)) {
6719
                        $min = $item['date_reg'];
6720
                    }
6721
6722
                    if ($item['date_reg'] < $min) {
6723
                        $min = $item['date_reg'];
6724
                    }
6725
6726
                    switch ($tool) {
6727
                        case TOOL_AGENDA:
6728
                        case TOOL_FORUM:
6729
                        case TOOL_ANNOUNCEMENT:
6730
                        case TOOL_COURSE_DESCRIPTION:
6731
                        case TOOL_SURVEY:
6732
                        case TOOL_NOTEBOOK:
6733
                        case TOOL_GRADEBOOK:
6734
                        case TOOL_DROPBOX:
6735
                        case 'Reports':
6736
                        case 'Videoconference':
6737
                        case TOOL_LINK:
6738
                        case TOOL_CHAT:
6739
                        case 'course-main':
6740
                            if (!isset($result[$tool])) {
6741
                                $result[$tool] = 0;
6742
                            }
6743
                            $result[$tool] += $partialTime;
6744
                            break;
6745
                        case TOOL_LEARNPATH:
6746
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6747
                                break;
6748
                            }
6749
                            if (!isset($lpTime[$item['tool_id']])) {
6750
                                $lpTime[$item['tool_id']] = 0;
6751
                            }
6752
6753
                            // Saving the attempt id "action_details"
6754
                            if (!empty($item['tool_id'])) {
6755
                                if (!empty($item['tool_id_detail'])) {
6756
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6757
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6758
                                    }
6759
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6760
                                }
6761
                                $lpTime[$item['tool_id']] += $partialTime;
6762
                            }
6763
                            break;
6764
                        case TOOL_QUIZ:
6765
                            if (!isset($lpTime[$item['action_details']])) {
6766
                                $lpTime[$item['action_details']] = 0;
6767
                            }
6768
                            if ('learnpath_id' === $beforeItem['action']) {
6769
                                $lpTime[$item['action_details']] += $partialTime;
6770
                            } else {
6771
                                $quizTime += $partialTime;
6772
                            }
6773
                            break;
6774
                    }
6775
                    $beforeItem = $item;
6776
                }
6777
            }
6778
6779
            $sessionDiff += $max - $min;
6780
            if ($sessionDiff > 0) {
6781
                $totalTime += $sessionDiff;
6782
            }
6783
        }
6784
6785
        $totalLp = 0;
6786
        foreach ($lpTime as $value) {
6787
            $totalLp += $value;
6788
        }
6789
6790
        $result['learnpath_detailed'] = $lpDetailTime;
6791
        $result[TOOL_LEARNPATH] = $lpTime;
6792
        $result[TOOL_QUIZ] = $quizTime;
6793
        $result['total_learnpath'] = $totalLp;
6794
        $result['total_time'] = $totalTime;
6795
        $result['number_connections'] = count($sessions);
6796
        $result['first'] = $firstConnection;
6797
        $result['last'] = $lastConnection;
6798
6799
        return $result;
6800
    }
6801
6802
    /**
6803
     * Gets the IP of a given user, using the last login before the given date.
6804
     *
6805
     * @param int User ID
6806
     * @param string Datetime
6807
     * @param bool Whether to return the IP as a link or just as an IP
6808
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6809
     *
6810
     * @return string IP address (or false on error)
6811
     * @assert (0,0) === false
6812
     */
6813
    public static function get_ip_from_user_event(
6814
        $user_id,
6815
        $event_date,
6816
        $return_as_link = false,
6817
        $body_replace = null
6818
    ) {
6819
        if (empty($user_id) || empty($event_date)) {
6820
            return false;
6821
        }
6822
        $user_id = intval($user_id);
6823
        $event_date = Database::escape_string($event_date);
6824
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6825
        $sql_ip = "SELECT login_date, user_ip
6826
                   FROM $table_login
6827
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6828
                   ORDER BY login_date DESC LIMIT 1";
6829
        $ip = '';
6830
        $res_ip = Database::query($sql_ip);
6831
        if (false !== $res_ip && Database::num_rows($res_ip) > 0) {
6832
            $row_ip = Database::fetch_row($res_ip);
6833
            if ($return_as_link) {
6834
                $ip = Display::url(
6835
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6836
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6837
                    ['title' => get_lang('Trace IP'), 'target' => '_blank']
6838
                );
6839
            } else {
6840
                $ip = $row_ip[1];
6841
            }
6842
        }
6843
6844
        return $ip;
6845
    }
6846
6847
    /**
6848
     * @param int   $userId
6849
     * @param array $courseInfo
6850
     * @param int   $sessionId
6851
     *
6852
     * @return array
6853
     */
6854
    public static function getToolInformation(
6855
        $userId,
6856
        $courseInfo,
6857
        $sessionId = 0
6858
    ) {
6859
        $csvContent = [];
6860
        $courseToolInformation = '';
6861
        $headerTool = [
6862
            [get_lang('Title')],
6863
            [get_lang('Created at')],
6864
            [get_lang('Updated at')],
6865
        ];
6866
6867
        $headerListForCSV = [];
6868
        foreach ($headerTool as $item) {
6869
            $headerListForCSV[] = $item[0];
6870
        }
6871
6872
        $courseForumInformationArray = getForumCreatedByUser(
6873
            $userId,
6874
            $courseInfo,
6875
            $sessionId
6876
        );
6877
6878
        if (!empty($courseForumInformationArray)) {
6879
            $csvContent[] = [];
6880
            $csvContent[] = [get_lang('Forums')];
6881
            $csvContent[] = $headerListForCSV;
6882
            foreach ($courseForumInformationArray as $row) {
6883
                $csvContent[] = $row;
6884
            }
6885
6886
            $courseToolInformation .= Display::page_subheader2(
6887
                get_lang('Forums')
6888
            );
6889
            $courseToolInformation .= Display::return_sortable_table(
6890
                $headerTool,
6891
                $courseForumInformationArray
6892
            );
6893
        }
6894
6895
        $courseWorkInformationArray = getWorkCreatedByUser($userId, $courseInfo['real_id'], $sessionId);
6896
6897
        if (!empty($courseWorkInformationArray)) {
6898
            $csvContent[] = null;
6899
            $csvContent[] = [get_lang('Assignments')];
6900
            $csvContent[] = $headerListForCSV;
6901
6902
            foreach ($courseWorkInformationArray as $row) {
6903
                $csvContent[] = $row;
6904
            }
6905
            $csvContent[] = null;
6906
6907
            $courseToolInformation .= Display::page_subheader2(
6908
                get_lang('Assignments')
6909
            );
6910
            $courseToolInformation .= Display::return_sortable_table(
6911
                $headerTool,
6912
                $courseWorkInformationArray
6913
            );
6914
        }
6915
6916
        $courseToolInformationTotal = null;
6917
        if (!empty($courseToolInformation)) {
6918
            $sessionTitle = null;
6919
            if (!empty($sessionId)) {
6920
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6921
            }
6922
6923
            $courseToolInformationTotal .= Display::page_subheader(
6924
                $courseInfo['title'].$sessionTitle
6925
            );
6926
            $courseToolInformationTotal .= $courseToolInformation;
6927
        }
6928
6929
        return [
6930
            'array' => $csvContent,
6931
            'html' => $courseToolInformationTotal,
6932
        ];
6933
    }
6934
6935
    /**
6936
     * @param int $sessionId
6937
     *
6938
     * @return bool
6939
     */
6940
    public static function isAllowToTrack($sessionId)
6941
    {
6942
        return
6943
            api_is_platform_admin(true, true) ||
6944
            SessionManager::user_is_general_coach(api_get_user_id(), $sessionId) ||
6945
            api_is_allowed_to_create_course() ||
6946
            api_is_course_tutor() ||
6947
            api_is_course_admin();
6948
    }
6949
6950
    public static function getCourseLpProgress($userId, $sessionId)
6951
    {
6952
        $controller = new IndexManager(get_lang('MyCourses'));
6953
        $data = $controller->returnCoursesAndSessions($userId);
6954
        $courseList = $data['courses'];
6955
        $result = [];
6956
        if ($courseList) {
6957
            //$counter = 1;
6958
            foreach ($courseList as $course) {
6959
                $courseId = $course['course_id'];
6960
                $courseInfo = api_get_course_info_by_id($courseId);
6961
                if (empty($courseInfo)) {
6962
                    continue;
6963
                }
6964
                $courseCode = $courseInfo['code'];
6965
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
6966
6967
                // total progress
6968
                $list = new LearnpathList(
6969
                    $userId,
6970
                     $courseInfo,
6971
                    0,
6972
                    'lp.publicatedOn ASC',
6973
                    true,
6974
                    null,
6975
                    true
6976
                );
6977
6978
                $list = $list->get_flat_list();
6979
                $totalProgress = 0;
6980
                $totalTime = 0;
6981
                if (!empty($list)) {
6982
                    foreach ($list as $lp_id => $learnpath) {
6983
                        if (!$learnpath['lp_visibility']) {
6984
                            continue;
6985
                        }
6986
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
6987
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
6988
                        if (100 == $lpProgress) {
6989
                            if (!empty($time)) {
6990
                                $timeInMinutes = $time / 60;
6991
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
6992
                                if ($timeInMinutes >= $min) {
6993
                                    $totalProgress++;
6994
                                }
6995
                            }
6996
                        }
6997
                        $totalTime += $time;
6998
                    }
6999
7000
                    if (!empty($totalProgress)) {
7001
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
7002
                    }
7003
                }
7004
7005
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
7006
7007
                $result[] = [
7008
                    'module' => $courseInfo['name'],
7009
                    'progress' => $progress,
7010
                    'qualification' => $totalProgress,
7011
                    'activeTime' => $totalTime,
7012
                ];
7013
            }
7014
        }
7015
7016
        return $result;
7017
    }
7018
7019
    /**
7020
     * @param int $userId
7021
     * @param int $courseId
7022
     * @param int $sessionId
7023
     *
7024
     * @return int
7025
     */
7026
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
7027
    {
7028
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7029
        $sessionCondition = api_get_session_condition($sessionId);
7030
        $courseId = (int) $courseId;
7031
        $userId = (int) $userId;
7032
7033
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
7034
            FROM $tblTrackCourseAccess
7035
            WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
7036
7037
        $result = Database::fetch_assoc(Database::query($sql));
7038
7039
        return (int) $result['c'];
7040
    }
7041
}
7042
7043
/**
7044
 * @todo move into a proper file
7045
 */
7046
class TrackingCourseLog
7047
{
7048
    /**
7049
     * @return mixed
7050
     */
7051
    public static function count_item_resources()
7052
    {
7053
        $session_id = api_get_session_id();
7054
        $course_id = api_get_course_int_id();
7055
7056
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
7057
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7058
7059
        $sql = "SELECT count(tool) AS total_number_of_items
7060
                FROM $table_item_property track_resource, $table_user user
7061
                WHERE
7062
                    track_resource.c_id = $course_id AND
7063
                    track_resource.insert_user_id = user.user_id AND
7064
                    session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7065
7066
        if (isset($_GET['keyword'])) {
7067
            $keyword = Database::escape_string(trim($_GET['keyword']));
7068
            $sql .= " AND (
7069
                        user.username LIKE '%".$keyword."%' OR
7070
                        lastedit_type LIKE '%".$keyword."%' OR
7071
                        tool LIKE '%".$keyword."%'
7072
                    )";
7073
        }
7074
7075
        $sql .= " AND tool IN (
7076
                    'document',
7077
                    'learnpath',
7078
                    'quiz',
7079
                    'glossary',
7080
                    'link',
7081
                    'course_description',
7082
                    'announcement',
7083
                    'thematic',
7084
                    'thematic_advance',
7085
                    'thematic_plan'
7086
                )";
7087
        $res = Database::query($sql);
7088
        $obj = Database::fetch_object($res);
7089
7090
        return $obj->total_number_of_items;
7091
    }
7092
7093
    /**
7094
     * @param $from
7095
     * @param $number_of_items
7096
     * @param $column
7097
     * @param $direction
7098
     *
7099
     * @return array
7100
     */
7101
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
7102
    {
7103
        $session_id = api_get_session_id();
7104
        $course_id = api_get_course_int_id();
7105
7106
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
7107
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7108
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
7109
        $session_id = intval($session_id);
7110
7111
        $sql = "SELECT
7112
                    tool as col0,
7113
                    lastedit_type as col1,
7114
                    ref as ref,
7115
                    user.username as col3,
7116
                    insert_date as col6,
7117
                    visibility as col7,
7118
                    user.user_id as user_id
7119
                FROM $table_item_property track_resource, $table_user user
7120
                WHERE
7121
                  track_resource.c_id = $course_id AND
7122
                  track_resource.insert_user_id = user.user_id AND
7123
                  session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7124
7125
        if (isset($_GET['keyword'])) {
7126
            $keyword = Database::escape_string(trim($_GET['keyword']));
7127
            $sql .= " AND (
7128
                        user.username LIKE '%".$keyword."%' OR
7129
                        lastedit_type LIKE '%".$keyword."%' OR
7130
                        tool LIKE '%".$keyword."%'
7131
                     ) ";
7132
        }
7133
7134
        $sql .= " AND tool IN (
7135
                    'document',
7136
                    'learnpath',
7137
                    'quiz',
7138
                    'glossary',
7139
                    'link',
7140
                    'course_description',
7141
                    'announcement',
7142
                    'thematic',
7143
                    'thematic_advance',
7144
                    'thematic_plan'
7145
                )";
7146
7147
        if (0 == $column) {
7148
            $column = '0';
7149
        }
7150
        if ('' != $column && '' != $direction) {
7151
            if (2 != $column && 4 != $column) {
7152
                $sql .= " ORDER BY col$column $direction";
7153
            }
7154
        } else {
7155
            $sql .= " ORDER BY col6 DESC ";
7156
        }
7157
7158
        $from = intval($from);
7159
        if ($from) {
7160
            $number_of_items = intval($number_of_items);
7161
            $sql .= " LIMIT $from, $number_of_items ";
7162
        }
7163
7164
        $res = Database::query($sql);
7165
        $resources = [];
7166
        $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan'];
7167
        while ($row = Database::fetch_array($res)) {
7168
            $ref = $row['ref'];
7169
            $table_name = self::get_tool_name_table($row['col0']);
7170
            $table_tool = Database::get_course_table($table_name['table_name']);
7171
7172
            $id = $table_name['id_tool'];
7173
            $recorset = false;
7174
7175
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
7176
                $tbl_thematic = Database::get_course_table(TABLE_THEMATIC);
7177
                $sql = "SELECT thematic_id FROM $table_tool
7178
                        WHERE c_id = $course_id AND id = $ref";
7179
                $rs_thematic = Database::query($sql);
7180
                if (Database::num_rows($rs_thematic)) {
7181
                    $row_thematic = Database::fetch_array($rs_thematic);
7182
                    $thematic_id = $row_thematic['thematic_id'];
7183
7184
                    $sql = "SELECT session.id, session.name, user.username
7185
                            FROM $tbl_thematic t, $table_session session, $table_user user
7186
                            WHERE
7187
                              t.c_id = $course_id AND
7188
                              t.session_id = session.id AND
7189
                              session.id_coach = user.user_id AND
7190
                              t.id = $thematic_id";
7191
                    $recorset = Database::query($sql);
7192
                }
7193
            } else {
7194
                $sql = "SELECT session.id, session.name, user.username
7195
                          FROM $table_tool tool, $table_session session, $table_user user
7196
                          WHERE
7197
                              tool.c_id = $course_id AND
7198
                              tool.session_id = session.id AND
7199
                              session.id_coach = user.user_id AND
7200
                              tool.$id = $ref";
7201
                $recorset = Database::query($sql);
7202
            }
7203
7204
            if (!empty($recorset)) {
7205
                $obj = Database::fetch_object($recorset);
7206
7207
                $name_session = '';
7208
                $coach_name = '';
7209
                if (!empty($obj)) {
7210
                    $name_session = $obj->name;
7211
                    $coach_name = $obj->username;
7212
                }
7213
7214
                $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
7215
                $row[0] = '';
7216
                if (2 != $row['col6']) {
7217
                    if (in_array($row['col0'], $thematic_tools)) {
7218
                        $exp_thematic_tool = explode('_', $row['col0']);
7219
                        $thematic_tool_title = '';
7220
                        if (is_array($exp_thematic_tool)) {
7221
                            foreach ($exp_thematic_tool as $exp) {
7222
                                $thematic_tool_title .= api_ucfirst($exp);
7223
                            }
7224
                        } else {
7225
                            $thematic_tool_title = api_ucfirst($row['col0']);
7226
                        }
7227
7228
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
7229
                    } else {
7230
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
7231
                    }
7232
                } else {
7233
                    $row[0] = api_ucfirst($row['col0']);
7234
                }
7235
                $row[1] = get_lang($row[1]);
7236
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
7237
                $row[5] = '';
7238
                //@todo Improve this code please
7239
                switch ($table_name['table_name']) {
7240
                    case 'document':
7241
                        $sql = "SELECT tool.title as title FROM $table_tool tool
7242
                                WHERE c_id = $course_id AND id = $ref";
7243
                        $rs_document = Database::query($sql);
7244
                        $obj_document = Database::fetch_object($rs_document);
7245
                        if ($obj_document) {
7246
                            $row[5] = $obj_document->title;
7247
                        }
7248
                        break;
7249
                    case 'announcement':
7250
                        $sql = "SELECT title FROM $table_tool
7251
                                WHERE c_id = $course_id AND id = $ref";
7252
                        $rs_document = Database::query($sql);
7253
                        $obj_document = Database::fetch_object($rs_document);
7254
                        if ($obj_document) {
7255
                            $row[5] = $obj_document->title;
7256
                        }
7257
                        break;
7258
                    case 'glossary':
7259
                        $sql = "SELECT name FROM $table_tool
7260
                                WHERE c_id = $course_id AND glossary_id = $ref";
7261
                        $rs_document = Database::query($sql);
7262
                        $obj_document = Database::fetch_object($rs_document);
7263
                        if ($obj_document) {
7264
                            $row[5] = $obj_document->name;
7265
                        }
7266
                        break;
7267
                    case 'lp':
7268
                        $sql = "SELECT name
7269
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
7270
                        $rs_document = Database::query($sql);
7271
                        $obj_document = Database::fetch_object($rs_document);
7272
                        $row[5] = $obj_document->name;
7273
                        break;
7274
                    case 'quiz':
7275
                        $sql = "SELECT title FROM $table_tool
7276
                                WHERE c_id = $course_id AND id = $ref";
7277
                        $rs_document = Database::query($sql);
7278
                        $obj_document = Database::fetch_object($rs_document);
7279
                        if ($obj_document) {
7280
                            $row[5] = $obj_document->title;
7281
                        }
7282
                        break;
7283
                    case 'course_description':
7284
                        $sql = "SELECT title FROM $table_tool
7285
                                WHERE c_id = $course_id AND id = $ref";
7286
                        $rs_document = Database::query($sql);
7287
                        $obj_document = Database::fetch_object($rs_document);
7288
                        if ($obj_document) {
7289
                            $row[5] = $obj_document->title;
7290
                        }
7291
                        break;
7292
                    case 'thematic':
7293
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7294
                        if (Database::num_rows($rs) > 0) {
7295
                            $obj = Database::fetch_object($rs);
7296
                            if ($obj) {
7297
                                $row[5] = $obj->title;
7298
                            }
7299
                        }
7300
                        break;
7301
                    case 'thematic_advance':
7302
                        $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7303
                        if (Database::num_rows($rs) > 0) {
7304
                            $obj = Database::fetch_object($rs);
7305
                            if ($obj) {
7306
                                $row[5] = $obj->content;
7307
                            }
7308
                        }
7309
                        break;
7310
                    case 'thematic_plan':
7311
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7312
                        if (Database::num_rows($rs) > 0) {
7313
                            $obj = Database::fetch_object($rs);
7314
                            if ($obj) {
7315
                                $row[5] = $obj->title;
7316
                            }
7317
                        }
7318
                        break;
7319
                    default:
7320
                        break;
7321
                }
7322
7323
                $row2 = $name_session;
7324
                if (!empty($coach_name)) {
7325
                    $row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
7326
                }
7327
                $row[2] = $row2;
7328
                if (!empty($row['col3'])) {
7329
                    $userInfo = api_get_user_info($row['user_id']);
7330
                    $row['col3'] = Display::url(
7331
                        $row['col3'],
7332
                        $userInfo['profile_url']
7333
                    );
7334
                    $row[3] = $row['col3'];
7335
7336
                    $ip = Tracking::get_ip_from_user_event(
7337
                        $row['user_id'],
7338
                        $row['col6'],
7339
                        true
7340
                    );
7341
                    if (empty($ip)) {
7342
                        $ip = get_lang('Unknown');
7343
                    }
7344
                    $row[4] = $ip;
7345
                }
7346
7347
                $resources[] = $row;
7348
            }
7349
        }
7350
7351
        return $resources;
7352
    }
7353
7354
    /**
7355
     * @param string $tool
7356
     *
7357
     * @return array
7358
     */
7359
    public static function get_tool_name_table($tool)
7360
    {
7361
        switch ($tool) {
7362
            case 'document':
7363
                $table_name = TABLE_DOCUMENT;
7364
                $link_tool = 'document/document.php';
7365
                $id_tool = 'id';
7366
                break;
7367
            case 'learnpath':
7368
                $table_name = TABLE_LP_MAIN;
7369
                $link_tool = 'lp/lp_controller.php';
7370
                $id_tool = 'id';
7371
                break;
7372
            case 'quiz':
7373
                $table_name = TABLE_QUIZ_TEST;
7374
                $link_tool = 'exercise/exercise.php';
7375
                $id_tool = 'id';
7376
                break;
7377
            case 'glossary':
7378
                $table_name = TABLE_GLOSSARY;
7379
                $link_tool = 'glossary/index.php';
7380
                $id_tool = 'glossary_id';
7381
                break;
7382
            case 'link':
7383
                $table_name = TABLE_LINK;
7384
                $link_tool = 'link/link.php';
7385
                $id_tool = 'id';
7386
                break;
7387
            case 'course_description':
7388
                $table_name = TABLE_COURSE_DESCRIPTION;
7389
                $link_tool = 'course_description/';
7390
                $id_tool = 'id';
7391
                break;
7392
            case 'announcement':
7393
                $table_name = TABLE_ANNOUNCEMENT;
7394
                $link_tool = 'announcements/announcements.php';
7395
                $id_tool = 'id';
7396
                break;
7397
            case 'thematic':
7398
                $table_name = TABLE_THEMATIC;
7399
                $link_tool = 'course_progress/index.php';
7400
                $id_tool = 'id';
7401
                break;
7402
            case 'thematic_advance':
7403
                $table_name = TABLE_THEMATIC_ADVANCE;
7404
                $link_tool = 'course_progress/index.php';
7405
                $id_tool = 'id';
7406
                break;
7407
            case 'thematic_plan':
7408
                $table_name = TABLE_THEMATIC_PLAN;
7409
                $link_tool = 'course_progress/index.php';
7410
                $id_tool = 'id';
7411
                break;
7412
            default:
7413
                $table_name = $tool;
7414
            break;
7415
        }
7416
7417
        return [
7418
            'table_name' => $table_name,
7419
            'link_tool' => $link_tool,
7420
            'id_tool' => $id_tool,
7421
        ];
7422
    }
7423
7424
    /**
7425
     * @return string
7426
     */
7427
    public static function display_additional_profile_fields()
7428
    {
7429
        // getting all the extra profile fields that are defined by the platform administrator
7430
        $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
7431
7432
        // creating the form
7433
        $return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
7434
7435
        // the select field with the additional user profile fields (= this is where we select the field of which we want to see
7436
        // the information the users have entered or selected.
7437
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
7438
        $return .= '<option value="-">'.get_lang('Select user profile field to add').'</option>';
7439
        $extra_fields_to_show = 0;
7440
        foreach ($extra_fields as $key => $field) {
7441
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
7442
            if (1 == $field[6] && 1 == $field[8]) {
7443
                if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field']) {
7444
                    $selected = 'selected="selected"';
7445
                } else {
7446
                    $selected = '';
7447
                }
7448
                $extra_fields_to_show++;
7449
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
7450
            }
7451
        }
7452
        $return .= '</select>';
7453
7454
        // the form elements for the $_GET parameters (because the form is passed through GET
7455
        foreach ($_GET as $key => $value) {
7456
            if ('additional_profile_field' != $key) {
7457
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
7458
            }
7459
        }
7460
        // the submit button
7461
        $return .= '<button class="save" type="submit">'.get_lang('Add user profile field').'</button>';
7462
        $return .= '</form>';
7463
        if ($extra_fields_to_show > 0) {
7464
            return $return;
7465
        } else {
7466
            return '';
7467
        }
7468
    }
7469
7470
    /**
7471
     * This function gets all the information of a certrain ($field_id)
7472
     * additional profile field for a specific list of users is more efficent
7473
     * than get_addtional_profile_information_of_field() function
7474
     * It gets the information of all the users so that it can be displayed
7475
     * in the sortable table or in the csv or xls export.
7476
     *
7477
     * @author    Julio Montoya <[email protected]>
7478
     *
7479
     * @param    int field id
7480
     * @param    array list of user ids
7481
     *
7482
     * @return array
7483
     *
7484
     * @since    Nov 2009
7485
     *
7486
     * @version    1.8.6.2
7487
     */
7488
    public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users)
7489
    {
7490
        // Database table definition
7491
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7492
        $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
7493
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
7494
        $result_extra_field = UserManager::get_extra_field_information($field_id);
7495
        $return = [];
7496
        if (!empty($users)) {
7497
            if (UserManager::USER_FIELD_TYPE_TAG == $result_extra_field['field_type']) {
7498
                foreach ($users as $user_id) {
7499
                    $user_result = UserManager::get_user_tags($user_id, $field_id);
7500
                    $tag_list = [];
7501
                    foreach ($user_result as $item) {
7502
                        $tag_list[] = $item['tag'];
7503
                    }
7504
                    $return[$user_id][] = implode(', ', $tag_list);
7505
                }
7506
            } else {
7507
                $new_user_array = [];
7508
                foreach ($users as $user_id) {
7509
                    $new_user_array[] = "'".$user_id."'";
7510
                }
7511
                $users = implode(',', $new_user_array);
7512
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
7513
                // Selecting only the necessary information NOT ALL the user list
7514
                $sql = "SELECT user.user_id, v.value
7515
                        FROM $table_user user
7516
                        INNER JOIN $table_user_field_values v
7517
                        ON (user.user_id = v.item_id)
7518
                        INNER JOIN $extraField f
7519
                        ON (f.id = v.field_id)
7520
                        WHERE
7521
                            f.extra_field_type = $extraFieldType AND
7522
                            v.field_id=".intval($field_id)." AND
7523
                            user.user_id IN ($users)";
7524
7525
                $result = Database::query($sql);
7526
                while ($row = Database::fetch_array($result)) {
7527
                    // get option value for field type double select by id
7528
                    if (!empty($row['value'])) {
7529
                        if (ExtraField::FIELD_TYPE_DOUBLE_SELECT ==
7530
                            $result_extra_field['field_type']
7531
                        ) {
7532
                            $id_double_select = explode(';', $row['value']);
7533
                            if (is_array($id_double_select)) {
7534
                                $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
7535
                                $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
7536
                                $row['value'] = ($value1.';'.$value2);
7537
                            }
7538
                        }
7539
7540
                        if (ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD == $result_extra_field['field_type']) {
7541
                            $parsedValue = explode('::', $row['value']);
7542
7543
                            if ($parsedValue) {
7544
                                $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text'];
7545
                                $value2 = $parsedValue[1];
7546
7547
                                $row['value'] = "$value1: $value2";
7548
                            }
7549
                        }
7550
7551
                        if (ExtraField::FIELD_TYPE_TRIPLE_SELECT == $result_extra_field['field_type']) {
7552
                            list($level1, $level2, $level3) = explode(';', $row['value']);
7553
7554
                            $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / ';
7555
                            $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / ';
7556
                            $row['value'] .= $result_extra_field['options'][$level3]['display_text'];
7557
                        }
7558
                    }
7559
                    // get other value from extra field
7560
                    $return[$row['user_id']][] = $row['value'];
7561
                }
7562
            }
7563
        }
7564
7565
        return $return;
7566
    }
7567
7568
    /**
7569
     * count the number of students in this course (used for SortableTable)
7570
     * Deprecated.
7571
     */
7572
    public function count_student_in_course()
7573
    {
7574
        global $nbStudents;
7575
7576
        return $nbStudents;
7577
    }
7578
7579
    public function sort_users($a, $b)
7580
    {
7581
        $tracking = Session::read('tracking_column');
7582
7583
        return strcmp(
7584
            trim(api_strtolower($a[$tracking])),
7585
            trim(api_strtolower($b[$tracking]))
7586
        );
7587
    }
7588
7589
    public function sort_users_desc($a, $b)
7590
    {
7591
        $tracking = Session::read('tracking_column');
7592
7593
        return strcmp(
7594
            trim(api_strtolower($b[$tracking])),
7595
            trim(api_strtolower($a[$tracking]))
7596
        );
7597
    }
7598
7599
    /**
7600
     * Get number of users for sortable with pagination.
7601
     *
7602
     * @return int
7603
     */
7604
    public static function get_number_of_users($conditions)
7605
    {
7606
        $conditions['get_count'] = true;
7607
7608
        return self::get_user_data(null, null, null, null, $conditions);
7609
    }
7610
7611
    /**
7612
     * Get data for users list in sortable with pagination.
7613
     *
7614
     * @param int $from
7615
     * @param int $number_of_items
7616
     * @param $column
7617
     * @param $direction
7618
     * @param $conditions
7619
     *
7620
     * @return array
7621
     */
7622
    public static function get_user_data(
7623
        $from,
7624
        $number_of_items,
7625
        $column,
7626
        $direction,
7627
        $conditions = []
7628
    ) {
7629
        global $user_ids, $course_code, $export_csv, $session_id;
7630
        $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users
7631
        $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false;
7632
7633
        $csv_content = [];
7634
        $course_code = Database::escape_string($course_code);
7635
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
7636
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
7637
        $access_url_id = api_get_current_access_url_id();
7638
7639
        // get all users data from a course for sortable with limit
7640
        if (is_array($user_ids)) {
7641
            $user_ids = array_map('intval', $user_ids);
7642
            $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") ";
7643
        } else {
7644
            $user_ids = (int) $user_ids;
7645
            $condition_user = " WHERE user.id = $user_ids ";
7646
        }
7647
7648
        if (!empty($_GET['user_keyword'])) {
7649
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
7650
            $condition_user .= " AND (
7651
                user.firstname LIKE '%".$keyword."%' OR
7652
                user.lastname LIKE '%".$keyword."%'  OR
7653
                user.username LIKE '%".$keyword."%'  OR
7654
                user.email LIKE '%".$keyword."%'
7655
             ) ";
7656
        }
7657
7658
        $url_table = '';
7659
        $url_condition = '';
7660
        if (api_is_multiple_url_enabled()) {
7661
            $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)";
7662
            $url_condition = " AND access_url_id = '$access_url_id'";
7663
        }
7664
7665
        $invitedUsersCondition = '';
7666
        if (!$includeInvitedUsers) {
7667
            $invitedUsersCondition = " AND user.status != ".INVITEE;
7668
        }
7669
7670
        $select = '
7671
                SELECT user.id as user_id,
7672
                    user.official_code  as col0,
7673
                    user.lastname       as col1,
7674
                    user.firstname      as col2,
7675
                    user.username       as col3,
7676
                    user.email          as col4';
7677
        if ($getCount) {
7678
            $select = ' SELECT COUNT(distinct(user.id)) as count ';
7679
        }
7680
7681
        $sqlInjectJoins = '';
7682
        $where = 'AND 1 = 1 ';
7683
        $sqlInjectWhere = '';
7684
        if (!empty($conditions)) {
7685
            if (isset($conditions['inject_joins'])) {
7686
                $sqlInjectJoins = $conditions['inject_joins'];
7687
            }
7688
            if (isset($conditions['where'])) {
7689
                $where = $conditions['where'];
7690
            }
7691
            if (isset($conditions['inject_where'])) {
7692
                $sqlInjectWhere = $conditions['inject_where'];
7693
            }
7694
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
7695
            $injectExtraFields = rtrim($injectExtraFields, ', ');
7696
            if (false === $getCount) {
7697
                $select .= " , $injectExtraFields";
7698
            }
7699
        }
7700
7701
        $sql = "$select
7702
                FROM $tbl_user as user
7703
                $url_table
7704
                $sqlInjectJoins
7705
                $condition_user
7706
                $url_condition
7707
                $invitedUsersCondition
7708
                $where
7709
                $sqlInjectWhere
7710
                ";
7711
7712
        if (!in_array($direction, ['ASC', 'DESC'])) {
7713
            $direction = 'ASC';
7714
        }
7715
7716
        $column = (int) $column;
7717
        $from = (int) $from;
7718
        $number_of_items = (int) $number_of_items;
7719
7720
        if ($getCount) {
7721
            $res = Database::query($sql);
7722
            $row = Database::fetch_array($res);
7723
7724
            return $row['count'];
7725
        }
7726
7727
        $sql .= " ORDER BY col$column $direction ";
7728
        $sql .= " LIMIT $from, $number_of_items";
7729
7730
        $res = Database::query($sql);
7731
        $users = [];
7732
7733
        $courseInfo = api_get_course_info($course_code);
7734
        $courseId = $courseInfo['real_id'];
7735
        $courseCode = $courseInfo['code'];
7736
7737
        $total_surveys = 0;
7738
        $total_exercises = ExerciseLib::get_all_exercises(
7739
            $courseInfo,
7740
            $session_id,
7741
            false,
7742
            null,
7743
            false,
7744
            3
7745
        );
7746
7747
        if (empty($session_id)) {
7748
            $survey_user_list = [];
7749
            $surveyList = SurveyManager::get_surveys($course_code, $session_id);
7750
            if ($surveyList) {
7751
                $total_surveys = count($surveyList);
7752
                foreach ($surveyList as $survey) {
7753
                    $user_list = SurveyManager::get_people_who_filled_survey(
7754
                    $survey['survey_id'],
7755
                    false,
7756
                        $courseId
7757
                );
7758
7759
                    foreach ($user_list as $user_id) {
7760
                        isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
7761
                    }
7762
                }
7763
            }
7764
        }
7765
7766
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode.
7767
            '&course='.$course_code.'&origin=tracking_course&id_session='.$session_id;
7768
7769
        $sortByFirstName = api_sort_by_first_name();
7770
        Session::write('user_id_list', []);
7771
        $userIdList = [];
7772
        while ($user = Database::fetch_array($res, 'ASSOC')) {
7773
            $userIdList[] = $user['user_id'];
7774
            $user['official_code'] = $user['col0'];
7775
            $user['username'] = $user['col3'];
7776
            $user['time'] = api_time_to_hms(
7777
                Tracking::get_time_spent_on_the_course(
7778
                    $user['user_id'],
7779
                    $courseId,
7780
                    $session_id
7781
                )
7782
            );
7783
7784
            $avg_student_score = Tracking::get_avg_student_score(
7785
                $user['user_id'],
7786
                $course_code,
7787
                [],
7788
                $session_id
7789
            );
7790
7791
            $avg_student_progress = Tracking::get_avg_student_progress(
7792
                $user['user_id'],
7793
                $course_code,
7794
                [],
7795
                $session_id
7796
            );
7797
7798
            if (empty($avg_student_progress)) {
7799
                $avg_student_progress = 0;
7800
            }
7801
            $user['average_progress'] = $avg_student_progress.'%';
7802
7803
            $total_user_exercise = Tracking::get_exercise_student_progress(
7804
                $total_exercises,
7805
                $user['user_id'],
7806
                $courseId,
7807
                $session_id
7808
            );
7809
7810
            $user['exercise_progress'] = $total_user_exercise;
7811
7812
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
7813
                $total_exercises,
7814
                $user['user_id'],
7815
                $courseId,
7816
                $session_id
7817
            );
7818
7819
            $user['exercise_average_best_attempt'] = $total_user_exercise;
7820
7821
            if (is_numeric($avg_student_score)) {
7822
                $user['student_score'] = $avg_student_score.'%';
7823
            } else {
7824
                $user['student_score'] = $avg_student_score;
7825
            }
7826
7827
            $user['count_assignments'] = Tracking::count_student_assignments(
7828
                $user['user_id'],
7829
                $course_code,
7830
                $session_id
7831
            );
7832
            $user['count_messages'] = Tracking::count_student_messages(
7833
                $user['user_id'],
7834
                $course_code,
7835
                $session_id
7836
            );
7837
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
7838
                $user['user_id'],
7839
                $courseId,
7840
                $session_id,
7841
                false === $export_csv
7842
            );
7843
7844
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
7845
                $user['user_id'],
7846
                $courseInfo,
7847
                $session_id,
7848
                false === $export_csv
7849
            );
7850
7851
            if ($export_csv) {
7852
                if (!empty($user['first_connection'])) {
7853
                    $user['first_connection'] = api_get_local_time($user['first_connection']);
7854
                } else {
7855
                    $user['first_connection'] = '-';
7856
                }
7857
                if (!empty($user['last_connection'])) {
7858
                    $user['last_connection'] = api_get_local_time($user['last_connection']);
7859
                } else {
7860
                    $user['last_connection'] = '-';
7861
                }
7862
            }
7863
7864
            if (empty($session_id)) {
7865
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys;
7866
            }
7867
7868
            $url = $urlBase.'&student='.$user['user_id'];
7869
7870
            $user['link'] = '<center><a href="'.$url.'">
7871
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
7872
                             </a></center>';
7873
7874
            // store columns in array $users
7875
            $user_row = [];
7876
            $user_row['official_code'] = $user['official_code']; //0
7877
            if ($sortByFirstName) {
7878
                $user_row['firstname'] = $user['col2'];
7879
                $user_row['lastname'] = $user['col1'];
7880
            } else {
7881
                $user_row['lastname'] = $user['col1'];
7882
                $user_row['firstname'] = $user['col2'];
7883
            }
7884
            $user_row['username'] = $user['username'];
7885
            $user_row['time'] = $user['time'];
7886
            $user_row['average_progress'] = $user['average_progress'];
7887
            $user_row['exercise_progress'] = $user['exercise_progress'];
7888
            $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
7889
            $user_row['student_score'] = $user['student_score'];
7890
            $user_row['count_assignments'] = $user['count_assignments'];
7891
            $user_row['count_messages'] = $user['count_messages'];
7892
7893
            $userGroupManager = new UserGroup();
7894
            $user_row['classes'] = $userGroupManager->getLabelsFromNameList($user['user_id'], UserGroup::NORMAL_CLASS);
7895
7896
            if (empty($session_id)) {
7897
                $user_row['survey'] = $user['survey'];
7898
            } else {
7899
                $userSession = SessionManager::getUserSession($user['user_id'], $session_id);
7900
                $user_row['registered_at'] = '';
7901
                if ($userSession) {
7902
                    $user_row['registered_at'] = api_get_local_time($userSession['registered_at']);
7903
                }
7904
            }
7905
7906
            $user_row['first_connection'] = $user['first_connection'];
7907
            $user_row['last_connection'] = $user['last_connection'];
7908
7909
            // we need to display an additional profile field
7910
            if (isset($_GET['additional_profile_field'])) {
7911
                $data = Session::read('additional_user_profile_info');
7912
7913
                $extraFieldInfo = Session::read('extra_field_info');
7914
                foreach ($_GET['additional_profile_field'] as $fieldId) {
7915
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
7916
                        if (is_array($data[$fieldId][$user['user_id']])) {
7917
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = implode(
7918
                                ', ',
7919
                                $data[$fieldId][$user['user_id']]
7920
                            );
7921
                        } else {
7922
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
7923
                        }
7924
                    } else {
7925
                        $user_row[$extraFieldInfo[$fieldId]['variable']] = '';
7926
                    }
7927
                }
7928
            }
7929
7930
            if ('true' === api_get_setting('show_email_addresses')) {
7931
                $user_row['email'] = $user['col4'];
7932
            }
7933
7934
            $user_row['link'] = $user['link'];
7935
7936
            if ($export_csv) {
7937
                if (empty($session_id)) {
7938
                    unset($user_row['classes']);
7939
                    unset($user_row['link']);
7940
                } else {
7941
                    unset($user_row['classes']);
7942
                    unset($user_row['link']);
7943
                }
7944
7945
                $csv_content[] = $user_row;
7946
            }
7947
            $users[] = array_values($user_row);
7948
        }
7949
7950
        if ($export_csv) {
7951
            Session::write('csv_content', $csv_content);
7952
        }
7953
7954
        Session::erase('additional_user_profile_info');
7955
        Session::erase('extra_field_info');
7956
        Session::write('user_id_list', $userIdList);
7957
7958
        return $users;
7959
    }
7960
7961
    /**
7962
     * Get data for users list in sortable with pagination.
7963
     *
7964
     * @param $from
7965
     * @param $number_of_items
7966
     * @param $column
7967
     * @param $direction
7968
     * @param $includeInvitedUsers boolean Whether include the invited users
7969
     *
7970
     * @return array
7971
     */
7972
    public static function getTotalTimeReport(
7973
        $from,
7974
        $number_of_items,
7975
        $column,
7976
        $direction,
7977
        $includeInvitedUsers = false
7978
    ) {
7979
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
7980
7981
        $course_code = Database::escape_string($course_code);
7982
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
7983
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
7984
        $access_url_id = api_get_current_access_url_id();
7985
7986
        // get all users data from a course for sortable with limit
7987
        if (is_array($user_ids)) {
7988
            $user_ids = array_map('intval', $user_ids);
7989
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
7990
        } else {
7991
            $user_ids = intval($user_ids);
7992
            $condition_user = " WHERE user.user_id = $user_ids ";
7993
        }
7994
7995
        $url_table = null;
7996
        $url_condition = null;
7997
        if (api_is_multiple_url_enabled()) {
7998
            $url_table = ", ".$tbl_url_rel_user." as url_users";
7999
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
8000
        }
8001
8002
        $invitedUsersCondition = '';
8003
        if (!$includeInvitedUsers) {
8004
            $invitedUsersCondition = " AND user.status != ".INVITEE;
8005
        }
8006
8007
        $sql = "SELECT  user.user_id as user_id,
8008
                    user.official_code  as col0,
8009
                    user.lastname       as col1,
8010
                    user.firstname      as col2,
8011
                    user.username       as col3
8012
                FROM $tbl_user as user $url_table
8013
                $condition_user $url_condition $invitedUsersCondition";
8014
8015
        if (!in_array($direction, ['ASC', 'DESC'])) {
8016
            $direction = 'ASC';
8017
        }
8018
8019
        $column = (int) $column;
8020
        $from = (int) $from;
8021
        $number_of_items = (int) $number_of_items;
8022
8023
        $sql .= " ORDER BY col$column $direction ";
8024
        $sql .= " LIMIT $from,$number_of_items";
8025
8026
        $res = Database::query($sql);
8027
        $users = [];
8028
8029
        $sortByFirstName = api_sort_by_first_name();
8030
        $courseInfo = api_get_course_info($course_code);
8031
        $courseId = $courseInfo['real_id'];
8032
8033
        while ($user = Database::fetch_array($res, 'ASSOC')) {
8034
            $user['official_code'] = $user['col0'];
8035
            $user['lastname'] = $user['col1'];
8036
            $user['firstname'] = $user['col2'];
8037
            $user['username'] = $user['col3'];
8038
8039
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
8040
                $user['user_id'],
8041
                $courseId,
8042
                $session_id
8043
            );
8044
8045
            $user['time'] = api_time_to_hms($totalCourseTime);
8046
            $totalLpTime = Tracking::get_time_spent_in_lp(
8047
                $user['user_id'],
8048
                $course_code,
8049
                [],
8050
                $session_id
8051
            );
8052
8053
            $user['total_lp_time'] = $totalLpTime;
8054
            $warning = '';
8055
            if ($totalLpTime > $totalCourseTime) {
8056
                $warning = '&nbsp;'.Display::label(get_lang('Time difference'), 'danger');
8057
            }
8058
8059
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
8060
8061
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
8062
                $user['user_id'],
8063
                $courseId,
8064
                $session_id
8065
            );
8066
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
8067
                $user['user_id'],
8068
                $courseInfo,
8069
                $session_id,
8070
                false === $export_csv
8071
            );
8072
8073
            $user['link'] = '<center>
8074
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
8075
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
8076
                             </a>
8077
                         </center>';
8078
8079
            // store columns in array $users
8080
            $user_row = [];
8081
            $user_row['official_code'] = $user['official_code']; //0
8082
            if ($sortByFirstName) {
8083
                $user_row['firstname'] = $user['firstname'];
8084
                $user_row['lastname'] = $user['lastname'];
8085
            } else {
8086
                $user_row['lastname'] = $user['lastname'];
8087
                $user_row['firstname'] = $user['firstname'];
8088
            }
8089
            $user_row['username'] = $user['username'];
8090
            $user_row['time'] = $user['time'];
8091
            $user_row['total_lp_time'] = $user['total_lp_time'];
8092
            $user_row['first_connection'] = $user['first_connection'];
8093
            $user_row['last_connection'] = $user['last_connection'];
8094
8095
            $user_row['link'] = $user['link'];
8096
            $users[] = array_values($user_row);
8097
        }
8098
8099
        return $users;
8100
    }
8101
8102
    /**
8103
     * @param string $current
8104
     */
8105
    public static function actionsLeft($current, $sessionId = 0)
8106
    {
8107
        $usersLink = Display::url(
8108
            Display::return_icon('user.png', get_lang('Report on learners'), [], ICON_SIZE_MEDIUM),
8109
            'courseLog.php?'.api_get_cidreq(true, false)
8110
        );
8111
8112
        $groupsLink = Display::url(
8113
            Display::return_icon('group.png', get_lang('Group reporting'), [], ICON_SIZE_MEDIUM),
8114
            'course_log_groups.php?'.api_get_cidreq()
8115
        );
8116
8117
        $resourcesLink = Display::url(
8118
            Display::return_icon('tools.png', get_lang('Report on resource'), [], ICON_SIZE_MEDIUM),
8119
            'course_log_resources.php?'.api_get_cidreq(true, false)
8120
        );
8121
8122
        $courseLink = Display::url(
8123
            Display::return_icon('course.png', get_lang('Course report'), [], ICON_SIZE_MEDIUM),
8124
            'course_log_tools.php?'.api_get_cidreq(true, false)
8125
        );
8126
8127
        $examLink = Display::url(
8128
            Display::return_icon('quiz.png', get_lang('Exam tracking'), [], ICON_SIZE_MEDIUM),
8129
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
8130
        );
8131
8132
        $eventsLink = Display::url(
8133
            Display::return_icon('security.png', get_lang('Audit report'), [], ICON_SIZE_MEDIUM),
8134
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
8135
        );
8136
8137
        $lpLink = Display::url(
8138
            Display::return_icon('scorms.png', get_lang('CourseLPsGenericStats'), [], ICON_SIZE_MEDIUM),
8139
            api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq()
8140
        );
8141
8142
        $attendanceLink = '';
8143
        if (!empty($sessionId)) {
8144
            $attendanceLink = Display::url(
8145
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8146
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
8147
            );
8148
        }
8149
8150
        switch ($current) {
8151
            case 'users':
8152
                $usersLink = Display::url(
8153
                        Display::return_icon(
8154
                        'user_na.png',
8155
                        get_lang('Report on learners'),
8156
                        [],
8157
                        ICON_SIZE_MEDIUM
8158
                    ),
8159
                    '#'
8160
                );
8161
                break;
8162
            case 'groups':
8163
                $groupsLink = Display::url(
8164
                    Display::return_icon('group_na.png', get_lang('Group reporting'), [], ICON_SIZE_MEDIUM),
8165
                    '#'
8166
                );
8167
                break;
8168
            case 'courses':
8169
                $courseLink = Display::url(
8170
                    Display::return_icon('course_na.png', get_lang('Course report'), [], ICON_SIZE_MEDIUM),
8171
                    '#'
8172
                );
8173
                break;
8174
            case 'resources':
8175
                $resourcesLink = Display::url(
8176
                    Display::return_icon(
8177
                    'tools_na.png',
8178
                    get_lang('Report on resource'),
8179
                    [],
8180
                    ICON_SIZE_MEDIUM
8181
                    ),
8182
                    '#'
8183
                );
8184
                break;
8185
            case 'exams':
8186
                $examLink = Display::url(
8187
                    Display::return_icon('quiz_na.png', get_lang('Exam tracking'), [], ICON_SIZE_MEDIUM),
8188
                    '#'
8189
                );
8190
                break;
8191
            case 'logs':
8192
                $eventsLink = Display::url(
8193
                    Display::return_icon('security_na.png', get_lang('Audit report'), [], ICON_SIZE_MEDIUM),
8194
                    '#'
8195
                );
8196
                break;
8197
            case 'attendance':
8198
                if (!empty($sessionId)) {
8199
                    $attendanceLink = Display::url(
8200
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8201
                        '#'
8202
                    );
8203
                }
8204
                break;
8205
            case 'lp':
8206
                $lpLink = Display::url(
8207
                    Display::return_icon('scorms_na.png', get_lang('CourseLPsGenericStats'), [], ICON_SIZE_MEDIUM),
8208
                    '#'
8209
                );
8210
                break;
8211
        }
8212
8213
        $items = [
8214
            $usersLink,
8215
            $groupsLink,
8216
            $courseLink,
8217
            $resourcesLink,
8218
            $examLink,
8219
            $eventsLink,
8220
            $lpLink,
8221
            $attendanceLink,
8222
        ];
8223
8224
        return implode('', $items).'&nbsp;';
8225
    }
8226
}
8227