Passed
Push — 1.11.x ( 390a37...977fbd )
by Julito
09:45
created

Tracking::get_courses_followed_by_coach()   F

Complexity

Conditions 12
Paths 576

Size

Total Lines 90
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 54
c 0
b 0
f 0
nc 576
nop 2
dl 0
loc 90
rs 3.3888

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