Passed
Push — 1.11.x ( 036fa1...927d0d )
by Julito
14:55
created

Tracking::compareUserData()   C

Complexity

Conditions 13

Size

Total Lines 47
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 34
nop 1
dl 0
loc 47
rs 6.6166
c 0
b 0
f 0

How to fix   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 showUserProgress(
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 display_end_date DESC, 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_header(
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($user_id, $courseInfo);
4946
4947
                    if (is_null($progress) || empty($progress)) {
4948
                        $progress = '0%';
4949
                    } else {
4950
                        $progress = $progress.'%';
4951
                    }
4952
4953
                    $filterByCourse = isset($_GET['course']) && $course_code == $_GET['course'] &&
4954
                        empty($_GET['session_id']);
4955
4956
                    $url = api_get_course_url($course_code, $session_id);
4957
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
4958
                    $bestScoreResult = empty($bestScore) ? '-' : sprintf(get_lang('XPercent'), $bestScore);
4959
                    $bestScoreNotInLP = empty($bestScoreAverageNotInLP)
4960
                        ? '-'
4961
                        : sprintf(get_lang('XPercent'), $bestScoreAverageNotInLP);
4962
4963
                    $detailsLink = '';
4964
                    if ($filterByCourse) {
4965
                        $detailsLink .= '<a href="#course_session_data">';
4966
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4967
                        $detailsLink .= '</a>';
4968
                    } else {
4969
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_data">';
4970
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4971
                        $detailsLink .= '</a>';
4972
                    }
4973
4974
                    $result = array_filter(
4975
                        [
4976
                            'course_title' => $course_url,
4977
                            'time_spent' => $time,
4978
                            'progress' => $progress,
4979
                            'best_score_in_lp' => $bestScoreResult,
4980
                            'best_score_not_in_lp' => $bestScoreNotInLP,
4981
                            'latest_login' => $last_connection,
4982
                            'details' => $detailsLink,
4983
                        ],
4984
                        function ($data, $columnKey) {
4985
                            if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
4986
                                return false;
4987
                            }
4988
4989
                            return true;
4990
                        },
4991
                        ARRAY_FILTER_USE_BOTH
4992
                    );
4993
4994
                    $coursesTable->addRow(
4995
                        array_values($result),
4996
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
4997
                    );
4998
                }
4999
5000
                $html .= Display::div($coursesTable->toHtml(), ['class' => 'table-responsive']);
5001
            }
5002
        }
5003
5004
        $allowCareerUser = api_get_configuration_value('allow_career_users');
5005
5006
        // Session list.
5007
        if (!empty($course_in_session)) {
5008
            $main_session_graph = '';
5009
            // Load graphics only when calling to an specific session
5010
            $all_exercise_graph_name_list = [];
5011
            $my_results = [];
5012
            $all_exercise_graph_list = [];
5013
            $all_exercise_start_time = [];
5014
            $allCareers = [];
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
                $visibility = api_get_session_visibility($my_session_id, null, false, $user_id);
5022
5023
                if (SESSION_AVAILABLE === $visibility) {
5024
                    $careers = SessionManager::getCareersFromSession($my_session_id);
5025
                    if (!empty($careers)) {
5026
                        $allCareers = array_merge($allCareers, $careers);
5027
                    }
5028
                }
5029
5030
                foreach ($course_list as $course_data) {
5031
                    $exercise_list = ExerciseLib::get_all_exercises(
5032
                        $course_data,
5033
                        $my_session_id,
5034
                        false,
5035
                        null,
5036
                        false,
5037
                        1
5038
                    );
5039
5040
                    foreach ($exercise_list as $exercise_data) {
5041
                        $exercise_obj = new Exercise($course_data['real_id']);
5042
                        $exercise_obj->read($exercise_data['id']);
5043
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
5044
                        //$visible_return = $exercise_obj->is_visible();
5045
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
5046
                            $best_average = (int)
5047
                                ExerciseLib::get_best_average_score_by_exercise(
5048
                                    $exercise_data['id'],
5049
                                    $course_data['real_id'],
5050
                                    $my_session_id,
5051
                                    $user_count
5052
                                )
5053
                            ;
5054
5055
                            $exercise_graph_list[] = $best_average;
5056
                            $all_exercise_graph_list[] = $best_average;
5057
5058
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
5059
                                api_get_user_id(),
5060
                                $exercise_data['id'],
5061
                                $course_data['real_id'],
5062
                                $my_session_id
5063
                            );
5064
5065
                            $score = 0;
5066
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
5067
                                $score = intval($user_result_data['exe_result'] / $user_result_data['exe_weighting'] * 100);
5068
                            }
5069
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
5070
                            $all_exercise_start_time[] = $time;
5071
                            $my_results[] = $score;
5072
                            if (count($exercise_list) <= 10) {
5073
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
5074
                                $exercise_graph_name_list[] = $title;
5075
                                $all_exercise_graph_name_list[] = $title;
5076
                            } else {
5077
                                // if there are more than 10 results, space becomes difficult to find,
5078
                                // so only show the title of the exercise, not the tool
5079
                                $title = cut($exercise_data['title'], 30);
5080
                                $exercise_graph_name_list[] = $title;
5081
                                $all_exercise_graph_name_list[] = $title;
5082
                            }
5083
                        }
5084
                    }
5085
                }
5086
            }
5087
5088
            // Complete graph
5089
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
5090
                asort($all_exercise_start_time);
5091
5092
                //Fix exams order
5093
                $final_all_exercise_graph_name_list = [];
5094
                $my_results_final = [];
5095
                $final_all_exercise_graph_list = [];
5096
5097
                foreach ($all_exercise_start_time as $key => $time) {
5098
                    $label_time = '';
5099
                    if (!empty($time)) {
5100
                        $label_time = date('d-m-y', $time);
5101
                    }
5102
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
5103
                    $my_results_final[] = $my_results[$key];
5104
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
5105
                }
5106
                $main_session_graph = '<div class="row"><div class="col-md-10 col-md-offset-1">'
5107
                    .self::generate_session_exercise_graph(
5108
                        $final_all_exercise_graph_name_list,
5109
                        $my_results_final,
5110
                        $final_all_exercise_graph_list
5111
                    )
5112
                    .'</div></div>';
5113
            }
5114
5115
            $sessionIcon = Display::return_icon('session.png', get_lang('Sessions'));
5116
5117
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5118
            $html .= $anchor.Display::page_header(
5119
                $sessionIcon.PHP_EOL.get_lang('Sessions')
5120
            );
5121
5122
            $sessionsTable = new SortableTableFromArray([], 0, 0, 'sessions');
5123
            $sessionsTable->setHeaders(
5124
                [
5125
                    get_lang('Session'),
5126
                    get_lang('PublishedExercises'),
5127
                    get_lang('NewExercises'),
5128
                    get_lang('AverageExerciseResult'),
5129
                    get_lang('Details'),
5130
                ]
5131
            );
5132
5133
            foreach ($course_in_session as $my_session_id => $session_data) {
5134
                $course_list = $session_data['course_list'];
5135
                $session_name = $session_data['name'];
5136
                if ($showAllSessions == false) {
5137
                    if (isset($session_id) && !empty($session_id)) {
5138
                        if ($session_id != $my_session_id) {
5139
                            continue;
5140
                        }
5141
                    }
5142
                }
5143
5144
                $all_exercises = 0;
5145
                $all_unanswered_exercises_by_user = 0;
5146
                $all_average = 0;
5147
                $stats_array = [];
5148
5149
                foreach ($course_list as $course_data) {
5150
                    // All exercises in the course @todo change for a real count
5151
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5152
                    $count_exercises = 0;
5153
                    if (is_array($exercises) && !empty($exercises)) {
5154
                        $count_exercises = count($exercises);
5155
                    }
5156
5157
                    // Count of user results
5158
                    $done_exercises = null;
5159
                    $courseInfo = api_get_course_info($course_data['code']);
5160
5161
                    $answered_exercises = 0;
5162
                    if (!empty($exercises)) {
5163
                        foreach ($exercises as $exercise_item) {
5164
                            $attempts = Event::count_exercise_attempts_by_user(
5165
                                api_get_user_id(),
5166
                                $exercise_item['id'],
5167
                                $courseInfo['real_id'],
5168
                                $my_session_id
5169
                            );
5170
                            if ($attempts > 1) {
5171
                                $answered_exercises++;
5172
                            }
5173
                        }
5174
                    }
5175
5176
                    // Average
5177
                    $average = ExerciseLib::get_average_score_by_course(
5178
                        $courseInfo['real_id'],
5179
                        $my_session_id
5180
                    );
5181
                    $all_exercises += $count_exercises;
5182
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5183
                    $all_average += $average;
5184
                }
5185
5186
                if (!empty($course_list)) {
5187
                    $all_average = $all_average / count($course_list);
5188
                }
5189
5190
                $filterBySession = isset($_GET['session_id']) && $my_session_id == $_GET['session_id'];
5191
5192
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5193
5194
                $sessionsTable->addRow(
5195
                    [
5196
                        Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]),
5197
                        $all_exercises,
5198
                        $all_unanswered_exercises_by_user,
5199
                        ExerciseLib::convert_to_percentage($all_average),
5200
                        Display::url(
5201
                            Display::return_icon(
5202
                                $filterBySession ? '2rightarrow_na.png' : '2rightarrow.png',
5203
                                get_lang('Details')
5204
                            ),
5205
                            api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5206
                        ),
5207
                    ],
5208
                    ['style' => $filterBySession ? 'background-color: #FBF09D;' : '']
5209
                );
5210
            }
5211
5212
            if ($allowCareerUser && !empty($allCareers)) {
5213
                $careers = [];
5214
                foreach ($allCareers as $career) {
5215
                    $careers[$career['id']] = $career;
5216
                }
5217
5218
                $title = Display::page_subheader(get_lang('OngoingTraining'));
5219
                $html .= $title.MyStudents::getCareersTable($careers);
5220
            }
5221
5222
            $html .= Display::div($sessionsTable->toHtml(), ['class' => 'table-responsive']);
5223
            $html .= Display::div(
5224
                $main_session_graph,
5225
                [
5226
                    'id' => 'session_graph',
5227
                    'class' => 'chart-session',
5228
                ]
5229
            );
5230
5231
            // Checking selected session.
5232
            if (isset($_GET['session_id'])) {
5233
                $session_id_from_get = (int) $_GET['session_id'];
5234
                $session_data = $course_in_session[$session_id_from_get];
5235
                $course_list = $session_data['course_list'];
5236
5237
                $html .= '<a name="course_session_list"></a>';
5238
                $html .= Display::page_subheader($session_data['name'], get_lang('CourseList'));
5239
5240
                $columnHeaders = array_filter(
5241
                    [
5242
                        'course_title' => get_lang('Course'),
5243
                        'published_exercises' => get_lang('PublishedExercises'),
5244
                        'new_exercises' => get_lang('NewExercises'),
5245
                        'my_average' => get_lang('MyAverage'),
5246
                        'average_exercise_result' => get_lang('AverageExerciseResult'),
5247
                        'time_spent' => get_lang('TimeSpentInTheCourse'),
5248
                        'lp_progress' => get_lang('LPProgress'),
5249
                        'score' => get_lang('Score')
5250
                            .Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage')),
5251
                        'best_score' => get_lang('BestScore'),
5252
                        'last_connection' => get_lang('LastConnexion'),
5253
                        'details' => get_lang('Details'),
5254
                    ],
5255
                    function ($column, $key) use ($trackingColumns) {
5256
                        if (isset($trackingColumns['course_session']) &&
5257
                            in_array($key, $trackingColumns['course_session']) &&
5258
                            $trackingColumns['course_session'][$key]
5259
                        ) {
5260
                            return true;
5261
                        }
5262
5263
                        return false;
5264
                    },
5265
                    ARRAY_FILTER_USE_BOTH
5266
                );
5267
5268
                $sessionCoursesTable = new SortableTableFromArray([], 0, 0, 'session_courses');
5269
                $sessionCoursesTable->setHeaders($columnHeaders);
5270
5271
                foreach ($course_list as $course_data) {
5272
                    $course_code = $course_data['code'];
5273
                    $course_title = $course_data['title'];
5274
                    $courseId = $course_data['real_id'];
5275
5276
                    // All exercises in the course @todo change for a real count
5277
                    $exercises = ExerciseLib::get_all_exercises(
5278
                        $course_data,
5279
                        $session_id_from_get
5280
                    );
5281
                    $count_exercises = 0;
5282
                    if (!empty($exercises)) {
5283
                        $count_exercises = count($exercises);
5284
                    }
5285
                    $answered_exercises = 0;
5286
                    foreach ($exercises as $exercise_item) {
5287
                        $attempts = Event::count_exercise_attempts_by_user(
5288
                            api_get_user_id(),
5289
                            $exercise_item['id'],
5290
                            $courseId,
5291
                            $session_id_from_get
5292
                        );
5293
                        if ($attempts > 1) {
5294
                            $answered_exercises++;
5295
                        }
5296
                    }
5297
5298
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5299
5300
                    // Average
5301
                    $average = ExerciseLib::get_average_score_by_course(
5302
                        $courseId,
5303
                        $session_id_from_get
5304
                    );
5305
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5306
                        api_get_user_id(),
5307
                        $courseId,
5308
                        $session_id_from_get
5309
                    );
5310
5311
                    $bestScore = self::get_avg_student_score(
5312
                        $user_id,
5313
                        $course_code,
5314
                        [],
5315
                        $session_id_from_get,
5316
                        false,
5317
                        false,
5318
                        true
5319
                    );
5320
5321
                    $stats_array[$course_code] = [
5322
                        'exercises' => $count_exercises,
5323
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5324
                        'done_exercises' => $done_exercises,
5325
                        'average' => $average,
5326
                        'my_average' => $my_average,
5327
                        'best_score' => $bestScore,
5328
                    ];
5329
5330
                    $last_connection = self::get_last_connection_date_on_the_course(
5331
                        $user_id,
5332
                        $course_data,
5333
                        $session_id_from_get
5334
                    );
5335
5336
                    $progress = self::get_avg_student_progress(
5337
                        $user_id,
5338
                        $course_code,
5339
                        [],
5340
                        $session_id_from_get
5341
                    );
5342
5343
                    $total_time_login = self::get_time_spent_on_the_course(
5344
                        $user_id,
5345
                        $courseId,
5346
                        $session_id_from_get
5347
                    );
5348
                    $time = api_time_to_hms($total_time_login);
5349
5350
                    $percentage_score = self::get_avg_student_score(
5351
                        $user_id,
5352
                        $course_code,
5353
                        [],
5354
                        $session_id_from_get
5355
                    );
5356
                    $courseCodeFromGet = $_GET['course'] ?? null;
5357
5358
                    $filterByCourse = $course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get;
5359
5360
                    $url = api_get_course_url($course_code, $session_id_from_get);
5361
                    $course_url = Display::url(
5362
                        $course_title,
5363
                        $url,
5364
                        ['target' => SESSION_LINK_TARGET]
5365
                    );
5366
5367
                    if (is_numeric($progress)) {
5368
                        $progress = $progress.'%';
5369
                    } else {
5370
                        $progress = '0%';
5371
                    }
5372
                    if (is_numeric($percentage_score)) {
5373
                        $percentage_score = $percentage_score.'%';
5374
                    } else {
5375
                        $percentage_score = '0%';
5376
                    }
5377
5378
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5379
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5380
                    } else {
5381
                        $bestScore = '-';
5382
                    }
5383
5384
                    if (empty($last_connection) || is_bool($last_connection)) {
5385
                        $last_connection = '';
5386
                    }
5387
5388
                    if ($course_code == $courseCodeFromGet &&
5389
                        $_GET['session_id'] == $session_id_from_get
5390
                    ) {
5391
                        $details = Display::url(
5392
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
5393
                        '#course_session_data'
5394
                        );
5395
                    } else {
5396
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5397
                        $details = Display::url(
5398
                            Display::return_icon(
5399
                                '2rightarrow.png',
5400
                                get_lang('Details')
5401
                            ),
5402
                            $url
5403
                        );
5404
                    }
