Passed
Push — 1.11.x ( efc14f...a62b75 )
by
unknown
11:29
created

main/inc/lib/tracking.lib.php (2 issues)

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CourseBundle\Entity\CLpCategory;
9
use Chamilo\UserBundle\Entity\User;
10
use ChamiloSession as Session;
11
use CpChart\Cache as pCache;
12
use CpChart\Data as pData;
13
use CpChart\Image as pImage;
14
use ExtraField as ExtraFieldModel;
15
16
/**
17
 *  Class Tracking.
18
 *
19
 *  @author  Julio Montoya <[email protected]>
20
 */
21
class Tracking
22
{
23
    /**
24
     * Get group reporting.
25
     *
26
     * @param int    $course_id
27
     * @param int    $sessionId
28
     * @param int    $group_id
29
     * @param string $type
30
     * @param int    $start
31
     * @param int    $limit
32
     * @param int    $sidx
33
     * @param string $sord
34
     * @param array  $where_condition
35
     *
36
     * @return array|null
37
     */
38
    public static function get_group_reporting(
39
        $course_id,
40
        $sessionId = 0,
41
        $group_id = 0,
42
        $type = 'all',
43
        $start = 0,
44
        $limit = 1000,
45
        $sidx = 1,
46
        $sord = 'desc',
47
        $where_condition = []
48
    ) {
49
        $course_id = (int) $course_id;
50
        $sessionId = (int) $sessionId;
51
52
        if (empty($course_id)) {
53
            return null;
54
        }
55
        $courseInfo = api_get_course_info_by_id($course_id);
56
        if ('count' == $type) {
57
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
58
        }
59
60
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
61
        $parsedResult = [];
62
        if (!empty($groupList)) {
63
            foreach ($groupList as $group) {
64
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
65
                $time = 0;
66
                $avg_student_score = 0;
67
                $avg_student_progress = 0;
68
                $work = 0;
69
                $messages = 0;
70
71
                foreach ($users as $user_data) {
72
                    $time += self::get_time_spent_on_the_course(
73
                        $user_data['user_id'],
74
                        $courseInfo['real_id'],
75
                        $sessionId
76
                    );
77
                    $average = self::get_avg_student_score(
78
                        $user_data['user_id'],
79
                        $courseInfo['code'],
80
                        [],
81
                        $sessionId
82
                    );
83
                    if (is_numeric($average)) {
84
                        $avg_student_score += $average;
85
                    }
86
                    $avg_student_progress += self::get_avg_student_progress(
87
                        $user_data['user_id'],
88
                        $courseInfo['code'],
89
                        [],
90
                        $sessionId
91
                    );
92
                    $work += self::count_student_assignments(
93
                        $user_data['user_id'],
94
                        $courseInfo['code'],
95
                        $sessionId
96
                    );
97
                    $messages += self::count_student_messages(
98
                        $user_data['user_id'],
99
                        $courseInfo['code'],
100
                        $sessionId
101
                    );
102
                }
103
104
                $countUsers = count($users);
105
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
106
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
107
108
                $groupItem = [
109
                    'id' => $group['id'],
110
                    'name' => $group['name'],
111
                    'time' => api_time_to_hms($time),
112
                    'progress' => $averageProgress,
113
                    'score' => $averageScore,
114
                    'works' => $work,
115
                    'messages' => $messages,
116
                ];
117
                $parsedResult[] = $groupItem;
118
            }
119
        }
120
121
        return $parsedResult;
122
    }
123
124
    /**
125
     * @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
        $lp_id = (int) $lp_id;
162
        $lp_item_id = (int) $lp_item_id;
163
        $user_id = (int) $user_id;
164
        $session_id = (int) $session_id;
165
        $origin = Security::remove_XSS($origin);
166
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
167
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
168
        $course_id = $courseInfo['real_id'];
169
        $courseCode = $courseInfo['code'];
170
        $session_condition = api_get_session_condition($session_id);
171
172
        // Extend all button
173
        $output = '';
174
175
        $extra = '<script>
176
        $(function() {
177
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
178
            $( "#dialog-confirm" ).dialog({
179
                autoOpen: false,
180
                show: "blind",
181
                resizable: false,
182
                height:300,
183
                modal: true
184
            });
185
186
            $(".export").click(function() {
187
                var targetUrl = $(this).attr("href");
188
                $( "#dialog-confirm" ).dialog({
189
                    width:400,
190
                    height:300,
191
                    buttons: {
192
                        "'.addslashes(get_lang('Download')).'": function() {
193
                            var option = $("input[name=add_logo]:checked").val();
194
                            location.href = targetUrl+"&add_logo="+option;
195
                            $(this).dialog("close");
196
                        }
197
                    }
198
                });
199
                $("#dialog-confirm").dialog("open");
200
201
                return false;
202
            });
203
        });
204
        </script>';
205
206
        $extra .= '<div id="dialog-confirm" title="'.get_lang('ConfirmYourChoice').'">';
207
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
208
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
209
        $extra .= $form->returnForm();
210
        $extra .= '</div>';
211
212
        $output .= $extra;
213
214
        $url_suffix = '&lp_id='.$lp_id;
215
        if ('tracking' === $origin) {
216
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
217
        }
218
219
        $extend_all = 0;
220
        if (!empty($extendedAll)) {
221
            $extend_all_link = Display::url(
222
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
223
                api_get_self().'?action=stats'.$url_suffix
224
            );
225
            $extend_all = 1;
226
        } else {
227
            $extend_all_link = Display::url(
228
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
229
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
230
            );
231
        }
232
233
        if ($origin != 'tracking') {
234
            $output .= '<div class="section-status">';
235
            $output .= Display::page_header(get_lang('ScormMystatus'));
236
            $output .= '</div>';
237
        }
238
239
        $actionColumn = null;
240
        if ($type === 'classic') {
241
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
242
        }
243
244
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
245
        if ($hideTime) {
246
            $timeHeader = '';
247
        }
248
        $output .= '<div class="table-responsive">';
249
        $output .= '<table id="lp_tracking" class="table tracking">
250
            <thead>
251
            <tr class="table-header">
252
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
253
                <th colspan="4">
254
                    '.get_lang('ScormLessonTitle').'
255
                </th>
256
                <th colspan="2">
257
                    '.get_lang('ScormStatus').'
258
                </th>
259
                <th colspan="2">
260
                    '.get_lang('ScormScore').'
261
                </th>
262
                '.$timeHeader.'
263
                '.$actionColumn.'
264
                </tr>
265
            </thead>
266
            <tbody>
267
        ';
268
269
        // Going through the items using the $items[] array instead of the database order ensures
270
        // we get them in the same order as in the imsmanifest file, which is rather random when using
271
        // the database table.
272
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
273
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
274
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
275
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
276
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
277
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
278
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
279
280
        $sql = "SELECT max(view_count)
281
                FROM $TBL_LP_VIEW
282
                WHERE
283
                    c_id = $course_id AND
284
                    lp_id = $lp_id AND
285
                    user_id = $user_id
286
                    $session_condition";
287
        $res = Database::query($sql);
288
        $view = 0;
289
        if (Database::num_rows($res) > 0) {
290
            $myrow = Database::fetch_array($res);
291
            $view = (int) $myrow[0];
292
        }
293
294
        $counter = 0;
295
        $total_time = 0;
296
        $h = get_lang('h');
297
298
        if (!empty($export_csv)) {
299
            $csvHeaders = [
300
                get_lang('ScormLessonTitle'),
301
                get_lang('ScormStatus'),
302
                get_lang('ScormScore'),
303
            ];
304
305
            if ($hideTime === false) {
306
                $csvHeaders[] = get_lang('ScormTime');
307
            }
308
309
            $csv_content[] = $csvHeaders;
310
        }
311
312
        $result_disabled_ext_all = true;
313
        $chapterTypes = learnpath::getChapterTypes();
314
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
315
316
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
317
        $timeCourse = [];
318
        if ($minimumAvailable) {
319
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
320
            Session::write('trackTimeCourse', $timeCourse);
321
        }
322
323
        // Show lp items
324
        if (is_array($list) && count($list) > 0) {
325
            foreach ($list as $my_item_id) {
326
                $extend_this = 0;
327
                $order = 'DESC';
328
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
329
                    $extend_this = 1;
330
                    $order = 'ASC';
331
                }
332
333
                // Prepare statement to go through each attempt.
334
                $viewCondition = null;
335
                if (!empty($view)) {
336
                    $viewCondition = " AND v.view_count = $view  ";
337
                }
338
339
                $sql = "SELECT
340
                    iv.status as mystatus,
341
                    v.view_count as mycount,
342
                    iv.score as myscore,
343
                    iv.total_time as mytime,
344
                    i.iid as myid,
345
                    i.lp_id as mylpid,
346
                    iv.lp_view_id as mylpviewid,
347
                    i.title as mytitle,
348
                    i.max_score as mymaxscore,
349
                    iv.max_score as myviewmaxscore,
350
                    i.item_type as item_type,
351
                    iv.view_count as iv_view_count,
352
                    iv.id as iv_id,
353
                    path
354
                FROM $TBL_LP_ITEM as i
355
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
356
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
357
                INNER JOIN $TBL_LP_VIEW as v
358
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
359
                WHERE
360
                    v.c_id = $course_id AND
361
                    i.iid = $my_item_id AND
362
                    i.lp_id = $lp_id  AND
363
                    v.user_id = $user_id AND
364
                    v.session_id = $session_id
365
                    $viewCondition
366
                ORDER BY iv.view_count $order ";
367
368
                $result = Database::query($sql);
369
                $num = Database::num_rows($result);
370
                $time_for_total = 0;
371
                $attemptResult = 0;
372
373
                if ($timeCourse) {
374
                    if (isset($timeCourse['learnpath_detailed']) &&
375
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
376
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
377
                    ) {
378
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
379
                    }
380
                }
381
382
                // Extend all
383
                if (($extend_this || $extend_all) && $num > 0) {
384
                    $row = Database::fetch_array($result);
385
                    $result_disabled_ext_all = false;
386
                    if ('quiz' === $row['item_type']) {
387
                        // Check results_disabled in quiz table.
388
                        $lpItemPath = (int) $row['path'];
389
                        $sql = "SELECT results_disabled
390
                                FROM $TBL_QUIZ
391
                                WHERE
392
                                    iid = $lpItemPath";
393
                        $res_result_disabled = Database::query($sql);
394
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
395
396
                        if (Database::num_rows($res_result_disabled) > 0 &&
397
                            1 === (int) $row_result_disabled[0]
398
                        ) {
399
                            $result_disabled_ext_all = true;
400
                        }
401
                    }
402
403
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
404
                    $oddclass = 'row_even';
405
                    if (($counter % 2) === 0) {
406
                        $oddclass = 'row_odd';
407
                    }
408
                    $extend_link = '';
409
                    if (!empty($inter_num)) {
410
                        $extend_link = Display::url(
411
                            Display::return_icon(
412
                                'visible.png',
413
                                get_lang('HideAttemptView')
414
                            ),
415
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
416
                        );
417
                    }
418
                    $title = $row['mytitle'];
419
420
                    if (empty($title)) {
421
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
422
                    }
423
424
                    if (in_array($row['item_type'], $chapterTypes)) {
425
                        $title = "<h4> $title </h4>";
426
                    }
427
                    $lesson_status = $row['mystatus'];
428
                    $title = Security::remove_XSS($title);
429
                    $counter++;
430
431
                    $action = null;
432
                    if ('classic' === $type) {
433
                        $action = '<td></td>';
434
                    }
435
436
                    if (in_array($row['item_type'], $chapterTypes)) {
437
                        $output .= '<tr class="'.$oddclass.'">
438
                                <td>'.$extend_link.'</td>
439
                                <td colspan="4">
440
                                   '.$title.'
441
                                </td>
442
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
443
                                <td colspan="2"></td>
444
                                <td colspan="2"></td>
445
                                '.$action.'
446
                            </tr>';
447
                        continue;
448
                    } else {
449
                        $output .= '<tr class="'.$oddclass.'">
450
                                <td>'.$extend_link.'</td>
451
                                <td colspan="4">'.$title.'</td>
452
                                <td colspan="2"></td>
453
                                <td colspan="2"></td>
454
                                <td colspan="2"></td>
455
                                '.$action.'
456
                            </tr>';
457
                    }
458
459
                    $attemptCount = 1;
460
                    do {
461
                        // Check if there are interactions below.
462
                        $extend_attempt_link = '';
463
                        $extend_this_attempt = 0;
464
465
                        if ($timeCourse) {
466
                            //$attemptResult = 0;
467
                            if (isset($timeCourse['learnpath_detailed']) &&
468
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
469
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
470
                            ) {
471
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
472
                            }
473
                        }
474
                        if ((
475
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
476
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
477
                            ) &&
478
                            !$extend_all
479
                        ) {
480
                            if ($extendAttemptId == $row['iv_id']) {
481
                                // The extend button for this attempt has been clicked.
482
                                $extend_this_attempt = 1;
483
                                $extend_attempt_link = Display::url(
484
                                    Display::return_icon('visible.png', get_lang('HideAttemptView')),
485
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
486
                                );
487
                                if ($accessToPdfExport) {
488
                                    $extend_attempt_link .= '&nbsp;'.
489
                                        Display::url(
490
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
491
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
492
                                            ['class' => 'export']
493
                                        );
494
                                }
495
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
496
                                // The extend button for this attempt has not been clicked.
497
                                $extend_attempt_link = Display::url(
498
                                    Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
499
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
500
                                );
501
                                if ($accessToPdfExport) {
502
                                    $extend_attempt_link .= '&nbsp;'.
503
                                        Display::url(
504
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
505
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
506
                                            ['class' => 'export']
507
                                        );
508
                                }
509
                            }
510
                        }
511
512
                        $oddclass = 'row_even';
513
                        if (($counter % 2) == 0) {
514
                            $oddclass = 'row_odd';
515
                        }
516
517
                        $lesson_status = $row['mystatus'];
518
                        $score = $row['myscore'];
519
                        $time_for_total += $row['mytime'];
520
                        $attemptTime = $row['mytime'];
521
522
                        if ($minimumAvailable) {
523
                            /*$lp_time = $timeCourse[TOOL_LEARNPATH];
524
                            $lpTime = null;
525
                            if (isset($lp_time[$lp_id])) {
526
                                $lpTime = (int) $lp_time[$lp_id];
527
                            }
528
                            $time_for_total = $lpTime;*/
529
                            $time_for_total = (int) $attemptResult;
530
                            $attemptTime = (int) $attemptResult;
531
                        }
532
533
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
534
535
                        if ($score == 0) {
536
                            $maxscore = $row['mymaxscore'];
537
                        } else {
538
                            if ($row['item_type'] === 'sco') {
539
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
540
                                    $maxscore = $row['myviewmaxscore'];
541
                                } elseif ($row['myviewmaxscore'] === '') {
542
                                    $maxscore = 0;
543
                                } else {
544
                                    $maxscore = $row['mymaxscore'];
545
                                }
546
                            } else {
547
                                $maxscore = $row['mymaxscore'];
548
                            }
549
                        }
550
551
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
552
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
553
                        if ($row['item_type'] !== 'dir') {
554
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
555
                                $view_score = Display::return_icon(
556
                                    'invisible.png',
557
                                    get_lang('ResultsHiddenByExerciseSetting')
558
                                );
559
                            } else {
560
                                switch ($row['item_type']) {
561
                                    case 'sco':
562
                                        if ($maxscore == 0) {
563
                                            $view_score = $score;
564
                                        } else {
565
                                            $view_score = ExerciseLib::show_score(
566
                                                $score,
567
                                                $maxscore,
568
                                                false
569
                                            );
570
                                        }
571
                                        break;
572
                                    case 'document':
573
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
574
                                        break;
575
                                    default:
576
                                        $view_score = ExerciseLib::show_score(
577
                                            $score,
578
                                            $maxscore,
579
                                            false
580
                                        );
581
                                        break;
582
                                }
583
                            }
584
585
                            $action = null;
586
                            if ($type === 'classic') {
587
                                $action = '<td></td>';
588
                            }
589
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
590
                            if ($hideTime) {
591
                                $timeRow = '';
592
                            }
593
                            $output .= '<tr class="'.$oddclass.'">
594
                                    <td></td>
595
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
596
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
597
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
598
                                    <td colspan="2">'.$view_score.'</td>
599
                                    '.$timeRow.'
600
                                    '.$action.'
601
                                </tr>';
602
                            $attemptCount++;
603
                            if (!empty($export_csv)) {
604
                                $temp = [];
605
                                $temp[] = $title = Security::remove_XSS($title);
606
                                $temp[] = Security::remove_XSS(
607
                                    learnpathItem::humanize_status($lesson_status, false, $type)
608
                                );
609
610
                                if ($row['item_type'] === 'quiz') {
611
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
612
                                        $temp[] = '/';
613
                                    } else {
614
                                        $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
615
                                    }
616
                                } else {
617
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
618
                                }
619
620
                                if ($hideTime === false) {
621
                                    $temp[] = $time;
622
                                }
623
                                $csv_content[] = $temp;
624
                            }
625
                        }
626
627
                        $counter++;
628
                        $action = null;
629
                        if ($type === 'classic') {
630
                            $action = '<td></td>';
631
                        }
632
633
                        if ($extend_this_attempt || $extend_all) {
634
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
635
                            foreach ($list1 as $id => $interaction) {
636
                                $oddclass = 'row_even';
637
                                if (($counter % 2) == 0) {
638
                                    $oddclass = 'row_odd';
639
                                }
640
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
641
                                if ($hideTime) {
642
                                    $timeRow = '';
643
                                }
644
645
                                $output .= '<tr class="'.$oddclass.'">
646
                                        <td></td>
647
                                        <td></td>
648
                                        <td></td>
649
                                        <td>'.$interaction['order_id'].'</td>
650
                                        <td>'.$interaction['id'].'</td>';
651
652
                                $output .= '
653
                                        <td colspan="2">'.$interaction['type'].'</td>
654
                                        <td>'.$interaction['student_response_formatted'].'</td>
655
                                        <td>'.$interaction['result'].'</td>
656
                                        <td>'.$interaction['latency'].'</td>
657
                                        '.$timeRow.'
658
                                        '.$action.'
659
                                    </tr>';
660
                                $counter++;
661
                            }
662
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
663
                            foreach ($list2 as $id => $interaction) {
664
                                $oddclass = 'row_even';
665
                                if (($counter % 2) === 0) {
666
                                    $oddclass = 'row_odd';
667
                                }
668
                                $output .= '<tr class="'.$oddclass.'">
669
                                        <td></td>
670
                                        <td></td>
671
                                        <td></td>
672
                                        <td>'.$interaction['order_id'].'</td>
673
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
674
                                        <td colspan="2">'.$interaction['status'].'</td>
675
                                        <td>'.$interaction['score_raw'].'</td>
676
                                        <td>'.$interaction['score_max'].'</td>
677
                                        <td>'.$interaction['score_min'].'</td>
678
                                        '.$action.'
679
                                     </tr>';
680
                                $counter++;
681
                            }
682
                        }
683
                    } while ($row = Database::fetch_array($result));
684
                } elseif ($num > 0) {
685
                    // Not extended.
686
                    $row = Database::fetch_array($result, 'ASSOC');
687
                    $my_id = $row['myid'];
688
                    $my_lp_id = $row['mylpid'];
689
                    $my_lp_view_id = $row['mylpviewid'];
690
                    $lpItemPath = (int) $row['path'];
691
                    $result_disabled_ext_all = false;
692
                    if ($row['item_type'] === 'quiz') {
693
                        // Check results_disabled in quiz table.
694
                        $sql = "SELECT results_disabled
695
                                FROM $TBL_QUIZ
696
                                WHERE iid = $lpItemPath";
697
                        $res_result_disabled = Database::query($sql);
698
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
699
700
                        if (Database::num_rows($res_result_disabled) > 0 &&
701
                            (int) $row_result_disabled[0] === 1
702
                        ) {
703
                            $result_disabled_ext_all = true;
704
                        }
705
                    }
706
707
                    // Check if there are interactions below
708
                    $extend_this_attempt = 0;
709
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
710
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
711
                    $extend_attempt_link = '';
712
                    if ($inter_num > 0 || $objec_num > 0) {
713
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
714
                            // The extend button for this attempt has been clicked.
715
                            $extend_this_attempt = 1;
716
                            $extend_attempt_link = Display::url(
717
                                Display::return_icon('visible.png', get_lang('HideAttemptView')),
718
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
719
                            );
720
                        } else {
721
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
722
                            // The extend button for this attempt has not been clicked.
723
                            $extend_attempt_link = Display::url(
724
                                Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
725
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
726
                            );
727
                        }
728
                    }
729
730
                    $oddclass = 'row_even';
731
                    if (($counter % 2) == 0) {
732
                        $oddclass = 'row_odd';
733
                    }
734
735
                    $extend_link = '';
736
                    if ($inter_num > 1) {
737
                        $extend_link = Display::url(
738
                            Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
739
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
740
                        );
741
                    }
742
743
                    $lesson_status = $row['mystatus'];
744
                    $score = $row['myscore'];
745
                    $subtotal_time = $row['mytime'];
746
                    while ($tmp_row = Database::fetch_array($result)) {
747
                        $subtotal_time += $tmp_row['mytime'];
748
                    }
749
750
                    $title = $row['mytitle'];
751
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
752
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
753
                            WHERE
754
                                exe_exo_id="'.$row['path'].'" AND
755
                                exe_user_id="'.$user_id.'" AND
756
                                orig_lp_id = "'.$lp_id.'" AND
757
                                orig_lp_item_id = "'.$row['myid'].'" AND
758
                                c_id = '.$course_id.' AND
759
                                status <> "incomplete" AND
760
                                session_id = '.$session_id.'
761
                             ORDER BY exe_date DESC
762
                             LIMIT 1';
763
764
                    $resultLastAttempt = Database::query($sql);
765
                    $num = Database::num_rows($resultLastAttempt);
766
                    $id_last_attempt = null;
767
                    if ($num > 0) {
768
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
769
                            $id_last_attempt = $rowLA['exe_id'];
770
                        }
771
                    }
772
773
                    switch ($row['item_type']) {
774
                        case 'sco':
775
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
776
                                $maxscore = $row['myviewmaxscore'];
777
                            } elseif ($row['myviewmaxscore'] === '') {
778
                                $maxscore = 0;
779
                            } else {
780
                                $maxscore = $row['mymaxscore'];
781
                            }
782
                            break;
783
                        case 'quiz':
784
                            // Get score and total time from last attempt of a exercise en lp.
785
                            $sql = "SELECT iid, score
786
                                    FROM $TBL_LP_ITEM_VIEW
787
                                    WHERE
788
                                        c_id = $course_id AND
789
                                        lp_item_id = '".(int) $my_id."' AND
790
                                        lp_view_id = '".(int) $my_lp_view_id."'
791
                                    ORDER BY view_count DESC
792
                                    LIMIT 1";
793
                            $res_score = Database::query($sql);
794
                            $row_score = Database::fetch_array($res_score);
795
796
                            $sql = "SELECT SUM(total_time) as total_time
797
                                    FROM $TBL_LP_ITEM_VIEW
798
                                    WHERE
799
                                        c_id = $course_id AND
800
                                        lp_item_id = '".(int) $my_id."' AND
801
                                        lp_view_id = '".(int) $my_lp_view_id."'";
802
                            $res_time = Database::query($sql);
803
                            $row_time = Database::fetch_array($res_time);
804
805
                            $score = 0;
806
                            $subtotal_time = 0;
807
                            if (Database::num_rows($res_score) > 0 &&
808
                                Database::num_rows($res_time) > 0
809
                            ) {
810
                                $score = (float) $row_score['score'];
811
                                $subtotal_time = (int) $row_time['total_time'];
812
                            }
813
                            // Selecting the max score from an attempt.
814
                            $sql = "SELECT SUM(t.ponderation) as maxscore
815
                                    FROM (
816
                                        SELECT DISTINCT
817
                                            question_id, marks, ponderation
818
                                        FROM $tbl_stats_attempts as at
819
                                        INNER JOIN $tbl_quiz_questions as q
820
                                        ON q.iid = at.question_id
821
                                        WHERE exe_id ='$id_last_attempt'
822
                                    ) as t";
823
824
                            $result = Database::query($sql);
825
                            $row_max_score = Database::fetch_array($result);
826
                            $maxscore = $row_max_score['maxscore'];
827
828
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
829
                            $sql = 'SELECT SUM(exe_duration) exe_duration
830
                                    FROM '.$tbl_stats_exercices.'
831
                                    WHERE
832
                                        exe_exo_id="'.$row['path'].'" AND
833
                                        exe_user_id="'.$user_id.'" AND
834
                                        orig_lp_id = "'.$lp_id.'" AND
835
                                        orig_lp_item_id = "'.$row['myid'].'" AND
836
                                        c_id = '.$course_id.' AND
837
                                        status <> "incomplete" AND
838
                                        session_id = '.$session_id.'
839
                                     ORDER BY exe_date DESC ';
840
                            $sumScoreResult = Database::query($sql);
841
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
842
                            if (!empty($durationRow['exe_duration'])) {
843
                                $exeDuration = $durationRow['exe_duration'];
844
                                if ($exeDuration != $subtotal_time &&
845
                                    !empty($row_score['iid']) &&
846
                                    !empty($exeDuration)
847
                                ) {
848
                                    $subtotal_time = $exeDuration;
849
                                    // Update c_lp_item_view.total_time
850
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
851
                                                  WHERE iid = ".$row_score['iid'];
852
                                    Database::query($sqlUpdate);
853
                                }
854
                            }
855
                            break;
856
                        default:
857
                            $maxscore = $row['mymaxscore'];
858
                            break;
859
                    }
860
861
                    $time_for_total = $subtotal_time;
862
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
863
                    if (empty($title)) {
864
                        $title = learnpath::rl_get_resource_name(
865
                            $courseInfo['code'],
866
                            $lp_id,
867
                            $row['myid']
868
                        );
869
                    }
870
871
                    $action = null;
872
                    if ($type === 'classic') {
873
                        $action = '<td></td>';
874
                    }
875
876
                    if (in_array($row['item_type'], $chapterTypes)) {
877
                        $title = Security::remove_XSS($title);
878
                        $output .= '<tr class="'.$oddclass.'">
879
                                <td>'.$extend_link.'</td>
880
                                <td colspan="10">
881
                                <h4>'.$title.'</h4>
882
                                </td>
883
                                '.$action.'
884
                            </tr>';
885
                    } else {
886
                        $correct_test_link = '-';
887
                        $showRowspan = false;
888
                        if ($row['item_type'] === 'quiz') {
889
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
890
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
891
                                     WHERE
892
                                        exe_exo_id="'.$row['path'].'" AND
893
                                        exe_user_id="'.$user_id.'" AND
894
                                        orig_lp_id = "'.$lp_id.'" AND
895
                                        orig_lp_item_id = "'.$row['myid'].'" AND
896
                                        c_id = '.$course_id.' AND
897
                                        status <> "incomplete" AND
898
                                        session_id = '.$session_id.'
899
                                     ORDER BY exe_date DESC ';
900
901
                            $resultLastAttempt = Database::query($sql);
902
                            $num = Database::num_rows($resultLastAttempt);
903
                            $showRowspan = false;
904
                            if ($num > 0) {
905
                                $linkId = 'link_'.$my_id;
906
                                if ($extendedAttempt == 1 &&
907
                                    $lp_id == $my_lp_id &&
908
                                    $lp_item_id == $my_id
909
                                ) {
910
                                    $showRowspan = true;
911
                                    $correct_test_link = Display::url(
912
                                        Display::return_icon(
913
                                            'view_less_stats.gif',
914
                                            get_lang('HideAllAttempts')
915
                                        ),
916
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
917
                                        ['id' => $linkId]
918
                                    );
919
                                } else {
920
                                    $correct_test_link = Display::url(
921
                                        Display::return_icon(
922
                                            'view_more_stats.gif',
923
                                            get_lang(
924
                                                'ShowAllAttemptsByExercise'
925
                                            )
926
                                        ),
927
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
928
                                        ['id' => $linkId]
929
                                    );
930
                                }
931
                            }
932
                        }
933
934
                        $title = Security::remove_XSS($title);
935
                        $action = null;
936
                        if ($type === 'classic') {
937
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
938
                        }
939
940
                        if ($lp_id == $my_lp_id && false) {
941
                            $output .= '<tr class ='.$oddclass.'>
942
                                    <td>'.$extend_link.'</td>
943
                                    <td colspan="4">'.$title.'</td>
944
                                    <td colspan="2">&nbsp;</td>
945
                                    <td colspan="2">&nbsp;</td>
946
                                    <td colspan="2">&nbsp;</td>
947
                                    '.$action.'
948
                                </tr>';
949
                            $output .= '</tr>';
950
                        } else {
951
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
952
                                $output .= "<tr class='$oddclass'>";
953
                            } else {
954
                                $output .= "<tr class='$oddclass'>";
955
                            }
956
957
                            $scoreItem = null;
958
                            if ($row['item_type'] === 'quiz') {
959
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
960
                                    $scoreItem .= Display::return_icon(
961
                                        'invisible.gif',
962
                                        get_lang('ResultsHiddenByExerciseSetting')
963
                                    );
964
                                } else {
965
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
966
                                }
967
                            } else {
968
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
969
                            }
970
971
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
972
                            if ($hideTime) {
973
                                $timeRow = '';
974
                            }
975
976
                            $output .= '
977
                                <td>'.$extend_link.'</td>
978
                                <td colspan="4">'.$title.'</td>
979
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
980
                                <td colspan="2">'.$scoreItem.'</td>
981
                                '.$timeRow.'
982
                                '.$action.'
983
                             ';
984
                            $output .= '</tr>';
985
                        }
986
987
                        if (!empty($export_csv)) {
988
                            $temp = [];
989
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
990
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
991
                            if ($row['item_type'] === 'quiz') {
992
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
993
                                    $temp[] = '/';
994
                                } else {
995
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
996
                                }
997
                            } else {
998
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
999
                            }
1000
1001
                            if ($hideTime === false) {
1002
                                $temp[] = $time;
1003
                            }
1004
                            $csv_content[] = $temp;
1005
                        }
1006
                    }
1007
1008
                    $counter++;
1009
                    $action = null;
1010
                    if ($type === 'classic') {
1011
                        $action = '<td></td>';
1012
                    }
1013
1014
                    if ($extend_this_attempt || $extend_all) {
1015
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1016
                        foreach ($list1 as $id => $interaction) {
1017
                            $oddclass = 'row_even';
1018
                            if (($counter % 2) == 0) {
1019
                                $oddclass = 'row_odd';
1020
                            }
1021
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1022
                            if ($hideTime) {
1023
                                $timeRow = '';
1024
                            }
1025
1026
                            $output .= '<tr class="'.$oddclass.'">
1027
                                    <td></td>
1028
                                    <td></td>
1029
                                    <td></td>
1030
                                    <td>'.$interaction['order_id'].'</td>
1031
                                    <td>'.$interaction['id'].'</td>
1032
                                    <td colspan="2">'.$interaction['type'].'</td>
1033
                                    <td>'.urldecode($interaction['student_response']).'</td>
1034
                                    <td>'.$interaction['result'].'</td>
1035
                                    <td>'.$interaction['latency'].'</td>
1036
                                    '.$timeRow.'
1037
                                    '.$action.'
1038
                               </tr>';
1039
                            $counter++;
1040
                        }
1041
1042
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1043
                        foreach ($list2 as $id => $interaction) {
1044
                            $oddclass = 'row_even';
1045
                            if (($counter % 2) == 0) {
1046
                                $oddclass = 'row_odd';
1047
                            }
1048
                            $output .= '<tr class="'.$oddclass.'">
1049
                                    <td></td>
1050
                                    <td></td>
1051
                                    <td></td>
1052
                                    <td>'.$interaction['order_id'].'</td>
1053
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1054
                                    <td colspan="2">'.$interaction['status'].'</td>
1055
                                    <td>'.$interaction['score_raw'].'</td>
1056
                                    <td>'.$interaction['score_max'].'</td>
1057
                                    <td>'.$interaction['score_min'].'</td>
1058
                                    '.$action.'
1059
                               </tr>';
1060
                            $counter++;
1061
                        }
1062
                    }
1063
1064
                    // Attempts listing by exercise.
1065
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1066
                        // Get attempts of a exercise.
1067
                        if (!empty($lp_id) &&
1068
                            !empty($lp_item_id) &&
1069
                            'quiz' === $row['item_type']
1070
                        ) {
1071
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1072
                                    WHERE
1073
                                        c_id = $course_id AND
1074
                                        iid = '$lp_item_id' AND
1075
                                        lp_id = '$lp_id'";
1076
                            $res_path = Database::query($sql);
1077
                            $row_path = Database::fetch_array($res_path);
1078
1079
                            if (Database::num_rows($res_path) > 0) {
1080
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1081
                                        WHERE
1082
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1083
                                            status <> "incomplete" AND
1084
                                            exe_user_id="'.$user_id.'" AND
1085
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1086
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1087
                                            c_id = '.$course_id.'  AND
1088
                                            session_id = '.$session_id.'
1089
                                        ORDER BY exe_date';
1090
                                $res_attempts = Database::query($sql);
1091
                                $num_attempts = Database::num_rows($res_attempts);
1092
                                if ($num_attempts > 0) {
1093
                                    $n = 1;
1094
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1095
                                        $my_score = $row_attempts['exe_result'];
1096
                                        $my_maxscore = $row_attempts['exe_weighting'];
1097
                                        $my_exe_id = $row_attempts['exe_id'];
1098
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1099
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1100
                                        $time_attemp = ' - ';
1101
                                        if ($mktime_start_date && $mktime_exe_date) {
1102
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1103
                                        }
1104
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1105
                                            $view_score = Display::return_icon(
1106
                                                'invisible.png',
1107
                                                get_lang(
1108
                                                    'ResultsHiddenByExerciseSetting'
1109
                                                )
1110
                                            );
1111
                                        } else {
1112
                                            // Show only float when need it
1113
                                            if ($my_score == 0) {
1114
                                                $view_score = ExerciseLib::show_score(
1115
                                                    0,
1116
                                                    $my_maxscore,
1117
                                                    false
1118
                                                );
1119
                                            } else {
1120
                                                if ($my_maxscore == 0) {
1121
                                                    $view_score = $my_score;
1122
                                                } else {
1123
                                                    $view_score = ExerciseLib::show_score(
1124
                                                        $my_score,
1125
                                                        $my_maxscore,
1126
                                                        false
1127
                                                    );
1128
                                                }
1129
                                            }
1130
                                        }
1131
                                        $my_lesson_status = $row_attempts['status'];
1132
                                        if ($my_lesson_status == '') {
1133
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1134
                                        } elseif ($my_lesson_status == 'incomplete') {
1135
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1136
                                        }
1137
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1138
                                        if ($hideTime) {
1139
                                            $timeRow = '';
1140
                                        }
1141
1142
                                        $output .= '<tr class="'.$oddclass.'" >
1143
                                        <td></td>
1144
                                        <td>'.$extend_attempt_link.'</td>
1145
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1146
                                        <td colspan="2">'.$my_lesson_status.'</td>
1147
                                        <td colspan="2">'.$view_score.'</td>
1148
                                        '.$timeRow;
1149
1150
                                        if ($action == 'classic') {
1151
                                            if ($origin != 'tracking') {
1152
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1153
                                                    $output .= '<td>
1154
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1155
                                                            </td>';
1156
                                                } else {
1157
                                                    $output .= '<td>
1158
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1159
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1160
                                                            </a></td>';
1161
                                                }
1162
                                            } else {
1163
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1164
                                                    $output .= '<td>
1165
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
1166
                                                } else {
1167
                                                    $output .= '<td>
1168
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1169
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
1170
                                                }
1171
                                            }
1172
                                        }
1173
                                        $output .= '</tr>';
1174
                                        $n++;
1175
                                    }
1176
                                }
1177
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1178
                            }
1179
                        }
1180
                    }
1181
                }
1182
1183
                $total_time += $time_for_total;
