Passed
Push — 1.11.x ( a9dc20...339d57 )
by Angel Fernando Quiroz
11:02
created

Tracking::generateQuizzesTable()   C

Complexity

Conditions 13
Paths 118

Size

Total Lines 167
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 105
c 1
b 0
f 0
nc 118
nop 2
dl 0
loc 167
rs 5.1733

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\UserBundle\Entity\User;
9
use ChamiloSession as Session;
10
use CpChart\Cache as pCache;
11
use CpChart\Data as pData;
12
use CpChart\Image as pImage;
13
use ExtraField as ExtraFieldModel;
14
15
/**
16
 *  Class Tracking.
17
 *
18
 *  @author  Julio Montoya <[email protected]>
19
 */
20
class Tracking
21
{
22
    /**
23
     * Get group reporting.
24
     *
25
     * @param int    $course_id
26
     * @param int    $sessionId
27
     * @param int    $group_id
28
     * @param string $type
29
     * @param int    $start
30
     * @param int    $limit
31
     * @param int    $sidx
32
     * @param string $sord
33
     * @param array  $where_condition
34
     *
35
     * @return array|null
36
     */
37
    public static function get_group_reporting(
38
        $course_id,
39
        $sessionId = 0,
40
        $group_id = 0,
41
        $type = 'all',
42
        $start = 0,
43
        $limit = 1000,
44
        $sidx = 1,
45
        $sord = 'desc',
46
        $where_condition = []
47
    ) {
48
        $course_id = (int) $course_id;
49
        $sessionId = (int) $sessionId;
50
51
        if (empty($course_id)) {
52
            return null;
53
        }
54
        $courseInfo = api_get_course_info_by_id($course_id);
55
        if ('count' == $type) {
56
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
57
        }
58
59
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
60
        $parsedResult = [];
61
        if (!empty($groupList)) {
62
            foreach ($groupList as $group) {
63
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
64
                $time = 0;
65
                $avg_student_score = 0;
66
                $avg_student_progress = 0;
67
                $work = 0;
68
                $messages = 0;
69
70
                foreach ($users as $user_data) {
71
                    $time += self::get_time_spent_on_the_course(
72
                        $user_data['user_id'],
73
                        $courseInfo['real_id'],
74
                        $sessionId
75
                    );
76
                    $average = self::get_avg_student_score(
77
                        $user_data['user_id'],
78
                        $courseInfo['code'],
79
                        [],
80
                        $sessionId
81
                    );
82
                    if (is_numeric($average)) {
83
                        $avg_student_score += $average;
84
                    }
85
                    $avg_student_progress += self::get_avg_student_progress(
86
                        $user_data['user_id'],
87
                        $courseInfo['code'],
88
                        [],
89
                        $sessionId
90
                    );
91
                    $work += self::count_student_assignments(
92
                        $user_data['user_id'],
93
                        $courseInfo['code'],
94
                        $sessionId
95
                    );
96
                    $messages += self::count_student_messages(
97
                        $user_data['user_id'],
98
                        $courseInfo['code'],
99
                        $sessionId
100
                    );
101
                }
102
103
                $countUsers = count($users);
104
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
105
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
106
107
                $groupItem = [
108
                    'id' => $group['id'],
109
                    'name' => $group['name'],
110
                    'time' => api_time_to_hms($time),
111
                    'progress' => $averageProgress,
112
                    'score' => $averageScore,
113
                    'works' => $work,
114
                    'messages' => $messages,
115
                ];
116
                $parsedResult[] = $groupItem;
117
            }
118
        }
119
120
        return $parsedResult;
121
    }
122
123
    /**
124
     * @param int    $user_id
125
     * @param array  $courseInfo
126
     * @param int    $session_id
127
     * @param string $origin
128
     * @param bool   $export_csv
129
     * @param int    $lp_id
130
     * @param int    $lp_item_id
131
     * @param int    $extendId
132
     * @param int    $extendAttemptId
133
     * @param string $extendedAttempt
134
     * @param string $extendedAll
135
     * @param string $type            classic or simple
136
     * @param bool   $allowExtend     Optional. Allow or not extend te results
137
     *
138
     * @return string
139
     */
140
    public static function getLpStats(
141
        $user_id,
142
        $courseInfo,
143
        $session_id,
144
        $origin,
145
        $export_csv,
146
        $lp_id,
147
        $lp_item_id = null,
148
        $extendId = null,
149
        $extendAttemptId = null,
150
        $extendedAttempt = null,
151
        $extendedAll = null,
152
        $type = 'classic',
153
        $allowExtend = true
154
    ) {
155
        if (empty($courseInfo) || empty($lp_id)) {
156
            return '';
157
        }
158
159
        $hideTime = api_get_configuration_value('hide_lp_time');
160
        $lp_id = (int) $lp_id;
161
        $lp_item_id = (int) $lp_item_id;
162
        $user_id = (int) $user_id;
163
        $session_id = (int) $session_id;
164
        $origin = Security::remove_XSS($origin);
165
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
166
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
167
        $course_id = $courseInfo['real_id'];
168
        $courseCode = $courseInfo['code'];
169
        $session_condition = api_get_session_condition($session_id);
170
171
        // Extend all button
172
        $output = '';
173
174
        $extra = '<script>
175
        $(function() {
176
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
177
            $( "#dialog-confirm" ).dialog({
178
                autoOpen: false,
179
                show: "blind",
180
                resizable: false,
181
                height:300,
182
                modal: true
183
            });
184
185
            $(".export").click(function() {
186
                var targetUrl = $(this).attr("href");
187
                $( "#dialog-confirm" ).dialog({
188
                    width:400,
189
                    height:300,
190
                    buttons: {
191
                        "'.addslashes(get_lang('Download')).'": function() {
192
                            var option = $("input[name=add_logo]:checked").val();
193
                            location.href = targetUrl+"&add_logo="+option;
194
                            $(this).dialog("close");
195
                        }
196
                    }
197
                });
198
                $("#dialog-confirm").dialog("open");
199
200
                return false;
201
            });
202
        });
203
        </script>';
204
205
        $extra .= '<div id="dialog-confirm" title="'.get_lang('ConfirmYourChoice').'">';
206
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
207
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
208
        $extra .= $form->returnForm();
209
        $extra .= '</div>';
210
211
        $output .= $extra;
212
213
        $url_suffix = '&lp_id='.$lp_id;
214
        if ('tracking' === $origin) {
215
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
216
        }
217
218
        $extend_all = 0;
219
        if (!empty($extendedAll)) {
220
            $extend_all_link = Display::url(
221
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
222
                api_get_self().'?action=stats'.$url_suffix
223
            );
224
            $extend_all = 1;
225
        } else {
226
            $extend_all_link = Display::url(
227
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
228
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
229
            );
230
        }
231
232
        if ($origin != 'tracking') {
233
            $output .= '<div class="section-status">';
234
            $output .= Display::page_header(get_lang('ScormMystatus'));
235
            $output .= '</div>';
236
        }
237
238
        $actionColumn = null;
239
        if ($type === 'classic') {
240
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
241
        }
242
243
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
244
        if ($hideTime) {
245
            $timeHeader = '';
246
        }
247
        $output .= '<div class="table-responsive">';
248
        $output .= '<table id="lp_tracking" class="table tracking">
249
            <thead>
250
            <tr class="table-header">
251
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
252
                <th colspan="4">
253
                    '.get_lang('ScormLessonTitle').'
254
                </th>
255
                <th colspan="2">
256
                    '.get_lang('ScormStatus').'
257
                </th>
258
                <th colspan="2">
259
                    '.get_lang('ScormScore').'
260
                </th>
261
                '.$timeHeader.'
262
                '.$actionColumn.'
263
                </tr>
264
            </thead>
265
            <tbody>
266
        ';
267
268
        // Going through the items using the $items[] array instead of the database order ensures
269
        // we get them in the same order as in the imsmanifest file, which is rather random when using
270
        // the database table.
271
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
272
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
273
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
274
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
275
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
276
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
277
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
278
279
        $sql = "SELECT max(view_count)
280
                FROM $TBL_LP_VIEW
281
                WHERE
282
                    c_id = $course_id AND
283
                    lp_id = $lp_id AND
284
                    user_id = $user_id
285
                    $session_condition";
286
        $res = Database::query($sql);
287
        $view = 0;
288
        if (Database::num_rows($res) > 0) {
289
            $myrow = Database::fetch_array($res);
290
            $view = (int) $myrow[0];
291
        }
292
293
        $counter = 0;
294
        $total_time = 0;
295
        $h = get_lang('h');
296
297
        if (!empty($export_csv)) {
298
            $csvHeaders = [
299
                get_lang('ScormLessonTitle'),
300
                get_lang('ScormStatus'),
301
                get_lang('ScormScore'),
302
            ];
303
304
            if ($hideTime === false) {
305
                $csvHeaders[] = get_lang('ScormTime');
306
            }
307
308
            $csv_content[] = $csvHeaders;
309
        }
310
311
        $result_disabled_ext_all = true;
312
        $chapterTypes = learnpath::getChapterTypes();
313
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
314
315
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
316
        $timeCourse = [];
317
        if ($minimumAvailable) {
318
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
319
            Session::write('trackTimeCourse', $timeCourse);
320
        }
321
322
        // Show lp items
323
        if (is_array($list) && count($list) > 0) {
324
            foreach ($list as $my_item_id) {
325
                $extend_this = 0;
326
                $order = 'DESC';
327
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
328
                    $extend_this = 1;
329
                    $order = 'ASC';
330
                }
331
332
                // Prepare statement to go through each attempt.
333
                $viewCondition = null;
334
                if (!empty($view)) {
335
                    $viewCondition = " AND v.view_count = $view  ";
336
                }
337
338
                $sql = "SELECT
339
                    iv.status as mystatus,
340
                    v.view_count as mycount,
341
                    iv.score as myscore,
342
                    iv.total_time as mytime,
343
                    i.iid as myid,
344
                    i.lp_id as mylpid,
345
                    iv.lp_view_id as mylpviewid,
346
                    i.title as mytitle,
347
                    i.max_score as mymaxscore,
348
                    iv.max_score as myviewmaxscore,
349
                    i.item_type as item_type,
350
                    iv.view_count as iv_view_count,
351
                    iv.id as iv_id,
352
                    path
353
                FROM $TBL_LP_ITEM as i
354
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
355
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
356
                INNER JOIN $TBL_LP_VIEW as v
357
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
358
                WHERE
359
                    v.c_id = $course_id AND
360
                    i.iid = $my_item_id AND
361
                    i.lp_id = $lp_id  AND
362
                    v.user_id = $user_id AND
363
                    v.session_id = $session_id
364
                    $viewCondition
365
                ORDER BY iv.view_count $order ";
366
367
                $result = Database::query($sql);
368
                $num = Database::num_rows($result);
369
                $time_for_total = 0;
370
                $attemptResult = 0;
371
372
                if ($timeCourse) {
373
                    if (isset($timeCourse['learnpath_detailed']) &&
374
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
375
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
376
                    ) {
377
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
378
                    }
379
                }
380
381
                // Extend all
382
                if (($extend_this || $extend_all) && $num > 0) {
383
                    $row = Database::fetch_array($result);
384
                    $result_disabled_ext_all = false;
385
                    if ('quiz' === $row['item_type']) {
386
                        // Check results_disabled in quiz table.
387
                        $my_path = Database::escape_string($row['path']);
388
                        $sql = "SELECT results_disabled
389
                                FROM $TBL_QUIZ
390
                                WHERE
391
                                    c_id = $course_id AND
392
                                    id ='".$my_path."'";
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
                    $my_path = $row['path'];
691
                    $result_disabled_ext_all = false;
692
                    if ($row['item_type'] === 'quiz') {
693
                        // Check results_disabled in quiz table.
694
                        $my_path = Database::escape_string($my_path);
695
                        $sql = "SELECT results_disabled
696
                                FROM $TBL_QUIZ
697
                                WHERE c_id = $course_id AND id = '$my_path' ";
698
                        $res_result_disabled = Database::query($sql);
699
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
700
701
                        if (Database::num_rows($res_result_disabled) > 0 &&
702
                            (int) $row_result_disabled[0] === 1
703
                        ) {
704
                            $result_disabled_ext_all = true;
705
                        }
706
                    }
707
708
                    // Check if there are interactions below
709
                    $extend_this_attempt = 0;
710
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
711
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
712
                    $extend_attempt_link = '';
713
                    if ($inter_num > 0 || $objec_num > 0) {
714
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
715
                            // The extend button for this attempt has been clicked.
716
                            $extend_this_attempt = 1;
717
                            $extend_attempt_link = Display::url(
718
                                Display::return_icon('visible.png', get_lang('HideAttemptView')),
719
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
720
                            );
721
                        } else {
722
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
723
                            // The extend button for this attempt has not been clicked.
724
                            $extend_attempt_link = Display::url(
725
                                Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
726
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
727
                            );
728
                        }
729
                    }
730
731
                    $oddclass = 'row_even';
732
                    if (($counter % 2) == 0) {
733
                        $oddclass = 'row_odd';
734
                    }
735
736
                    $extend_link = '';
737
                    if ($inter_num > 1) {
738
                        $extend_link = Display::url(
739
                            Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
740
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
741
                        );
742
                    }
743
744
                    $lesson_status = $row['mystatus'];
745
                    $score = $row['myscore'];
746
                    $subtotal_time = $row['mytime'];
747
                    while ($tmp_row = Database::fetch_array($result)) {
748
                        $subtotal_time += $tmp_row['mytime'];
749
                    }
750
751
                    $title = $row['mytitle'];
752
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
753
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
754
                            WHERE
755
                                exe_exo_id="'.$row['path'].'" AND
756
                                exe_user_id="'.$user_id.'" AND
757
                                orig_lp_id = "'.$lp_id.'" AND
758
                                orig_lp_item_id = "'.$row['myid'].'" AND
759
                                c_id = '.$course_id.' AND
760
                                status <> "incomplete" AND
761
                                session_id = '.$session_id.'
762
                             ORDER BY exe_date DESC
763
                             LIMIT 1';
764
765
                    $resultLastAttempt = Database::query($sql);
766
                    $num = Database::num_rows($resultLastAttempt);
767
                    $id_last_attempt = null;
768
                    if ($num > 0) {
769
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
770
                            $id_last_attempt = $rowLA['exe_id'];
771
                        }
772
                    }
773
774
                    switch ($row['item_type']) {
775
                        case 'sco':
776
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
777
                                $maxscore = $row['myviewmaxscore'];
778
                            } elseif ($row['myviewmaxscore'] === '') {
779
                                $maxscore = 0;
780
                            } else {
781
                                $maxscore = $row['mymaxscore'];
782
                            }
783
                            break;
784
                        case 'quiz':
785
                            // Get score and total time from last attempt of a exercise en lp.
786
                            $sql = "SELECT iid, score
787
                                    FROM $TBL_LP_ITEM_VIEW
788
                                    WHERE
789
                                        c_id = $course_id AND
790
                                        lp_item_id = '".(int) $my_id."' AND
791
                                        lp_view_id = '".(int) $my_lp_view_id."'
792
                                    ORDER BY view_count DESC
793
                                    LIMIT 1";
794
                            $res_score = Database::query($sql);
795
                            $row_score = Database::fetch_array($res_score);
796
797
                            $sql = "SELECT SUM(total_time) as total_time
798
                                    FROM $TBL_LP_ITEM_VIEW
799
                                    WHERE
800
                                        c_id = $course_id AND
801
                                        lp_item_id = '".(int) $my_id."' AND
802
                                        lp_view_id = '".(int) $my_lp_view_id."'";
803
                            $res_time = Database::query($sql);
804
                            $row_time = Database::fetch_array($res_time);
805
806
                            $score = 0;
807
                            $subtotal_time = 0;
808
                            if (Database::num_rows($res_score) > 0 &&
809
                                Database::num_rows($res_time) > 0
810
                            ) {
811
                                $score = (float) $row_score['score'];
812
                                $subtotal_time = (int) $row_time['total_time'];
813
                            }
814
                            // Selecting the max score from an attempt.
815
                            $sql = "SELECT SUM(t.ponderation) as maxscore
816
                                    FROM (
817
                                        SELECT DISTINCT
818
                                            question_id, marks, ponderation
819
                                        FROM $tbl_stats_attempts as at
820
                                        INNER JOIN $tbl_quiz_questions as q
821
                                        ON (q.id = at.question_id AND q.c_id = $course_id)
822
                                        WHERE exe_id ='$id_last_attempt'
823
                                    ) as t";
824
825
                            $result = Database::query($sql);
826
                            $row_max_score = Database::fetch_array($result);
827
                            $maxscore = $row_max_score['maxscore'];
828
829
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
830
                            $sql = 'SELECT SUM(exe_duration) exe_duration
831
                                    FROM '.$tbl_stats_exercices.'
832
                                    WHERE
833
                                        exe_exo_id="'.$row['path'].'" AND
834
                                        exe_user_id="'.$user_id.'" AND
835
                                        orig_lp_id = "'.$lp_id.'" AND
836
                                        orig_lp_item_id = "'.$row['myid'].'" AND
837
                                        c_id = '.$course_id.' AND
838
                                        status <> "incomplete" AND
839
                                        session_id = '.$session_id.'
840
                                     ORDER BY exe_date DESC ';
841
                            $sumScoreResult = Database::query($sql);
842
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
843
                            if (!empty($durationRow['exe_duration'])) {
844
                                $exeDuration = $durationRow['exe_duration'];
845
                                if ($exeDuration != $subtotal_time &&
846
                                    !empty($row_score['iid']) &&
847
                                    !empty($exeDuration)
848
                                ) {
849
                                    $subtotal_time = $exeDuration;
850
                                    // Update c_lp_item_view.total_time
851
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
852
                                                  WHERE iid = ".$row_score['iid'];
853
                                    Database::query($sqlUpdate);
854
                                }
855
                            }
856
                            break;
857
                        default:
858
                            $maxscore = $row['mymaxscore'];
859
                            break;
860
                    }
861
862
                    $time_for_total = $subtotal_time;
863
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
864
                    if (empty($title)) {
865
                        $title = learnpath::rl_get_resource_name(
866
                            $courseInfo['code'],
867
                            $lp_id,
868
                            $row['myid']
869
                        );
870
                    }
871
872
                    $action = null;
873
                    if ($type === 'classic') {
874
                        $action = '<td></td>';
875
                    }
876
877
                    if (in_array($row['item_type'], $chapterTypes)) {
878
                        $title = Security::remove_XSS($title);
879
                        $output .= '<tr class="'.$oddclass.'">
880
                                <td>'.$extend_link.'</td>
881
                                <td colspan="10">
882
                                <h4>'.$title.'</h4>
883
                                </td>
884
                                '.$action.'
885
                            </tr>';
886
                    } else {
887
                        $correct_test_link = '-';
888
                        $showRowspan = false;
889
                        if ($row['item_type'] === 'quiz') {
890
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
891
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
892
                                     WHERE
893
                                        exe_exo_id="'.$row['path'].'" AND
894
                                        exe_user_id="'.$user_id.'" AND
895
                                        orig_lp_id = "'.$lp_id.'" AND
896
                                        orig_lp_item_id = "'.$row['myid'].'" AND
897
                                        c_id = '.$course_id.' AND
898
                                        status <> "incomplete" AND
899
                                        session_id = '.$session_id.'
900
                                     ORDER BY exe_date DESC ';
901
902
                            $resultLastAttempt = Database::query($sql);
903
                            $num = Database::num_rows($resultLastAttempt);
904
                            $showRowspan = false;
905
                            if ($num > 0) {
906
                                $linkId = 'link_'.$my_id;
907
                                if ($extendedAttempt == 1 &&
908
                                    $lp_id == $my_lp_id &&
909
                                    $lp_item_id == $my_id
910
                                ) {
911
                                    $showRowspan = true;
912
                                    $correct_test_link = Display::url(
913
                                        Display::return_icon(
914
                                            'view_less_stats.gif',
915
                                            get_lang('HideAllAttempts')
916
                                        ),
917
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
918
                                        ['id' => $linkId]
919
                                    );
920
                                } else {
921
                                    $correct_test_link = Display::url(
922
                                        Display::return_icon(
923
                                            'view_more_stats.gif',
924
                                            get_lang(
925
                                                'ShowAllAttemptsByExercise'
926
                                            )
927
                                        ),
928
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
929
                                        ['id' => $linkId]
930
                                    );
931
                                }
932
                            }
933
                        }
934
935
                        $title = Security::remove_XSS($title);
936
                        $action = null;
937
                        if ($type === 'classic') {
938
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
939
                        }
940
941
                        if ($lp_id == $my_lp_id && false) {
942
                            $output .= '<tr class ='.$oddclass.'>
943
                                    <td>'.$extend_link.'</td>
944
                                    <td colspan="4">'.$title.'</td>
945
                                    <td colspan="2">&nbsp;</td>
946
                                    <td colspan="2">&nbsp;</td>
947
                                    <td colspan="2">&nbsp;</td>
948
                                    '.$action.'
949
                                </tr>';
950
                            $output .= '</tr>';
951
                        } else {
952
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
953
                                $output .= "<tr class='$oddclass'>";
954
                            } else {
955
                                $output .= "<tr class='$oddclass'>";
956
                            }
957
958
                            $scoreItem = null;
959
                            if ($row['item_type'] === 'quiz') {
960
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
961
                                    $scoreItem .= Display::return_icon(
962
                                        'invisible.gif',
963
                                        get_lang('ResultsHiddenByExerciseSetting')
964
                                    );
965
                                } else {
966
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
967
                                }
968
                            } else {
969
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
970
                            }
971
972
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
973
                            if ($hideTime) {
974
                                $timeRow = '';
975
                            }
976
977
                            $output .= '
978
                                <td>'.$extend_link.'</td>
979
                                <td colspan="4">'.$title.'</td>
980
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
981
                                <td colspan="2">'.$scoreItem.'</td>
982
                                '.$timeRow.'
983
                                '.$action.'
984
                             ';
985
                            $output .= '</tr>';
986
                        }
987
988
                        if (!empty($export_csv)) {
989
                            $temp = [];
990
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
991
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
992
                            if ($row['item_type'] === 'quiz') {
993
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
994
                                    $temp[] = '/';
995
                                } else {
996
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
997
                                }
998
                            } else {
999
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1000
                            }
1001
1002
                            if ($hideTime === false) {
1003
                                $temp[] = $time;
1004
                            }
1005
                            $csv_content[] = $temp;
1006
                        }