5405
5406
                    $data = array_filter(
5407
                        [
5408
                            'course_title' => $course_url,
5409
                            'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5410
                            'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5411
                            'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5412
                            'average_exercise_result' => $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5413
                            'time_spent' => $time,
5414
                            'lp_progress' => $progress,
5415
                            'score' => $percentage_score,
5416
                            'best_score' => $bestScore,
5417
                            'last_connection' => $last_connection,
5418
                            'details' => $details,
5419
                        ],
5420
                        function ($value, $key) use ($trackingColumns) {
5421
                            if (in_array($key, $trackingColumns['course_session']) && $trackingColumns['course_session'][$key]) {
5422
                                return true;
5423
                            }
5424
5425
                            return false;
5426
                        },
5427
                        ARRAY_FILTER_USE_BOTH
5428
                    );
5429
5430
                    $sessionCoursesTable->addRow(
5431
                        array_values($data),
5432
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
5433
                    );
5434
                }
5435
5436
                $html .= Display::div($sessionCoursesTable->toHtml(), ['class' => 'table-responsive']);
5437
            }
5438
        }
5439
5440
        $pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true';
5441
        if ($pluginCalendar) {
5442
            $course_in_session[0] = $courseIdList;
5443
            $plugin = LearningCalendarPlugin::create();
5444
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
5445
        }
5446
5447
        return $html;
5448
    }
5449
5450
    /**
5451
     * Shows the user detail progress (when clicking in the details link).
5452
     *
5453
     * @param int    $user_id
5454
     * @param string $course_code
5455
     * @param int    $session_id
5456
     * @param bool   $showDiagram
5457
     */
5458
    public static function show_course_detail($user_id, $course_code, $session_id, $showDiagram = false): string
5459
    {
5460
        if (empty($user_id) || empty($course_code)) {
5461
            return '';
5462
        }
5463
5464
        $course_info = api_get_course_info($course_code);
5465
        if (empty($course_info)) {
5466
            return '';
5467
        }
5468
5469
        $user_id = (int) $user_id;
5470
        $session_id = (int) $session_id;
5471
5472
        $html = '<a name="course_session_data"></a>';
5473
        $html .= Display::page_subheader2($course_info['title']);
5474
5475
        if ($showDiagram && !empty($session_id)) {
5476
            $visibility = api_get_session_visibility($session_id);
5477
            if (SESSION_AVAILABLE === $visibility) {
5478
                $html .= Display::page_subheader2($course_info['title']);
5479
            }
5480
        }
5481
5482
        // Show exercise results of invisible exercises? see BT#4091
5483
        $quizzesHtml = self::generateQuizzesTable($course_info, $session_id);
5484
        // LP table results
5485
        $learningPathsHtml = self::generateLearningPathsTable($user_id, $course_info, $session_id);
5486
        $skillsHtml = self::displayUserSkills($user_id, $course_info['id'], $session_id);
5487
5488
        $toolsHtml = [
5489
            'quizzes' => $quizzesHtml,
5490
            'learning_paths' => $learningPathsHtml,
5491
            'skills' => $skillsHtml,
5492
        ];
5493
5494
        $toolsOrder = api_get_configuration_value('my_progress_course_tools_order');
5495
5496
        if (empty($toolsOrder)) {
5497
            $html .= implode(PHP_EOL, $toolsHtml);
5498
        } else {
5499
            foreach ($toolsOrder['order'] as $tool) {
5500
                $html .= $toolsHtml[$tool].PHP_EOL;
5501
            }
5502
        }
5503
5504
        return $html;
5505
    }
5506
5507
    /**
5508
     * Generates an histogram.
5509
     *
5510
     * @param array $names      list of exercise names
5511
     * @param array $my_results my results 0 to 100
5512
     * @param array $average    average scores 0-100
5513
     *
5514
     * @return string
5515
     */
5516
    public static function generate_session_exercise_graph($names, $my_results, $average)
5517
    {
5518
        $html = api_get_js('chartjs/Chart.js');
5519
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
5520
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
5521
        $jsStr = " var data = {
5522
                       labels:".json_encode($names).",
5523
                       datasets: [
5524
                       {
5525
                         label: '".get_lang('MyResults')."',
5526
                         backgroundColor: 'rgb(255, 99, 132)',
5527
                         stack: 'Stack1',
5528
                         data: ".json_encode($my_results).",
5529
                        },
5530
                        {
5531
                         label: '".get_lang('AverageScore')."',
5532
                         backgroundColor: 'rgb(75, 192, 192)',
5533
                         stack: 'Stack2',
5534
                         data: ".json_encode($average).",
5535
                        },
5536
                        ],
5537
                    };
5538
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
5539
                    var myBarChart = new Chart(ctx, {
5540
                    type: 'bar',
5541
                    data: data,
5542
                    options: {
5543
                            title: {
5544
                                    display: true,
5545
                                    text: '".get_lang('ExercisesInTimeProgressChart')."'
5546
                            },
5547
                            tooltips: {
5548
                                    mode: 'index',
5549
                                    intersect: false
5550
                            },
5551
                            responsive: true,
5552
                            scales: {
5553
                                yAxes: [{
5554
                                    ticks: {
5555
                                        // Include a dollar sign in the ticks
5556
                                        callback: function(value, index, values) {
5557
                                            return value + '%';
5558
                                        }
5559
                                    }
5560
                                }]
5561
                            }
5562
                    }
5563
                });";
5564
        $html .= Display::tag('script', $jsStr);
5565
5566
        return $html;
5567
    }
5568
5569
    /**
5570
     * Returns a thumbnail of the function generate_exercise_result_graph.
5571
     *
5572
     * @param array $attempts
5573
     */
5574
    public static function generate_exercise_result_thumbnail_graph($attempts)
5575
    {
5576
        //$exercise_title = $attempts['title'];
5577
        $attempts = $attempts['data'];
5578
        $my_exercise_result_array = $exercise_result = [];
5579
        if (empty($attempts)) {
5580
            return null;
5581
        }
5582
5583
        foreach ($attempts as $attempt) {
5584
            if (api_get_user_id() == $attempt['exe_user_id']) {
5585
                if ($attempt['exe_weighting'] != 0) {
5586
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5587
                }
5588
            } else {
5589
                if ($attempt['exe_weighting'] != 0) {
5590
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5591
                }
5592
            }
5593
        }
5594
5595
        // Getting best result
5596
        rsort($my_exercise_result_array);
5597
        $my_exercise_result = 0;
5598
        if (isset($my_exercise_result_array[0])) {
5599
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5600
        }
5601
5602
        $max = 100;
5603
        $pieces = 5;
5604
        $part = round($max / $pieces);
5605
        $x_axis = [];
5606
        $final_array = [];
5607
        $my_final_array = [];
5608
5609
        for ($i = 1; $i <= $pieces; $i++) {
5610
            $sum = 1;
5611
            if ($i == 1) {
5612
                $sum = 0;
5613
            }
5614
            $min = ($i - 1) * $part + $sum;
5615
            $max = ($i) * $part;
5616
            $x_axis[] = $min." - ".$max;
5617
            $count = 0;
5618
            foreach ($exercise_result as $result) {
5619
                $percentage = $result * 100;
5620
                if ($percentage >= $min && $percentage <= $max) {
5621
                    //echo ' is > ';
5622
                    $count++;
5623
                }
5624
            }
5625
            //echo '<br />';
5626
            $final_array[] = $count;
5627
5628
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5629
                $my_final_array[] = 1;
5630
            } else {
5631
                $my_final_array[] = 0;
5632
            }
5633
        }
5634
5635
        // Fix to remove the data of the user with my data
5636
        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...
5637
            if (!empty($my_final_array[$i])) {
5638
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5639
                $final_array[$i] = 0;
5640
            }
5641
        }
5642
5643
        // Dataset definition
5644
        $dataSet = new pData();
5645
        $dataSet->addPoints($final_array, 'Serie1');
5646
        $dataSet->addPoints($my_final_array, 'Serie2');
5647
        $dataSet->normalize(100, "%");
5648
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5649
5650
        // Cache definition
5651
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5652
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5653
        $chartHash = $myCache->getHash($dataSet);
5654
        if ($myCache->isInCache($chartHash)) {
5655
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5656
            $myCache->saveFromCache($chartHash, $imgPath);
5657
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5658
        } else {
5659
            /* Create the pChart object */
5660
            $widthSize = 80;
5661
            $heightSize = 35;
5662
            $fontSize = 2;
5663
5664
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5665
5666
            /* Turn of Antialiasing */
5667
            $myPicture->Antialias = false;
5668
5669
            /* Add a border to the picture */
5670
            $myPicture->drawRectangle(
5671
                0,
5672
                0,
5673
                $widthSize - 1,
5674
                $heightSize - 1,
5675
                ['R' => 0, 'G' => 0, 'B' => 0]
5676
            );
5677
5678
            /* Set the default font */
5679
            $myPicture->setFontProperties(
5680
                [
5681
                    'FontName' => api_get_path(
5682
                            SYS_FONTS_PATH
5683
                        ).'opensans/OpenSans-Regular.ttf',
5684
                    'FontSize' => $fontSize,
5685
                ]
5686
            );
5687
5688
            /* Do not write the chart title */
5689
            /* Define the chart area */
5690
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
5691
5692
            /* Draw the scale */
5693
            $scaleSettings = [
5694
                'GridR' => 200,
5695
                'GridG' => 200,
5696
                'GridB' => 200,
5697
                'DrawSubTicks' => true,
5698
                'CycleBackground' => true,
5699
                'Mode' => SCALE_MODE_MANUAL,
5700
                'ManualScale' => [
5701
                    '0' => [
5702
                        'Min' => 0,
5703
                        'Max' => 100,
5704
                    ],
5705
                ],
5706
            ];
5707
            $myPicture->drawScale($scaleSettings);
5708
5709
            /* Turn on shadow computing */
5710
            $myPicture->setShadow(
5711
                true,
5712
                [
5713
                    'X' => 1,
5714
                    'Y' => 1,
5715
                    'R' => 0,
5716
                    'G' => 0,
5717
                    'B' => 0,
5718
                    'Alpha' => 10,
5719
                ]
5720
            );
5721
5722
            /* Draw the chart */
5723
            $myPicture->setShadow(
5724
                true,
5725
                [
5726
                    'X' => 1,
5727
                    'Y' => 1,
5728
                    'R' => 0,
5729
                    'G' => 0,
5730
                    'B' => 0,
5731
                    'Alpha' => 10,
5732
                ]
5733
            );
5734
            $settings = [
5735
                'DisplayValues' => true,
5736
                'DisplaySize' => $fontSize,
5737
                'DisplayR' => 0,
5738
                'DisplayG' => 0,
5739
                'DisplayB' => 0,
5740
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5741
                'Gradient' => false,
5742
                'Surrounding' => 5,
5743
                'InnerSurrounding' => 5,
5744
            ];
5745
            $myPicture->drawStackedBarChart($settings);
5746
5747
            /* Save and write in cache */
5748
            $myCache->writeToCache($chartHash, $myPicture);
5749
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5750
            $myCache->saveFromCache($chartHash, $imgPath);
5751
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5752
        }
5753
5754
        return $imgPath;
5755
    }
5756
5757
    /**
5758
     * Generates a big graph with the number of best results.
5759
     *
5760
     * @param	array
5761
     */
5762
    public static function generate_exercise_result_graph($attempts)
5763
    {
5764
        $exercise_title = strip_tags($attempts['title']);
5765
        $attempts = $attempts['data'];
5766
        $my_exercise_result_array = $exercise_result = [];
5767
        if (empty($attempts)) {
5768
            return null;
5769
        }
5770
        foreach ($attempts as $attempt) {
5771
            if (api_get_user_id() == $attempt['exe_user_id']) {
5772
                if ($attempt['exe_weighting'] != 0) {
5773
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5774
                }
5775
            } else {
5776
                if ($attempt['exe_weighting'] != 0) {
5777
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5778
                }
5779
            }
5780
        }
5781
5782
        //Getting best result
5783
        rsort($my_exercise_result_array);
5784
        $my_exercise_result = 0;
5785
        if (isset($my_exercise_result_array[0])) {
5786
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5787
        }
5788
5789
        $max = 100;
5790
        $pieces = 5;
5791
        $part = round($max / $pieces);
5792
        $x_axis = [];
5793
        $final_array = [];
5794
        $my_final_array = [];
5795
5796
        for ($i = 1; $i <= $pieces; $i++) {
5797
            $sum = 1;
5798
            if ($i == 1) {
5799
                $sum = 0;
5800
            }
5801
            $min = ($i - 1) * $part + $sum;
5802
            $max = ($i) * $part;
5803
            $x_axis[] = $min." - ".$max;
5804
            $count = 0;
5805
            foreach ($exercise_result as $result) {
5806
                $percentage = $result * 100;
5807
                if ($percentage >= $min && $percentage <= $max) {
5808
                    $count++;
5809
                }
5810
            }
5811
            $final_array[] = $count;
5812
5813
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5814
                $my_final_array[] = 1;
5815
            } else {
5816
                $my_final_array[] = 0;
5817
            }
5818
        }
5819
5820
        //Fix to remove the data of the user with my data
5821
5822
        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...
5823
            if (!empty($my_final_array[$i])) {
5824
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5825
                $final_array[$i] = 0;
5826
            }
5827
        }
5828
5829
        // Dataset definition
5830
        $dataSet = new pData();
5831
        $dataSet->addPoints($final_array, 'Serie1');
5832
        $dataSet->addPoints($my_final_array, 'Serie2');
5833
        $dataSet->addPoints($x_axis, 'Serie3');
5834
5835
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
5836
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
5837
        $dataSet->setAbscissa('Serie3');
5838
5839
        $dataSet->setXAxisName(get_lang('Score'));
5840
        $dataSet->normalize(100, "%");
5841
5842
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5843
5844
        // Cache definition
5845
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5846
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5847
        $chartHash = $myCache->getHash($dataSet);
5848
5849
        if ($myCache->isInCache($chartHash)) {
5850
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5851
            $myCache->saveFromCache($chartHash, $imgPath);
5852
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5853
        } else {
5854
            /* Create the pChart object */
5855
            $widthSize = 480;
5856
            $heightSize = 250;
5857
            $fontSize = 8;
5858
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5859
5860
            /* Turn of Antialiasing */
5861
            $myPicture->Antialias = false;
5862
5863
            /* Add a border to the picture */
5864
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
5865
5866
            /* Set the default font */
5867
            $myPicture->setFontProperties(
5868
                [
5869
                    'FontName' => api_get_path(
5870
                            SYS_FONTS_PATH
5871
                        ).'opensans/OpenSans-Regular.ttf',
5872
                    'FontSize' => 10,
5873
                ]
5874
            );
5875
5876
            /* Write the chart title */
5877
            $myPicture->drawText(
5878
                250,
5879
                20,
5880
                $exercise_title,
5881
                [
5882
                    'FontSize' => 12,
5883
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
5884
                ]
5885
            );
5886
5887
            /* Define the chart area */
5888
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
5889
5890
            /* Draw the scale */
5891
            $scaleSettings = [
5892
                'GridR' => 200,
5893
                'GridG' => 200,
5894
                'GridB' => 200,
5895
                'DrawSubTicks' => true,
5896
                'CycleBackground' => true,
5897
                'Mode' => SCALE_MODE_MANUAL,
5898
                'ManualScale' => [
5899
                    '0' => [
5900
                        'Min' => 0,
5901
                        'Max' => 100,
5902
                    ],
5903
                ],
5904
            ];
5905
            $myPicture->drawScale($scaleSettings);
5906
5907
            /* Turn on shadow computing */
5908
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
5909
5910
            /* Draw the chart */
5911
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
5912
            $settings = [
5913
                'DisplayValues' => true,
5914
                'DisplaySize' => $fontSize,
5915
                'DisplayR' => 0,
5916
                'DisplayG' => 0,
5917
                'DisplayB' => 0,
5918
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5919
                'Gradient' => false,
5920
                'Surrounding' => 30,
5921
                'InnerSurrounding' => 25,
5922
            ];
5923
            $myPicture->drawStackedBarChart($settings);
5924
5925
            $legendSettings = [
5926
                'Mode' => LEGEND_HORIZONTAL,
5927
                'Style' => LEGEND_NOBORDER,
5928
            ];
5929
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
5930
5931
            /* Write and save into cache */
5932
            $myCache->writeToCache($chartHash, $myPicture);
5933
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5934
            $myCache->saveFromCache($chartHash, $imgPath);
5935
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5936
        }