1184
                // QUIZZ IN LP
1185
                $a_my_id = [];
1186
                if (!empty($my_lp_id)) {
1187
                    $a_my_id[] = $my_lp_id;
1188
                }
1189
            }
1190
        }
1191
1192
        // NOT Extend all "left green cross"
1193
        if (!empty($a_my_id)) {
1194
            if ($extendedAttempt) {
1195
                // "Right green cross" extended
1196
                $total_score = self::get_avg_student_score(
1197
                    $user_id,
1198
                    $course_id,
1199
                    $a_my_id,
1200
                    $session_id,
1201
                    false,
1202
                    false
1203
                );
1204
            } else {
1205
                // "Left green cross" extended
1206
                $total_score = self::get_avg_student_score(
1207
                    $user_id,
1208
                    $course_id,
1209
                    $a_my_id,
1210
                    $session_id,
1211
                    false,
1212
                    true
1213
                );
1214
            }
1215
        } else {
1216
            // Extend all "left green cross"
1217
            $total_score = self::get_avg_student_score(
1218
                $user_id,
1219
                $course_id,
1220
                [$lp_id],
1221
                $session_id,
1222
                false,
1223
                false
1224
            );
1225
        }
1226
1227
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1228
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1229
1230
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1231
            $final_score = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
1232
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
1233
        } else {
1234
            if (is_numeric($total_score)) {
1235
                $final_score = $total_score.'%';
1236
            } else {
1237
                $final_score = $total_score;
1238
            }
1239
            $finalScoreToCsv = $final_score;
1240
        }
1241
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1242
1243
        $oddclass = 'row_even';
1244
        if (($counter % 2) == 0) {
1245
            $oddclass = 'row_odd';
1246
        }
1247
1248
        $action = null;
1249
        if ('classic' === $type) {
1250
            $action = '<td></td>';
1251
        }
1252
1253
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1254
        if ($hideTime) {
1255
            $timeTotal = '';
1256
        }
1257
1258
        $output .= '<tr class="'.$oddclass.'">
1259
                <td></td>
1260
                <td colspan="4">
1261
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
1262
                </td>
1263
                <td colspan="2">'.$progress.'%</td>
1264
                <td colspan="2">'.$final_score.'</td>
1265
                '.$timeTotal.'
1266
                '.$action.'
1267
           </tr>';
1268
1269
        $output .= '
1270
                    </tbody>
1271
                </table>
1272
            </div>
1273
        ';
1274
1275
        if (!empty($export_csv)) {
1276
            $temp = [
1277
                '',
1278
                '',
1279
                '',
1280
                '',
1281
            ];
1282
            $csv_content[] = $temp;
1283
            $temp = [
1284
                get_lang('AccomplishedStepsTotal'),
1285
                '',
1286
                $finalScoreToCsv,
1287
            ];
1288
1289
            if ($hideTime === false) {
1290
                $temp[] = $total_time;
1291
            }
1292
1293
            $csv_content[] = $temp;
1294
            ob_end_clean();
1295
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1296
            exit;
1297
        }
1298
1299
        return $output;
1300
    }
1301
1302
    /**
1303
     * @param int  $userId
1304
     * @param bool $getCount
1305
     *
1306
     * @return array
1307
     */
1308
    public static function getStats($userId, $getCount = false)
1309
    {
1310
        $courses = [];
1311
        $assignedCourses = [];
1312
        $drhCount = 0;
1313
        $teachersCount = 0;
1314
        $studentBossCount = 0;
1315
        $courseCount = 0;
1316
        $assignedCourseCount = 0;
1317
        $checkSessionVisibility = api_get_configuration_value('show_users_in_active_sessions_in_tracking');
1318
1319
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1320
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1321
                'drh_all',
1322
                $userId,
1323
                false,
1324
                null,
1325
                null,
1326
                null,
1327
                null,
1328
                null,
1329
                null,
1330
                null,
1331
                [],
1332
                [],
1333
                STUDENT
1334
            );
1335
1336
            $students = [];
1337
            if (is_array($studentList)) {
1338
                foreach ($studentList as $studentData) {
1339
                    $students[] = $studentData['user_id'];
1340
                }
1341
            }
1342
1343
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1344
                'drh_all',
1345
                $userId,
1346
                $getCount,
1347
                null,
1348
                null,
1349
                null,
1350
                null,
1351
                null,
1352
                null,
1353
                null,
1354
                [],
1355
                [],
1356
                STUDENT_BOSS
1357
            );
1358
1359
            if ($getCount) {
1360
                $studentBossCount = $studentBossesList;
1361
            } else {
1362
                $studentBosses = [];
1363
                if (is_array($studentBossesList)) {
1364
                    foreach ($studentBossesList as $studentBossData) {
1365
                        $studentBosses[] = $studentBossData['user_id'];
1366
                    }
1367
                }
1368
            }
1369
1370
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1371
                'drh_all',
1372
                $userId,
1373
                $getCount,
1374
                null,
1375
                null,
1376
                null,
1377
                null,
1378
                null,
1379
                null,
1380
                null,
1381
                [],
1382
                [],
1383
                COURSEMANAGER
1384
            );
1385
1386
            if ($getCount) {
1387
                $teachersCount = $teacherList;
1388
            } else {
1389
                $teachers = [];
1390
                foreach ($teacherList as $teacherData) {
1391
                    $teachers[] = $teacherData['user_id'];
1392
                }
1393
            }
1394
1395
            $humanResources = 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
                DRH
1409
            );
1410
1411
            if ($getCount) {
1412
                $drhCount = $humanResources;
1413
            } else {
1414
                $humanResourcesList = [];
1415
                if (is_array($humanResources)) {
1416
                    foreach ($humanResources as $item) {
1417
                        $humanResourcesList[] = $item['user_id'];
1418
                    }
1419
                }
1420
            }
1421
1422
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1423
                $userId,
1424
                null,
1425
                null,
1426
                null,
1427
                null,
1428
                null,
1429
                $getCount
1430
            );
1431
1432
            if ($getCount) {
1433
                $courseCount = $platformCourses;
1434
            } else {
1435
                foreach ($platformCourses as $course) {
1436
                    $courses[$course['code']] = $course['code'];
1437
                }
1438
            }
1439
1440
            $sessions = SessionManager::get_sessions_followed_by_drh(
1441
                $userId,
1442
                null,
1443
                null,
1444
                false
1445
            );
1446
        } else {
1447
            $studentList = UserManager::getUsersFollowedByUser(
1448
                $userId,
1449
                STUDENT,
1450
                false,
1451
                false,
1452
                false,
1453
                null,
1454
                null,
1455
                null,
1456
                null,
1457
                null,
1458
                null,
1459
                COURSEMANAGER,
1460
                null,
1461
                $checkSessionVisibility
1462
            );
1463
1464
            $students = [];
1465
            if (is_array($studentList)) {
1466
                foreach ($studentList as $studentData) {
1467
                    $students[] = $studentData['user_id'];
1468
                }
1469
            }
1470
1471
            $studentBossesList = UserManager::getUsersFollowedByUser(
1472
                $userId,
1473
                STUDENT_BOSS,
1474
                false,
1475
                false,
1476
                $getCount,
1477
                null,
1478
                null,
1479
                null,
1480
                null,
1481
                null,
1482
                null,
1483
                COURSEMANAGER,
1484
                null,
1485
                $checkSessionVisibility
1486
            );
1487
1488
            if ($getCount) {
1489
                $studentBossCount = $studentBossesList;
1490
            } else {
1491
                $studentBosses = [];
1492
                if (is_array($studentBossesList)) {
1493
                    foreach ($studentBossesList as $studentBossData) {
1494
                        $studentBosses[] = $studentBossData['user_id'];
1495
                    }
1496
                }
1497
            }
1498
1499
            $teacherList = UserManager::getUsersFollowedByUser(
1500
                $userId,
1501
                COURSEMANAGER,
1502
                false,
1503
                false,
1504
                $getCount,
1505
                null,
1506
                null,
1507
                null,
1508
                null,
1509
                null,
1510
                null,
1511
                COURSEMANAGER,
1512
                null,
1513
                $checkSessionVisibility
1514
            );
1515
1516
            if ($getCount) {
1517
                $teachersCount = $teacherList;
1518
            } else {
1519
                $teachers = [];
1520
                foreach ($teacherList as $teacherData) {
1521
                    $teachers[] = $teacherData['user_id'];
1522
                }
1523
            }
1524
1525
            $humanResources = UserManager::getUsersFollowedByUser(
1526
                $userId,
1527
                DRH,
1528
                false,
1529
                false,
1530
                $getCount,
1531
                null,
1532
                null,
1533
                null,
1534
                null,
1535
                null,
1536
                null,
1537
                COURSEMANAGER,
1538
                null,
1539
                $checkSessionVisibility
1540
            );
1541
1542
            if ($getCount) {
1543
                $drhCount = $humanResources;
1544
            } else {
1545
                $humanResourcesList = [];
1546
                foreach ($humanResources as $item) {
1547
                    $humanResourcesList[] = $item['user_id'];
1548
                }
1549
            }
1550
1551
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1552
                $userId,
1553
                COURSEMANAGER,
1554
                null,
1555
                null,
1556
                null,
1557
                null,
1558
                $getCount,
1559
                null,
1560
                null,
1561
                true
1562
            );
1563
1564
            if ($getCount) {
1565
                $assignedCourseCount = $platformCourses;
1566
            } else {
1567
                foreach ($platformCourses as $course) {
1568
                    $assignedCourses[$course['code']] = $course['code'];
1569
                }
1570
            }
1571
1572
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1573
                $userId,
1574
                COURSEMANAGER,
1575
                null,
1576
                null,
1577
                null,
1578
                null,
1579
                $getCount
1580
            );
1581
1582
            if ($getCount) {
1583
                $courseCount = $platformCourses;
1584
            } else {
1585
                foreach ($platformCourses as $course) {
1586
                    $courses[$course['code']] = $course['code'];
1587
                }
1588
            }
1589
1590
            $sessions = SessionManager::getSessionsFollowedByUser(
1591
                $userId,
1592
                COURSEMANAGER,
1593
                null,
1594
                null,
1595
                false
1596
            );
1597
        }
1598
1599
        if ($getCount) {
1600
            return [
1601
                'drh' => $drhCount,
1602
                'teachers' => $teachersCount,
1603
                'student_count' => count($students),
1604
                'student_list' => $students,
1605
                'student_bosses' => $studentBossCount,
1606
                'courses' => $courseCount,
1607
                'session_count' => count($sessions),
1608
                'session_list' => $sessions,
1609
                'assigned_courses' => $assignedCourseCount,
1610
            ];
1611
        }
1612
1613
        return [
1614
            'drh' => $humanResourcesList,
1615
            'teachers' => $teachers,
1616
            'student_list' => $students,
1617
            'student_bosses' => $studentBosses,
1618
            'courses' => $courses,
1619
            'sessions' => $sessions,
1620
            'assigned_courses' => $assignedCourses,
1621
        ];
1622
    }
1623
1624
    /**
1625
     * Calculates the time spent on the platform by a user.
1626
     *
1627
     * @param int|array $userId
1628
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
1629
     * @param string    $start_date       start date date('Y-m-d H:i:s')
1630
     * @param string    $end_date         end date date('Y-m-d H:i:s')
1631
     * @param bool      $returnAllRecords
1632
     *
1633
     * @return int
1634
     */
1635
    public static function get_time_spent_on_the_platform(
1636
        $userId,
1637
        $timeFilter = 'last_7_days',
1638
        $start_date = null,
1639
        $end_date = null,
1640
        $returnAllRecords = false
1641
    ) {
1642
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1643
        $condition_time = '';
1644
1645
        if (is_array($userId)) {
1646
            $userList = array_map('intval', $userId);
1647
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1648
        } else {
1649
            $userId = (int) $userId;
1650
            $userCondition = " login_user_id = $userId ";
1651
        }
1652
1653
        $url_condition = null;
1654
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1655
        $url_table = null;
1656
        if (api_is_multiple_url_enabled()) {
1657
            $access_url_id = api_get_current_access_url_id();
1658
            $url_table = ", $tbl_url_rel_user as url_users";
1659
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1660
        }
1661
1662
        if (empty($timeFilter)) {
1663
            $timeFilter = 'last_week';
1664
        }
1665
1666
        $today = new DateTime('now', new DateTimeZone('UTC'));
1667
1668
        switch ($timeFilter) {
1669
            case 'last_7_days':
1670
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1671
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1672
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1673
                break;
1674
            case 'last_30_days':
1675
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1676
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1677
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1678
                break;
1679
            case 'wide':
1680
                if (!empty($start_date) && !empty($end_date)) {
1681
                    $start_date = Database::escape_string($start_date);
1682
                    $end_date = Database::escape_string($end_date);
1683
                    $condition_time = ' AND (
1684
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
1685
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
1686
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
1687
                    ) ';
1688
                }
1689
                break;
1690
            case 'custom':
1691
                if (!empty($start_date) && !empty($end_date)) {
1692
                    $start_date = Database::escape_string($start_date);
1693
                    $end_date = Database::escape_string($end_date);
1694
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1695
                }
1696
                break;
1697
        }
1698
1699
        if ($returnAllRecords) {
1700
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
1701
                    FROM $tbl_track_login u $url_table
1702
                    WHERE $userCondition $condition_time $url_condition
1703
                    ORDER BY login_date";
1704
            $rs = Database::query($sql);
1705
1706
            return Database::store_result($rs, 'ASSOC');
1707
        }
1708
1709
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1710
    	        FROM $tbl_track_login u $url_table
1711
                WHERE $userCondition $condition_time $url_condition";
1712
        $rs = Database::query($sql);
1713
        $row = Database::fetch_array($rs, 'ASSOC');
1714
        $diff = $row['diff'];
1715
1716
        if ($diff >= 0) {
1717
            return $diff;
1718
        }
1719
1720
        return -1;
1721
    }
1722
1723
    /**
1724
     * @param string $startDate
1725
     * @param string $endDate
1726
     *
1727
     * @return int
1728
     */
1729
    public static function getTotalTimeSpentOnThePlatform(
1730
        $startDate = '',
1731
        $endDate = ''
1732
    ) {
1733
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1734
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1735
1736
        $url_table = null;
1737
        $url_condition = null;
1738
        if (api_is_multiple_url_enabled()) {
1739
            $access_url_id = api_get_current_access_url_id();
1740
            $url_table = ", ".$tbl_url_rel_user." as url_users";
1741
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1742
        }
1743
1744
        if (!empty($startDate) && !empty($endDate)) {
1745
            $startDate = Database::escape_string($startDate);
1746
            $endDate = Database::escape_string($endDate);
1747
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
1748
        }
1749
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1750
    	        FROM $tbl_track_login u $url_table
1751
                WHERE $condition_time $url_condition";
1752
        $rs = Database::query($sql);
1753
        $row = Database::fetch_array($rs, 'ASSOC');
1754
        $diff = $row['diff'];
1755
1756
        if ($diff >= 0) {
1757
            return $diff;
1758
        }
1759
1760
        return -1;
1761
    }
1762
1763
    /**
1764
     * Checks if the "lp_minimum_time" feature is available for the course.
1765
     *
1766
     * @param int $sessionId
1767
     * @param int $courseId
1768
     *
1769
     * @return bool
1770
     */
1771
    public static function minimumTimeAvailable($sessionId, $courseId)
1772
    {
1773
        if (!api_get_configuration_value('lp_minimum_time')) {
1774
            return false;
1775
        }
1776
1777
        if (!empty($sessionId)) {
1778
            $extraFieldValue = new ExtraFieldValue('session');
1779
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
1780
1781
            if ($value && isset($value['value']) && 1 == $value['value']) {
1782
                return true;
1783
            }
1784
        } else {
1785
            if ($courseId) {
1786
                $extraFieldValue = new ExtraFieldValue('course');
1787
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
1788
                if ($value && isset($value['value']) && 1 == $value['value']) {
1789
                    return true;
1790
                }
1791
            }
1792
        }
1793
1794
        return false;
1795
    }
1796
1797
    /**
1798
     * Calculates the time spent on the course.
1799
     *
1800
     * @param int $user_id
1801
     * @param int $courseId
1802
     * @param int $session_id
1803
     *
1804
     * @return int Time in seconds
1805
     */
1806
    public static function get_time_spent_on_the_course(
1807
        $user_id,
1808
        $courseId,
1809
        $session_id = 0
1810
    ) {
1811
        $courseId = (int) $courseId;
1812
1813
        if (empty($courseId) || empty($user_id)) {
1814
            return 0;
1815
        }
1816
1817
        if (self::minimumTimeAvailable($session_id, $courseId)) {
1818
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
1819
1820
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
1821
        }
1822
1823
        $conditionUser = '';
1824
        $session_id = (int) $session_id;
1825
        if (is_array($user_id)) {
1826
            $user_id = array_map('intval', $user_id);
1827
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
1828
        } else {
1829
            if (!empty($user_id)) {
1830
                $user_id = (int) $user_id;
1831
                $conditionUser = " AND user_id = $user_id ";
1832
            }
1833
        }
1834
1835
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1836
        $sql = "SELECT
1837
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1838
                FROM $table
1839
                WHERE
1840
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
1841
                    c_id = '$courseId' ";
1842
1843
        if (-1 != $session_id) {
1844
            $sql .= "AND session_id = '$session_id' ";
1845
        }
1846
1847
        $sql .= $conditionUser;
1848
1849
        $rs = Database::query($sql);
1850
        $row = Database::fetch_array($rs);
1851
1852
        return $row['nb_seconds'];
1853
    }
1854
1855
    /**
1856
     * Get first connection date for a student.
1857
     *
1858
     * @param int $student_id
1859
     *
1860
     * @return string|bool Date format long without day or false if there are no connections
1861
     */
1862
    public static function get_first_connection_date($student_id)
1863
    {
1864
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1865
        $sql = 'SELECT login_date
1866
                FROM '.$table.'
1867
                WHERE login_user_id = '.intval($student_id).'
1868
                ORDER BY login_date ASC
1869
                LIMIT 0,1';
1870
1871
        $rs = Database::query($sql);
1872
        if (Database::num_rows($rs) > 0) {
1873
            if ($first_login_date = Database::result($rs, 0, 0)) {
1874
                return api_convert_and_format_date(
1875
                    $first_login_date,
1876
                    DATE_FORMAT_SHORT
1877
                );
1878
            }
1879
        }
1880
1881
        return false;
1882
    }
1883
1884
    /**
1885
     * Get las connection date for a student.
1886
     *
1887
     * @param int  $student_id
1888
     * @param bool $warning_message  Show a warning message (optional)
1889
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1890
     *
1891
     * @return string|int|bool Date format long without day, false if there are no connections or
1892
     *                         timestamp if parameter $return_timestamp is true
1893
     */
1894
    public static function get_last_connection_date(
1895
        $student_id,
1896
        $warning_message = false,
1897
        $return_timestamp = false
1898
    ) {
1899
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1900
        $sql = 'SELECT login_date
1901
                FROM '.$table.'
1902
                WHERE login_user_id = '.intval($student_id).'
1903
                ORDER BY login_date
1904
                DESC LIMIT 0,1';
1905
1906
        $rs = Database::query($sql);
1907
        if (Database::num_rows($rs) > 0) {
1908
            if ($last_login_date = Database::result($rs, 0, 0)) {
1909
                $last_login_date = api_get_local_time($last_login_date);
1910
                if ($return_timestamp) {
1911
                    return api_strtotime($last_login_date, 'UTC');
1912
                } else {
1913
                    if (!$warning_message) {
1914
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1915
                    } else {
1916
                        $timestamp = api_strtotime($last_login_date, 'UTC');
1917
                        $currentTimestamp = time();
1918
1919
                        //If the last connection is > than 7 days, the text is red
1920
                        //345600 = 7 days in seconds
1921
                        if ($currentTimestamp - $timestamp > 604800) {
1922
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</span>';
1923
                        } else {
1924
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1925
                        }
1926
                    }
1927
                }
1928
            }
1929
        }
1930
1931
        return false;
1932
    }
1933
1934
    /**
1935
     * Get first user's connection date on the course.
1936
     *
1937
     * @param int User id
1938
     * @param int $courseId
1939
     * @param int Session id (optional, default=0)
1940
     * @param bool $convert_date
1941
     *
1942
     * @return string|bool Date with format long without day or false if there is no date
1943
     */
1944
    public static function get_first_connection_date_on_the_course(
1945
        $student_id,
1946
        $courseId,
1947
        $session_id = 0,
1948
        $convert_date = true
1949
    ) {
1950
        $student_id = (int) $student_id;
1951
        $courseId = (int) $courseId;
1952
        $session_id = (int) $session_id;
1953
1954
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1955
        $sql = 'SELECT login_course_date
1956
                FROM '.$table.'
1957
                WHERE
1958
                    user_id = '.$student_id.' AND
1959
                    c_id = '.$courseId.' AND
1960
                    session_id = '.$session_id.'
1961
                ORDER BY login_course_date ASC
1962
                LIMIT 0,1';
1963
        $rs = Database::query($sql);
1964
        if (Database::num_rows($rs) > 0) {
1965
            if ($first_login_date = Database::result($rs, 0, 0)) {
1966
                if (empty($first_login_date)) {
1967
                    return false;
1968
                }
1969
1970
                if ($convert_date) {
1971
                    return api_convert_and_format_date(
1972
                        $first_login_date,
1973
                        DATE_FORMAT_SHORT
1974
                    );
1975
                }
1976
1977
                return $first_login_date;
1978
            }
1979
        }
1980
1981
        return false;
1982
    }
1983
1984
    /**
1985
     * Get last user's connection date on the course.
1986
     *
1987
     * @param int         User id
1988
     * @param array $courseInfo real_id and code are used
1989
     * @param int            Session id (optional, default=0)
1990
     * @param bool $convert_date
1991
     *
1992
     * @return string|bool Date with format long without day or false if there is no date
1993
     */
1994
    public static function get_last_connection_date_on_the_course(
1995
        $student_id,
1996
        $courseInfo,
1997
        $session_id = 0,
1998
        $convert_date = true
1999
    ) {
2000
        // protect data
2001
        $student_id = (int) $student_id;
2002
        $session_id = (int) $session_id;
2003
2004
        if (empty($courseInfo) || empty($student_id)) {
2005
            return false;
2006
        }
2007
2008
        $courseId = $courseInfo['real_id'];
2009
2010
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2011
2012
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2013
            // Show the last date on which the user acceed the session when it was active
2014
            $where_condition = '';
2015
            $userInfo = api_get_user_info($student_id);
2016
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
2017
                // fin de acceso a la sesión
2018
                $sessionInfo = SessionManager::fetch($session_id);
2019
                $last_access = $sessionInfo['access_end_date'];
2020
                if (!empty($last_access)) {
2021
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
2022
                }
2023
            }
2024
            $sql = "SELECT logout_course_date
2025
                    FROM $table
2026
                    WHERE   user_id = $student_id AND
2027
                            c_id = $courseId AND
2028
                            session_id = $session_id $where_condition
2029
                    ORDER BY logout_course_date DESC
2030
                    LIMIT 0,1";
2031
2032
            $rs = Database::query($sql);
2033
            if (Database::num_rows($rs) > 0) {
2034
                if ($last_login_date = Database::result($rs, 0, 0)) {
2035
                    if (empty($last_login_date)) {
2036
                        return false;
2037
                    }
2038
                    if ($convert_date) {
2039
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2040
                    }
2041
2042
                    return $last_login_date;
2043
                }
2044
            }
2045
        } else {
2046
            $sql = "SELECT logout_course_date
2047
                    FROM $table
2048
                    WHERE   user_id = $student_id AND
2049
                            c_id = $courseId AND
2050
                            session_id = $session_id
2051
                    ORDER BY logout_course_date DESC
2052
                    LIMIT 0,1";
2053
2054
            $rs = Database::query($sql);
2055
            if (Database::num_rows($rs) > 0) {
2056
                if ($last_login_date = Database::result($rs, 0, 0)) {
2057
                    if (empty($last_login_date)) {
2058
                        return false;
2059
                    }
2060
                    //see #5736
2061
                    $last_login_date_timestamp = api_strtotime($last_login_date);
2062
                    $now = time();
2063
                    //If the last connection is > than 7 days, the text is red
2064
                    //345600 = 7 days in seconds
2065
                    if ($now - $last_login_date_timestamp > 604800) {
2066
                        if ($convert_date) {
2067
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2068
                            $icon = null;
2069
                            if (api_is_allowed_to_edit()) {
2070
                                $url = api_get_path(WEB_CODE_PATH).
2071
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
2072
                                $icon = '<a href="'.$url.'" title="'.get_lang('RemindInactiveUser').'">
2073
                                  '.Display::return_icon('messagebox_warning.gif').'
2074
                                 </a>';
2075
                            }
2076
2077
                            return $icon.Display::label($last_login_date, 'warning');
2078
                        }
2079
2080
                        return $last_login_date;
2081
                    } else {
2082
                        if ($convert_date) {
2083
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2084
                        }
2085
2086
                        return $last_login_date;
2087
                    }
2088
                }
2089
            }
2090
        }
2091
2092
        return false;
2093
    }
2094
2095
    public static function getLastConnectionInAnyCourse($studentId)
2096
    {
2097
        $studentId = (int) $studentId;
2098
2099
        if (empty($studentId)) {
2100
            return false;
2101
        }
2102
2103
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2104
        $sql = "SELECT logout_course_date
2105
                FROM $table
2106
                WHERE user_id = $studentId
2107
                ORDER BY logout_course_date DESC
2108
                LIMIT 1";
2109
        $result = Database::query($sql);
2110
        if (Database::num_rows($result)) {
2111
            $row = Database::fetch_array($result);
2112
2113
            return $row['logout_course_date'];
2114
        }
2115
2116
        return false;
2117
    }
2118
2119
    /**
2120
     * Get last course access by course/session.
2121
     */
2122
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
2123
    {
2124
        $courseId = (int) $courseId;
2125
        $sessionId = (int) $sessionId;
2126
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2127
2128
        $sql = "SELECT logout_course_date
2129
                FROM $table
2130
                WHERE
2131
                        c_id = $courseId AND
2132
                        session_id = $sessionId
2133
                ORDER BY logout_course_date DESC
2134
                LIMIT 0,1";
2135
2136
        $result = Database::query($sql);
2137
        if (Database::num_rows($result)) {
2138
            $row = Database::fetch_array($result);
2139
            if ($row) {
2140
                return $row['logout_course_date'];
2141
            }
2142
        }
2143
2144
        return '';
2145
    }
2146
2147
    /**
2148
     * Get count of the connections to the course during a specified period.
2149
     *
2150
     * @param int $courseId
2151
     * @param   int     Session id (optional)
2152
     * @param   int     Datetime from which to collect data (defaults to 0)
2153
     * @param   int     Datetime to which to collect data (defaults to now)
2154
     *
2155
     * @return int count connections
2156
     */
2157
    public static function get_course_connections_count(
2158
        $courseId,
2159
        $session_id = 0,
2160
        $start = 0,
2161
        $stop = null
2162
    ) {
2163
        if ($start < 0) {
2164
            $start = 0;
2165
        }
2166
        if (!isset($stop) || $stop < 0) {
2167
            $stop = api_get_utc_datetime();
2168
        }
2169
2170
        // Given we're storing in cache, round the start and end times
2171
        // to the lower minute
2172
        $roundedStart = substr($start, 0, -2).'00';
2173
        $roundedStop = substr($stop, 0, -2).'00';
2174
        $roundedStart = Database::escape_string($roundedStart);
2175
        $roundedStop = Database::escape_string($roundedStop);
2176
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
2177
        $courseId = (int) $courseId;
2178
        $session_id = (int) $session_id;
2179
        $count = 0;
2180
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2181
        $sql = "SELECT count(*) as count_connections
2182
                FROM $table
2183
                WHERE
2184
                    c_id = $courseId AND
2185
                    session_id = $session_id
2186
                    $month_filter";
2187
2188
        //This query can be very slow (several seconds on an indexed table
2189
        // with 14M rows). As such, we'll try to use APCu if it is
2190
        // available to store the resulting value for a few seconds
2191
        $cacheAvailable = api_get_configuration_value('apc');
2192
        if ($cacheAvailable === true) {
2193
            $apc = apcu_cache_info(true);
2194
            $apc_end = $apc['start_time'] + $apc['ttl'];
2195
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
2196
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
2197
                apcu_fetch($apc_var) > 0
2198
            ) {
2199
                $count = apcu_fetch($apc_var);
2200
            } else {
2201
                $rs = Database::query($sql);
2202
                if (Database::num_rows($rs) > 0) {
2203
                    $row = Database::fetch_object($rs);
2204
                    $count = $row->count_connections;
2205
                }
2206
                apcu_clear_cache();
2207
                apcu_store($apc_var, $count, 60);
2208
            }
2209
        } else {
2210
            $rs = Database::query($sql);
2211
            if (Database::num_rows($rs) > 0) {
2212
                $row = Database::fetch_object($rs);
2213
                $count = $row->count_connections;
2214
            }
2215
        }
2216
2217
        return $count;
2218
    }
2219
2220
    /**
2221
     * Get count courses per student.
2222
     *
2223
     * @param int  $user_id          Student id
2224
     * @param bool $include_sessions Include sessions (optional)
2225
     *
2226
     * @return int count courses
2227
     */
2228
    public static function count_course_per_student($user_id, $include_sessions = true)
2229
    {
2230
        $user_id = (int) $user_id;
2231
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2232
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2233
2234
        $sql = 'SELECT DISTINCT c_id
2235
                FROM '.$tbl_course_rel_user.'
2236
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
2237
        $rs = Database::query($sql);
2238
        $nb_courses = Database::num_rows($rs);
2239
2240
        if ($include_sessions) {
2241
            $sql = 'SELECT DISTINCT c_id
2242
                    FROM '.$tbl_session_course_rel_user.'
2243
                    WHERE user_id = '.$user_id;
2244
            $rs = Database::query($sql);
2245
            $nb_courses += Database::num_rows($rs);
2246
        }
2247
2248
        return $nb_courses;
2249
    }
2250
2251
    /**
2252
     * Gets the score average from all tests in a course by student.
2253
     *
2254
     * @param $student_id
2255
     * @param $course_code
2256
     * @param int  $exercise_id
2257
     * @param null $session_id
2258
     * @param int  $active_filter 2 for consider all tests
2259
     *                            1 for active <> -1
2260
     *                            0 for active <> 0
2261
     * @param int  $into_lp       1 for all exercises
2262
     *                            0 for without LP
2263
     * @param mixed id
2264
     * @param string code
2265
     * @param int id (optional), filtered by exercise
2266
     * @param int id (optional), if param $session_id is null
2267
     *                                                it'll return results including sessions, 0 = session is not
2268
     *                                                filtered
2269
     *
2270
     * @return string value (number %) Which represents a round integer about the score average
2271
     */
2272
    public static function get_avg_student_exercise_score(
2273
        $student_id,
2274
        $course_code,
2275
        $exercise_id = 0,
2276
        $session_id = null,
2277
        $active_filter = 1,
2278
        $into_lp = 0
2279
    ) {
2280
        $course_code = Database::escape_string($course_code);
2281
        $course_info = api_get_course_info($course_code);
2282
        if (!empty($course_info)) {
2283
            // table definition
2284
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2285
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2286
2287
            // Compose a filter based on optional exercise given
2288
            $condition_quiz = "";
2289
            if (!empty($exercise_id)) {
2290
                $exercise_id = intval($exercise_id);
2291
                $condition_quiz = " AND id = $exercise_id ";
2292
            }
2293
2294
            // Compose a filter based on optional session id given
2295
            $condition_session = '';
2296
            if (isset($session_id)) {
2297
                $session_id = intval($session_id);
2298
                $condition_session = " AND session_id = $session_id ";
2299
            }
2300
            if ($active_filter == 1) {
2301
                $condition_active = 'AND active <> -1';
2302
            } elseif ($active_filter == 0) {
2303
                $condition_active = 'AND active <> 0';
2304
            } else {
2305
                $condition_active = '';
2306
            }
2307
            $condition_into_lp = '';
2308
            $select_lp_id = '';
2309
            if ($into_lp == 0) {
2310
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2311
            } else {
2312
                $select_lp_id = ', orig_lp_id as lp_id ';
2313
            }
2314
2315
            $sql = "SELECT count(iid)
2316
    		        FROM $tbl_course_quiz
2317
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2318
            $count_quiz = 0;
2319
            $countQuizResult = Database::query($sql);
2320
            if (!empty($countQuizResult)) {
2321
                $count_quiz = Database::fetch_row($countQuizResult);
2322
            }
2323
2324
            if (!empty($count_quiz[0]) && !empty($student_id)) {
2325
                if (is_array($student_id)) {
2326
                    $student_id = array_map('intval', $student_id);
2327
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2328
                } else {
2329
                    $student_id = intval($student_id);
2330
                    $condition_user = " AND exe_user_id = '$student_id' ";
2331
                }
2332
2333
                if (empty($exercise_id)) {
2334
                    $sql = "SELECT iid FROM $tbl_course_quiz
2335
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2336
                    $result = Database::query($sql);
2337
                    $exercise_list = [];
2338
                    $exercise_id = null;
2339
                    if (!empty($result) && Database::num_rows($result)) {
2340
                        while ($row = Database::fetch_array($result)) {
2341
                            $exercise_list[] = $row['iid'];
2342
                        }
2343
                    }
2344
                    if (!empty($exercise_list)) {
2345
                        $exercise_id = implode("','", $exercise_list);
2346
                    }
2347
                }
2348
2349
                $count_quiz = Database::fetch_row(Database::query($sql));
2350
                $sql = "SELECT
2351
                        SUM(exe_result/exe_weighting*100) as avg_score,
2352
                        COUNT(*) as num_attempts
2353
                        $select_lp_id
2354
                        FROM $tbl_stats_exercise
2355
                        WHERE
2356
                            exe_exo_id IN ('".$exercise_id."')
2357
                            $condition_user AND
2358
                            status = '' AND
2359
                            c_id = {$course_info['real_id']}
2360
                            $condition_session
2361
                            $condition_into_lp
2362
                        ORDER BY exe_date DESC";
2363
2364
                $res = Database::query($sql);
2365
                $row = Database::fetch_array($res);
2366
                $quiz_avg_score = null;
2367
2368
                if (!empty($row['avg_score'])) {
2369
                    $quiz_avg_score = round($row['avg_score'], 2);
2370
                }
2371
2372
                if (!empty($row['num_attempts'])) {
2373
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2374
                }
2375
                if (is_array($student_id)) {
2376
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2377
                }
2378
                if ($into_lp == 0) {
2379
                    return $quiz_avg_score;
2380
                } else {
2381
                    if (!empty($row['lp_id'])) {
2382
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2383
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2384
                        $sql = "SELECT lp.name
2385
                                FROM $tbl_lp as lp, $tbl_course as c
2386
                                WHERE
2387
                                    c.code = '$course_code' AND
2388
                                    lp.id = ".$row['lp_id']." AND
2389
                                    lp.c_id = c.id
2390
                                LIMIT 1;
2391
                        ";
2392
                        $result = Database::query($sql);
2393
                        $row_lp = Database::fetch_row($result);
2394
                        $lp_name = null;
2395
                        if ($row_lp && isset($row_lp[0])) {
2396
                            $lp_name = $row_lp[0];
2397
                        }
2398
2399
                        return [$quiz_avg_score, $lp_name];
2400
                    } else {
2401
                        return [$quiz_avg_score, null];
2402
                    }
2403
                }
2404
            }
2405
        }
2406
2407
        return null;
2408
    }
2409
2410
    /**
2411
     * Get count student's exercise COMPLETED attempts.
2412
     *
2413
     * @param int $student_id
2414
     * @param int $courseId
2415
     * @param int $exercise_id
2416
     * @param int $lp_id
2417
     * @param int $lp_item_id
2418
     * @param int $session_id
2419
     * @param int $find_all_lp 0 = just LP specified
2420
     *                         1 = LP specified or whitout LP,
2421
     *                         2 = all rows
2422
     *
2423
     * @internal param \Student $int id
2424
     * @internal param \Course $string code
2425
     * @internal param \Exercise $int id
2426
     * @internal param \Learning $int path id (optional),
2427
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2428
     * @internal param \Learning $int path item id (optional),
2429
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2430
     *
2431
     * @return int count of attempts
2432
     */