1007
                    }
1008
1009
                    $counter++;
1010
                    $action = null;
1011
                    if ($type === 'classic') {
1012
                        $action = '<td></td>';
1013
                    }
1014
1015
                    if ($extend_this_attempt || $extend_all) {
1016
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1017
                        foreach ($list1 as $id => $interaction) {
1018
                            $oddclass = 'row_even';
1019
                            if (($counter % 2) == 0) {
1020
                                $oddclass = 'row_odd';
1021
                            }
1022
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1023
                            if ($hideTime) {
1024
                                $timeRow = '';
1025
                            }
1026
1027
                            $output .= '<tr class="'.$oddclass.'">
1028
                                    <td></td>
1029
                                    <td></td>
1030
                                    <td></td>
1031
                                    <td>'.$interaction['order_id'].'</td>
1032
                                    <td>'.$interaction['id'].'</td>
1033
                                    <td colspan="2">'.$interaction['type'].'</td>
1034
                                    <td>'.urldecode($interaction['student_response']).'</td>
1035
                                    <td>'.$interaction['result'].'</td>
1036
                                    <td>'.$interaction['latency'].'</td>
1037
                                    '.$timeRow.'
1038
                                    '.$action.'
1039
                               </tr>';
1040
                            $counter++;
1041
                        }
1042
1043
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1044
                        foreach ($list2 as $id => $interaction) {
1045
                            $oddclass = 'row_even';
1046
                            if (($counter % 2) == 0) {
1047
                                $oddclass = 'row_odd';
1048
                            }
1049
                            $output .= '<tr class="'.$oddclass.'">
1050
                                    <td></td>
1051
                                    <td></td>
1052
                                    <td></td>
1053
                                    <td>'.$interaction['order_id'].'</td>
1054
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1055
                                    <td colspan="2">'.$interaction['status'].'</td>
1056
                                    <td>'.$interaction['score_raw'].'</td>
1057
                                    <td>'.$interaction['score_max'].'</td>
1058
                                    <td>'.$interaction['score_min'].'</td>
1059
                                    '.$action.'
1060
                               </tr>';
1061
                            $counter++;
1062
                        }
1063
                    }
