Passed
Push — 1.11.x ( a5a0b8...4f166c )
by Julito
13:24
created

Tracking::getCalculateTime()   F

Complexity

Conditions 38
Paths 10353

Size

Total Lines 153
Code Lines 104

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 38
eloc 104
nc 10353
nop 3
dl 0
loc 153
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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