2433
    public static function count_student_exercise_attempts(
2434
        $student_id,
2435
        $courseId,
2436
        $exercise_id,
2437
        $lp_id = 0,
2438
        $lp_item_id = 0,
2439
        $session_id = 0,
2440
        $find_all_lp = 0
2441
    ) {
2442
        $courseId = intval($courseId);
2443
        $student_id = intval($student_id);
2444
        $exercise_id = intval($exercise_id);
2445
        $session_id = intval($session_id);
2446
2447
        $lp_id = intval($lp_id);
2448
        $lp_item_id = intval($lp_item_id);
2449
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2450
2451
        $sql = "SELECT COUNT(ex.exe_id) as essais
2452
                FROM $tbl_stats_exercises AS ex
2453
                WHERE
2454
                    ex.c_id = $courseId AND
2455
                    ex.exe_exo_id = $exercise_id AND
2456
                    status = '' AND
2457
                    exe_user_id= $student_id AND
2458
                    session_id = $session_id ";
2459
2460
        if ($find_all_lp == 1) {
2461
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2462
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2463
        } elseif ($find_all_lp == 0) {
2464
            $sql .= "AND orig_lp_id = $lp_id
2465
                AND orig_lp_item_id = $lp_item_id";
2466
        }
2467
2468
        $rs = Database::query($sql);
2469
        $row = Database::fetch_row($rs);
2470
        $count_attempts = $row[0];
2471
2472
        return $count_attempts;
2473
    }
2474
2475
    /**
2476
     * Get count student's exercise progress.
2477
     *
2478
     * @param array $exercise_list
2479
     * @param int   $user_id
2480
     * @param int   $courseId
2481
     * @param int   $session_id
2482
     *
2483
     * @return string
2484
     */
2485
    public static function get_exercise_student_progress(
2486
        $exercise_list,
2487
        $user_id,
2488
        $courseId,
2489
        $session_id
2490
    ) {
2491
        $courseId = (int) $courseId;
2492
        $user_id = (int) $user_id;
2493
        $session_id = (int) $session_id;
2494
2495
        if (empty($exercise_list)) {
2496
            return '0%';
2497
        }
2498
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2499
        $exercise_list = array_keys($exercise_list);
2500
        $exercise_list = array_map('intval', $exercise_list);
2501
2502
        $exercise_list_imploded = implode("' ,'", $exercise_list);
2503
2504
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
2505
                FROM $tbl_stats_exercises AS ex
2506
                WHERE
2507
                    ex.c_id = $courseId AND
2508
                    ex.session_id  = $session_id AND
2509
                    ex.exe_user_id = $user_id AND
2510
                    ex.status = '' AND
2511
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
2512
2513
        $rs = Database::query($sql);
2514
        $count = 0;
2515
        if ($rs) {
2516
            $row = Database::fetch_row($rs);
2517
            $count = $row[0];
2518
        }
2519
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
2520
2521
        return $count;
2522
    }
2523
2524
    /**
2525
     * @param array $exercise_list
2526
     * @param int   $user_id
2527
     * @param int   $courseId
2528
     * @param int   $session_id
2529
     *
2530
     * @return string
2531
     */
2532
    public static function get_exercise_student_average_best_attempt(
2533
        $exercise_list,
2534
        $user_id,
2535
        $courseId,
2536
        $session_id
2537
    ) {
2538
        $result = 0;
2539
        if (!empty($exercise_list)) {
2540
            foreach ($exercise_list as $exercise_data) {
2541
                $exercise_id = $exercise_data['iid'];
2542
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2543
                    $user_id,
2544
                    $exercise_id,
2545
                    $courseId,
2546
                    $session_id
2547
                );
2548
2549
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
2550
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
2551
                }
2552
            }
2553
            $result = $result / count($exercise_list);
2554
            $result = round($result, 2) * 100;
2555
        }
2556
2557
        return $result.'%';
2558
    }
2559
2560
    /**
2561
     * Returns the average student progress in the learning paths of the given
2562
     * course, it will take into account the progress that were not started.
2563
     *
2564
     * @param int|array $studentId
2565
     * @param string    $courseCode
2566
     * @param array     $lpIdList        Limit average to listed lp ids
2567
     * @param int       $sessionId       Session id (optional),
2568
     *                                   if parameter $session_id is null(default) it'll return results including
2569
     *                                   sessions, 0 = session is not filtered
2570
     * @param bool      $returnArray     Will return an array of the type:
2571
     *                                   [sum_of_progresses, number] if it is set to true
2572
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2573
     *
2574
     * @return float Average progress of the user in this course from 0 to 100
2575
     */
2576
    public static function get_avg_student_progress(
2577
        $studentId,
2578
        $courseCode = null,
2579
        $lpIdList = [],
2580
        $sessionId = null,
2581
        $returnArray = false,
2582
        $onlySeriousGame = false
2583
    ) {
2584
        // If there is at least one learning path and one student.
2585
        if (empty($studentId)) {
2586
            return false;
2587
        }
2588
2589
        $sessionId = (int) $sessionId;
2590
        $courseInfo = api_get_course_info($courseCode);
2591
2592
        if (empty($courseInfo)) {
2593
            return false;
2594
        }
2595
2596
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2597
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2598
        $lpConditions = [];
2599
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2600
2601
        if ($sessionId > 0) {
2602
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2603
        } else {
2604
            $lpConditions['AND session_id = ?'] = $sessionId;
2605
        }
2606
2607
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2608
            $placeHolders = [];
2609
            for ($i = 0; $i < count($lpIdList); $i++) {
2610
                $placeHolders[] = '?';
2611
            }
2612
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
2613
        }
2614
2615
        if ($onlySeriousGame) {
2616
            $lpConditions['AND seriousgame_mode = ? '] = true;
2617
        }
2618
2619
        $resultLP = Database::select(
2620
            'id',
2621
            $lPTable,
2622
            ['where' => $lpConditions]
2623
        );
2624
        $filteredLP = array_keys($resultLP);
2625
2626
        if (empty($filteredLP)) {
2627
            return false;
2628
        }
2629
2630
        $conditions = [
2631
            " c_id = {$courseInfo['real_id']} ",
2632
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
2633
        ];
2634
2635
        $groupBy = 'GROUP BY lp_id';
2636
2637
        if (is_array($studentId)) {
2638
            $studentId = array_map('intval', $studentId);
2639
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
2640
        } else {
2641
            $studentId = (int) $studentId;
2642
            $conditions[] = " lp_view.user_id = '$studentId' ";
2643
2644
            if (empty($lpIdList)) {
2645
                $lpList = new LearnpathList(
2646
                    $studentId,
2647
                    $courseInfo,
2648
                    $sessionId,
2649
                    null,
2650
                    false,
2651
                    null,
2652
                    true
2653
                );
2654
                $lpList = $lpList->get_flat_list();
2655
                if (!empty($lpList)) {
2656
                    /** @var $lp */
2657
                    foreach ($lpList as $lpId => $lp) {
2658
                        $lpIdList[] = $lp['lp_old_id'];
2659
                    }
2660
                }
2661
            }
2662
        }
2663
2664
        if (!empty($sessionId)) {
2665
            $conditions[] = " session_id = $sessionId ";
2666
        } else {
2667
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
2668
        }
2669
2670
        $conditionToString = implode('AND', $conditions);
2671
        $sql = "SELECT lp_id, view_count, progress
2672
                FROM $lpViewTable lp_view
2673
                WHERE
2674
                    $conditionToString
2675
                    $groupBy
2676
                ORDER BY view_count DESC";
2677
2678
        $result = Database::query($sql);
2679
2680
        $progress = [];
2681
        $viewCount = [];
2682
        while ($row = Database::fetch_array($result, 'ASSOC')) {
2683
            if (!isset($viewCount[$row['lp_id']])) {
2684
                $progress[$row['lp_id']] = $row['progress'];
2685
            }
2686
            $viewCount[$row['lp_id']] = $row['view_count'];
2687
        }
2688
2689
        // Fill with lp ids
2690
        $newProgress = [];
2691
        if (!empty($lpIdList)) {
2692
            foreach ($lpIdList as $lpId) {
2693
                if (isset($progress[$lpId])) {
2694
                    $newProgress[] = $progress[$lpId];
2695
                }
2696
            }
2697
            $total = count($lpIdList);
2698
        } else {
2699
            $newProgress = $progress;
2700
            $total = count($newProgress);
2701
        }
2702
2703
        $average = 0;
2704
        $sum = 0;
2705
        if (!empty($newProgress)) {
2706
            $sum = array_sum($newProgress);
2707
            $average = $sum / $total;
2708
        }
2709
2710
        if ($returnArray) {
2711
            return [
2712
                $sum,
2713
                $total,
2714
            ];
2715
        }
2716
2717
        return round($average, 1);
2718
    }
2719
2720
    /**
2721
     * This function gets:
2722
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2723
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2724
     * 3. And finally it will return the average between 1. and 2.
2725
     *
2726
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2727
     * This function does not take the results of a Test out of a LP
2728
     *
2729
     * @param mixed  $student_id                      Array of user ids or an user id
2730
     * @param string $course_code
2731
     * @param array  $lp_ids                          List of LP ids
2732
     * @param int    $session_id                      Session id (optional),
2733
     *                                                if param $session_id is null(default) it'll return results
2734
     *                                                including sessions, 0 = session is not filtered
2735
     * @param bool   $return_array                    Returns an array of the
2736
     *                                                type [sum_score, num_score] if set to true
2737
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2738
     * @param bool   $getOnlyBestAttempt
2739
     *
2740
     * @return string value (number %) Which represents a round integer explain in got in 3
2741
     */
2742
    public static function get_avg_student_score(
2743
        $student_id,
2744
        $course_code,
2745
        $lp_ids = [],
2746
        $session_id = null,
2747
        $return_array = false,
2748
        $get_only_latest_attempt_results = false,
2749
        $getOnlyBestAttempt = false
2750
    ) {
2751
        $debug = false;
2752
        if ($debug) {
2753
            echo '<h1>Tracking::get_avg_student_score</h1>';
2754
        }
2755
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2756
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2757
        $course = api_get_course_info($course_code);
2758
2759
        if (empty($course)) {
2760
            return null;
2761
        }
2762
2763
        // Get course tables names
2764
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
2765
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
2766
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
2767
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
2768
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2769
        $course_id = $course['real_id'];
2770
2771
        // Compose a filter based on optional learning paths list given
2772
        $condition_lp = '';
2773
        if (count($lp_ids) > 0) {
2774
            $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
2775
        }
2776
2777
        // Compose a filter based on optional session id
2778
        $session_id = (int) $session_id;
2779
        if (count($lp_ids) > 0) {
2780
            $condition_session = " AND session_id = $session_id ";
2781
        } else {
2782
            $condition_session = " WHERE session_id = $session_id ";
2783
        }
2784
2785
        // Check the real number of LPs corresponding to the filter in the
2786
        // database (and if no list was given, get them all)
2787
        if (empty($session_id)) {
2788
            $sql = "SELECT DISTINCT(id), use_max_score
2789
                    FROM $lp_table
2790
                    WHERE
2791
                        c_id = $course_id AND
2792
                        (session_id = 0 OR session_id IS NULL) $condition_lp ";
2793
        } else {
2794
            $sql = "SELECT DISTINCT(id), use_max_score
2795
                    FROM $lp_table
2796
                    WHERE c_id = $course_id $condition_lp ";
2797
        }
2798
2799
        $res_row_lp = Database::query($sql);
2800
        $count_row_lp = Database::num_rows($res_row_lp);
2801
2802
        $lp_list = $use_max_score = [];
2803
        while ($row_lp = Database::fetch_array($res_row_lp)) {
2804
            $lp_list[] = $row_lp['id'];
2805
            $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
2806
        }
2807
2808
        // prepare filter on users
2809
        if (is_array($student_id)) {
2810
            array_walk($student_id, 'intval');
2811
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
2812
        } else {
2813
            $condition_user1 = " AND user_id = $student_id ";
2814
        }
2815
2816
        if (empty($count_row_lp) || empty($student_id)) {
2817
            return null;
2818
        }
2819
2820
        // Getting latest LP result for a student
2821
        //@todo problem when a  course have more than 1500 users
2822
        $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
2823
                FROM $lp_view_table
2824
                WHERE
2825
                    c_id = $course_id AND
2826
                    lp_id IN (".implode(',', $lp_list).")
2827
                    $condition_user1 AND
2828
                    session_id = $session_id
2829
                GROUP BY lp_id, user_id";
2830
2831
        $rs_last_lp_view_id = Database::query($sql);
2832
        $global_result = 0;
2833
2834
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
2835
            // Cycle through each line of the results (grouped by lp_id, user_id)
2836
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2837
                $count_items = 0;
2838
                $lpPartialTotal = 0;
2839
                $list = [];
2840
                $lp_view_id = $row_lp_view['id'];
2841
                $lp_id = $row_lp_view['lp_id'];
2842
                $user_id = $row_lp_view['user_id'];
2843
2844
                if ($debug) {
2845
                    echo '<h2>LP id '.$lp_id.'</h2>';
2846
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
2847
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
2848
                }
2849
2850
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
2851
                    // Getting lp_items done by the user
2852
                    $sql = "SELECT DISTINCT lp_item_id
2853
                            FROM $lp_item_view_table
2854
                            WHERE
2855
                                c_id = $course_id AND
2856
                                lp_view_id = $lp_view_id
2857
                            ORDER BY lp_item_id";
2858
                    $res_lp_item = Database::query($sql);
2859
2860
                    while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
2861
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
2862
                        $order = ' view_count DESC';
2863
                        if ($getOnlyBestAttempt) {
2864
                            $order = ' lp_iv.score DESC';
2865
                        }
2866
2867
                        // Getting the most recent attempt
2868
                        $sql = "SELECT
2869
                                    lp_iv.id as lp_item_view_id,
2870
                                    lp_iv.score as score,
2871
                                    lp_i.max_score,
2872
                                    lp_iv.max_score as max_score_item_view,
2873
                                    lp_i.path,
2874
                                    lp_i.item_type,
2875
                                    lp_i.id as iid
2876
                                FROM $lp_item_view_table as lp_iv
2877
                                INNER JOIN $lp_item_table as lp_i
2878
                                ON (
2879
                                    lp_i.id = lp_iv.lp_item_id AND
2880
                                    lp_iv.c_id = lp_i.c_id
2881
                                )
2882
                                WHERE
2883
                                    lp_iv.c_id = $course_id AND
2884
                                    lp_i.c_id  = $course_id AND
2885
                                    lp_item_id = $my_lp_item_id AND
2886
                                    lp_view_id = $lp_view_id AND
2887
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2888
                                ORDER BY $order
2889
                                LIMIT 1";
2890
2891
                        $res_lp_item_result = Database::query($sql);
2892
                        while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
2893
                            $list[] = $row_max_score;
2894
                        }
2895
                    }
2896
                } else {
2897
                    // For the currently analysed view, get the score and
2898
                    // max_score of each item if it is a sco or a TOOL_QUIZ
2899
                    $sql = "SELECT
2900
                                lp_iv.id as lp_item_view_id,
2901
                                lp_iv.score as score,
2902
                                lp_i.max_score,
2903
                                lp_iv.max_score as max_score_item_view,
2904
                                lp_i.path,
2905
                                lp_i.item_type,
2906
                                lp_i.id as iid
2907
                              FROM $lp_item_view_table as lp_iv
2908
                              INNER JOIN $lp_item_table as lp_i
2909
                              ON lp_i.id = lp_iv.lp_item_id AND
2910
                                 lp_iv.c_id = lp_i.c_id
2911
                              WHERE
2912
                                lp_iv.c_id = $course_id AND
2913
                                lp_i.c_id  = $course_id AND
2914
                                lp_view_id = $lp_view_id AND
2915
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2916
                            ";
2917
                    $res_max_score = Database::query($sql);
2918
                    while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
2919
                        $list[] = $row_max_score;
2920
                    }
2921
                }
2922
2923
                // Go through each scorable element of this view
2924
                $score_of_scorm_calculate = 0;
2925
                foreach ($list as $row_max_score) {
2926
                    // Came from the original lp_item
2927
                    $max_score = $row_max_score['max_score'];
2928
                    // Came from the lp_item_view
2929
                    $max_score_item_view = $row_max_score['max_score_item_view'];
2930
                    $score = $row_max_score['score'];
2931
                    if ($debug) {
2932
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
2933
                    }
2934
2935
                    if ($row_max_score['item_type'] === 'sco') {
2936
                        /* Check if it is sco (easier to get max_score)
2937
                           when there's no max score, we assume 100 as the max score,
2938
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
2939
                        */
2940
                        if ($max_score == 0 || is_null($max_score) || $max_score == '') {
2941
                            // Chamilo style
2942
                            if ($use_max_score[$lp_id]) {
2943
                                $max_score = 100;
2944
                            } else {
2945
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
2946
                                $max_score = $max_score_item_view;
2947
                            }
2948
                        }
2949
                        // Avoid division by zero errors
2950
                        if (!empty($max_score)) {
2951
                            $lpPartialTotal += $score / $max_score;
2952
                        }
2953
                        if ($debug) {
2954
                            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...
2955
                            var_dump("score: $score");
2956
                            var_dump("max_score: $max_score");
2957
                        }
2958
                    } else {
2959
                        // Case of a TOOL_QUIZ element
2960
                        $item_id = $row_max_score['iid'];
2961
                        $item_path = $row_max_score['path'];
2962
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
2963
2964
                        if (empty($lp_item_view_id)) {
2965
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
2966
                        } else {
2967
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
2968
                        }
2969
2970
                        // Get last attempt to this exercise through
2971
                        // the current lp for the current user
2972
                        $order = 'exe_date DESC';
2973
                        if ($getOnlyBestAttempt) {
2974
                            $order = 'exe_result DESC';
2975
                        }
2976
                        $sql = "SELECT exe_id, exe_result
2977
                                FROM $tbl_stats_exercices
2978
                                WHERE
2979
                                    exe_exo_id = '$item_path' AND
2980
                                    exe_user_id = $user_id AND
2981
                                    orig_lp_item_id = $item_id AND
2982
                                    $lpItemCondition AND
2983
                                    c_id = $course_id AND
2984
                                    session_id = $session_id AND
2985
                                    status = ''
2986
                                ORDER BY $order
2987
                                LIMIT 1";
2988
2989
                        $result_last_attempt = Database::query($sql);
2990
                        $num = Database::num_rows($result_last_attempt);
2991
                        if ($num > 0) {
2992
                            $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
2993
                            $id_last_attempt = (int) $attemptResult['exe_id'];
2994
                            // We overwrite the score with the best one not the one saved in the LP (latest)
2995
                            if ($getOnlyBestAttempt && $get_only_latest_attempt_results == false) {
2996
                                if ($debug) {
2997
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
2998
                                }
2999
                                $score = $attemptResult['exe_result'];
3000
                            }
3001
3002
                            if ($debug) {
3003
                                echo "Attempt id: $id_last_attempt with score $score<br />";
3004
                            }
3005
                            // Within the last attempt number tracking, get the sum of
3006
                            // the max_scores of all questions that it was
3007
                            // made of (we need to make this call dynamic because of random questions selection)
3008
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
3009
                                    (
3010
                                        SELECT DISTINCT
3011
                                            question_id,
3012
                                            marks,
3013
                                            ponderation
3014
                                        FROM $tbl_stats_attempts AS at
3015
                                        INNER JOIN $tbl_quiz_questions AS q
3016
                                        ON q.iid = at.question_id
3017
                                        WHERE
3018
                                            exe_id = $id_last_attempt AND
3019
                                            at.c_id = $course_id
3020
                                    )
3021
                                    AS t";
3022
3023
                            $res_max_score_bis = Database::query($sql);
3024
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
3025
3026
                            if (!empty($row_max_score_bis['maxscore'])) {
3027
                                $max_score = $row_max_score_bis['maxscore'];
3028
                            }
3029
                            if (!empty($max_score) && floatval($max_score) > 0) {
3030
                                $lpPartialTotal += $score / $max_score;
3031
                            }
3032
                            if ($debug) {
3033
                                var_dump("score: $score");
3034
                                var_dump("max_score: $max_score");
3035
                                var_dump("lpPartialTotal: $lpPartialTotal");
3036
                            }
3037
                        }
3038
                    }
3039
3040
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
3041
                        // Normal way
3042
                        if ($use_max_score[$lp_id]) {
3043
                            $count_items++;
3044
                        } else {
3045
                            if ($max_score != '') {
3046
                                $count_items++;
3047
                            }
3048
                        }
3049
                        if ($debug) {
3050
                            echo '$count_items: '.$count_items;
3051
                        }
3052
                    }
3053
                } //end for
3054
3055
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
3056
                $global_result += $score_of_scorm_calculate;
3057
3058
                if ($debug) {
3059
                    var_dump("count_items: $count_items");
3060
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
3061
                    var_dump("global_result: $global_result");
3062
                }
3063
            } // end while
3064
        }
3065
3066
        $lp_with_quiz = 0;
3067
        foreach ($lp_list as $lp_id) {
3068
            // Check if LP have a score we assume that all SCO have an score
3069
            $sql = "SELECT count(id) as count
3070
                    FROM $lp_item_table
3071
                    WHERE
3072
                        c_id = $course_id AND
3073
                        (item_type = 'quiz' OR item_type = 'sco') AND
3074
                        lp_id = ".$lp_id;
3075
            $result_have_quiz = Database::query($sql);
3076
            if (Database::num_rows($result_have_quiz) > 0) {
3077
                $row = Database::fetch_array($result_have_quiz, 'ASSOC');
3078
                if (is_numeric($row['count']) && $row['count'] != 0) {
3079
                    $lp_with_quiz++;
3080
                }
3081
            }
3082
        }
3083
3084
        if ($debug) {
3085
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
3086
        }
3087
        if ($debug) {
3088
            echo '<h3>Final return</h3>';
3089
        }
3090
3091
        if ($lp_with_quiz != 0) {
3092
            if (!$return_array) {
3093
                $score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
3094
                if ($debug) {
3095
                    var_dump($score_of_scorm_calculate);
3096
                }
3097
                if (empty($lp_ids)) {
3098
                    if ($debug) {
3099
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
3100
                    }
3101
                }
3102
3103
                return $score_of_scorm_calculate;
3104
            }
3105
3106
            if ($debug) {
3107
                var_dump($global_result, $lp_with_quiz);
3108
            }
3109
3110
            return [$global_result, $lp_with_quiz];
3111
        }
3112
3113
        return '-';
3114
    }
3115
3116
    /**
3117
     * This function gets:
3118
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3119
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3120
     * 3. And finally it will return the average between 1. and 2.
3121
     * This function does not take the results of a Test out of a LP.
3122
     *
3123
     * @param int|array $student_id  Array of user ids or an user id
3124
     * @param string    $course_code Course code
3125
     * @param array     $lp_ids      List of LP ids
3126
     * @param int       $session_id  Session id (optional), if param $session_id is 0(default)
3127
     *                               it'll return results including sessions, 0 = session is not filtered
3128
     *
3129
     * @return string value (number %) Which represents a round integer explain in got in 3
3130
     */
3131
    public static function getAverageStudentScore(
3132
        $student_id,
3133
        $course_code = '',
3134
        $lp_ids = [],
3135
        $session_id = 0
3136
    ) {
3137
        if (empty($student_id)) {
3138
            return 0;
3139
        }
3140
3141
        $conditions = [];
3142
        if (!empty($course_code)) {
3143
            $course = api_get_course_info($course_code);
3144
            $courseId = $course['real_id'];
3145
            $conditions[] = " lp.c_id = $courseId";
3146
        }
3147
3148
        // Get course tables names
3149
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3150
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3151
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3152
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3153
3154
        // Compose a filter based on optional learning paths list given
3155
        if (!empty($lp_ids) && count($lp_ids) > 0) {
3156
            $conditions[] = ' lp.id IN ('.implode(',', $lp_ids).') ';
3157
        }
3158
3159
        // Compose a filter based on optional session id
3160
        $session_id = (int) $session_id;
3161
        if (!empty($session_id)) {
3162
            $conditions[] = " lp_view.session_id = $session_id ";
3163
        }
3164
3165
        if (is_array($student_id)) {
3166
            array_walk($student_id, 'intval');
3167
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
3168
        } else {
3169
            $student_id = (int) $student_id;
3170
            $conditions[] = " lp_view.user_id = $student_id ";
3171
        }
3172
3173
        $conditionsToString = implode(' AND ', $conditions);
3174
        $sql = "SELECT
3175
                    SUM(lp_iv.score) sum_score,
3176
                    SUM(lp_i.max_score) sum_max_score
3177
                FROM $lp_table as lp
3178
                INNER JOIN $lp_item_table as lp_i
3179
                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
3180
                INNER JOIN $lp_view_table as lp_view
3181
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
3182
                INNER JOIN $lp_item_view_table as lp_iv
3183
                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
3184
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
3185
                $conditionsToString
3186
        ";
3187
        $result = Database::query($sql);
3188
        $row = Database::fetch_array($result, 'ASSOC');
3189
3190
        if (empty($row['sum_max_score'])) {
3191
            return 0;
3192
        }
3193
3194
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
3195
    }
3196
3197
    /**
3198
     * This function gets time spent in learning path for a student inside a course.
3199
     *
3200
     * @param int|array $student_id  Student id(s)
3201
     * @param string    $course_code Course code
3202
     * @param array     $lp_ids      Limit average to listed lp ids
3203
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
3204
     *                               it'll return results including sessions, 0 = session is not filtered
3205
     *
3206
     * @return int Total time in seconds
3207
     */
3208
    public static function get_time_spent_in_lp(
3209
        $student_id,
3210
        $course_code,
3211
        $lp_ids = [],
3212
        $session_id = 0
3213
    ) {
3214
        $course = api_get_course_info($course_code);
3215
        $student_id = (int) $student_id;
3216
        $session_id = (int) $session_id;
3217
        $total_time = 0;
3218
3219
        if (!empty($course)) {
3220
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
3221
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
3222
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3223
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3224
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3225
            $course_id = $course['real_id'];
3226
3227
            // Compose a filter based on optional learning paths list given
3228
            $condition_lp = '';
3229
            if (count($lp_ids) > 0) {
3230
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3231
            }
3232
3233
            // Check the real number of LPs corresponding to the filter in the
3234
            // database (and if no list was given, get them all)
3235
            $sql = "SELECT DISTINCT(id) FROM $lpTable
3236
                    WHERE c_id = $course_id $condition_lp";
3237
            $result = Database::query($sql);
3238
            $session_condition = api_get_session_condition($session_id);
3239
3240
            // calculates time
3241
            if (Database::num_rows($result) > 0) {
3242
                while ($row = Database::fetch_array($result)) {
3243
                    $lp_id = (int) $row['id'];
3244
3245
                    // Start Exercise in LP total_time
3246
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
3247
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
3248
                    foreach ($list as $itemId) {
3249
                        $sql = "SELECT max(view_count)
3250
                                FROM $lpViewTable
3251
                                WHERE
3252
                                    c_id = $course_id AND
3253
                                    lp_id = $lp_id AND
3254
                                    user_id = $student_id
3255
                                    $session_condition";
3256
                        $res = Database::query($sql);
3257
                        $view = '';
3258
                        if (Database::num_rows($res) > 0) {
3259
                            $myrow = Database::fetch_array($res);
3260
                            $view = $myrow[0];
3261
                        }
3262
                        $viewCondition = null;
3263
                        if (!empty($view)) {
3264
                            $viewCondition = " AND v.view_count = $view  ";
3265
                        }
3266
                        $sql = "SELECT
3267
                            iv.iid,
3268
                            iv.total_time as mytime,
3269
                            i.id as myid,
3270
                            iv.view_count as iv_view_count,
3271
                            path
3272
                        FROM $lpItemTable as i
3273
                        INNER JOIN $lpItemViewTable as iv
3274
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
3275
                        INNER JOIN $lpViewTable as v
3276
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
3277
                        WHERE
3278
                            v.c_id = $course_id AND
3279
                            i.id = $itemId AND
3280
                            i.lp_id = $lp_id  AND
3281
                            v.user_id = $student_id AND
3282
                            item_type = 'quiz' AND
3283
                            path <> '' AND
3284
                            v.session_id = $session_id
3285
                            $viewCondition
3286
                        ORDER BY iv.view_count DESC ";
3287
3288
                        $resultRow = Database::query($sql);
3289
                        if (Database::num_rows($resultRow)) {
3290
                            $row = Database::fetch_array($resultRow);
3291
                            $totalTimeInLpItemView = $row['mytime'];
3292
                            $lpItemViewId = $row['iid'];
3293
3294
                            $sql = 'SELECT SUM(exe_duration) exe_duration
3295
                                    FROM '.$trackExercises.'
3296
                                    WHERE
3297
                                        exe_exo_id="'.$row['path'].'" AND
3298
                                        exe_user_id="'.$student_id.'" AND
3299
                                        orig_lp_id = "'.$lp_id.'" AND
3300
                                        orig_lp_item_id = "'.$row['myid'].'" AND
3301
                                        c_id = '.$course_id.' AND
3302
                                        status <> "incomplete" AND
3303
                                        session_id = '.$session_id.'
3304
                                     ORDER BY exe_date DESC ';
3305
3306
                            $sumScoreResult = Database::query($sql);
3307
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
3308
                            if (!empty($durationRow['exe_duration'])) {
3309
                                $exeDuration = $durationRow['exe_duration'];
3310
                                if ($exeDuration != $totalTimeInLpItemView &&
3311
                                    !empty($lpItemViewId) &&
3312
                                    !empty($exeDuration)
3313
                                ) {
3314
                                    // Update c_lp_item_view.total_time
3315
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration'
3316
                                                  WHERE iid = ".$lpItemViewId;
3317
                                    Database::query($sqlUpdate);
3318
                                }
3319
                            }
3320
                        }
3321
                    }
3322
3323
                    // End total_time fix
3324
3325
                    // Calculate total time
3326
                    $sql = "SELECT SUM(total_time)
3327
                            FROM $lpItemViewTable AS item_view
3328
                            INNER JOIN $lpViewTable AS view
3329
                            ON (
3330
                                item_view.lp_view_id = view.id AND
3331
                                item_view.c_id = view.c_id
3332
                            )
3333
                            WHERE
3334
                                item_view.c_id = $course_id AND
3335
                                view.c_id = $course_id AND
3336
                                view.lp_id = $lp_id AND
3337
                                view.user_id = $student_id AND
3338
                                session_id = $session_id";
3339
3340
                    $rs = Database::query($sql);
3341
                    if (Database::num_rows($rs) > 0) {
3342
                        $total_time += Database::result($rs, 0, 0);
3343
                    }
3344
                }
3345
            }
3346
        }
3347
3348
        return $total_time;
3349
    }
3350
3351
    /**
3352
     * This function gets last connection time to one learning path.
3353
     *
3354
     * @param int|array $student_id  Student id(s)
3355
     * @param string    $course_code Course code
3356
     * @param int       $lp_id       Learning path id
3357
     * @param int       $session_id
3358
     *
3359
     * @return int last connection timestamp
3360
     */
3361
    public static function get_last_connection_time_in_lp(
3362
        $student_id,
3363
        $course_code,
3364
        $lp_id,
3365
        $session_id = 0
3366
    ) {
3367
        $course = api_get_course_info($course_code);
3368
3369
        if (empty($course)) {
3370
            return 0;
3371
        }
3372
3373
        $course_id = $course['real_id'];
3374
        $student_id = (int) $student_id;
3375
        $lp_id = (int) $lp_id;
3376
        $session_id = (int) $session_id;
3377
        $lastTime = 0;
3378
3379
        // Use new system
3380
        if (self::minimumTimeAvailable($session_id, $course_id)) {
3381
            $sql = "SELECT MAX(date_reg) max
3382
                    FROM track_e_access_complete
3383
                    WHERE
3384
                        user_id = $student_id AND
3385
                        c_id = $course_id AND
3386
                        session_id = $session_id AND
3387
                        tool = 'learnpath' AND
3388
                        tool_id = $lp_id AND
3389
                        action = 'view' AND
3390
                        login_as = 0
3391
                    ORDER BY date_reg ASC
3392
                    LIMIT 1";
3393
            $rs = Database::query($sql);
3394
3395
            $lastConnection = 0;
3396
            if (Database::num_rows($rs) > 0) {
3397
                $value = Database::fetch_array($rs);
3398
                if (isset($value['max']) && !empty($value['max'])) {
3399
                    $lastConnection = api_strtotime($value['max'], 'UTC');
3400
                }
3401
            }
3402
3403
            if (!empty($lastConnection)) {
3404
                return $lastConnection;
3405
            }
3406
        }
3407
3408
        if (!empty($course)) {
3409
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3410
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3411
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3412
3413
            // Check the real number of LPs corresponding to the filter in the
3414
            // database (and if no list was given, get them all)
3415
            $sql = "SELECT id FROM $lp_table
3416
                    WHERE c_id = $course_id AND id = $lp_id ";
3417
            $row = Database::query($sql);
3418
            $count = Database::num_rows($row);
3419
3420
            // calculates last connection time
3421
            if ($count > 0) {
3422
                $sql = 'SELECT MAX(start_time)
3423
                        FROM '.$t_lpiv.' AS item_view
3424
                        INNER JOIN '.$t_lpv.' AS view
3425
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3426
                        WHERE
3427
                            status != "not attempted" AND
3428
                            item_view.c_id = '.$course_id.' AND
3429
                            view.c_id = '.$course_id.' AND
3430
                            view.lp_id = '.$lp_id.' AND
3431
                            view.user_id = '.$student_id.' AND
3432
                            view.session_id = '.$session_id;
3433
                $rs = Database::query($sql);
3434
                if (Database::num_rows($rs) > 0) {
3435
                    $lastTime = Database::result($rs, 0, 0);
3436
                }
3437
            }
3438
        }
3439
3440
        return $lastTime;
3441
    }
3442
3443
    public static function getFirstConnectionTimeInLp(
3444
        $student_id,
3445
        $course_code,
3446
        $lp_id,
3447
        $session_id = 0
3448
    ) {
3449
        $course = api_get_course_info($course_code);
3450
        $student_id = (int) $student_id;
3451
        $lp_id = (int) $lp_id;
3452
        $session_id = (int) $session_id;
3453
        $time = 0;
3454
3455
        if (!empty($course)) {
3456
            $course_id = $course['real_id'];
3457
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3458
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3459
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3460
3461
            // Check the real number of LPs corresponding to the filter in the
3462
            // database (and if no list was given, get them all)
3463
            $sql = "SELECT id FROM $lp_table
3464
                    WHERE c_id = $course_id AND id = $lp_id ";
3465
            $row = Database::query($sql);
3466
            $count = Database::num_rows($row);
3467
3468
            // calculates first connection time
3469
            if ($count > 0) {
3470
                $sql = 'SELECT MIN(start_time)
3471
                        FROM '.$t_lpiv.' AS item_view
3472
                        INNER JOIN '.$t_lpv.' AS view
3473
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3474
                        WHERE
3475
                            status != "not attempted" AND
3476
                            item_view.c_id = '.$course_id.' AND
3477
                            view.c_id = '.$course_id.' AND
3478
                            view.lp_id = '.$lp_id.' AND
3479
                            view.user_id = '.$student_id.' AND
3480
                            view.session_id = '.$session_id;
3481
                $rs = Database::query($sql);
3482
                if (Database::num_rows($rs) > 0) {
3483
                    $time = Database::result($rs, 0, 0);
3484
                }
3485
            }
3486
        }
3487
3488
        return $time;
3489
    }
3490
3491
    /**
3492
     * gets the list of students followed by coach.
3493
     *
3494
     * @param int $coach_id Coach id
3495
     *
3496
     * @return array List of students
3497
     */