1064
1065
                    // Attempts listing by exercise.
1066
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1067
                        // Get attempts of a exercise.
1068
                        if (!empty($lp_id) &&
1069
                            !empty($lp_item_id) &&
1070
                            'quiz' === $row['item_type']
1071
                        ) {
1072
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1073
                                    WHERE
1074
                                        c_id = $course_id AND
1075
                                        iid = '$lp_item_id' AND
1076
                                        lp_id = '$lp_id'";
1077
                            $res_path = Database::query($sql);
1078
                            $row_path = Database::fetch_array($res_path);
1079
1080
                            if (Database::num_rows($res_path) > 0) {
1081
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1082
                                        WHERE
1083
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1084
                                            status <> "incomplete" AND
1085
                                            exe_user_id="'.$user_id.'" AND
1086
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1087
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1088
                                            c_id = '.$course_id.'  AND
1089
                                            session_id = '.$session_id.'
1090
                                        ORDER BY exe_date';
1091
                                $res_attempts = Database::query($sql);
1092
                                $num_attempts = Database::num_rows($res_attempts);
1093
                                if ($num_attempts > 0) {
1094
                                    $n = 1;
1095
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1096
                                        $my_score = $row_attempts['exe_result'];
1097
                                        $my_maxscore = $row_attempts['exe_weighting'];
1098
                                        $my_exe_id = $row_attempts['exe_id'];
1099
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1100
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1101
                                        $time_attemp = ' - ';
1102
                                        if ($mktime_start_date && $mktime_exe_date) {
1103
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1104
                                        }
1105
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1106
                                            $view_score = Display::return_icon(
1107
                                                'invisible.png',
1108
                                                get_lang(
1109
                                                    'ResultsHiddenByExerciseSetting'
1110
                                                )
1111
                                            );
1112
                                        } else {
1113
                                            // Show only float when need it
1114
                                            if ($my_score == 0) {
1115
                                                $view_score = ExerciseLib::show_score(
1116
                                                    0,
1117
                                                    $my_maxscore,
1118
                                                    false
1119
                                                );
1120
                                            } else {
1121
                                                if ($my_maxscore == 0) {
1122
                                                    $view_score = $my_score;
1123
                                                } else {
1124
                                                    $view_score = ExerciseLib::show_score(
1125
                                                        $my_score,
1126
                                                        $my_maxscore,
1127
                                                        false
1128
                                                    );
1129
                                                }
1130
                                            }
1131
                                        }
1132
                                        $my_lesson_status = $row_attempts['status'];
1133
                                        if ($my_lesson_status == '') {
1134
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1135
                                        } elseif ($my_lesson_status == 'incomplete') {
1136
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1137
                                        }
1138
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1139
                                        if ($hideTime) {
1140
                                            $timeRow = '';
1141
                                        }
1142
1143
                                        $output .= '<tr class="'.$oddclass.'" >
1144
                                        <td></td>
1145
                                        <td>'.$extend_attempt_link.'</td>
1146
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1147
                                        <td colspan="2">'.$my_lesson_status.'</td>
1148
                                        <td colspan="2">'.$view_score.'</td>
1149
                                        '.$timeRow;
1150
1151
                                        if ($action == 'classic') {
1152
                                            if ($origin != 'tracking') {
1153
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1154
                                                    $output .= '<td>
1155
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1156
                                                            </td>';
1157
                                                } else {
1158
                                                    $output .= '<td>
1159
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1160
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1161
                                                            </a></td>';
1162
                                                }
1163
                                            } else {
1164
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1165
                                                    $output .= '<td>
1166
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
1167
                                                } else {
1168
                                                    $output .= '<td>
1169
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1170
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
1171
                                                }
1172
                                            }
1173
                                        }
1174
                                        $output .= '</tr>';
1175
                                        $n++;
1176
                                    }
1177
                                }