5937
5938
        return $imgPath;
5939
    }
5940
5941
    /**
5942
     * @param FormValidator $form
5943
     *
5944
     * @return mixed
5945
     */
5946
    public static function setUserSearchForm($form)
5947
    {
5948
        global $_configuration;
5949
        $form->addElement('text', 'keyword', get_lang('Keyword'));
5950
        $form->addElement(
5951
            'select',
5952
            'active',
5953
            get_lang('Status'),
5954
            [1 => get_lang('Active'), 0 => get_lang('Inactive')]
5955
        );
5956
5957
        $form->addElement(
5958
            'select',
5959
            'sleeping_days',
5960
            get_lang('InactiveDays'),
5961
            [
5962
                '',
5963
                1 => 1,
5964
                5 => 5,
5965
                15 => 15,
5966
                30 => 30,
5967
                60 => 60,
5968
                90 => 90,
5969
                120 => 120,
5970
            ]
5971
        );
5972
5973
        $form->addButtonSearch(get_lang('Search'));
5974
5975
        return $form;
5976
    }
5977
5978
    /**
5979
     * Get the progress of a exercise.
5980
     *
5981
     * @param int    $sessionId  The session ID (session.id)
5982
     * @param int    $courseId   The course ID (course.id)
5983
     * @param int    $exerciseId The quiz ID (c_quiz.id)
5984
     * @param string $date_from
5985
     * @param string $date_to
5986
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
5987
     *
5988
     * @return array An array with the data of exercise(s) progress
5989
     */
5990
    public static function get_exercise_progress(
5991
        $sessionId = 0,
5992
        $courseId = 0,
5993
        $exerciseId = 0,
5994
        $date_from = null,
5995
        $date_to = null,
5996
        $options = []
5997
    ) {
5998
        $sessionId = intval($sessionId);
5999
        $courseId = intval($courseId);
6000
        $exerciseId = intval($exerciseId);
6001
        $date_from = Database::escape_string($date_from);
6002
        $date_to = Database::escape_string($date_to);
6003
        /*
6004
         * This method gets the data by blocks, as previous attempts at one single
6005
         * query made it take ages. The logic of query division is described below
6006
         */
6007
        // Get tables names
6008
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6009
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6010
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6011
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6012
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6013
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6014
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6015
6016
        $sessions = [];
6017
        $courses = [];
6018
        // if session ID is defined but course ID is empty, get all the courses
6019
        // from that session
6020
        if (!empty($sessionId) && empty($courseId)) {
6021
            // $courses is an array of course int id as index and course details hash as value
6022
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6023
            $sessions[$sessionId] = api_get_session_info($sessionId);
6024
        } elseif (empty($sessionId) && !empty($courseId)) {
6025
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6026
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
6027
            $course = api_get_course_info_by_id($courseId);
6028
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6029
            $courses[$courseId] = $course;
6030
            foreach ($sessionsTemp as $sessionItem) {
6031
                $sessions[$sessionItem['id']] = $sessionItem;
6032
            }
6033
        } elseif (!empty($courseId) && !empty($sessionId)) {
6034
            //none is empty
6035
            $course = api_get_course_info_by_id($courseId);
6036
            $courses[$courseId] = [$course['code']];
6037
            $courses[$courseId]['code'] = $course['code'];
6038
            $sessions[$sessionId] = api_get_session_info($sessionId);
6039
        } else {
6040
            //both are empty, not enough data, return an empty array
6041
            return [];
6042
        }
6043
        // Now we have two arrays of courses and sessions with enough data to proceed
6044
        // If no course could be found, we shouldn't return anything.
6045
        // Sessions can be empty (then we only return the pure-course-context results)
6046
        if (count($courses) < 1) {
6047
            return [];
6048
        }
6049
6050
        $data = [];
6051
        // The following loop is less expensive than what it seems:
6052
        // - if a course was defined, then we only loop through sessions
6053
        // - if a session was defined, then we only loop through courses
6054
        // - if a session and a course were defined, then we only loop once
6055
        foreach ($courses as $courseIdx => $courseData) {
6056
            $where = '';
6057
            $whereParams = [];
6058
            $whereSessionParams = '';
6059
            if (count($sessions > 0)) {
6060
                foreach ($sessions as $sessionIdx => $sessionData) {
6061
                    if (!empty($sessionIdx)) {
6062
                        $whereSessionParams .= $sessionIdx.',';
6063
                    }
6064
                }
6065
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6066
            }
6067
6068
            if (!empty($exerciseId)) {
6069
                $exerciseId = intval($exerciseId);
6070
                $where .= ' AND q.id = %d ';
6071
                $whereParams[] = $exerciseId;
6072
            }
6073
6074
            /*
6075
             * This feature has been disabled for now, to avoid having to
6076
             * join two very large tables
6077
            //2 = show all questions (wrong and correct answered)
6078
            if ($answer != 2) {
6079
                $answer = intval($answer);
6080
                //$where .= ' AND qa.correct = %d';
6081
                //$whereParams[] = $answer;
6082
            }
6083
            */
6084
6085
            $limit = '';
6086
            if (!empty($options['limit'])) {
6087
                $limit = " LIMIT ".$options['limit'];
6088
            }
6089
6090
            if (!empty($options['where'])) {
6091
                $where .= ' AND '.Database::escape_string($options['where']);
6092
            }
6093
6094
            $order = '';
6095
            if (!empty($options['order'])) {
6096
                $order = " ORDER BY ".$options['order'];
6097
            }
6098
6099
            if (!empty($date_to) && !empty($date_from)) {
6100
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6101
            }
6102
6103
            $sql = "SELECT
6104
                te.session_id,
6105
                ta.id as attempt_id,
6106
                te.exe_user_id as user_id,
6107
                te.exe_id as exercise_attempt_id,
6108
                ta.question_id,
6109
                ta.answer as answer_id,
6110
                ta.tms as time,
6111
                te.exe_exo_id as quiz_id,
6112
                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
6113
                q.title as quiz_title,
6114
                qq.description as description
6115
                FROM $ttrack_exercises te
6116
                INNER JOIN $ttrack_attempt ta
6117
                ON ta.exe_id = te.exe_id
6118
                INNER JOIN $tquiz q
6119
                ON q.id = te.exe_exo_id
6120
                INNER JOIN $tquiz_rel_question rq
6121
                ON rq.exercice_id = q.id AND rq.c_id = q.c_id
6122
                INNER JOIN $tquiz_question qq
6123
                ON
6124
                    qq.id = rq.question_id AND
6125
                    qq.c_id = rq.c_id AND
6126
                    qq.position = rq.question_order AND
6127
                    ta.question_id = rq.question_id
6128
                WHERE
6129
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6130
                    AND q.c_id = $courseIdx
6131
                    $where $order $limit";
6132
            $sql_query = vsprintf($sql, $whereParams);
6133
6134
            // Now browse through the results and get the data
6135
            $rs = Database::query($sql_query);
6136
            $userIds = [];
6137
            $questionIds = [];
6138
            $answerIds = [];
6139
            while ($row = Database::fetch_array($rs)) {
6140
                //only show if exercise is visible
6141
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6142
                    $userIds[$row['user_id']] = $row['user_id'];
6143
                    $questionIds[$row['question_id']] = $row['question_id'];
6144
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6145
                    $row['session'] = $sessions[$row['session_id']];
6146
                    $data[] = $row;
6147
                }
6148
            }
6149
            // Now fill questions data. Query all questions and answers for this test to avoid
6150
            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
6151
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
6152
                            FROM $tquiz_question tq, $tquiz_answer tqa
6153
                            WHERE
6154
                                tqa.question_id = tq.id AND
6155
                                tqa.c_id = tq.c_id AND
6156
                                tq.c_id = $courseIdx AND
6157
                                tq.id IN (".implode(',', $questionIds).")";
6158
6159
            $resQuestions = Database::query($sqlQuestions);
6160
            $answer = [];
6161
            $question = [];
6162
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6163
                $questionId = $rowQuestion['question_id'];
6164
                $answerId = $rowQuestion['answer_id'];
6165
                $answer[$questionId][$answerId] = [
6166
                    'position' => $rowQuestion['position'],
6167
                    'question' => $rowQuestion['question'],
6168
                    'answer' => $rowQuestion['answer'],
6169
                    'correct' => $rowQuestion['correct'],
6170
                ];
6171
                $question[$questionId]['question'] = $rowQuestion['question'];
6172
            }
6173
6174
            // Now fill users data
6175
            $sqlUsers = "SELECT user_id, username, lastname, firstname
6176
                         FROM $tuser
6177
                         WHERE user_id IN (".implode(',', $userIds).")";
6178
            $resUsers = Database::query($sqlUsers);
6179
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6180
                $users[$rowUser['user_id']] = $rowUser;
6181
            }
6182
6183
            foreach ($data as $id => $row) {
6184
                $rowQuestId = $row['question_id'];
6185
                $rowAnsId = $row['answer_id'];
6186
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
6187
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
6188
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6189
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6190
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6191
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
6192
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6193
                $data[$id]['question_id'] = $rowQuestId;
6194
                $data[$id]['description'] = $row['description'];
6195
            }
6196
6197
            /*
6198
            The minimum expected array structure at the end is:
6199
            attempt_id,
6200
            session name,
6201
            exercise_id,
6202
            quiz_title,
6203
            username,
6204
            lastname,
6205
            firstname,
6206
            time,
6207
            question_id,
6208
            question,
6209
            answer,
6210
            */
6211
        }
6212
6213
        return $data;
6214
    }
6215
6216
    /**
6217
     * @param string              $tool
6218
     * @param sessionEntity |null $session Optional
6219
     *
6220
     * @throws \Doctrine\ORM\NonUniqueResultException
6221
     *
6222
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
6223
     */
6224
    public static function getLastStudentPublication(
6225
        User $user,
6226
        $tool,
6227
        Course $course,
6228
        SessionEntity $session = null
6229
    ) {
6230
        return Database::getManager()
6231
            ->createQuery("
6232
                SELECT csp
6233
                FROM ChamiloCourseBundle:CStudentPublication csp
6234
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6235
                    WITH (
6236
                        csp.iid = cip.ref AND
6237
                        csp.session = cip.session AND
6238
                        csp.cId = cip.course AND
6239
                        csp.userId = cip.lasteditUserId
6240
                    )
6241
                WHERE
6242
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6243
                ORDER BY csp.iid DESC
6244
            ")
6245
            ->setMaxResults(1)
6246
            ->setParameters([
6247
                'tool' => $tool,
6248
                'session' => $session,
6249
                'course' => $course,
6250
                'user' => $user,
6251
            ])
6252
            ->getOneOrNullResult();
6253
    }
6254
6255
    /**
6256
     * Get the HTML code for show a block with the achieved user skill on course/session.
6257
     *
6258
     * @param int  $userId
6259
     * @param int  $courseId
6260
     * @param int  $sessionId
6261
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6262
     *
6263
     * @return string
6264
     */
6265
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6266
    {
6267
        if (Skill::isAllowed($userId, false) === false && $forceView == false) {
6268
            return '';
6269
        }
6270
        $skillManager = new Skill();
6271
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6272
6273
        return $html;
6274
    }
6275
6276
    /**
6277
     * @param int $userId
6278
     * @param int $courseId
6279
     * @param int $sessionId
6280
     *
6281
     * @return array
6282
     */
6283
    public static function getCalculateTime($userId, $courseId, $sessionId)
6284
    {
6285
        $userId = (int) $userId;
6286
        $courseId = (int) $courseId;
6287
        $sessionId = (int) $sessionId;
6288
6289
        if (empty($userId) || empty($courseId)) {
6290
            return [];
6291
        }
6292
6293
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6294
                FROM track_e_access_complete
6295
                WHERE
6296
                    user_id = $userId AND
6297
                    c_id = $courseId AND
6298
                    session_id = $sessionId AND
6299
                    login_as = 0
6300
                ORDER BY date_reg ASC
6301
                LIMIT 1";
6302
        $rs = Database::query($sql);
6303
6304
        $firstConnection = '';
6305
        $lastConnection = '';
6306
        if (Database::num_rows($rs) > 0) {
6307
            $value = Database::fetch_array($rs);
6308
            $firstConnection = $value['min'];
6309
            $lastConnection = $value['max'];
6310
        }
6311
6312
        $sql = "SELECT * FROM track_e_access_complete
6313
                WHERE
6314
                    user_id = $userId AND
6315
                    c_id = $courseId AND
6316
                    session_id = $sessionId AND
6317
                    login_as = 0 AND current_id <> 0";
6318
6319
        $res = Database::query($sql);
6320
        $reg = [];
6321
        while ($row = Database::fetch_assoc($res)) {
6322
            $reg[$row['id']] = $row;
6323
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6324
        }
6325
6326
        $sessions = [];
6327
        foreach ($reg as $key => $value) {
6328
            $sessions[$value['current_id']][$value['tool']][] = $value;
6329
        }
6330
6331
        $quizTime = 0;
6332
        $result = [];
6333
        $totalTime = 0;
6334
        $lpTime = [];
6335
        $lpDetailTime = [];
6336
        foreach ($sessions as $listPerTool) {
6337
            $min = 0;
6338
            $max = 0;
6339
            $sessionDiff = 0;
6340
            foreach ($listPerTool as $tool => $results) {
6341
                $beforeItem = [];
6342
                foreach ($results as $item) {
6343
                    if (empty($beforeItem)) {
6344
                        $beforeItem = $item;
6345
                        if (empty($min)) {
6346
                            $min = $item['date_reg'];
6347
                        }
6348
6349
                        if (empty($max)) {
6350
                            $max = $item['date_reg'];
6351
                        }
6352
                        continue;
6353
                    }
6354
6355
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6356
                    if ($item['date_reg'] > $max) {
6357
                        $max = $item['date_reg'];
6358
                    }
6359
6360
                    if (empty($min)) {
6361
                        $min = $item['date_reg'];
6362
                    }
6363
6364
                    if ($item['date_reg'] < $min) {
6365
                        $min = $item['date_reg'];
6366
                    }
6367
6368
                    switch ($tool) {
6369
                        case TOOL_AGENDA:
6370
                        case TOOL_FORUM:
6371
                        case TOOL_ANNOUNCEMENT:
6372
                        case TOOL_COURSE_DESCRIPTION:
6373
                        case TOOL_SURVEY:
6374
                        case TOOL_NOTEBOOK:
6375
                        case TOOL_GRADEBOOK:
6376
                        case TOOL_DROPBOX:
6377
                        case 'Reports':
6378
                        case 'Videoconference':
6379
                        case TOOL_LINK:
6380
                        case TOOL_CHAT:
6381
                        case 'course-main':
6382
                            if (!isset($result[$tool])) {
6383
                                $result[$tool] = 0;
6384
                            }
6385
                            $result[$tool] += $partialTime;
6386
                            break;
6387
                        case TOOL_LEARNPATH:
6388
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6389
                                break;
6390
                            }
6391
                            if (!isset($lpTime[$item['tool_id']])) {
6392
                                $lpTime[$item['tool_id']] = 0;
6393
                            }
6394
6395
                            // Saving the attempt id "action_details"
6396
                            if (!empty($item['tool_id'])) {
6397
                                if (!empty($item['tool_id_detail'])) {
6398
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6399
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6400
                                    }
6401
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6402
                                }
6403
                                $lpTime[$item['tool_id']] += $partialTime;
6404
                            }
6405
                            break;
6406
                        case TOOL_QUIZ:
6407
                            if (!isset($lpTime[$item['action_details']])) {
6408
                                $lpTime[$item['action_details']] = 0;
6409
                            }
6410
                            if ($beforeItem['action'] === 'learnpath_id') {
6411
                                $lpTime[$item['action_details']] += $partialTime;
6412
                            } else {
6413
                                $quizTime += $partialTime;
6414
                            }
6415
                            break;
6416
                    }
6417
                    $beforeItem = $item;
6418
                }
6419
            }
6420
6421
            $sessionDiff += $max - $min;
6422
            if ($sessionDiff > 0) {
6423
                $totalTime += $sessionDiff;
6424
            }
6425
        }