3498
    public static function get_student_followed_by_coach($coach_id)
3499
    {
3500
        $coach_id = intval($coach_id);
3501
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3502
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3503
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3504
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3505
3506
        $students = [];
3507
        // At first, courses where $coach_id is coach of the course //
3508
        $sql = 'SELECT session_id, c_id
3509
                FROM '.$tbl_session_course_user.'
3510
                WHERE user_id='.$coach_id.' AND status=2';
3511
3512
        if (api_is_multiple_url_enabled()) {
3513
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3514
            $access_url_id = api_get_current_access_url_id();
3515
            if (-1 != $access_url_id) {
3516
                $sql = 'SELECT scu.session_id, scu.c_id
3517
                        FROM '.$tbl_session_course_user.' scu
3518
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
3519
                        ON (scu.session_id=sru.session_id)
3520
                        WHERE
3521
                            scu.user_id='.$coach_id.' AND
3522
                            scu.status=2 AND
3523
                            sru.access_url_id = '.$access_url_id;
3524
            }
3525
        }
3526
3527
        $result = Database::query($sql);
3528
3529
        while ($a_courses = Database::fetch_array($result)) {
3530
            $courseId = $a_courses['c_id'];
3531
            $id_session = $a_courses['session_id'];
3532
3533
            $sql = "SELECT DISTINCT srcru.user_id
3534
                    FROM $tbl_session_course_user AS srcru
3535
                    INNER JOIN $tbl_session_user sru
3536
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
3537
                    WHERE
3538
                        sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
3539
                        srcru.c_id = '$courseId' AND
3540
                        srcru.session_id = '$id_session'";
3541
3542
            $rs = Database::query($sql);
3543
            while ($row = Database::fetch_array($rs)) {
3544
                $students[$row['user_id']] = $row['user_id'];
3545
            }
3546
        }
3547
3548
        // Then, courses where $coach_id is coach of the session
3549
        $sql = 'SELECT session_course_user.user_id
3550
                FROM '.$tbl_session_course_user.' as session_course_user
3551
                INNER JOIN '.$tbl_session_user.' sru
3552
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
3553
                INNER JOIN '.$tbl_session_course.' as session_course
3554
                ON session_course.c_id = session_course_user.c_id
3555
                AND session_course_user.session_id = session_course.session_id
3556
                INNER JOIN '.$tbl_session.' as session
3557
                ON session.id = session_course.session_id
3558
                AND session.id_coach = '.$coach_id;
3559
        if (api_is_multiple_url_enabled()) {
3560
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3561
            $access_url_id = api_get_current_access_url_id();
3562
            if (-1 != $access_url_id) {
3563
                $sql = 'SELECT session_course_user.user_id
3564
                        FROM '.$tbl_session_course_user.' as session_course_user
3565
                        INNER JOIN '.$tbl_session_user.' sru
3566
                        ON session_course_user.user_id = sru.user_id AND
3567
                           session_course_user.session_id = sru.session_id
3568
                        INNER JOIN '.$tbl_session_course.' as session_course
3569
                        ON session_course.c_id = session_course_user.c_id AND
3570
                        session_course_user.session_id = session_course.session_id
3571
                        INNER JOIN '.$tbl_session.' as session
3572
                        ON session.id = session_course.session_id AND
3573
                        session.id_coach = '.$coach_id.'
3574
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
3575
                        ON session.id = session_rel_url.session_id
3576
                        WHERE access_url_id = '.$access_url_id;
3577
            }
3578
        }
3579
3580
        $result = Database::query($sql);
3581
        while ($row = Database::fetch_array($result)) {
3582
            $students[$row['user_id']] = $row['user_id'];
3583
        }
3584
3585
        return $students;
3586
    }
3587
3588
    /**
3589
     * Check if a coach is allowed to follow a student.
3590
     *
3591
     * @param    int        Coach id
3592
     * @param    int        Student id
3593
     *
3594
     * @return bool
3595
     */
3596
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3597
    {
3598
        $coach_id = intval($coach_id);
3599
        $student_id = intval($student_id);
3600
3601
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3602
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3603
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3604
3605
        // At first, courses where $coach_id is coach of the course
3606
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
3607
                WHERE user_id='.$coach_id.' AND status=2';
3608
        $result = Database::query($sql);
3609
        if (Database::num_rows($result) > 0) {
3610
            return true;
3611
        }
3612
3613
        // Then, courses where $coach_id is coach of the session
3614
        $sql = 'SELECT session_course_user.user_id
3615
                FROM '.$tbl_session_course_user.' as session_course_user
3616
                INNER JOIN '.$tbl_session_course.' as session_course
3617
                ON session_course.c_id = session_course_user.c_id
3618
                INNER JOIN '.$tbl_session.' as session
3619
                ON session.id = session_course.session_id
3620
                AND session.id_coach = '.$coach_id.'
3621
                WHERE user_id = '.$student_id;
3622
        $result = Database::query($sql);
3623
        if (Database::num_rows($result) > 0) {
3624
            return true;
3625
        }
3626
3627
        return false;
3628
    }
3629
3630
    /**
3631
     * Get courses followed by coach.
3632
     *
3633
     * @param     int        Coach id
3634
     * @param    int        Session id (optional)
3635
     *
3636
     * @return array Courses list
3637
     */
3638
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
3639
    {
3640
        $coach_id = intval($coach_id);
3641
        if (!empty($id_session)) {
3642
            $id_session = intval($id_session);
3643
        }
3644
3645
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3646
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3647
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3648
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3649
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3650
3651
        // At first, courses where $coach_id is coach of the course.
3652
        $sql = 'SELECT DISTINCT c.code
3653
                FROM '.$tbl_session_course_user.' sc
3654
                INNER JOIN '.$tbl_course.' c
3655
                ON (c.id = sc.c_id)
3656
                WHERE user_id = '.$coach_id.' AND status = 2';
3657
3658
        if (api_is_multiple_url_enabled()) {
3659
            $access_url_id = api_get_current_access_url_id();
3660
            if (-1 != $access_url_id) {
3661
                $sql = 'SELECT DISTINCT c.code
3662
                        FROM '.$tbl_session_course_user.' scu
3663
                        INNER JOIN '.$tbl_course.' c
3664
                        ON (c.code = scu.c_id)
3665
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3666
                        ON (c.id = cru.c_id)
3667
                        WHERE
3668
                            scu.user_id='.$coach_id.' AND
3669
                            scu.status=2 AND
3670
                            cru.access_url_id = '.$access_url_id;
3671
            }
3672
        }
3673
3674
        if (!empty($id_session)) {
3675
            $sql .= ' AND session_id='.$id_session;
3676
        }
3677
3678
        $courseList = [];
3679
        $result = Database::query($sql);
3680
        while ($row = Database::fetch_array($result)) {
3681
            $courseList[$row['code']] = $row['code'];
3682
        }
3683
3684
        // Then, courses where $coach_id is coach of the session
3685
        $sql = 'SELECT DISTINCT course.code
3686
                FROM '.$tbl_session_course.' as session_course
3687
                INNER JOIN '.$tbl_session.' as session
3688
                    ON session.id = session_course.session_id
3689
                    AND session.id_coach = '.$coach_id.'
3690
                INNER JOIN '.$tbl_course.' as course
3691
                    ON course.id = session_course.c_id';
3692
3693
        if (api_is_multiple_url_enabled()) {
3694
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3695
            $access_url_id = api_get_current_access_url_id();
3696
            if (-1 != $access_url_id) {
3697
                $sql = 'SELECT DISTINCT c.code
3698
                    FROM '.$tbl_session_course.' as session_course
3699
                    INNER JOIN '.$tbl_course.' c
3700
                    ON (c.id = session_course.c_id)
3701
                    INNER JOIN '.$tbl_session.' as session
3702
                    ON session.id = session_course.session_id
3703
                        AND session.id_coach = '.$coach_id.'
3704
                    INNER JOIN '.$tbl_course.' as course
3705
                        ON course.id = session_course.c_id
3706
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
3707
                    ON (course_rel_url.c_id = c.id)';
3708
            }
3709
        }
3710
3711
        if (!empty($id_session)) {
3712
            $sql .= ' WHERE session_course.session_id='.$id_session;
3713
            if (api_is_multiple_url_enabled()) {
3714
                $sql .= ' AND access_url_id = '.$access_url_id;
3715
            }
3716
        } else {
3717
            if (api_is_multiple_url_enabled()) {
3718
                $sql .= ' WHERE access_url_id = '.$access_url_id;
3719
            }
3720
        }
3721
3722
        $result = Database::query($sql);
3723
        while ($row = Database::fetch_array($result)) {
3724
            $courseList[$row['code']] = $row['code'];
3725
        }
3726
3727
        return $courseList;
3728
    }
3729
3730
    /**
3731
     * Get sessions coached by user.
3732
     *
3733
     * @param int    $coach_id
3734
     * @param int    $start
3735
     * @param int    $limit
3736
     * @param bool   $getCount
3737
     * @param string $keyword
3738
     * @param string $description
3739
     * @param string $orderByName
3740
     * @param string $orderByDirection
3741
     * @param array  $options
3742
     *
3743
     * @return mixed
3744
     */
3745
    public static function get_sessions_coached_by_user(
3746
        $coach_id,
3747
        $start = 0,
3748
        $limit = 0,
3749
        $getCount = false,
3750
        $keyword = '',
3751
        $description = '',
3752
        $orderByName = '',
3753
        $orderByDirection = '',
3754
        $options = []
3755
    ) {
3756
        // table definition
3757
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3758
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3759
        $coach_id = (int) $coach_id;
3760
3761
        $select = ' SELECT * FROM ';
3762
        if ($getCount) {
3763
            $select = ' SELECT count(DISTINCT id) as count FROM ';
3764
        }
3765
3766
        $limitCondition = null;
3767
        if (!empty($start) && !empty($limit)) {
3768
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3769
        }
3770
3771
        $keywordCondition = null;
3772
        if (!empty($keyword)) {
3773
            $keyword = Database::escape_string($keyword);
3774
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
3775
3776
            if (!empty($description)) {
3777
                $description = Database::escape_string($description);
3778
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3779
            }
3780
        }
3781
3782
        $extraFieldModel = new ExtraFieldModel('session');
3783
        $conditions = $extraFieldModel->parseConditions($options);
3784
        $sqlInjectJoins = $conditions['inject_joins'];
3785
        $extraFieldsConditions = $conditions['where'];
3786
        $sqlInjectWhere = $conditions['inject_where'];
3787
        $injectExtraFields = $conditions['inject_extra_fields'];
3788
3789
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3790
        $access_url_id = api_get_current_access_url_id();
3791
3792
        $orderBy = '';
3793
        if (!empty($orderByName)) {
3794
            if (in_array($orderByName, ['name', 'access_start_date'])) {
3795
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
3796
                $orderByName = Database::escape_string($orderByName);
3797
                $orderBy .= " ORDER BY `$orderByName` $orderByDirection";
3798
            }
3799
        }
3800
3801
        $sql = "
3802
            $select
3803
            (
3804
                SELECT DISTINCT
3805
                    s.id,
3806
                    name,
3807
                    $injectExtraFields
3808
                    access_start_date,
3809
                    access_end_date
3810
                FROM $tbl_session s
3811
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3812
                ON (s.id = session_rel_url.session_id)
3813
                $sqlInjectJoins
3814
                WHERE
3815
                    id_coach = $coach_id AND
3816
                    access_url_id = $access_url_id
3817
                    $keywordCondition
3818
                    $extraFieldsConditions
3819
                    $sqlInjectWhere
3820
            UNION
3821
                SELECT DISTINCT
3822
                    s.id,
3823
                    s.name,
3824
                    $injectExtraFields
3825
                    s.access_start_date,
3826
                    s.access_end_date
3827
                FROM $tbl_session as s
3828
                INNER JOIN $tbl_session_course_user as session_course_user
3829
                ON
3830
                    s.id = session_course_user.session_id AND
3831
                    session_course_user.user_id = $coach_id AND
3832
                    session_course_user.status = 2
3833
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3834
                ON (s.id = session_rel_url.session_id)
3835
                $sqlInjectJoins
3836
                WHERE
3837
                    access_url_id = $access_url_id
3838
                    $keywordCondition
3839
                    $extraFieldsConditions
3840
                    $sqlInjectWhere
3841
            ) as sessions $limitCondition $orderBy
3842
            ";
3843
3844
        $rs = Database::query($sql);
3845
        if ($getCount) {
3846
            $row = Database::fetch_array($rs);
3847
3848
            return $row['count'];
3849
        }
3850
3851
        $sessions = [];
3852
        while ($row = Database::fetch_array($rs)) {
3853
            if ($row['access_start_date'] === '0000-00-00 00:00:00') {
3854
                $row['access_start_date'] = null;
3855
            }
3856
3857
            $sessions[$row['id']] = $row;
3858
        }
3859
3860
        if (!empty($sessions)) {
3861
            foreach ($sessions as &$session) {
3862
                if (empty($session['access_start_date'])) {
3863
                    $session['status'] = get_lang('SessionActive');
3864
                } else {
3865
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3866
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3867
                    if ($time_start < time() && time() < $time_end) {
3868
                        $session['status'] = get_lang('SessionActive');
3869
                    } else {
3870
                        if (time() < $time_start) {
3871
                            $session['status'] = get_lang('SessionFuture');
3872
                        } else {
3873
                            if (time() > $time_end) {
3874
                                $session['status'] = get_lang('SessionPast');
3875
                            }
3876
                        }
3877
                    }
3878
                }
3879
            }
3880
        }
3881
3882
        return $sessions;
3883
    }
3884
3885
    /**
3886
     * Get courses list from a session.
3887
     *
3888
     * @param    int        Session id
3889
     *
3890
     * @return array Courses list
3891
     */
3892
    public static function get_courses_list_from_session($session_id)
3893
    {
3894
        $session_id = (int) $session_id;
3895
3896
        // table definition
3897
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3898
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3899
3900
        $sql = "SELECT DISTINCT code, c_id
3901
                FROM $tbl_session_course sc
3902
                INNER JOIN $courseTable c
3903
                ON sc.c_id = c.id
3904
                WHERE session_id= $session_id";
3905
3906
        $result = Database::query($sql);
3907
3908
        $courses = [];
3909
        while ($row = Database::fetch_array($result)) {
3910
            $courses[$row['code']] = $row;
3911
        }
3912
3913
        return $courses;
3914
    }
3915
3916
    /**
3917
     * Count the number of documents that an user has uploaded to a course.
3918
     *
3919
     * @param    int|array   Student id(s)
3920
     * @param    string      Course code
3921
     * @param    int         Session id (optional),
3922
     * if param $session_id is null(default)
3923
     * return count of assignments including sessions, 0 = session is not filtered
3924
     *
3925
     * @return int Number of documents
3926
     */
3927
    public static function count_student_uploaded_documents(
3928
        $student_id,
3929
        $course_code,
3930
        $session_id = null
3931
    ) {
3932
        // get the information of the course
3933
        $a_course = api_get_course_info($course_code);
3934
        if (!empty($a_course)) {
3935
            // table definition
3936
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3937
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
3938
            $course_id = $a_course['real_id'];
3939
            if (is_array($student_id)) {
3940
                $studentList = array_map('intval', $student_id);
3941
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
3942
            } else {
3943
                $student_id = (int) $student_id;
3944
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
3945
            }
3946
3947
            $condition_session = null;
3948
            if (isset($session_id)) {
3949
                $session_id = (int) $session_id;
3950
                $condition_session = " AND pub.session_id = $session_id ";
3951
            }
3952
3953
            $sql = "SELECT count(ip.tool) AS count
3954
                    FROM $tbl_item_property ip
3955
                    INNER JOIN $tbl_document pub
3956
                    ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
3957
                    WHERE
3958
                        ip.c_id  = $course_id AND
3959
                        pub.c_id  = $course_id AND
3960
                        pub.filetype ='file' AND
3961
                        ip.tool = 'document'
3962
                        $condition_user $condition_session ";
3963
            $rs = Database::query($sql);
3964
            $row = Database::fetch_array($rs, 'ASSOC');
3965
3966
            return $row['count'];
3967
        }
3968
3969
        return null;
3970
    }
3971
3972
    /**
3973
     * Count assignments per student.
3974
     *
3975
     * @param array|int $student_id
3976
     * @param string    $course_code
3977
     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
3978
     *                               0 = session is not filtered
3979
     *
3980
     * @return int Count of assignments
3981
     */
3982
    public static function count_student_assignments(
3983
        $student_id,
3984
        $course_code = null,
3985
        $session_id = null
3986
    ) {
3987
        if (empty($student_id)) {
3988
            return 0;
3989
        }
3990
3991
        $conditions = [];
3992
3993
        // Get the information of the course
3994
        $a_course = api_get_course_info($course_code);
3995
        if (!empty($a_course)) {
3996
            $course_id = $a_course['real_id'];
3997
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
3998
        }
3999
4000
        // table definition
4001
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
4002
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
4003
4004
        if (is_array($student_id)) {
4005
            $studentList = array_map('intval', $student_id);
4006
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
4007
        } else {
4008
            $student_id = (int) $student_id;
4009
            $conditions[] = " ip.insert_user_id = '$student_id' ";
4010
        }
4011
4012
        $conditions[] = ' pub.active <> 2 ';
4013
        $conditionToString = implode(' AND ', $conditions);
4014
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
4015
        $conditionToString .= $sessionCondition;
4016
4017
        $sql = "SELECT count(ip.tool) as count
4018
                FROM $tbl_item_property ip
4019
                INNER JOIN $tbl_student_publication pub
4020
                ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
4021
                WHERE
4022
                    ip.tool='work' AND
4023
                    $conditionToString";
4024
        $rs = Database::query($sql);
4025
        $row = Database::fetch_array($rs, 'ASSOC');
4026
4027
        return $row['count'];
4028
    }
4029
4030
    /**
4031
     * Count messages per student inside forum tool.
4032
     *
4033
     * @param int|array  Student id
4034
     * @param string     Course code
4035
     * @param int        Session id if null(default) return count of messages including sessions, 0 = session is not
4036
     *                           filtered
4037
     *
4038
     * @return int Count of messages
4039
     */
4040
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
4041
    {
4042
        if (empty($student_id)) {
4043
            return 0;
4044
        }
4045
4046
        // Table definition.
4047
        $tbl_forum_post = Database::get_course_table(TABLE_FORUM_POST);
4048
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
4049
4050
        $conditions = [];
4051
        if (is_array($student_id)) {
4052
            $studentList = array_map('intval', $student_id);
4053
            $conditions[] = " post.poster_id IN ('".implode("','", $studentList)."') ";
4054
        } else {
4055
            $student_id = (int) $student_id;
4056
            $conditions[] = " post.poster_id = '$student_id' ";
4057
        }
4058
4059
        $conditionsToString = implode('AND ', $conditions);
4060
4061
        if (empty($courseCode)) {
4062
            $sql = "SELECT count(poster_id) as count
4063
                    FROM $tbl_forum_post post
4064
                    INNER JOIN $tbl_forum forum
4065
                    ON (forum.forum_id = post.forum_id AND forum.c_id = post.c_id)
4066
                    WHERE $conditionsToString";
4067
4068
            $rs = Database::query($sql);
4069
            $row = Database::fetch_array($rs, 'ASSOC');
4070
4071
            return $row['count'];
4072
        }
4073
4074
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
4075
4076
        $courseInfo = api_get_course_info($courseCode);
4077
4078
        $forums = [];
4079
        if (!empty($courseInfo)) {
4080
            $forums = get_forums('', $courseCode, true, $session_id);
4081
            $course_id = $courseInfo['real_id'];
4082
            $conditions[] = " post.c_id  = $course_id ";
4083
        }
4084
4085
        if (!empty($forums)) {
4086
            $idList = array_column($forums, 'forum_id');
4087
            $idListToString = implode("', '", $idList);
4088
            $conditions[] = " post.forum_id  IN ('$idListToString')";
4089
        }
4090
4091
        $conditionsToString = implode('AND ', $conditions);
4092
        $sql = "SELECT count(poster_id) as count
4093
                FROM $tbl_forum_post post
4094
                WHERE $conditionsToString";
4095
4096
        $rs = Database::query($sql);
4097
        $row = Database::fetch_array($rs, 'ASSOC');
4098
        $count = $row['count'];
4099
4100
        return $count;
4101
    }
4102
4103
    /**
4104
     * This function counts the number of post by course.
4105
     *
4106
     * @param string $course_code
4107
     * @param int    $session_id  (optional), if is null(default) it'll return results including sessions,
4108
     *                            0 = session is not filtered
4109
     * @param int    $groupId
4110
     *
4111
     * @return int The number of post by course
4112
     */
4113
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
4114
    {
4115
        $courseInfo = api_get_course_info($course_code);
4116
        if (!empty($courseInfo)) {
4117
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
4118
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
4119
4120
            $condition_session = '';
4121
            if (isset($session_id)) {
4122
                $session_id = (int) $session_id;
4123
                $condition_session = api_get_session_condition(
4124
                    $session_id,
4125
                    true,
4126
                    false,
4127
                    'f.session_id'
4128
                );
4129
            }
4130
4131
            $course_id = $courseInfo['real_id'];
4132
            $groupId = (int) $groupId;
4133
            if (!empty($groupId)) {
4134
                $groupCondition = " i.to_group_id = $groupId ";
4135
            } else {
4136
                $groupCondition = ' (i.to_group_id = 0 OR i.to_group_id IS NULL) ';
4137
            }
4138
4139
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4140
            $sql = "SELECT count(*) FROM $tbl_posts p
4141
                    INNER JOIN $tbl_forums f
4142
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
4143
                    INNER JOIN $item i
4144
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
4145
                    WHERE
4146
                        p.c_id = $course_id AND
4147
                        f.c_id = $course_id AND
4148
                        $groupCondition
4149
                        $condition_session
4150
                    ";
4151
            $result = Database::query($sql);
4152
            $row = Database::fetch_row($result);
4153
            $count = $row[0];
4154
4155
            return $count;
4156
        }
4157
4158
        return 0;
4159
    }
4160
4161
    /**
4162
     * This function counts the number of threads by course.
4163
     *
4164
     * @param      string     Course code
4165
     * @param    int        Session id (optional),
4166
     * if param $session_id is null(default) it'll return results including
4167
     * sessions, 0 = session is not filtered
4168
     * @param int $groupId
4169
     *
4170
     * @return int The number of threads by course
4171
     */
4172
    public static function count_number_of_threads_by_course(
4173
        $course_code,
4174
        $session_id = null,
4175
        $groupId = 0
4176
    ) {
4177
        $course_info = api_get_course_info($course_code);
4178
        if (empty($course_info)) {
4179
            return null;
4180
        }
4181
4182
        $course_id = $course_info['real_id'];
4183
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
4184
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4185
4186
        $condition_session = '';
4187
        if (isset($session_id)) {
4188
            $session_id = (int) $session_id;
4189
            $condition_session = ' AND f.session_id = '.$session_id;
4190
        }
4191
4192
        $groupId = (int) $groupId;
4193
4194
        if (!empty($groupId)) {
4195
            $groupCondition = " i.to_group_id = $groupId ";
4196
        } else {
4197
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4198
        }
4199
4200
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4201
        $sql = "SELECT count(*)
4202
                FROM $tbl_threads t
4203
                INNER JOIN $tbl_forums f
4204
                ON f.iid = t.forum_id AND f.c_id = t.c_id
4205
                INNER JOIN $item i
4206
                ON (
4207
                    tool = '".TOOL_FORUM_THREAD."' AND
4208
                    f.c_id = i.c_id AND
4209
                    t.iid = i.ref
4210
                )
4211
                WHERE
4212
                    t.c_id = $course_id AND
4213
                    f.c_id = $course_id AND
4214
                    $groupCondition
4215
                    $condition_session
4216
                ";
4217
4218
        $result = Database::query($sql);
4219
        if (Database::num_rows($result)) {
4220
            $row = Database::fetch_row($result);
4221
            $count = $row[0];
4222
4223
            return $count;
4224
        }
4225
4226
        return 0;
4227
    }
4228
4229
    /**
4230
     * This function counts the number of forums by course.
4231
     *
4232
     * @param      string     Course code
4233
     * @param    int        Session id (optional),
4234
     * if param $session_id is null(default) it'll return results
4235
     * including sessions, 0 = session is not filtered
4236
     * @param int $groupId
4237
     *
4238
     * @return int The number of forums by course
4239
     */
4240
    public static function count_number_of_forums_by_course(
4241
        $course_code,
4242
        $session_id = null,
4243
        $groupId = 0
4244
    ) {
4245
        $course_info = api_get_course_info($course_code);
4246
        if (empty($course_info)) {
4247
            return null;
4248
        }
4249
        $course_id = $course_info['real_id'];
4250
4251
        $condition_session = '';
4252
        if (isset($session_id)) {
4253
            $session_id = (int) $session_id;
4254
            $condition_session = ' AND f.session_id = '.$session_id;
4255
        }
4256
4257
        $groupId = (int) $groupId;
4258
        if (!empty($groupId)) {
4259
            $groupCondition = " i.to_group_id = $groupId ";
4260
        } else {
4261
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4262
        }
4263
4264
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4265
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4266
4267
        $sql = "SELECT count(*)
4268
                FROM $tbl_forums f
4269
                INNER JOIN $item i
4270
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
4271
                WHERE
4272
                    f.c_id = $course_id AND
4273
                    $groupCondition
4274
                    $condition_session
4275
                ";
4276
        $result = Database::query($sql);
4277
        if (Database::num_rows($result)) {
4278
            $row = Database::fetch_row($result);
4279
            $count = $row[0];
4280
4281
            return $count;
4282
        }
4283
4284
        return 0;
4285
    }
4286
4287
    /**
4288
     * This function counts the chat last connections by course in x days.
4289
     *
4290
     * @param      string     Course code
4291
     * @param      int     Last x days
4292
     * @param    int        Session id (optional)
4293
     *
4294
     * @return int Chat last connections by course in x days
4295
     */
4296
    public static function chat_connections_during_last_x_days_by_course(
4297
        $course_code,
4298
        $last_days,
4299
        $session_id = 0
4300
    ) {
4301
        $course_info = api_get_course_info($course_code);
4302
        if (empty($course_info)) {
4303
            return null;
4304
        }
4305
        $course_id = $course_info['real_id'];
4306
4307
        // Protect data
4308
        $last_days = (int) $last_days;
4309
        $session_id = (int) $session_id;
4310
4311
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4312
        $now = api_get_utc_datetime();
4313
4314
        $sql = "SELECT count(*) FROM $tbl_stats_access
4315
                WHERE
4316
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
4317
                    c_id = '$course_id' AND
4318
                    access_tool='".TOOL_CHAT."' AND
4319
                    access_session_id = '$session_id' ";
4320
        $result = Database::query($sql);
4321
        if (Database::num_rows($result)) {
4322
            $row = Database::fetch_row($result);
4323
            $count = $row[0];
4324
4325
            return $count;
4326
        }
4327
4328
        return 0;
4329
    }
4330
4331
    /**
4332
     * This function gets the last student's connection in chat.
4333
     *
4334
     * @param      int     Student id
4335
     * @param      string     Course code
4336
     * @param    int        Session id (optional)
4337
     *
4338
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
4339
     */
4340
    public static function chat_last_connection(
4341
        $student_id,
4342
        $courseId,
4343
        $session_id = 0
4344
    ) {
4345
        $student_id = (int) $student_id;
4346
        $courseId = (int) $courseId;
4347
        $session_id = (int) $session_id;
4348
        $date_time = '';
4349
4350
        // table definition
4351
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4352
        $sql = "SELECT access_date
4353
                FROM $tbl_stats_access
4354
                WHERE
4355
                     access_tool='".TOOL_CHAT."' AND
4356
                     access_user_id='$student_id' AND
4357
                     c_id = $courseId AND
4358
                     access_session_id = '$session_id'
4359
                ORDER BY access_date DESC limit 1";
4360
        $rs = Database::query($sql);
4361
        if (Database::num_rows($rs) > 0) {
4362
            $row = Database::fetch_array($rs);
4363
            $date_time = api_convert_and_format_date(
4364
                $row['access_date'],
4365
                null,
4366
                date_default_timezone_get()
4367
            );
4368
        }
4369
4370
        return $date_time;
4371
    }
4372
4373
    /**
4374
     * Get count student's visited links.
4375
     *
4376
     * @param int $student_id Student id
4377
     * @param int $courseId
4378
     * @param int $session_id Session id (optional)
4379
     *
4380
     * @return int count of visited links
4381
     */
4382
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
4383
    {
4384
        $student_id = (int) $student_id;
4385
        $courseId = (int) $courseId;
4386
        $session_id = (int) $session_id;
4387
4388
        // table definition
4389
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4390
4391
        $sql = 'SELECT 1
4392
                FROM '.$table.'
4393
                WHERE
4394
                    links_user_id= '.$student_id.' AND
4395
                    c_id = "'.$courseId.'" AND
4396
                    links_session_id = '.$session_id.' ';
4397
4398
        $rs = Database::query($sql);
4399
4400
        return Database::num_rows($rs);
4401
    }
4402
4403
    /**
4404
     * Get count student downloaded documents.
4405
     *
4406
     * @param    int        Student id
4407
     * @param int $courseId
4408
     * @param    int        Session id (optional)
4409
     *
4410
     * @return int Count downloaded documents
4411
     */
4412
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
4413
    {
4414
        $student_id = (int) $student_id;
4415
        $courseId = (int) $courseId;
4416
        $session_id = (int) $session_id;
4417
4418
        // table definition
4419
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4420
4421
        $sql = 'SELECT 1
4422
                FROM '.$table.'
4423
                WHERE down_user_id = '.$student_id.'
4424
                AND c_id  = "'.$courseId.'"
4425
                AND down_session_id = '.$session_id.' ';
4426
        $rs = Database::query($sql);
4427
4428
        return Database::num_rows($rs);
4429
    }
4430
4431
    /**
4432
     * Get course list inside a session from a student.
4433
     *
4434
     * @param int $user_id    Student id
4435
     * @param int $id_session Session id (optional)
4436
     *
4437
     * @return array Courses list
4438
     */
4439
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
4440
    {
4441
        $user_id = intval($user_id);
4442
        $id_session = intval($id_session);
4443
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4444
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4445
4446
        $sql = "SELECT c.code
4447
                FROM $tbl_session_course_user sc
4448
                INNER JOIN $courseTable c
4449
                WHERE
4450
                    user_id= $user_id  AND
4451
                    session_id = $id_session";
4452
        $result = Database::query($sql);
4453
        $courses = [];
4454
        while ($row = Database::fetch_array($result)) {
4455
            $courses[$row['code']] = $row['code'];
4456
        }
4457
4458
        return $courses;
4459
    }
4460
4461
    /**
4462
     * Get inactive students in course.
4463
     *
4464
     * @param int        $courseId
4465
     * @param string|int $since      Since login course date (optional, default = 'never')
4466
     * @param int        $session_id (optional)
4467
     *
4468
     * @return array Inactive users
4469
     */
4470
    public static function getInactiveStudentsInCourse(
4471
        $courseId,
4472
        $since = 'never',
4473
        $session_id = 0
4474
    ) {
4475
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4476
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4477
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4478
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4479
        $now = api_get_utc_datetime();
4480
        $courseId = (int) $courseId;
4481
        $session_id = (int) $session_id;
4482
4483
        if (empty($courseId)) {
4484
            return false;
4485
        }
4486
4487
        if ($since === 'never') {
4488
            if (empty($session_id)) {
4489
                $sql = 'SELECT course_user.user_id
4490
                        FROM '.$table_course_rel_user.' course_user
4491
                        LEFT JOIN '.$tbl_track_login.' stats_login
4492
                        ON course_user.user_id = stats_login.user_id AND
4493
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
4494
                        INNER JOIN '.$tableCourse.' c
4495
                        ON (c.id = course_user.c_id)
4496
                        WHERE
4497
                            course_user.c_id = '.$courseId.' AND
4498
                            stats_login.login_course_date IS NULL
4499
                        GROUP BY course_user.user_id';
4500
            } else {
4501
                $sql = 'SELECT session_course_user.user_id
4502
                        FROM '.$tbl_session_course_user.' session_course_user
4503
                        LEFT JOIN '.$tbl_track_login.' stats_login
4504
                        ON session_course_user.user_id = stats_login.user_id
4505
                        INNER JOIN '.$tableCourse.' c
4506
                        ON (c.id = session_course_user.c_id)
4507
                        WHERE
4508
                            session_course_user.c_id = '.$courseId.' AND
4509
                            stats_login.login_course_date IS NULL
4510
                        GROUP BY session_course_user.user_id';
4511
            }
4512
        } else {
4513
            $since = (int) $since;
4514
            if (empty($session_id)) {
4515
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
4516
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
4517
            } else {
4518
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
4519
                          ON
4520
                            c.id = session_course_user.c_id AND
4521
                            session_course_user.session_id = '.$session_id.' AND
4522
                            session_course_user.user_id = stats_login.user_id ';
4523
            }
4524
4525
            $sql = 'SELECT
4526
                    stats_login.user_id,
4527
                    MAX(login_course_date) max_date
4528
                FROM '.$tbl_track_login.' stats_login
4529
                INNER JOIN '.$tableCourse.' c
4530
                ON (c.id = stats_login.c_id)
4531
                '.$inner.'
4532
                WHERE c.id = '.$courseId.'
4533
                GROUP BY stats_login.user_id
4534
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
4535
        }
4536
4537
        $rs = Database::query($sql);
4538
4539
        $allow = api_get_plugin_setting('pausetraining', 'tool_enable') === 'true';
4540
        $allowPauseFormation = api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') === 'true';
4541
4542
        $extraFieldValue = new ExtraFieldValue('user');
4543
        $users = [];
4544
        while ($user = Database::fetch_array($rs)) {
4545
            $userId = $user['user_id'];
4546
4547
            if ($allow && $allowPauseFormation) {
4548
                $pause = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'pause_formation');
4549
                if (!empty($pause) && isset($pause['value']) && 1 == $pause['value']) {
4550
                    // Skip user because he paused his formation.
4551
                    continue;
4552
                }
4553
            }
4554
4555
            $users[] = $userId;
4556
        }
4557
4558
        return $users;
4559
    }
4560
4561
    /**
4562
     * get count clicks about tools most used by course.
4563
     *
4564
     * @param int $courseId
4565
     * @param    int        Session id (optional),
4566
     * if param $session_id is null(default) it'll return results
4567
     * including sessions, 0 = session is not filtered
4568
     *
4569
     * @return array tools data
4570
     */
4571
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
4572
    {
4573
        $courseId = (int) $courseId;
4574
        $data = [];
4575
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4576
        $condition_session = '';
4577
        if (isset($session_id)) {
4578
            $session_id = (int) $session_id;
4579
            $condition_session = ' AND access_session_id = '.$session_id;
4580
        }
4581
        $sql = "SELECT
4582
                    access_tool,
4583
                    COUNT(DISTINCT access_user_id),
4584
                    count(access_tool) as count_access_tool
4585
                FROM $TABLETRACK_ACCESS
4586
                WHERE
4587
                    access_tool IS NOT NULL AND
4588
                    access_tool != '' AND
4589
                    c_id = '$courseId'
4590
                    $condition_session
4591
                GROUP BY access_tool
4592
                ORDER BY count_access_tool DESC
4593
                LIMIT 0, 3";
4594
        $rs = Database::query($sql);
4595
        if (Database::num_rows($rs) > 0) {
4596
            while ($row = Database::fetch_array($rs)) {
4597
                $data[] = $row;
4598
            }
4599
        }
4600
4601
        return $data;
4602
    }
4603
4604
    /**
4605
     * get documents most downloaded by course.
4606
     *
4607
     * @param      string     Course code
4608
     * @param    int        Session id (optional),
4609
     * if param $session_id is null(default) it'll return results including
4610
     * sessions, 0 = session is not filtered
4611
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4612
     *
4613
     * @return array documents downloaded
4614
     */