1178
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1179
                            }
1180
                        }
1181
                    }
1182
                }
1183
1184
                $total_time += $time_for_total;
1185
                // QUIZZ IN LP
1186
                $a_my_id = [];
1187
                if (!empty($my_lp_id)) {
1188
                    $a_my_id[] = $my_lp_id;
1189
                }
1190
            }
1191
        }
1192
1193
        // NOT Extend all "left green cross"
1194
        if (!empty($a_my_id)) {
1195
            if ($extendedAttempt) {
1196
                // "Right green cross" extended
1197
                $total_score = self::get_avg_student_score(
1198
                    $user_id,
1199
                    $course_id,
1200
                    $a_my_id,
1201
                    $session_id,
1202
                    false,
1203
                    false
1204
                );
1205
            } else {
1206
                // "Left green cross" extended
1207
                $total_score = self::get_avg_student_score(
1208
                    $user_id,
1209
                    $course_id,
1210
                    $a_my_id,
1211
                    $session_id,
1212
                    false,
1213
                    true
1214
                );
1215
            }
1216
        } else {
1217
            // Extend all "left green cross"
1218
            $total_score = self::get_avg_student_score(
1219
                $user_id,
1220
                $course_id,
1221
                [$lp_id],
1222
                $session_id,
1223
                false,
1224
                false
1225
            );
1226
        }
1227
1228
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1229
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1230
1231
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1232
            $final_score = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
1233
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
1234
        } else {
1235
            if (is_numeric($total_score)) {
1236
                $final_score = $total_score.'%';
1237
            } else {
1238
                $final_score = $total_score;
1239
            }
1240
            $finalScoreToCsv = $final_score;
1241
        }
1242
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1243
1244
        $oddclass = 'row_even';
1245
        if (($counter % 2) == 0) {
1246
            $oddclass = 'row_odd';
1247
        }
1248
1249
        $action = null;
1250
        if ('classic' === $type) {
1251
            $action = '<td></td>';
1252
        }
1253
1254
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1255
        if ($hideTime) {
1256
            $timeTotal = '';
1257
        }
1258
1259
        $output .= '<tr class="'.$oddclass.'">
1260
                <td></td>
1261
                <td colspan="4">
1262
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
1263
                </td>
1264
                <td colspan="2">'.$progress.'%</td>
1265
                <td colspan="2">'.$final_score.'</td>
1266
                '.$timeTotal.'
1267
                '.$action.'
1268
           </tr>';
1269
1270
        $output .= '
1271
                    </tbody>
1272
                </table>
1273
            </div>
1274
        ';
1275
1276
        if (!empty($export_csv)) {
1277
            $temp = [
1278
                '',
1279
                '',
1280
                '',
1281
                '',
1282
            ];
1283
            $csv_content[] = $temp;
1284
            $temp = [
1285
                get_lang('AccomplishedStepsTotal'),
1286
                '',
1287
                $finalScoreToCsv,
1288
            ];
1289
1290
            if ($hideTime === false) {
1291
                $temp[] = $total_time;
1292
            }
1293
1294
            $csv_content[] = $temp;
1295
            ob_end_clean();
1296
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1297
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
1298
        }
1299
1300
        return $output;
1301
    }
1302
1303
    /**
1304
     * @param int  $userId
1305
     * @param bool $getCount
1306
     *
1307
     * @return array
1308
     */
1309
    public static function getStats($userId, $getCount = false)
1310
    {
1311
        $courses = [];
1312
        $assignedCourses = [];
1313
        $drhCount = 0;
1314
        $teachersCount = 0;
1315
        $studentsCount = 0;
1316
        $studentBossCount = 0;
1317
        $courseCount = 0;
1318
        $sessionCount = 0;
1319
        $assignedCourseCount = 0;
1320
1321
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1322
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1323
                'drh_all',
1324
                $userId,
1325
                false,
1326
                null,
1327
                null,
1328
                null,
1329
                null,
1330
                null,
1331
                null,
1332
                null,
1333
                [],
1334
                [],
1335
                STUDENT
1336
            );
1337
1338
            $students = [];
1339
            if (is_array($studentList)) {
1340
                foreach ($studentList as $studentData) {
1341
                    $students[] = $studentData['user_id'];
1342
                }
1343
            }
1344
1345
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1346
                'drh_all',
1347
                $userId,
1348
                $getCount,
1349
                null,
1350
                null,
1351
                null,
1352
                null,
1353
                null,
1354
                null,
1355
                null,
1356
                [],
1357
                [],
1358
                STUDENT_BOSS
1359
            );
1360
1361
            if ($getCount) {
1362
                $studentBossCount = $studentBossesList;
1363
            } else {
1364
                $studentBosses = [];
1365
                if (is_array($studentBossesList)) {
1366
                    foreach ($studentBossesList as $studentBossData) {
1367
                        $studentBosses[] = $studentBossData['user_id'];
1368
                    }
1369
                }
1370
            }
1371
1372
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1373
                'drh_all',
1374
                $userId,
1375
                $getCount,
1376
                null,
1377
                null,
1378
                null,
1379
                null,
1380
                null,
1381
                null,
1382
                null,
1383
                [],
1384
                [],
1385
                COURSEMANAGER
1386
            );
1387
1388
            if ($getCount) {
1389
                $teachersCount = $teacherList;
1390
            } else {
1391
                $teachers = [];
1392
                foreach ($teacherList as $teacherData) {
1393
                    $teachers[] = $teacherData['user_id'];
1394
                }
1395
            }
1396
1397
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1398
                'drh_all',
1399
                $userId,
1400
                $getCount,
1401
                null,
1402
                null,
1403
                null,
1404
                null,
1405
                null,
1406
                null,
1407
                null,
1408
                [],
1409
                [],
1410
                DRH
1411
            );
1412
1413
            if ($getCount) {
1414
                $drhCount = $humanResources;
1415
            } else {
1416
                $humanResourcesList = [];
1417
                if (is_array($humanResources)) {
1418
                    foreach ($humanResources as $item) {
1419
                        $humanResourcesList[] = $item['user_id'];
1420
                    }
1421
                }
1422
            }
1423
1424
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1425
                $userId,
1426
                null,
1427
                null,
1428
                null,
1429
                null,
1430
                null,
1431
                $getCount
1432
            );
1433
1434
            if ($getCount) {
1435
                $courseCount = $platformCourses;
1436
            } else {
1437
                foreach ($platformCourses as $course) {
1438
                    $courses[$course['code']] = $course['code'];
1439
                }
1440
            }
1441
1442
            $sessions = SessionManager::get_sessions_followed_by_drh(
1443
                $userId,
1444
                null,
1445
                null,
1446
                false
1447
            );
1448
        } else {
1449
            $studentList = UserManager::getUsersFollowedByUser(
1450
                $userId,
1451
                STUDENT,
1452
                false,
1453
                false,
1454
                false,
1455
                null,
1456
                null,
1457
                null,
1458
                null,
1459
                null,
1460
                null,
1461
                COURSEMANAGER
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
            );
1485
1486
            if ($getCount) {
1487
                $studentBossCount = $studentBossesList;
1488
            } else {
1489
                $studentBosses = [];
1490
                if (is_array($studentBossesList)) {
1491
                    foreach ($studentBossesList as $studentBossData) {
1492
                        $studentBosses[] = $studentBossData['user_id'];
1493
                    }
1494
                }
1495
            }
1496
1497
            $teacherList = UserManager::getUsersFollowedByUser(
1498
                $userId,
1499
                COURSEMANAGER,
1500
                false,
1501
                false,
1502
                $getCount,
1503
                null,
1504
                null,
1505
                null,
1506
                null,
1507
                null,
1508
                null,
1509
                COURSEMANAGER
1510
            );
1511
1512
            if ($getCount) {
1513
                $teachersCount = $teacherList;
1514
            } else {
1515
                $teachers = [];
1516
                foreach ($teacherList as $teacherData) {
1517
                    $teachers[] = $teacherData['user_id'];
1518
                }
1519
            }
1520
1521
            $humanResources = UserManager::getUsersFollowedByUser(
1522
                $userId,
1523
                DRH,
1524
                false,
1525
                false,
1526
                $getCount,
1527
                null,
1528
                null,
1529
                null,
1530
                null,
1531
                null,
1532
                null,
1533
                COURSEMANAGER
1534
            );
1535
1536
            if ($getCount) {
1537
                $drhCount = $humanResources;
1538
            } else {
1539
                $humanResourcesList = [];
1540
                foreach ($humanResources as $item) {
1541
                    $humanResourcesList[] = $item['user_id'];
1542
                }
1543
            }
1544
1545
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1546
                $userId,
1547
                COURSEMANAGER,
1548
                null,
1549
                null,
1550
                null,
1551
                null,
1552
                $getCount,
1553
                null,
1554
                null,
1555
                true
1556
            );
1557
1558
            if ($getCount) {
1559
                $assignedCourseCount = $platformCourses;
1560
            } else {
1561
                foreach ($platformCourses as $course) {
1562
                    $assignedCourses[$course['code']] = $course['code'];
1563
                }
1564
            }
1565
1566
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1567
                $userId,
1568
                COURSEMANAGER,
1569
                null,
1570
                null,
1571
                null,
1572
                null,
1573
                $getCount
1574
            );
1575
1576
            if ($getCount) {
1577
                $courseCount = $platformCourses;
1578
            } else {
1579
                foreach ($platformCourses as $course) {
1580
                    $courses[$course['code']] = $course['code'];
1581
                }
1582
            }
