Passed
Push — 1.11.x ( fed5eb...182ef7 )
by Julito
11:07
created

Tracking::getCourseLpProgress()   C

Complexity

Conditions 12
Paths 2

Size

Total Lines 140
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 12
eloc 42
c 3
b 0
f 0
nc 2
nop 2
dl 0
loc 140
rs 6.9666

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