6426
6427
        $totalLp = 0;
6428
        foreach ($lpTime as $value) {
6429
            $totalLp += $value;
6430
        }
6431
6432
        $result['learnpath_detailed'] = $lpDetailTime;
6433
        $result[TOOL_LEARNPATH] = $lpTime;
6434
        $result[TOOL_QUIZ] = $quizTime;
6435
        $result['total_learnpath'] = $totalLp;
6436
        $result['total_time'] = $totalTime;
6437
        $result['number_connections'] = count($sessions);
6438
        $result['first'] = $firstConnection;
6439
        $result['last'] = $lastConnection;
6440
6441
        return $result;
6442
    }
6443
6444
    /**
6445
     * Gets the IP of a given user, using the last login before the given date.
6446
     *
6447
     * @param int User ID
6448
     * @param string Datetime
6449
     * @param bool Whether to return the IP as a link or just as an IP
6450
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6451
     *
6452
     * @return string IP address (or false on error)
6453
     * @assert (0,0) === false
6454
     */
6455
    public static function get_ip_from_user_event(
6456
        $user_id,
6457
        $event_date,
6458
        $return_as_link = false,
6459
        $body_replace = null
6460
    ) {
6461
        if (empty($user_id) || empty($event_date)) {
6462
            return false;
6463
        }
6464
        $user_id = intval($user_id);
6465
        $event_date = Database::escape_string($event_date);
6466
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6467
        $sql_ip = "SELECT login_date, user_ip
6468
                   FROM $table_login
6469
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6470
                   ORDER BY login_date DESC LIMIT 1";
6471
        $ip = '';
6472
        $res_ip = Database::query($sql_ip);
6473
        if ($res_ip !== false && Database::num_rows($res_ip) > 0) {
6474
            $row_ip = Database::fetch_row($res_ip);
6475
            if ($return_as_link) {
6476
                $ip = Display::url(
6477
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6478
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6479
                    ['title' => get_lang('TraceIP'), 'target' => '_blank']
6480
                );
6481
            } else {
6482
                $ip = $row_ip[1];
6483
            }
6484
        }
6485
6486
        return $ip;
6487
    }
6488
6489
    /**
6490
     * @param int   $userId
6491
     * @param array $courseInfo
6492
     * @param int   $sessionId
6493
     *
6494
     * @return array
6495
     */
6496
    public static function getToolInformation(
6497
        $userId,
6498
        $courseInfo,
6499
        $sessionId = 0
6500
    ) {
6501
        $csvContent = [];
6502
        $courseToolInformation = '';
6503
        $headerTool = [
6504
            [get_lang('Title')],
6505
            [get_lang('CreatedAt')],
6506
            [get_lang('UpdatedAt')],
6507
        ];
6508
6509
        $headerListForCSV = [];
6510
        foreach ($headerTool as $item) {
6511
            $headerListForCSV[] = $item[0];
6512
        }
6513
6514
        $courseForumInformationArray = getForumCreatedByUser(
6515
            $userId,
6516
            $courseInfo,
6517
            $sessionId
6518
        );
6519
6520
        if (!empty($courseForumInformationArray)) {
6521
            $csvContent[] = [];
6522
            $csvContent[] = [get_lang('Forums')];
6523
            $csvContent[] = $headerListForCSV;
6524
            foreach ($courseForumInformationArray as $row) {
6525
                $csvContent[] = $row;
6526
            }
6527
6528
            $courseToolInformation .= Display::page_subheader2(
6529
                get_lang('Forums')
6530
            );
6531
            $courseToolInformation .= Display::return_sortable_table(
6532
                $headerTool,
6533
                $courseForumInformationArray
6534
            );
6535
        }
6536
6537
        $courseWorkInformationArray = getWorkCreatedByUser(
6538
            $userId,
6539
            $courseInfo['real_id'],
6540
            $sessionId
6541
        );
6542
6543
        if (!empty($courseWorkInformationArray)) {
6544
            $csvContent[] = null;
6545
            $csvContent[] = [get_lang('Works')];
6546
            $csvContent[] = $headerListForCSV;
6547
6548
            foreach ($courseWorkInformationArray as $row) {
6549
                $csvContent[] = $row;
6550
            }
6551
            $csvContent[] = null;
6552
6553
            $courseToolInformation .= Display::page_subheader2(
6554
                get_lang('Works')
6555
            );
6556
            $courseToolInformation .= Display::return_sortable_table(
6557
                $headerTool,
6558
                $courseWorkInformationArray
6559
            );
6560
        }
6561
6562
        $courseToolInformationTotal = null;
6563
        if (!empty($courseToolInformation)) {
6564
            $sessionTitle = null;
6565
            if (!empty($sessionId)) {
6566
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6567
            }
6568
6569
            $courseToolInformationTotal .= Display::page_subheader(
6570
                $courseInfo['title'].$sessionTitle
6571
            );
6572
            $courseToolInformationTotal .= $courseToolInformation;
6573
        }
6574
6575
        return [
6576
            'array' => $csvContent,
6577
            'html' => $courseToolInformationTotal,
6578
        ];
6579
    }
6580
6581
    /**
6582
     * @param int $sessionId
6583
     *
6584
     * @return bool
6585
     */
6586
    public static function isAllowToTrack($sessionId)
6587
    {
6588
        return
6589
            api_is_platform_admin(true, true) ||
6590
            SessionManager::user_is_general_coach(api_get_user_id(), $sessionId) ||
6591
            api_is_allowed_to_create_course() ||
6592
            api_is_course_tutor() ||
6593
            api_is_course_admin();
6594
    }
6595
6596
    public static function getCourseLpProgress($userId, $sessionId)
6597
    {
6598
        $controller = new IndexManager(get_lang('MyCourses'));
6599
        $data = $controller->returnCoursesAndSessions($userId);
6600
        $courseList = $data['courses'];
6601
        $result = [];
6602
        if ($courseList) {
6603
            //$counter = 1;
6604
            foreach ($courseList as $course) {
6605
                $courseId = $course['course_id'];
6606
                $courseInfo = api_get_course_info_by_id($courseId);
6607
                if (empty($courseInfo)) {
6608
                    continue;
6609
                }
6610
                $courseCode = $courseInfo['code'];
6611
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
6612
6613
                // total progress
6614
                $list = new LearnpathList(
6615
                    $userId,
6616
                     $courseInfo,
6617
                    0,
6618
                    'lp.publicatedOn ASC',
6619
                    true,
6620
                    null,
6621
                    true
6622
                );
6623
6624
                $list = $list->get_flat_list();
6625
                $totalProgress = 0;
6626
                $totalTime = 0;
6627
                if (!empty($list)) {
6628
                    foreach ($list as $lp_id => $learnpath) {
6629
                        if (!$learnpath['lp_visibility']) {
6630
                            continue;
6631
                        }
6632
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
6633
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
6634
                        if ($lpProgress == 100) {
6635
                            if (!empty($time)) {
6636
                                $timeInMinutes = $time / 60;
6637
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
6638
                                if ($timeInMinutes >= $min) {
6639
                                    $totalProgress++;
6640
                                }
6641
                            }
6642
                        }
6643
                        $totalTime += $time;
6644
                    }
6645
6646
                    if (!empty($totalProgress)) {
6647
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
6648
                    }
6649
                }
6650
6651
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
6652
6653
                $result[] = [
6654
                    'module' => $courseInfo['name'],
6655
                    'progress' => $progress,
6656
                    'qualification' => $totalProgress,
6657
                    'activeTime' => $totalTime,
6658
                ];
6659
            }
6660
        }
6661
6662
        return $result;
6663
    }
6664
6665
    /**
6666
     * @param int $userId
6667
     * @param int $courseId
6668
     * @param int $sessionId
6669
     *
6670
     * @return int
6671
     */
6672
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
6673
    {
6674
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6675
        $sessionCondition = api_get_session_condition($sessionId);
6676
        $courseId = (int) $courseId;
6677
        $userId = (int) $userId;
6678
6679
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
6680
                FROM $tblTrackCourseAccess
6681
                WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
6682
6683
        $result = Database::fetch_assoc(Database::query($sql));
6684
6685
        return (int) $result['c'];
6686
    }
6687
6688
    public static function processUserDataMove(
6689
        $user_id,
6690
        $course_info,
6691
        $origin_session_id,
6692
        $new_session_id,
6693
        $update_database,
6694
        $debug = false
6695
    ) {
6696
        // Begin with the import process
6697
        $origin_course_code = $course_info['code'];
6698
        $course_id = $course_info['real_id'];
6699
        $user_id = (int) $user_id;
6700
        $origin_session_id = (int) $origin_session_id;
6701
        $new_session_id = (int) $new_session_id;
6702
        $session = api_get_session_entity($new_session_id);
6703
        $em = Database::getManager();
6704
6705
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6706
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6707
        $attemptRecording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
6708
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6709
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
6710
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
6711
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
6712
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
6713
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
6714
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
6715
6716
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
6717
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
6718
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
6719
6720
        //1. track_e_exercises
6721
        //ORIGINAL COURSE
6722
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
6723
                WHERE c_id = $course_id AND  session_id = $origin_session_id AND exe_user_id = $user_id ";
6724
        $res = Database::query($sql);
6725
        $list = [];
6726
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6727
            $list[$row['exe_id']] = $row;
6728
        }
6729
6730
        $result_message = [];
6731
        $result_message_compare = [];
6732
        if (!empty($list)) {
6733
            foreach ($list as $exe_id => $data) {
6734
                if ($update_database) {
6735
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
6736
                    Database::query($sql);
6737
6738
                    $sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
6739
                    Database::query($sql);
6740
6741
                    $sql = "UPDATE $attemptRecording SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
6742
                    Database::query($sql);
6743
6744
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
6745
                        $result_message[$TABLETRACK_EXERCICES] = 0;
6746
                    }
6747
                    $result_message[$TABLETRACK_EXERCICES]++;
6748
                } else {
6749
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
6750
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
6751
                    } else {
6752
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
6753
                    }
6754
                }
6755
            }
6756
        }
6757
6758
        // DESTINY COURSE
6759
        if (!$update_database) {
6760
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
6761
                    WHERE
6762
                        c_id = $course_id AND
6763
                        session_id = $new_session_id AND
6764
                        exe_user_id = $user_id ";
6765
            $res = Database::query($sql);
6766
            $list = [];
6767
            while ($row = Database::fetch_array($res, 'ASSOC')) {
6768
                $list[$row['exe_id']] = $row;
6769
            }
6770
6771
            if (!empty($list)) {
6772
                foreach ($list as $exe_id => $data) {
6773
                    if ($update_database) {
6774
                        $sql = "UPDATE $TABLETRACK_EXERCICES
6775
                                SET session_id = '$new_session_id'
6776
                                WHERE exe_id = $exe_id";
6777
                        Database::query($sql);
6778
                        $result_message[$TABLETRACK_EXERCICES]++;
6779
                    } else {
6780
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
6781
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
6782
                        } else {
6783
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
6784
                        }
6785
                    }
6786
                }
6787
            }
6788
        }
6789
6790
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
6791
        // Nothing to do because there are not relationship with a session
6792
        // 3. track_e_course_access
6793
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
6794
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
6795
        $res = Database::query($sql);
6796
        $list = [];
6797
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6798
            $list[$row['course_access_id']] = $row;
6799
        }
6800
6801
        if (!empty($list)) {
6802
            foreach ($list as $id => $data) {
6803
                if ($update_database) {
6804
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
6805
                            SET session_id = $new_session_id
6806
                            WHERE course_access_id = $id";
6807
                    if ($debug) {
6808
                        echo $sql;
6809
                    }
6810
                    Database::query($sql);
6811
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
6812
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
6813
                    }
6814
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
6815
                }
6816
            }
6817
        }
6818
6819
        // 4. track_e_lastaccess
6820
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
6821
                WHERE
6822
                    c_id = $course_id AND
6823
                    access_session_id = $origin_session_id AND
6824
                    access_user_id = $user_id ";
6825
        $res = Database::query($sql);
6826
        $list = [];
6827
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6828
            $list[] = $row['access_id'];
6829
        }
6830
6831
        if (!empty($list)) {
6832
            foreach ($list as $id) {
6833
                if ($update_database) {
6834
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
6835
                            SET access_session_id = $new_session_id
6836
                            WHERE access_id = $id";
6837
                    if ($debug) {
6838
                        echo $sql;
6839
                    }
6840
                    Database::query($sql);
6841
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
6842
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
6843
                    }
6844
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
6845
                }
6846
            }
6847
        }
6848
6849
        // 5. lp_item_view
6850
        // CHECK ORIGIN
6851
        $sql = "SELECT * FROM $TBL_LP_VIEW
6852
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
6853
        $res = Database::query($sql);
6854
6855
        // Getting the list of LPs in the new session
6856
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
6857
        $flat_list = $lp_list->get_flat_list();
6858
        $list = [];
6859
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6860
            // Checking if the LP exist in the new session
6861
            //if (in_array($row['lp_id'], array_keys($flat_list))) {
6862
            $list[$row['id']] = $row;
6863
            //}
6864
        }
6865
6866
        if (!empty($list)) {
6867
            foreach ($list as $id => $data) {
6868
                if ($update_database) {
6869
                    $sql = "UPDATE $TBL_LP_VIEW
6870
                            SET session_id = $new_session_id
6871
                            WHERE c_id = $course_id AND id = $id ";
6872
                    if ($debug) {
6873
                        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...
6874
                    }
6875
                    $res = Database::query($sql);
6876
                    if ($debug) {
6877
                        var_dump($res);
6878
                    }
6879
                    if (!isset($result_message[$TBL_LP_VIEW])) {
6880
                        $result_message[$TBL_LP_VIEW] = 0;
6881
                    }
6882
                    $result_message[$TBL_LP_VIEW]++;
6883
                } else {
6884
                    // Getting all information of that lp_item_id
6885
                    $score = self::get_avg_student_score(
6886
                        $user_id,
6887
                        $origin_course_code,
6888
                        [$data['lp_id']],
6889
                        $origin_session_id
6890
                    );
6891
                    $progress = self::get_avg_student_progress(
6892
                        $user_id,
6893
                        $origin_course_code,
6894
                        [$data['lp_id']],
6895
                        $origin_session_id
6896
                    );
6897
                    $result_message['LP_VIEW'][$data['lp_id']] = [
6898
                        'score' => $score,
6899
                        'progress' => $progress,
6900
                    ];
6901
                }
6902
            }
6903
        }
6904
6905
        // Check destination.
6906
        if (!$update_database) {
6907
            $sql = "SELECT * FROM $TBL_LP_VIEW
6908
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
6909
            $res = Database::query($sql);
6910
6911
            // Getting the list of LPs in the new session
6912
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
6913
            $flat_list = $lp_list->get_flat_list();
6914
6915
            $list = [];
6916
            while ($row = Database::fetch_array($res, 'ASSOC')) {
6917
                //Checking if the LP exist in the new session
6918
                //if (in_array($row['lp_id'], array_keys($flat_list))) {
6919
                $list[$row['id']] = $row;
6920
                //}
6921
            }
6922
6923
            if (!empty($list)) {
6924
                foreach ($list as $id => $data) {
6925
                    // Getting all information of that lp_item_id
6926
                    $score = self::get_avg_student_score(
6927
                        $user_id,
6928
                        $origin_course_code,
6929
                        [$data['lp_id']],
6930
                        $new_session_id
6931
                    );
6932
                    $progress = self::get_avg_student_progress(
6933
                        $user_id,
6934
                        $origin_course_code,
6935
                        [$data['lp_id']],
6936
                        $new_session_id
6937
                    );
6938
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
6939
                        'score' => $score,
6940
                        'progress' => $progress,
6941
                    ];
6942
                }
6943
            }
6944
        }
6945
6946
        // 6. Agenda
6947
        // calendar_event_attachment no problems no session_id
6948
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
6949
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
6950
        $res = Database::query($sql);
6951
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6952
            $id = $row['ref'];
6953
            if ($update_database) {
6954
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id ";
6955
                if ($debug) {
6956
                    var_dump($sql);
6957
                }
6958
                $res_update = Database::query($sql);
6959
                if ($debug) {
6960
                    var_dump($res_update);
6961
                }
6962
                if (!isset($result_message['agenda'])) {
6963
                    $result_message['agenda'] = 0;
6964
                }
6965
                $result_message['agenda']++;
6966
            }