4615
    public static function get_documents_most_downloaded_by_course(
4616
        $course_code,
4617
        $session_id = 0,
4618
        $limit = 0
4619
    ) {
4620
        $courseId = api_get_course_int_id($course_code);
4621
        $data = [];
4622
4623
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4624
        $condition_session = '';
4625
        $session_id = intval($session_id);
4626
        if (!empty($session_id)) {
4627
            $condition_session = ' AND down_session_id = '.$session_id;
4628
        }
4629
        $sql = "SELECT
4630
                    down_doc_path,
4631
                    COUNT(DISTINCT down_user_id),
4632
                    COUNT(down_doc_path) as count_down
4633
                FROM $TABLETRACK_DOWNLOADS
4634
                WHERE c_id = $courseId
4635
                    $condition_session
4636
                GROUP BY down_doc_path
4637
                ORDER BY count_down DESC
4638
                LIMIT 0,  $limit";
4639
        $rs = Database::query($sql);
4640
4641
        if (Database::num_rows($rs) > 0) {
4642
            while ($row = Database::fetch_array($rs)) {
4643
                $data[] = $row;
4644
            }
4645
        }
4646
4647
        return $data;
4648
    }
4649
4650
    /**
4651
     * get links most visited by course.
4652
     *
4653
     * @param      string     Course code
4654
     * @param    int        Session id (optional),
4655
     * if param $session_id is null(default) it'll
4656
     * return results including sessions, 0 = session is not filtered
4657
     *
4658
     * @return array links most visited
4659
     */
4660
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4661
    {
4662
        $course_code = Database::escape_string($course_code);
4663
        $course_info = api_get_course_info($course_code);
4664
        $course_id = $course_info['real_id'];
4665
        $data = [];
4666
4667
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4668
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4669
4670
        $condition_session = '';
4671
        if (isset($session_id)) {
4672
            $session_id = intval($session_id);
4673
            $condition_session = ' AND cl.session_id = '.$session_id;
4674
        }
4675
4676
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4677
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4678
                WHERE
4679
                    cl.c_id = $course_id AND
4680
                    sl.links_link_id = cl.id AND
4681
                    sl.c_id = $course_id
4682
                    $condition_session
4683
                GROUP BY cl.title, cl.url
4684
                ORDER BY count_visits DESC
4685
                LIMIT 0, 3";
4686
        $rs = Database::query($sql);
4687
        if (Database::num_rows($rs) > 0) {
4688
            while ($row = Database::fetch_array($rs)) {
4689
                $data[] = $row;
4690
            }
4691
        }
4692
4693
        return $data;
4694
    }
4695
4696
    /**
4697
     * Shows the user progress (when clicking in the Progress tab).
4698
     *
4699
     * @param int    $user_id
4700
     * @param int    $session_id
4701
     * @param string $extra_params
4702
     * @param bool   $show_courses
4703
     * @param bool   $showAllSessions
4704
     * @param bool   $returnArray
4705
     *
4706
     * @return string|array
4707
     */
4708
    public static function showUserProgress(
4709
        $user_id,
4710
        $session_id = 0,
4711
        $extra_params = '',
4712
        $show_courses = true,
4713
        $showAllSessions = true,
4714
        $returnArray = false,
4715
        $showGraph = true
4716
    ) {
4717
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4718
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4719
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4720
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4721
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4722
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4723
4724
        $trackingColumns = [
4725
            'course_session' => [
4726
                'course_title' => true,
4727
                'published_exercises' => true,
4728
                'new_exercises' => true,
4729
                'my_average' => true,
4730
                'average_exercise_result' => true,
4731
                'time_spent' => true,
4732
                'lp_progress' => true,
4733
                'score' => true,
4734
                'best_score' => true,
4735
                'last_connection' => true,
4736
                'details' => true,
4737
            ],
4738
        ];
4739
4740
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
4741
        if (!empty($trackingColumnsConfig)) {
4742
            $trackingColumns = $trackingColumnsConfig;
4743
        }
4744
4745
        $user_id = (int) $user_id;
4746
        $session_id = (int) $session_id;
4747
        $urlId = api_get_current_access_url_id();
4748
4749
        if (api_is_multiple_url_enabled()) {
4750
            $sql = "SELECT c.id, c.code, title
4751
                    FROM $tbl_course_user cu
4752
                    INNER JOIN $tbl_course c
4753
                    ON (cu.c_id = c.id)
4754
                    INNER JOIN $tbl_access_rel_course a
4755
                    ON (a.c_id = c.id)
4756
                    WHERE
4757
                        cu.user_id = $user_id AND
4758
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4759
                        access_url_id = $urlId
4760
                    ORDER BY title";
4761
        } else {
4762
            $sql = "SELECT c.id, c.code, title
4763
                    FROM $tbl_course_user u
4764
                    INNER JOIN $tbl_course c ON (c_id = c.id)
4765
                    WHERE
4766
                        u.user_id= $user_id AND
4767
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
4768
                    ORDER BY title";
4769
        }
4770
4771
        $rs = Database::query($sql);
4772
        $courses = $course_in_session = $temp_course_in_session = [];
4773
        $courseIdList = [];
4774
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4775
            $courses[$row['code']] = $row['title'];
4776
            $courseIdList[] = $row['id'];
4777
        }
4778
4779
        $orderBy = ' ORDER BY display_end_date DESC, name ';
4780
        $extraInnerJoin = null;
4781
4782
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4783
            $orderBy = ' ORDER BY s.id, src.position ';
4784
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4785
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4786
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4787
        }
4788
4789
        $sessionCondition = '';
4790
        if (!empty($session_id)) {
4791
            $sessionCondition = " AND s.id = $session_id";
4792
        }
4793
4794
        // Get the list of sessions where the user is subscribed as student
4795
        if (api_is_multiple_url_enabled()) {
4796
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4797
                    FROM $tbl_session_course_user cu
4798
                    INNER JOIN $tbl_access_rel_session a
4799
                    ON (a.session_id = cu.session_id)
4800
                    INNER JOIN $tbl_session s
4801
                    ON (s.id = a.session_id)
4802
                    INNER JOIN $tbl_course c
4803
                    ON (c.id = cu.c_id)
4804
                    $extraInnerJoin
4805
                    WHERE
4806
                        cu.user_id = $user_id AND
4807
                        access_url_id = ".$urlId."
4808
                        $sessionCondition
4809
                    $orderBy ";
4810
        } else {
4811
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4812
                    FROM $tbl_session_course_user cu
4813
                    INNER JOIN $tbl_session s
4814
                    ON (s.id = cu.session_id)
4815
                    INNER JOIN $tbl_course c
4816
                    ON (c.id = cu.c_id)
4817
                    $extraInnerJoin
4818
                    WHERE
4819
                        cu.user_id = $user_id
4820
                        $sessionCondition
4821
                    $orderBy ";
4822
        }
4823
4824
        $rs = Database::query($sql);
4825
        $simple_session_array = [];
4826
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4827
            $course_info = api_get_course_info($row['code']);
4828
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4829
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
4830
            $simple_session_array[$row['session_id']] = $row['name'];
4831
        }
4832
4833
        foreach ($simple_session_array as $my_session_id => $session_name) {
4834
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4835
            $my_course_data = [];
4836
            foreach ($course_list as $courseId => $course_data) {
4837
                $my_course_data[$courseId] = $course_data['title'];
4838
            }
4839
4840
            if (empty($session_id)) {
4841
                $my_course_data = utf8_sort($my_course_data);
4842
            }
4843
4844
            $final_course_data = [];
4845
            foreach ($my_course_data as $course_id => $value) {
4846
                if (isset($course_list[$course_id])) {
4847
                    $final_course_data[$course_id] = $course_list[$course_id];
4848
                }
4849
            }
4850
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4851
            $course_in_session[$my_session_id]['name'] = $session_name;
4852
        }
4853
4854
        if ($returnArray) {
4855
            $course_in_session[0] = $courseIdList;
4856
4857
            return $course_in_session;
4858
        }
4859
4860
        $html = '';
4861
        // Course list.
4862
        if ($show_courses) {
4863
            if (!empty($courses)) {
4864
                $html .= Display::page_header(
4865
                    Display::return_icon('course.png', get_lang('MyCourses')).PHP_EOL.get_lang('MyCourses')
4866
                );
4867
4868
                $columns = [
4869
                    'course_title' => get_lang('Course'),
4870
                    'time_spent' => get_lang('TimeSpentInTheCourse'),
4871
                    'progress' => get_lang('Progress'),
4872
                    'best_score_in_lp' => get_lang('BestScoreInLearningPath'),
4873
                    'best_score_not_in_lp' => get_lang('BestScoreNotInLearningPath'),
4874
                    'latest_login' => get_lang('LastConnexion'),
4875
                    'details' => get_lang('Details'),
4876
                ];
4877
                $availableColumns = [];
4878
                if (isset($trackingColumns['my_progress_courses'])) {
4879
                    $availableColumns = $trackingColumns['my_progress_courses'];
4880
                }
4881
4882
                $columns = array_filter(
4883
                    $columns,
4884
                    function ($column, $columnKey) use ($availableColumns) {
4885
                        if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
4886
                            return false;
4887
                        }
4888
4889
                        return true;
4890
                    },
4891
                    ARRAY_FILTER_USE_BOTH
4892
                );
4893
4894
                $coursesTable = new SortableTableFromArray([], 0, 0, 'courses');
4895
                $coursesTable->setHeaders($columns);
4896
4897
                foreach ($courses as $course_code => $course_title) {
4898
                    $courseInfo = api_get_course_info($course_code);
4899
                    $courseId = $courseInfo['real_id'];
4900
4901
                    $total_time_login = self::get_time_spent_on_the_course(
4902
                        $user_id,
4903
                        $courseId
4904
                    );
4905
                    $time = api_time_to_hms($total_time_login);
4906
                    $progress = self::get_avg_student_progress(
4907
                        $user_id,
4908
                        $course_code
4909
                    );
4910
                    $bestScore = self::get_avg_student_score(
4911
                        $user_id,
4912
                        $course_code,
4913
                        [],
4914
                        null,
4915
                        false,
4916
                        false,
4917
                        true
4918
                    );
4919
4920
                    $exerciseList = ExerciseLib::get_all_exercises(
4921
                        $courseInfo,
4922
                        0,
4923
                        false,
4924
                        null,
4925
                        false,
4926
                        1
4927
                    );
4928
4929
                    $bestScoreAverageNotInLP = 0;
4930
                    if (!empty($exerciseList)) {
4931
                        foreach ($exerciseList as $exerciseData) {
4932
                            $results = Event::get_best_exercise_results_by_user(
4933
                                $exerciseData['iid'],
4934
                                $courseInfo['real_id'],
4935
                                0,
4936
                                $user_id
4937
                            );
4938
                            $best = 0;
4939
                            if (!empty($results)) {
4940
                                foreach ($results as $result) {
4941
                                    if (!empty($result['exe_weighting'])) {
4942
                                        $score = $result['exe_result'] / $result['exe_weighting'];
4943
                                        if ($score > $best) {
4944
                                            $best = $score;
4945
                                        }
4946
                                    }
4947
                                }
4948
                            }
4949
                            $bestScoreAverageNotInLP += $best;
4950
                        }
4951
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
4952
                    }
4953
4954
                    $last_connection = self::get_last_connection_date_on_the_course($user_id, $courseInfo);
4955
4956
                    if (is_null($progress) || empty($progress)) {
4957
                        $progress = '0%';
4958
                    } else {
4959
                        $progress = $progress.'%';
4960
                    }
4961
4962
                    $filterByCourse = isset($_GET['course']) && $course_code == $_GET['course'] &&
4963
                        empty($_GET['session_id']);
4964
4965
                    $url = api_get_course_url($course_code, $session_id);
4966
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
4967
                    $bestScoreResult = empty($bestScore) ? '-' : sprintf(get_lang('XPercent'), $bestScore);
4968
                    $bestScoreNotInLP = empty($bestScoreAverageNotInLP)
4969
                        ? '-'
4970
                        : sprintf(get_lang('XPercent'), $bestScoreAverageNotInLP);
4971
4972
                    $detailsLink = '';
4973
                    if ($filterByCourse) {
4974
                        $detailsLink .= '<a href="#course_session_data">';
4975
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4976
                        $detailsLink .= '</a>';
4977
                    } else {
4978
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_data">';
4979
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4980
                        $detailsLink .= '</a>';
4981
                    }
4982
4983
                    $result = array_filter(
4984
                        [
4985
                            'course_title' => $course_url,
4986
                            'time_spent' => $time,
4987
                            'progress' => $progress,
4988
                            'best_score_in_lp' => $bestScoreResult,
4989
                            'best_score_not_in_lp' => $bestScoreNotInLP,
4990
                            'latest_login' => $last_connection,
4991
                            'details' => $detailsLink,
4992
                        ],
4993
                        function ($data, $columnKey) {
4994
                            if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
4995
                                return false;
4996
                            }
4997
4998
                            return true;
4999
                        },
5000
                        ARRAY_FILTER_USE_BOTH
5001
                    );
5002
5003
                    $coursesTable->addRow(
5004
                        array_values($result),
5005
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
5006
                    );
5007
                }
5008
5009
                $html .= Display::div($coursesTable->toHtml(), ['class' => 'table-responsive']);
5010
            }
5011
        }
5012
5013
        $allowCareerUser = api_get_configuration_value('allow_career_users');
5014
5015
        // Session list.
5016
        $visibleSessions = [];
5017
        if (!empty($course_in_session)) {
5018
            $main_session_graph = '';
5019
            // Load graphics only when calling to an specific session
5020
            $all_exercise_graph_name_list = [];
5021
            $my_results = [];
5022
            $all_exercise_graph_list = [];
5023
            $all_exercise_start_time = [];
5024
            foreach ($course_in_session as $my_session_id => $session_data) {
5025
                $course_list = $session_data['course_list'];
5026
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
5027
                $exercise_graph_name_list = [];
5028
                $exercise_graph_list = [];
5029
5030
                $visibility = api_get_session_visibility($my_session_id, null, false, $user_id);
5031
5032
                if (SESSION_AVAILABLE === $visibility) {
5033
                    $visibleSessions[] = $my_session_id;
5034
                }
5035
5036
                foreach ($course_list as $course_data) {
5037
                    $exercise_list = ExerciseLib::get_all_exercises(
5038
                        $course_data,
5039
                        $my_session_id,
5040
                        false,
5041
                        null,
5042
                        false,
5043
                        1
5044
                    );
5045
5046
                    foreach ($exercise_list as $exercise_data) {
5047
                        $exercise_obj = new Exercise($course_data['real_id']);
5048
                        $exercise_obj->read($exercise_data['iid']);
5049
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
5050
                        //$visible_return = $exercise_obj->is_visible();
5051
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
5052
                            $best_average = (int)
5053
                                ExerciseLib::get_best_average_score_by_exercise(
5054
                                    $exercise_data['iid'],
5055
                                    $course_data['real_id'],
5056
                                    $my_session_id,
5057
                                    $user_count
5058
                                )
5059
                            ;
5060
5061
                            $exercise_graph_list[] = $best_average;
5062
                            $all_exercise_graph_list[] = $best_average;
5063
5064
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
5065
                                api_get_user_id(),
5066
                                $exercise_data['iid'],
5067
                                $course_data['real_id'],
5068
                                $my_session_id
5069
                            );
5070
5071
                            $score = 0;
5072
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
5073
                                $score = intval($user_result_data['exe_result'] / $user_result_data['exe_weighting'] * 100);
5074
                            }
5075
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
5076
                            $all_exercise_start_time[] = $time;
5077
                            $my_results[] = $score;
5078
                            if (count($exercise_list) <= 10) {
5079
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
5080
                                $exercise_graph_name_list[] = $title;
5081
                                $all_exercise_graph_name_list[] = $title;
5082
                            } else {
5083
                                // if there are more than 10 results, space becomes difficult to find,
5084
                                // so only show the title of the exercise, not the tool
5085
                                $title = cut($exercise_data['title'], 30);
5086
                                $exercise_graph_name_list[] = $title;
5087
                                $all_exercise_graph_name_list[] = $title;
5088
                            }
5089
                        }
5090
                    }
5091
                }
5092
            }
5093
5094
            // Complete graph
5095
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
5096
                asort($all_exercise_start_time);
5097
5098
                //Fix exams order
5099
                $final_all_exercise_graph_name_list = [];
5100
                $my_results_final = [];
5101
                $final_all_exercise_graph_list = [];
5102
5103
                foreach ($all_exercise_start_time as $key => $time) {
5104
                    $label_time = '';
5105
                    if (!empty($time)) {
5106
                        $label_time = date('d-m-y', $time);
5107
                    }
5108
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
5109
                    $my_results_final[] = $my_results[$key];
5110
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
5111
                }
5112
                if ($showGraph) {
5113
                    $main_session_graph = '<div class="row"><div class="col-md-10 col-md-offset-1">'
5114
                        .self::generate_session_exercise_graph(
5115
                            $final_all_exercise_graph_name_list,
5116
                            $my_results_final,
5117
                            $final_all_exercise_graph_list
5118
                        )
5119
                        .'</div></div>';
5120
                }
5121
            }
5122
5123
            $sessionIcon = Display::return_icon('session.png', get_lang('Sessions'));
5124
5125
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5126
            $html .= $anchor.Display::page_header(
5127
                $sessionIcon.PHP_EOL.get_lang('Sessions')
5128
            );
5129
5130
            $sessionsTable = new SortableTableFromArray([], 0, 0, 'sessions');
5131
            $sessionsTable->setHeaders(
5132
                [
5133
                    get_lang('Session'),
5134
                    get_lang('PublishedExercises'),
5135
                    get_lang('NewExercises'),
5136
                    get_lang('AverageExerciseResult'),
5137
                    get_lang('Details'),
5138
                ]
5139
            );
5140
5141
            foreach ($course_in_session as $my_session_id => $session_data) {
5142
                $course_list = $session_data['course_list'];
5143
                $session_name = $session_data['name'];
5144
                if ($showAllSessions == false) {
5145
                    if (isset($session_id) && !empty($session_id)) {
5146
                        if ($session_id != $my_session_id) {
5147
                            continue;
5148
                        }
5149
                    }
5150
                }
5151
5152
                $all_exercises = 0;
5153
                $all_unanswered_exercises_by_user = 0;
5154
                $all_average = 0;
5155
                $stats_array = [];
5156
5157
                foreach ($course_list as $course_data) {
5158
                    // All exercises in the course @todo change for a real count
5159
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5160
                    $count_exercises = 0;
5161
                    if (is_array($exercises) && !empty($exercises)) {
5162
                        $count_exercises = count($exercises);
5163
                    }
5164
5165
                    // Count of user results
5166
                    $done_exercises = null;
5167
                    $courseInfo = api_get_course_info($course_data['code']);
5168
5169
                    $answered_exercises = 0;
5170
                    if (!empty($exercises)) {
5171
                        foreach ($exercises as $exercise_item) {
5172
                            $attempts = Event::count_exercise_attempts_by_user(
5173
                                api_get_user_id(),
5174
                                $exercise_item['iid'],
5175
                                $courseInfo['real_id'],
5176
                                $my_session_id
5177
                            );
5178
                            if ($attempts > 1) {
5179
                                $answered_exercises++;
5180
                            }
5181
                        }
5182
                    }
5183
5184
                    // Average
5185
                    $average = ExerciseLib::get_average_score_by_course(
5186
                        $courseInfo['real_id'],
5187
                        $my_session_id
5188
                    );
5189
                    $all_exercises += $count_exercises;
5190
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5191
                    $all_average += $average;
5192
                }
5193
5194
                if (!empty($course_list)) {
5195
                    $all_average = $all_average / count($course_list);
5196
                }
5197
5198
                $filterBySession = isset($_GET['session_id']) && $my_session_id == $_GET['session_id'];
5199
5200
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5201
5202
                $sessionsTable->addRow(
5203
                    [
5204
                        Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]),
5205
                        $all_exercises,
5206
                        $all_unanswered_exercises_by_user,
5207
                        ExerciseLib::convert_to_percentage($all_average),
5208
                        Display::url(
5209
                            Display::return_icon(
5210
                                $filterBySession ? '2rightarrow_na.png' : '2rightarrow.png',
5211
                                get_lang('Details')
5212
                            ),
5213
                            api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5214
                        ),
5215
                    ],
5216
                    ['style' => $filterBySession ? 'background-color: #FBF09D;' : '']
5217
                );
5218
            }
5219
5220
            if ($allowCareerUser) {
5221
                $diagrams = '';
5222
                if (!empty($visibleSessions)) {
5223
                    $diagrams .= SessionManager::getCareerDiagramPerSessionList($visibleSessions, $user_id);
5224
                }
5225
                $html .= $diagrams.MyStudents::userCareersTable($user_id);
5226
            }
5227
5228
            $html .= Display::div($sessionsTable->toHtml(), ['class' => 'table-responsive']);
5229
5230
            if ($showGraph) {
5231
                $html .= Display::div(
5232
                    $main_session_graph,
5233
                    [
5234
                        'id' => 'session_graph',
5235
                        'class' => 'chart-session',
5236
                    ]
5237
                );
5238
            }
5239
5240
            // Checking selected session.
5241
            if (isset($_GET['session_id'])) {
5242
                $session_id_from_get = (int) $_GET['session_id'];
5243
                $session_data = $course_in_session[$session_id_from_get];
5244
                $course_list = $session_data['course_list'];
5245
5246
                $html .= '<a name="course_session_list"></a>';
5247
                $html .= Display::page_subheader($session_data['name'], get_lang('CourseList'));
5248
5249
                $columnHeaders = array_filter(
5250
                    [
5251
                        'course_title' => get_lang('Course'),
5252
                        'published_exercises' => get_lang('PublishedExercises'),
5253
                        'new_exercises' => get_lang('NewExercises'),
5254
                        'my_average' => get_lang('MyAverage'),
5255
                        'average_exercise_result' => get_lang('AverageExerciseResult'),
5256
                        'time_spent' => get_lang('TimeSpentInTheCourse'),
5257
                        'lp_progress' => get_lang('LPProgress'),
5258
                        'score' => get_lang('Score')
5259
                            .Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage')),
5260
                        'best_score' => get_lang('BestScore'),
5261
                        'last_connection' => get_lang('LastConnexion'),
5262
                        'details' => get_lang('Details'),
5263
                    ],
5264
                    function ($column, $key) use ($trackingColumns) {
5265
                        if (isset($trackingColumns['course_session']) &&
5266
                            in_array($key, $trackingColumns['course_session']) &&
5267
                            $trackingColumns['course_session'][$key]
5268
                        ) {
5269
                            return true;
5270
                        }
5271
5272
                        return false;
5273
                    },
5274
                    ARRAY_FILTER_USE_BOTH
5275
                );
5276
5277
                $sessionCoursesTable = new SortableTableFromArray([], 0, 0, 'session_courses');
5278
                $sessionCoursesTable->setHeaders($columnHeaders);
5279
5280
                foreach ($course_list as $course_data) {
5281
                    $course_code = $course_data['code'];
5282
                    $course_title = $course_data['title'];
5283
                    $courseId = $course_data['real_id'];
5284
5285
                    // All exercises in the course @todo change for a real count
5286
                    $exercises = ExerciseLib::get_all_exercises(
5287
                        $course_data,
5288
                        $session_id_from_get
5289
                    );
5290
                    $count_exercises = 0;
5291
                    if (!empty($exercises)) {
5292
                        $count_exercises = count($exercises);
5293
                    }
5294
                    $answered_exercises = 0;
5295
                    foreach ($exercises as $exercise_item) {
5296
                        $attempts = Event::count_exercise_attempts_by_user(
5297
                            api_get_user_id(),
5298
                            $exercise_item['iid'],
5299
                            $courseId,
5300
                            $session_id_from_get
5301
                        );
5302
                        if ($attempts > 1) {
5303
                            $answered_exercises++;
5304
                        }
5305
                    }
5306
5307
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5308
5309
                    // Average
5310
                    $average = ExerciseLib::get_average_score_by_course(
5311
                        $courseId,
5312
                        $session_id_from_get
5313
                    );
5314
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5315
                        api_get_user_id(),
5316
                        $courseId,
5317
                        $session_id_from_get
5318
                    );
5319
5320
                    $bestScore = self::get_avg_student_score(
5321
                        $user_id,
5322
                        $course_code,
5323
                        [],
5324
                        $session_id_from_get,
5325
                        false,
5326
                        false,
5327
                        true
5328
                    );
5329
5330
                    $stats_array[$course_code] = [
5331
                        'exercises' => $count_exercises,
5332
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5333
                        'done_exercises' => $done_exercises,
5334
                        'average' => $average,
5335
                        'my_average' => $my_average,
5336
                        'best_score' => $bestScore,
5337
                    ];
5338
5339
                    $last_connection = self::get_last_connection_date_on_the_course(
5340
                        $user_id,
5341
                        $course_data,
5342
                        $session_id_from_get
5343
                    );
5344
5345
                    $progress = self::get_avg_student_progress(
5346
                        $user_id,
5347
                        $course_code,
5348
                        [],
5349
                        $session_id_from_get
5350
                    );
5351
5352
                    $total_time_login = self::get_time_spent_on_the_course(
5353
                        $user_id,
5354
                        $courseId,
5355
                        $session_id_from_get
5356
                    );
5357
                    $time = api_time_to_hms($total_time_login);
5358
5359
                    $percentage_score = self::get_avg_student_score(
5360
                        $user_id,
5361
                        $course_code,
5362
                        [],
5363
                        $session_id_from_get
5364
                    );
5365
                    $courseCodeFromGet = $_GET['course'] ?? null;
5366
5367
                    $filterByCourse = $course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get;
5368
5369
                    $url = api_get_course_url($course_code, $session_id_from_get);
5370
                    $course_url = Display::url(
5371
                        $course_title,
5372
                        $url,
5373
                        ['target' => SESSION_LINK_TARGET]
5374
                    );
5375
5376
                    if (is_numeric($progress)) {
5377
                        $progress = $progress.'%';
5378
                    } else {
5379
                        $progress = '0%';
5380
                    }
5381
                    if (is_numeric($percentage_score)) {
5382
                        $percentage_score = $percentage_score.'%';
5383
                    } else {
5384
                        $percentage_score = '0%';
5385
                    }
5386
5387
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5388
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5389
                    } else {
5390
                        $bestScore = '-';
5391
                    }
5392
5393
                    if (empty($last_connection) || is_bool($last_connection)) {
5394
                        $last_connection = '';
5395
                    }
5396
5397
                    if ($course_code == $courseCodeFromGet &&
5398
                        $_GET['session_id'] == $session_id_from_get
5399
                    ) {
5400
                        $details = Display::url(
5401
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
5402
                        '#course_session_data'
5403
                        );
5404
                    } else {
5405
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5406
                        $details = Display::url(
5407
                            Display::return_icon(
5408
                                '2rightarrow.png',
5409
                                get_lang('Details')
5410
                            ),
5411
                            $url
5412
                        );
5413
                    }
5414
5415
                    $data = array_filter(
5416
                        [
5417
                            'course_title' => $course_url,
5418
                            'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5419
                            'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5420
                            'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5421
                            'average_exercise_result' => $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5422
                            'time_spent' => $time,
5423
                            'lp_progress' => $progress,
5424
                            'score' => $percentage_score,
5425
                            'best_score' => $bestScore,
5426
                            'last_connection' => $last_connection,
5427
                            'details' => $details,
5428
                        ],
5429
                        function ($value, $key) use ($trackingColumns) {
5430
                            if (in_array($key, $trackingColumns['course_session']) && $trackingColumns['course_session'][$key]) {
5431
                                return true;
5432
                            }
5433
5434
                            return false;
5435
                        },
5436
                        ARRAY_FILTER_USE_BOTH
5437
                    );
5438
5439
                    $sessionCoursesTable->addRow(
5440
                        array_values($data),
5441
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
5442
                    );
5443
                }
5444
5445
                $html .= Display::div($sessionCoursesTable->toHtml(), ['class' => 'table-responsive']);
5446
            }
5447
        }
5448
5449
        $pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true';
5450
        if ($pluginCalendar) {
5451
            $course_in_session[0] = $courseIdList;
5452
            $plugin = LearningCalendarPlugin::create();
5453
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
5454
        }
5455
5456
        return $html;
5457
    }
5458
5459
    /**
5460
     * Shows the user detail progress (when clicking in the details link).
5461
     *
5462
     * @param int    $user_id
5463
     * @param string $course_code
5464
     * @param int    $session_id
5465
     */
5466
    public static function show_course_detail($user_id, $course_code, $session_id, $isAllowedToEdit = true): string
5467
    {
5468
        if (empty($user_id) || empty($course_code)) {
5469
            return '';
5470
        }
5471
5472
        $course_info = api_get_course_info($course_code);
5473
        if (empty($course_info)) {
5474
            return '';
5475
        }
5476
5477
        $user_id = (int) $user_id;
5478
        $user = api_get_user_entity($user_id);
5479
        $session_id = (int) $session_id;
5480
5481
        $html = '<a name="course_session_data"></a>';
5482
        $html .= Display::page_subheader2($course_info['title']);
5483
        // Show exercise results of invisible exercises? see BT#4091
5484
        $quizzesHtml = self::generateQuizzesTable($course_info, $session_id);
5485
        // LP table results
5486
        $learningPathsHtml = self::generateLearningPathsTable($user, $course_info, $session_id, $isAllowedToEdit);
5487
        $skillsHtml = self::displayUserSkills($user_id, $course_info['id'], $session_id);
5488
5489
        $toolsHtml = [
5490
            'quizzes' => $quizzesHtml,
5491
            'learning_paths' => $learningPathsHtml,
5492
            'skills' => $skillsHtml,
5493
        ];
5494
5495
        $toolsOrder = api_get_configuration_value('my_progress_course_tools_order');
5496
5497
        if (empty($toolsOrder)) {
5498
            $html .= implode(PHP_EOL, $toolsHtml);
5499
        } else {
5500
            foreach ($toolsOrder['order'] as $tool) {
5501
                $html .= $toolsHtml[$tool].PHP_EOL;
5502
            }
5503
        }
5504
5505
        return $html;
5506
    }
5507
5508
    /**
5509
     * Generates an histogram.
5510
     *
5511
     * @param array $names      list of exercise names
5512
     * @param array $my_results my results 0 to 100
5513
     * @param array $average    average scores 0-100
5514
     *
5515
     * @return string
5516
     */
5517
    public static function generate_session_exercise_graph($names, $my_results, $average)
5518
    {
5519
        $html = api_get_js('chartjs/Chart.js');
5520
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
5521
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
5522
        $jsStr = " var data = {
5523
                       labels:".json_encode($names).",
5524
                       datasets: [
5525
                       {
5526
                         label: '".get_lang('MyResults')."',
5527
                         backgroundColor: 'rgb(255, 99, 132)',
5528
                         stack: 'Stack1',
5529
                         data: ".json_encode($my_results).",
5530
                        },
5531
                        {
5532
                         label: '".get_lang('AverageScore')."',
5533
                         backgroundColor: 'rgb(75, 192, 192)',
5534
                         stack: 'Stack2',
5535
                         data: ".json_encode($average).",
5536
                        },
5537
                        ],
5538
                    };
5539
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
5540
                    var myBarChart = new Chart(ctx, {
5541
                    type: 'bar',
5542
                    data: data,
5543
                    options: {
5544
                            title: {
5545
                                    display: true,
5546
                                    text: '".get_lang('ExercisesInTimeProgressChart')."'
5547
                            },
5548
                            tooltips: {
5549
                                    mode: 'index',
5550
                                    intersect: false
5551
                            },
5552
                            responsive: true,
5553
                            scales: {
5554
                                yAxes: [{
5555
                                    ticks: {
5556
                                        // Include a dollar sign in the ticks
5557
                                        callback: function(value, index, values) {
5558
                                            return value + '%';
5559
                                        }
5560
                                    }
5561
                                }]
5562
                            }
5563
                    }
5564
                });";
5565
        $html .= Display::tag('script', $jsStr);
5566
5567
        return $html;
5568
    }
5569
5570
    /**
5571
     * Returns a thumbnail of the function generate_exercise_result_graph.
5572
     *
5573
     * @param array $attempts
5574
     */
5575
    public static function generate_exercise_result_thumbnail_graph($attempts)
5576
    {
5577
        //$exercise_title = $attempts['title'];
5578
        $attempts = $attempts['data'];
5579
        $my_exercise_result_array = $exercise_result = [];
5580
        if (empty($attempts)) {
5581
            return null;
5582
        }
5583
5584
        foreach ($attempts as $attempt) {
5585
            if (api_get_user_id() == $attempt['exe_user_id']) {
5586
                if ($attempt['exe_weighting'] != 0) {
5587
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5588
                }
5589
            } else {
5590
                if ($attempt['exe_weighting'] != 0) {
5591
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5592
                }
5593
            }
5594
        }
5595
5596
        // Getting best result
5597
        rsort($my_exercise_result_array);
5598
        $my_exercise_result = 0;
5599
        if (isset($my_exercise_result_array[0])) {
5600
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5601
        }
5602
5603
        $max = 100;
5604
        $pieces = 5;
5605
        $part = round($max / $pieces);
5606
        $x_axis = [];
5607
        $final_array = [];
5608
        $my_final_array = [];
5609
5610
        for ($i = 1; $i <= $pieces; $i++) {
5611
            $sum = 1;
5612
            if ($i == 1) {
5613
                $sum = 0;
5614
            }
5615
            $min = ($i - 1) * $part + $sum;
5616
            $max = ($i) * $part;
5617
            $x_axis[] = $min." - ".$max;
5618
            $count = 0;
5619
            foreach ($exercise_result as $result) {
5620
                $percentage = $result * 100;
5621
                if ($percentage >= $min && $percentage <= $max) {
5622
                    //echo ' is > ';
5623
                    $count++;
5624
                }
5625
            }
5626
            //echo '<br />';
5627
            $final_array[] = $count;
5628
5629
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5630
                $my_final_array[] = 1;
5631
            } else {
5632
                $my_final_array[] = 0;
5633
            }
5634
        }
5635
5636
        // Fix to remove the data of the user with my data
5637
        for ($i = 0; $i <= count($my_final_array); $i++) {
5638
            if (!empty($my_final_array[$i])) {
5639
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5640
                $final_array[$i] = 0;
5641
            }
5642
        }
5643
5644
        // Dataset definition
5645
        $dataSet = new pData();
5646
        $dataSet->addPoints($final_array, 'Serie1');
5647
        $dataSet->addPoints($my_final_array, 'Serie2');
5648
        $dataSet->normalize(100, "%");
5649
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5650
5651
        // Cache definition
5652
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5653
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5654
        $chartHash = $myCache->getHash($dataSet);