1583
1584
            $sessions = SessionManager::getSessionsFollowedByUser(
1585
                $userId,
1586
                COURSEMANAGER,
1587
                null,
1588
                null,
1589
                false
1590
            );
1591
        }
1592
1593
        if ($getCount) {
1594
            return [
1595
                'drh' => $drhCount,
1596
                'teachers' => $teachersCount,
1597
                'student_count' => count($students),
1598
                'student_list' => $students,
1599
                'student_bosses' => $studentBossCount,
1600
                'courses' => $courseCount,
1601
                'session_count' => count($sessions),
1602
                'session_list' => $sessions,
1603
                'assigned_courses' => $assignedCourseCount,
1604
            ];
1605
        }
1606
1607
        return [
1608
            'drh' => $humanResourcesList,
1609
            'teachers' => $teachers,
1610
            'student_list' => $students,
1611
            'student_bosses' => $studentBosses,
1612
            'courses' => $courses,
1613
            'sessions' => $sessions,
1614
            'assigned_courses' => $assignedCourses,
1615
        ];
1616
    }
1617
1618
    /**
1619
     * Calculates the time spent on the platform by a user.
1620
     *
1621
     * @param int|array $userId
1622
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
1623
     * @param string    $start_date       start date date('Y-m-d H:i:s')
1624
     * @param string    $end_date         end date date('Y-m-d H:i:s')
1625
     * @param bool      $returnAllRecords
1626
     *
1627
     * @return int
1628
     */
1629
    public static function get_time_spent_on_the_platform(
1630
        $userId,
1631
        $timeFilter = 'last_7_days',
1632
        $start_date = null,
1633
        $end_date = null,
1634
        $returnAllRecords = false
1635
    ) {
1636
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1637
        $condition_time = '';
1638
1639
        if (is_array($userId)) {
1640
            $userList = array_map('intval', $userId);
1641
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1642
        } else {
1643
            $userId = (int) $userId;
1644
            $userCondition = " login_user_id = $userId ";
1645
        }
1646
1647
        $url_condition = null;
1648
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1649
        $url_table = null;
1650
        if (api_is_multiple_url_enabled()) {
1651
            $access_url_id = api_get_current_access_url_id();
1652
            $url_table = ", $tbl_url_rel_user as url_users";
1653
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1654
        }
1655
1656
        if (empty($timeFilter)) {
1657
            $timeFilter = 'last_week';
1658
        }
1659
1660
        $today = new DateTime('now', new DateTimeZone('UTC'));
1661
1662
        switch ($timeFilter) {
1663
            case 'last_7_days':
1664
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1665
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1666
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1667
                break;
1668
            case 'last_30_days':
1669
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1670
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1671
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1672
                break;
1673
            case 'wide':
1674
                if (!empty($start_date) && !empty($end_date)) {
1675
                    $start_date = Database::escape_string($start_date);
1676
                    $end_date = Database::escape_string($end_date);
1677
                    $condition_time = ' AND (
1678
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
1679
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
1680
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
1681
                    ) ';
1682
                }
1683
                break;
1684
            case 'custom':
1685
                if (!empty($start_date) && !empty($end_date)) {
1686
                    $start_date = Database::escape_string($start_date);
1687
                    $end_date = Database::escape_string($end_date);
1688
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1689
                }
1690
                break;
1691
        }
1692
1693
        if ($returnAllRecords) {
1694
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
1695
                    FROM $tbl_track_login u $url_table
1696
                    WHERE $userCondition $condition_time $url_condition
1697
                    ORDER BY login_date";
1698
            $rs = Database::query($sql);
1699
1700
            return Database::store_result($rs, 'ASSOC');
1701
        }
1702
1703
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1704
    	        FROM $tbl_track_login u $url_table
1705
                WHERE $userCondition $condition_time $url_condition";
1706
        $rs = Database::query($sql);
1707
        $row = Database::fetch_array($rs, 'ASSOC');
1708
        $diff = $row['diff'];
1709
1710
        if ($diff >= 0) {
1711
            return $diff;
1712
        }
1713
1714
        return -1;
1715
    }
1716
1717
    /**
1718
     * @param string $startDate
1719
     * @param string $endDate
1720
     *
1721
     * @return int
1722
     */
1723
    public static function getTotalTimeSpentOnThePlatform(
1724
        $startDate = '',
1725
        $endDate = ''
1726
    ) {
1727
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1728
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1729
1730
        $url_table = null;
1731
        $url_condition = null;
1732
        if (api_is_multiple_url_enabled()) {
1733
            $access_url_id = api_get_current_access_url_id();
1734
            $url_table = ", ".$tbl_url_rel_user." as url_users";
1735
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1736
        }
1737
1738
        if (!empty($startDate) && !empty($endDate)) {
1739
            $startDate = Database::escape_string($startDate);
1740
            $endDate = Database::escape_string($endDate);
1741
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
1742
        }
1743
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1744
    	        FROM $tbl_track_login u $url_table
1745
                WHERE $condition_time $url_condition";
1746
        $rs = Database::query($sql);
1747
        $row = Database::fetch_array($rs, 'ASSOC');
1748
        $diff = $row['diff'];
1749
1750
        if ($diff >= 0) {
1751
            return $diff;
1752
        }
1753
1754
        return -1;
1755
    }
1756
1757
    /**
1758
     * Checks if the "lp_minimum_time" feature is available for the course.
1759
     *
1760
     * @param int $sessionId
1761
     * @param int $courseId
1762
     *
1763
     * @return bool
1764
     */
1765
    public static function minimumTimeAvailable($sessionId, $courseId)
1766
    {
1767
        if (!api_get_configuration_value('lp_minimum_time')) {
1768
            return false;
1769
        }
1770
1771
        if (!empty($sessionId)) {
1772
            $extraFieldValue = new ExtraFieldValue('session');
1773
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
1774
1775
            if ($value && isset($value['value']) && 1 == $value['value']) {
1776
                return true;
1777
            }
1778
        } else {
1779
            if ($courseId) {
1780
                $extraFieldValue = new ExtraFieldValue('course');
1781
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
1782
                if ($value && isset($value['value']) && 1 == $value['value']) {
1783
                    return true;
1784
                }
1785
            }
1786
        }
1787
1788
        return false;
1789
    }
1790
1791
    /**
1792
     * Calculates the time spent on the course.
1793
     *
1794
     * @param int $user_id
1795
     * @param int $courseId
1796
     * @param int $session_id
1797
     *
1798
     * @return int Time in seconds
1799
     */
1800
    public static function get_time_spent_on_the_course(
1801
        $user_id,
1802
        $courseId,
1803
        $session_id = 0
1804
    ) {
1805
        $courseId = (int) $courseId;
1806
1807
        if (empty($courseId) || empty($user_id)) {
1808
            return 0;
1809
        }
1810
1811
        if (self::minimumTimeAvailable($session_id, $courseId)) {
1812
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
1813
1814
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
1815
        }
1816
1817
        $conditionUser = '';
1818
        $session_id = (int) $session_id;
1819
        if (is_array($user_id)) {
1820
            $user_id = array_map('intval', $user_id);
1821
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
1822
        } else {
1823
            if (!empty($user_id)) {
1824
                $user_id = (int) $user_id;
1825
                $conditionUser = " AND user_id = $user_id ";
1826
            }
1827
        }
1828
1829
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1830
        $sql = "SELECT
1831
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1832
                FROM $table
1833
                WHERE
1834
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
1835
                    c_id = '$courseId' ";
1836
1837
        if (-1 != $session_id) {
1838
            $sql .= "AND session_id = '$session_id' ";
1839
        }
1840
1841
        $sql .= $conditionUser;
1842
1843
        $rs = Database::query($sql);
1844
        $row = Database::fetch_array($rs);
1845
1846
        return $row['nb_seconds'];
1847
    }
1848
1849
    /**
1850
     * Get first connection date for a student.
1851
     *
1852
     * @param int $student_id
1853
     *
1854
     * @return string|bool Date format long without day or false if there are no connections
1855
     */
1856
    public static function get_first_connection_date($student_id)
1857
    {
1858
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1859
        $sql = 'SELECT login_date
1860
                FROM '.$table.'
1861
                WHERE login_user_id = '.intval($student_id).'
1862
                ORDER BY login_date ASC
1863
                LIMIT 0,1';
1864
1865
        $rs = Database::query($sql);
1866
        if (Database::num_rows($rs) > 0) {
1867
            if ($first_login_date = Database::result($rs, 0, 0)) {
1868
                return api_convert_and_format_date(
1869
                    $first_login_date,
1870
                    DATE_FORMAT_SHORT
1871
                );
1872
            }
1873
        }
1874
1875
        return false;
1876
    }
1877
1878
    /**
1879
     * Get las connection date for a student.
1880
     *
1881
     * @param int  $student_id
1882
     * @param bool $warning_message  Show a warning message (optional)
1883
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1884
     *
1885
     * @return string|int|bool Date format long without day, false if there are no connections or
1886
     *                         timestamp if parameter $return_timestamp is true
1887
     */