6967
        }
6968
6969
        // 7. Forum ?? So much problems when trying to import data
6970
        // 8. Student publication - Works
6971
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
6972
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
6973
        if ($debug) {
6974
            echo $sql;
6975
        }
6976
        $res = Database::query($sql);
6977
        while ($row = Database::fetch_array($res, 'ASSOC')) {
6978
            $id = $row['ref'];
6979
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
6980
                    WHERE id = $id AND session_id = $origin_session_id AND c_id = $course_id";
6981
            if ($debug) {
6982
                var_dump($sql);
6983
            }
6984
            $sub_res = Database::query($sql);
6985
            if (Database::num_rows($sub_res) > 0) {
6986
                $data = Database::fetch_array($sub_res, 'ASSOC');
6987
                if ($debug) {
6988
                    var_dump($data);
6989
                }
6990
                $parent_id = $data['parent_id'];
6991
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
6992
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
6993
                            WHERE id = $parent_id AND c_id = $course_id";
6994
                    $select_res = Database::query($sql);
6995
                    $parent_data = Database::fetch_array($select_res, 'ASSOC');
6996
                    if ($debug) {
6997
                        var_dump($parent_data);
6998
                    }
6999
7000
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
7001
                    $course_dir = $sys_course_path.$course_info['path'];
7002
                    $base_work_dir = $course_dir.'/work';
7003
7004
                    // Creating the parent folder in the session if does not exists already
7005
                    //@todo ugly fix
7006
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
7007
                    $search_this2 = $parent_data['url'];
7008
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7009
                            WHERE description like '%$search_this%' AND
7010
                                  url LIKE '%$search_this2%' AND
7011
                                  session_id = $new_session_id AND
7012
                                  c_id = $course_id
7013
                            ORDER BY id desc  LIMIT 1";
7014
                    if ($debug) {
7015
                        echo $sql;
7016
                    }
7017
                    $sub_res = Database::query($sql);
7018
                    $num_rows = Database::num_rows($sub_res);
7019
                    $new_parent_id = 0;
7020
                    if ($num_rows > 0) {
7021
                        $new_result = Database::fetch_array($sub_res, 'ASSOC');
7022
                        $created_dir = $new_result['url'];
7023
                        $new_parent_id = $new_result['id'];
7024
                    } else {
7025
                        if ($update_database) {
7026
                            $dir_name = substr($parent_data['url'], 1);
7027
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
7028
                            $created_dir = '/'.$created_dir;
7029
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
7030
                            // Creating directory
7031
                            $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
7032
                            $publication
7033
                                ->setUrl($created_dir)
7034
                                ->setCId($course_id)
7035
                                ->setTitle($parent_data['title'])
7036
                                ->setDescription(
7037
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
7038
                                )
7039
                                ->setActive(false)
7040
                                ->setAccepted(true)
7041
                                ->setPostGroupId(0)
7042
                                ->setHasProperties($parent_data['has_properties'])
7043
                                ->setWeight($parent_data['weight'])
7044
                                ->setContainsFile($parent_data['contains_file'])
7045
                                ->setFiletype('folder')
7046
                                ->setSentDate($now)
7047
                                ->setQualification($parent_data['qualification'])
7048
                                ->setParentId(0)
7049
                                ->setQualificatorId(0)
7050
                                ->setUserId($parent_data['user_id'])
7051
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
7052
                                ->setSession($session);
7053
7054
                            $publication->setDocumentId($parent_data['document_id']);
7055
7056
                            Database::getManager()->persist($publication);
7057
                            Database::getManager()->flush();
7058
                            $id = $publication->getIid();
7059
                            // Folder created
7060
                            api_item_property_update(
7061
                                $course_info,
7062
                                'work',
7063
                                $id,
7064
                                'DirectoryCreated',
7065
                                api_get_user_id(),
7066
                                null,
7067
                                null,
7068
                                null,
7069
                                null,
7070
                                $new_session_id
7071
                            );
7072
                            $new_parent_id = $id;
7073
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
7074
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
7075
                            }
7076
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
7077
                        }
7078
                    }
7079
7080
                    //Creating student_publication_assignment if exists
7081
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
7082
                            WHERE publication_id = $parent_id AND c_id = $course_id";
7083
                    if ($debug) {
7084
                        var_dump($sql);
7085
                    }
7086
                    $rest_select = Database::query($sql);
7087
                    if (Database::num_rows($rest_select) > 0) {
7088
                        if ($update_database && $new_parent_id) {
7089
                            $assignment_data = Database::fetch_array($rest_select, 'ASSOC');
7090
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
7091
                                c_id = '$course_id',
7092
                                expires_on = '".$assignment_data['expires_on']."',
7093
                                ends_on = '".$assignment_data['ends_on']."',
7094
                                add_to_calendar = '".$assignment_data['add_to_calendar']."',
7095
                                enable_qualification = '".$assignment_data['enable_qualification']."',
7096
                                publication_id = '".$new_parent_id."'";
7097
                            if ($debug) {
7098
                                echo $sql_add_publication;
7099
                            }
7100
                            Database::query($sql_add_publication);
7101
                            $id = (int) Database::insert_id();
7102
                            if ($id) {
7103
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
7104
                                    SET  has_properties = '".$id."',
7105
                                        view_properties = '1'
7106
                                    WHERE id = ".$new_parent_id;
7107
                                if ($debug) {
7108
                                    echo $sql_update;
7109
                                }
7110
                                Database::query($sql_update);
7111
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
7112
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
7113
                                }
7114
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
7115
                            }
7116
                        }
7117
                    }
7118
7119
                    $doc_url = $data['url'];
7120
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
7121
7122
                    if ($update_database) {
7123
                        // Creating a new work
7124
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
7125
                        $data['post_group_id'] = (int) $data['post_group_id'];
7126
                        $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
7127
                        $publication
7128
                            ->setUrl($new_url)
7129
                            ->setCId($course_id)
7130
                            ->setTitle($data['title'])
7131
                            ->setDescription($data['description'].' file moved')
7132
                            ->setActive($data['active'])
7133
                            ->setAccepted($data['accepted'])
7134
                            ->setPostGroupId($data['post_group_id'])
7135
                            ->setSentDate($data['sent_date'])
7136
                            ->setParentId($new_parent_id)
7137
                            ->setWeight($data['weight'])
7138
                            ->setHasProperties(0)
7139
                            ->setWeight($data['weight'])
7140
                            ->setContainsFile($data['contains_file'])
7141
                            ->setSession($session)
7142
                            ->setUserId($data['user_id'])
7143
                            ->setFiletype('file')
7144
                            ->setDocumentId(0)
7145
                        ;
7146
7147
                        $em->persist($publication);
7148
                        $em->flush();
7149
7150
                        $id = $publication->getIid();
7151
                        api_item_property_update(
7152
                            $course_info,
7153
                            'work',
7154
                            $id,
7155
                            'DocumentAdded',
7156
                            $user_id,
7157
                            null,
7158
                            null,
7159
                            null,
7160
                            null,
7161
                            $new_session_id
7162
                        );
7163
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
7164
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
7165
                        }
7166
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
7167
                        $full_file_name = $course_dir.'/'.$doc_url;
7168
                        $new_file = $course_dir.'/'.$new_url;
7169
7170
                        if (file_exists($full_file_name)) {
7171
                            // deleting old assignment
7172
                            $result = copy($full_file_name, $new_file);
7173
                            if ($result) {
7174
                                unlink($full_file_name);
7175
                                if (isset($data['id'])) {
7176
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
7177
                                    if ($debug) {
7178
                                        var_dump($sql);
7179
                                    }
7180
                                    Database::query($sql);
7181
                                }
7182
                                api_item_property_update(
7183
                                    $course_info,
7184
                                    'work',
7185
                                    $data['id'],
7186
                                    'DocumentDeleted',
7187
                                    api_get_user_id()
7188
                                );
7189
                            }
7190
                        }
7191
                    }
7192
                }
7193
            }
7194
        }
7195
7196
        //9. Survey   Pending
7197
        //10. Dropbox - not neccesary to move categories (no presence of session_id)
7198
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
7199
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
7200
        if ($debug) {
7201
            var_dump($sql);
7202
        }
7203
        $res = Database::query($sql);
7204
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7205
            $id = (int) $row['id'];
7206
            if ($update_database) {
7207
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id";
7208
                if ($debug) {
7209
                    var_dump($sql);
7210
                }
7211
                Database::query($sql);
7212
                if ($debug) {
7213
                    var_dump($res);
7214
                }
7215
7216
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
7217
                if ($debug) {
7218
                    var_dump($sql);
7219
                }
7220
                Database::query($sql);
7221
                if ($debug) {
7222
                    var_dump($res);
7223
                }
7224
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
7225
                    $result_message[$TBL_DROPBOX_FILE] = 0;
7226
                }
7227
                $result_message[$TBL_DROPBOX_FILE]++;
7228
            }
7229
        }
7230
7231
        // 11. Notebook
7232
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
7233
                WHERE
7234
                    user_id = $user_id AND
7235
                    session_id = $origin_session_id AND
7236
                    course = '$origin_course_code' AND
7237
                    c_id = $course_id";
7238
        if ($debug) {
7239
            var_dump($sql);
7240
        }
7241
        $res = Database::query($sql);
7242
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7243
            $id = $row['notebook_id'];
7244
            if ($update_database) {
7245
                $sql = "UPDATE $TBL_NOTEBOOK
7246
                        SET session_id = $new_session_id
7247
                        WHERE c_id = $course_id AND notebook_id = $id";
7248
                if ($debug) {
7249
                    var_dump($sql);
7250
                }
7251
                $res = Database::query($sql);
7252
                if ($debug) {
7253
                    var_dump($res);
7254
                }
7255
            }
7256
        }*/
7257
7258
        if ($update_database) {
7259
            echo Display::return_message(get_lang('StatsMoved'));
7260
            if (is_array($result_message)) {
7261
                foreach ($result_message as $table => $times) {
7262
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
7263
                }
7264
            }
7265
        } else {
7266
            echo '<h4>'.get_lang('UserInformationOfThisCourse').'</h4>';
7267
            echo '<br />';
7268
            echo '<table class="table" width="100%">';
7269
            echo '<tr>';
7270
            echo '<td width="50%" valign="top">';
7271
            if ($origin_session_id == 0) {
7272
                echo '<h5>'.get_lang('OriginCourse').'</h5>';
7273
            } else {
7274
                echo '<h5>'.get_lang('OriginSession').' #'.$origin_session_id.'</h5>';
7275
            }
7276
            self::compareUserData($result_message);
7277
            echo '</td>';
7278
            echo '<td width="50%" valign="top">';
7279
            if ($new_session_id == 0) {
7280
                echo '<h5>'.get_lang('DestinyCourse').'</h5>';
7281
            } else {
7282
                echo '<h5>'.get_lang('DestinySession').' #'.$new_session_id.'</h5>';
7283
            }
7284
            self::compareUserData($result_message_compare);
7285
            echo '</td>';
7286
            echo '</tr>';
7287
            echo '</table>';
7288
        }
7289
    }
7290
7291
    public static function compareUserData($result_message)
7292
    {
7293
        foreach ($result_message as $table => $data) {
7294
            $title = $table;
7295
            if ($table === 'TRACK_E_EXERCISES') {
7296
                $title = get_lang('Exercises');
7297
            } elseif ($table === 'TRACK_E_EXERCISES_IN_LP') {
7298
                $title = get_lang('ExercisesInLp');
7299
            } elseif ($table === 'LP_VIEW') {
7300
                $title = get_lang('LearningPaths');
7301
            }
7302
            echo '<br / ><h3>'.get_lang($title).' </h3><hr />';
7303
7304
            if (is_array($data)) {
7305
                foreach ($data as $id => $item) {
7306
                    if ($table === 'TRACK_E_EXERCISES' || $table === 'TRACK_E_EXERCISES_IN_LP') {
7307
                        echo "<br /><h3>".get_lang('Attempt')." #$id</h3>";
7308
                        echo '<h3>';
7309
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
7310
                        echo '</h3>';
7311
                        if (!empty($item['orig_lp_id'])) {
7312
                            echo '<h3>';
7313
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
7314
                            echo '</h3>';
7315
                        }
7316
                        // Process data.
7317
                        $array = [
7318
                            'exe_date' => get_lang('Date'),
7319
                            'exe_result' => get_lang('Score'),
7320
                            'exe_weighting' => get_lang('Weighting'),
7321
                        ];
7322
                        foreach ($item as $key => $value) {
7323
                            if (in_array($key, array_keys($array))) {
7324
                                $key = $array[$key];
7325
                                echo "$key =  $value <br />";
7326
                            }
7327
                        }
7328
                    } else {
7329
                        echo "<br /><h3>".get_lang('Id')." #$id</h3>";
7330
                        // process data
7331
                        foreach ($item as $key => $value) {
7332
                            echo "$key =  $value <br />";
7333
                        }
7334
                    }
7335
                }
7336
            } else {
7337
                echo get_lang('NoResults');
7338
            }
7339
        }
7340
    }
7341
7342
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
7343
    {
7344
        if (empty($sessionId)) {
7345
            $userList = CourseManager::get_user_list_from_course_code(
7346
                $courseInfo['code'],
7347
                $sessionId,
7348
                null,
7349
                null,
7350
                STUDENT
7351
            );
7352
        } else {
7353
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
7354
        }
7355
7356
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null);
7357
7358
        if (empty($exerciseList)) {
7359
            return Display::return_message(get_lang('NoEx'));
7360
        }
7361
7362
        $toGraphExerciseResult = [];
7363
7364
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
7365
        $quizzesTable->setHeaders(
7366
            [
7367
                get_lang('Exercises'),
7368
                get_lang('Attempts'),
7369
                get_lang('BestAttempt'),
7370
                get_lang('Ranking'),
7371
                get_lang('BestResultInCourse'),
7372
                get_lang('Statistics').Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent')),
7373
            ]
7374
        );
7375
7376
        $webCodePath = api_get_path(WEB_CODE_PATH);
7377
7378
        foreach ($exerciseList as $exercices) {
7379
            $objExercise = new Exercise($courseInfo['real_id']);
7380
            $objExercise->read($exercices['id']);
7381
            $visibleReturn = $objExercise->is_visible();
7382
7383
            // Getting count of attempts by user
7384
            $attempts = Event::count_exercise_attempts_by_user(
7385
                api_get_user_id(),
7386
                $exercices['id'],
7387
                $courseInfo['real_id'],
7388
                $sessionId
7389
            );
7390
7391
            $url = $webCodePath.'exercise/overview.php?'
7392
                .http_build_query(
7393
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['id']]
7394
                );
7395
7396
            if ($visibleReturn['value'] == true) {
7397
                $exercices['title'] = Display::url(
7398
                    $exercices['title'],
7399
                    $url,
7400
                    ['target' => SESSION_LINK_TARGET]
7401
                );
7402
            } elseif ($exercices['active'] == -1) {
7403
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
7404
            }
7405
7406
            $quizData = [
7407
                $exercices['title'],
7408
                $attempts,
7409
                '-',
7410
                '-',
7411
                '-',
7412
                '-',
7413
            ];
7414
7415
            // Exercise configuration show results or show only score
7416
            if (!in_array($exercices['results_disabled'], [0, 2])
7417
                || empty($attempts)
7418
            ) {
7419
                $quizzesTable->addRow($quizData);
7420
7421
                continue;
7422
            }
7423
7424
            //For graphics
7425
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
7426
                $exercices['id'],
7427
                $courseInfo['real_id'],
7428
                $sessionId
7429
            );
7430
7431
            $toGraphExerciseResult[$exercices['id']] = [
7432
                'title' => $exercices['title'],
7433
                'data' => $bestExerciseAttempts,
7434
            ];
7435
7436
            // Getting best results
7437
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
7438
                $exercices['id'],
7439
                $courseInfo['real_id'],
7440
                $sessionId
7441
            );
7442
7443
            if (!empty($bestScoreData)) {
7444
                $quizData[5] = ExerciseLib::show_score(
7445
                    $bestScoreData['exe_result'],
7446
                    $bestScoreData['exe_weighting']
7447
                );
7448
            }