5655
        if ($myCache->isInCache($chartHash)) {
5656
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5657
            $myCache->saveFromCache($chartHash, $imgPath);
5658
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5659
        } else {
5660
            /* Create the pChart object */
5661
            $widthSize = 80;
5662
            $heightSize = 35;
5663
            $fontSize = 2;
5664
5665
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5666
5667
            /* Turn of Antialiasing */
5668
            $myPicture->Antialias = false;
5669
5670
            /* Add a border to the picture */
5671
            $myPicture->drawRectangle(
5672
                0,
5673
                0,
5674
                $widthSize - 1,
5675
                $heightSize - 1,
5676
                ['R' => 0, 'G' => 0, 'B' => 0]
5677
            );
5678
5679
            /* Set the default font */
5680
            $myPicture->setFontProperties(
5681
                [
5682
                    'FontName' => api_get_path(
5683
                            SYS_FONTS_PATH
5684
                        ).'opensans/OpenSans-Regular.ttf',
5685
                    'FontSize' => $fontSize,
5686
                ]
5687
            );
5688
5689
            /* Do not write the chart title */
5690
            /* Define the chart area */
5691
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
5692
5693
            /* Draw the scale */
5694
            $scaleSettings = [
5695
                'GridR' => 200,
5696
                'GridG' => 200,
5697
                'GridB' => 200,
5698
                'DrawSubTicks' => true,
5699
                'CycleBackground' => true,
5700
                'Mode' => SCALE_MODE_MANUAL,
5701
                'ManualScale' => [
5702
                    '0' => [
5703
                        'Min' => 0,
5704
                        'Max' => 100,
5705
                    ],
5706
                ],
5707
            ];
5708
            $myPicture->drawScale($scaleSettings);
5709
5710
            /* Turn on shadow computing */
5711
            $myPicture->setShadow(
5712
                true,
5713
                [
5714
                    'X' => 1,
5715
                    'Y' => 1,
5716
                    'R' => 0,
5717
                    'G' => 0,
5718
                    'B' => 0,
5719
                    'Alpha' => 10,
5720
                ]
5721
            );
5722
5723
            /* Draw the chart */
5724
            $myPicture->setShadow(
5725
                true,
5726
                [
5727
                    'X' => 1,
5728
                    'Y' => 1,
5729
                    'R' => 0,
5730
                    'G' => 0,
5731
                    'B' => 0,
5732
                    'Alpha' => 10,
5733
                ]
5734
            );
5735
            $settings = [
5736
                'DisplayValues' => true,
5737
                'DisplaySize' => $fontSize,
5738
                'DisplayR' => 0,
5739
                'DisplayG' => 0,
5740
                'DisplayB' => 0,
5741
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5742
                'Gradient' => false,
5743
                'Surrounding' => 5,
5744
                'InnerSurrounding' => 5,
5745
            ];
5746
            $myPicture->drawStackedBarChart($settings);
5747
5748
            /* Save and write in cache */
5749
            $myCache->writeToCache($chartHash, $myPicture);
5750
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5751
            $myCache->saveFromCache($chartHash, $imgPath);
5752
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5753
        }
5754
5755
        return $imgPath;
5756
    }
5757
5758
    /**
5759
     * Generates a big graph with the number of best results.
5760
     *
5761
     * @param	array
5762
     */
5763
    public static function generate_exercise_result_graph($attempts)
5764
    {
5765
        $exercise_title = strip_tags($attempts['title']);
5766
        $attempts = $attempts['data'];
5767
        $my_exercise_result_array = $exercise_result = [];
5768
        if (empty($attempts)) {
5769
            return null;
5770
        }
5771
        foreach ($attempts as $attempt) {
5772
            if (api_get_user_id() == $attempt['exe_user_id']) {
5773
                if ($attempt['exe_weighting'] != 0) {
5774
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5775
                }
5776
            } else {
5777
                if ($attempt['exe_weighting'] != 0) {
5778
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5779
                }
5780
            }
5781
        }
5782
5783
        //Getting best result
5784
        rsort($my_exercise_result_array);
5785
        $my_exercise_result = 0;
5786
        if (isset($my_exercise_result_array[0])) {
5787
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5788
        }
5789
5790
        $max = 100;
5791
        $pieces = 5;
5792
        $part = round($max / $pieces);
5793
        $x_axis = [];
5794
        $final_array = [];
5795
        $my_final_array = [];
5796
5797
        for ($i = 1; $i <= $pieces; $i++) {
5798
            $sum = 1;
5799
            if ($i == 1) {
5800
                $sum = 0;
5801
            }
5802
            $min = ($i - 1) * $part + $sum;
5803
            $max = ($i) * $part;
5804
            $x_axis[] = $min." - ".$max;
5805
            $count = 0;
5806
            foreach ($exercise_result as $result) {
5807
                $percentage = $result * 100;
5808
                if ($percentage >= $min && $percentage <= $max) {
5809
                    $count++;
5810
                }
5811
            }
5812
            $final_array[] = $count;
5813
5814
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5815
                $my_final_array[] = 1;
5816
            } else {
5817
                $my_final_array[] = 0;
5818
            }
5819
        }
5820
5821
        //Fix to remove the data of the user with my data
5822
5823
        for ($i = 0; $i <= count($my_final_array); $i++) {
5824
            if (!empty($my_final_array[$i])) {
5825
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5826
                $final_array[$i] = 0;
5827
            }
5828
        }
5829
5830
        // Dataset definition
5831
        $dataSet = new pData();
5832
        $dataSet->addPoints($final_array, 'Serie1');
5833
        $dataSet->addPoints($my_final_array, 'Serie2');
5834
        $dataSet->addPoints($x_axis, 'Serie3');
5835
5836
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
5837
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
5838
        $dataSet->setAbscissa('Serie3');
5839
5840
        $dataSet->setXAxisName(get_lang('Score'));
5841
        $dataSet->normalize(100, "%");
5842
5843
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5844
5845
        // Cache definition
5846
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5847
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5848
        $chartHash = $myCache->getHash($dataSet);
5849
5850
        if ($myCache->isInCache($chartHash)) {
5851
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5852
            $myCache->saveFromCache($chartHash, $imgPath);
5853
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5854
        } else {
5855
            /* Create the pChart object */
5856
            $widthSize = 480;
5857
            $heightSize = 250;
5858
            $fontSize = 8;
5859
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5860
5861
            /* Turn of Antialiasing */
5862
            $myPicture->Antialias = false;
5863
5864
            /* Add a border to the picture */
5865
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
5866
5867
            /* Set the default font */
5868
            $myPicture->setFontProperties(
5869
                [
5870
                    'FontName' => api_get_path(
5871
                            SYS_FONTS_PATH
5872
                        ).'opensans/OpenSans-Regular.ttf',
5873
                    'FontSize' => 10,
5874
                ]
5875
            );
5876
5877
            /* Write the chart title */
5878
            $myPicture->drawText(
5879
                250,
5880
                20,
5881
                $exercise_title,
5882
                [
5883
                    'FontSize' => 12,
5884
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
5885
                ]
5886
            );
5887
5888
            /* Define the chart area */
5889
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
5890
5891
            /* Draw the scale */
5892
            $scaleSettings = [
5893
                'GridR' => 200,
5894
                'GridG' => 200,
5895
                'GridB' => 200,
5896
                'DrawSubTicks' => true,
5897
                'CycleBackground' => true,
5898
                'Mode' => SCALE_MODE_MANUAL,
5899
                'ManualScale' => [
5900
                    '0' => [
5901
                        'Min' => 0,
5902
                        'Max' => 100,
5903
                    ],
5904
                ],
5905
            ];
5906
            $myPicture->drawScale($scaleSettings);
5907
5908
            /* Turn on shadow computing */
5909
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
5910
5911
            /* Draw the chart */
5912
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
5913
            $settings = [
5914
                'DisplayValues' => true,
5915
                'DisplaySize' => $fontSize,
5916
                'DisplayR' => 0,
5917
                'DisplayG' => 0,
5918
                'DisplayB' => 0,
5919
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5920
                'Gradient' => false,
5921
                'Surrounding' => 30,
5922
                'InnerSurrounding' => 25,
5923
            ];
5924
            $myPicture->drawStackedBarChart($settings);
5925
5926
            $legendSettings = [
5927
                'Mode' => LEGEND_HORIZONTAL,
5928
                'Style' => LEGEND_NOBORDER,
5929
            ];
5930
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
5931
5932
            /* Write and save into cache */
5933
            $myCache->writeToCache($chartHash, $myPicture);
5934
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5935
            $myCache->saveFromCache($chartHash, $imgPath);
5936
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5937
        }
5938
5939
        return $imgPath;
5940
    }
5941
5942
    /**
5943
     * @param FormValidator $form
5944
     *
5945
     * @return mixed
5946
     */
5947
    public static function setUserSearchForm($form)
5948
    {
5949
        global $_configuration;
5950
        $form->addElement('text', 'keyword', get_lang('Keyword'));
5951
        $form->addElement(
5952
            'select',
5953
            'active',
5954
            get_lang('Status'),
5955
            [1 => get_lang('Active'), 0 => get_lang('Inactive')]
5956
        );
5957
5958
        $form->addElement(
5959
            'select',
5960
            'sleeping_days',
5961
            get_lang('InactiveDays'),
5962
            [
5963
                '',
5964
                1 => 1,
5965
                5 => 5,
5966
                15 => 15,
5967
                30 => 30,
5968
                60 => 60,
5969
                90 => 90,
5970
                120 => 120,
5971
            ]
5972
        );
5973
5974
        $form->addButtonSearch(get_lang('Search'));
5975
5976
        return $form;
5977
    }
5978
5979
    /**
5980
     * Get the progress of a exercise.
5981
     *
5982
     * @param int    $sessionId  The session ID (session.id)
5983
     * @param int    $courseId   The course ID (course.id)
5984
     * @param int    $exerciseId The quiz ID (c_quiz.id)
5985
     * @param string $date_from
5986
     * @param string $date_to
5987
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
5988
     *
5989
     * @return array An array with the data of exercise(s) progress
5990
     */
5991
    public static function get_exercise_progress(
5992
        $sessionId = 0,
5993
        $courseId = 0,
5994
        $exerciseId = 0,
5995
        $date_from = null,
5996
        $date_to = null,
5997
        $options = []
5998
    ) {
5999
        $sessionId = intval($sessionId);
6000
        $courseId = intval($courseId);
6001
        $exerciseId = intval($exerciseId);
6002
        $date_from = Database::escape_string($date_from);
6003
        $date_to = Database::escape_string($date_to);
6004
        /*
6005
         * This method gets the data by blocks, as previous attempts at one single
6006
         * query made it take ages. The logic of query division is described below
6007
         */
6008
        // Get tables names
6009
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6010
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6011
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6012
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6013
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6014
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6015
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6016
6017
        $sessions = [];
6018
        $courses = [];
6019
        // if session ID is defined but course ID is empty, get all the courses
6020
        // from that session
6021
        if (!empty($sessionId) && empty($courseId)) {
6022
            // $courses is an array of course int id as index and course details hash as value
6023
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6024
            $sessions[$sessionId] = api_get_session_info($sessionId);
6025
        } elseif (empty($sessionId) && !empty($courseId)) {
6026
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6027
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
6028
            $course = api_get_course_info_by_id($courseId);
6029
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6030
            $courses[$courseId] = $course;
6031
            foreach ($sessionsTemp as $sessionItem) {
6032
                $sessions[$sessionItem['id']] = $sessionItem;
6033
            }
6034
        } elseif (!empty($courseId) && !empty($sessionId)) {
6035
            //none is empty
6036
            $course = api_get_course_info_by_id($courseId);
6037
            $courses[$courseId] = [$course['code']];
6038
            $courses[$courseId]['code'] = $course['code'];
6039
            $sessions[$sessionId] = api_get_session_info($sessionId);
6040
        } else {
6041
            //both are empty, not enough data, return an empty array
6042
            return [];
6043
        }
6044
        // Now we have two arrays of courses and sessions with enough data to proceed
6045
        // If no course could be found, we shouldn't return anything.
6046
        // Sessions can be empty (then we only return the pure-course-context results)
6047
        if (count($courses) < 1) {
6048
            return [];
6049
        }
6050
6051
        $data = [];
6052
        // The following loop is less expensive than what it seems:
6053
        // - if a course was defined, then we only loop through sessions
6054
        // - if a session was defined, then we only loop through courses
6055
        // - if a session and a course were defined, then we only loop once
6056
        foreach ($courses as $courseIdx => $courseData) {
6057
            $where = '';
6058
            $whereParams = [];
6059
            $whereSessionParams = '';
6060
            if (count($sessions > 0)) {
6061
                foreach ($sessions as $sessionIdx => $sessionData) {
6062
                    if (!empty($sessionIdx)) {
6063
                        $whereSessionParams .= $sessionIdx.',';
6064
                    }
6065
                }
6066
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6067
            }
6068
6069
            if (!empty($exerciseId)) {
6070
                $exerciseId = intval($exerciseId);
6071
                $where .= ' AND q.id = %d ';
6072
                $whereParams[] = $exerciseId;
6073
            }
6074
6075
            /*
6076
             * This feature has been disabled for now, to avoid having to
6077
             * join two very large tables
6078
            //2 = show all questions (wrong and correct answered)
6079
            if ($answer != 2) {
6080
                $answer = intval($answer);
6081
                //$where .= ' AND qa.correct = %d';
6082
                //$whereParams[] = $answer;
6083
            }
6084
            */
6085
6086
            $limit = '';
6087
            if (!empty($options['limit'])) {
6088
                $limit = " LIMIT ".$options['limit'];
6089
            }
6090
6091
            if (!empty($options['where'])) {
6092
                $where .= ' AND '.Database::escape_string($options['where']);
6093
            }
6094
6095
            $order = '';
6096
            if (!empty($options['order'])) {
6097
                $order = " ORDER BY ".$options['order'];
6098
            }
6099
6100
            if (!empty($date_to) && !empty($date_from)) {
6101
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6102
            }
6103
6104
            $sql = "SELECT
6105
                te.session_id,
6106
                ta.id as attempt_id,
6107
                te.exe_user_id as user_id,
6108
                te.exe_id as exercise_attempt_id,
6109
                ta.question_id,
6110
                ta.answer as answer_id,
6111
                ta.tms as time,
6112
                te.exe_exo_id as quiz_id,
6113
                CONCAT ('c', q.c_id, '_e', q.iid) as exercise_id,
6114
                q.title as quiz_title,
6115
                qq.description as description
6116
                FROM $ttrack_exercises te
6117
                INNER JOIN $ttrack_attempt ta
6118
                ON ta.exe_id = te.exe_id
6119
                INNER JOIN $tquiz q
6120
                ON q.iid = te.exe_exo_id
6121
                INNER JOIN $tquiz_rel_question rq
6122
                ON rq.exercice_id = q.iid AND rq.c_id = q.c_id
6123
                INNER JOIN $tquiz_question qq
6124
                ON
6125
                    qq.iid = rq.question_id AND
6126
                    qq.position = rq.question_order AND
6127
                    ta.question_id = rq.question_id
6128
                WHERE
6129
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6130
                    AND q.c_id = $courseIdx
6131
                    $where $order $limit";
6132
            $sql_query = vsprintf($sql, $whereParams);
6133
6134
            // Now browse through the results and get the data
6135
            $rs = Database::query($sql_query);
6136
            $userIds = [];
6137
            $questionIds = [];
6138
            $answerIds = [];
6139
            while ($row = Database::fetch_array($rs)) {
6140
                //only show if exercise is visible
6141
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6142
                    $userIds[$row['user_id']] = $row['user_id'];
6143
                    $questionIds[$row['question_id']] = $row['question_id'];
6144
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6145
                    $row['session'] = $sessions[$row['session_id']];
6146
                    $data[] = $row;
6147
                }
6148
            }
6149
            // Now fill questions data. Query all questions and answers for this test to avoid
6150
            $sqlQuestions = "SELECT tq.c_id, tq.iid as question_id, tq.question, tqa.id_auto,
6151
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
6152
                            FROM $tquiz_question tq, $tquiz_answer tqa
6153
                            WHERE
6154
                                tqa.question_id = tq.iid AND
6155
                                tq.c_id = $courseIdx AND
6156
                                tq.id IN (".implode(',', $questionIds).")";
6157
6158
            $resQuestions = Database::query($sqlQuestions);
6159
            $answer = [];
6160
            $question = [];
6161
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6162
                $questionId = $rowQuestion['question_id'];
6163
                $answerId = $rowQuestion['answer_id'];
6164
                $answer[$questionId][$answerId] = [
6165
                    'position' => $rowQuestion['position'],
6166
                    'question' => $rowQuestion['question'],
6167
                    'answer' => $rowQuestion['answer'],
6168
                    'correct' => $rowQuestion['correct'],
6169
                ];
6170
                $question[$questionId]['question'] = $rowQuestion['question'];
6171
            }
6172
6173
            // Now fill users data
6174
            $sqlUsers = "SELECT user_id, username, lastname, firstname
6175
                         FROM $tuser
6176
                         WHERE user_id IN (".implode(',', $userIds).")";
6177
            $resUsers = Database::query($sqlUsers);
6178
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6179
                $users[$rowUser['user_id']] = $rowUser;
6180
            }
6181
6182
            foreach ($data as $id => $row) {
6183
                $rowQuestId = $row['question_id'];
6184
                $rowAnsId = $row['answer_id'];
6185
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
6186
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
6187
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6188
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6189
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6190
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
6191
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6192
                $data[$id]['question_id'] = $rowQuestId;
6193
                $data[$id]['description'] = $row['description'];
6194
            }
6195
6196
            /*
6197
            The minimum expected array structure at the end is:
6198
            attempt_id,
6199
            session name,
6200
            exercise_id,
6201
            quiz_title,
6202
            username,
6203
            lastname,
6204
            firstname,
6205
            time,
6206
            question_id,
6207
            question,
6208
            answer,
6209
            */
6210
        }
6211
6212
        return $data;
6213
    }
6214
6215
    /**
6216
     * @param string              $tool
6217
     * @param sessionEntity |null $session Optional
6218
     *
6219
     * @throws \Doctrine\ORM\NonUniqueResultException
6220
     *
6221
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
6222
     */
6223
    public static function getLastStudentPublication(
6224
        User $user,
6225
        $tool,
6226
        Course $course,
6227
        SessionEntity $session = null
6228
    ) {
6229
        return Database::getManager()
6230
            ->createQuery("
6231
                SELECT csp
6232
                FROM ChamiloCourseBundle:CStudentPublication csp
6233
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6234
                    WITH (
6235
                        csp.iid = cip.ref AND
6236
                        csp.session = cip.session AND
6237
                        csp.cId = cip.course AND
6238
                        csp.userId = cip.lasteditUserId
6239
                    )
6240
                WHERE
6241
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6242
                ORDER BY csp.iid DESC
6243
            ")
6244
            ->setMaxResults(1)
6245
            ->setParameters([
6246
                'tool' => $tool,
6247
                'session' => $session,
6248
                'course' => $course,
6249
                'user' => $user,
6250
            ])
6251
            ->getOneOrNullResult();
6252
    }
6253
6254
    /**
6255
     * Get the HTML code for show a block with the achieved user skill on course/session.
6256
     *
6257
     * @param int  $userId
6258
     * @param int  $courseId
6259
     * @param int  $sessionId
6260
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6261
     *
6262
     * @return string
6263
     */
6264
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6265
    {
6266
        if (Skill::isAllowed($userId, false) === false && $forceView == false) {
6267
            return '';
6268
        }
6269
        $skillManager = new Skill();
6270
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6271
6272
        return $html;
6273
    }
6274
6275
    /**
6276
     * @param int $userId
6277
     * @param int $courseId
6278
     * @param int $sessionId
6279
     *
6280
     * @return array
6281
     */
6282
    public static function getCalculateTime($userId, $courseId, $sessionId)
6283
    {
6284
        $userId = (int) $userId;
6285
        $courseId = (int) $courseId;
6286
        $sessionId = (int) $sessionId;
6287
6288
        if (empty($userId) || empty($courseId)) {
6289
            return [];
6290
        }
6291
6292
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6293
                FROM track_e_access_complete
6294
                WHERE
6295
                    user_id = $userId AND
6296
                    c_id = $courseId AND
6297
                    session_id = $sessionId AND
6298
                    login_as = 0
6299
                ORDER BY date_reg ASC
6300
                LIMIT 1";
6301
        $rs = Database::query($sql);
6302
6303
        $firstConnection = '';
6304
        $lastConnection = '';
6305
        if (Database::num_rows($rs) > 0) {
6306
            $value = Database::fetch_array($rs);
6307
            $firstConnection = $value['min'];
6308
            $lastConnection = $value['max'];
6309
        }
6310
6311
        $sql = "SELECT * FROM track_e_access_complete
6312
                WHERE
6313
                    user_id = $userId AND
6314
                    c_id = $courseId AND
6315
                    session_id = $sessionId AND
6316
                    login_as = 0 AND current_id <> 0";
6317
6318
        $res = Database::query($sql);
6319
        $reg = [];
6320
        while ($row = Database::fetch_assoc($res)) {
6321
            $reg[$row['id']] = $row;
6322
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6323
        }
6324
6325
        $sessions = [];
6326
        foreach ($reg as $key => $value) {
6327
            $sessions[$value['current_id']][$value['tool']][] = $value;
6328
        }
6329
6330
        $quizTime = 0;
6331
        $result = [];
6332
        $totalTime = 0;
6333
        $lpTime = [];
6334
        $lpDetailTime = [];
6335
        foreach ($sessions as $listPerTool) {
6336
            $min = 0;
6337
            $max = 0;
6338
            $sessionDiff = 0;
6339
            foreach ($listPerTool as $tool => $results) {
6340
                $beforeItem = [];
6341
                foreach ($results as $item) {
6342
                    if (empty($beforeItem)) {
6343
                        $beforeItem = $item;
6344
                        if (empty($min)) {
6345
                            $min = $item['date_reg'];
6346
                        }
6347
6348
                        if (empty($max)) {
6349
                            $max = $item['date_reg'];
6350
                        }
6351
                        continue;
6352
                    }
6353
6354
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6355
                    if ($item['date_reg'] > $max) {
6356
                        $max = $item['date_reg'];
6357
                    }
6358
6359
                    if (empty($min)) {
6360
                        $min = $item['date_reg'];
6361
                    }
6362
6363
                    if ($item['date_reg'] < $min) {
6364
                        $min = $item['date_reg'];
6365
                    }
6366
6367
                    switch ($tool) {
6368
                        case TOOL_AGENDA:
6369
                        case TOOL_FORUM:
6370
                        case TOOL_ANNOUNCEMENT:
6371
                        case TOOL_COURSE_DESCRIPTION:
6372
                        case TOOL_SURVEY:
6373
                        case TOOL_NOTEBOOK:
6374
                        case TOOL_GRADEBOOK:
6375
                        case TOOL_DROPBOX:
6376
                        case 'Reports':
6377
                        case 'Videoconference':
6378
                        case TOOL_LINK:
6379
                        case TOOL_CHAT:
6380
                        case 'course-main':
6381
                            if (!isset($result[$tool])) {
6382
                                $result[$tool] = 0;
6383
                            }
6384
                            $result[$tool] += $partialTime;
6385
                            break;
6386
                        case TOOL_LEARNPATH:
6387
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6388
                                break;
6389
                            }
6390
                            if (!isset($lpTime[$item['tool_id']])) {
6391
                                $lpTime[$item['tool_id']] = 0;
6392
                            }
6393
6394
                            // Saving the attempt id "action_details"
6395
                            if (!empty($item['tool_id'])) {
6396
                                if (!empty($item['tool_id_detail'])) {
6397
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6398
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6399
                                    }
6400
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6401
                                }
6402
                                $lpTime[$item['tool_id']] += $partialTime;
6403
                            }
6404
                            break;
6405
                        case TOOL_QUIZ:
6406
                            if (!isset($lpTime[$item['action_details']])) {
6407
                                $lpTime[$item['action_details']] = 0;
6408
                            }
6409
                            if ($beforeItem['action'] === 'learnpath_id') {
6410
                                $lpTime[$item['action_details']] += $partialTime;
6411
                            } else {
6412
                                $quizTime += $partialTime;
6413
                            }
6414
                            break;
6415
                    }
6416
                    $beforeItem = $item;
6417
                }
6418
            }
6419
6420
            $sessionDiff += $max - $min;
6421
            if ($sessionDiff > 0) {
6422
                $totalTime += $sessionDiff;
6423
            }
6424
        }
6425
6426
        $totalLp = 0;
6427
        foreach ($lpTime as $value) {
6428
            $totalLp += $value;
6429
        }
6430
6431
        $result['learnpath_detailed'] = $lpDetailTime;
6432
        $result[TOOL_LEARNPATH] = $lpTime;
6433
        $result[TOOL_QUIZ] = $quizTime;
6434
        $result['total_learnpath'] = $totalLp;
6435
        $result['total_time'] = $totalTime;
6436
        $result['number_connections'] = count($sessions);
6437
        $result['first'] = $firstConnection;
6438
        $result['last'] = $lastConnection;
6439
6440
        return $result;
6441
    }
6442
6443
    /**
6444
     * Gets the IP of a given user, using the last login before the given date.
6445
     *
6446
     * @param int User ID
6447
     * @param string Datetime
6448
     * @param bool Whether to return the IP as a link or just as an IP
6449
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6450
     *
6451
     * @return string IP address (or false on error)
6452
     * @assert (0,0) === false
6453
     */
6454
    public static function get_ip_from_user_event(
6455
        $user_id,
6456
        $event_date,
6457
        $return_as_link = false,
6458
        $body_replace = null
6459
    ) {
6460
        if (empty($user_id) || empty($event_date)) {
6461
            return false;
6462
        }
6463
        $user_id = intval($user_id);
6464
        $event_date = Database::escape_string($event_date);
6465
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6466
        $sql_ip = "SELECT login_date, user_ip
6467
                   FROM $table_login
6468
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6469
                   ORDER BY login_date DESC LIMIT 1";
6470
        $ip = '';
6471
        $res_ip = Database::query($sql_ip);
6472
        if ($res_ip !== false && Database::num_rows($res_ip) > 0) {
6473
            $row_ip = Database::fetch_row($res_ip);
6474
            if ($return_as_link) {
6475
                $ip = Display::url(
6476
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6477
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6478
                    ['title' => get_lang('TraceIP'), 'target' => '_blank']
6479
                );
6480
            } else {
6481
                $ip = $row_ip[1];
6482
            }
6483
        }
6484
6485
        return $ip;
6486
    }
6487
6488
    /**
6489
     * @param int   $userId
6490
     * @param array $courseInfo
6491
     * @param int   $sessionId
6492
     *
6493
     * @return array
6494
     */
6495
    public static function getToolInformation(
6496
        $userId,
6497
        $courseInfo,
6498
        $sessionId = 0
6499
    ) {
6500
        $csvContent = [];
6501
        $courseToolInformation = '';
6502
        $headerTool = [
6503
            [get_lang('Title')],
6504
            [get_lang('CreatedAt')],
6505
            [get_lang('UpdatedAt')],
6506
        ];
6507
6508
        $headerListForCSV = [];
6509
        foreach ($headerTool as $item) {
6510
            $headerListForCSV[] = $item[0];
6511
        }
6512
6513
        $courseForumInformationArray = getForumCreatedByUser(
6514
            $userId,
6515
            $courseInfo,
6516
            $sessionId
6517
        );
6518
6519
        if (!empty($courseForumInformationArray)) {
6520
            $csvContent[] = [];
6521
            $csvContent[] = [get_lang('Forums')];
6522
            $csvContent[] = $headerListForCSV;
6523
            foreach ($courseForumInformationArray as $row) {
6524
                $csvContent[] = $row;
6525
            }
6526
6527
            $courseToolInformation .= Display::page_subheader2(
6528
                get_lang('Forums')
6529
            );
6530
            $courseToolInformation .= Display::return_sortable_table(
6531
                $headerTool,
6532
                $courseForumInformationArray
6533
            );
6534
        }
6535
6536
        $courseWorkInformationArray = getWorkCreatedByUser(
6537
            $userId,
6538
            $courseInfo['real_id'],
6539
            $sessionId
6540
        );
6541
6542
        if (!empty($courseWorkInformationArray)) {
6543
            $csvContent[] = null;
6544
            $csvContent[] = [get_lang('Works')];
6545
            $csvContent[] = $headerListForCSV;
6546
6547
            foreach ($courseWorkInformationArray as $row) {
6548
                $csvContent[] = $row;
6549
            }
6550
            $csvContent[] = null;
6551
6552
            $courseToolInformation .= Display::page_subheader2(
6553
                get_lang('Works')
6554
            );
6555
            $courseToolInformation .= Display::return_sortable_table(
6556
                $headerTool,
6557
                $courseWorkInformationArray
6558
            );
6559
        }
6560
6561
        $courseToolInformationTotal = null;
6562
        if (!empty($courseToolInformation)) {
6563
            $sessionTitle = null;
6564
            if (!empty($sessionId)) {
6565
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6566
            }
6567
6568
            $courseToolInformationTotal .= Display::page_subheader(
6569
                $courseInfo['title'].$sessionTitle
6570
            );
6571
            $courseToolInformationTotal .= $courseToolInformation;
6572
        }
6573
6574
        return [
6575
            'array' => $csvContent,
6576
            'html' => $courseToolInformationTotal,
6577
        ];
6578
    }
6579
6580
    /**
6581
     * @param int $sessionId
6582
     *
6583
     * @return bool
6584
     */
6585
    public static function isAllowToTrack($sessionId)
6586
    {
6587
        return
6588
            api_is_platform_admin(true, true) ||
6589
            SessionManager::user_is_general_coach(api_get_user_id(), $sessionId) ||
6590
            api_is_allowed_to_create_course() ||
6591
            api_is_course_tutor() ||
6592
            api_is_course_admin();
6593
    }
6594
6595
    public static function getCourseLpProgress($userId, $sessionId)
6596
    {
6597
        $controller = new IndexManager(get_lang('MyCourses'));
6598
        $data = $controller->returnCoursesAndSessions($userId);
6599
        $courseList = $data['courses'];
6600
        $result = [];
6601
        if ($courseList) {
6602
            //$counter = 1;
6603
            foreach ($courseList as $course) {
6604
                $courseId = $course['course_id'];
6605
                $courseInfo = api_get_course_info_by_id($courseId);
6606
                if (empty($courseInfo)) {
6607
                    continue;
6608
                }
6609
                $courseCode = $courseInfo['code'];
6610
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
6611
6612
                // total progress
6613
                $list = new LearnpathList(
6614
                    $userId,
6615
                     $courseInfo,
6616
                    0,
6617
                    'lp.publicatedOn ASC',
6618
                    true,
6619
                    null,
6620
                    true
6621
                );
6622
6623
                $list = $list->get_flat_list();
6624
                $totalProgress = 0;
6625
                $totalTime = 0;
6626
                if (!empty($list)) {
6627
                    foreach ($list as $lp_id => $learnpath) {
6628
                        if (!$learnpath['lp_visibility']) {
6629
                            continue;
6630
                        }
6631
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
6632
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
6633
                        if ($lpProgress == 100) {
6634
                            if (!empty($time)) {
6635
                                $timeInMinutes = $time / 60;
6636
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
6637
                                if ($timeInMinutes >= $min) {
6638
                                    $totalProgress++;
6639
                                }
6640
                            }
6641
                        }
6642
                        $totalTime += $time;
6643
                    }
6644
6645
                    if (!empty($totalProgress)) {
6646
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
6647
                    }
6648
                }
6649
6650
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
6651
6652
                $result[] = [
6653
                    'module' => $courseInfo['name'],
6654
                    'progress' => $progress,
6655
                    'qualification' => $totalProgress,
6656
                    'activeTime' => $totalTime,
6657
                ];
6658
            }
6659
        }
6660
6661
        return $result;
6662
    }
6663
6664
    /**
6665
     * @param int $userId
6666
     * @param int $courseId
6667
     * @param int $sessionId
6668
     *
6669
     * @return int
6670
     */
6671
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
6672
    {
6673
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6674
        $sessionCondition = api_get_session_condition($sessionId);
6675
        $courseId = (int) $courseId;
6676
        $userId = (int) $userId;
6677
6678
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
6679
                FROM $tblTrackCourseAccess
6680
                WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
6681
6682
        $result = Database::fetch_assoc(Database::query($sql));
6683
6684
        return (int) $result['c'];
6685
    }
6686
6687
    public static function processUserDataMove(
6688
        $user_id,
6689
        $course_info,
6690
        $origin_session_id,
6691
        $new_session_id,
6692
        $update_database,
6693
        $debug = false
6694
    ) {
6695
        // Begin with the import process
6696
        $origin_course_code = $course_info['code'];
6697
        $course_id = $course_info['real_id'];
6698
        $user_id = (int) $user_id;
6699
        $origin_session_id = (int) $origin_session_id;
6700
        $new_session_id = (int) $new_session_id;
6701
        $session = api_get_session_entity($new_session_id);
6702
        $em = Database::getManager();
6703
6704
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6705
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6706
        $attemptRecording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
6707
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6708
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
6709
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
6710
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
6711
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
6712
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
6713
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
6714
6715
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
6716
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
6717
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
6718
6719
        //1. track_e_exercises
6720
        //ORIGINAL COURSE
6721
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
6722
                WHERE c_id = $course_id AND  session_id = $origin_session_id AND exe_user_id = $user_id ";
6723
        $res = Database::query($sql);
6724
        $list = [];
6725
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6726
            $list[$row['exe_id']] = $row;
6727
        }
6728
6729
        $result_message = [];
6730
        $result_message_compare = [];
6731
        if (!empty($list)) {
6732
            foreach ($list as $exe_id => $data) {
6733
                if ($update_database) {
6734
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
6735
                    Database::query($sql);
6736
6737
                    $sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
6738
                    Database::query($sql);
6739
6740
                    $sql = "UPDATE $attemptRecording SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
6741
                    Database::query($sql);
6742
6743
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
6744
                        $result_message[$TABLETRACK_EXERCICES] = 0;
6745
                    }
6746
                    $result_message[$TABLETRACK_EXERCICES]++;
6747
                } else {
6748
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
6749
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
6750
                    } else {
6751
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
6752
                    }
6753
                }
6754
            }
6755
        }
6756
6757
        // DESTINY COURSE
6758
        if (!$update_database) {
6759
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
6760
                    WHERE
6761
                        c_id = $course_id AND
6762
                        session_id = $new_session_id AND
6763
                        exe_user_id = $user_id ";
6764
            $res = Database::query($sql);
6765
            $list = [];
6766
            while ($row = Database::fetch_array($res, 'ASSOC')) {
6767
                $list[$row['exe_id']] = $row;
6768
            }
6769
6770
            if (!empty($list)) {
6771
                foreach ($list as $exe_id => $data) {
6772
                    if ($update_database) {
6773
                        $sql = "UPDATE $TABLETRACK_EXERCICES
6774
                                SET session_id = '$new_session_id'
6775
                                WHERE exe_id = $exe_id";
6776
                        Database::query($sql);
6777
                        $result_message[$TABLETRACK_EXERCICES]++;
6778
                    } else {
6779
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
6780
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
6781
                        } else {
6782
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
6783
                        }
6784
                    }
6785
                }
6786
            }
6787
        }
6788
6789
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
6790
        // Nothing to do because there are not relationship with a session
6791
        // 3. track_e_course_access
6792
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
6793
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
6794
        $res = Database::query($sql);
6795
        $list = [];
6796
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6797
            $list[$row['course_access_id']] = $row;
6798
        }
6799
6800
        if (!empty($list)) {
6801
            foreach ($list as $id => $data) {
6802
                if ($update_database) {
6803
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
6804
                            SET session_id = $new_session_id
6805
                            WHERE course_access_id = $id";
6806
                    if ($debug) {
6807
                        echo $sql;
6808
                    }
6809
                    Database::query($sql);
6810
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
6811
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
6812
                    }
6813
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
6814
                }
6815
            }
6816
        }
6817
6818
        // 4. track_e_lastaccess
6819
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
6820
                WHERE
6821
                    c_id = $course_id AND
6822
                    access_session_id = $origin_session_id AND
6823
                    access_user_id = $user_id ";
6824
        $res = Database::query($sql);
6825
        $list = [];
6826
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6827
            $list[] = $row['access_id'];
6828
        }
6829
6830
        if (!empty($list)) {
6831
            foreach ($list as $id) {
6832
                if ($update_database) {
6833
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
6834
                            SET access_session_id = $new_session_id
6835
                            WHERE access_id = $id";
6836
                    if ($debug) {
6837
                        echo $sql;
6838
                    }
6839
                    Database::query($sql);
6840
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
6841
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
6842
                    }
6843
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
6844
                }
6845
            }
6846
        }
6847
6848
        // 5. lp_item_view
6849
        // CHECK ORIGIN
6850
        $sql = "SELECT * FROM $TBL_LP_VIEW
6851
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
6852
        $res = Database::query($sql);
6853
6854
        // Getting the list of LPs in the new session
6855
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
6856
        $flat_list = $lp_list->get_flat_list();
6857
        $list = [];
6858
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6859
            // Checking if the LP exist in the new session
6860
            //if (in_array($row['lp_id'], array_keys($flat_list))) {
6861
            $list[$row['id']] = $row;
6862
            //}
6863
        }