1888
    public static function get_last_connection_date(
1889
        $student_id,
1890
        $warning_message = false,
1891
        $return_timestamp = false
1892
    ) {
1893
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1894
        $sql = 'SELECT login_date
1895
                FROM '.$table.'
1896
                WHERE login_user_id = '.intval($student_id).'
1897
                ORDER BY login_date
1898
                DESC LIMIT 0,1';
1899
1900
        $rs = Database::query($sql);
1901
        if (Database::num_rows($rs) > 0) {
1902
            if ($last_login_date = Database::result($rs, 0, 0)) {
1903
                $last_login_date = api_get_local_time($last_login_date);
1904
                if ($return_timestamp) {
1905
                    return api_strtotime($last_login_date, 'UTC');
1906
                } else {
1907
                    if (!$warning_message) {
1908
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1909
                    } else {
1910
                        $timestamp = api_strtotime($last_login_date, 'UTC');
1911
                        $currentTimestamp = time();
1912
1913
                        //If the last connection is > than 7 days, the text is red
1914
                        //345600 = 7 days in seconds
1915
                        if ($currentTimestamp - $timestamp > 604800) {
1916
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</span>';
1917
                        } else {
1918
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1919
                        }
1920
                    }
1921
                }
1922
            }
1923
        }
1924
1925
        return false;
1926
    }
1927
1928
    /**
1929
     * Get first user's connection date on the course.
1930
     *
1931
     * @param int User id
1932
     * @param int $courseId
1933
     * @param int Session id (optional, default=0)
1934
     * @param bool $convert_date
1935
     *
1936
     * @return string|bool Date with format long without day or false if there is no date
1937
     */
1938
    public static function get_first_connection_date_on_the_course(
1939
        $student_id,
1940
        $courseId,
1941
        $session_id = 0,
1942
        $convert_date = true
1943
    ) {
1944
        $student_id = (int) $student_id;
1945
        $courseId = (int) $courseId;
1946
        $session_id = (int) $session_id;
1947
1948
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1949
        $sql = 'SELECT login_course_date
1950
                FROM '.$table.'
1951
                WHERE
1952
                    user_id = '.$student_id.' AND
1953
                    c_id = '.$courseId.' AND
1954
                    session_id = '.$session_id.'
1955
                ORDER BY login_course_date ASC
1956
                LIMIT 0,1';
1957
        $rs = Database::query($sql);
1958
        if (Database::num_rows($rs) > 0) {
1959
            if ($first_login_date = Database::result($rs, 0, 0)) {
1960
                if (empty($first_login_date)) {
1961
                    return false;
1962
                }
1963
1964
                if ($convert_date) {
1965
                    return api_convert_and_format_date(
1966
                        $first_login_date,
1967
                        DATE_FORMAT_SHORT
1968
                    );
1969
                }
1970
1971
                return $first_login_date;
1972
            }
1973
        }
1974
1975
        return false;
1976
    }
1977
1978
    /**
1979
     * Get last user's connection date on the course.
1980
     *
1981
     * @param int         User id
1982
     * @param array $courseInfo real_id and code are used
1983
     * @param int            Session id (optional, default=0)
1984
     * @param bool $convert_date
1985
     *
1986
     * @return string|bool Date with format long without day or false if there is no date
1987
     */
1988
    public static function get_last_connection_date_on_the_course(
1989
        $student_id,
1990
        $courseInfo,
1991
        $session_id = 0,
1992
        $convert_date = true
1993
    ) {
1994
        // protect data
1995
        $student_id = (int) $student_id;
1996
        $session_id = (int) $session_id;
1997
1998
        if (empty($courseInfo) || empty($student_id)) {
1999
            return false;
2000
        }
2001
2002
        $courseId = $courseInfo['real_id'];
2003
2004
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2005
2006
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2007
            // Show the last date on which the user acceed the session when it was active
2008
            $where_condition = '';
2009
            $userInfo = api_get_user_info($student_id);
2010
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
2011
                // fin de acceso a la sesión
2012
                $sessionInfo = SessionManager::fetch($session_id);
2013
                $last_access = $sessionInfo['access_end_date'];
2014
                if (!empty($last_access)) {
2015
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
2016
                }
2017
            }
2018
            $sql = "SELECT logout_course_date
2019
                    FROM $table
2020
                    WHERE   user_id = $student_id AND
2021
                            c_id = $courseId AND
2022
                            session_id = $session_id $where_condition
2023
                    ORDER BY logout_course_date DESC
2024
                    LIMIT 0,1";
2025
2026
            $rs = Database::query($sql);
2027
            if (Database::num_rows($rs) > 0) {
2028
                if ($last_login_date = Database::result($rs, 0, 0)) {
2029
                    if (empty($last_login_date)) {
2030
                        return false;
2031
                    }
2032
                    if ($convert_date) {
2033
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2034
                    }
2035
2036
                    return $last_login_date;
2037
                }
2038
            }
2039
        } else {
2040
            $sql = "SELECT logout_course_date
2041
                    FROM $table
2042
                    WHERE   user_id = $student_id AND
2043
                            c_id = $courseId AND
2044
                            session_id = $session_id
2045
                    ORDER BY logout_course_date DESC
2046
                    LIMIT 0,1";
2047
2048
            $rs = Database::query($sql);
2049
            if (Database::num_rows($rs) > 0) {
2050
                if ($last_login_date = Database::result($rs, 0, 0)) {
2051
                    if (empty($last_login_date)) {
2052
                        return false;
2053
                    }
2054
                    //see #5736
2055
                    $last_login_date_timestamp = api_strtotime($last_login_date);
2056
                    $now = time();
2057
                    //If the last connection is > than 7 days, the text is red
2058
                    //345600 = 7 days in seconds
2059
                    if ($now - $last_login_date_timestamp > 604800) {
2060
                        if ($convert_date) {
2061
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2062
                            $icon = null;
2063
                            if (api_is_allowed_to_edit()) {
2064
                                $url = api_get_path(WEB_CODE_PATH).
2065
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
2066
                                $icon = '<a href="'.$url.'" title="'.get_lang('RemindInactiveUser').'">
2067
                                  '.Display::return_icon('messagebox_warning.gif').'
2068
                                 </a>';
2069
                            }
2070
2071
                            return $icon.Display::label($last_login_date, 'warning');
2072
                        }
2073
2074
                        return $last_login_date;
2075
                    } else {
2076
                        if ($convert_date) {
2077
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2078
                        }
2079
2080
                        return $last_login_date;
2081
                    }
2082
                }
2083
            }
2084
        }
2085
2086
        return false;
2087
    }
2088
2089
    public static function getLastConnectionInAnyCourse($studentId)
2090
    {
2091
        $studentId = (int) $studentId;
2092
2093
        if (empty($studentId)) {
2094
            return false;
2095
        }
2096
2097
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2098
        $sql = "SELECT logout_course_date
2099
                FROM $table
2100
                WHERE user_id = $studentId
2101
                ORDER BY logout_course_date DESC
2102
                LIMIT 1";
2103
        $result = Database::query($sql);
2104
        if (Database::num_rows($result)) {
2105
            $row = Database::fetch_array($result);
2106
2107
            return $row['logout_course_date'];
2108
        }
2109
2110
        return false;
2111
    }
2112
2113
    /**
2114
     * Get last course access by course/session.
2115
     */
2116
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
2117
    {
2118
        $courseId = (int) $courseId;
2119
        $sessionId = (int) $sessionId;
2120
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2121
2122
        $sql = "SELECT logout_course_date
2123
                FROM $table
2124
                WHERE
2125
                        c_id = $courseId AND
2126
                        session_id = $sessionId
2127
                ORDER BY logout_course_date DESC
2128
                LIMIT 0,1";
2129
2130
        $result = Database::query($sql);
2131
        if (Database::num_rows($result)) {
2132
            $row = Database::fetch_array($result);
2133
            if ($row) {
2134
                return $row['logout_course_date'];
2135
            }
2136
        }
2137
2138
        return '';
2139
    }
2140
2141
    /**
2142
     * Get count of the connections to the course during a specified period.
2143
     *
2144
     * @param int $courseId
2145
     * @param   int     Session id (optional)
2146
     * @param   int     Datetime from which to collect data (defaults to 0)
2147
     * @param   int     Datetime to which to collect data (defaults to now)
2148
     *
2149
     * @return int count connections
2150
     */
2151
    public static function get_course_connections_count(
2152
        $courseId,
2153
        $session_id = 0,
2154
        $start = 0,
2155
        $stop = null
2156
    ) {
2157
        if ($start < 0) {
2158
            $start = 0;
2159
        }
2160
        if (!isset($stop) || $stop < 0) {
2161
            $stop = api_get_utc_datetime();
2162
        }
2163
2164
        // Given we're storing in cache, round the start and end times
2165
        // to the lower minute
2166
        $roundedStart = substr($start, 0, -2).'00';
2167
        $roundedStop = substr($stop, 0, -2).'00';
2168
        $roundedStart = Database::escape_string($roundedStart);
2169
        $roundedStop = Database::escape_string($roundedStop);
2170
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
2171
        $courseId = (int) $courseId;
2172
        $session_id = (int) $session_id;
2173
        $count = 0;
2174
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2175
        $sql = "SELECT count(*) as count_connections
2176
                FROM $table
2177
                WHERE
2178
                    c_id = $courseId AND
2179
                    session_id = $session_id
2180
                    $month_filter";
2181
2182
        //This query can be very slow (several seconds on an indexed table
2183
        // with 14M rows). As such, we'll try to use APCu if it is
2184
        // available to store the resulting value for a few seconds
2185
        $cacheAvailable = api_get_configuration_value('apc');
2186
        if ($cacheAvailable === true) {
2187
            $apc = apcu_cache_info(true);
2188
            $apc_end = $apc['start_time'] + $apc['ttl'];
2189
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
2190
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
2191
                apcu_fetch($apc_var) > 0
2192
            ) {
2193
                $count = apcu_fetch($apc_var);
2194
            } else {
2195
                $rs = Database::query($sql);
2196
                if (Database::num_rows($rs) > 0) {
2197
                    $row = Database::fetch_object($rs);
2198
                    $count = $row->count_connections;
2199
                }
2200
                apcu_clear_cache();
2201
                apcu_store($apc_var, $count, 60);
2202
            }
2203
        } else {
2204
            $rs = Database::query($sql);
2205
            if (Database::num_rows($rs) > 0) {
2206
                $row = Database::fetch_object($rs);
2207
                $count = $row->count_connections;
2208
            }
2209
        }