7449
7450
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
7451
                api_get_user_id(),
7452
                $exercices['id'],
7453
                $courseInfo['real_id'],
7454
                $sessionId
7455
            );
7456
7457
            if (!empty($exerciseAttempt)) {
7458
                // Always getting the BEST attempt
7459
                $score = $exerciseAttempt['exe_result'];
7460
                $weighting = $exerciseAttempt['exe_weighting'];
7461
                $exeId = $exerciseAttempt['exe_id'];
7462
7463
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
7464
                    .http_build_query(
7465
                        [
7466
                            'id' => $exeId,
7467
                            'cidReq' => $courseInfo['code'],
7468
                            'show_headers' => 1,
7469
                            'id_session' => $sessionId,
7470
                        ]
7471
                    );
7472
7473
                $quizData[3] = Display::url(
7474
                    ExerciseLib::show_score($score, $weighting),
7475
                    $latestAttemptUrl
7476
                );
7477
7478
                $myScore = !empty($weighting) && intval($weighting) != 0 ? $score / $weighting : 0;
7479
7480
                //@todo this function slows the page
7481
                if (is_int($userList)) {
7482
                    $userList = [$userList];
7483
                }
7484
7485
                $quizData[4] = ExerciseLib::get_exercise_result_ranking(
7486
                    $myScore,
7487
                    $exeId,
7488
                    $exercices['id'],
7489
                    $courseInfo['code'],
7490
                    $sessionId,
7491
                    $userList
7492
                );
7493
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['id']]);
7494
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['id']]);
7495
7496
                $quizData[6] = Display::url(
7497
                    Display::img($graph, '', [], false),
7498
                    $normalGraph,
7499
                    ['id' => $exercices['id'], 'class' => 'expand-image']
7500
                );
7501
            }
7502
7503
            $quizzesTable->addRow($quizData);
7504
        }
7505
7506
        return Display::div(
7507
            $quizzesTable->toHtml(),
7508
            ['class' => 'table-responsive']
7509
        );
7510
    }
7511
7512
    private static function generateLearningPathsTable(int $userId, array $courseInfo, int $sessionId = 0): string
7513
    {
7514
        $columnHeaders = [
7515
            'lp' => get_lang('LearningPath'),
7516
            'time' => get_lang('LatencyTimeSpent'),
7517
            'progress' => get_lang('Progress'),
7518
            'score' => get_lang('Score'),
7519
            'best_score' => get_lang('BestScore'),
7520
            'last_connection' => get_lang('LastConnexion'),
7521
        ];
7522
7523
        $trackingColumns = api_get_configuration_value('tracking_columns');
7524
7525
        if (isset($trackingColumns['my_progress_lp'])) {
7526
            $columnHeaders = array_filter(
7527
                $columnHeaders,
7528
                function ($columHeader, $key) use ($trackingColumns) {
7529
                    if (!isset($trackingColumns['my_progress_lp'][$key])
7530
                        || $trackingColumns['my_progress_lp'][$key] == false
7531
                    ) {
7532
                        return false;
7533
                    }
7534
7535
                    return true;
7536
                },
7537
                ARRAY_FILTER_USE_BOTH
7538
            );
7539
        }
7540
7541
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
7542
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
7543
        }
7544
7545
        if (true === api_get_configuration_value('student_follow_page_add_LP_acquisition_info')) {
7546
            $columnHeaders['student_follow_page_add_LP_acquisition_info'] = get_lang('Acquisition');
7547
        }
7548
7549
        $addLpInvisibleCheckbox = api_get_configuration_value('student_follow_page_add_LP_invisible_checkbox');
7550
7551
        $columnHeadersKeys = array_keys($columnHeaders);
7552
7553
        $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
7554
        $learningpathsTable->setHeaders($columnHeaders);
7555
7556
        // LP table results
7557
        $list = new LearnpathList(
7558
            api_get_user_id(),
7559
            $courseInfo,
7560
            $sessionId,
7561
            'lp.publicatedOn ASC',
7562
            true,
7563
            null,
7564
            true
7565
        );
7566
7567
        $lpList = $list->get_flat_list();
7568
7569
        if (empty($lpList)) {
7570
            return Display::return_message(get_lang('NoLearnpath'));
7571
        }
7572
7573
        $webCodePath = api_get_path(WEB_CODE_PATH);
7574
7575
        foreach ($lpList as $lpId => $learnpath) {
7576
            $learningpathData = [];
7577
7578
            if (!$learnpath['lp_visibility']) {
7579
                continue;
7580
            }
7581
7582
            if ($addLpInvisibleCheckbox) {
7583
                if (!StudentFollowPage::isViewVisible($lpId, $userId, $courseInfo['real_id'], $sessionId)) {
7584
                    continue;
7585
                }
7586
            }
7587
7588
            $url = $webCodePath.'lp/lp_controller.php?'
7589
                .http_build_query(
7590
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
7591
                );
7592
7593
            if (in_array('lp', $columnHeadersKeys)) {
7594
                if ($learnpath['lp_visibility'] == 0) {
7595
                    $learningpathData[] = $learnpath['lp_name'];
7596
                } else {
7597
                    $learningpathData[] = Display::url(
7598
                        $learnpath['lp_name'],
7599
                        $url,
7600
                        ['target' => SESSION_LINK_TARGET]
7601
                    );
7602
                }
7603
            }
7604
7605
            if (in_array('time', $columnHeadersKeys)) {
7606
                $time_spent_in_lp = self::get_time_spent_in_lp(
7607
                    $userId,
7608
                    $courseInfo['code'],
7609
                    [$lpId],
7610
                    $sessionId
7611
                );
7612
7613
                $learningpathData[] = api_time_to_hms($time_spent_in_lp);
7614
            }
7615
7616
            if (in_array('progress', $columnHeadersKeys)) {
7617
                $progress = self::get_avg_student_progress(
7618
                    $userId,
7619
                    $courseInfo['code'],
7620
                    [$lpId],
7621
                    $sessionId
7622
                );
7623
7624
                if (is_numeric($progress)) {
7625
                    $progress = sprintf(get_lang('XPercent'), $progress);
7626
                }
7627
7628
                $learningpathData[] = $progress;
7629
            }
7630
7631
            if (in_array('score', $columnHeadersKeys)) {
7632
                $percentage_score = self::get_avg_student_score(
7633
                    $userId,
7634
                    $courseInfo['code'],
7635
                    [$lpId],
7636
                    $sessionId
7637
                );
7638
7639
                if (is_numeric($percentage_score)) {
7640
                    $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
7641
                } else {
7642
                    $percentage_score = sprintf(get_lang('XPercent'), 0);
7643
                }
7644
7645
                $learningpathData[] = $percentage_score;
7646
            }
7647
7648
            if (in_array('best_score', $columnHeadersKeys)) {
7649
                $bestScore = self::get_avg_student_score(
7650
                    $userId,
7651
                    $courseInfo['code'],
7652
                    [$lpId],
7653
                    $sessionId,
7654
                    false,
7655
                    false,
7656
                    true
7657
                );
7658
7659
                if (is_numeric($bestScore)) {
7660
                    $bestScore = sprintf(get_lang('XPercent'), $bestScore);
7661
                } else {
7662
                    $bestScore = '-';
7663
                }
7664
7665
                $learningpathData[] = $bestScore;
7666
            }
7667
7668
            if (in_array('last_connection', $columnHeadersKeys)) {
7669
                $lastConnectionInLp = self::get_last_connection_time_in_lp(
7670
                    $userId,
7671
                    $courseInfo['code'],
7672
                    $lpId,
7673
                    $sessionId
7674
                );
7675
7676
                $lastConnection = '-';
7677
7678
                if (!empty($lastConnectionInLp)) {
7679
                    $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
7680
                }
7681
7682
                $learningpathData[] = $lastConnection;
7683
            }
7684
7685
            if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
7686
                $learningpathData[] = StudentFollowPage::getLpSubscription(
7687
                    $learnpath,
7688
                    $userId,
7689
                    $courseInfo['real_id'],
7690
                    $sessionId
7691
                );
7692
            }
7693
7694
            if (in_array('student_follow_page_add_LP_acquisition_info', $columnHeadersKeys)) {
7695
                $learningpathData[] = StudentFollowPage::getLpAcquisition(
7696
                    $learnpath,
7697
                    $userId,
7698
                    $courseInfo['real_id'],
7699
                    $sessionId
7700
                );
7701
            }
7702
7703
            $learningpathsTable->addRow($learningpathData);
7704
        }
7705
7706
        return Display::div(
7707
            $learningpathsTable->toHtml(),
7708
            ['class' => 'table-responsive']
7709
        );
7710
    }
7711
}
7712
7713
/**
7714
 * @todo move into a proper file
7715
 */
7716
class TrackingCourseLog
7717
{
7718
    /**
7719
     * @return mixed
7720
     */
7721
    public static function count_item_resources()
7722
    {
7723
        $session_id = api_get_session_id();
7724
        $course_id = api_get_course_int_id();
7725
7726
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
7727
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7728
7729
        $sql = "SELECT count(tool) AS total_number_of_items
7730
                FROM $table_item_property track_resource, $table_user user
7731
                WHERE
7732
                    track_resource.c_id = $course_id AND
7733
                    track_resource.insert_user_id = user.user_id AND
7734
                    session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7735
7736
        if (isset($_GET['keyword'])) {
7737
            $keyword = Database::escape_string(trim($_GET['keyword']));
7738
            $sql .= " AND (
7739
                        user.username LIKE '%".$keyword."%' OR
7740
                        lastedit_type LIKE '%".$keyword."%' OR
7741
                        tool LIKE '%".$keyword."%'
7742
                    )";
7743
        }
7744
7745
        $sql .= " AND tool IN (
7746
                    'document',
7747
                    'learnpath',
7748
                    'quiz',
7749
                    'glossary',
7750
                    'link',
7751
                    'course_description',
7752
                    'announcement',
7753
                    'thematic',
7754
                    'thematic_advance',
7755
                    'thematic_plan'
7756
                )";
7757
        $res = Database::query($sql);
7758
        $obj = Database::fetch_object($res);
7759
7760
        return $obj->total_number_of_items;
7761
    }
7762
7763
    /**
7764
     * @param $from
7765
     * @param $number_of_items
7766
     * @param $column
7767
     * @param $direction
7768
     *
7769
     * @return array
7770
     */
7771
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
7772
    {
7773
        $session_id = api_get_session_id();
7774
        $course_id = api_get_course_int_id();
7775
7776
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
7777
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7778
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
7779
        $column = (int) $column;
7780
        $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction;
7781
7782
        $sql = "SELECT
7783
                    tool as col0,
7784
                    lastedit_type as col1,
7785
                    ref as ref,
7786
                    user.username as col3,
7787
                    insert_date as col6,
7788
                    visibility as col7,
7789
                    user.user_id as user_id
7790
                FROM $table_item_property track_resource, $table_user user
7791
                WHERE
7792
                  track_resource.c_id = $course_id AND
7793
                  track_resource.insert_user_id = user.user_id AND
7794
                  session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7795
7796
        if (isset($_GET['keyword'])) {
7797
            $keyword = Database::escape_string(trim($_GET['keyword']));
7798
            $sql .= " AND (
7799
                        user.username LIKE '%".$keyword."%' OR
7800
                        lastedit_type LIKE '%".$keyword."%' OR
7801
                        tool LIKE '%".$keyword."%'
7802
                     ) ";
7803
        }
7804
7805
        $sql .= " AND tool IN (
7806
                    'document',
7807
                    'learnpath',
7808
                    'quiz',
7809
                    'glossary',
7810
                    'link',
7811
                    'course_description',
7812
                    'announcement',
7813
                    'thematic',
7814
                    'thematic_advance',
7815
                    'thematic_plan'
7816
                )";
7817
7818
        if ($column == 0) {
7819
            $column = '0';
7820
        }
7821
        if ($column != '' && $direction != '') {
7822
            if ($column != 2 && $column != 4) {
7823
                $sql .= " ORDER BY col$column $direction";
7824
            }
7825
        } else {
7826
            $sql .= " ORDER BY col6 DESC ";
7827
        }
7828
7829
        $from = intval($from);
7830
        if ($from) {
7831
            $number_of_items = intval($number_of_items);
7832
            $sql .= " LIMIT $from, $number_of_items ";
7833
        }
7834
7835
        $res = Database::query($sql);
7836
        $resources = [];
7837
        $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan'];
7838
        while ($row = Database::fetch_array($res)) {
7839
            $ref = $row['ref'];
7840
            $table_name = self::get_tool_name_table($row['col0']);
7841
            $table_tool = Database::get_course_table($table_name['table_name']);
7842
7843
            $id = $table_name['id_tool'];
7844
            $recorset = false;
7845
7846
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
7847
                $tbl_thematic = Database::get_course_table(TABLE_THEMATIC);
7848
                $sql = "SELECT thematic_id FROM $table_tool
7849
                        WHERE c_id = $course_id AND id = $ref";
7850
                $rs_thematic = Database::query($sql);
7851
                if (Database::num_rows($rs_thematic)) {
7852
                    $row_thematic = Database::fetch_array($rs_thematic);
7853
                    $thematic_id = $row_thematic['thematic_id'];
7854
7855
                    $sql = "SELECT session.id, session.name, user.username
7856
                            FROM $tbl_thematic t, $table_session session, $table_user user
7857
                            WHERE
7858
                              t.c_id = $course_id AND
7859
                              t.session_id = session.id AND
7860
                              session.id_coach = user.user_id AND
7861
                              t.id = $thematic_id";
7862
                    $recorset = Database::query($sql);
7863
                }
7864
            } else {
7865
                $sql = "SELECT session.id, session.name, user.username
7866
                          FROM $table_tool tool, $table_session session, $table_user user
7867
                          WHERE
7868
                              tool.c_id = $course_id AND
7869
                              tool.session_id = session.id AND
7870
                              session.id_coach = user.user_id AND
7871
                              tool.$id = $ref";
7872
                $recorset = Database::query($sql);
7873
            }
7874
7875
            if (!empty($recorset)) {
7876
                $obj = Database::fetch_object($recorset);
7877
7878
                $name_session = '';
7879
                $coach_name = '';
7880
                if (!empty($obj)) {
7881
                    $name_session = $obj->name;
7882
                    $coach_name = $obj->username;
7883
                }
7884
7885
                $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
7886
                $row[0] = '';
7887
                if ($row['col6'] != 2) {
7888
                    if (in_array($row['col0'], $thematic_tools)) {
7889
                        $exp_thematic_tool = explode('_', $row['col0']);
7890
                        $thematic_tool_title = '';
7891
                        if (is_array($exp_thematic_tool)) {
7892
                            foreach ($exp_thematic_tool as $exp) {
7893
                                $thematic_tool_title .= api_ucfirst($exp);
7894
                            }
7895
                        } else {
7896
                            $thematic_tool_title = api_ucfirst($row['col0']);
7897
                        }
7898
7899
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
7900
                    } else {
7901
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
7902
                    }
7903
                } else {
7904
                    $row[0] = api_ucfirst($row['col0']);
7905
                }
7906
                $row[1] = get_lang($row[1]);
7907
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
7908
                $row[5] = '';
7909
                //@todo Improve this code please
7910
                switch ($table_name['table_name']) {
7911
                    case 'document':
7912
                        $sql = "SELECT tool.title as title FROM $table_tool tool
7913
                                WHERE c_id = $course_id AND 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->title;
7918
                        }
7919
                        break;
7920
                    case 'announcement':
7921
                        $sql = "SELECT title FROM $table_tool
7922
                                WHERE c_id = $course_id AND id = $ref";
7923
                        $rs_document = Database::query($sql);
7924
                        $obj_document = Database::fetch_object($rs_document);
7925
                        if ($obj_document) {
7926
                            $row[5] = $obj_document->title;
7927
                        }
7928
                        break;
7929
                    case 'glossary':
7930
                        $sql = "SELECT name FROM $table_tool
7931
                                WHERE c_id = $course_id AND glossary_id = $ref";
7932
                        $rs_document = Database::query($sql);
7933
                        $obj_document = Database::fetch_object($rs_document);
7934
                        if ($obj_document) {
7935
                            $row[5] = $obj_document->name;
7936
                        }
7937
                        break;
7938
                    case 'lp':
7939
                        $sql = "SELECT name
7940
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
7941
                        $rs_document = Database::query($sql);
7942
                        $obj_document = Database::fetch_object($rs_document);
7943
                        $row[5] = $obj_document->name;
7944
                        break;
7945
                    case 'quiz':
7946
                        $sql = "SELECT title FROM $table_tool
7947
                                WHERE c_id = $course_id AND id = $ref";
7948
                        $rs_document = Database::query($sql);
7949
                        $obj_document = Database::fetch_object($rs_document);
7950
                        if ($obj_document) {
7951
                            $row[5] = $obj_document->title;
7952
                        }
7953
                        break;
7954
                    case 'course_description':
7955
                        $sql = "SELECT title FROM $table_tool
7956
                                WHERE c_id = $course_id AND id = $ref";
7957
                        $rs_document = Database::query($sql);
7958
                        $obj_document = Database::fetch_object($rs_document);
7959
                        if ($obj_document) {
7960
                            $row[5] = $obj_document->title;
7961
                        }
7962
                        break;
7963
                    case 'thematic':
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
                    case 'thematic_advance':
7973
                        $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7974
                        if (Database::num_rows($rs) > 0) {
7975
                            $obj = Database::fetch_object($rs);
7976
                            if ($obj) {
7977
                                $row[5] = $obj->content;
7978
                            }
7979
                        }
7980
                        break;
7981
                    case 'thematic_plan':
7982
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7983
                        if (Database::num_rows($rs) > 0) {
7984
                            $obj = Database::fetch_object($rs);
7985
                            if ($obj) {
7986
                                $row[5] = $obj->title;
7987
                            }
7988
                        }
7989
                        break;
7990
                    default:
7991
                        break;
7992
                }