6864
6865
        if (!empty($list)) {
6866
            foreach ($list as $id => $data) {
6867
                if ($update_database) {
6868
                    $sql = "UPDATE $TBL_LP_VIEW
6869
                            SET session_id = $new_session_id
6870
                            WHERE c_id = $course_id AND id = $id ";
6871
                    if ($debug) {
6872
                        var_dump($sql);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($sql) looks like debug code. Are you sure you do not want to remove it?
Loading history...
6873
                    }
6874
                    $res = Database::query($sql);
6875
                    if ($debug) {
6876
                        var_dump($res);
6877
                    }
6878
                    if (!isset($result_message[$TBL_LP_VIEW])) {
6879
                        $result_message[$TBL_LP_VIEW] = 0;
6880
                    }
6881
                    $result_message[$TBL_LP_VIEW]++;
6882
                } else {
6883
                    // Getting all information of that lp_item_id
6884
                    $score = self::get_avg_student_score(
6885
                        $user_id,
6886
                        $origin_course_code,
6887
                        [$data['lp_id']],
6888
                        $origin_session_id
6889
                    );
6890
                    $progress = self::get_avg_student_progress(
6891
                        $user_id,
6892
                        $origin_course_code,
6893
                        [$data['lp_id']],
6894
                        $origin_session_id
6895
                    );
6896
                    $result_message['LP_VIEW'][$data['lp_id']] = [
6897
                        'score' => $score,
6898
                        'progress' => $progress,
6899
                    ];
6900
                }
6901
            }
6902
        }
6903
6904
        // Check destination.
6905
        if (!$update_database) {
6906
            $sql = "SELECT * FROM $TBL_LP_VIEW
6907
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
6908
            $res = Database::query($sql);
6909
6910
            // Getting the list of LPs in the new session
6911
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
6912
            $flat_list = $lp_list->get_flat_list();
6913
6914
            $list = [];
6915
            while ($row = Database::fetch_array($res, 'ASSOC')) {
6916
                //Checking if the LP exist in the new session
6917
                //if (in_array($row['lp_id'], array_keys($flat_list))) {
6918
                $list[$row['id']] = $row;
6919
                //}
6920
            }
6921
6922
            if (!empty($list)) {
6923
                foreach ($list as $id => $data) {
6924
                    // Getting all information of that lp_item_id
6925
                    $score = self::get_avg_student_score(
6926
                        $user_id,
6927
                        $origin_course_code,
6928
                        [$data['lp_id']],
6929
                        $new_session_id
6930
                    );
6931
                    $progress = self::get_avg_student_progress(
6932
                        $user_id,
6933
                        $origin_course_code,
6934
                        [$data['lp_id']],
6935
                        $new_session_id
6936
                    );
6937
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
6938
                        'score' => $score,
6939
                        'progress' => $progress,
6940
                    ];
6941
                }
6942
            }
6943
        }
6944
6945
        // 6. Agenda
6946
        // calendar_event_attachment no problems no session_id
6947
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
6948
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
6949
        $res = Database::query($sql);
6950
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6951
            $id = $row['ref'];
6952
            if ($update_database) {
6953
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id ";
6954
                if ($debug) {
6955
                    var_dump($sql);
6956
                }
6957
                $res_update = Database::query($sql);
6958
                if ($debug) {
6959
                    var_dump($res_update);
6960
                }
6961
                if (!isset($result_message['agenda'])) {
6962
                    $result_message['agenda'] = 0;
6963
                }
6964
                $result_message['agenda']++;
6965
            }
6966
        }
6967
6968
        // 7. Forum ?? So much problems when trying to import data
6969
        // 8. Student publication - Works
6970
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
6971
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
6972
        if ($debug) {
6973
            echo $sql;
6974
        }
6975
        $res = Database::query($sql);
6976
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6977
            $id = $row['ref'];
6978
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
6979
                    WHERE id = $id AND session_id = $origin_session_id AND c_id = $course_id";
6980
            if ($debug) {
6981
                var_dump($sql);
6982
            }
6983
            $sub_res = Database::query($sql);
6984
            if (Database::num_rows($sub_res) > 0) {
6985
                $data = Database::fetch_array($sub_res, 'ASSOC');
6986
                if ($debug) {
6987
                    var_dump($data);
6988
                }
6989
                $parent_id = $data['parent_id'];
6990
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
6991
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
6992
                            WHERE id = $parent_id AND c_id = $course_id";
6993
                    $select_res = Database::query($sql);
6994
                    $parent_data = Database::fetch_array($select_res, 'ASSOC');
6995
                    if ($debug) {
6996
                        var_dump($parent_data);
6997
                    }
6998
6999
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
7000
                    $course_dir = $sys_course_path.$course_info['path'];
7001
                    $base_work_dir = $course_dir.'/work';
7002
7003
                    // Creating the parent folder in the session if does not exists already
7004
                    //@todo ugly fix
7005
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
7006
                    $search_this2 = $parent_data['url'];
7007
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7008
                            WHERE description like '%$search_this%' AND
7009
                                  url LIKE '%$search_this2%' AND
7010
                                  session_id = $new_session_id AND
7011
                                  c_id = $course_id
7012
                            ORDER BY id desc  LIMIT 1";
7013
                    if ($debug) {
7014
                        echo $sql;
7015
                    }
7016
                    $sub_res = Database::query($sql);
7017
                    $num_rows = Database::num_rows($sub_res);
7018
                    $new_parent_id = 0;
7019
                    if ($num_rows > 0) {
7020
                        $new_result = Database::fetch_array($sub_res, 'ASSOC');
7021
                        $created_dir = $new_result['url'];
7022
                        $new_parent_id = $new_result['id'];
7023
                    } else {
7024
                        if ($update_database) {
7025
                            $dir_name = substr($parent_data['url'], 1);
7026
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
7027
                            $created_dir = '/'.$created_dir;
7028
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
7029
                            // Creating directory
7030
                            $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
7031
                            $publication
7032
                                ->setUrl($created_dir)
7033
                                ->setCId($course_id)
7034
                                ->setTitle($parent_data['title'])
7035
                                ->setDescription(
7036
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
7037
                                )
7038
                                ->setActive(false)
7039
                                ->setAccepted(true)
7040
                                ->setPostGroupId(0)
7041
                                ->setHasProperties($parent_data['has_properties'])
7042
                                ->setWeight($parent_data['weight'])
7043
                                ->setContainsFile($parent_data['contains_file'])
7044
                                ->setFiletype('folder')
7045
                                ->setSentDate($now)
7046
                                ->setQualification($parent_data['qualification'])
7047
                                ->setParentId(0)
7048
                                ->setQualificatorId(0)
7049
                                ->setUserId($parent_data['user_id'])
7050
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
7051
                                ->setSession($session);
7052
7053
                            $publication->setDocumentId($parent_data['document_id']);
7054
7055
                            Database::getManager()->persist($publication);
7056
                            Database::getManager()->flush();
7057
                            $id = $publication->getIid();
7058
                            // Folder created
7059
                            api_item_property_update(
7060
                                $course_info,
7061
                                'work',
7062
                                $id,
7063
                                'DirectoryCreated',
7064
                                api_get_user_id(),
7065
                                null,
7066
                                null,
7067
                                null,
7068
                                null,
7069
                                $new_session_id
7070
                            );
7071
                            $new_parent_id = $id;
7072
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
7073
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
7074
                            }
7075
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
7076
                        }
7077
                    }
7078
7079
                    //Creating student_publication_assignment if exists
7080
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
7081
                            WHERE publication_id = $parent_id AND c_id = $course_id";
7082
                    if ($debug) {
7083
                        var_dump($sql);
7084
                    }
7085
                    $rest_select = Database::query($sql);
7086
                    if (Database::num_rows($rest_select) > 0) {
7087
                        if ($update_database && $new_parent_id) {
7088
                            $assignment_data = Database::fetch_array($rest_select, 'ASSOC');
7089
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
7090
                                c_id = '$course_id',
7091
                                expires_on = '".$assignment_data['expires_on']."',
7092
                                ends_on = '".$assignment_data['ends_on']."',
7093
                                add_to_calendar = '".$assignment_data['add_to_calendar']."',
7094
                                enable_qualification = '".$assignment_data['enable_qualification']."',
7095
                                publication_id = '".$new_parent_id."'";
7096
                            if ($debug) {
7097
                                echo $sql_add_publication;
7098
                            }
7099
                            Database::query($sql_add_publication);
7100
                            $id = (int) Database::insert_id();
7101
                            if ($id) {
7102
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
7103
                                    SET  has_properties = '".$id."',
7104
                                        view_properties = '1'
7105
                                    WHERE id = ".$new_parent_id;
7106
                                if ($debug) {
7107
                                    echo $sql_update;
7108
                                }
7109
                                Database::query($sql_update);
7110
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
7111
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
7112
                                }
7113
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
7114
                            }
7115
                        }
7116
                    }
7117
7118
                    $doc_url = $data['url'];
7119
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
7120
7121
                    if ($update_database) {
7122
                        // Creating a new work
7123
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
7124
                        $data['post_group_id'] = (int) $data['post_group_id'];
7125
                        $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
7126
                        $publication
7127
                            ->setUrl($new_url)
7128
                            ->setCId($course_id)
7129
                            ->setTitle($data['title'])
7130
                            ->setDescription($data['description'].' file moved')
7131
                            ->setActive($data['active'])
7132
                            ->setAccepted($data['accepted'])
7133
                            ->setPostGroupId($data['post_group_id'])
7134
                            ->setSentDate($data['sent_date'])
7135
                            ->setParentId($new_parent_id)
7136
                            ->setWeight($data['weight'])
7137
                            ->setHasProperties(0)
7138
                            ->setWeight($data['weight'])
7139
                            ->setContainsFile($data['contains_file'])
7140
                            ->setSession($session)
7141
                            ->setUserId($data['user_id'])
7142
                            ->setFiletype('file')
7143
                            ->setDocumentId(0)
7144
                        ;
7145
7146
                        $em->persist($publication);
7147
                        $em->flush();
7148
7149
                        $id = $publication->getIid();
7150
                        api_item_property_update(
7151
                            $course_info,
7152
                            'work',
7153
                            $id,
7154
                            'DocumentAdded',
7155
                            $user_id,
7156
                            null,
7157
                            null,
7158
                            null,
7159
                            null,
7160
                            $new_session_id
7161
                        );
7162
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
7163
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
7164
                        }
7165
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
7166
                        $full_file_name = $course_dir.'/'.$doc_url;
7167
                        $new_file = $course_dir.'/'.$new_url;
7168
7169
                        if (file_exists($full_file_name)) {
7170
                            // deleting old assignment
7171
                            $result = copy($full_file_name, $new_file);
7172
                            if ($result) {
7173
                                unlink($full_file_name);
7174
                                if (isset($data['id'])) {
7175
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
7176
                                    if ($debug) {
7177
                                        var_dump($sql);
7178
                                    }
7179
                                    Database::query($sql);
7180
                                }
7181
                                api_item_property_update(
7182
                                    $course_info,
7183
                                    'work',
7184
                                    $data['id'],
7185
                                    'DocumentDeleted',
7186
                                    api_get_user_id()
7187
                                );
7188
                            }
7189
                        }
7190
                    }
7191
                }
7192
            }
7193
        }
7194
7195
        //9. Survey   Pending
7196
        //10. Dropbox - not neccesary to move categories (no presence of session_id)
7197
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
7198
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
7199
        if ($debug) {
7200
            var_dump($sql);
7201
        }
7202
        $res = Database::query($sql);
7203
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7204
            $id = (int) $row['id'];
7205
            if ($update_database) {
7206
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id";
7207
                if ($debug) {
7208
                    var_dump($sql);
7209
                }
7210
                Database::query($sql);
7211
                if ($debug) {
7212
                    var_dump($res);
7213
                }
7214
7215
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
7216
                if ($debug) {
7217
                    var_dump($sql);
7218
                }
7219
                Database::query($sql);
7220
                if ($debug) {
7221
                    var_dump($res);
7222
                }
7223
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
7224
                    $result_message[$TBL_DROPBOX_FILE] = 0;
7225
                }
7226
                $result_message[$TBL_DROPBOX_FILE]++;
7227
            }
7228
        }
7229
7230
        // 11. Notebook
7231
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
7232
                WHERE
7233
                    user_id = $user_id AND
7234
                    session_id = $origin_session_id AND
7235
                    course = '$origin_course_code' AND
7236
                    c_id = $course_id";
7237
        if ($debug) {
7238
            var_dump($sql);
7239
        }
7240
        $res = Database::query($sql);
7241
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7242
            $id = $row['notebook_id'];
7243
            if ($update_database) {
7244
                $sql = "UPDATE $TBL_NOTEBOOK
7245
                        SET session_id = $new_session_id
7246
                        WHERE c_id = $course_id AND notebook_id = $id";
7247
                if ($debug) {
7248
                    var_dump($sql);
7249
                }
7250
                $res = Database::query($sql);
7251
                if ($debug) {
7252
                    var_dump($res);
7253
                }
7254
            }
7255
        }*/
7256
7257
        if ($update_database) {
7258
            echo Display::return_message(get_lang('StatsMoved'));
7259
            if (is_array($result_message)) {
7260
                foreach ($result_message as $table => $times) {
7261
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
7262
                }
7263
            }
7264
        } else {
7265
            echo '<h4>'.get_lang('UserInformationOfThisCourse').'</h4>';
7266
            echo '<br />';
7267
            echo '<table class="table" width="100%">';
7268
            echo '<tr>';
7269
            echo '<td width="50%" valign="top">';
7270
            if ($origin_session_id == 0) {
7271
                echo '<h5>'.get_lang('OriginCourse').'</h5>';
7272
            } else {
7273
                echo '<h5>'.get_lang('OriginSession').' #'.$origin_session_id.'</h5>';
7274
            }
7275
            self::compareUserData($result_message);
7276
            echo '</td>';
7277
            echo '<td width="50%" valign="top">';
7278
            if ($new_session_id == 0) {
7279
                echo '<h5>'.get_lang('DestinyCourse').'</h5>';
7280
            } else {
7281
                echo '<h5>'.get_lang('DestinySession').' #'.$new_session_id.'</h5>';
7282
            }
7283
            self::compareUserData($result_message_compare);
7284
            echo '</td>';
7285
            echo '</tr>';
7286
            echo '</table>';
7287
        }
7288
    }
7289
7290
    public static function compareUserData($result_message)
7291
    {
7292
        foreach ($result_message as $table => $data) {
7293
            $title = $table;
7294
            if ($table === 'TRACK_E_EXERCISES') {
7295
                $title = get_lang('Exercises');
7296
            } elseif ($table === 'TRACK_E_EXERCISES_IN_LP') {
7297
                $title = get_lang('ExercisesInLp');
7298
            } elseif ($table === 'LP_VIEW') {
7299
                $title = get_lang('LearningPaths');
7300
            }
7301
            echo '<br / ><h3>'.get_lang($title).' </h3><hr />';
7302
7303
            if (is_array($data)) {
7304
                foreach ($data as $id => $item) {
7305
                    if ($table === 'TRACK_E_EXERCISES' || $table === 'TRACK_E_EXERCISES_IN_LP') {
7306
                        echo "<br /><h3>".get_lang('Attempt')." #$id</h3>";
7307
                        echo '<h3>';
7308
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
7309
                        echo '</h3>';
7310
                        if (!empty($item['orig_lp_id'])) {
7311
                            echo '<h3>';
7312
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
7313
                            echo '</h3>';
7314
                        }
7315
                        // Process data.
7316
                        $array = [
7317
                            'exe_date' => get_lang('Date'),
7318
                            'exe_result' => get_lang('Score'),
7319
                            'exe_weighting' => get_lang('Weighting'),
7320
                        ];
7321
                        foreach ($item as $key => $value) {
7322
                            if (in_array($key, array_keys($array))) {
7323
                                $key = $array[$key];
7324
                                echo "$key =  $value <br />";
7325
                            }
7326
                        }
7327
                    } else {
7328
                        echo "<br /><h3>".get_lang('Id')." #$id</h3>";
7329
                        // process data
7330
                        foreach ($item as $key => $value) {
7331
                            echo "$key =  $value <br />";
7332
                        }
7333
                    }
7334
                }
7335
            } else {
7336
                echo get_lang('NoResults');
7337
            }
7338
        }
7339
    }
7340
7341
    public static function updateUserLastLogin($userId)
7342
    {
7343
        $tblTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7344
        $sql = "SELECT login_id, login_date
7345
            FROM $tblTrackLogin
7346
            WHERE
7347
                login_user_id='".$userId."'
7348
            ORDER BY login_date DESC
7349
            LIMIT 0,1";
7350
7351
        $qLastConnection = Database::query($sql);
7352
        if (Database::num_rows($qLastConnection) > 0) {
7353
            $now = api_get_utc_datetime();
7354
            $iIdLastConnection = Database::result($qLastConnection, 0, 'login_id');
7355
7356
            // is the latest logout_date still relevant?
7357
            $sql = "SELECT logout_date FROM $tblTrackLogin
7358
                WHERE login_id = $iIdLastConnection";
7359
            $qLogoutDate = Database::query($sql);
7360
            $resLogoutDate = convert_sql_date(Database::result($qLogoutDate, 0, 'logout_date'));
7361
            $lifeTime = api_get_configuration_value('session_lifetime');
7362
7363
            if ($resLogoutDate < time() - $lifeTime) {
7364
                // it isn't, we should create a fresh entry
7365
                Event::eventLogin($userId);
7366
            // now that it's created, we can get its ID and carry on
7367
            } else {
7368
                $sql = "UPDATE $tblTrackLogin SET logout_date = '$now'
7369
                    WHERE login_id = '$iIdLastConnection'";
7370
                Database::query($sql);
7371
            }
7372
7373
            $tableUser = Database::get_main_table(TABLE_MAIN_USER);
7374
            $sql = "UPDATE $tableUser SET last_login = '$now'
7375
                WHERE user_id = ".$userId;
7376
            Database::query($sql);
7377
        }
7378
    }
7379
7380
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
7381
    {
7382
        if (empty($sessionId)) {
7383
            $userList = CourseManager::get_user_list_from_course_code(
7384
                $courseInfo['code'],
7385
                $sessionId,
7386
                null,
7387
                null,
7388
                STUDENT
7389
            );
7390
        } else {
7391
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
7392
        }
7393
7394
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null);
7395
7396
        if (empty($exerciseList)) {
7397
            return Display::return_message(get_lang('NoEx'));
7398
        }
7399
7400
        $toGraphExerciseResult = [];
7401
7402
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
7403
        $quizzesTable->setHeaders(
7404
            [
7405
                get_lang('Title'),
7406
                get_lang('Attempts'),
7407
                get_lang('BestAttempt'),
7408
                get_lang('Ranking'),
7409
                get_lang('BestResultInCourse'),
7410
                get_lang('Statistics').Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent')),
7411
            ]
7412
        );
7413
7414
        $webCodePath = api_get_path(WEB_CODE_PATH);
7415
7416
        foreach ($exerciseList as $exercices) {
7417
            $objExercise = new Exercise($courseInfo['real_id']);
7418
            $objExercise->read($exercices['iid']);
7419
            $visibleReturn = $objExercise->is_visible();
7420
7421
            // Getting count of attempts by user
7422
            $attempts = Event::count_exercise_attempts_by_user(
7423
                api_get_user_id(),
7424
                $exercices['iid'],
7425
                $courseInfo['real_id'],
7426
                $sessionId
7427
            );
7428
7429
            $url = $webCodePath.'exercise/overview.php?'
7430
                .http_build_query(
7431
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['iid']]
7432
                );
7433
7434
            if ($visibleReturn['value'] == true) {
7435
                $exercices['title'] = Display::url(
7436
                    $exercices['title'],
7437
                    $url,
7438
                    ['target' => SESSION_LINK_TARGET]
7439
                );
7440
            } elseif ($exercices['active'] == -1) {
7441
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
7442
            }
7443
7444
            $quizData = [
7445
                $exercices['title'],
7446
                $attempts,
7447
                '-',
7448
                '-',
7449
                '-',
7450
                '-',
7451
            ];
7452
7453
            // Exercise configuration show results or show only score
7454
            if (!in_array($exercices['results_disabled'], [0, 2])
7455
                || empty($attempts)
7456
            ) {
7457
                $quizzesTable->addRow($quizData);
7458
7459
                continue;
7460
            }
7461
7462
            //For graphics
7463
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
7464
                $exercices['iid'],
7465
                $courseInfo['real_id'],
7466
                $sessionId
7467
            );
7468
7469
            $toGraphExerciseResult[$exercices['iid']] = [
7470
                'title' => $exercices['title'],
7471
                'data' => $bestExerciseAttempts,
7472
            ];
7473
7474
            // Getting best results
7475
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
7476
                $exercices['iid'],
7477
                $courseInfo['real_id'],
7478
                $sessionId
7479
            );
7480
7481
            if (!empty($bestScoreData)) {
7482
                $quizData[5] = ExerciseLib::show_score(
7483
                    $bestScoreData['exe_result'],
7484
                    $bestScoreData['exe_weighting']
7485
                );
7486
            }
7487
7488
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
7489
                api_get_user_id(),
7490
                $exercices['iid'],
7491
                $courseInfo['real_id'],
7492
                $sessionId
7493
            );
7494
7495
            if (!empty($exerciseAttempt)) {
7496
                // Always getting the BEST attempt
7497
                $score = $exerciseAttempt['exe_result'];
7498
                $weighting = $exerciseAttempt['exe_weighting'];
7499
                $exeId = $exerciseAttempt['exe_id'];
7500
7501
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
7502
                    .http_build_query(
7503
                        [
7504
                            'id' => $exeId,
7505
                            'cidReq' => $courseInfo['code'],
7506
                            'show_headers' => 1,
7507
                            'id_session' => $sessionId,
7508
                        ]
7509
                    );
7510
7511
                $quizData[3] = Display::url(
7512
                    ExerciseLib::show_score($score, $weighting),
7513
                    $latestAttemptUrl
7514
                );
7515
7516
                $myScore = !empty($weighting) && intval($weighting) != 0 ? $score / $weighting : 0;
7517
7518
                //@todo this function slows the page
7519
                if (is_int($userList)) {
7520
                    $userList = [$userList];
7521
                }
7522
7523
                $quizData[4] = ExerciseLib::get_exercise_result_ranking(
7524
                    $myScore,
7525
                    $exeId,
7526
                    $exercices['iid'],
7527
                    $courseInfo['code'],
7528
                    $sessionId,
7529
                    $userList
7530
                );
7531
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['iid']]);
7532
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['iid']]);
7533
7534
                $quizData[6] = Display::url(
7535
                    Display::img($graph, '', [], false),
7536
                    $normalGraph,
7537
                    ['id' => $exercices['iid'], 'class' => 'expand-image']
7538
                );
7539
            }
7540
7541
            $quizzesTable->addRow($quizData);
7542
        }
7543
7544
        return Display::page_subheader2(get_lang('Exercises'))
7545
            .Display::div(
7546
                $quizzesTable->toHtml(),
7547
                ['class' => 'table-responsive']
7548
            );
7549
    }
7550
7551
    private static function generateLearningPathsTable(
7552
        User $user,
7553
        array $courseInfo,
7554
        int $sessionId = 0,
7555
        bool $isAllowedToEdit = true
7556
    ): string {
7557
        $html = [];
7558
7559
        $columnHeaders = [
7560
            'lp' => get_lang('LearningPath'),
7561
            'time' => get_lang('LatencyTimeSpent'),
7562
            'progress' => get_lang('Progress'),
7563
            'score' => get_lang('Score'),
7564
            'best_score' => get_lang('BestScore'),
7565
            'last_connection' => get_lang('LastConnexion'),
7566
        ];
7567
7568
        $trackingColumns = api_get_configuration_value('tracking_columns');
7569
7570
        if (isset($trackingColumns['my_progress_lp'])) {
7571
            $columnHeaders = array_filter(
7572
                $columnHeaders,
7573
                function ($columHeader, $key) use ($trackingColumns) {
7574
                    if (!isset($trackingColumns['my_progress_lp'][$key])
7575
                        || $trackingColumns['my_progress_lp'][$key] == false
7576
                    ) {
7577
                        return false;
7578
                    }
7579
7580
                    return true;
7581
                },
7582
                ARRAY_FILTER_USE_BOTH
7583
            );
7584
        }
7585
7586
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
7587
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
7588
        }
7589
7590
        if (true === api_get_configuration_value('student_follow_page_add_LP_acquisition_info')) {
7591
            $columnHeaders['student_follow_page_add_LP_acquisition_info'] = get_lang('Acquisition');
7592
        }
7593
7594
        $addLpInvisibleCheckbox = api_get_configuration_value('student_follow_page_add_LP_invisible_checkbox');
7595
        $includeNotsubscribedLp = api_get_configuration_value('student_follow_page_include_not_subscribed_lp_students');
7596
7597
        $columnHeadersKeys = array_keys($columnHeaders);
7598
7599
        $categories = learnpath::getCategories($courseInfo['real_id'], true);
7600
        $countCategories = count($categories);
7601
7602
        $webCodePath = api_get_path(WEB_CODE_PATH);
7603
7604
        /** @var CLpCategory $category */
7605
        foreach ($categories as $category) {
7606
            // LP table results
7607
            $objLearnpathList = new LearnpathList(
7608
                $user->getId(),
7609
                $courseInfo,
7610
                $sessionId,
7611
                'lp.publicatedOn ASC',
7612
                true,
7613
                $category->getId(),
7614
                false,
7615
                false,
7616
                $includeNotsubscribedLp === false
7617
            );
7618
            $lpList = $objLearnpathList->get_flat_list();
7619
7620
            $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
7621
            $learningpathsTable->setHeaders($columnHeaders);
7622
7623
            foreach ($lpList as $lpId => $learnpath) {
7624
                $learningpathData = [];
7625
7626
                if (!$learnpath['lp_visibility']) {
7627
                    continue;
7628
                }
7629
7630
                if ($addLpInvisibleCheckbox) {
7631
                    if (!StudentFollowPage::isViewVisible($lpId, $user->getId(), $courseInfo['real_id'], $sessionId)) {
7632
                        continue;
7633
                    }
7634
                }
7635
7636
                $url = $webCodePath.'lp/lp_controller.php?'
7637
                    .http_build_query(
7638
                        ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
7639
                    );
7640
7641
                if (in_array('lp', $columnHeadersKeys)) {
7642
                    if ($learnpath['lp_visibility'] == 0) {
7643
                        $learningpathData[] = $learnpath['lp_name'];
7644
                    } else {
7645
                        $learningpathData[] = Display::url(
7646
                            $learnpath['lp_name'],
7647
                            $url,
7648
                            ['target' => SESSION_LINK_TARGET]
7649
                        );
7650
                    }
7651
                }
7652
7653
                if (in_array('time', $columnHeadersKeys)) {
7654
                    $time_spent_in_lp = self::get_time_spent_in_lp(
7655
                        $user->getId(),
7656
                        $courseInfo['code'],
7657
                        [$lpId],
7658
                        $sessionId
7659
                    );
7660
7661
                    $learningpathData[] = api_time_to_hms($time_spent_in_lp);
7662
                }
7663
7664
                if (in_array('progress', $columnHeadersKeys)) {
7665
                    $progress = self::get_avg_student_progress(
7666
                        $user->getId(),
7667
                        $courseInfo['code'],
7668
                        [$lpId],
7669
                        $sessionId
7670
                    );
7671
7672
                    if (is_numeric($progress)) {
7673
                        $progress = sprintf(get_lang('XPercent'), $progress);
7674
                    }
7675
7676
                    $learningpathData[] = $progress;
7677
                }
7678
7679
                if (in_array('score', $columnHeadersKeys)) {
7680
                    $percentage_score = self::get_avg_student_score(
7681
                        $user->getId(),
7682
                        $courseInfo['code'],
7683
                        [$lpId],
7684
                        $sessionId
7685
                    );
7686
7687
                    if (is_numeric($percentage_score)) {
7688
                        $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
7689
                    } else {
7690
                        $percentage_score = sprintf(get_lang('XPercent'), 0);
7691
                    }
7692
7693
                    $learningpathData[] = $percentage_score;
7694
                }
7695
7696
                if (in_array('best_score', $columnHeadersKeys)) {
7697
                    $bestScore = self::get_avg_student_score(
7698
                        $user->getId(),
7699
                        $courseInfo['code'],
7700
                        [$lpId],
7701
                        $sessionId,
7702
                        false,
7703
                        false,
7704
                        true
7705
                    );
7706
7707
                    if (is_numeric($bestScore)) {
7708
                        $bestScore = sprintf(get_lang('XPercent'), $bestScore);
7709
                    } else {
7710
                        $bestScore = '-';
7711
                    }
7712
7713
                    $learningpathData[] = $bestScore;
7714
                }
7715
7716
                if (in_array('last_connection', $columnHeadersKeys)) {
7717
                    $lastConnectionInLp = self::get_last_connection_time_in_lp(
7718
                        $user->getId(),
7719
                        $courseInfo['code'],
7720
                        $lpId,
7721
                        $sessionId
7722
                    );
7723
7724
                    $lastConnection = '-';
7725
7726
                    if (!empty($lastConnectionInLp)) {
7727
                        $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
7728
                    }
7729
7730
                    $learningpathData[] = $lastConnection;
7731
                }
7732
7733
                if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
7734
                    $learningpathData[] = StudentFollowPage::getLpSubscription(
7735
                        $learnpath,
7736
                        $user->getId(),
7737
                        $courseInfo['real_id'],
7738
                        $sessionId,
7739
                        $isAllowedToEdit
7740
                    );
7741
                }
7742
7743
                if (in_array('student_follow_page_add_LP_acquisition_info', $columnHeadersKeys)) {
7744
                    $learningpathData[] = StudentFollowPage::getLpAcquisition(
7745
                        $learnpath,
7746
                        $user->getId(),
7747
                        $courseInfo['real_id'],
7748
                        $sessionId
7749
                    );
7750
                }
7751
7752
                $learningpathsTable->addRow($learningpathData);
7753
            }
7754
7755
            if ($learningpathsTable->getRowCount() < 2) {
7756
                continue;
7757
            }
7758
7759
            if ($countCategories > 1) {
7760
                $html[] = Display::tag('h5', $category->getName());
7761
            }
7762
7763
            $html[] = Display::div(
7764
                $learningpathsTable->toHtml(),
7765
                ['class' => 'table-responsive']
7766
            );
7767
        }
7768
7769
        return implode(PHP_EOL, $html);
7770
    }
7771
}
7772
7773
/**
7774
 * @todo move into a proper file
7775
 */
7776
class TrackingCourseLog
7777
{
7778
    /**
7779
     * @return mixed
7780
     */
7781
    public static function count_item_resources()
7782
    {
7783
        $session_id = api_get_session_id();
7784
        $course_id = api_get_course_int_id();
7785
7786
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
7787
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7788
7789
        $sql = "SELECT count(tool) AS total_number_of_items
7790
                FROM $table_item_property track_resource, $table_user user
7791
                WHERE
7792
                    track_resource.c_id = $course_id AND
7793
                    track_resource.insert_user_id = user.user_id AND
7794
                    session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7795
7796
        if (isset($_GET['keyword'])) {
7797
            $keyword = Database::escape_string(trim($_GET['keyword']));
7798
            $sql .= " AND (
7799
                        user.username LIKE '%".$keyword."%' OR
7800
                        lastedit_type LIKE '%".$keyword."%' OR
7801
                        tool LIKE '%".$keyword."%'
7802
                    )";
7803
        }
7804
7805
        $sql .= " AND tool IN (
7806
                    'document',
7807
                    'learnpath',
7808
                    'quiz',
7809
                    'glossary',
7810
                    'link',
7811
                    'course_description',
7812
                    'announcement',
7813
                    'thematic',
7814
                    'thematic_advance',
7815
                    'thematic_plan'
7816
                )";
7817
        $res = Database::query($sql);
7818
        $obj = Database::fetch_object($res);
7819
7820
        return $obj->total_number_of_items;
7821
    }
7822
7823
    /**
7824
     * @param $from
7825
     * @param $number_of_items
7826
     * @param $column
7827
     * @param $direction
7828
     *
7829
     * @return array
7830
     */
7831
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
7832
    {
7833
        $session_id = api_get_session_id();
7834
        $course_id = api_get_course_int_id();
7835
7836
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
7837
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7838
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
7839
        $column = (int) $column;
7840
        $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
7841
7842
        $sql = "SELECT
7843
                    tool as col0,
7844
                    lastedit_type as col1,
7845
                    ref as ref,
7846
                    user.username as col3,
7847
                    insert_date as col6,
7848
                    visibility as col7,
7849
                    user.user_id as user_id
7850
                FROM $table_item_property track_resource, $table_user user
7851
                WHERE
7852
                  track_resource.c_id = $course_id AND
7853
                  track_resource.insert_user_id = user.user_id AND
7854
                  session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7855
7856
        if (isset($_GET['keyword'])) {
7857
            $keyword = Database::escape_string(trim($_GET['keyword']));
7858
            $sql .= " AND (
7859
                        user.username LIKE '%".$keyword."%' OR
7860
                        lastedit_type LIKE '%".$keyword."%' OR
7861
                        tool LIKE '%".$keyword."%'
7862
                     ) ";
7863
        }
7864
7865
        $sql .= " AND tool IN (
7866
                    'document',
7867
                    'learnpath',
7868
                    'quiz',
7869
                    'glossary',
7870
                    'link',
7871
                    'course_description',
7872
                    'announcement',
7873
                    'thematic',
7874
                    'thematic_advance',
7875
                    'thematic_plan'
7876
                )";
7877
7878
        if ($column == 0) {
7879
            $column = '0';
7880
        }
7881
        if ($column != '' && $direction != '') {
7882
            if ($column != 2 && $column != 4) {
7883
                $sql .= " ORDER BY col$column $direction";
7884
            }
7885
        } else {
7886
            $sql .= " ORDER BY col6 DESC ";
7887
        }
7888
7889
        $from = intval($from);
7890
        if ($from) {
7891
            $number_of_items = intval($number_of_items);
7892
            $sql .= " LIMIT $from, $number_of_items ";
7893
        }
7894
7895
        $res = Database::query($sql);
7896
        $resources = [];
7897
        $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan'];
7898
        while ($row = Database::fetch_array($res)) {
7899
            $ref = $row['ref'];
7900
            $table_name = self::get_tool_name_table($row['col0']);
7901
            $table_tool = Database::get_course_table($table_name['table_name']);
7902
7903
            $id = $table_name['id_tool'];
7904
            $recorset = false;
7905
7906
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
7907
                $tbl_thematic = Database::get_course_table(TABLE_THEMATIC);
7908
                $sql = "SELECT thematic_id FROM $table_tool
7909
                        WHERE c_id = $course_id AND id = $ref";
7910
                $rs_thematic = Database::query($sql);
7911
                if (Database::num_rows($rs_thematic)) {
7912
                    $row_thematic = Database::fetch_array($rs_thematic);
7913
                    $thematic_id = $row_thematic['thematic_id'];
7914
7915
                    $sql = "SELECT session.id, session.name, user.username
7916
                            FROM $tbl_thematic t, $table_session session, $table_user user
7917
                            WHERE
7918
                              t.c_id = $course_id AND
7919
                              t.session_id = session.id AND
7920
                              session.id_coach = user.user_id AND
7921
                              t.id = $thematic_id";
7922
                    $recorset = Database::query($sql);
7923
                }
7924
            } else {
7925
                $sql = "SELECT session.id, session.name, user.username
7926
                          FROM $table_tool tool, $table_session session, $table_user user
7927
                          WHERE
7928
                              tool.c_id = $course_id AND
7929
                              tool.session_id = session.id AND
7930
                              session.id_coach = user.user_id AND
7931
                              tool.$id = $ref";
7932
                $recorset = Database::query($sql);
7933
            }