2210
2211
        return $count;
2212
    }
2213
2214
    /**
2215
     * Get count courses per student.
2216
     *
2217
     * @param int  $user_id          Student id
2218
     * @param bool $include_sessions Include sessions (optional)
2219
     *
2220
     * @return int count courses
2221
     */
2222
    public static function count_course_per_student($user_id, $include_sessions = true)
2223
    {
2224
        $user_id = (int) $user_id;
2225
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2226
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2227
2228
        $sql = 'SELECT DISTINCT c_id
2229
                FROM '.$tbl_course_rel_user.'
2230
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
2231
        $rs = Database::query($sql);
2232
        $nb_courses = Database::num_rows($rs);
2233
2234
        if ($include_sessions) {
2235
            $sql = 'SELECT DISTINCT c_id
2236
                    FROM '.$tbl_session_course_rel_user.'
2237
                    WHERE user_id = '.$user_id;
2238
            $rs = Database::query($sql);
2239
            $nb_courses += Database::num_rows($rs);
2240
        }
2241
2242
        return $nb_courses;
2243
    }
2244
2245
    /**
2246
     * Gets the score average from all tests in a course by student.
2247
     *
2248
     * @param $student_id
2249
     * @param $course_code
2250
     * @param int  $exercise_id
2251
     * @param null $session_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $session_id is correct as it would always require null to be passed?
Loading history...
2252
     * @param int  $active_filter 2 for consider all tests
2253
     *                            1 for active <> -1
2254
     *                            0 for active <> 0
2255
     * @param int  $into_lp       1 for all exercises
2256
     *                            0 for without LP
2257
     * @param mixed id
2258
     * @param string code
2259
     * @param int id (optional), filtered by exercise
2260
     * @param int id (optional), if param $session_id is null
2261
     *                                                it'll return results including sessions, 0 = session is not filtered
2262
     *
2263
     * @return string value (number %) Which represents a round integer about the score average
2264
     */
2265
    public static function get_avg_student_exercise_score(
2266
        $student_id,
2267
        $course_code,
2268
        $exercise_id = 0,
2269
        $session_id = null,
2270
        $active_filter = 1,
2271
        $into_lp = 0
2272
    ) {
2273
        $course_code = Database::escape_string($course_code);
2274
        $course_info = api_get_course_info($course_code);
2275
        if (!empty($course_info)) {
2276
            // table definition
2277
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2278
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2279
2280
            // Compose a filter based on optional exercise given
2281
            $condition_quiz = "";
2282
            if (!empty($exercise_id)) {
2283
                $exercise_id = intval($exercise_id);
2284
                $condition_quiz = " AND id = $exercise_id ";
2285
            }
2286
2287
            // Compose a filter based on optional session id given
2288
            $condition_session = '';
2289
            if (isset($session_id)) {
2290
                $session_id = intval($session_id);
2291
                $condition_session = " AND session_id = $session_id ";
2292
            }
2293
            if ($active_filter == 1) {
2294
                $condition_active = 'AND active <> -1';
2295
            } elseif ($active_filter == 0) {
2296
                $condition_active = 'AND active <> 0';
2297
            } else {
2298
                $condition_active = '';
2299
            }
2300
            $condition_into_lp = '';
2301
            $select_lp_id = '';
2302
            if ($into_lp == 0) {
2303
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2304
            } else {
2305
                $select_lp_id = ', orig_lp_id as lp_id ';
2306
            }
2307
2308
            $sql = "SELECT count(id)
2309
    		        FROM $tbl_course_quiz
2310
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2311
            $count_quiz = 0;
2312
            $countQuizResult = Database::query($sql);
2313
            if (!empty($countQuizResult)) {
2314
                $count_quiz = Database::fetch_row($countQuizResult);
2315
            }
2316
2317
            if (!empty($count_quiz[0]) && !empty($student_id)) {
2318
                if (is_array($student_id)) {
2319
                    $student_id = array_map('intval', $student_id);
2320
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2321
                } else {
2322
                    $student_id = intval($student_id);
2323
                    $condition_user = " AND exe_user_id = '$student_id' ";
2324
                }
2325
2326
                if (empty($exercise_id)) {
2327
                    $sql = "SELECT id FROM $tbl_course_quiz
2328
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2329
                    $result = Database::query($sql);
2330
                    $exercise_list = [];
2331
                    $exercise_id = null;
2332
                    if (!empty($result) && Database::num_rows($result)) {
2333
                        while ($row = Database::fetch_array($result)) {
2334
                            $exercise_list[] = $row['id'];
2335
                        }
2336
                    }
2337
                    if (!empty($exercise_list)) {
2338
                        $exercise_id = implode("','", $exercise_list);
2339
                    }
2340
                }
2341
2342
                $count_quiz = Database::fetch_row(Database::query($sql));
2343
                $sql = "SELECT
2344
                        SUM(exe_result/exe_weighting*100) as avg_score,
2345
                        COUNT(*) as num_attempts
2346
                        $select_lp_id
2347
                        FROM $tbl_stats_exercise
2348
                        WHERE
2349
                            exe_exo_id IN ('".$exercise_id."')
2350
                            $condition_user AND
2351
                            status = '' AND
2352
                            c_id = {$course_info['real_id']}
2353
                            $condition_session
2354
                            $condition_into_lp
2355
                        ORDER BY exe_date DESC";
2356
2357
                $res = Database::query($sql);
2358
                $row = Database::fetch_array($res);
2359
                $quiz_avg_score = null;
2360
2361
                if (!empty($row['avg_score'])) {
2362
                    $quiz_avg_score = round($row['avg_score'], 2);
2363
                }
2364
2365
                if (!empty($row['num_attempts'])) {
2366
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2367
                }
2368
                if (is_array($student_id)) {
2369
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2370
                }
2371
                if ($into_lp == 0) {
2372
                    return $quiz_avg_score;
2373
                } else {
2374
                    if (!empty($row['lp_id'])) {
2375
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2376
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2377
                        $sql = "SELECT lp.name
2378
                                FROM $tbl_lp as lp, $tbl_course as c
2379
                                WHERE
2380
                                    c.code = '$course_code' AND
2381
                                    lp.id = ".$row['lp_id']." AND
2382
                                    lp.c_id = c.id
2383
                                LIMIT 1;
2384
                        ";
2385
                        $result = Database::query($sql);
2386
                        $row_lp = Database::fetch_row($result);
2387
                        $lp_name = null;
2388
                        if ($row_lp && isset($row_lp[0])) {
2389
                            $lp_name = $row_lp[0];
2390
                        }
2391
2392
                        return [$quiz_avg_score, $lp_name];
2393
                    } else {
2394
                        return [$quiz_avg_score, null];
2395
                    }
2396
                }
2397
            }
2398
        }
2399
2400
        return null;
2401
    }
2402
2403
    /**
2404
     * Get count student's exercise COMPLETED attempts.
2405
     *
2406
     * @param int $student_id
2407
     * @param int $courseId
2408
     * @param int $exercise_id
2409
     * @param int $lp_id
2410
     * @param int $lp_item_id
2411
     * @param int $session_id
2412
     * @param int $find_all_lp 0 = just LP specified
2413
     *                         1 = LP specified or whitout LP,
2414
     *                         2 = all rows
2415
     *
2416
     * @internal param \Student $int id
2417
     * @internal param \Course $string code
2418
     * @internal param \Exercise $int id
2419
     * @internal param \Learning $int path id (optional),
2420
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2421
     * @internal param \Learning $int path item id (optional),
2422
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2423
     *
2424
     * @return int count of attempts
2425
     */
2426
    public static function count_student_exercise_attempts(
2427
        $student_id,
2428
        $courseId,
2429
        $exercise_id,
2430
        $lp_id = 0,
2431
        $lp_item_id = 0,
2432
        $session_id = 0,
2433
        $find_all_lp = 0
2434
    ) {
2435
        $courseId = intval($courseId);
2436
        $student_id = intval($student_id);
2437
        $exercise_id = intval($exercise_id);
2438
        $session_id = intval($session_id);
2439
2440
        $lp_id = intval($lp_id);
2441
        $lp_item_id = intval($lp_item_id);
2442
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2443
2444
        $sql = "SELECT COUNT(ex.exe_id) as essais
2445
                FROM $tbl_stats_exercises AS ex
2446
                WHERE
2447
                    ex.c_id = $courseId AND
2448
                    ex.exe_exo_id = $exercise_id AND
2449
                    status = '' AND
2450
                    exe_user_id= $student_id AND
2451
                    session_id = $session_id ";
2452
2453
        if ($find_all_lp == 1) {
2454
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2455
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2456
        } elseif ($find_all_lp == 0) {
2457
            $sql .= "AND orig_lp_id = $lp_id
2458
                AND orig_lp_item_id = $lp_item_id";
2459
        }
2460
2461
        $rs = Database::query($sql);
2462
        $row = Database::fetch_row($rs);
2463
        $count_attempts = $row[0];
2464
2465
        return $count_attempts;
2466
    }
2467
2468
    /**
2469
     * Get count student's exercise progress.
2470
     *
2471
     * @param array $exercise_list
2472
     * @param int   $user_id
2473
     * @param int   $courseId
2474
     * @param int   $session_id
2475
     *
2476
     * @return string
2477
     */
