Passed
Push — 1.11.x ( b1fec8...36e938 )
by Julito
10:30 queued 22s
created

Tracking::displayUserSkills()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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