7934
7935
            if (!empty($recorset)) {
7936
                $obj = Database::fetch_object($recorset);
7937
7938
                $name_session = '';
7939
                $coach_name = '';
7940
                if (!empty($obj)) {
7941
                    $name_session = $obj->name;
7942
                    $coach_name = $obj->username;
7943
                }
7944
7945
                $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
7946
                $row[0] = '';
7947
                if ($row['col6'] != 2) {
7948
                    if (in_array($row['col0'], $thematic_tools)) {
7949
                        $exp_thematic_tool = explode('_', $row['col0']);
7950
                        $thematic_tool_title = '';
7951
                        if (is_array($exp_thematic_tool)) {
7952
                            foreach ($exp_thematic_tool as $exp) {
7953
                                $thematic_tool_title .= api_ucfirst($exp);
7954
                            }
7955
                        } else {
7956
                            $thematic_tool_title = api_ucfirst($row['col0']);
7957
                        }
7958
7959
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
7960
                    } else {
7961
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
7962
                    }
7963
                } else {
7964
                    $row[0] = api_ucfirst($row['col0']);
7965
                }
7966
                $row[1] = get_lang($row[1]);
7967
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
7968
                $row[5] = '';
7969
                //@todo Improve this code please
7970
                switch ($table_name['table_name']) {
7971
                    case 'document':
7972
                        $sql = "SELECT tool.title as title FROM $table_tool tool
7973
                                WHERE c_id = $course_id AND id = $ref";
7974
                        $rs_document = Database::query($sql);
7975
                        $obj_document = Database::fetch_object($rs_document);
7976
                        if ($obj_document) {
7977
                            $row[5] = $obj_document->title;
7978
                        }
7979
                        break;
7980
                    case 'announcement':
7981
                        $sql = "SELECT title FROM $table_tool
7982
                                WHERE c_id = $course_id AND id = $ref";
7983
                        $rs_document = Database::query($sql);
7984
                        $obj_document = Database::fetch_object($rs_document);
7985
                        if ($obj_document) {
7986
                            $row[5] = $obj_document->title;
7987
                        }
7988
                        break;
7989
                    case 'glossary':
7990
                        $sql = "SELECT name FROM $table_tool
7991
                                WHERE c_id = $course_id AND glossary_id = $ref";
7992
                        $rs_document = Database::query($sql);
7993
                        $obj_document = Database::fetch_object($rs_document);
7994
                        if ($obj_document) {
7995
                            $row[5] = $obj_document->name;
7996
                        }
7997
                        break;
7998
                    case 'lp':
7999
                        $sql = "SELECT name
8000
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
8001
                        $rs_document = Database::query($sql);
8002
                        $obj_document = Database::fetch_object($rs_document);
8003
                        $row[5] = $obj_document->name;
8004
                        break;
8005
                    case 'quiz':
8006
                        $sql = "SELECT title FROM $table_tool
8007
                                WHERE c_id = $course_id AND id = $ref";
8008
                        $rs_document = Database::query($sql);
8009
                        $obj_document = Database::fetch_object($rs_document);
8010
                        if ($obj_document) {
8011
                            $row[5] = $obj_document->title;
8012
                        }
8013
                        break;
8014
                    case 'course_description':
8015
                        $sql = "SELECT title FROM $table_tool
8016
                                WHERE c_id = $course_id AND id = $ref";
8017
                        $rs_document = Database::query($sql);
8018
                        $obj_document = Database::fetch_object($rs_document);
8019
                        if ($obj_document) {
8020
                            $row[5] = $obj_document->title;
8021
                        }
8022
                        break;
8023
                    case 'thematic':
8024
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
8025
                        if (Database::num_rows($rs) > 0) {
8026
                            $obj = Database::fetch_object($rs);
8027
                            if ($obj) {
8028
                                $row[5] = $obj->title;
8029
                            }
8030
                        }
8031
                        break;
8032
                    case 'thematic_advance':
8033
                        $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
8034
                        if (Database::num_rows($rs) > 0) {
8035
                            $obj = Database::fetch_object($rs);
8036
                            if ($obj) {
8037
                                $row[5] = $obj->content;
8038
                            }
8039
                        }
8040
                        break;
8041
                    case 'thematic_plan':
8042
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
8043
                        if (Database::num_rows($rs) > 0) {
8044
                            $obj = Database::fetch_object($rs);
8045
                            if ($obj) {
8046
                                $row[5] = $obj->title;
8047
                            }
8048
                        }
8049
                        break;
8050
                    default:
8051
                        break;
8052
                }
8053
8054
                $row2 = $name_session;
8055
                if (!empty($coach_name)) {
8056
                    $row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
8057
                }
8058
                $row[2] = $row2;
8059
                if (!empty($row['col3'])) {
8060
                    $userInfo = api_get_user_info($row['user_id']);
8061
                    $row['col3'] = Display::url(
8062
                        $row['col3'],
8063
                        $userInfo['profile_url']
8064
                    );
8065
                    $row[3] = $row['col3'];
8066
8067
                    $ip = Tracking::get_ip_from_user_event(
8068
                        $row['user_id'],
8069
                        $row['col6'],
8070
                        true
8071
                    );
8072
                    if (empty($ip)) {
8073
                        $ip = get_lang('Unknown');
8074
                    }
8075
                    $row[4] = $ip;
8076
                }
8077
8078
                $resources[] = $row;
8079
            }
8080
        }
8081
8082
        return $resources;
8083
    }
8084
8085
    /**
8086
     * @param string $tool
8087
     *
8088
     * @return array
8089
     */
8090
    public static function get_tool_name_table($tool)
8091
    {
8092
        switch ($tool) {
8093
            case 'document':
8094
                $table_name = TABLE_DOCUMENT;
8095
                $link_tool = 'document/document.php';
8096
                $id_tool = 'id';
8097
                break;
8098
            case 'learnpath':
8099
                $table_name = TABLE_LP_MAIN;
8100
                $link_tool = 'lp/lp_controller.php';
8101
                $id_tool = 'id';
8102
                break;
8103
            case 'quiz':
8104
                $table_name = TABLE_QUIZ_TEST;
8105
                $link_tool = 'exercise/exercise.php';
8106
                $id_tool = 'iid';
8107
                break;
8108
            case 'glossary':
8109
                $table_name = TABLE_GLOSSARY;
8110
                $link_tool = 'glossary/index.php';
8111
                $id_tool = 'glossary_id';
8112
                break;
8113
            case 'link':
8114
                $table_name = TABLE_LINK;
8115
                $link_tool = 'link/link.php';
8116
                $id_tool = 'id';
8117
                break;
8118
            case 'course_description':
8119
                $table_name = TABLE_COURSE_DESCRIPTION;
8120
                $link_tool = 'course_description/';
8121
                $id_tool = 'id';
8122
                break;
8123
            case 'announcement':
8124
                $table_name = TABLE_ANNOUNCEMENT;
8125
                $link_tool = 'announcements/announcements.php';
8126
                $id_tool = 'id';
8127
                break;
8128
            case 'thematic':
8129
                $table_name = TABLE_THEMATIC;
8130
                $link_tool = 'course_progress/index.php';
8131
                $id_tool = 'id';
8132
                break;
8133
            case 'thematic_advance':
8134
                $table_name = TABLE_THEMATIC_ADVANCE;
8135
                $link_tool = 'course_progress/index.php';
8136
                $id_tool = 'id';
8137
                break;
8138
            case 'thematic_plan':
8139
                $table_name = TABLE_THEMATIC_PLAN;
8140
                $link_tool = 'course_progress/index.php';
8141
                $id_tool = 'id';
8142
                break;
8143
            default:
8144
                $table_name = $tool;
8145
            break;
8146
        }
8147
8148
        return [
8149
            'table_name' => $table_name,
8150
            'link_tool' => $link_tool,
8151
            'id_tool' => $id_tool,
8152
        ];
8153
    }
8154
8155
    /**
8156
     * @param array $exclude Extra fields to be skipped, by ID
8157
     *
8158
     * @return string
8159
     */
8160
    public static function display_additional_profile_fields($exclude = [])
8161
    {
8162
        // getting all the extra profile fields that are defined by the platform administrator
8163
        $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
8164
8165
        // creating the form
8166
        $return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
8167
8168
        // the select field with the additional user profile fields (= this is where we select the field of which we want to see
8169
        // the information the users have entered or selected.
8170
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
8171
        $return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
8172
        $extra_fields_to_show = 0;
8173
        foreach ($extra_fields as $key => $field) {
8174
            // exclude extra profile fields by id
8175
            if (in_array($field[3], $exclude)) {
8176
                continue;
8177
            }
8178
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
8179
            if ($field[6] == 1 && $field[8] == 1) {
8180
                if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field']) {
8181
                    $selected = 'selected="selected"';
8182
                } else {
8183
                    $selected = '';
8184
                }
8185
                $extra_fields_to_show++;
8186
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
8187
            }
8188
        }
8189
        $return .= '</select>';
8190
8191
        // the form elements for the $_GET parameters (because the form is passed through GET
8192
        foreach ($_GET as $key => $value) {
8193
            if ($key != 'additional_profile_field') {
8194
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
8195
            }
8196
        }
8197
        // the submit button
8198
        $return .= '<button class="save" type="submit">'.get_lang('AddAdditionalProfileField').'</button>';
8199
        $return .= '</form>';
8200
        if ($extra_fields_to_show > 0) {
8201
            return $return;
8202
        } else {
8203
            return '';
8204
        }
8205
    }
8206
8207
    /**
8208
     * This function gets all the information of a certrain ($field_id)
8209
     * additional profile field for a specific list of users is more efficent
8210
     * than get_addtional_profile_information_of_field() function
8211
     * It gets the information of all the users so that it can be displayed
8212
     * in the sortable table or in the csv or xls export.
8213
     *
8214
     * @author    Julio Montoya <[email protected]>
8215
     *
8216
     * @param    int field id
8217
     * @param    array list of user ids
8218
     *
8219
     * @return array
8220
     *
8221
     * @since    Nov 2009
8222
     *
8223
     * @version    1.8.6.2
8224
     */
8225
    public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users)
8226
    {
8227
        // Database table definition
8228
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8229
        $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
8230
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
8231
        $result_extra_field = UserManager::get_extra_field_information($field_id);
8232
        $return = [];
8233
        if (!empty($users)) {
8234
            if ($result_extra_field['field_type'] == UserManager::USER_FIELD_TYPE_TAG) {
8235
                foreach ($users as $user_id) {
8236
                    $user_result = UserManager::get_user_tags($user_id, $field_id);
8237
                    $tag_list = [];
8238
                    foreach ($user_result as $item) {
8239
                        $tag_list[] = $item['tag'];
8240
                    }
8241
                    $return[$user_id][] = implode(', ', $tag_list);
8242
                }
8243
            } else {
8244
                $new_user_array = [];
8245
                foreach ($users as $user_id) {
8246
                    $new_user_array[] = "'".$user_id."'";
8247
                }
8248
                $users = implode(',', $new_user_array);
8249
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
8250
                // Selecting only the necessary information NOT ALL the user list
8251
                $sql = "SELECT user.user_id, v.value
8252
                        FROM $table_user user
8253
                        INNER JOIN $table_user_field_values v
8254
                        ON (user.user_id = v.item_id)
8255
                        INNER JOIN $extraField f
8256
                        ON (f.id = v.field_id)
8257
                        WHERE
8258
                            f.extra_field_type = $extraFieldType AND
8259
                            v.field_id=".intval($field_id)." AND
8260
                            user.user_id IN ($users)";
8261
8262
                $result = Database::query($sql);
8263
                while ($row = Database::fetch_array($result)) {
8264
                    // get option value for field type double select by id
8265
                    if (!empty($row['value'])) {
8266
                        if ($result_extra_field['field_type'] ==
8267
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
8268
                        ) {
8269
                            $id_double_select = explode(';', $row['value']);
8270
                            if (is_array($id_double_select)) {
8271
                                $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
8272
                                $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
8273
                                $row['value'] = ($value1.';'.$value2);
8274
                            }
8275
                        }
8276
8277
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) {
8278
                            $parsedValue = explode('::', $row['value']);
8279
8280
                            if ($parsedValue) {
8281
                                $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text'];
8282
                                $value2 = $parsedValue[1];
8283
8284
                                $row['value'] = "$value1: $value2";
8285
                            }
8286
                        }
8287
8288
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) {
8289
                            [$level1, $level2, $level3] = explode(';', $row['value']);
8290
8291
                            $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / ';
8292
                            $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / ';
8293
                            $row['value'] .= $result_extra_field['options'][$level3]['display_text'];
8294
                        }
8295
                    }
8296
                    // get other value from extra field
8297
                    $return[$row['user_id']][] = $row['value'];
8298
                }
8299
            }
8300
        }
8301
8302
        return $return;
8303
    }
8304
8305
    /**
8306
     * count the number of students in this course (used for SortableTable)
8307
     * Deprecated.
8308
     */
8309
    public function count_student_in_course()
8310
    {
8311
        global $nbStudents;
8312
8313
        return $nbStudents;
8314
    }
8315
8316
    public function sort_users($a, $b)
8317
    {
8318
        $tracking = Session::read('tracking_column');
8319
8320
        return strcmp(
8321
            trim(api_strtolower($a[$tracking])),
8322
            trim(api_strtolower($b[$tracking]))
8323
        );
8324
    }
8325
8326
    public function sort_users_desc($a, $b)
8327
    {
8328
        $tracking = Session::read('tracking_column');
8329
8330
        return strcmp(
8331
            trim(api_strtolower($b[$tracking])),
8332
            trim(api_strtolower($a[$tracking]))
8333
        );
8334
    }
8335
8336
    /**
8337
     * Get number of users for sortable with pagination.
8338
     *
8339
     * @return int
8340
     */
8341
    public static function get_number_of_users($conditions)
8342
    {
8343
        $conditions['get_count'] = true;
8344
8345
        return self::get_user_data(null, null, null, null, $conditions);
8346
    }
8347
8348
    /**
8349
     * Get data for users list in sortable with pagination.
8350
     *
8351
     * @param int $from
8352
     * @param int $number_of_items
8353
     * @param $column
8354
     * @param $direction
8355
     * @param $conditions
8356
     *
8357
     * @return array
8358
     */
8359
    public static function get_user_data(
8360
        $from,
8361
        $number_of_items,
8362
        $column,
8363
        $direction,
8364
        $conditions = []
8365
    ) {
8366
        global $user_ids, $course_code, $export_csv, $session_id;
8367
        $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users
8368
        $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false;
8369
8370
        $csv_content = [];
8371
        $course_code = $course_code ? Database::escape_string($course_code) : api_get_course_id();
8372
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
8373
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8374
        $access_url_id = api_get_current_access_url_id();
8375
8376
        // get all users data from a course for sortable with limit
8377
        if (is_array($user_ids)) {
8378
            $user_ids = array_map('intval', $user_ids);
8379
            $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") ";
8380
        } else {
8381
            $user_ids = (int) $user_ids;
8382
            $condition_user = " WHERE user.id = $user_ids ";
8383
        }
8384
8385
        if (!empty($_GET['user_keyword'])) {
8386
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
8387
            $condition_user .= " AND (
8388
                user.firstname LIKE '%".$keyword."%' OR
8389
                user.lastname LIKE '%".$keyword."%'  OR
8390
                user.username LIKE '%".$keyword."%'  OR
8391
                user.email LIKE '%".$keyword."%'
8392
             ) ";
8393
        }
8394
8395
        $url_table = '';
8396
        $url_condition = '';
8397
        if (api_is_multiple_url_enabled()) {
8398
            $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)";
8399
            $url_condition = " AND access_url_id = '$access_url_id'";
8400
        }
8401
8402
        $invitedUsersCondition = '';
8403
        if (!$includeInvitedUsers) {
8404
            $invitedUsersCondition = " AND user.status != ".INVITEE;
8405
        }
8406
8407
        $select = '
8408
                SELECT user.id as user_id,
8409
                    user.official_code  as col0,
8410
                    user.lastname       as col1,
8411
                    user.firstname      as col2,
8412
                    user.username       as col3,
8413
                    user.email          as col4';
8414
        if ($getCount) {
8415
            $select = ' SELECT COUNT(distinct(user.id)) as count ';
8416
        }
8417
8418
        $sqlInjectJoins = '';
8419
        $where = 'AND 1 = 1 ';
8420
        $sqlInjectWhere = '';
8421
        if (!empty($conditions)) {
8422
            if (isset($conditions['inject_joins'])) {
8423
                $sqlInjectJoins = $conditions['inject_joins'];
8424
            }
8425
            if (isset($conditions['where'])) {
8426
                $where = $conditions['where'];
8427
            }
8428
            if (isset($conditions['inject_where'])) {
8429
                $sqlInjectWhere = $conditions['inject_where'];
8430
            }
8431
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
8432
            $injectExtraFields = rtrim($injectExtraFields, ', ');
8433
            if (false === $getCount) {
8434
                $select .= " , $injectExtraFields";
8435
            }
8436
        }
8437
8438
        $sql = "$select
8439
                FROM $tbl_user as user
8440
                $url_table
8441
                $sqlInjectJoins
8442
                $condition_user
8443
                $url_condition
8444
                $invitedUsersCondition
8445
                $where
8446
                $sqlInjectWhere
8447
                ";
8448
8449
        if (!in_array($direction, ['ASC', 'DESC'])) {
8450
            $direction = 'ASC';
8451
        }
8452
8453
        $column = (int) $column;
8454
        $from = (int) $from;
8455
        $number_of_items = (int) $number_of_items;
8456
8457
        if ($getCount) {
8458
            $res = Database::query($sql);
8459
            $row = Database::fetch_array($res);
8460
8461
            return $row['count'];
8462
        }
8463
8464
        $sql .= " ORDER BY col$column $direction ";
8465
        $sql .= " LIMIT $from, $number_of_items";
8466
8467
        $res = Database::query($sql);
8468
        $users = [];
8469
8470
        $courseInfo = api_get_course_info($course_code);
8471
        $courseId = $courseInfo['real_id'];
8472
        $courseCode = $courseInfo['code'];
8473
8474
        $total_surveys = 0;
8475
        $total_exercises = ExerciseLib::get_all_exercises(
8476
            $courseInfo,
8477
            $session_id,
8478
            false,
8479
            null,
8480
            false,
8481
            3
8482
        );
8483
8484
        if (empty($session_id)) {
8485
            $survey_user_list = [];
8486
            $surveyList = SurveyManager::get_surveys($course_code, $session_id);
8487
            if ($surveyList) {
8488
                $total_surveys = count($surveyList);
8489
                foreach ($surveyList as $survey) {
8490
                    $user_list = SurveyManager::get_people_who_filled_survey(
8491
                        $survey['survey_id'],
8492
                        false,
8493
                        $courseId
8494
                    );
8495
8496
                    foreach ($user_list as $user_id) {
8497
                        isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
8498
                    }
8499
                }
8500
            }
8501
        }
8502
8503
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode.
8504
            '&course='.$course_code.'&origin=tracking_course&id_session='.$session_id;
8505
8506
        $sortByFirstName = api_sort_by_first_name();
8507
        Session::write('user_id_list', []);
8508
        $userIdList = [];
8509
8510
        $addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report');
8511
        $exerciseResultsToCheck = [];
8512
        if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) &&
8513
            isset($addExerciseOption['courses'][$courseCode])
8514
        ) {
8515
            foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) {
8516
                $exercise = new Exercise();
8517
                $exercise->read($exerciseId);
8518
                if ($exercise->iId) {
8519
                    $exerciseResultsToCheck[] = $exercise;
8520
                }
8521
            }
8522
        }
8523
8524
        while ($user = Database::fetch_array($res, 'ASSOC')) {
8525
            $userIdList[] = $user['user_id'];
8526
            $user['official_code'] = $user['col0'];
8527
            $user['username'] = $user['col3'];
8528
            $user['time'] = api_time_to_hms(
8529
                Tracking::get_time_spent_on_the_course(
8530
                    $user['user_id'],
8531
                    $courseId,
8532
                    $session_id
8533
                )
8534
            );
8535
8536
            $avg_student_score = Tracking::get_avg_student_score(
8537
                $user['user_id'],
8538
                $course_code,
8539
                [],
8540
                $session_id
8541
            );
8542
8543
            $averageBestScore = Tracking::get_avg_student_score(
8544
                $user['user_id'],
8545
                $course_code,
8546
                [],
8547
                $session_id,
8548
                false,
8549
                false,
8550
                true
8551
            );
8552
8553
            $avg_student_progress = Tracking::get_avg_student_progress(
8554
                $user['user_id'],
8555
                $course_code,
8556
                [],
8557
                $session_id
8558
            );
8559
8560
            if (empty($avg_student_progress)) {
8561
                $avg_student_progress = 0;
8562
            }
8563
            $user['average_progress'] = $avg_student_progress.'%';
8564
8565
            $total_user_exercise = Tracking::get_exercise_student_progress(
8566
                $total_exercises,
8567
                $user['user_id'],
8568
                $courseId,
8569
                $session_id
8570
            );
8571
8572
            $user['exercise_progress'] = $total_user_exercise;
8573
8574
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
8575
                $total_exercises,
8576
                $user['user_id'],
8577
                $courseId,
8578
                $session_id
8579
            );
8580
8581
            $user['exercise_average_best_attempt'] = $total_user_exercise;
8582
8583
            if (is_numeric($avg_student_score)) {
8584
                $user['student_score'] = $avg_student_score.'%';
8585
            } else {
8586
                $user['student_score'] = $avg_student_score;
8587
            }
8588
8589
            if (is_numeric($averageBestScore)) {
8590
                $user['student_score_best'] = $averageBestScore.'%';
8591
            } else {
8592
                $user['student_score_best'] = $averageBestScore;
8593
            }
8594
8595
            $exerciseResults = [];
8596
            if (!empty($exerciseResultsToCheck)) {
8597
                foreach ($exerciseResultsToCheck as $exercise) {
8598
                    $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user(
8599
                        $user['user_id'],
8600
                        $exercise->iId,
8601
                        $courseId,
8602
                        $session_id,
8603
                        false
8604
                    );
8605
8606
                    $best = null;
8607
                    if ($bestExerciseResult) {
8608
                        $best = $bestExerciseResult['exe_result'] / $bestExerciseResult['exe_weighting'];
8609
                        $best = round($best, 2) * 100;
8610
                        $best .= '%';
8611
                    }
8612
                    $exerciseResults['exercise_'.$exercise->iId] = $best;
8613
                }
8614
            }
8615
8616
            $user['count_assignments'] = Tracking::count_student_assignments(
8617
                $user['user_id'],
8618
                $course_code,
8619
                $session_id
8620
            );
8621
            $user['count_messages'] = Tracking::count_student_messages(
8622
                $user['user_id'],
8623
                $course_code,
8624
                $session_id
8625
            );
8626
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
8627
                $user['user_id'],
8628
                $courseId,
8629
                $session_id,
8630
                false === $export_csv
8631
            );
8632
8633
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
8634
                $user['user_id'],
8635
                $courseInfo,
8636
                $session_id,
8637
                false === $export_csv
8638
            );
8639
8640
            if ($export_csv) {
8641
                if (!empty($user['first_connection'])) {
8642
                    $user['first_connection'] = api_get_local_time($user['first_connection']);
8643
                } else {
8644
                    $user['first_connection'] = '-';
8645
                }
8646
                if (!empty($user['last_connection'])) {
8647
                    $user['last_connection'] = api_get_local_time($user['last_connection']);
8648
                } else {
8649
                    $user['last_connection'] = '-';
8650
                }
8651
            }
8652
8653
            if (empty($session_id)) {
8654
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys;
8655
            }
8656
8657
            $url = $urlBase.'&student='.$user['user_id'];
8658
8659
            $user['link'] = '<center><a href="'.$url.'">
8660
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
8661
                             </a></center>';
8662
8663
            // store columns in array $users
8664
            $user_row = [];
8665
            $user_row['official_code'] = $user['official_code']; //0
8666
            if ($sortByFirstName) {
8667
                $user_row['firstname'] = $user['col2'];
8668
                $user_row['lastname'] = $user['col1'];
8669
            } else {
8670
                $user_row['lastname'] = $user['col1'];
8671
                $user_row['firstname'] = $user['col2'];
8672
            }
8673
            $user_row['username'] = $user['username'];
8674
            $user_row['time'] = $user['time'];
8675
            $user_row['average_progress'] = $user['average_progress'];
8676
            $user_row['exercise_progress'] = $user['exercise_progress'];
8677
            $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
8678
            $user_row['student_score'] = $user['student_score'];
8679
            $user_row['student_score_best'] = $user['student_score_best'];
8680
            if (!empty($exerciseResults)) {
8681
                foreach ($exerciseResults as $exerciseId => $bestResult) {
8682
                    $user_row[$exerciseId] = $bestResult;
8683
                }
8684
            }
8685
8686
            $user_row['count_assignments'] = $user['count_assignments'];
8687
            $user_row['count_messages'] = $user['count_messages'];
8688
8689
            $userGroupManager = new UserGroup();
8690
            if ($export_csv) {
8691
                $user_row['classes'] = implode(
8692
                    ',',
8693
                    $userGroupManager->getNameListByUser($user['user_id'], UserGroup::NORMAL_CLASS)
8694
                );
8695
            } else {
8696
                $user_row['classes'] = $userGroupManager->getLabelsFromNameList(
8697
                    $user['user_id'],
8698
                    UserGroup::NORMAL_CLASS
8699
                );
8700
            }
8701
8702
            if (empty($session_id)) {
8703
                $user_row['survey'] = $user['survey'];
8704
            } else {
8705
                $userSession = SessionManager::getUserSession($user['user_id'], $session_id);
8706
                $user_row['registered_at'] = '';
8707
                if ($userSession) {
8708
                    $user_row['registered_at'] = api_get_local_time($userSession['registered_at']);
8709
                }
8710
            }
8711
8712
            $user_row['first_connection'] = $user['first_connection'];
8713
            $user_row['last_connection'] = $user['last_connection'];
8714
8715
            // we need to display an additional profile field
8716
            if (isset($_GET['additional_profile_field'])) {
8717
                $data = Session::read('additional_user_profile_info');
8718
8719
                $extraFieldInfo = Session::read('extra_field_info');
8720
                foreach ($_GET['additional_profile_field'] as $fieldId) {
8721
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
8722
                        if (is_array($data[$fieldId][$user['user_id']])) {
8723
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = implode(
8724
                                ', ',
8725
                                $data[$fieldId][$user['user_id']]
8726
                            );
8727
                        } else {
8728
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
8729
                        }
8730
                    } else {
8731
                        $user_row[$extraFieldInfo[$fieldId]['variable']] = '';
8732
                    }
8733
                }
8734
            }
8735
8736
            $data = Session::read('default_additional_user_profile_info');
8737
            $defaultExtraFieldInfo = Session::read('default_extra_field_info');
8738
            if (isset($defaultExtraFieldInfo) && isset($data)) {
8739
                foreach ($data as $key => $val) {
8740
                    if (isset($val[$user['user_id']])) {
8741
                        if (is_array($val[$user['user_id']])) {
8742
                            $user_row[$defaultExtraFieldInfo[$key]['variable']] = implode(
8743
                                ', ',
8744
                                $val[$user['user_id']]
8745
                            );
8746
                        } else {
8747
                            $user_row[$defaultExtraFieldInfo[$key]['variable']] = $val[$user['user_id']];
8748
                        }
8749
                    } else {
8750
                        $user_row[$defaultExtraFieldInfo[$key]['variable']] = '';
8751
                    }
8752
                }
8753
            }
8754
8755
            if (api_get_setting('show_email_addresses') === 'true') {
8756
                $user_row['email'] = $user['col4'];
8757
            }
8758
8759
            $user_row['link'] = $user['link'];
8760
8761
            if ($export_csv) {
8762
                if (empty($session_id)) {
8763
                    //unset($user_row['classes']);
8764
                    unset($user_row['link']);
8765
                } else {
8766
                    //unset($user_row['classes']);
8767
                    unset($user_row['link']);
8768
                }
8769
                $csv_content[] = $user_row;
8770
            }
8771
            $users[] = array_values($user_row);
8772
        }
8773
8774
        if ($export_csv) {
8775
            Session::write('csv_content', $csv_content);
8776
        }
8777
8778
        Session::erase('additional_user_profile_info');
8779
        Session::erase('extra_field_info');
8780
        Session::erase('default_additional_user_profile_info');
8781
        Session::erase('default_extra_field_info');
8782
        Session::write('user_id_list', $userIdList);
8783
8784
        return $users;
8785
    }
8786
8787
    /**
8788
     * Get data for users list in sortable with pagination.
8789
     *
8790
     * @param $from
8791
     * @param $number_of_items
8792
     * @param $column
8793
     * @param $direction
8794
     * @param $includeInvitedUsers boolean Whether include the invited users
8795
     *
8796
     * @return array
8797
     */
8798
    public static function getTotalTimeReport(
8799
        $from,
8800
        $number_of_items,
8801
        $column,
8802
        $direction,
8803
        $includeInvitedUsers = false
8804
    ) {
8805
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
8806
8807
        $course_code = Database::escape_string($course_code);
8808
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
8809
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8810
        $access_url_id = api_get_current_access_url_id();
8811
8812
        // get all users data from a course for sortable with limit
8813
        if (is_array($user_ids)) {
8814
            $user_ids = array_map('intval', $user_ids);
8815
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
8816
        } else {
8817
            $user_ids = intval($user_ids);
8818
            $condition_user = " WHERE user.user_id = $user_ids ";
8819
        }
8820
8821
        $url_table = null;
8822
        $url_condition = null;
8823
        if (api_is_multiple_url_enabled()) {
8824
            $url_table = ", ".$tbl_url_rel_user." as url_users";
8825
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
8826
        }
8827
8828
        $invitedUsersCondition = '';
8829
        if (!$includeInvitedUsers) {
8830
            $invitedUsersCondition = " AND user.status != ".INVITEE;
8831
        }
8832
8833
        $sql = "SELECT  user.user_id as user_id,
8834
                    user.official_code  as col0,
8835
                    user.lastname       as col1,
8836
                    user.firstname      as col2,
8837
                    user.username       as col3
8838
                FROM $tbl_user as user $url_table
8839
                $condition_user $url_condition $invitedUsersCondition";
8840
8841
        if (!in_array($direction, ['ASC', 'DESC'])) {
8842
            $direction = 'ASC';
8843
        }
8844
8845
        $column = (int) $column;
8846
        $from = (int) $from;
8847
        $number_of_items = (int) $number_of_items;
8848
8849
        $sql .= " ORDER BY col$column $direction ";
8850
        $sql .= " LIMIT $from,$number_of_items";
8851
8852
        $res = Database::query($sql);
8853
        $users = [];
8854
8855
        $sortByFirstName = api_sort_by_first_name();
8856
        $courseInfo = api_get_course_info($course_code);
8857
        $courseId = $courseInfo['real_id'];
8858
8859
        while ($user = Database::fetch_array($res, 'ASSOC')) {
8860
            $user['official_code'] = $user['col0'];
8861
            $user['lastname'] = $user['col1'];
8862
            $user['firstname'] = $user['col2'];
8863
            $user['username'] = $user['col3'];
8864
8865
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
8866
                $user['user_id'],
8867
                $courseId,
8868
                $session_id
8869
            );
8870
8871
            $user['time'] = api_time_to_hms($totalCourseTime);
8872
            $totalLpTime = Tracking::get_time_spent_in_lp(
8873
                $user['user_id'],
8874
                $course_code,
8875
                [],
8876
                $session_id
8877
            );
8878
8879
            $user['total_lp_time'] = $totalLpTime;
8880
            $warning = '';
8881
            if ($totalLpTime > $totalCourseTime) {
8882
                $warning = '&nbsp;'.Display::label(get_lang('TimeDifference'), 'danger');
8883
            }
8884
8885
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
8886
8887
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
8888
                $user['user_id'],
8889
                $courseId,
8890
                $session_id
8891
            );
8892
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
8893
                $user['user_id'],
8894
                $courseInfo,
8895
                $session_id,
8896
                $export_csv === false
8897
            );
8898
8899
            $user['link'] = '<center>
8900
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
8901
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
8902
                             </a>
8903
                         </center>';
8904
8905
            // store columns in array $users
8906
            $user_row = [];
8907
            $user_row['official_code'] = $user['official_code']; //0
8908
            if ($sortByFirstName) {
8909
                $user_row['firstname'] = $user['firstname'];
8910
                $user_row['lastname'] = $user['lastname'];
8911
            } else {
8912
                $user_row['lastname'] = $user['lastname'];
8913
                $user_row['firstname'] = $user['firstname'];
8914
            }
8915
            $user_row['username'] = $user['username'];
8916
            $user_row['time'] = $user['time'];
8917
            $user_row['total_lp_time'] = $user['total_lp_time'];
8918
            $user_row['first_connection'] = $user['first_connection'];
8919
            $user_row['last_connection'] = $user['last_connection'];
8920
8921
            $user_row['link'] = $user['link'];
8922
            $users[] = array_values($user_row);
8923
        }
8924
8925
        return $users;
8926
    }
8927
8928
    /**
8929
     * @param string $current
8930
     */
8931
    public static function actionsLeft($current, $sessionId = 0)
8932
    {
8933
        $usersLink = Display::url(
8934
            Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM),
8935
            'courseLog.php?'.api_get_cidreq(true, false)
8936
        );
8937
8938
        $groupsLink = Display::url(
8939
            Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
8940
            'course_log_groups.php?'.api_get_cidreq()
8941
        );
8942
8943
        $resourcesLink = Display::url(
8944
            Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM),
8945
            'course_log_resources.php?'.api_get_cidreq(true, false)
8946
        );
8947
8948
        $courseLink = Display::url(
8949
            Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
8950
            'course_log_tools.php?'.api_get_cidreq(true, false)
8951
        );
8952
8953
        $examLink = Display::url(
8954
            Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
8955
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
8956
        );
8957
8958
        $eventsLink = Display::url(
8959
            Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
8960
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
8961
        );
8962
8963
        $lpLink = Display::url(
8964
            Display::return_icon('scorms.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
8965
            api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq()
8966
        );
8967
8968
        $attendanceLink = '';
8969
        if (!empty($sessionId)) {
8970
            $attendanceLink = Display::url(
8971
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8972
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
8973
            );
8974
        }
8975
8976
        switch ($current) {
8977
            case 'users':
8978
                $usersLink = Display::url(
8979
                        Display::return_icon(
8980
                        'user_na.png',
8981
                        get_lang('StudentsTracking'),
8982
                        [],
8983
                        ICON_SIZE_MEDIUM
8984
                    ),
8985
                    '#'
8986
                );
8987
                break;
8988
            case 'groups':
8989
                $groupsLink = Display::url(
8990
                    Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
8991
                    '#'
8992
                );
8993
                break;
8994
            case 'courses':
8995
                $courseLink = Display::url(
8996
                    Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
8997
                    '#'
8998
                );
8999
                break;
9000
            case 'resources':
9001
                $resourcesLink = Display::url(
9002
                    Display::return_icon(
9003
                    'tools_na.png',
9004
                    get_lang('ResourcesTracking'),
9005
                    [],
9006
                    ICON_SIZE_MEDIUM
9007
                    ),
9008
                    '#'
9009
                );
9010
                break;
9011
            case 'exams':
9012
                $examLink = Display::url(
9013
                    Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
9014
                    '#'
9015
                );
9016
                break;
9017
            case 'logs':
9018
                $eventsLink = Display::url(
9019
                    Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
9020
                    '#'
9021
                );
9022
                break;
9023
            case 'attendance':
9024
                if (!empty($sessionId)) {
9025
                    $attendanceLink = Display::url(
9026
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
9027
                        '#'
9028
                    );
9029
                }
9030
                break;
9031
            case 'lp':
9032
                $lpLink = Display::url(
9033
                    Display::return_icon('scorms_na.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
9034
                    '#'
9035
                );
9036
                break;
9037
        }
9038
9039
        $items = [
9040
            $usersLink,
9041
            $groupsLink,
9042
            $courseLink,
9043
            $resourcesLink,
9044
            $examLink,
9045
            $eventsLink,
9046
            $lpLink,
9047
            $attendanceLink,
9048
        ];
9049
9050
        return implode('', $items).'&nbsp;';
9051
    }
9052
}
9053