7993
7994
                $row2 = $name_session;
7995
                if (!empty($coach_name)) {
7996
                    $row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
7997
                }
7998
                $row[2] = $row2;
7999
                if (!empty($row['col3'])) {
8000
                    $userInfo = api_get_user_info($row['user_id']);
8001
                    $row['col3'] = Display::url(
8002
                        $row['col3'],
8003
                        $userInfo['profile_url']
8004
                    );
8005
                    $row[3] = $row['col3'];
8006
8007
                    $ip = Tracking::get_ip_from_user_event(
8008
                        $row['user_id'],
8009
                        $row['col6'],
8010
                        true
8011
                    );
8012
                    if (empty($ip)) {
8013
                        $ip = get_lang('Unknown');
8014
                    }
8015
                    $row[4] = $ip;
8016
                }
8017
8018
                $resources[] = $row;
8019
            }
8020
        }
8021
8022
        return $resources;
8023
    }
8024
8025
    /**
8026
     * @param string $tool
8027
     *
8028
     * @return array
8029
     */
8030
    public static function get_tool_name_table($tool)
8031
    {
8032
        switch ($tool) {
8033
            case 'document':
8034
                $table_name = TABLE_DOCUMENT;
8035
                $link_tool = 'document/document.php';
8036
                $id_tool = 'id';
8037
                break;
8038
            case 'learnpath':
8039
                $table_name = TABLE_LP_MAIN;
8040
                $link_tool = 'lp/lp_controller.php';
8041
                $id_tool = 'id';
8042
                break;
8043
            case 'quiz':
8044
                $table_name = TABLE_QUIZ_TEST;
8045
                $link_tool = 'exercise/exercise.php';
8046
                $id_tool = 'id';
8047
                break;
8048
            case 'glossary':
8049
                $table_name = TABLE_GLOSSARY;
8050
                $link_tool = 'glossary/index.php';
8051
                $id_tool = 'glossary_id';
8052
                break;
8053
            case 'link':
8054
                $table_name = TABLE_LINK;
8055
                $link_tool = 'link/link.php';
8056
                $id_tool = 'id';
8057
                break;
8058
            case 'course_description':
8059
                $table_name = TABLE_COURSE_DESCRIPTION;
8060
                $link_tool = 'course_description/';
8061
                $id_tool = 'id';
8062
                break;
8063
            case 'announcement':
8064
                $table_name = TABLE_ANNOUNCEMENT;
8065
                $link_tool = 'announcements/announcements.php';
8066
                $id_tool = 'id';
8067
                break;
8068
            case 'thematic':
8069
                $table_name = TABLE_THEMATIC;
8070
                $link_tool = 'course_progress/index.php';
8071
                $id_tool = 'id';
8072
                break;
8073
            case 'thematic_advance':
8074
                $table_name = TABLE_THEMATIC_ADVANCE;
8075
                $link_tool = 'course_progress/index.php';
8076
                $id_tool = 'id';
8077
                break;
8078
            case 'thematic_plan':
8079
                $table_name = TABLE_THEMATIC_PLAN;
8080
                $link_tool = 'course_progress/index.php';
8081
                $id_tool = 'id';
8082
                break;
8083
            default:
8084
                $table_name = $tool;
8085
            break;
8086
        }
8087
8088
        return [
8089
            'table_name' => $table_name,
8090
            'link_tool' => $link_tool,
8091
            'id_tool' => $id_tool,
8092
        ];
8093
    }
8094
8095
    /**
8096
     * @return string
8097
     */
8098
    public static function display_additional_profile_fields()
8099
    {
8100
        // getting all the extra profile fields that are defined by the platform administrator
8101
        $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
8102
8103
        // creating the form
8104
        $return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
8105
8106
        // the select field with the additional user profile fields (= this is where we select the field of which we want to see
8107
        // the information the users have entered or selected.
8108
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
8109
        $return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
8110
        $extra_fields_to_show = 0;
8111
        foreach ($extra_fields as $key => $field) {
8112
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
8113
            if ($field[6] == 1 && $field[8] == 1) {
8114
                if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field']) {
8115
                    $selected = 'selected="selected"';
8116
                } else {
8117
                    $selected = '';
8118
                }
8119
                $extra_fields_to_show++;
8120
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
8121
            }
8122
        }
8123
        $return .= '</select>';
8124
8125
        // the form elements for the $_GET parameters (because the form is passed through GET
8126
        foreach ($_GET as $key => $value) {
8127
            if ($key != 'additional_profile_field') {
8128
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
8129
            }
8130
        }
8131
        // the submit button
8132
        $return .= '<button class="save" type="submit">'.get_lang('AddAdditionalProfileField').'</button>';
8133
        $return .= '</form>';
8134
        if ($extra_fields_to_show > 0) {
8135
            return $return;
8136
        } else {
8137
            return '';
8138
        }
8139
    }
8140
8141
    /**
8142
     * This function gets all the information of a certrain ($field_id)
8143
     * additional profile field for a specific list of users is more efficent
8144
     * than get_addtional_profile_information_of_field() function
8145
     * It gets the information of all the users so that it can be displayed
8146
     * in the sortable table or in the csv or xls export.
8147
     *
8148
     * @author    Julio Montoya <[email protected]>
8149
     *
8150
     * @param    int field id
8151
     * @param    array list of user ids
8152
     *
8153
     * @return array
8154
     *
8155
     * @since    Nov 2009
8156
     *
8157
     * @version    1.8.6.2
8158
     */
8159
    public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users)
8160
    {
8161
        // Database table definition
8162
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
8163
        $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
8164
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
8165
        $result_extra_field = UserManager::get_extra_field_information($field_id);
8166
        $return = [];
8167
        if (!empty($users)) {
8168
            if ($result_extra_field['field_type'] == UserManager::USER_FIELD_TYPE_TAG) {
8169
                foreach ($users as $user_id) {
8170
                    $user_result = UserManager::get_user_tags($user_id, $field_id);
8171
                    $tag_list = [];
8172
                    foreach ($user_result as $item) {
8173
                        $tag_list[] = $item['tag'];
8174
                    }
8175
                    $return[$user_id][] = implode(', ', $tag_list);
8176
                }
8177
            } else {
8178
                $new_user_array = [];
8179
                foreach ($users as $user_id) {
8180
                    $new_user_array[] = "'".$user_id."'";
8181
                }
8182
                $users = implode(',', $new_user_array);
8183
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
8184
                // Selecting only the necessary information NOT ALL the user list
8185
                $sql = "SELECT user.user_id, v.value
8186
                        FROM $table_user user
8187
                        INNER JOIN $table_user_field_values v
8188
                        ON (user.user_id = v.item_id)
8189
                        INNER JOIN $extraField f
8190
                        ON (f.id = v.field_id)
8191
                        WHERE
8192
                            f.extra_field_type = $extraFieldType AND
8193
                            v.field_id=".intval($field_id)." AND
8194
                            user.user_id IN ($users)";
8195
8196
                $result = Database::query($sql);
8197
                while ($row = Database::fetch_array($result)) {
8198
                    // get option value for field type double select by id
8199
                    if (!empty($row['value'])) {
8200
                        if ($result_extra_field['field_type'] ==
8201
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
8202
                        ) {
8203
                            $id_double_select = explode(';', $row['value']);
8204
                            if (is_array($id_double_select)) {
8205
                                $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
8206
                                $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
8207
                                $row['value'] = ($value1.';'.$value2);
8208
                            }
8209
                        }
8210
8211
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) {
8212
                            $parsedValue = explode('::', $row['value']);
8213
8214
                            if ($parsedValue) {
8215
                                $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text'];
8216
                                $value2 = $parsedValue[1];
8217
8218
                                $row['value'] = "$value1: $value2";
8219
                            }
8220
                        }
8221
8222
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) {
8223
                            [$level1, $level2, $level3] = explode(';', $row['value']);
8224
8225
                            $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / ';
8226
                            $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / ';
8227
                            $row['value'] .= $result_extra_field['options'][$level3]['display_text'];
8228
                        }
8229
                    }
8230
                    // get other value from extra field
8231
                    $return[$row['user_id']][] = $row['value'];
8232
                }
8233
            }
8234
        }
8235
8236
        return $return;
8237
    }
8238
8239
    /**
8240
     * count the number of students in this course (used for SortableTable)
8241
     * Deprecated.
8242
     */
8243
    public function count_student_in_course()
8244
    {
8245
        global $nbStudents;
8246
8247
        return $nbStudents;
8248
    }
8249
8250
    public function sort_users($a, $b)
8251
    {
8252
        $tracking = Session::read('tracking_column');
8253
8254
        return strcmp(
8255
            trim(api_strtolower($a[$tracking])),
8256
            trim(api_strtolower($b[$tracking]))
8257
        );
8258
    }
8259
8260
    public function sort_users_desc($a, $b)
8261
    {
8262
        $tracking = Session::read('tracking_column');
8263
8264
        return strcmp(
8265
            trim(api_strtolower($b[$tracking])),
8266
            trim(api_strtolower($a[$tracking]))
8267
        );
8268
    }
8269
8270
    /**
8271
     * Get number of users for sortable with pagination.
8272
     *
8273
     * @return int
8274
     */
8275
    public static function get_number_of_users($conditions)
8276
    {
8277
        $conditions['get_count'] = true;
8278
8279
        return self::get_user_data(null, null, null, null, $conditions);
8280
    }
8281
8282
    /**
8283
     * Get data for users list in sortable with pagination.
8284
     *
8285
     * @param int $from
8286
     * @param int $number_of_items
8287
     * @param $column
8288
     * @param $direction
8289
     * @param $conditions
8290
     *
8291
     * @return array
8292
     */
8293
    public static function get_user_data(
8294
        $from,
8295
        $number_of_items,
8296
        $column,
8297
        $direction,
8298
        $conditions = []
8299
    ) {
8300
        global $user_ids, $course_code, $export_csv, $session_id;
8301
        $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users
8302
        $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false;
8303
8304
        $csv_content = [];
8305
        $course_code = $course_code ? Database::escape_string($course_code) : api_get_course_id();
8306
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
8307
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8308
        $access_url_id = api_get_current_access_url_id();
8309
8310
        // get all users data from a course for sortable with limit
8311
        if (is_array($user_ids)) {
8312
            $user_ids = array_map('intval', $user_ids);
8313
            $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") ";
8314
        } else {
8315
            $user_ids = (int) $user_ids;
8316
            $condition_user = " WHERE user.id = $user_ids ";
8317
        }
8318
8319
        if (!empty($_GET['user_keyword'])) {
8320
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
8321
            $condition_user .= " AND (
8322
                user.firstname LIKE '%".$keyword."%' OR
8323
                user.lastname LIKE '%".$keyword."%'  OR
8324
                user.username LIKE '%".$keyword."%'  OR
8325
                user.email LIKE '%".$keyword."%'
8326
             ) ";
8327
        }
8328
8329
        $url_table = '';
8330
        $url_condition = '';
8331
        if (api_is_multiple_url_enabled()) {
8332
            $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)";
8333
            $url_condition = " AND access_url_id = '$access_url_id'";
8334
        }
8335
8336
        $invitedUsersCondition = '';
8337
        if (!$includeInvitedUsers) {
8338
            $invitedUsersCondition = " AND user.status != ".INVITEE;
8339
        }
8340
8341
        $select = '
8342
                SELECT user.id as user_id,
8343
                    user.official_code  as col0,
8344
                    user.lastname       as col1,
8345
                    user.firstname      as col2,
8346
                    user.username       as col3,
8347
                    user.email          as col4';
8348
        if ($getCount) {
8349
            $select = ' SELECT COUNT(distinct(user.id)) as count ';
8350
        }
8351
8352
        $sqlInjectJoins = '';
8353
        $where = 'AND 1 = 1 ';
8354
        $sqlInjectWhere = '';
8355
        if (!empty($conditions)) {
8356
            if (isset($conditions['inject_joins'])) {
8357
                $sqlInjectJoins = $conditions['inject_joins'];
8358
            }
8359
            if (isset($conditions['where'])) {
8360
                $where = $conditions['where'];
8361
            }
8362
            if (isset($conditions['inject_where'])) {
8363
                $sqlInjectWhere = $conditions['inject_where'];
8364
            }
8365
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
8366
            $injectExtraFields = rtrim($injectExtraFields, ', ');
8367
            if (false === $getCount) {
8368
                $select .= " , $injectExtraFields";
8369
            }
8370
        }
8371
8372
        $sql = "$select
8373
                FROM $tbl_user as user
8374
                $url_table
8375
                $sqlInjectJoins
8376
                $condition_user
8377
                $url_condition
8378
                $invitedUsersCondition
8379
                $where
8380
                $sqlInjectWhere
8381
                ";
8382
8383
        if (!in_array($direction, ['ASC', 'DESC'])) {
8384
            $direction = 'ASC';
8385
        }
8386
8387
        $column = (int) $column;
8388
        $from = (int) $from;
8389
        $number_of_items = (int) $number_of_items;
8390
8391
        if ($getCount) {
8392
            $res = Database::query($sql);
8393
            $row = Database::fetch_array($res);
8394
8395
            return $row['count'];
8396
        }
8397
8398
        $sql .= " ORDER BY col$column $direction ";
8399
        $sql .= " LIMIT $from, $number_of_items";
8400
8401
        $res = Database::query($sql);
8402
        $users = [];
8403
8404
        $courseInfo = api_get_course_info($course_code);
8405
        $courseId = $courseInfo['real_id'];
8406
        $courseCode = $courseInfo['code'];
8407
8408
        $total_surveys = 0;
8409
        $total_exercises = ExerciseLib::get_all_exercises(
8410
            $courseInfo,
8411
            $session_id,
8412
            false,
8413
            null,
8414
            false,
8415
            3
8416
        );
8417
8418
        if (empty($session_id)) {
8419
            $survey_user_list = [];
8420
            $surveyList = SurveyManager::get_surveys($course_code, $session_id);
8421
            if ($surveyList) {
8422
                $total_surveys = count($surveyList);
8423
                foreach ($surveyList as $survey) {
8424
                    $user_list = SurveyManager::get_people_who_filled_survey(
8425
                        $survey['survey_id'],
8426
                        false,
8427
                        $courseId
8428
                    );
8429
8430
                    foreach ($user_list as $user_id) {
8431
                        isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
8432
                    }
8433
                }
8434
            }
8435
        }
8436
8437
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode.
8438
            '&course='.$course_code.'&origin=tracking_course&id_session='.$session_id;