2478
    public static function get_exercise_student_progress(
2479
        $exercise_list,
2480
        $user_id,
2481
        $courseId,
2482
        $session_id
2483
    ) {
2484
        $courseId = (int) $courseId;
2485
        $user_id = (int) $user_id;
2486
        $session_id = (int) $session_id;
2487
2488
        if (empty($exercise_list)) {
2489
            return '0%';
2490
        }
2491
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2492
        $exercise_list = array_keys($exercise_list);
2493
        $exercise_list = array_map('intval', $exercise_list);
2494
2495
        $exercise_list_imploded = implode("' ,'", $exercise_list);
2496
2497
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
2498
                FROM $tbl_stats_exercises AS ex
2499
                WHERE
2500
                    ex.c_id = $courseId AND
2501
                    ex.session_id  = $session_id AND
2502
                    ex.exe_user_id = $user_id AND
2503
                    ex.status = '' AND
2504
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
2505
2506
        $rs = Database::query($sql);
2507
        $count = 0;
2508
        if ($rs) {
2509
            $row = Database::fetch_row($rs);
2510
            $count = $row[0];
2511
        }
2512
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
2513
2514
        return $count;
2515
    }
2516
2517
    /**
2518
     * @param array $exercise_list
2519
     * @param int   $user_id
2520
     * @param int   $courseId
2521
     * @param int   $session_id
2522
     *
2523
     * @return string
2524
     */
2525
    public static function get_exercise_student_average_best_attempt(
2526
        $exercise_list,
2527
        $user_id,
2528
        $courseId,
2529
        $session_id
2530
    ) {
2531
        $result = 0;
2532
        if (!empty($exercise_list)) {
2533
            foreach ($exercise_list as $exercise_data) {
2534
                $exercise_id = $exercise_data['id'];
2535
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2536
                    $user_id,
2537
                    $exercise_id,
2538
                    $courseId,
2539
                    $session_id
2540
                );
2541
2542
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
2543
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
2544
                }
2545
            }
2546
            $result = $result / count($exercise_list);
2547
            $result = round($result, 2) * 100;
2548
        }
2549
2550
        return $result.'%';
2551
    }
2552
2553
    /**
2554
     * Returns the average student progress in the learning paths of the given
2555
     * course, it will take into account the progress that were not started.
2556
     *
2557
     * @param int|array $studentId
2558
     * @param string    $courseCode
2559
     * @param array     $lpIdList        Limit average to listed lp ids
2560
     * @param int       $sessionId       Session id (optional),
2561
     *                                   if parameter $session_id is null(default) it'll return results including
2562
     *                                   sessions, 0 = session is not filtered
2563
     * @param bool      $returnArray     Will return an array of the type:
2564
     *                                   [sum_of_progresses, number] if it is set to true
2565
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2566
     *
2567
     * @return float Average progress of the user in this course from 0 to 100
2568
     */
2569
    public static function get_avg_student_progress(
2570
        $studentId,
2571
        $courseCode = null,
2572
        $lpIdList = [],
2573
        $sessionId = null,
2574
        $returnArray = false,
2575
        $onlySeriousGame = false
2576
    ) {
2577
        // If there is at least one learning path and one student.
2578
        if (empty($studentId)) {
2579
            return false;
2580
        }
2581
2582
        $sessionId = (int) $sessionId;
2583
        $courseInfo = api_get_course_info($courseCode);
2584
2585
        if (empty($courseInfo)) {
2586
            return false;
2587
        }
2588
2589
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2590
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2591
        $lpConditions = [];
2592
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2593
2594
        if ($sessionId > 0) {
2595
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2596
        } else {
2597
            $lpConditions['AND session_id = ?'] = $sessionId;
2598
        }
2599
2600
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2601
            $placeHolders = [];
2602
            for ($i = 0; $i < count($lpIdList); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

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

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

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

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

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

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
5989
            if (!empty($my_final_array[$i])) {
5990
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5991
                $final_array[$i] = 0;
5992
            }
5993
        }
5994
5995
        // Dataset definition
5996
        $dataSet = new pData();
5997
        $dataSet->addPoints($final_array, 'Serie1');
5998
        $dataSet->addPoints($my_final_array, 'Serie2');
5999
        $dataSet->normalize(100, "%");
6000
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6001
6002
        // Cache definition
6003
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6004
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6005
        $chartHash = $myCache->getHash($dataSet);
6006
        if ($myCache->isInCache($chartHash)) {
6007
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6008
            $myCache->saveFromCache($chartHash, $imgPath);
6009
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6010
        } else {
6011
            /* Create the pChart object */
6012
            $widthSize = 80;
6013
            $heightSize = 35;
6014
            $fontSize = 2;
6015
6016
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6017
6018
            /* Turn of Antialiasing */
6019
            $myPicture->Antialias = false;
6020
6021
            /* Add a border to the picture */
6022
            $myPicture->drawRectangle(
6023
                0,
6024
                0,
6025
                $widthSize - 1,
6026
                $heightSize - 1,
6027
                ['R' => 0, 'G' => 0, 'B' => 0]
6028
            );
6029
6030
            /* Set the default font */
6031
            $myPicture->setFontProperties(
6032
                [
6033
                    'FontName' => api_get_path(
6034
                            SYS_FONTS_PATH
6035
                        ).'opensans/OpenSans-Regular.ttf',
6036
                    'FontSize' => $fontSize,
6037
                ]
6038
            );
6039
6040
            /* Do not write the chart title */
6041
            /* Define the chart area */
6042
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
6043
6044
            /* Draw the scale */
6045
            $scaleSettings = [
6046
                'GridR' => 200,
6047
                'GridG' => 200,
6048
                'GridB' => 200,
6049
                'DrawSubTicks' => true,
6050
                'CycleBackground' => true,
6051
                'Mode' => SCALE_MODE_MANUAL,
6052
                'ManualScale' => [
6053
                    '0' => [
6054
                        'Min' => 0,
6055
                        'Max' => 100,
6056
                    ],
6057
                ],
6058
            ];
6059
            $myPicture->drawScale($scaleSettings);
6060
6061
            /* Turn on shadow computing */
6062
            $myPicture->setShadow(
6063
                true,
6064
                [
6065
                    'X' => 1,
6066
                    'Y' => 1,
6067
                    'R' => 0,
6068
                    'G' => 0,
6069
                    'B' => 0,
6070
                    'Alpha' => 10,
6071
                ]
6072
            );
6073
6074
            /* Draw the chart */
6075
            $myPicture->setShadow(
6076
                true,
6077
                [
6078
                    'X' => 1,
6079
                    'Y' => 1,
6080
                    'R' => 0,
6081
                    'G' => 0,
6082
                    'B' => 0,
6083
                    'Alpha' => 10,
6084
                ]
6085
            );
6086
            $settings = [
6087
                'DisplayValues' => true,
6088
                'DisplaySize' => $fontSize,
6089
                'DisplayR' => 0,
6090
                'DisplayG' => 0,
6091
                'DisplayB' => 0,
6092
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6093
                'Gradient' => false,
6094
                'Surrounding' => 5,
6095
                'InnerSurrounding' => 5,
6096
            ];
6097
            $myPicture->drawStackedBarChart($settings);
6098
6099
            /* Save and write in cache */
6100
            $myCache->writeToCache($chartHash, $myPicture);
6101
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6102
            $myCache->saveFromCache($chartHash, $imgPath);
6103
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6104
        }
6105
6106
        return $imgPath;
6107
    }
6108
6109
    /**
6110
     * Generates a big graph with the number of best results.
6111
     *
6112
     * @param	array
6113
     */
6114
    public static function generate_exercise_result_graph($attempts)
6115
    {
6116
        $exercise_title = strip_tags($attempts['title']);
6117
        $attempts = $attempts['data'];
6118
        $my_exercise_result_array = $exercise_result = [];
6119
        if (empty($attempts)) {
6120
            return null;
6121
        }
6122
        foreach ($attempts as $attempt) {
6123
            if (api_get_user_id() == $attempt['exe_user_id']) {
6124
                if ($attempt['exe_weighting'] != 0) {
6125
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6126
                }
6127
            } else {
6128
                if ($attempt['exe_weighting'] != 0) {
6129
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6130
                }
6131
            }
6132
        }
6133
6134
        //Getting best result
6135
        rsort($my_exercise_result_array);
6136
        $my_exercise_result = 0;
6137
        if (isset($my_exercise_result_array[0])) {
6138
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6139
        }
6140
6141
        $max = 100;
6142
        $pieces = 5;
6143
        $part = round($max / $pieces);
6144
        $x_axis = [];
6145
        $final_array = [];
6146
        $my_final_array = [];
6147
6148
        for ($i = 1; $i <= $pieces; $i++) {
6149
            $sum = 1;
6150
            if ($i == 1) {
6151
                $sum = 0;
6152
            }
6153
            $min = ($i - 1) * $part + $sum;
6154
            $max = ($i) * $part;
6155
            $x_axis[] = $min." - ".$max;
6156
            $count = 0;
6157
            foreach ($exercise_result as $result) {
6158
                $percentage = $result * 100;
6159
                if ($percentage >= $min && $percentage <= $max) {
6160
                    $count++;
6161
                }
6162
            }
6163
            $final_array[] = $count;
6164
6165
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6166
                $my_final_array[] = 1;
6167
            } else {
6168
                $my_final_array[] = 0;
6169
            }
6170
        }
6171
6172
        //Fix to remove the data of the user with my data
6173
6174
        for ($i = 0; $i <= count($my_final_array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

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

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

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