Passed
Push — 1.11.x ( 913236...0be848 )
by Julito
12:33 queued 14s
created

Tracking::is_allowed_to_coach_student()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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