8439
8440
        $sortByFirstName = api_sort_by_first_name();
8441
        Session::write('user_id_list', []);
8442
        $userIdList = [];
8443
8444
        $addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report');
8445
        $exerciseResultsToCheck = [];
8446
        if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) &&
8447
            isset($addExerciseOption['courses'][$courseCode])
8448
        ) {
8449
            foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) {
8450
                $exercise = new Exercise();
8451
                $exercise->read($exerciseId);
8452
                if ($exercise->iId) {
8453
                    $exerciseResultsToCheck[] = $exercise;
8454
                }
8455
            }
8456
        }
8457
8458
        while ($user = Database::fetch_array($res, 'ASSOC')) {
8459
            $userIdList[] = $user['user_id'];
8460
            $user['official_code'] = $user['col0'];
8461
            $user['username'] = $user['col3'];
8462
            $user['time'] = api_time_to_hms(
8463
                Tracking::get_time_spent_on_the_course(
8464
                    $user['user_id'],
8465
                    $courseId,
8466
                    $session_id
8467
                )
8468
            );
8469
8470
            $avg_student_score = Tracking::get_avg_student_score(
8471
                $user['user_id'],
8472
                $course_code,
8473
                [],
8474
                $session_id
8475
            );
8476
8477
            $averageBestScore = Tracking::get_avg_student_score(
8478
                $user['user_id'],
8479
                $course_code,
8480
                [],
8481
                $session_id,
8482
                false,
8483
                false,
8484
                true
8485
            );
8486
8487
            $avg_student_progress = Tracking::get_avg_student_progress(
8488
                $user['user_id'],
8489
                $course_code,
8490
                [],
8491
                $session_id
8492
            );
8493
8494
            if (empty($avg_student_progress)) {
8495
                $avg_student_progress = 0;
8496
            }
8497
            $user['average_progress'] = $avg_student_progress.'%';
8498
8499
            $total_user_exercise = Tracking::get_exercise_student_progress(
8500
                $total_exercises,
8501
                $user['user_id'],
8502
                $courseId,
8503
                $session_id
8504
            );
8505
8506
            $user['exercise_progress'] = $total_user_exercise;
8507
8508
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
8509
                $total_exercises,
8510
                $user['user_id'],
8511
                $courseId,
8512
                $session_id
8513
            );
8514
8515
            $user['exercise_average_best_attempt'] = $total_user_exercise;
8516
8517
            if (is_numeric($avg_student_score)) {
8518
                $user['student_score'] = $avg_student_score.'%';
8519
            } else {
8520
                $user['student_score'] = $avg_student_score;
8521
            }
8522
8523
            if (is_numeric($averageBestScore)) {
8524
                $user['student_score_best'] = $averageBestScore.'%';
8525
            } else {
8526
                $user['student_score_best'] = $averageBestScore;
8527
            }
8528
8529
            $exerciseResults = [];
8530
            if (!empty($exerciseResultsToCheck)) {
8531
                foreach ($exerciseResultsToCheck as $exercise) {
8532
                    $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user(
8533
                        $user['user_id'],
8534
                        $exercise->iId,
8535
                        $courseId,
8536
                        $session_id,
8537
                        false
8538
                    );
8539
8540
                    $best = null;
8541
                    if ($bestExerciseResult) {
8542
                        $best = $bestExerciseResult['exe_result'] / $bestExerciseResult['exe_weighting'];
8543
                        $best = round($best, 2) * 100;
8544
                        $best .= '%';
8545
                    }
8546
                    $exerciseResults['exercise_'.$exercise->iId] = $best;
8547
                }
8548
            }
8549
8550
            $user['count_assignments'] = Tracking::count_student_assignments(
8551
                $user['user_id'],
8552
                $course_code,
8553
                $session_id
8554
            );
8555
            $user['count_messages'] = Tracking::count_student_messages(
8556
                $user['user_id'],
8557
                $course_code,
8558
                $session_id
8559
            );
8560
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
8561
                $user['user_id'],
8562
                $courseId,
8563
                $session_id,
8564
                false === $export_csv
8565
            );
8566
8567
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
8568
                $user['user_id'],
8569
                $courseInfo,
8570
                $session_id,
8571
                false === $export_csv
8572
            );
8573
8574
            if ($export_csv) {
8575
                if (!empty($user['first_connection'])) {
8576
                    $user['first_connection'] = api_get_local_time($user['first_connection']);
8577
                } else {
8578
                    $user['first_connection'] = '-';
8579
                }
8580
                if (!empty($user['last_connection'])) {
8581
                    $user['last_connection'] = api_get_local_time($user['last_connection']);
8582
                } else {
8583
                    $user['last_connection'] = '-';
8584
                }
8585
            }
8586
8587
            if (empty($session_id)) {
8588
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys;
8589
            }
8590
8591
            $url = $urlBase.'&student='.$user['user_id'];
8592
8593
            $user['link'] = '<center><a href="'.$url.'">
8594
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
8595
                             </a></center>';
8596
8597
            // store columns in array $users
8598
            $user_row = [];
8599
            $user_row['official_code'] = $user['official_code']; //0
8600
            if ($sortByFirstName) {
8601
                $user_row['firstname'] = $user['col2'];
8602
                $user_row['lastname'] = $user['col1'];
8603
            } else {
8604
                $user_row['lastname'] = $user['col1'];
8605
                $user_row['firstname'] = $user['col2'];
8606
            }
8607
            $user_row['username'] = $user['username'];
8608
            $user_row['time'] = $user['time'];
8609
            $user_row['average_progress'] = $user['average_progress'];
8610
            $user_row['exercise_progress'] = $user['exercise_progress'];
8611
            $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
8612
            $user_row['student_score'] = $user['student_score'];
8613
            $user_row['student_score_best'] = $user['student_score_best'];
8614
            if (!empty($exerciseResults)) {
8615
                foreach ($exerciseResults as $exerciseId => $bestResult) {
8616
                    $user_row[$exerciseId] = $bestResult;
8617
                }
8618
            }
8619
8620
            $user_row['count_assignments'] = $user['count_assignments'];
8621
            $user_row['count_messages'] = $user['count_messages'];
8622
8623
            $userGroupManager = new UserGroup();
8624
            $user_row['classes'] = $userGroupManager->getLabelsFromNameList($user['user_id'], UserGroup::NORMAL_CLASS);
8625
8626
            if (empty($session_id)) {
8627
                $user_row['survey'] = $user['survey'];
8628
            } else {
8629
                $userSession = SessionManager::getUserSession($user['user_id'], $session_id);
8630
                $user_row['registered_at'] = '';
8631
                if ($userSession) {
8632
                    $user_row['registered_at'] = api_get_local_time($userSession['registered_at']);
8633
                }
8634
            }
8635
8636
            $user_row['first_connection'] = $user['first_connection'];
8637
            $user_row['last_connection'] = $user['last_connection'];
8638
8639
            // we need to display an additional profile field
8640
            if (isset($_GET['additional_profile_field'])) {
8641
                $data = Session::read('additional_user_profile_info');
8642
8643
                $extraFieldInfo = Session::read('extra_field_info');
8644
                foreach ($_GET['additional_profile_field'] as $fieldId) {
8645
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
8646
                        if (is_array($data[$fieldId][$user['user_id']])) {
8647
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = implode(
8648
                                ', ',
8649
                                $data[$fieldId][$user['user_id']]
8650
                            );
8651
                        } else {
8652
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
8653
                        }
8654
                    } else {
8655
                        $user_row[$extraFieldInfo[$fieldId]['variable']] = '';
8656
                    }
8657
                }
8658
            }
8659
8660
            if (api_get_setting('show_email_addresses') === 'true') {
8661
                $user_row['email'] = $user['col4'];
8662
            }
8663
8664
            $user_row['link'] = $user['link'];
8665
8666
            if ($export_csv) {
8667
                if (empty($session_id)) {
8668
                    unset($user_row['classes']);
8669
                    unset($user_row['link']);
8670
                } else {
8671
                    unset($user_row['classes']);
8672
                    unset($user_row['link']);
8673
                }
8674
8675
                $csv_content[] = $user_row;
8676
            }
8677
            $users[] = array_values($user_row);
8678
        }
8679
8680
        if ($export_csv) {
8681
            Session::write('csv_content', $csv_content);
8682
        }
8683
8684
        Session::erase('additional_user_profile_info');
8685
        Session::erase('extra_field_info');
8686
        Session::write('user_id_list', $userIdList);
8687
8688
        return $users;
8689
    }
8690
8691
    /**
8692
     * Get data for users list in sortable with pagination.
8693
     *
8694
     * @param $from
8695
     * @param $number_of_items
8696
     * @param $column
8697
     * @param $direction
8698
     * @param $includeInvitedUsers boolean Whether include the invited users
8699
     *
8700
     * @return array
8701
     */
8702
    public static function getTotalTimeReport(
8703
        $from,
8704
        $number_of_items,
8705
        $column,
8706
        $direction,
8707
        $includeInvitedUsers = false
8708
    ) {
8709
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
8710
8711
        $course_code = Database::escape_string($course_code);
8712
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
8713
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8714
        $access_url_id = api_get_current_access_url_id();
8715
8716
        // get all users data from a course for sortable with limit
8717
        if (is_array($user_ids)) {
8718
            $user_ids = array_map('intval', $user_ids);
8719
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
8720
        } else {
8721
            $user_ids = intval($user_ids);
8722
            $condition_user = " WHERE user.user_id = $user_ids ";
8723
        }
8724
8725
        $url_table = null;
8726
        $url_condition = null;
8727
        if (api_is_multiple_url_enabled()) {
8728
            $url_table = ", ".$tbl_url_rel_user." as url_users";
8729
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
8730
        }
8731
8732
        $invitedUsersCondition = '';
8733
        if (!$includeInvitedUsers) {
8734
            $invitedUsersCondition = " AND user.status != ".INVITEE;
8735
        }
8736
8737
        $sql = "SELECT  user.user_id as user_id,
8738
                    user.official_code  as col0,
8739
                    user.lastname       as col1,
8740
                    user.firstname      as col2,
8741
                    user.username       as col3
8742
                FROM $tbl_user as user $url_table
8743
                $condition_user $url_condition $invitedUsersCondition";
8744
8745
        if (!in_array($direction, ['ASC', 'DESC'])) {
8746
            $direction = 'ASC';
8747
        }
8748
8749
        $column = (int) $column;
8750
        $from = (int) $from;
8751
        $number_of_items = (int) $number_of_items;
8752
8753
        $sql .= " ORDER BY col$column $direction ";
8754
        $sql .= " LIMIT $from,$number_of_items";
8755
8756
        $res = Database::query($sql);
8757
        $users = [];
8758
8759
        $sortByFirstName = api_sort_by_first_name();
8760
        $courseInfo = api_get_course_info($course_code);
8761
        $courseId = $courseInfo['real_id'];
8762
8763
        while ($user = Database::fetch_array($res, 'ASSOC')) {
8764
            $user['official_code'] = $user['col0'];
8765
            $user['lastname'] = $user['col1'];
8766
            $user['firstname'] = $user['col2'];
8767
            $user['username'] = $user['col3'];
8768
8769
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
8770
                $user['user_id'],
8771
                $courseId,
8772
                $session_id
8773
            );
8774
8775
            $user['time'] = api_time_to_hms($totalCourseTime);
8776
            $totalLpTime = Tracking::get_time_spent_in_lp(
8777
                $user['user_id'],
8778
                $course_code,
8779
                [],
8780
                $session_id
8781
            );
8782
8783
            $user['total_lp_time'] = $totalLpTime;
8784
            $warning = '';
8785
            if ($totalLpTime > $totalCourseTime) {
8786
                $warning = '&nbsp;'.Display::label(get_lang('TimeDifference'), 'danger');
8787
            }
8788
8789
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
8790
8791
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
8792
                $user['user_id'],
8793
                $courseId,
8794
                $session_id
8795
            );
8796
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
8797
                $user['user_id'],
8798
                $courseInfo,
8799
                $session_id,
8800
                $export_csv === false
8801
            );
8802
8803
            $user['link'] = '<center>
8804
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
8805
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
8806
                             </a>
8807
                         </center>';
8808
8809
            // store columns in array $users
8810
            $user_row = [];
8811
            $user_row['official_code'] = $user['official_code']; //0
8812
            if ($sortByFirstName) {
8813
                $user_row['firstname'] = $user['firstname'];
8814
                $user_row['lastname'] = $user['lastname'];
8815
            } else {
8816
                $user_row['lastname'] = $user['lastname'];
8817
                $user_row['firstname'] = $user['firstname'];
8818
            }
8819
            $user_row['username'] = $user['username'];
8820
            $user_row['time'] = $user['time'];
8821
            $user_row['total_lp_time'] = $user['total_lp_time'];
8822
            $user_row['first_connection'] = $user['first_connection'];
8823
            $user_row['last_connection'] = $user['last_connection'];
8824
8825
            $user_row['link'] = $user['link'];
8826
            $users[] = array_values($user_row);
8827
        }
8828
8829
        return $users;
8830
    }
8831
8832
    /**
8833
     * @param string $current
8834
     */
8835
    public static function actionsLeft($current, $sessionId = 0)
8836
    {
8837
        $usersLink = Display::url(
8838
            Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM),
8839
            'courseLog.php?'.api_get_cidreq(true, false)
8840
        );
8841
8842
        $groupsLink = Display::url(
8843
            Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
8844
            'course_log_groups.php?'.api_get_cidreq()
8845
        );
8846
8847
        $resourcesLink = Display::url(
8848
            Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM),
8849
            'course_log_resources.php?'.api_get_cidreq(true, false)
8850
        );
8851
8852
        $courseLink = Display::url(
8853
            Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
8854
            'course_log_tools.php?'.api_get_cidreq(true, false)
8855
        );
8856
8857
        $examLink = Display::url(
8858
            Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
8859
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
8860
        );
8861
8862
        $eventsLink = Display::url(
8863
            Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
8864
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
8865
        );
8866
8867
        $lpLink = Display::url(
8868
            Display::return_icon('scorms.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
8869
            api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq()
8870
        );
8871
8872
        $attendanceLink = '';
8873
        if (!empty($sessionId)) {
8874
            $attendanceLink = Display::url(
8875
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8876
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
8877
            );
8878
        }
8879
8880
        switch ($current) {
8881
            case 'users':
8882
                $usersLink = Display::url(
8883
                        Display::return_icon(
8884
                        'user_na.png',
8885
                        get_lang('StudentsTracking'),
8886
                        [],
8887
                        ICON_SIZE_MEDIUM
8888
                    ),
8889
                    '#'
8890
                );
8891
                break;
8892
            case 'groups':
8893
                $groupsLink = Display::url(
8894
                    Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
8895
                    '#'
8896
                );
8897
                break;
8898
            case 'courses':
8899
                $courseLink = Display::url(
8900
                    Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
8901
                    '#'
8902
                );
8903
                break;
8904
            case 'resources':
8905
                $resourcesLink = Display::url(
8906
                    Display::return_icon(
8907
                    'tools_na.png',
8908
                    get_lang('ResourcesTracking'),
8909
                    [],
8910
                    ICON_SIZE_MEDIUM
8911
                    ),
8912
                    '#'
8913
                );
8914
                break;
8915
            case 'exams':
8916
                $examLink = Display::url(
8917
                    Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
8918
                    '#'
8919
                );
8920
                break;
8921
            case 'logs':
8922
                $eventsLink = Display::url(
8923
                    Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
8924
                    '#'
8925
                );
8926
                break;
8927
            case 'attendance':
8928
                if (!empty($sessionId)) {
8929
                    $attendanceLink = Display::url(
8930
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8931
                        '#'
8932
                    );
8933
                }
8934
                break;
8935
            case 'lp':
8936
                $lpLink = Display::url(
8937
                    Display::return_icon('scorms_na.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM),
8938
                    '#'
8939
                );
8940
                break;
8941
        }
8942
8943
        $items = [
8944
            $usersLink,
8945
            $groupsLink,
8946
            $courseLink,
8947
            $resourcesLink,
8948
            $examLink,
8949
            $eventsLink,
8950
            $lpLink,
8951
            $attendanceLink,
8952
        ];
8953
8954
        return implode('', $items).'&nbsp;';
8955
    }
8956
}
8957