Completed
Push — master ( 024a20...a0a31d )
by Julito
09:32
created

Tracking::get_time_spent_on_the_platform()   C

Complexity

Conditions 14
Paths 168

Size

Total Lines 86
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 59
c 0
b 0
f 0
nop 5
dl 0
loc 86
rs 5.7
nc 168

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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\CoreBundle\Framework\Container;
9
use Chamilo\CoreBundle\Entity\User;
10
use ChamiloSession as Session;
11
use CpChart\Cache as pCache;
12
use CpChart\Data as pData;
13
use CpChart\Image as pImage;
14
use ExtraField as ExtraFieldModel;
15
16
/**
17
 *  Class Tracking.
18
 *
19
 *  @author  Julio Montoya <[email protected]>
20
 */
21
class Tracking
22
{
23
    /**
24
     * Get group reporting.
25
     *
26
     * @param int    $course_id
27
     * @param int    $sessionId
28
     * @param int    $group_id
29
     * @param string $type
30
     * @param int    $start
31
     * @param int    $limit
32
     * @param int    $sidx
33
     * @param string $sord
34
     * @param array  $where_condition
35
     *
36
     * @return array|null
37
     */
38
    public static function get_group_reporting(
39
        $course_id,
40
        $sessionId = 0,
41
        $group_id = 0,
42
        $type = 'all',
43
        $start = 0,
44
        $limit = 1000,
45
        $sidx = 1,
46
        $sord = 'desc',
47
        $where_condition = []
48
    ) {
49
        $course_id = (int) $course_id;
50
        $sessionId = (int) $sessionId;
51
52
        if (empty($course_id)) {
53
            return null;
54
        }
55
        $courseInfo = api_get_course_info_by_id($course_id);
56
        if ('count' == $type) {
57
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
58
        }
59
60
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
61
        $parsedResult = [];
62
        if (!empty($groupList)) {
63
            foreach ($groupList as $group) {
64
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
65
                $time = 0;
66
                $avg_student_score = 0;
67
                $avg_student_progress = 0;
68
                $work = 0;
69
                $messages = 0;
70
71
                foreach ($users as $user_data) {
72
                    $time += self::get_time_spent_on_the_course(
73
                        $user_data['user_id'],
74
                        $courseInfo['real_id'],
75
                        $sessionId
76
                    );
77
                    $average = self::get_avg_student_score(
78
                        $user_data['user_id'],
79
                        $courseInfo['code'],
80
                        [],
81
                        $sessionId
82
                    );
83
                    if (is_numeric($average)) {
84
                        $avg_student_score += $average;
85
                    }
86
                    $avg_student_progress += self::get_avg_student_progress(
87
                        $user_data['user_id'],
88
                        $courseInfo['code'],
89
                        [],
90
                        $sessionId
91
                    );
92
                    $work += self::count_student_assignments(
93
                        $user_data['user_id'],
94
                        $courseInfo['code'],
95
                        $sessionId
96
                    );
97
                    $messages += self::count_student_messages(
98
                        $user_data['user_id'],
99
                        $courseInfo['code'],
100
                        $sessionId
101
                    );
102
                }
103
104
                $countUsers = count($users);
105
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
106
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
107
108
                $groupItem = [
109
                    'id' => $group['id'],
110
                    'name' => $group['name'],
111
                    'time' => api_time_to_hms($time),
112
                    'progress' => $averageProgress,
113
                    'score' => $averageScore,
114
                    'works' => $work,
115
                    'messages' => $messages,
116
                ];
117
                $parsedResult[] = $groupItem;
118
            }
119
        }
120
121
        return $parsedResult;
122
    }
123
124
    /**
125
     * @param int    $user_id
126
     * @param array  $courseInfo
127
     * @param int    $session_id
128
     * @param string $origin
129
     * @param bool   $export_csv
130
     * @param int    $lp_id
131
     * @param int    $lp_item_id
132
     * @param int    $extendId
133
     * @param int    $extendAttemptId
134
     * @param string $extendedAttempt
135
     * @param string $extendedAll
136
     * @param string $type            classic or simple
137
     * @param bool   $allowExtend     Optional. Allow or not extend te results
138
     *
139
     * @return string
140
     */
141
    public static function getLpStats(
142
        $user_id,
143
        $courseInfo,
144
        $session_id,
145
        $origin,
146
        $export_csv,
147
        $lp_id,
148
        $lp_item_id = null,
149
        $extendId = null,
150
        $extendAttemptId = null,
151
        $extendedAttempt = null,
152
        $extendedAll = null,
153
        $type = 'classic',
154
        $allowExtend = true
155
    ) {
156
        if (empty($courseInfo) || empty($lp_id)) {
157
            return '';
158
        }
159
160
        $hideTime = api_get_configuration_value('hide_lp_time');
161
        $allowNewTracking = api_get_configuration_value('use_new_tracking_in_lp_item');
162
        $lp_id = (int) $lp_id;
163
164
        if ($allowNewTracking) {
165
            $extraField = new ExtraFieldValue('lp');
166
            $result = $extraField->get_values_by_handler_and_field_variable($lp_id, 'track_lp_item');
167
            if (empty($result)) {
168
                $allowNewTracking = false;
169
            } else {
170
                if (isset($result['value']) && 1 == $result['value']) {
171
                    $allowNewTracking = true;
172
                }
173
            }
174
        }
175
176
        $lp_item_id = (int) $lp_item_id;
177
        $user_id = (int) $user_id;
178
        $session_id = (int) $session_id;
179
        $origin = Security::remove_XSS($origin);
180
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
181
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
182
        $course_id = $courseInfo['real_id'];
183
        $courseCode = $courseInfo['code'];
184
        $session_condition = api_get_session_condition($session_id);
185
186
        // Extend all button
187
        $output = '';
188
189
        $extra = '<script>
190
        $(function() {
191
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
192
            $( "#dialog-confirm" ).dialog({
193
                autoOpen: false,
194
                show: "blind",
195
                resizable: false,
196
                height:300,
197
                modal: true
198
            });
199
200
            $(".export").click(function() {
201
                var targetUrl = $(this).attr("href");
202
                $( "#dialog-confirm" ).dialog({
203
                    width:400,
204
                    height:300,
205
                    buttons: {
206
                        "'.addslashes(get_lang('Download')).'": function() {
207
                            var option = $("input[name=add_logo]:checked").val();
208
                            location.href = targetUrl+"&add_logo="+option;
209
                            $(this).dialog("close");
210
                        }
211
                    }
212
                });
213
                $("#dialog-confirm").dialog("open");
214
215
                return false;
216
            });
217
        });
218
        </script>';
219
220
        $extra .= '<div id="dialog-confirm" title="'.get_lang('Please confirm your choice').'">';
221
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
222
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
223
        $extra .= $form->returnForm();
224
        $extra .= '</div>';
225
226
        $output .= $extra;
227
228
        $url_suffix = '&lp_id='.$lp_id;
229
        if ('tracking' === $origin) {
230
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
231
        }
232
233
        $extend_all = 0;
234
        if (!empty($extendedAll)) {
235
            $extend_all_link = Display::url(
236
                Display::return_icon('view_less_stats.gif', get_lang('Hide all attempts')),
237
                api_get_self().'?action=stats'.$url_suffix
238
            );
239
            $extend_all = 1;
240
        } else {
241
            $extend_all_link = Display::url(
242
                Display::return_icon('view_more_stats.gif', get_lang('Show all attempts')),
243
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
244
            );
245
        }
246
247
        if ('tracking' != $origin) {
248
            $output .= '<div class="section-status">';
249
            $output .= Display::page_header(get_lang('My progress'));
250
            $output .= '</div>';
251
        }
252
253
        $actionColumn = null;
254
        if ('classic' === $type) {
255
            $actionColumn = ' <th>'.get_lang('Detail').'</th>';
256
        }
257
258
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('Time').'</th>';
259
        if ($hideTime) {
260
            $timeHeader = '';
261
        }
262
        $output .= '<div class="table-responsive">';
263
        $output .= '<table id="lp_tracking" class="table tracking">
264
            <thead>
265
            <tr class="table-header">
266
                <th width="16">'.(true == $allowExtend ? $extend_all_link : '&nbsp;').'</th>
267
                <th colspan="4">
268
                    '.get_lang('Learning object name').'
269
                </th>
270
                <th colspan="2">
271
                    '.get_lang('Status').'
272
                </th>
273
                <th colspan="2">
274
                    '.get_lang('Score').'
275
                </th>
276
                '.$timeHeader.'
277
                '.$actionColumn.'
278
                </tr>
279
            </thead>
280
            <tbody>
281
        ';
282
283
        // Going through the items using the $items[] array instead of the database order ensures
284
        // we get them in the same order as in the imsmanifest file, which is rather random when using
285
        // the database table.
286
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
287
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
288
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
289
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
290
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
291
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
292
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
293
294
        $sql = "SELECT max(view_count)
295
                FROM $TBL_LP_VIEW
296
                WHERE
297
                    c_id = $course_id AND
298
                    lp_id = $lp_id AND
299
                    user_id = $user_id
300
                    $session_condition";
301
        $res = Database::query($sql);
302
        $view = 0;
303
        if (Database::num_rows($res) > 0) {
304
            $myrow = Database::fetch_array($res);
305
            $view = (int) $myrow[0];
306
        }
307
308
        $counter = 0;
309
        $total_time = 0;
310
        $h = get_lang('h');
311
312
        if (!empty($export_csv)) {
313
            $csvHeaders = [
314
                get_lang('Learning object name'),
315
                get_lang('Status'),
316
                get_lang('Score'),
317
            ];
318
319
            if (false === $hideTime) {
320
                $csvHeaders[] = get_lang('Time');
321
            }
322
323
            $csv_content[] = $csvHeaders;
324
        }
325
326
        $result_disabled_ext_all = true;
327
        $chapterTypes = learnpath::getChapterTypes();
328
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
329
330
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
331
        $timeCourse = [];
332
        if ($minimumAvailable) {
333
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
334
            Session::write('trackTimeCourse', $timeCourse);
335
        }
336
337
        // Show lp items
338
        if (is_array($list) && count($list) > 0) {
339
            foreach ($list as $my_item_id) {
340
                $extend_this = 0;
341
                $order = 'DESC';
342
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
343
                    $extend_this = 1;
344
                    $order = 'ASC';
345
                }
346
347
                // Prepare statement to go through each attempt.
348
                $viewCondition = null;
349
                if (!empty($view)) {
350
                    $viewCondition = " AND v.view_count = $view  ";
351
                }
352
353
                $sql = "SELECT
354
                    iv.status as mystatus,
355
                    v.view_count as mycount,
356
                    iv.score as myscore,
357
                    iv.total_time as mytime,
358
                    i.iid as myid,
359
                    i.lp_id as mylpid,
360
                    iv.lp_view_id as mylpviewid,
361
                    i.title as mytitle,
362
                    i.max_score as mymaxscore,
363
                    iv.max_score as myviewmaxscore,
364
                    i.item_type as item_type,
365
                    iv.view_count as iv_view_count,
366
                    iv.id as iv_id,
367
                    path
368
                FROM $TBL_LP_ITEM as i
369
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
370
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
371
                INNER JOIN $TBL_LP_VIEW as v
372
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
373
                WHERE
374
                    v.c_id = $course_id AND
375
                    i.iid = $my_item_id AND
376
                    i.lp_id = $lp_id  AND
377
                    v.user_id = $user_id AND
378
                    v.session_id = $session_id
379
                    $viewCondition
380
                ORDER BY iv.view_count $order ";
381
382
                $result = Database::query($sql);
383
                $num = Database::num_rows($result);
384
                $time_for_total = 0;
385
                $attemptResult = 0;
386
387
                if ($allowNewTracking && $timeCourse) {
388
                    if (isset($timeCourse['learnpath_detailed']) &&
389
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
390
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
391
                    ) {
392
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
393
                    }
394
                }
395
396
                // Extend all
397
                if (($extend_this || $extend_all) && $num > 0) {
398
                    $row = Database::fetch_array($result);
399
                    $result_disabled_ext_all = false;
400
                    if ('quiz' === $row['item_type']) {
401
                        // Check results_disabled in quiz table.
402
                        $my_path = Database::escape_string($row['path']);
403
                        $sql = "SELECT results_disabled
404
                                FROM $TBL_QUIZ
405
                                WHERE
406
                                    c_id = $course_id AND
407
                                    id ='".$my_path."'";
408
                        $res_result_disabled = Database::query($sql);
409
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
410
411
                        if (Database::num_rows($res_result_disabled) > 0 &&
412
                            1 === (int) $row_result_disabled[0]
413
                        ) {
414
                            $result_disabled_ext_all = true;
415
                        }
416
                    }
417
418
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
419
                    $oddclass = 'row_even';
420
                    if (0 === ($counter % 2)) {
421
                        $oddclass = 'row_odd';
422
                    }
423
                    $extend_link = '';
424
                    if (!empty($inter_num)) {
425
                        $extend_link = Display::url(
426
                            Display::return_icon(
427
                                'visible.png',
428
                                get_lang('Hide attempt view')
429
                            ),
430
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
431
                        );
432
                    }
433
                    $title = $row['mytitle'];
434
435
                    if (empty($title)) {
436
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
437
                    }
438
439
                    if (in_array($row['item_type'], $chapterTypes)) {
440
                        $title = "<h4> $title </h4>";
441
                    }
442
                    $lesson_status = $row['mystatus'];
443
                    $title = Security::remove_XSS($title);
444
                    $counter++;
445
446
                    $action = null;
447
                    if ('classic' === $type) {
448
                        $action = '<td></td>';
449
                    }
450
451
                    if (in_array($row['item_type'], $chapterTypes)) {
452
                        $output .= '<tr class="'.$oddclass.'">
453
                                <td>'.$extend_link.'</td>
454
                                <td colspan="4">
455
                                   '.$title.'
456
                                </td>
457
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
458
                                <td colspan="2"></td>
459
                                <td colspan="2"></td>
460
                                '.$action.'
461
                            </tr>';
462
                        continue;
463
                    } else {
464
                        $output .= '<tr class="'.$oddclass.'">
465
                                <td>'.$extend_link.'</td>
466
                                <td colspan="4">'.$title.'</td>
467
                                <td colspan="2"></td>
468
                                <td colspan="2"></td>
469
                                <td colspan="2"></td>
470
                                '.$action.'
471
                            </tr>';
472
                    }
473
474
                    $attemptCount = 1;
475
                    do {
476
                        // Check if there are interactions below.
477
                        $extend_attempt_link = '';
478
                        $extend_this_attempt = 0;
479
480
                        if ($allowNewTracking && $timeCourse) {
481
                            //$attemptResult = 0;
482
                            if (isset($timeCourse['learnpath_detailed']) &&
483
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
484
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
485
                            ) {
486
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
487
                            }
488
                        }
489
                        if ((
490
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
491
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
492
                            ) &&
493
                            !$extend_all
494
                        ) {
495
                            if ($extendAttemptId == $row['iv_id']) {
496
                                // The extend button for this attempt has been clicked.
497
                                $extend_this_attempt = 1;
498
                                $extend_attempt_link = Display::url(
499
                                    Display::return_icon('visible.png', get_lang('Hide attempt view')),
500
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
501
                                );
502
                                if ($accessToPdfExport) {
503
                                    $extend_attempt_link .= '&nbsp;'.
504
                                        Display::url(
505
                                            Display::return_icon('pdf.png', get_lang('Export to PDF')),
506
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
507
                                            ['class' => 'export']
508
                                        );
509
                                }
510
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
511
                                // The extend button for this attempt has not been clicked.
512
                                $extend_attempt_link = Display::url(
513
                                    Display::return_icon('invisible.png', get_lang('Extend attempt view')),
514
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
515
                                );
516
                                if ($accessToPdfExport) {
517
                                    $extend_attempt_link .= '&nbsp;'.
518
                                        Display::url(
519
                                            Display::return_icon('pdf.png', get_lang('Export to PDF')),
520
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
521
                                            ['class' => 'export']
522
                                        );
523
                                }
524
                            }
525
                        }
526
527
                        $oddclass = 'row_even';
528
                        if (0 == ($counter % 2)) {
529
                            $oddclass = 'row_odd';
530
                        }
531
532
                        $lesson_status = $row['mystatus'];
533
                        $score = $row['myscore'];
534
                        $time_for_total = $row['mytime'];
535
                        $attemptTime = $row['mytime'];
536
537
                        if ($minimumAvailable) {
538
                            $lp_time = $timeCourse[TOOL_LEARNPATH];
539
                            $lpTime = null;
540
                            if (isset($lp_time[$lp_id])) {
541
                                $lpTime = (int) $lp_time[$lp_id];
542
                            }
543
                            $time_for_total = $lpTime;
544
545
                            if ($allowNewTracking) {
546
                                $time_for_total = (int) $attemptResult;
547
                                $attemptTime = (int) $attemptResult;
548
                            }
549
                        }
550
551
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
552
553
                        if (0 == $score) {
554
                            $maxscore = $row['mymaxscore'];
555
                        } else {
556
                            if ('sco' === $row['item_type']) {
557
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
558
                                    $maxscore = $row['myviewmaxscore'];
559
                                } elseif ('' === $row['myviewmaxscore']) {
560
                                    $maxscore = 0;
561
                                } else {
562
                                    $maxscore = $row['mymaxscore'];
563
                                }
564
                            } else {
565
                                $maxscore = $row['mymaxscore'];
566
                            }
567
                        }
568
569
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
570
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
571
572
                        if ('dir' !== $row['item_type']) {
573
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
574
                                $view_score = Display::return_icon(
575
                                    'invisible.png',
576
                                    get_lang('Results hidden by the exercise setting')
577
                                );
578
                            } else {
579
                                switch ($row['item_type']) {
580
                                    case 'sco':
581
                                        if (0 == $maxscore) {
582
                                            $view_score = $score;
583
                                        } else {
584
                                            $view_score = ExerciseLib::show_score(
585
                                                $score,
586
                                                $maxscore,
587
                                                false
588
                                            );
589
                                        }
590
                                        break;
591
                                    case 'document':
592
                                        $view_score = (0 == $score ? '/' : ExerciseLib::show_score($score, $maxscore, false));
593
                                        break;
594
                                    default:
595
                                        $view_score = ExerciseLib::show_score(
596
                                            $score,
597
                                            $maxscore,
598
                                            false
599
                                        );
600
                                        break;
601
                                }
602
                            }
603
604
                            $action = null;
605
                            if ('classic' == $type) {
606
                                $action = '<td></td>';
607
                            }
608
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
609
                            if ($hideTime) {
610
                                $timeRow = '';
611
                            }
612
                            $output .= '<tr class="'.$oddclass.'">
613
                                    <td></td>
614
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
615
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
616
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
617
                                    <td colspan="2">'.$view_score.'</td>
618
                                    '.$timeRow.'
619
                                    '.$action.'
620
                                </tr>';
621
                            $attemptCount++;
622
                            if (!empty($export_csv)) {
623
                                $temp = [];
624
                                $temp[] = $title = Security::remove_XSS($title);
625
                                $temp[] = Security::remove_XSS(
626
                                    learnpathItem::humanize_status($lesson_status, false, $type)
627
                                );
628
629
                                if ('quiz' === $row['item_type']) {
630
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
631
                                        $temp[] = '/';
632
                                    } else {
633
                                        $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
634
                                    }
635
                                } else {
636
                                    $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
637
                                }
638
639
                                if (false === $hideTime) {
640
                                    $temp[] = $time;
641
                                }
642
                                $csv_content[] = $temp;
643
                            }
644
                        }
645
646
                        $counter++;
647
                        $action = null;
648
                        if ('classic' === $type) {
649
                            $action = '<td></td>';
650
                        }
651
652
                        if ($extend_this_attempt || $extend_all) {
653
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
654
                            foreach ($list1 as $id => $interaction) {
655
                                $oddclass = 'row_even';
656
                                if (0 == ($counter % 2)) {
657
                                    $oddclass = 'row_odd';
658
                                }
659
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
660
                                if ($hideTime) {
661
                                    $timeRow = '';
662
                                }
663
664
                                $output .= '<tr class="'.$oddclass.'">
665
                                        <td></td>
666
                                        <td></td>
667
                                        <td></td>
668
                                        <td>'.$interaction['order_id'].'</td>
669
                                        <td>'.$interaction['id'].'</td>';
670
671
                                $output .= '
672
                                        <td colspan="2">'.$interaction['type'].'</td>
673
                                        <td>'.$interaction['student_response_formatted'].'</td>
674
                                        <td>'.$interaction['result'].'</td>
675
                                        <td>'.$interaction['latency'].'</td>
676
                                        '.$timeRow.'
677
                                        '.$action.'
678
                                    </tr>';
679
                                $counter++;
680
                            }
681
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
682
                            foreach ($list2 as $id => $interaction) {
683
                                $oddclass = 'row_even';
684
                                if (0 === ($counter % 2)) {
685
                                    $oddclass = 'row_odd';
686
                                }
687
                                $output .= '<tr class="'.$oddclass.'">
688
                                        <td></td>
689
                                        <td></td>
690
                                        <td></td>
691
                                        <td>'.$interaction['order_id'].'</td>
692
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
693
                                        <td colspan="2">'.$interaction['status'].'</td>
694
                                        <td>'.$interaction['score_raw'].'</td>
695
                                        <td>'.$interaction['score_max'].'</td>
696
                                        <td>'.$interaction['score_min'].'</td>
697
                                        '.$action.'
698
                                     </tr>';
699
                                $counter++;
700
                            }
701
                        }
702
                    } while ($row = Database::fetch_array($result));
703
                } elseif ($num > 0) {
704
                    // Not extended.
705
                    $row = Database::fetch_array($result, 'ASSOC');
706
                    $my_id = $row['myid'];
707
                    $my_lp_id = $row['mylpid'];
708
                    $my_lp_view_id = $row['mylpviewid'];
709
                    $my_path = $row['path'];
710
                    $result_disabled_ext_all = false;
711
                    if ('quiz' === $row['item_type']) {
712
                        // Check results_disabled in quiz table.
713
                        $my_path = Database::escape_string($my_path);
714
                        $sql = "SELECT results_disabled
715
                                FROM $TBL_QUIZ
716
                                WHERE c_id = $course_id AND id = '$my_path' ";
717
                        $res_result_disabled = Database::query($sql);
718
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
719
720
                        if (Database::num_rows($res_result_disabled) > 0 &&
721
                            1 === (int) $row_result_disabled[0]
722
                        ) {
723
                            $result_disabled_ext_all = true;
724
                        }
725
                    }
726
727
                    // Check if there are interactions below
728
                    $extend_this_attempt = 0;
729
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
730
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
731
                    $extend_attempt_link = '';
732
                    if ($inter_num > 0 || $objec_num > 0) {
733
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
734
                            // The extend button for this attempt has been clicked.
735
                            $extend_this_attempt = 1;
736
                            $extend_attempt_link = Display::url(
737
                                Display::return_icon('visible.png', get_lang('Hide attempt view')),
738
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
739
                            );
740
                        } else {
741
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
742
                            // The extend button for this attempt has not been clicked.
743
                            $extend_attempt_link = Display::url(
744
                                Display::return_icon('invisible.png', get_lang('Extend attempt view')),
745
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
746
                            );
747
                        }
748
                    }
749
750
                    $oddclass = 'row_even';
751
                    if (0 == ($counter % 2)) {
752
                        $oddclass = 'row_odd';
753
                    }
754
755
                    $extend_link = '';
756
                    if ($inter_num > 1) {
757
                        $extend_link = Display::url(
758
                            Display::return_icon('invisible.png', get_lang('Extend attempt view')),
759
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
760
                        );
761
                    }
762
763
                    $lesson_status = $row['mystatus'];
764
                    $score = $row['myscore'];
765
                    $subtotal_time = $row['mytime'];
766
                    while ($tmp_row = Database::fetch_array($result)) {
767
                        $subtotal_time += $tmp_row['mytime'];
768
                    }
769
770
                    if ($allowNewTracking) {
771
                        $subtotal_time = $attemptResult;
772
                    }
773
774
                    $title = $row['mytitle'];
775
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
776
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
777
                            WHERE
778
                                exe_exo_id="'.$row['path'].'" AND
779
                                exe_user_id="'.$user_id.'" AND
780
                                orig_lp_id = "'.$lp_id.'" AND
781
                                orig_lp_item_id = "'.$row['myid'].'" AND
782
                                c_id = '.$course_id.' AND
783
                                status <> "incomplete" AND
784
                                session_id = '.$session_id.'
785
                             ORDER BY exe_date DESC
786
                             LIMIT 1';
787
788
                    $resultLastAttempt = Database::query($sql);
789
                    $num = Database::num_rows($resultLastAttempt);
790
                    $id_last_attempt = null;
791
                    if ($num > 0) {
792
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
793
                            $id_last_attempt = $rowLA['exe_id'];
794
                        }
795
                    }
796
797
                    switch ($row['item_type']) {
798
                        case 'sco':
799
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
800
                                $maxscore = $row['myviewmaxscore'];
801
                            } elseif ('' === $row['myviewmaxscore']) {
802
                                $maxscore = 0;
803
                            } else {
804
                                $maxscore = $row['mymaxscore'];
805
                            }
806
                            break;
807
                        case 'quiz':
808
                            // Get score and total time from last attempt of a exercise en lp.
809
                            $sql = "SELECT iid, score
810
                                    FROM $TBL_LP_ITEM_VIEW
811
                                    WHERE
812
                                        c_id = $course_id AND
813
                                        lp_item_id = '".(int) $my_id."' AND
814
                                        lp_view_id = '".(int) $my_lp_view_id."'
815
                                    ORDER BY view_count DESC
816
                                    LIMIT 1";
817
                            $res_score = Database::query($sql);
818
                            $row_score = Database::fetch_array($res_score);
819
820
                            $sql = "SELECT SUM(total_time) as total_time
821
                                    FROM $TBL_LP_ITEM_VIEW
822
                                    WHERE
823
                                        c_id = $course_id AND
824
                                        lp_item_id = '".(int) $my_id."' AND
825
                                        lp_view_id = '".(int) $my_lp_view_id."'";
826
                            $res_time = Database::query($sql);
827
                            $row_time = Database::fetch_array($res_time);
828
829
                            $score = 0;
830
                            $subtotal_time = 0;
831
                            if (Database::num_rows($res_score) > 0 &&
832
                                Database::num_rows($res_time) > 0
833
                            ) {
834
                                $score = (float) $row_score['score'];
835
                                $subtotal_time = (int) $row_time['total_time'];
836
                            }
837
                            // Selecting the max score from an attempt.
838
                            $sql = "SELECT SUM(t.ponderation) as maxscore
839
                                    FROM (
840
                                        SELECT DISTINCT
841
                                            question_id, marks, ponderation
842
                                        FROM $tbl_stats_attempts as at
843
                                        INNER JOIN $tbl_quiz_questions as q
844
                                        ON (q.id = at.question_id AND q.c_id = $course_id)
845
                                        WHERE exe_id ='$id_last_attempt'
846
                                    ) as t";
847
848
                            $result = Database::query($sql);
849
                            $row_max_score = Database::fetch_array($result);
850
                            $maxscore = $row_max_score['maxscore'];
851
852
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
853
                            $sql = 'SELECT SUM(exe_duration) exe_duration
854
                                    FROM '.$tbl_stats_exercices.'
855
                                    WHERE
856
                                        exe_exo_id="'.$row['path'].'" AND
857
                                        exe_user_id="'.$user_id.'" AND
858
                                        orig_lp_id = "'.$lp_id.'" AND
859
                                        orig_lp_item_id = "'.$row['myid'].'" AND
860
                                        c_id = '.$course_id.' AND
861
                                        status <> "incomplete" AND
862
                                        session_id = '.$session_id.'
863
                                     ORDER BY exe_date DESC ';
864
                            $sumScoreResult = Database::query($sql);
865
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
866
                            if (!empty($durationRow['exe_duration'])) {
867
                                $exeDuration = $durationRow['exe_duration'];
868
                                if ($exeDuration != $subtotal_time &&
869
                                    !empty($row_score['iid']) &&
870
                                    !empty($exeDuration)
871
                                ) {
872
                                    $subtotal_time = $exeDuration;
873
                                    // Update c_lp_item_view.total_time
874
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
875
                                                  WHERE iid = ".$row_score['iid'];
876
                                    Database::query($sqlUpdate);
877
                                }
878
                            }
879
                            break;
880
                        default:
881
                            $maxscore = $row['mymaxscore'];
882
                            break;
883
                    }
884
885
                    $time_for_total = $subtotal_time;
886
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
887
                    if (empty($title)) {
888
                        $title = learnpath::rl_get_resource_name(
889
                            $courseInfo['code'],
890
                            $lp_id,
891
                            $row['myid']
892
                        );
893
                    }
894
895
                    $action = null;
896
                    if ('classic' == $type) {
897
                        $action = '<td></td>';
898
                    }
899
900
                    if (in_array($row['item_type'], $chapterTypes)) {
901
                        $title = Security::remove_XSS($title);
902
                        $output .= '<tr class="'.$oddclass.'">
903
                                <td>'.$extend_link.'</td>
904
                                <td colspan="4">
905
                                <h4>'.$title.'</h4>
906
                                </td>
907
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
908
                                <td colspan="2"></td>
909
                                <td colspan="2"></td>
910
                                '.$action.'
911
                            </tr>';
912
                    } else {
913
                        $correct_test_link = '-';
914
                        $showRowspan = false;
915
                        if ('quiz' === $row['item_type']) {
916
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
917
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
918
                                     WHERE
919
                                        exe_exo_id="'.$row['path'].'" AND
920
                                        exe_user_id="'.$user_id.'" AND
921
                                        orig_lp_id = "'.$lp_id.'" AND
922
                                        orig_lp_item_id = "'.$row['myid'].'" AND
923
                                        c_id = '.$course_id.' AND
924
                                        status <> "incomplete" AND
925
                                        session_id = '.$session_id.'
926
                                     ORDER BY exe_date DESC ';
927
928
                            $resultLastAttempt = Database::query($sql);
929
                            $num = Database::num_rows($resultLastAttempt);
930
                            $showRowspan = false;
931
                            if ($num > 0) {
932
                                $linkId = 'link_'.$my_id;
933
                                if (1 == $extendedAttempt &&
934
                                    $lp_id == $my_lp_id &&
935
                                    $lp_item_id == $my_id
936
                                ) {
937
                                    $showRowspan = true;
938
                                    $correct_test_link = Display::url(
939
                                        Display::return_icon(
940
                                            'view_less_stats.gif',
941
                                            get_lang('Hide all attempts')
942
                                        ),
943
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
944
                                        ['id' => $linkId]
945
                                    );
946
                                } else {
947
                                    $correct_test_link = Display::url(
948
                                        Display::return_icon(
949
                                            'view_more_stats.gif',
950
                                            get_lang(
951
                                                'Show all attemptsByExercise'
952
                                            )
953
                                        ),
954
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
955
                                        ['id' => $linkId]
956
                                    );
957
                                }
958
                            }
959
                        }
960
961
                        $title = Security::remove_XSS($title);
962
                        $action = null;
963
                        if ('classic' === $type) {
964
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
965
                        }
966
967
                        if ($lp_id == $my_lp_id && false) {
968
                            $output .= '<tr class ='.$oddclass.'>
969
                                    <td>'.$extend_link.'</td>
970
                                    <td colspan="4">'.$title.'</td>
971
                                    <td colspan="2">&nbsp;</td>
972
                                    <td colspan="2">&nbsp;</td>
973
                                    <td colspan="2">&nbsp;</td>
974
                                    '.$action.'
975
                                </tr>';
976
                            $output .= '</tr>';
977
                        } else {
978
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
979
                                $output .= "<tr class='$oddclass'>";
980
                            } else {
981
                                $output .= "<tr class='$oddclass'>";
982
                            }
983
984
                            $scoreItem = null;
985
                            if ('quiz' == $row['item_type']) {
986
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
987
                                    $scoreItem .= Display::return_icon(
988
                                        'invisible.png',
989
                                        get_lang('Results hidden by the exercise setting')
990
                                    );
991
                                } else {
992
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
993
                                }
994
                            } else {
995
                                $scoreItem .= 0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.$maxscore);
996
                            }
997
998
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
999
                            if ($hideTime) {
1000
                                $timeRow = '';
1001
                            }
1002
1003
                            $output .= '
1004
                                <td>'.$extend_link.'</td>
1005
                                <td colspan="4">'.$title.'</td>
1006
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1007
                                <td colspan="2">'.$scoreItem.'</td>
1008
                                '.$timeRow.'
1009
                                '.$action.'
1010
                             ';
1011
                            $output .= '</tr>';
1012
                        }
1013
1014
                        if (!empty($export_csv)) {
1015
                            $temp = [];
1016
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1017
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1018
                            if ('quiz' === $row['item_type']) {
1019
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1020
                                    $temp[] = '/';
1021
                                } else {
1022
                                    $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1023
                                }
1024
                            } else {
1025
                                $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1026
                            }
1027
1028
                            if (false === $hideTime) {
1029
                                $temp[] = $time;
1030
                            }
1031
                            $csv_content[] = $temp;
1032
                        }
1033
                    }
1034
1035
                    $counter++;
1036
                    $action = null;
1037
                    if ('classic' === $type) {
1038
                        $action = '<td></td>';
1039
                    }
1040
1041
                    if ($extend_this_attempt || $extend_all) {
1042
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1043
                        foreach ($list1 as $id => $interaction) {
1044
                            $oddclass = 'row_even';
1045
                            if (($counter % 2) == 0) {
1046
                                $oddclass = 'row_odd';
1047
                            }
1048
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1049
                            if ($hideTime) {
1050
                                $timeRow = '';
1051
                            }
1052
1053
                            $output .= '<tr class="'.$oddclass.'">
1054
                                    <td></td>
1055
                                    <td></td>
1056
                                    <td></td>
1057
                                    <td>'.$interaction['order_id'].'</td>
1058
                                    <td>'.$interaction['id'].'</td>
1059
                                    <td colspan="2">'.$interaction['type'].'</td>
1060
                                    <td>'.urldecode($interaction['student_response']).'</td>
1061
                                    <td>'.$interaction['result'].'</td>
1062
                                    <td>'.$interaction['latency'].'</td>
1063
                                    '.$timeRow.'
1064
                                    '.$action.'
1065
                               </tr>';
1066
                            $counter++;
1067
                        }
1068
1069
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1070
                        foreach ($list2 as $id => $interaction) {
1071
                            $oddclass = 'row_even';
1072
                            if (($counter % 2) == 0) {
1073
                                $oddclass = 'row_odd';
1074
                            }
1075
                            $output .= '<tr class="'.$oddclass.'">
1076
                                    <td></td>
1077
                                    <td></td>
1078
                                    <td></td>
1079
                                    <td>'.$interaction['order_id'].'</td>
1080
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1081
                                    <td colspan="2">'.$interaction['status'].'</td>
1082
                                    <td>'.$interaction['score_raw'].'</td>
1083
                                    <td>'.$interaction['score_max'].'</td>
1084
                                    <td>'.$interaction['score_min'].'</td>
1085
                                    '.$action.'
1086
                               </tr>';
1087
                            $counter++;
1088
                        }
1089
                    }
1090
1091
                    // Attempts listing by exercise.
1092
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1093
                        // Get attempts of a exercise.
1094
                        if (!empty($lp_id) &&
1095
                            !empty($lp_item_id) &&
1096
                            'quiz' === $row['item_type']
1097
                        ) {
1098
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1099
                                    WHERE
1100
                                        c_id = $course_id AND
1101
                                        iid = '$lp_item_id' AND
1102
                                        lp_id = '$lp_id'";
1103
                            $res_path = Database::query($sql);
1104
                            $row_path = Database::fetch_array($res_path);
1105
1106
                            if (Database::num_rows($res_path) > 0) {
1107
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1108
                                        WHERE
1109
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1110
                                            status <> "incomplete" AND
1111
                                            exe_user_id="'.$user_id.'" AND
1112
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1113
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1114
                                            c_id = '.$course_id.'  AND
1115
                                            session_id = '.$session_id.'
1116
                                        ORDER BY exe_date';
1117
                                $res_attempts = Database::query($sql);
1118
                                $num_attempts = Database::num_rows($res_attempts);
1119
                                if ($num_attempts > 0) {
1120
                                    $n = 1;
1121
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1122
                                        $my_score = $row_attempts['score'];
1123
                                        $my_maxscore = $row_attempts['max_score'];
1124
                                        $my_exe_id = $row_attempts['exe_id'];
1125
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1126
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1127
                                        $time_attemp = ' - ';
1128
                                        if ($mktime_start_date && $mktime_exe_date) {
1129
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1130
                                        }
1131
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1132
                                            $view_score = Display::return_icon(
1133
                                                'invisible.png',
1134
                                                get_lang(
1135
                                                    'Results hidden by the exercise setting'
1136
                                                )
1137
                                            );
1138
                                        } else {
1139
                                            // Show only float when need it
1140
                                            if (0 == $my_score) {
1141
                                                $view_score = ExerciseLib::show_score(
1142
                                                    0,
1143
                                                    $my_maxscore,
1144
                                                    false
1145
                                                );
1146
                                            } else {
1147
                                                if (0 == $my_maxscore) {
1148
                                                    $view_score = $my_score;
1149
                                                } else {
1150
                                                    $view_score = ExerciseLib::show_score(
1151
                                                        $my_score,
1152
                                                        $my_maxscore,
1153
                                                        false
1154
                                                    );
1155
                                                }
1156
                                            }
1157
                                        }
1158
                                        $my_lesson_status = $row_attempts['status'];
1159
                                        if ('' == $my_lesson_status) {
1160
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1161
                                        } elseif ('incomplete' == $my_lesson_status) {
1162
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1163
                                        }
1164
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1165
                                        if ($hideTime) {
1166
                                            $timeRow = '';
1167
                                        }
1168
1169
                                        $output .= '<tr class="'.$oddclass.'" >
1170
                                        <td></td>
1171
                                        <td>'.$extend_attempt_link.'</td>
1172
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1173
                                        <td colspan="2">'.$my_lesson_status.'</td>
1174
                                        <td colspan="2">'.$view_score.'</td>
1175
                                        '.$timeRow;
1176
1177
                                        if ('classic' == $action) {
1178
                                            if ('tracking' != $origin) {
1179
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1180
                                                    $output .= '<td>
1181
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('Show attempt').'" title="'.get_lang('Show attempt').'">
1182
                                                            </td>';
1183
                                                } else {
1184
                                                    $output .= '<td>
1185
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1186
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('Show attempt').'" title="'.get_lang('Show attempt').'">
1187
                                                            </a></td>';
1188
                                                }
1189
                                            } else {
1190
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1191
                                                    $output .= '<td>
1192
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('Show and grade attempt').'" title="'.get_lang('Show and grade attempt').'"></td>';
1193
                                                } else {
1194
                                                    $output .= '<td>
1195
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1196
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('Show and grade attempt').'" title="'.get_lang('Show and grade attempt').'"></a></td>';
1197
                                                }
1198
                                            }
1199
                                        }
1200
                                        $output .= '</tr>';
1201
                                        $n++;
1202
                                    }
1203
                                }
1204
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1205
                            }
1206
                        }
1207
                    }
1208
                }
1209
1210
                $total_time += $time_for_total;
1211
                // QUIZZ IN LP
1212
                $a_my_id = [];
1213
                if (!empty($my_lp_id)) {
1214
                    $a_my_id[] = $my_lp_id;
1215
                }
1216
            }
1217
        }
1218
1219
        // NOT Extend all "left green cross"
1220
        if (!empty($a_my_id)) {
1221
            if ($extendedAttempt) {
1222
                // "Right green cross" extended
1223
                $total_score = self::get_avg_student_score(
1224
                    $user_id,
1225
                    $course_id,
1226
                    $a_my_id,
1227
                    $session_id,
1228
                    false,
1229
                    false
1230
                );
1231
            } else {
1232
                // "Left green cross" extended
1233
                $total_score = self::get_avg_student_score(
1234
                    $user_id,
1235
                    $course_id,
1236
                    $a_my_id,
1237
                    $session_id,
1238
                    false,
1239
                    true
1240
                );
1241
            }
1242
        } else {
1243
            // Extend all "left green cross"
1244
            $total_score = self::get_avg_student_score(
1245
                $user_id,
1246
                $course_id,
1247
                [$lp_id],
1248
                $session_id,
1249
                false,
1250
                false
1251
            );
1252
        }
1253
1254
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1255
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1256
1257
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1258
            $final_score = Display::return_icon('invisible.png', get_lang('Results hidden by the exercise setting'));
1259
            $finalScoreToCsv = get_lang('Results hidden by the exercise setting');
1260
        } else {
1261
            if (is_numeric($total_score)) {
1262
                $final_score = $total_score.'%';
1263
            } else {
1264
                $final_score = $total_score;
1265
            }
1266
            $finalScoreToCsv = $final_score;
1267
        }
1268
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1269
1270
        $oddclass = 'row_even';
1271
        if (0 == ($counter % 2)) {
1272
            $oddclass = 'row_odd';
1273
        }
1274
1275
        $action = null;
1276
        if ('classic' === $type) {
1277
            $action = '<td></td>';
1278
        }
1279
1280
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1281
        if ($hideTime) {
1282
            $timeTotal = '';
1283
        }
1284
1285
        $output .= '<tr class="'.$oddclass.'">
1286
                <td></td>
1287
                <td colspan="4">
1288
                    <i>'.get_lang('Total of completed learning objects').'</i>
1289
                </td>
1290
                <td colspan="2">'.$progress.'%</td>
1291
                <td colspan="2">'.$final_score.'</td>
1292
                '.$timeTotal.'
1293
                '.$action.'
1294
           </tr>';
1295
1296
        $output .= '
1297
                    </tbody>
1298
                </table>
1299
            </div>
1300
        ';
1301
1302
        if (!empty($export_csv)) {
1303
            $temp = [
1304
                '',
1305
                '',
1306
                '',
1307
                '',
1308
            ];
1309
            $csv_content[] = $temp;
1310
            $temp = [
1311
                get_lang('Total of completed learning objects'),
1312
                '',
1313
                $finalScoreToCsv,
1314
            ];
1315
1316
            if (false === $hideTime) {
1317
                $temp[] = $total_time;
1318
            }
1319
1320
            $csv_content[] = $temp;
1321
            ob_end_clean();
1322
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1323
            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...
1324
        }
1325
1326
        return $output;
1327
    }
1328
1329
    /**
1330
     * @param int  $userId
1331
     * @param bool $getCount
1332
     *
1333
     * @return array
1334
     */
1335
    public static function getStats($userId, $getCount = false)
1336
    {
1337
        $courses = [];
1338
        $assignedCourses = [];
1339
        $drhCount = 0;
1340
        $teachersCount = 0;
1341
        $studentsCount = 0;
1342
        $studentBossCount = 0;
1343
        $courseCount = 0;
1344
        $sessionCount = 0;
1345
        $assignedCourseCount = 0;
1346
1347
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1348
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1349
                'drh_all',
1350
                $userId,
1351
                false,
1352
                null,
1353
                null,
1354
                null,
1355
                null,
1356
                null,
1357
                null,
1358
                null,
1359
                [],
1360
                [],
1361
                STUDENT
1362
            );
1363
1364
            $students = [];
1365
            if (is_array($studentList)) {
1366
                foreach ($studentList as $studentData) {
1367
                    $students[] = $studentData['user_id'];
1368
                }
1369
            }
1370
1371
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1372
                'drh_all',
1373
                $userId,
1374
                $getCount,
1375
                null,
1376
                null,
1377
                null,
1378
                null,
1379
                null,
1380
                null,
1381
                null,
1382
                [],
1383
                [],
1384
                STUDENT_BOSS
1385
            );
1386
1387
            if ($getCount) {
1388
                $studentBossCount = $studentBossesList;
1389
            } else {
1390
                $studentBosses = [];
1391
                if (is_array($studentBossesList)) {
1392
                    foreach ($studentBossesList as $studentBossData) {
1393
                        $studentBosses[] = $studentBossData['user_id'];
1394
                    }
1395
                }
1396
            }
1397
1398
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1399
                'drh_all',
1400
                $userId,
1401
                $getCount,
1402
                null,
1403
                null,
1404
                null,
1405
                null,
1406
                null,
1407
                null,
1408
                null,
1409
                [],
1410
                [],
1411
                COURSEMANAGER
1412
            );
1413
1414
            if ($getCount) {
1415
                $teachersCount = $teacherList;
1416
            } else {
1417
                $teachers = [];
1418
                foreach ($teacherList as $teacherData) {
1419
                    $teachers[] = $teacherData['user_id'];
1420
                }
1421
            }
1422
1423
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1424
                'drh_all',
1425
                $userId,
1426
                $getCount,
1427
                null,
1428
                null,
1429
                null,
1430
                null,
1431
                null,
1432
                null,
1433
                null,
1434
                [],
1435
                [],
1436
                DRH
1437
            );
1438
1439
            if ($getCount) {
1440
                $drhCount = $humanResources;
1441
            } else {
1442
                $humanResourcesList = [];
1443
                if (is_array($humanResources)) {
1444
                    foreach ($humanResources as $item) {
1445
                        $humanResourcesList[] = $item['user_id'];
1446
                    }
1447
                }
1448
            }
1449
1450
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1451
                $userId,
1452
                null,
1453
                null,
1454
                null,
1455
                null,
1456
                null,
1457
                $getCount
1458
            );
1459
1460
            if ($getCount) {
1461
                $courseCount = $platformCourses;
1462
            } else {
1463
                foreach ($platformCourses as $course) {
1464
                    $courses[$course['code']] = $course['code'];
1465
                }
1466
            }
1467
1468
            $sessions = SessionManager::get_sessions_followed_by_drh(
1469
                $userId,
1470
                null,
1471
                null,
1472
                false
1473
            );
1474
        } else {
1475
            $studentList = UserManager::getUsersFollowedByUser(
1476
                $userId,
1477
                STUDENT,
1478
                false,
1479
                false,
1480
                false,
1481
                null,
1482
                null,
1483
                null,
1484
                null,
1485
                null,
1486
                null,
1487
                COURSEMANAGER
1488
            );
1489
1490
            $students = [];
1491
            if (is_array($studentList)) {
1492
                foreach ($studentList as $studentData) {
1493
                    $students[] = $studentData['user_id'];
1494
                }
1495
            }
1496
1497
            $studentBossesList = UserManager::getUsersFollowedByUser(
1498
                $userId,
1499
                STUDENT_BOSS,
1500
                false,
1501
                false,
1502
                $getCount,
1503
                null,
1504
                null,
1505
                null,
1506
                null,
1507
                null,
1508
                null,
1509
                COURSEMANAGER
1510
            );
1511
1512
            if ($getCount) {
1513
                $studentBossCount = $studentBossesList;
1514
            } else {
1515
                $studentBosses = [];
1516
                if (is_array($studentBossesList)) {
1517
                    foreach ($studentBossesList as $studentBossData) {
1518
                        $studentBosses[] = $studentBossData['user_id'];
1519
                    }
1520
                }
1521
            }
1522
1523
            $teacherList = UserManager::getUsersFollowedByUser(
1524
                $userId,
1525
                COURSEMANAGER,
1526
                false,
1527
                false,
1528
                $getCount,
1529
                null,
1530
                null,
1531
                null,
1532
                null,
1533
                null,
1534
                null,
1535
                COURSEMANAGER
1536
            );
1537
1538
            if ($getCount) {
1539
                $teachersCount = $teacherList;
1540
            } else {
1541
                $teachers = [];
1542
                foreach ($teacherList as $teacherData) {
1543
                    $teachers[] = $teacherData['user_id'];
1544
                }
1545
            }
1546
1547
            $humanResources = UserManager::getUsersFollowedByUser(
1548
                $userId,
1549
                DRH,
1550
                false,
1551
                false,
1552
                $getCount,
1553
                null,
1554
                null,
1555
                null,
1556
                null,
1557
                null,
1558
                null,
1559
                COURSEMANAGER
1560
            );
1561
1562
            if ($getCount) {
1563
                $drhCount = $humanResources;
1564
            } else {
1565
                $humanResourcesList = [];
1566
                foreach ($humanResources as $item) {
1567
                    $humanResourcesList[] = $item['user_id'];
1568
                }
1569
            }
1570
1571
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1572
                $userId,
1573
                COURSEMANAGER,
1574
                null,
1575
                null,
1576
                null,
1577
                null,
1578
                $getCount,
1579
                null,
1580
                null,
1581
                true
1582
            );
1583
1584
            if ($getCount) {
1585
                $assignedCourseCount = $platformCourses;
1586
            } else {
1587
                foreach ($platformCourses as $course) {
1588
                    $assignedCourses[$course['code']] = $course['code'];
1589
                }
1590
            }
1591
1592
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1593
                $userId,
1594
                COURSEMANAGER,
1595
                null,
1596
                null,
1597
                null,
1598
                null,
1599
                $getCount
1600
            );
1601
1602
            if ($getCount) {
1603
                $courseCount = $platformCourses;
1604
            } else {
1605
                foreach ($platformCourses as $course) {
1606
                    $courses[$course['code']] = $course['code'];
1607
                }
1608
            }
1609
1610
            $sessions = SessionManager::getSessionsFollowedByUser(
1611
                $userId,
1612
                COURSEMANAGER,
1613
                null,
1614
                null,
1615
                false
1616
            );
1617
        }
1618
1619
        if ($getCount) {
1620
            return [
1621
                'drh' => $drhCount,
1622
                'teachers' => $teachersCount,
1623
                'student_count' => count($students),
1624
                'student_list' => $students,
1625
                'student_bosses' => $studentBossCount,
1626
                'courses' => $courseCount,
1627
                'session_count' => count($sessions),
1628
                'session_list' => $sessions,
1629
                'assigned_courses' => $assignedCourseCount,
1630
            ];
1631
        }
1632
1633
        return [
1634
            'drh' => $humanResourcesList,
1635
            'teachers' => $teachers,
1636
            'student_list' => $students,
1637
            'student_bosses' => $studentBosses,
1638
            'courses' => $courses,
1639
            'sessions' => $sessions,
1640
            'assigned_courses' => $assignedCourses,
1641
        ];
1642
    }
1643
1644
    /**
1645
     * Calculates the time spent on the platform by a user.
1646
     *
1647
     * @param int|array $userId
1648
     * @param string    $timeFilter type of time filter: 'last_week' or 'custom'
1649
     * @param string    $start_date start date date('Y-m-d H:i:s')
1650
     * @param string    $end_date   end date date('Y-m-d H:i:s')
1651
     * @param bool      $returnAllRecords
1652
     *
1653
     * @return int
1654
     */
1655
    public static function get_time_spent_on_the_platform(
1656
        $userId,
1657
        $timeFilter = 'last_7_days',
1658
        $start_date = null,
1659
        $end_date = null,
1660
        $returnAllRecords = false
1661
    ) {
1662
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1663
        $condition_time = '';
1664
1665
        if (is_array($userId)) {
1666
            $userList = array_map('intval', $userId);
1667
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1668
        } else {
1669
            $userId = (int) $userId;
1670
            $userCondition = " login_user_id = $userId ";
1671
        }
1672
1673
        $url_condition = null;
1674
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1675
        $url_table = null;
1676
        if (api_is_multiple_url_enabled()) {
1677
            $access_url_id = api_get_current_access_url_id();
1678
            $url_table = ", $tbl_url_rel_user as url_users";
1679
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1680
        }
1681
1682
        if (empty($timeFilter)) {
1683
            $timeFilter = 'last_week';
1684
        }
1685
1686
        $today = new DateTime('now', new DateTimeZone('UTC'));
1687
1688
        switch ($timeFilter) {
1689
            case 'last_7_days':
1690
                $newDate = new DateTime('-7 day', 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 'last_30_days':
1695
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1696
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1697
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1698
                break;
1699
            case 'wide':
1700
                if (!empty($start_date) && !empty($end_date)) {
1701
                    $start_date = Database::escape_string($start_date);
1702
                    $end_date = Database::escape_string($end_date);
1703
                    $condition_time = ' AND (
1704
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
1705
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
1706
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
1707
                    ) ';
1708
                }
1709
                break;
1710
            case 'custom':
1711
                if (!empty($start_date) && !empty($end_date)) {
1712
                    $start_date = Database::escape_string($start_date);
1713
                    $end_date = Database::escape_string($end_date);
1714
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1715
                }
1716
                break;
1717
        }
1718
1719
        if ($returnAllRecords) {
1720
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
1721
                    FROM $tbl_track_login u $url_table
1722
                    WHERE $userCondition $condition_time $url_condition
1723
                    ORDER BY login_date";
1724
            $rs = Database::query($sql);
1725
1726
            return Database::store_result($rs, 'ASSOC');
1727
        }
1728
1729
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1730
    	        FROM $tbl_track_login u $url_table
1731
                WHERE $userCondition $condition_time $url_condition";
1732
        $rs = Database::query($sql);
1733
        $row = Database::fetch_array($rs, 'ASSOC');
1734
        $diff = $row['diff'];
1735
1736
        if ($diff >= 0) {
1737
            return $diff;
1738
        }
1739
1740
        return -1;
1741
    }
1742
1743
    /**
1744
     * @param string $startDate
1745
     * @param string $endDate
1746
     *
1747
     * @return int
1748
     */
1749
    public static function getTotalTimeSpentOnThePlatform(
1750
        $startDate = '',
1751
        $endDate = ''
1752
    ) {
1753
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1754
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1755
1756
        $url_table = null;
1757
        $url_condition = null;
1758
        if (api_is_multiple_url_enabled()) {
1759
            $access_url_id = api_get_current_access_url_id();
1760
            $url_table = ", ".$tbl_url_rel_user." as url_users";
1761
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1762
        }
1763
1764
        if (!empty($startDate) && !empty($endDate)) {
1765
            $startDate = Database::escape_string($startDate);
1766
            $endDate = Database::escape_string($endDate);
1767
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
1768
        }
1769
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1770
    	        FROM $tbl_track_login u $url_table
1771
                WHERE $condition_time $url_condition";
1772
        $rs = Database::query($sql);
1773
        $row = Database::fetch_array($rs, 'ASSOC');
1774
        $diff = $row['diff'];
1775
1776
        if ($diff >= 0) {
1777
            return $diff;
1778
        }
1779
1780
        return -1;
1781
    }
1782
1783
    /**
1784
     * Checks if the "lp_minimum_time" feature is available for the course.
1785
     *
1786
     * @param int $sessionId
1787
     * @param int $courseId
1788
     *
1789
     * @return bool
1790
     */
1791
    public static function minimumTimeAvailable($sessionId, $courseId)
1792
    {
1793
        if (!api_get_configuration_value('lp_minimum_time')) {
1794
            return false;
1795
        }
1796
1797
        if (!empty($sessionId)) {
1798
            $extraFieldValue = new ExtraFieldValue('session');
1799
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
1800
1801
            if ($value && isset($value['value']) && 1 == $value['value']) {
1802
                return true;
1803
            }
1804
        } else {
1805
            if ($courseId) {
1806
                $extraFieldValue = new ExtraFieldValue('course');
1807
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
1808
                if ($value && isset($value['value']) && 1 == $value['value']) {
1809
                    return true;
1810
                }
1811
            }
1812
        }
1813
1814
        return false;
1815
    }
1816
1817
    /**
1818
     * Calculates the time spent on the course.
1819
     *
1820
     * @param int $user_id
1821
     * @param int $courseId
1822
     * @param int $session_id
1823
     *
1824
     * @return int Time in seconds
1825
     */
1826
    public static function get_time_spent_on_the_course(
1827
        $user_id,
1828
        $courseId,
1829
        $session_id = 0
1830
    ) {
1831
        $courseId = (int) $courseId;
1832
1833
        if (empty($courseId) || empty($user_id)) {
1834
            return 0;
1835
        }
1836
1837
        if (self::minimumTimeAvailable($session_id, $courseId)) {
1838
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
1839
1840
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
1841
        }
1842
1843
        $conditionUser = '';
1844
        $session_id = (int) $session_id;
1845
        if (is_array($user_id)) {
1846
            $user_id = array_map('intval', $user_id);
1847
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
1848
        } else {
1849
            if (!empty($user_id)) {
1850
                $user_id = (int) $user_id;
1851
                $conditionUser = " AND user_id = $user_id ";
1852
            }
1853
        }
1854
1855
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1856
        $sql = "SELECT
1857
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1858
                FROM $table
1859
                WHERE
1860
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
1861
                    c_id = '$courseId' ";
1862
1863
        if (-1 != $session_id) {
1864
            $sql .= "AND session_id = '$session_id' ";
1865
        }
1866
1867
        $sql .= $conditionUser;
1868
1869
        $rs = Database::query($sql);
1870
        $row = Database::fetch_array($rs);
1871
1872
        return $row['nb_seconds'];
1873
    }
1874
1875
    /**
1876
     * Get first connection date for a student.
1877
     *
1878
     * @param int $student_id
1879
     *
1880
     * @return string|bool Date format long without day or false if there are no connections
1881
     */
1882
    public static function get_first_connection_date($student_id)
1883
    {
1884
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1885
        $sql = 'SELECT login_date
1886
                FROM '.$table.'
1887
                WHERE login_user_id = '.intval($student_id).'
1888
                ORDER BY login_date ASC
1889
                LIMIT 0,1';
1890
1891
        $rs = Database::query($sql);
1892
        if (Database::num_rows($rs) > 0) {
1893
            if ($first_login_date = Database::result($rs, 0, 0)) {
1894
                return api_convert_and_format_date(
1895
                    $first_login_date,
1896
                    DATE_FORMAT_SHORT
1897
                );
1898
            }
1899
        }
1900
1901
        return false;
1902
    }
1903
1904
    /**
1905
     * Get las connection date for a student.
1906
     *
1907
     * @param int  $student_id
1908
     * @param bool $warning_message  Show a warning message (optional)
1909
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1910
     *
1911
     * @return string|int|bool Date format long without day, false if there are no connections or
1912
     *                         timestamp if parameter $return_timestamp is true
1913
     */
1914
    public static function get_last_connection_date(
1915
        $student_id,
1916
        $warning_message = false,
1917
        $return_timestamp = false
1918
    ) {
1919
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1920
        $sql = 'SELECT login_date
1921
                FROM '.$table.'
1922
                WHERE login_user_id = '.intval($student_id).'
1923
                ORDER BY login_date
1924
                DESC LIMIT 0,1';
1925
1926
        $rs = Database::query($sql);
1927
        if (Database::num_rows($rs) > 0) {
1928
            if ($last_login_date = Database::result($rs, 0, 0)) {
1929
                $last_login_date = api_get_local_time($last_login_date);
1930
                if ($return_timestamp) {
1931
                    return api_strtotime($last_login_date, 'UTC');
1932
                } else {
1933
                    if (!$warning_message) {
1934
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1935
                    } else {
1936
                        $timestamp = api_strtotime($last_login_date, 'UTC');
1937
                        $currentTimestamp = time();
1938
1939
                        //If the last connection is > than 7 days, the text is red
1940
                        //345600 = 7 days in seconds
1941
                        if ($currentTimestamp - $timestamp > 604800) {
1942
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</span>';
1943
                        } else {
1944
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1945
                        }
1946
                    }
1947
                }
1948
            }
1949
        }
1950
1951
        return false;
1952
    }
1953
1954
    /**
1955
     * Get first user's connection date on the course.
1956
     *
1957
     * @param int User id
1958
     * @param int $courseId
1959
     * @param int Session id (optional, default=0)
1960
     * @param bool $convert_date
1961
     *
1962
     * @return string|bool Date with format long without day or false if there is no date
1963
     */
1964
    public static function get_first_connection_date_on_the_course(
1965
        $student_id,
1966
        $courseId,
1967
        $session_id = 0,
1968
        $convert_date = true
1969
    ) {
1970
        $student_id = (int) $student_id;
1971
        $courseId = (int) $courseId;
1972
        $session_id = (int) $session_id;
1973
1974
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1975
        $sql = 'SELECT login_course_date
1976
                FROM '.$table.'
1977
                WHERE
1978
                    user_id = '.$student_id.' AND
1979
                    c_id = '.$courseId.' AND
1980
                    session_id = '.$session_id.'
1981
                ORDER BY login_course_date ASC
1982
                LIMIT 0,1';
1983
        $rs = Database::query($sql);
1984
        if (Database::num_rows($rs) > 0) {
1985
            if ($first_login_date = Database::result($rs, 0, 0)) {
1986
                if (empty($first_login_date)) {
1987
                    return false;
1988
                }
1989
1990
                if ($convert_date) {
1991
                    return api_convert_and_format_date(
1992
                        $first_login_date,
1993
                        DATE_FORMAT_SHORT
1994
                    );
1995
                }
1996
1997
                return $first_login_date;
1998
            }
1999
        }
2000
2001
        return false;
2002
    }
2003
2004
    /**
2005
     * Get last user's connection date on the course.
2006
     *
2007
     * @param     int         User id
2008
     * @param array $courseInfo real_id and code are used
2009
     * @param    int            Session id (optional, default=0)
2010
     * @param bool $convert_date
2011
     *
2012
     * @return string|bool Date with format long without day or false if there is no date
2013
     */
2014
    public static function get_last_connection_date_on_the_course(
2015
        $student_id,
2016
        $courseInfo,
2017
        $session_id = 0,
2018
        $convert_date = true
2019
    ) {
2020
        // protect data
2021
        $student_id = (int) $student_id;
2022
        $session_id = (int) $session_id;
2023
2024
        if (empty($courseInfo) || empty($student_id)) {
2025
            return false;
2026
        }
2027
2028
        $courseId = $courseInfo['real_id'];
2029
2030
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2031
2032
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2033
            // Show the last date on which the user acceed the session when it was active
2034
            $where_condition = '';
2035
            $userInfo = api_get_user_info($student_id);
2036
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
2037
                // fin de acceso a la sesión
2038
                $sessionInfo = SessionManager::fetch($session_id);
2039
                $last_access = $sessionInfo['access_end_date'];
2040
                if (!empty($last_access)) {
2041
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
2042
                }
2043
            }
2044
            $sql = "SELECT logout_course_date
2045
                    FROM $table
2046
                    WHERE   user_id = $student_id AND
2047
                            c_id = $courseId AND
2048
                            session_id = $session_id $where_condition
2049
                    ORDER BY logout_course_date DESC
2050
                    LIMIT 0,1";
2051
2052
            $rs = Database::query($sql);
2053
            if (Database::num_rows($rs) > 0) {
2054
                if ($last_login_date = Database::result($rs, 0, 0)) {
2055
                    if (empty($last_login_date)) {
2056
                        return false;
2057
                    }
2058
                    if ($convert_date) {
2059
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2060
                    }
2061
2062
                    return $last_login_date;
2063
                }
2064
            }
2065
        } else {
2066
            $sql = "SELECT logout_course_date
2067
                    FROM $table
2068
                    WHERE   user_id = $student_id AND
2069
                            c_id = $courseId AND
2070
                            session_id = $session_id
2071
                    ORDER BY logout_course_date DESC
2072
                    LIMIT 0,1";
2073
2074
            $rs = Database::query($sql);
2075
            if (Database::num_rows($rs) > 0) {
2076
                if ($last_login_date = Database::result($rs, 0, 0)) {
2077
                    if (empty($last_login_date)) {
2078
                        return false;
2079
                    }
2080
                    //see #5736
2081
                    $last_login_date_timestamp = api_strtotime($last_login_date);
2082
                    $now = time();
2083
                    //If the last connection is > than 7 days, the text is red
2084
                    //345600 = 7 days in seconds
2085
                    if ($now - $last_login_date_timestamp > 604800) {
2086
                        if ($convert_date) {
2087
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2088
                            $icon = null;
2089
                            if (api_is_allowed_to_edit()) {
2090
                                $url = api_get_path(WEB_CODE_PATH).
2091
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
2092
                                $icon = '<a href="'.$url.'" title="'.get_lang('Remind inactive user').'">
2093
                                  '.Display::return_icon('messagebox_warning.gif').'
2094
                                 </a>';
2095
                            }
2096
2097
                            return $icon.Display::label($last_login_date, 'warning');
2098
                        }
2099
2100
                        return $last_login_date;
2101
                    } else {
2102
                        if ($convert_date) {
2103
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2104
                        }
2105
2106
                        return $last_login_date;
2107
                    }
2108
                }
2109
            }
2110
        }
2111
2112
        return false;
2113
    }
2114
2115
    /**
2116
     * Get count of the connections to the course during a specified period.
2117
     *
2118
     * @param int $courseId
2119
     * @param   int     Session id (optional)
2120
     * @param   int     Datetime from which to collect data (defaults to 0)
2121
     * @param   int     Datetime to which to collect data (defaults to now)
2122
     *
2123
     * @return int count connections
2124
     */
2125
    public static function get_course_connections_count(
2126
        $courseId,
2127
        $session_id = 0,
2128
        $start = 0,
2129
        $stop = null
2130
    ) {
2131
        if ($start < 0) {
2132
            $start = 0;
2133
        }
2134
        if (!isset($stop) || $stop < 0) {
2135
            $stop = api_get_utc_datetime();
2136
        }
2137
2138
        // Given we're storing in cache, round the start and end times
2139
        // to the lower minute
2140
        $roundedStart = substr($start, 0, -2).'00';
2141
        $roundedStop = substr($stop, 0, -2).'00';
2142
        $roundedStart = Database::escape_string($roundedStart);
2143
        $roundedStop = Database::escape_string($roundedStop);
2144
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
2145
        $courseId = (int) $courseId;
2146
        $session_id = (int) $session_id;
2147
        $count = 0;
2148
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2149
        $sql = "SELECT count(*) as count_connections
2150
                FROM $table
2151
                WHERE
2152
                    c_id = $courseId AND
2153
                    session_id = $session_id
2154
                    $month_filter";
2155
2156
        //This query can be very slow (several seconds on an indexed table
2157
        // with 14M rows). As such, we'll try to use APCu if it is
2158
        // available to store the resulting value for a few seconds
2159
        $cacheAvailable = api_get_configuration_value('apc');
2160
        if (true === $cacheAvailable) {
2161
            $apc = apcu_cache_info(true);
2162
            $apc_end = $apc['start_time'] + $apc['ttl'];
2163
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
2164
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
2165
                apcu_fetch($apc_var) > 0
2166
            ) {
2167
                $count = apcu_fetch($apc_var);
2168
            } else {
2169
                $rs = Database::query($sql);
2170
                if (Database::num_rows($rs) > 0) {
2171
                    $row = Database::fetch_object($rs);
2172
                    $count = $row->count_connections;
2173
                }
2174
                apcu_clear_cache();
2175
                apcu_store($apc_var, $count, 60);
2176
            }
2177
        } else {
2178
            $rs = Database::query($sql);
2179
            if (Database::num_rows($rs) > 0) {
2180
                $row = Database::fetch_object($rs);
2181
                $count = $row->count_connections;
2182
            }
2183
        }
2184
2185
        return $count;
2186
    }
2187
2188
    /**
2189
     * Get count courses per student.
2190
     *
2191
     * @param int  $user_id          Student id
2192
     * @param bool $include_sessions Include sessions (optional)
2193
     *
2194
     * @return int count courses
2195
     */
2196
    public static function count_course_per_student($user_id, $include_sessions = true)
2197
    {
2198
        $user_id = (int) $user_id;
2199
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2200
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2201
2202
        $sql = 'SELECT DISTINCT c_id
2203
                FROM '.$tbl_course_rel_user.'
2204
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
2205
        $rs = Database::query($sql);
2206
        $nb_courses = Database::num_rows($rs);
2207
2208
        if ($include_sessions) {
2209
            $sql = 'SELECT DISTINCT c_id
2210
                    FROM '.$tbl_session_course_rel_user.'
2211
                    WHERE user_id = '.$user_id;
2212
            $rs = Database::query($sql);
2213
            $nb_courses += Database::num_rows($rs);
2214
        }
2215
2216
        return $nb_courses;
2217
    }
2218
2219
    /**
2220
     * Gets the score average from all tests in a course by student.
2221
     *
2222
     * @param $student_id
2223
     * @param $course_code
2224
     * @param int  $exercise_id
2225
     * @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...
2226
     * @param int  $active_filter 2 for consider all tests
2227
     *                            1 for active <> -1
2228
     *                            0 for active <> 0
2229
     * @param int  $into_lp       1 for all exercises
2230
     *                            0 for without LP
2231
     * @param mixed id
2232
     * @param string code
2233
     * @param int id (optional), filtered by exercise
2234
     * @param int id (optional), if param $session_id is null
2235
     *                                                it'll return results including sessions, 0 = session is not filtered
2236
     *
2237
     * @return string value (number %) Which represents a round integer about the score average
2238
     */
2239
    public static function get_avg_student_exercise_score(
2240
        $student_id,
2241
        $course_code,
2242
        $exercise_id = 0,
2243
        $session_id = null,
2244
        $active_filter = 1,
2245
        $into_lp = 0
2246
    ) {
2247
        $course_code = Database::escape_string($course_code);
2248
        $course_info = api_get_course_info($course_code);
2249
        if (!empty($course_info)) {
2250
            // table definition
2251
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2252
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2253
2254
            // Compose a filter based on optional exercise given
2255
            $condition_quiz = "";
2256
            if (!empty($exercise_id)) {
2257
                $exercise_id = intval($exercise_id);
2258
                $condition_quiz = " AND id = $exercise_id ";
2259
            }
2260
2261
            // Compose a filter based on optional session id given
2262
            $condition_session = '';
2263
            if (isset($session_id)) {
2264
                $session_id = intval($session_id);
2265
                $condition_session = " AND session_id = $session_id ";
2266
            }
2267
            if (1 == $active_filter) {
2268
                $condition_active = 'AND active <> -1';
2269
            } elseif (0 == $active_filter) {
2270
                $condition_active = 'AND active <> 0';
2271
            } else {
2272
                $condition_active = '';
2273
            }
2274
            $condition_into_lp = '';
2275
            $select_lp_id = '';
2276
            if (0 == $into_lp) {
2277
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2278
            } else {
2279
                $select_lp_id = ', orig_lp_id as lp_id ';
2280
            }
2281
2282
            $sql = "SELECT count(id)
2283
    		        FROM $tbl_course_quiz
2284
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2285
            $count_quiz = 0;
2286
            $countQuizResult = Database::query($sql);
2287
            if (!empty($countQuizResult)) {
2288
                $count_quiz = Database::fetch_row($countQuizResult);
2289
            }
2290
2291
            if (!empty($count_quiz[0]) && !empty($student_id)) {
2292
                if (is_array($student_id)) {
2293
                    $student_id = array_map('intval', $student_id);
2294
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2295
                } else {
2296
                    $student_id = intval($student_id);
2297
                    $condition_user = " AND exe_user_id = '$student_id' ";
2298
                }
2299
2300
                if (empty($exercise_id)) {
2301
                    $sql = "SELECT id FROM $tbl_course_quiz
2302
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2303
                    $result = Database::query($sql);
2304
                    $exercise_list = [];
2305
                    $exercise_id = null;
2306
                    if (!empty($result) && Database::num_rows($result)) {
2307
                        while ($row = Database::fetch_array($result)) {
2308
                            $exercise_list[] = $row['id'];
2309
                        }
2310
                    }
2311
                    if (!empty($exercise_list)) {
2312
                        $exercise_id = implode("','", $exercise_list);
2313
                    }
2314
                }
2315
2316
                $count_quiz = Database::fetch_row(Database::query($sql));
2317
                $sql = "SELECT
2318
                        SUM(score/max_score*100) as avg_score,
2319
                        COUNT(*) as num_attempts
2320
                        $select_lp_id
2321
                        FROM $tbl_stats_exercise
2322
                        WHERE
2323
                            exe_exo_id IN ('".$exercise_id."')
2324
                            $condition_user AND
2325
                            status = '' AND
2326
                            c_id = {$course_info['real_id']}
2327
                            $condition_session
2328
                            $condition_into_lp
2329
                        ORDER BY exe_date DESC";
2330
2331
                $res = Database::query($sql);
2332
                $row = Database::fetch_array($res);
2333
                $quiz_avg_score = null;
2334
2335
                if (!empty($row['avg_score'])) {
2336
                    $quiz_avg_score = round($row['avg_score'], 2);
2337
                }
2338
2339
                if (!empty($row['num_attempts'])) {
2340
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2341
                }
2342
                if (is_array($student_id)) {
2343
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2344
                }
2345
                if (0 == $into_lp) {
2346
                    return $quiz_avg_score;
2347
                } else {
2348
                    if (!empty($row['lp_id'])) {
2349
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2350
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2351
                        $sql = "SELECT lp.name
2352
                                FROM $tbl_lp as lp, $tbl_course as c
2353
                                WHERE
2354
                                    c.code = '$course_code' AND
2355
                                    lp.id = ".$row['lp_id']." AND
2356
                                    lp.c_id = c.id
2357
                                LIMIT 1;
2358
                        ";
2359
                        $result = Database::query($sql);
2360
                        $row_lp = Database::fetch_row($result);
2361
                        $lp_name = $row_lp[0];
2362
2363
                        return [$quiz_avg_score, $lp_name];
2364
                    } else {
2365
                        return [$quiz_avg_score, null];
2366
                    }
2367
                }
2368
            }
2369
        }
2370
2371
        return null;
2372
    }
2373
2374
    /**
2375
     * Get count student's exercise COMPLETED attempts.
2376
     *
2377
     * @param int $student_id
2378
     * @param int $courseId
2379
     * @param int $exercise_id
2380
     * @param int $lp_id
2381
     * @param int $lp_item_id
2382
     * @param int $session_id
2383
     * @param int $find_all_lp 0 = just LP specified
2384
     *                         1 = LP specified or whitout LP,
2385
     *                         2 = all rows
2386
     *
2387
     * @internal param \Student $int id
2388
     * @internal param \Course $string code
2389
     * @internal param \Exercise $int id
2390
     * @internal param \Learning $int path id (optional),
2391
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2392
     * @internal param \Learning $int path item id (optional),
2393
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2394
     *
2395
     * @return int count of attempts
2396
     */
2397
    public static function count_student_exercise_attempts(
2398
        $student_id,
2399
        $courseId,
2400
        $exercise_id,
2401
        $lp_id = 0,
2402
        $lp_item_id = 0,
2403
        $session_id = 0,
2404
        $find_all_lp = 0
2405
    ) {
2406
        $courseId = intval($courseId);
2407
        $student_id = intval($student_id);
2408
        $exercise_id = intval($exercise_id);
2409
        $session_id = intval($session_id);
2410
2411
        $lp_id = intval($lp_id);
2412
        $lp_item_id = intval($lp_item_id);
2413
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2414
2415
        $sql = "SELECT COUNT(ex.exe_id) as essais
2416
                FROM $tbl_stats_exercises AS ex
2417
                WHERE
2418
                    ex.c_id = $courseId AND
2419
                    ex.exe_exo_id = $exercise_id AND
2420
                    status = '' AND
2421
                    exe_user_id= $student_id AND
2422
                    session_id = $session_id ";
2423
2424
        if (1 == $find_all_lp) {
2425
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2426
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2427
        } elseif (0 == $find_all_lp) {
2428
            $sql .= "AND orig_lp_id = $lp_id
2429
                AND orig_lp_item_id = $lp_item_id";
2430
        }
2431
2432
        $rs = Database::query($sql);
2433
        $row = Database::fetch_row($rs);
2434
        $count_attempts = $row[0];
2435
2436
        return $count_attempts;
2437
    }
2438
2439
    /**
2440
     * Get count student's exercise progress.
2441
     *
2442
     * @param array $exercise_list
2443
     * @param int   $user_id
2444
     * @param int   $courseId
2445
     * @param int   $session_id
2446
     *
2447
     * @return string
2448
     */
2449
    public static function get_exercise_student_progress(
2450
        $exercise_list,
2451
        $user_id,
2452
        $courseId,
2453
        $session_id
2454
    ) {
2455
        $courseId = (int) $courseId;
2456
        $user_id = (int) $user_id;
2457
        $session_id = (int) $session_id;
2458
2459
        if (empty($exercise_list)) {
2460
            return '0%';
2461
        }
2462
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2463
        $exercise_list = array_keys($exercise_list);
2464
        $exercise_list = array_map('intval', $exercise_list);
2465
2466
        $exercise_list_imploded = implode("' ,'", $exercise_list);
2467
2468
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
2469
                FROM $tbl_stats_exercises AS ex
2470
                WHERE
2471
                    ex.c_id = $courseId AND
2472
                    ex.session_id  = $session_id AND
2473
                    ex.exe_user_id = $user_id AND
2474
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
2475
2476
        $rs = Database::query($sql);
2477
        $count = 0;
2478
        if ($rs) {
2479
            $row = Database::fetch_row($rs);
2480
            $count = $row[0];
2481
        }
2482
        $count = (0 != $count) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
2483
2484
        return $count;
2485
    }
2486
2487
    /**
2488
     * @param array $exercise_list
2489
     * @param int   $user_id
2490
     * @param int   $courseId
2491
     * @param int   $session_id
2492
     *
2493
     * @return string
2494
     */
2495
    public static function get_exercise_student_average_best_attempt(
2496
        $exercise_list,
2497
        $user_id,
2498
        $courseId,
2499
        $session_id
2500
    ) {
2501
        $result = 0;
2502
        if (!empty($exercise_list)) {
2503
            foreach ($exercise_list as $exercise_data) {
2504
                $exercise_id = $exercise_data['id'];
2505
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2506
                    $user_id,
2507
                    $exercise_id,
2508
                    $courseId,
2509
                    $session_id
2510
                );
2511
2512
                if (!empty($best_attempt) && !empty($best_attempt['max_score'])) {
2513
                    $result += $best_attempt['score'] / $best_attempt['max_score'];
2514
                }
2515
            }
2516
            $result = $result / count($exercise_list);
2517
            $result = round($result, 2) * 100;
2518
        }
2519
2520
        return $result.'%';
2521
    }
2522
2523
    /**
2524
     * Returns the average student progress in the learning paths of the given
2525
     * course, it will take into account the progress that were not started.
2526
     *
2527
     * @param int|array $studentId
2528
     * @param string    $courseCode
2529
     * @param array     $lpIdList        Limit average to listed lp ids
2530
     * @param int       $sessionId       Session id (optional),
2531
     *                                   if parameter $session_id is null(default) it'll return results including
2532
     *                                   sessions, 0 = session is not filtered
2533
     * @param bool      $returnArray     Will return an array of the type:
2534
     *                                   [sum_of_progresses, number] if it is set to true
2535
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2536
     *
2537
     * @return float Average progress of the user in this course
2538
     */
2539
    public static function get_avg_student_progress(
2540
        $studentId,
2541
        $courseCode = null,
2542
        $lpIdList = [],
2543
        $sessionId = null,
2544
        $returnArray = false,
2545
        $onlySeriousGame = false
2546
    ) {
2547
        // If there is at least one learning path and one student.
2548
        if (empty($studentId)) {
2549
            return false;
2550
        }
2551
2552
        $sessionId = (int) $sessionId;
2553
        $courseInfo = api_get_course_info($courseCode);
2554
2555
        if (empty($courseInfo)) {
2556
            return false;
2557
        }
2558
2559
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2560
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2561
        $lpConditions = [];
2562
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2563
2564
        if ($sessionId > 0) {
2565
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2566
        } else {
2567
            $lpConditions['AND session_id = ?'] = $sessionId;
2568
        }
2569
2570
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2571
            $placeHolders = [];
2572
            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...
2573
                $placeHolders[] = '?';
2574
            }
2575
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
2576
        }
2577
2578
        if ($onlySeriousGame) {
2579
            $lpConditions['AND seriousgame_mode = ? '] = true;
2580
        }
2581
2582
        $resultLP = Database::select(
2583
            'id',
2584
            $lPTable,
2585
            ['where' => $lpConditions]
2586
        );
2587
        $filteredLP = array_keys($resultLP);
2588
2589
        if (empty($filteredLP)) {
2590
            return false;
2591
        }
2592
2593
        $conditions = [
2594
            " c_id = {$courseInfo['real_id']} ",
2595
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
2596
        ];
2597
2598
        $groupBy = 'GROUP BY lp_id';
2599
2600
        if (is_array($studentId)) {
2601
            $studentId = array_map('intval', $studentId);
2602
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
2603
        } else {
2604
            $studentId = (int) $studentId;
2605
            $conditions[] = " lp_view.user_id = '$studentId' ";
2606
2607
            if (empty($lpIdList)) {
2608
                $lpList = new LearnpathList(
2609
                    $studentId,
2610
                    $courseInfo,
2611
                    $sessionId,
2612
                    null,
2613
                    false,
2614
                    null,
2615
                    true
2616
                );
2617
                $lpList = $lpList->get_flat_list();
2618
                if (!empty($lpList)) {
2619
                    /** @var $lp */
2620
                    foreach ($lpList as $lpId => $lp) {
2621
                        $lpIdList[] = $lp['lp_old_id'];
2622
                    }
2623
                }
2624
            }
2625
        }
2626
2627
        if (!empty($sessionId)) {
2628
            $conditions[] = " session_id = $sessionId ";
2629
        } else {
2630
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
2631
        }
2632
2633
        $conditionToString = implode('AND', $conditions);
2634
        $sql = "SELECT lp_id, view_count, progress
2635
                FROM $lpViewTable lp_view
2636
                WHERE
2637
                    $conditionToString
2638
                    $groupBy
2639
                ORDER BY view_count DESC";
2640
2641
        $result = Database::query($sql);
2642
2643
        $progress = [];
2644
        $viewCount = [];
2645
        while ($row = Database::fetch_array($result, 'ASSOC')) {
2646
            if (!isset($viewCount[$row['lp_id']])) {
2647
                $progress[$row['lp_id']] = $row['progress'];
2648
            }
2649
            $viewCount[$row['lp_id']] = $row['view_count'];
2650
        }
2651
2652
        // Fill with lp ids
2653
        $newProgress = [];
2654
        if (!empty($lpIdList)) {
2655
            foreach ($lpIdList as $lpId) {
2656
                if (isset($progress[$lpId])) {
2657
                    $newProgress[] = $progress[$lpId];
2658
                }
2659
            }
2660
            $total = count($lpIdList);
2661
        } else {
2662
            $newProgress = $progress;
2663
            $total = count($newProgress);
2664
        }
2665
2666
        $average = 0;
2667
        $sum = 0;
2668
        if (!empty($newProgress)) {
2669
            $sum = array_sum($newProgress);
2670
            $average = $sum / $total;
2671
        }
2672
2673
        if ($returnArray) {
2674
            return [
2675
                $sum,
2676
                $total,
2677
            ];
2678
        }
2679
2680
        return round($average, 1);
2681
    }
2682
2683
    /**
2684
     * This function gets:
2685
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2686
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2687
     * 3. And finally it will return the average between 1. and 2.
2688
     *
2689
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2690
     * This function does not take the results of a Test out of a LP
2691
     *
2692
     * @param mixed  $student_id                      Array of user ids or an user id
2693
     * @param string $course_code
2694
     * @param array  $lp_ids                          List of LP ids
2695
     * @param int    $session_id                      Session id (optional),
2696
     *                                                if param $session_id is null(default) it'll return results
2697
     *                                                including sessions, 0 = session is not filtered
2698
     * @param bool   $return_array                    Returns an array of the
2699
     *                                                type [sum_score, num_score] if set to true
2700
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2701
     * @param bool   $getOnlyBestAttempt
2702
     *
2703
     * @return string value (number %) Which represents a round integer explain in got in 3
2704
     */
2705
    public static function get_avg_student_score(
2706
        $student_id,
2707
        $course_code,
2708
        $lp_ids = [],
2709
        $session_id = null,
2710
        $return_array = false,
2711
        $get_only_latest_attempt_results = false,
2712
        $getOnlyBestAttempt = false
2713
    ) {
2714
        $debug = false;
2715
        if ($debug) {
2716
            echo '<h1>Tracking::get_avg_student_score</h1>';
2717
        }
2718
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2719
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2720
        $course = api_get_course_info($course_code);
2721
2722
        if (empty($course)) {
2723
            return null;
2724
        }
2725
2726
        // Get course tables names
2727
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
2728
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
2729
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
2730
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
2731
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2732
        $course_id = $course['real_id'];
2733
2734
        // Compose a filter based on optional learning paths list given
2735
        $condition_lp = '';
2736
        if (count($lp_ids) > 0) {
2737
            $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
2738
        }
2739
2740
        // Compose a filter based on optional session id
2741
        $session_id = intval($session_id);
2742
        if (count($lp_ids) > 0) {
2743
            $condition_session = " AND session_id = $session_id ";
2744
        } else {
2745
            $condition_session = " WHERE session_id = $session_id ";
2746
        }
2747
2748
        // Check the real number of LPs corresponding to the filter in the
2749
        // database (and if no list was given, get them all)
2750
        if (empty($session_id)) {
2751
            $sql = "SELECT DISTINCT(id), use_max_score
2752
                    FROM $lp_table
2753
                    WHERE
2754
                        c_id = $course_id AND
2755
                        (session_id = 0 OR session_id IS NULL) $condition_lp ";
2756
        } else {
2757
            $sql = "SELECT DISTINCT(id), use_max_score
2758
                    FROM $lp_table
2759
                    WHERE c_id = $course_id $condition_lp ";
2760
        }
2761
2762
        $res_row_lp = Database::query($sql);
2763
        $count_row_lp = Database::num_rows($res_row_lp);
2764
2765
        $lp_list = $use_max_score = [];
2766
        while ($row_lp = Database::fetch_array($res_row_lp)) {
2767
            $lp_list[] = $row_lp['id'];
2768
            $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
2769
        }
2770
2771
        // prepare filter on users
2772
        if (is_array($student_id)) {
2773
            array_walk($student_id, 'intval');
2774
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
2775
        } else {
2776
            $condition_user1 = " AND user_id = $student_id ";
2777
        }
2778
2779
        if (empty($count_row_lp) || empty($student_id)) {
2780
            return null;
2781
        }
2782
2783
        // Getting latest LP result for a student
2784
        //@todo problem when a  course have more than 1500 users
2785
        $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
2786
                FROM $lp_view_table
2787
                WHERE
2788
                    c_id = $course_id AND
2789
                    lp_id IN (".implode(',', $lp_list).")
2790
                    $condition_user1 AND
2791
                    session_id = $session_id
2792
                GROUP BY lp_id, user_id";
2793
2794
        $rs_last_lp_view_id = Database::query($sql);
2795
        $global_result = 0;
2796
2797
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
2798
            // Cycle through each line of the results (grouped by lp_id, user_id)
2799
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2800
                $count_items = 0;
2801
                $lpPartialTotal = 0;
2802
                $list = [];
2803
                $lp_view_id = $row_lp_view['id'];
2804
                $lp_id = $row_lp_view['lp_id'];
2805
                $user_id = $row_lp_view['user_id'];
2806
2807
                if ($debug) {
2808
                    echo '<h2>LP id '.$lp_id.'</h2>';
2809
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
2810
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
2811
                }
2812
2813
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
2814
                    // Getting lp_items done by the user
2815
                    $sql = "SELECT DISTINCT lp_item_id
2816
                            FROM $lp_item_view_table
2817
                            WHERE
2818
                                c_id = $course_id AND
2819
                                lp_view_id = $lp_view_id
2820
                            ORDER BY lp_item_id";
2821
                    $res_lp_item = Database::query($sql);
2822
2823
                    while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
2824
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
2825
                        $order = ' view_count DESC';
2826
                        if ($getOnlyBestAttempt) {
2827
                            $order = ' lp_iv.score DESC';
2828
                        }
2829
2830
                        // Getting the most recent attempt
2831
                        $sql = "SELECT
2832
                                    lp_iv.id as lp_item_view_id,
2833
                                    lp_iv.score as score,
2834
                                    lp_i.max_score,
2835
                                    lp_iv.max_score as max_score_item_view,
2836
                                    lp_i.path,
2837
                                    lp_i.item_type,
2838
                                    lp_i.id as iid
2839
                                FROM $lp_item_view_table as lp_iv
2840
                                INNER JOIN $lp_item_table as lp_i
2841
                                ON (
2842
                                    lp_i.id = lp_iv.lp_item_id AND
2843
                                    lp_iv.c_id = lp_i.c_id
2844
                                )
2845
                                WHERE
2846
                                    lp_iv.c_id = $course_id AND
2847
                                    lp_i.c_id  = $course_id AND
2848
                                    lp_item_id = $my_lp_item_id AND
2849
                                    lp_view_id = $lp_view_id AND
2850
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2851
                                ORDER BY $order
2852
                                LIMIT 1";
2853
2854
                        $res_lp_item_result = Database::query($sql);
2855
                        while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
2856
                            $list[] = $row_max_score;
2857
                        }
2858
                    }
2859
                } else {
2860
                    // For the currently analysed view, get the score and
2861
                    // max_score of each item if it is a sco or a TOOL_QUIZ
2862
                    $sql = "SELECT
2863
                                lp_iv.id as lp_item_view_id,
2864
                                lp_iv.score as score,
2865
                                lp_i.max_score,
2866
                                lp_iv.max_score as max_score_item_view,
2867
                                lp_i.path,
2868
                                lp_i.item_type,
2869
                                lp_i.id as iid
2870
                              FROM $lp_item_view_table as lp_iv
2871
                              INNER JOIN $lp_item_table as lp_i
2872
                              ON lp_i.id = lp_iv.lp_item_id AND
2873
                                 lp_iv.c_id = lp_i.c_id
2874
                              WHERE
2875
                                lp_iv.c_id = $course_id AND
2876
                                lp_i.c_id  = $course_id AND
2877
                                lp_view_id = $lp_view_id AND
2878
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2879
                            ";
2880
                    $res_max_score = Database::query($sql);
2881
                    while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
2882
                        $list[] = $row_max_score;
2883
                    }
2884
                }
2885
2886
                // Go through each scorable element of this view
2887
                $score_of_scorm_calculate = 0;
2888
                foreach ($list as $row_max_score) {
2889
                    // Came from the original lp_item
2890
                    $max_score = $row_max_score['max_score'];
2891
                    // Came from the lp_item_view
2892
                    $max_score_item_view = $row_max_score['max_score_item_view'];
2893
                    $score = $row_max_score['score'];
2894
                    if ($debug) {
2895
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
2896
                    }
2897
2898
                    if ('sco' == $row_max_score['item_type']) {
2899
                        /* Check if it is sco (easier to get max_score)
2900
                           when there's no max score, we assume 100 as the max score,
2901
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
2902
                        */
2903
                        if (0 == $max_score || is_null($max_score) || '' == $max_score) {
2904
                            // Chamilo style
2905
                            if ($use_max_score[$lp_id]) {
2906
                                $max_score = 100;
2907
                            } else {
2908
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
2909
                                $max_score = $max_score_item_view;
2910
                            }
2911
                        }
2912
                        // Avoid division by zero errors
2913
                        if (!empty($max_score)) {
2914
                            $lpPartialTotal += $score / $max_score;
2915
                        }
2916
                        if ($debug) {
2917
                            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...
2918
                            var_dump("score: $score");
2919
                            var_dump("max_score: $max_score");
2920
                        }
2921
                    } else {
2922
                        // Case of a TOOL_QUIZ element
2923
                        $item_id = $row_max_score['iid'];
2924
                        $item_path = $row_max_score['path'];
2925
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
2926
2927
                        $lpItemCondition = '';
2928
                        if (empty($lp_item_view_id)) {
2929
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
2930
                        } else {
2931
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
2932
                        }
2933
2934
                        // Get last attempt to this exercise through
2935
                        // the current lp for the current user
2936
                        $order = 'exe_date DESC';
2937
                        if ($getOnlyBestAttempt) {
2938
                            $order = 'score DESC';
2939
                        }
2940
                        $sql = "SELECT exe_id, score
2941
                                FROM $tbl_stats_exercices
2942
                                WHERE
2943
                                    exe_exo_id = '$item_path' AND
2944
                                    exe_user_id = $user_id AND
2945
                                    orig_lp_item_id = $item_id AND
2946
                                    $lpItemCondition AND
2947
                                    c_id = $course_id AND
2948
                                    session_id = $session_id AND
2949
                                    status = ''
2950
                                ORDER BY $order
2951
                                LIMIT 1";
2952
2953
                        $result_last_attempt = Database::query($sql);
2954
                        $num = Database::num_rows($result_last_attempt);
2955
                        if ($num > 0) {
2956
                            $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
2957
                            $id_last_attempt = $attemptResult['exe_id'];
2958
                            // We overwrite the score with the best one not the one saved in the LP (latest)
2959
                            if ($getOnlyBestAttempt && false == $get_only_latest_attempt_results) {
2960
                                if ($debug) {
2961
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
2962
                                }
2963
                                $score = $attemptResult['score'];
2964
                            }
2965
2966
                            if ($debug) {
2967
                                echo "Attempt id: $id_last_attempt with score $score<br />";
2968
                            }
2969
                            // Within the last attempt number tracking, get the sum of
2970
                            // the max_scores of all questions that it was
2971
                            // made of (we need to make this call dynamic because of random questions selection)
2972
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
2973
                                            (
2974
                                                SELECT DISTINCT
2975
                                                    question_id,
2976
                                                    marks,
2977
                                                    ponderation
2978
                                                FROM $tbl_stats_attempts AS at
2979
                                                INNER JOIN $tbl_quiz_questions AS q
2980
                                                ON (q.id = at.question_id AND q.c_id = q.c_id)
2981
                                                WHERE
2982
                                                    exe_id ='$id_last_attempt' AND
2983
                                                    q.c_id = $course_id
2984
                                            )
2985
                                            AS t";
2986
2987
                            $res_max_score_bis = Database::query($sql);
2988
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
2989
2990
                            if (!empty($row_max_score_bis['maxscore'])) {
2991
                                $max_score = $row_max_score_bis['maxscore'];
2992
                            }
2993
                            if (!empty($max_score) && floatval($max_score) > 0) {
2994
                                $lpPartialTotal += $score / $max_score;
2995
                            }
2996
                            if ($debug) {
2997
                                var_dump("score: $score");
2998
                                var_dump("max_score: $max_score");
2999
                                var_dump("lpPartialTotal: $lpPartialTotal");
3000
                            }
3001
                        }
3002
                    }
3003
3004
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
3005
                        // Normal way
3006
                        if ($use_max_score[$lp_id]) {
3007
                            $count_items++;
3008
                        } else {
3009
                            if ('' != $max_score) {
3010
                                $count_items++;
3011
                            }
3012
                        }
3013
                        if ($debug) {
3014
                            echo '$count_items: '.$count_items;
3015
                        }
3016
                    }
3017
                } //end for
3018
3019
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
3020
                $global_result += $score_of_scorm_calculate;
3021
3022
                if ($debug) {
3023
                    var_dump("count_items: $count_items");
3024
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
3025
                    var_dump("global_result: $global_result");
3026
                }
3027
            } // end while
3028
        }
3029
3030
        $lp_with_quiz = 0;
3031
        foreach ($lp_list as $lp_id) {
3032
            // Check if LP have a score we assume that all SCO have an score
3033
            $sql = "SELECT count(id) as count
3034
                    FROM $lp_item_table
3035
                    WHERE
3036
                        c_id = $course_id AND
3037
                        (item_type = 'quiz' OR item_type = 'sco') AND
3038
                        lp_id = ".$lp_id;
3039
            $result_have_quiz = Database::query($sql);
3040
            if (Database::num_rows($result_have_quiz) > 0) {
3041
                $row = Database::fetch_array($result_have_quiz, 'ASSOC');
3042
                if (is_numeric($row['count']) && 0 != $row['count']) {
3043
                    $lp_with_quiz++;
3044
                }
3045
            }
3046
        }
3047
3048
        if ($debug) {
3049
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
3050
        }
3051
        if ($debug) {
3052
            echo '<h3>Final return</h3>';
3053
        }
3054
3055
        if (0 != $lp_with_quiz) {
3056
            if (!$return_array) {
3057
                $score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
3058
                if ($debug) {
3059
                    var_dump($score_of_scorm_calculate);
3060
                }
3061
                if (empty($lp_ids)) {
3062
                    if ($debug) {
3063
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
3064
                    }
3065
                }
3066
3067
                return $score_of_scorm_calculate;
3068
            }
3069
3070
            if ($debug) {
3071
                var_dump($global_result, $lp_with_quiz);
3072
            }
3073
3074
            return [$global_result, $lp_with_quiz];
3075
        }
3076
3077
        return '-';
3078
    }
3079
3080
    /**
3081
     * This function gets:
3082
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3083
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3084
     * 3. And finally it will return the average between 1. and 2.
3085
     * This function does not take the results of a Test out of a LP.
3086
     *
3087
     * @param int|array $student_id  Array of user ids or an user id
3088
     * @param string    $course_code Course code
3089
     * @param array     $lp_ids      List of LP ids
3090
     * @param int       $session_id  Session id (optional), if param $session_id is 0(default)
3091
     *                               it'll return results including sessions, 0 = session is not filtered
3092
     *
3093
     * @return string value (number %) Which represents a round integer explain in got in 3
3094
     */
3095
    public static function getAverageStudentScore(
3096
        $student_id,
3097
        $course_code = '',
3098
        $lp_ids = [],
3099
        $session_id = 0
3100
    ) {
3101
        if (empty($student_id)) {
3102
            return 0;
3103
        }
3104
3105
        $conditions = [];
3106
        if (!empty($course_code)) {
3107
            $course = api_get_course_info($course_code);
3108
            $courseId = $course['real_id'];
3109
            $conditions[] = " lp.c_id = $courseId";
3110
        }
3111
3112
        // Get course tables names
3113
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3114
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3115
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3116
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3117
3118
        // Compose a filter based on optional learning paths list given
3119
        if (!empty($lp_ids) && count($lp_ids) > 0) {
3120
            $conditions[] = ' lp.id IN ('.implode(',', $lp_ids).') ';
3121
        }
3122
3123
        // Compose a filter based on optional session id
3124
        $session_id = (int) $session_id;
3125
        if (!empty($session_id)) {
3126
            $conditions[] = " lp_view.session_id = $session_id ";
3127
        }
3128
3129
        if (is_array($student_id)) {
3130
            array_walk($student_id, 'intval');
3131
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
3132
        } else {
3133
            $student_id = (int) $student_id;
3134
            $conditions[] = " lp_view.user_id = $student_id ";
3135
        }
3136
3137
        $conditionsToString = implode('AND ', $conditions);
3138
        $sql = "SELECT
3139
                    SUM(lp_iv.score) sum_score,
3140
                    SUM(lp_i.max_score) sum_max_score
3141
                FROM $lp_table as lp
3142
                INNER JOIN $lp_item_table as lp_i
3143
                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
3144
                INNER JOIN $lp_view_table as lp_view
3145
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
3146
                INNER JOIN $lp_item_view_table as lp_iv
3147
                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
3148
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
3149
                $conditionsToString
3150
        ";
3151
        $result = Database::query($sql);
3152
        $row = Database::fetch_array($result, 'ASSOC');
3153
3154
        if (empty($row['sum_max_score'])) {
3155
            return 0;
3156
        }
3157
3158
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
3159
    }
3160
3161
    /**
3162
     * This function gets time spent in learning path for a student inside a course.
3163
     *
3164
     * @param int|array $student_id  Student id(s)
3165
     * @param string    $course_code Course code
3166
     * @param array     $lp_ids      Limit average to listed lp ids
3167
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
3168
     *                               it'll return results including sessions, 0 = session is not filtered
3169
     *
3170
     * @return int Total time in seconds
3171
     */
3172
    public static function get_time_spent_in_lp(
3173
        $student_id,
3174
        $course_code,
3175
        $lp_ids = [],
3176
        $session_id = 0
3177
    ) {
3178
        $course = api_get_course_info($course_code);
3179
        $student_id = (int) $student_id;
3180
        $session_id = (int) $session_id;
3181
        $total_time = 0;
3182
3183
        if (!empty($course)) {
3184
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
3185
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
3186
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3187
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3188
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3189
            $course_id = $course['real_id'];
3190
3191
            // Compose a filter based on optional learning paths list given
3192
            $condition_lp = '';
3193
            if (count($lp_ids) > 0) {
3194
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3195
            }
3196
3197
            // Check the real number of LPs corresponding to the filter in the
3198
            // database (and if no list was given, get them all)
3199
            $sql = "SELECT DISTINCT(id) FROM $lpTable
3200
                    WHERE c_id = $course_id $condition_lp";
3201
            $result = Database::query($sql);
3202
            $session_condition = api_get_session_condition($session_id);
3203
3204
            // calculates time
3205
            if (Database::num_rows($result) > 0) {
3206
                while ($row = Database::fetch_array($result)) {
3207
                    $lp_id = (int) $row['id'];
3208
3209
                    // Start Exercise in LP total_time
3210
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
3211
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
3212
                    foreach ($list as $itemId) {
3213
                        $sql = "SELECT max(view_count)
3214
                                FROM $lpViewTable
3215
                                WHERE
3216
                                    c_id = $course_id AND
3217
                                    lp_id = $lp_id AND
3218
                                    user_id = $student_id
3219
                                    $session_condition";
3220
                        $res = Database::query($sql);
3221
                        $view = '';
3222
                        if (Database::num_rows($res) > 0) {
3223
                            $myrow = Database::fetch_array($res);
3224
                            $view = $myrow[0];
3225
                        }
3226
                        $viewCondition = null;
3227
                        if (!empty($view)) {
3228
                            $viewCondition = " AND v.view_count = $view  ";
3229
                        }
3230
                        $sql = "SELECT
3231
                            iv.iid,
3232
                            iv.total_time as mytime,
3233
                            i.id as myid,
3234
                            iv.view_count as iv_view_count,
3235
                            path
3236
                        FROM $lpItemTable as i
3237
                        INNER JOIN $lpItemViewTable as iv
3238
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
3239
                        INNER JOIN $lpViewTable as v
3240
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
3241
                        WHERE
3242
                            v.c_id = $course_id AND
3243
                            i.id = $itemId AND
3244
                            i.lp_id = $lp_id  AND
3245
                            v.user_id = $student_id AND
3246
                            item_type = 'quiz' AND
3247
                            path <> '' AND
3248
                            v.session_id = $session_id
3249
                            $viewCondition
3250
                        ORDER BY iv.view_count DESC ";
3251
3252
                        $resultRow = Database::query($sql);
3253
                        if (Database::num_rows($resultRow)) {
3254
                            $row = Database::fetch_array($resultRow);
3255
                            $totalTimeInLpItemView = $row['mytime'];
3256
                            $lpItemViewId = $row['iid'];
3257
3258
                            $sql = 'SELECT SUM(exe_duration) exe_duration
3259
                                    FROM '.$trackExercises.'
3260
                                    WHERE
3261
                                        exe_exo_id="'.$row['path'].'" AND
3262
                                        exe_user_id="'.$student_id.'" AND
3263
                                        orig_lp_id = "'.$lp_id.'" AND
3264
                                        orig_lp_item_id = "'.$row['myid'].'" AND
3265
                                        c_id = '.$course_id.' AND
3266
                                        status <> "incomplete" AND
3267
                                        session_id = '.$session_id.'
3268
                                     ORDER BY exe_date DESC ';
3269
3270
                            $sumScoreResult = Database::query($sql);
3271
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
3272
                            if (!empty($durationRow['exe_duration'])) {
3273
                                $exeDuration = $durationRow['exe_duration'];
3274
                                if ($exeDuration != $totalTimeInLpItemView &&
3275
                                    !empty($lpItemViewId) &&
3276
                                    !empty($exeDuration)
3277
                                ) {
3278
                                    // Update c_lp_item_view.total_time
3279
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration'
3280
                                                  WHERE iid = ".$lpItemViewId;
3281
                                    Database::query($sqlUpdate);
3282
                                }
3283
                            }
3284
                        }
3285
                    }
3286
3287
                    // End total_time fix
3288
3289
                    // Calculate total time
3290
                    $sql = "SELECT SUM(total_time)
3291
                            FROM $lpItemViewTable AS item_view
3292
                            INNER JOIN $lpViewTable AS view
3293
                            ON (
3294
                                item_view.lp_view_id = view.id AND
3295
                                item_view.c_id = view.c_id
3296
                            )
3297
                            WHERE
3298
                                item_view.c_id = $course_id AND
3299
                                view.c_id = $course_id AND
3300
                                view.lp_id = $lp_id AND
3301
                                view.user_id = $student_id AND
3302
                                session_id = $session_id";
3303
3304
                    $rs = Database::query($sql);
3305
                    if (Database::num_rows($rs) > 0) {
3306
                        $total_time += Database::result($rs, 0, 0);
3307
                    }
3308
                }
3309
            }
3310
        }
3311
3312
        return $total_time;
3313
    }
3314
3315
    /**
3316
     * This function gets last connection time to one learning path.
3317
     *
3318
     * @param int|array $student_id  Student id(s)
3319
     * @param string    $course_code Course code
3320
     * @param int       $lp_id       Learning path id
3321
     * @param int       $session_id
3322
     *
3323
     * @return int Total time
3324
     */
3325
    public static function get_last_connection_time_in_lp(
3326
        $student_id,
3327
        $course_code,
3328
        $lp_id,
3329
        $session_id = 0
3330
    ) {
3331
        $course = api_get_course_info($course_code);
3332
        $student_id = (int) $student_id;
3333
        $lp_id = (int) $lp_id;
3334
        $session_id = (int) $session_id;
3335
        $lastTime = 0;
3336
3337
        if (!empty($course)) {
3338
            $course_id = $course['real_id'];
3339
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3340
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3341
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3342
3343
            // Check the real number of LPs corresponding to the filter in the
3344
            // database (and if no list was given, get them all)
3345
            $sql = "SELECT id FROM $lp_table
3346
                    WHERE c_id = $course_id AND id = $lp_id ";
3347
            $row = Database::query($sql);
3348
            $count = Database::num_rows($row);
3349
3350
            // calculates last connection time
3351
            if ($count > 0) {
3352
                $sql = 'SELECT MAX(start_time)
3353
                        FROM '.$t_lpiv.' AS item_view
3354
                        INNER JOIN '.$t_lpv.' AS view
3355
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3356
                        WHERE
3357
                            status != "not attempted" AND
3358
                            item_view.c_id = '.$course_id.' AND
3359
                            view.c_id = '.$course_id.' AND
3360
                            view.lp_id = '.$lp_id.' AND
3361
                            view.user_id = '.$student_id.' AND
3362
                            view.session_id = '.$session_id;
3363
                $rs = Database::query($sql);
3364
                if (Database::num_rows($rs) > 0) {
3365
                    $lastTime = Database::result($rs, 0, 0);
3366
                }
3367
            }
3368
        }
3369
3370
        return $lastTime;
3371
    }
3372
3373
    public static function getFirstConnectionTimeInLp(
3374
        $student_id,
3375
        $course_code,
3376
        $lp_id,
3377
        $session_id = 0
3378
    ) {
3379
        $course = api_get_course_info($course_code);
3380
        $student_id = (int) $student_id;
3381
        $lp_id = (int) $lp_id;
3382
        $session_id = (int) $session_id;
3383
        $time = 0;
3384
3385
        if (!empty($course)) {
3386
            $course_id = $course['real_id'];
3387
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3388
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3389
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3390
3391
            // Check the real number of LPs corresponding to the filter in the
3392
            // database (and if no list was given, get them all)
3393
            $sql = "SELECT id FROM $lp_table
3394
                    WHERE c_id = $course_id AND id = $lp_id ";
3395
            $row = Database::query($sql);
3396
            $count = Database::num_rows($row);
3397
3398
            // calculates first connection time
3399
            if ($count > 0) {
3400
                $sql = 'SELECT MIN(start_time)
3401
                        FROM '.$t_lpiv.' AS item_view
3402
                        INNER JOIN '.$t_lpv.' AS view
3403
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3404
                        WHERE
3405
                            status != "not attempted" AND
3406
                            item_view.c_id = '.$course_id.' AND
3407
                            view.c_id = '.$course_id.' AND
3408
                            view.lp_id = '.$lp_id.' AND
3409
                            view.user_id = '.$student_id.' AND
3410
                            view.session_id = '.$session_id;
3411
                $rs = Database::query($sql);
3412
                if (Database::num_rows($rs) > 0) {
3413
                    $time = Database::result($rs, 0, 0);
3414
                }
3415
            }
3416
        }
3417
3418
        return $time;
3419
    }
3420
3421
    /**
3422
     * gets the list of students followed by coach.
3423
     *
3424
     * @param int $coach_id Coach id
3425
     *
3426
     * @return array List of students
3427
     */
3428
    public static function get_student_followed_by_coach($coach_id)
3429
    {
3430
        $coach_id = intval($coach_id);
3431
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3432
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3433
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3434
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3435
3436
        $students = [];
3437
        // At first, courses where $coach_id is coach of the course //
3438
        $sql = 'SELECT session_id, c_id
3439
                FROM '.$tbl_session_course_user.'
3440
                WHERE user_id='.$coach_id.' AND status=2';
3441
3442
        if (api_is_multiple_url_enabled()) {
3443
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3444
            $access_url_id = api_get_current_access_url_id();
3445
            if (-1 != $access_url_id) {
3446
                $sql = 'SELECT scu.session_id, scu.c_id
3447
                        FROM '.$tbl_session_course_user.' scu
3448
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
3449
                        ON (scu.session_id=sru.session_id)
3450
                        WHERE
3451
                            scu.user_id='.$coach_id.' AND
3452
                            scu.status=2 AND
3453
                            sru.access_url_id = '.$access_url_id;
3454
            }
3455
        }
3456
3457
        $result = Database::query($sql);
3458
3459
        while ($a_courses = Database::fetch_array($result)) {
3460
            $courseId = $a_courses['c_id'];
3461
            $id_session = $a_courses['session_id'];
3462
3463
            $sql = "SELECT DISTINCT srcru.user_id
3464
                    FROM $tbl_session_course_user AS srcru
3465
                    INNER JOIN $tbl_session_user sru
3466
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
3467
                    WHERE
3468
                        sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
3469
                        srcru.c_id = '$courseId' AND
3470
                        srcru.session_id = '$id_session'";
3471
3472
            $rs = Database::query($sql);
3473
            while ($row = Database::fetch_array($rs)) {
3474
                $students[$row['user_id']] = $row['user_id'];
3475
            }
3476
        }
3477
3478
        // Then, courses where $coach_id is coach of the session
3479
        $sql = 'SELECT session_course_user.user_id
3480
                FROM '.$tbl_session_course_user.' as session_course_user
3481
                INNER JOIN '.$tbl_session_user.' sru
3482
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
3483
                INNER JOIN '.$tbl_session_course.' as session_course
3484
                ON session_course.c_id = session_course_user.c_id
3485
                AND session_course_user.session_id = session_course.session_id
3486
                INNER JOIN '.$tbl_session.' as session
3487
                ON session.id = session_course.session_id
3488
                AND session.id_coach = '.$coach_id;
3489
        if (api_is_multiple_url_enabled()) {
3490
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3491
            $access_url_id = api_get_current_access_url_id();
3492
            if (-1 != $access_url_id) {
3493
                $sql = 'SELECT session_course_user.user_id
3494
                        FROM '.$tbl_session_course_user.' as session_course_user
3495
                        INNER JOIN '.$tbl_session_user.' sru
3496
                        ON session_course_user.user_id = sru.user_id AND
3497
                           session_course_user.session_id = sru.session_id
3498
                        INNER JOIN '.$tbl_session_course.' as session_course
3499
                        ON session_course.c_id = session_course_user.c_id AND
3500
                        session_course_user.session_id = session_course.session_id
3501
                        INNER JOIN '.$tbl_session.' as session
3502
                        ON session.id = session_course.session_id AND
3503
                        session.id_coach = '.$coach_id.'
3504
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
3505
                        ON session.id = session_rel_url.session_id
3506
                        WHERE access_url_id = '.$access_url_id;
3507
            }
3508
        }
3509
3510
        $result = Database::query($sql);
3511
        while ($row = Database::fetch_array($result)) {
3512
            $students[$row['user_id']] = $row['user_id'];
3513
        }
3514
3515
        return $students;
3516
    }
3517
3518
    /**
3519
     * Check if a coach is allowed to follow a student.
3520
     *
3521
     * @param    int        Coach id
3522
     * @param    int        Student id
3523
     *
3524
     * @return bool
3525
     */
3526
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3527
    {
3528
        $coach_id = intval($coach_id);
3529
        $student_id = intval($student_id);
3530
3531
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3532
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3533
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3534
3535
        // At first, courses where $coach_id is coach of the course
3536
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
3537
                WHERE user_id='.$coach_id.' AND status=2';
3538
        $result = Database::query($sql);
3539
        if (Database::num_rows($result) > 0) {
3540
            return true;
3541
        }
3542
3543
        // Then, courses where $coach_id is coach of the session
3544
        $sql = 'SELECT session_course_user.user_id
3545
                FROM '.$tbl_session_course_user.' as session_course_user
3546
                INNER JOIN '.$tbl_session_course.' as session_course
3547
                ON session_course.c_id = session_course_user.c_id
3548
                INNER JOIN '.$tbl_session.' as session
3549
                ON session.id = session_course.session_id
3550
                AND session.id_coach = '.$coach_id.'
3551
                WHERE user_id = '.$student_id;
3552
        $result = Database::query($sql);
3553
        if (Database::num_rows($result) > 0) {
3554
            return true;
3555
        }
3556
3557
        return false;
3558
    }
3559
3560
    /**
3561
     * Get courses followed by coach.
3562
     *
3563
     * @param     int        Coach id
3564
     * @param    int        Session id (optional)
3565
     *
3566
     * @return array Courses list
3567
     */
3568
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
3569
    {
3570
        $coach_id = intval($coach_id);
3571
        if (!empty($id_session)) {
3572
            $id_session = intval($id_session);
3573
        }
3574
3575
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3576
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3577
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3578
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3579
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3580
3581
        // At first, courses where $coach_id is coach of the course.
3582
        $sql = 'SELECT DISTINCT c.code
3583
                FROM '.$tbl_session_course_user.' sc
3584
                INNER JOIN '.$tbl_course.' c
3585
                ON (c.id = sc.c_id)
3586
                WHERE user_id = '.$coach_id.' AND status = 2';
3587
3588
        if (api_is_multiple_url_enabled()) {
3589
            $access_url_id = api_get_current_access_url_id();
3590
            if (-1 != $access_url_id) {
3591
                $sql = 'SELECT DISTINCT c.code
3592
                        FROM '.$tbl_session_course_user.' scu
3593
                        INNER JOIN '.$tbl_course.' c
3594
                        ON (c.code = scu.c_id)
3595
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3596
                        ON (c.id = cru.c_id)
3597
                        WHERE
3598
                            scu.user_id='.$coach_id.' AND
3599
                            scu.status=2 AND
3600
                            cru.access_url_id = '.$access_url_id;
3601
            }
3602
        }
3603
3604
        if (!empty($id_session)) {
3605
            $sql .= ' AND session_id='.$id_session;
3606
        }
3607
3608
        $courseList = [];
3609
        $result = Database::query($sql);
3610
        while ($row = Database::fetch_array($result)) {
3611
            $courseList[$row['code']] = $row['code'];
3612
        }
3613
3614
        // Then, courses where $coach_id is coach of the session
3615
        $sql = 'SELECT DISTINCT course.code
3616
                FROM '.$tbl_session_course.' as session_course
3617
                INNER JOIN '.$tbl_session.' as session
3618
                    ON session.id = session_course.session_id
3619
                    AND session.id_coach = '.$coach_id.'
3620
                INNER JOIN '.$tbl_course.' as course
3621
                    ON course.id = session_course.c_id';
3622
3623
        if (api_is_multiple_url_enabled()) {
3624
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3625
            $access_url_id = api_get_current_access_url_id();
3626
            if (-1 != $access_url_id) {
3627
                $sql = 'SELECT DISTINCT c.code
3628
                    FROM '.$tbl_session_course.' as session_course
3629
                    INNER JOIN '.$tbl_course.' c
3630
                    ON (c.id = session_course.c_id)
3631
                    INNER JOIN '.$tbl_session.' as session
3632
                    ON session.id = session_course.session_id
3633
                        AND session.id_coach = '.$coach_id.'
3634
                    INNER JOIN '.$tbl_course.' as course
3635
                        ON course.id = session_course.c_id
3636
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
3637
                    ON (course_rel_url.c_id = c.id)';
3638
            }
3639
        }
3640
3641
        if (!empty($id_session)) {
3642
            $sql .= ' WHERE session_course.session_id='.$id_session;
3643
            if (api_is_multiple_url_enabled()) {
3644
                $sql .= ' AND access_url_id = '.$access_url_id;
3645
            }
3646
        } else {
3647
            if (api_is_multiple_url_enabled()) {
3648
                $sql .= ' WHERE access_url_id = '.$access_url_id;
3649
            }
3650
        }
3651
3652
        $result = Database::query($sql);
3653
        while ($row = Database::fetch_array($result)) {
3654
            $courseList[$row['code']] = $row['code'];
3655
        }
3656
3657
        return $courseList;
3658
    }
3659
3660
    /**
3661
     * Get sessions coached by user.
3662
     *
3663
     * @param int    $coach_id
3664
     * @param int    $start
3665
     * @param int    $limit
3666
     * @param bool   $getCount
3667
     * @param string $keyword
3668
     * @param string $description
3669
     * @param string $orderByName
3670
     * @param string $orderByDirection
3671
     * @param array  $options
3672
     *
3673
     * @return mixed
3674
     */
3675
    public static function get_sessions_coached_by_user(
3676
        $coach_id,
3677
        $start = 0,
3678
        $limit = 0,
3679
        $getCount = false,
3680
        $keyword = '',
3681
        $description = '',
3682
        $orderByName = '',
3683
        $orderByDirection = '',
3684
        $options = []
3685
    ) {
3686
        // table definition
3687
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3688
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3689
        $coach_id = (int) $coach_id;
3690
3691
        $select = ' SELECT * FROM ';
3692
        if ($getCount) {
3693
            $select = ' SELECT count(DISTINCT id) as count FROM ';
3694
        }
3695
3696
        $limitCondition = null;
3697
        if (!empty($start) && !empty($limit)) {
3698
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3699
        }
3700
3701
        $keywordCondition = null;
3702
        if (!empty($keyword)) {
3703
            $keyword = Database::escape_string($keyword);
3704
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
3705
3706
            if (!empty($description)) {
3707
                $description = Database::escape_string($description);
3708
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3709
            }
3710
        }
3711
3712
        $extraFieldModel = new ExtraFieldModel('session');
3713
        $conditions = $extraFieldModel->parseConditions($options);
3714
        $sqlInjectJoins = $conditions['inject_joins'];
3715
        $extraFieldsConditions = $conditions['where'];
3716
        $sqlInjectWhere = $conditions['inject_where'];
3717
        $injectExtraFields = $conditions['inject_extra_fields'];
3718
3719
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3720
        $access_url_id = api_get_current_access_url_id();
3721
3722
        $orderBy = '';
3723
        if (!empty($orderByName)) {
3724
            if (in_array($orderByName, ['name', 'access_start_date'])) {
3725
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
3726
                $orderByName = Database::escape_string($orderByName);
3727
                $orderBy .= " ORDER BY $orderByName $orderByDirection";
3728
            }
3729
        }
3730
3731
        $sql = "
3732
            $select
3733
            (
3734
                SELECT DISTINCT
3735
                    s.id,
3736
                    name,
3737
                    $injectExtraFields
3738
                    access_start_date,
3739
                    access_end_date
3740
                FROM $tbl_session s
3741
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3742
                ON (s.id = session_rel_url.session_id)
3743
                $sqlInjectJoins
3744
                WHERE
3745
                    id_coach = $coach_id AND
3746
                    access_url_id = $access_url_id
3747
                    $keywordCondition
3748
                    $extraFieldsConditions
3749
                    $sqlInjectWhere
3750
            UNION
3751
                SELECT DISTINCT
3752
                    s.id,
3753
                    s.name,
3754
                    $injectExtraFields
3755
                    s.access_start_date,
3756
                    s.access_end_date
3757
                FROM $tbl_session as s
3758
                INNER JOIN $tbl_session_course_user as session_course_user
3759
                ON
3760
                    s.id = session_course_user.session_id AND
3761
                    session_course_user.user_id = $coach_id AND
3762
                    session_course_user.status = 2
3763
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3764
                ON (s.id = session_rel_url.session_id)
3765
                $sqlInjectJoins
3766
                WHERE
3767
                    access_url_id = $access_url_id
3768
                    $keywordCondition
3769
                    $extraFieldsConditions
3770
                    $sqlInjectWhere
3771
            ) as sessions $limitCondition $orderBy
3772
            ";
3773
3774
        $rs = Database::query($sql);
3775
        if ($getCount) {
3776
            $row = Database::fetch_array($rs);
3777
3778
            return $row['count'];
3779
        }
3780
3781
        $sessions = [];
3782
        while ($row = Database::fetch_array($rs)) {
3783
            if ('0000-00-00 00:00:00' === $row['access_start_date']) {
3784
                $row['access_start_date'] = null;
3785
            }
3786
3787
            $sessions[$row['id']] = $row;
3788
        }
3789
3790
        if (!empty($sessions)) {
3791
            foreach ($sessions as &$session) {
3792
                if (empty($session['access_start_date'])) {
3793
                    $session['status'] = get_lang('active');
3794
                } else {
3795
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3796
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3797
                    if ($time_start < time() && time() < $time_end) {
3798
                        $session['status'] = get_lang('active');
3799
                    } else {
3800
                        if (time() < $time_start) {
3801
                            $session['status'] = get_lang('Not yet begun');
3802
                        } else {
3803
                            if (time() > $time_end) {
3804
                                $session['status'] = get_lang('Past');
3805
                            }
3806
                        }
3807
                    }
3808
                }
3809
            }
3810
        }
3811
3812
        return $sessions;
3813
    }
3814
3815
    /**
3816
     * Get courses list from a session.
3817
     *
3818
     * @param    int        Session id
3819
     *
3820
     * @return array Courses list
3821
     */
3822
    public static function get_courses_list_from_session($session_id)
3823
    {
3824
        $session_id = (int) $session_id;
3825
3826
        // table definition
3827
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3828
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3829
3830
        $sql = "SELECT DISTINCT code, c_id
3831
                FROM $tbl_session_course sc
3832
                INNER JOIN $courseTable c
3833
                ON sc.c_id = c.id
3834
                WHERE session_id= $session_id";
3835
3836
        $result = Database::query($sql);
3837
3838
        $courses = [];
3839
        while ($row = Database::fetch_array($result)) {
3840
            $courses[$row['code']] = $row;
3841
        }
3842
3843
        return $courses;
3844
    }
3845
3846
    /**
3847
     * Count the number of documents that an user has uploaded to a course.
3848
     *
3849
     * @param    int|array   Student id(s)
3850
     * @param    string      Course code
3851
     * @param    int         Session id (optional),
3852
     * if param $session_id is null(default)
3853
     * return count of assignments including sessions, 0 = session is not filtered
3854
     *
3855
     * @return int Number of documents
3856
     */
3857
    public static function count_student_uploaded_documents(
3858
        $student_id,
3859
        $course_code,
3860
        $session_id = null
3861
    ) {
3862
        $a_course = api_get_course_info($course_code);
3863
        $repo = Container::getDocumentRepository();
3864
3865
        $user = api_get_user_entity($student_id);
3866
        $course = api_get_course_entity($a_course['real_id']);
3867
        $session = api_get_session_entity($session_id);
3868
        //$group = api_get_group_entity(api_get_group_id());
3869
3870
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
3871
3872
        $qb->select('count(resource)');
3873
        $count = $qb->getQuery()->getSingleScalarResult();
3874
3875
        return $count;
3876
3877
        // get the information of the course
3878
        $a_course = api_get_course_info($course_code);
0 ignored issues
show
Unused Code introduced by
$a_course = api_get_course_info($course_code) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
3879
        if (!empty($a_course)) {
3880
            // table definition
3881
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3882
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
3883
            $course_id = $a_course['real_id'];
3884
            if (is_array($student_id)) {
3885
                $studentList = array_map('intval', $student_id);
3886
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
3887
            } else {
3888
                $student_id = (int) $student_id;
3889
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
3890
            }
3891
3892
            $condition_session = null;
3893
            if (isset($session_id)) {
3894
                $session_id = (int) $session_id;
3895
                $condition_session = " AND pub.session_id = $session_id ";
3896
            }
3897
3898
            $sql = "SELECT count(ip.tool) AS count
3899
                    FROM $tbl_item_property ip
3900
                    INNER JOIN $tbl_document pub
3901
                    ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
3902
                    WHERE
3903
                        ip.c_id  = $course_id AND
3904
                        pub.c_id  = $course_id AND
3905
                        pub.filetype ='file' AND
3906
                        ip.tool = 'document'
3907
                        $condition_user $condition_session ";
3908
            $rs = Database::query($sql);
3909
            $row = Database::fetch_array($rs, 'ASSOC');
3910
3911
            return $row['count'];
3912
        }
3913
3914
        return null;
3915
    }
3916
3917
    /**
3918
     * Count assignments per student.
3919
     *
3920
     * @param array|int $student_id
3921
     * @param string    $course_code
3922
     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
3923
     *                               0 = session is not filtered
3924
     *
3925
     * @return int Count of assignments
3926
     */
3927
    public static function count_student_assignments(
3928
        $student_id,
3929
        $course_code = null,
3930
        $session_id = null
3931
    ) {
3932
        if (empty($student_id)) {
3933
            return 0;
3934
        }
3935
3936
        $a_course = api_get_course_info($course_code);
3937
        $repo = Container::getStudentPublicationRepository();
3938
3939
        $user = api_get_user_entity($student_id);
3940
        $course = api_get_course_entity($a_course['real_id']);
3941
        $session = api_get_session_entity($session_id);
3942
        //$group = api_get_group_entity(api_get_group_id());
3943
3944
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
3945
3946
        $qb->select('count(resource)');
3947
        $count = $qb->getQuery()->getSingleScalarResult();
3948
3949
        return $count;
3950
3951
        $conditions = [];
0 ignored issues
show
Unused Code introduced by
$conditions = array() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
3952
3953
        // Get the information of the course
3954
        $a_course = api_get_course_info($course_code);
3955
        if (!empty($a_course)) {
3956
            $course_id = $a_course['real_id'];
3957
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
3958
        }
3959
3960
        // table definition
3961
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3962
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
3963
3964
        if (is_array($student_id)) {
3965
            $studentList = array_map('intval', $student_id);
3966
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
3967
        } else {
3968
            $student_id = (int) $student_id;
3969
            $conditions[] = " ip.insert_user_id = '$student_id' ";
3970
        }
3971
3972
        $conditions[] = ' pub.active <> 2 ';
3973
        $conditionToString = implode(' AND ', $conditions);
3974
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
3975
        $conditionToString .= $sessionCondition;
3976
3977
        $sql = "SELECT count(ip.tool) as count
3978
                FROM $tbl_item_property ip
3979
                INNER JOIN $tbl_student_publication pub
3980
                ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
3981
                WHERE
3982
                    ip.tool='work' AND
3983
                    $conditionToString";
3984
        $rs = Database::query($sql);
3985
        $row = Database::fetch_array($rs, 'ASSOC');
3986
3987
        return $row['count'];
3988
    }
3989
3990
    /**
3991
     * Count messages per student inside forum tool.
3992
     *
3993
     * @param int|array  Student id
3994
     * @param string     Course code
3995
     * @param int        Session id if null(default) return count of messages including sessions, 0 = session is not filtered
3996
     *
3997
     * @return int Count of messages
3998
     */
3999
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
4000
    {
4001
        if (empty($student_id)) {
4002
            return 0;
4003
        }
4004
4005
        $a_course = api_get_course_info($courseCode);
4006
        $repo = Container::getForumPostRepository();
4007
4008
        $user = api_get_user_entity($student_id);
4009
        $course = api_get_course_entity($a_course['real_id']);
4010
        $session = api_get_session_entity($session_id);
4011
        //$group = api_get_group_entity(api_get_group_id());
4012
4013
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
4014
4015
        $qb->select('count(resource)');
4016
4017
        return $qb->getQuery()->getSingleScalarResult();
4018
    }
4019
4020
    /**
4021
     * This function counts the number of post by course.
4022
     *
4023
     * @param string $course_code
4024
     * @param int    $session_id  (optional), if is null(default) it'll return results including sessions,
4025
     *                            0 = session is not filtered
4026
     * @param int    $groupId
4027
     *
4028
     * @return int The number of post by course
4029
     */
4030
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
4031
    {
4032
        $courseInfo = api_get_course_info($course_code);
4033
        if (!empty($courseInfo)) {
4034
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
4035
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
4036
4037
            $condition_session = '';
4038
            if (isset($session_id)) {
4039
                $session_id = (int) $session_id;
4040
                $condition_session = api_get_session_condition(
4041
                    $session_id,
4042
                    true,
4043
                    false,
4044
                    'f.session_id'
4045
                );
4046
            }
4047
4048
            $course_id = $courseInfo['real_id'];
4049
            $groupId = (int) $groupId;
4050
            if (!empty($groupId)) {
4051
                $groupCondition = " i.to_group_id = $groupId ";
4052
            } else {
4053
                $groupCondition = ' (i.to_group_id = 0 OR i.to_group_id IS NULL) ';
4054
            }
4055
4056
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4057
            $sql = "SELECT count(*) FROM $tbl_posts p
4058
                    INNER JOIN $tbl_forums f
4059
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
4060
                    INNER JOIN $item i
4061
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
4062
                    WHERE
4063
                        p.c_id = $course_id AND
4064
                        f.c_id = $course_id AND
4065
                        $groupCondition
4066
                        $condition_session
4067
                    ";
4068
            $result = Database::query($sql);
4069
            $row = Database::fetch_row($result);
4070
            $count = $row[0];
4071
4072
            return $count;
4073
        }
4074
4075
        return 0;
4076
    }
4077
4078
    /**
4079
     * This function counts the number of threads by course.
4080
     *
4081
     * @param      string     Course code
4082
     * @param    int        Session id (optional),
4083
     * if param $session_id is null(default) it'll return results including
4084
     * sessions, 0 = session is not filtered
4085
     * @param int $groupId
4086
     *
4087
     * @return int The number of threads by course
4088
     */
4089
    public static function count_number_of_threads_by_course(
4090
        $course_code,
4091
        $session_id = null,
4092
        $groupId = 0
4093
    ) {
4094
        $course_info = api_get_course_info($course_code);
4095
        if (empty($course_info)) {
4096
            return null;
4097
        }
4098
4099
        $course_id = $course_info['real_id'];
4100
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
4101
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4102
4103
        $condition_session = '';
4104
        if (isset($session_id)) {
4105
            $session_id = (int) $session_id;
4106
            $condition_session = ' AND f.session_id = '.$session_id;
4107
        }
4108
4109
        $groupId = (int) $groupId;
4110
4111
        if (!empty($groupId)) {
4112
            $groupCondition = " i.to_group_id = $groupId ";
4113
        } else {
4114
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4115
        }
4116
4117
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4118
        $sql = "SELECT count(*)
4119
                FROM $tbl_threads t
4120
                INNER JOIN $tbl_forums f
4121
                ON f.iid = t.forum_id AND f.c_id = t.c_id
4122
                INNER JOIN $item i
4123
                ON (
4124
                    tool = '".TOOL_FORUM_THREAD."' AND
4125
                    f.c_id = i.c_id AND
4126
                    t.iid = i.ref
4127
                )
4128
                WHERE
4129
                    t.c_id = $course_id AND
4130
                    f.c_id = $course_id AND
4131
                    $groupCondition
4132
                    $condition_session
4133
                ";
4134
4135
        $result = Database::query($sql);
4136
        if (Database::num_rows($result)) {
4137
            $row = Database::fetch_row($result);
4138
            $count = $row[0];
4139
4140
            return $count;
4141
        }
4142
4143
        return 0;
4144
    }
4145
4146
    /**
4147
     * This function counts the number of forums by course.
4148
     *
4149
     * @param      string     Course code
4150
     * @param    int        Session id (optional),
4151
     * if param $session_id is null(default) it'll return results
4152
     * including sessions, 0 = session is not filtered
4153
     * @param int $groupId
4154
     *
4155
     * @return int The number of forums by course
4156
     */
4157
    public static function count_number_of_forums_by_course(
4158
        $course_code,
4159
        $session_id = null,
4160
        $groupId = 0
4161
    ) {
4162
        $course_info = api_get_course_info($course_code);
4163
        if (empty($course_info)) {
4164
            return null;
4165
        }
4166
        $course_id = $course_info['real_id'];
4167
4168
        $condition_session = '';
4169
        if (isset($session_id)) {
4170
            $session_id = (int) $session_id;
4171
            $condition_session = ' AND f.session_id = '.$session_id;
4172
        }
4173
4174
        $groupId = (int) $groupId;
4175
        if (!empty($groupId)) {
4176
            $groupCondition = " i.to_group_id = $groupId ";
4177
        } else {
4178
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4179
        }
4180
4181
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4182
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4183
4184
        $sql = "SELECT count(*)
4185
                FROM $tbl_forums f
4186
                INNER JOIN $item i
4187
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
4188
                WHERE
4189
                    f.c_id = $course_id AND
4190
                    $groupCondition
4191
                    $condition_session
4192
                ";
4193
        $result = Database::query($sql);
4194
        if (Database::num_rows($result)) {
4195
            $row = Database::fetch_row($result);
4196
            $count = $row[0];
4197
4198
            return $count;
4199
        }
4200
4201
        return 0;
4202
    }
4203
4204
    /**
4205
     * This function counts the chat last connections by course in x days.
4206
     *
4207
     * @param      string     Course code
4208
     * @param      int     Last x days
4209
     * @param    int        Session id (optional)
4210
     *
4211
     * @return int Chat last connections by course in x days
4212
     */
4213
    public static function chat_connections_during_last_x_days_by_course(
4214
        $course_code,
4215
        $last_days,
4216
        $session_id = 0
4217
    ) {
4218
        $course_info = api_get_course_info($course_code);
4219
        if (empty($course_info)) {
4220
            return null;
4221
        }
4222
        $course_id = $course_info['real_id'];
4223
4224
        // Protect data
4225
        $last_days = (int) $last_days;
4226
        $session_id = (int) $session_id;
4227
4228
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4229
        $now = api_get_utc_datetime();
4230
4231
        $sql = "SELECT count(*) FROM $tbl_stats_access
4232
                WHERE
4233
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
4234
                    c_id = '$course_id' AND
4235
                    access_tool='".TOOL_CHAT."' AND
4236
                    access_session_id = '$session_id' ";
4237
        $result = Database::query($sql);
4238
        if (Database::num_rows($result)) {
4239
            $row = Database::fetch_row($result);
4240
            $count = $row[0];
4241
4242
            return $count;
4243
        }
4244
4245
        return 0;
4246
    }
4247
4248
    /**
4249
     * This function gets the last student's connection in chat.
4250
     *
4251
     * @param      int     Student id
4252
     * @param      string     Course code
4253
     * @param    int        Session id (optional)
4254
     *
4255
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
4256
     */
4257
    public static function chat_last_connection(
4258
        $student_id,
4259
        $courseId,
4260
        $session_id = 0
4261
    ) {
4262
        $student_id = (int) $student_id;
4263
        $courseId = (int) $courseId;
4264
        $session_id = (int) $session_id;
4265
        $date_time = '';
4266
4267
        // table definition
4268
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4269
        $sql = "SELECT access_date
4270
                FROM $tbl_stats_access
4271
                WHERE
4272
                     access_tool='".TOOL_CHAT."' AND
4273
                     access_user_id='$student_id' AND
4274
                     c_id = $courseId AND
4275
                     access_session_id = '$session_id'
4276
                ORDER BY access_date DESC limit 1";
4277
        $rs = Database::query($sql);
4278
        if (Database::num_rows($rs) > 0) {
4279
            $row = Database::fetch_array($rs);
4280
            $date_time = api_convert_and_format_date(
4281
                $row['access_date'],
4282
                null,
4283
                date_default_timezone_get()
4284
            );
4285
        }
4286
4287
        return $date_time;
4288
    }
4289
4290
    /**
4291
     * Get count student's visited links.
4292
     *
4293
     * @param int $student_id Student id
4294
     * @param int $courseId
4295
     * @param int $session_id Session id (optional)
4296
     *
4297
     * @return int count of visited links
4298
     */
4299
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
4300
    {
4301
        $student_id = (int) $student_id;
4302
        $courseId = (int) $courseId;
4303
        $session_id = (int) $session_id;
4304
4305
        // table definition
4306
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4307
4308
        $sql = 'SELECT 1
4309
                FROM '.$table.'
4310
                WHERE
4311
                    links_user_id= '.$student_id.' AND
4312
                    c_id = "'.$courseId.'" AND
4313
                    links_session_id = '.$session_id.' ';
4314
4315
        $rs = Database::query($sql);
4316
4317
        return Database::num_rows($rs);
4318
    }
4319
4320
    /**
4321
     * Get count student downloaded documents.
4322
     *
4323
     * @param    int        Student id
4324
     * @param int $courseId
4325
     * @param    int        Session id (optional)
4326
     *
4327
     * @return int Count downloaded documents
4328
     */
4329
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
4330
    {
4331
        $student_id = (int) $student_id;
4332
        $courseId = (int) $courseId;
4333
        $session_id = (int) $session_id;
4334
4335
        // table definition
4336
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4337
4338
        $sql = 'SELECT 1
4339
                FROM '.$table.'
4340
                WHERE down_user_id = '.$student_id.'
4341
                AND c_id  = "'.$courseId.'"
4342
                AND down_session_id = '.$session_id.' ';
4343
        $rs = Database::query($sql);
4344
4345
        return Database::num_rows($rs);
4346
    }
4347
4348
    /**
4349
     * Get course list inside a session from a student.
4350
     *
4351
     * @param int $user_id    Student id
4352
     * @param int $id_session Session id (optional)
4353
     *
4354
     * @return array Courses list
4355
     */
4356
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
4357
    {
4358
        $user_id = intval($user_id);
4359
        $id_session = intval($id_session);
4360
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4361
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4362
4363
        $sql = "SELECT c.code
4364
                FROM $tbl_session_course_user sc
4365
                INNER JOIN $courseTable c
4366
                WHERE
4367
                    user_id= $user_id  AND
4368
                    session_id = $id_session";
4369
        $result = Database::query($sql);
4370
        $courses = [];
4371
        while ($row = Database::fetch_array($result)) {
4372
            $courses[$row['code']] = $row['code'];
4373
        }
4374
4375
        return $courses;
4376
    }
4377
4378
    /**
4379
     * Get inactive students in course.
4380
     *
4381
     * @param int        $courseId
4382
     * @param string|int $since      Since login course date (optional, default = 'never')
4383
     * @param int        $session_id (optional)
4384
     *
4385
     * @return array Inactive users
4386
     */
4387
    public static function getInactiveStudentsInCourse(
4388
        $courseId,
4389
        $since = 'never',
4390
        $session_id = 0
4391
    ) {
4392
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4393
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4394
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4395
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4396
        $now = api_get_utc_datetime();
4397
        $courseId = (int) $courseId;
4398
        $session_id = (int) $session_id;
4399
4400
        if (empty($courseId)) {
4401
            return false;
4402
        }
4403
4404
        if ('never' === $since) {
4405
            if (empty($session_id)) {
4406
                $sql = 'SELECT course_user.user_id
4407
                        FROM '.$table_course_rel_user.' course_user
4408
                        LEFT JOIN '.$tbl_track_login.' stats_login
4409
                        ON course_user.user_id = stats_login.user_id AND
4410
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
4411
                        INNER JOIN '.$tableCourse.' c
4412
                        ON (c.id = course_user.c_id)
4413
                        WHERE
4414
                            course_user.c_id = '.$courseId.' AND
4415
                            stats_login.login_course_date IS NULL
4416
                        GROUP BY course_user.user_id';
4417
            } else {
4418
                $sql = 'SELECT session_course_user.user_id
4419
                        FROM '.$tbl_session_course_user.' session_course_user
4420
                        LEFT JOIN '.$tbl_track_login.' stats_login
4421
                        ON session_course_user.user_id = stats_login.user_id
4422
                        INNER JOIN '.$tableCourse.' c
4423
                        ON (c.id = session_course_user.c_id)
4424
                        WHERE
4425
                            session_course_user.c_id = '.$courseId.' AND
4426
                            stats_login.login_course_date IS NULL
4427
                        GROUP BY session_course_user.user_id';
4428
            }
4429
        } else {
4430
            $since = (int) $since;
4431
            if (empty($session_id)) {
4432
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
4433
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
4434
            } else {
4435
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
4436
                          ON
4437
                            c.id = session_course_user.c_id AND
4438
                            session_course_user.session_id = '.$session_id.' AND
4439
                            session_course_user.user_id = stats_login.user_id ';
4440
            }
4441
4442
            $sql = 'SELECT
4443
                    stats_login.user_id,
4444
                    MAX(login_course_date) max_date
4445
                FROM '.$tbl_track_login.' stats_login
4446
                INNER JOIN '.$tableCourse.' c
4447
                ON (c.id = stats_login.c_id)
4448
                '.$inner.'
4449
                WHERE c.id = '.$courseId.'
4450
                GROUP BY stats_login.user_id
4451
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
4452
        }
4453
4454
        $rs = Database::query($sql);
4455
        $users = [];
4456
        while ($user = Database::fetch_array($rs)) {
4457
            $users[] = $user['user_id'];
4458
        }
4459
4460
        return $users;
4461
    }
4462
4463
    /**
4464
     * get count clicks about tools most used by course.
4465
     *
4466
     * @param int $courseId
4467
     * @param    int        Session id (optional),
4468
     * if param $session_id is null(default) it'll return results
4469
     * including sessions, 0 = session is not filtered
4470
     *
4471
     * @return array tools data
4472
     */
4473
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
4474
    {
4475
        $courseId = (int) $courseId;
4476
        $data = [];
4477
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4478
        $condition_session = '';
4479
        if (isset($session_id)) {
4480
            $session_id = (int) $session_id;
4481
            $condition_session = ' AND access_session_id = '.$session_id;
4482
        }
4483
        $sql = "SELECT
4484
                    access_tool,
4485
                    COUNT(DISTINCT access_user_id),
4486
                    count(access_tool) as count_access_tool
4487
                FROM $TABLETRACK_ACCESS
4488
                WHERE
4489
                    access_tool IS NOT NULL AND
4490
                    access_tool != '' AND
4491
                    c_id = '$courseId'
4492
                    $condition_session
4493
                GROUP BY access_tool
4494
                ORDER BY count_access_tool DESC
4495
                LIMIT 0, 3";
4496
        $rs = Database::query($sql);
4497
        if (Database::num_rows($rs) > 0) {
4498
            while ($row = Database::fetch_array($rs)) {
4499
                $data[] = $row;
4500
            }
4501
        }
4502
4503
        return $data;
4504
    }
4505
4506
    /**
4507
     * get documents most downloaded by course.
4508
     *
4509
     * @param      string     Course code
4510
     * @param    int        Session id (optional),
4511
     * if param $session_id is null(default) it'll return results including
4512
     * sessions, 0 = session is not filtered
4513
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4514
     *
4515
     * @return array documents downloaded
4516
     */
4517
    public static function get_documents_most_downloaded_by_course(
4518
        $course_code,
4519
        $session_id = 0,
4520
        $limit = 0
4521
    ) {
4522
        $courseId = api_get_course_int_id($course_code);
4523
        $data = [];
4524
4525
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4526
        $condition_session = '';
4527
        $session_id = intval($session_id);
4528
        if (!empty($session_id)) {
4529
            $condition_session = ' AND down_session_id = '.$session_id;
4530
        }
4531
        $sql = "SELECT
4532
                    down_doc_path,
4533
                    COUNT(DISTINCT down_user_id),
4534
                    COUNT(down_doc_path) as count_down
4535
                FROM $TABLETRACK_DOWNLOADS
4536
                WHERE c_id = $courseId
4537
                    $condition_session
4538
                GROUP BY down_doc_path
4539
                ORDER BY count_down DESC
4540
                LIMIT 0,  $limit";
4541
        $rs = Database::query($sql);
4542
4543
        if (Database::num_rows($rs) > 0) {
4544
            while ($row = Database::fetch_array($rs)) {
4545
                $data[] = $row;
4546
            }
4547
        }
4548
4549
        return $data;
4550
    }
4551
4552
    /**
4553
     * get links most visited by course.
4554
     *
4555
     * @param      string     Course code
4556
     * @param    int        Session id (optional),
4557
     * if param $session_id is null(default) it'll
4558
     * return results including sessions, 0 = session is not filtered
4559
     *
4560
     * @return array links most visited
4561
     */
4562
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4563
    {
4564
        $course_code = Database::escape_string($course_code);
4565
        $course_info = api_get_course_info($course_code);
4566
        $course_id = $course_info['real_id'];
4567
        $data = [];
4568
4569
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4570
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4571
4572
        $condition_session = '';
4573
        if (isset($session_id)) {
4574
            $session_id = intval($session_id);
4575
            $condition_session = ' AND cl.session_id = '.$session_id;
4576
        }
4577
4578
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4579
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4580
                WHERE
4581
                    cl.c_id = $course_id AND
4582
                    sl.links_link_id = cl.id AND
4583
                    sl.c_id = $course_id
4584
                    $condition_session
4585
                GROUP BY cl.title, cl.url
4586
                ORDER BY count_visits DESC
4587
                LIMIT 0, 3";
4588
        $rs = Database::query($sql);
4589
        if (Database::num_rows($rs) > 0) {
4590
            while ($row = Database::fetch_array($rs)) {
4591
                $data[] = $row;
4592
            }
4593
        }
4594
4595
        return $data;
4596
    }
4597
4598
    /**
4599
     * Shows the user progress (when clicking in the Progress tab).
4600
     *
4601
     * @param int    $user_id
4602
     * @param int    $session_id
4603
     * @param string $extra_params
4604
     * @param bool   $show_courses
4605
     * @param bool   $showAllSessions
4606
     * @param bool   $returnArray
4607
     *
4608
     * @return string|array
4609
     */
4610
    public static function show_user_progress(
4611
        $user_id,
4612
        $session_id = 0,
4613
        $extra_params = '',
4614
        $show_courses = true,
4615
        $showAllSessions = true,
4616
        $returnArray = false
4617
    ) {
4618
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4619
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4620
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4621
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4622
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4623
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4624
4625
        $trackingColumns = [
4626
            'course_session' => [
4627
                'course_title' => true,
4628
                'published_exercises' => true,
4629
                'new_exercises' => true,
4630
                'my_average' => true,
4631
                'average_exercise_result' => true,
4632
                'time_spent' => true,
4633
                'lp_progress' => true,
4634
                'score' => true,
4635
                'best_score' => true,
4636
                'last_connection' => true,
4637
                'details' => true,
4638
            ],
4639
        ];
4640
4641
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
4642
        if (!empty($trackingColumnsConfig)) {
4643
            $trackingColumns = $trackingColumnsConfig;
4644
        }
4645
4646
        $user_id = (int) $user_id;
4647
        $session_id = (int) $session_id;
4648
        $urlId = api_get_current_access_url_id();
4649
4650
        if (api_is_multiple_url_enabled()) {
4651
            $sql = "SELECT c.id, c.code, title
4652
                    FROM $tbl_course_user cu
4653
                    INNER JOIN $tbl_course c
4654
                    ON (cu.c_id = c.id)
4655
                    INNER JOIN $tbl_access_rel_course a
4656
                    ON (a.c_id = c.id)
4657
                    WHERE
4658
                        cu.user_id = $user_id AND
4659
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4660
                        access_url_id = $urlId
4661
                    ORDER BY title";
4662
        } else {
4663
            $sql = "SELECT c.id, c.code, title
4664
                    FROM $tbl_course_user u
4665
                    INNER JOIN $tbl_course c ON (c_id = c.id)
4666
                    WHERE
4667
                        u.user_id= $user_id AND
4668
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
4669
                    ORDER BY title";
4670
        }
4671
4672
        $rs = Database::query($sql);
4673
        $courses = $course_in_session = $temp_course_in_session = [];
4674
        $courseIdList = [];
4675
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4676
            $courses[$row['code']] = $row['title'];
4677
            $courseIdList[] = $row['id'];
4678
        }
4679
4680
        $orderBy = ' ORDER BY name ';
4681
        $extraInnerJoin = null;
4682
4683
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4684
            $orderBy = ' ORDER BY s.id, src.position ';
4685
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4686
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4687
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4688
        }
4689
4690
        $sessionCondition = '';
4691
        if (!empty($session_id)) {
4692
            $sessionCondition = " AND s.id = $session_id";
4693
        }
4694
4695
        // Get the list of sessions where the user is subscribed as student
4696
        if (api_is_multiple_url_enabled()) {
4697
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4698
                    FROM $tbl_session_course_user cu
4699
                    INNER JOIN $tbl_access_rel_session a
4700
                    ON (a.session_id = cu.session_id)
4701
                    INNER JOIN $tbl_session s
4702
                    ON (s.id = a.session_id)
4703
                    INNER JOIN $tbl_course c
4704
                    ON (c.id = cu.c_id)
4705
                    $extraInnerJoin
4706
                    WHERE
4707
                        cu.user_id = $user_id AND
4708
                        access_url_id = ".$urlId."
4709
                        $sessionCondition
4710
                    $orderBy ";
4711
        } else {
4712
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4713
                    FROM $tbl_session_course_user cu
4714
                    INNER JOIN $tbl_session s
4715
                    ON (s.id = cu.session_id)
4716
                    INNER JOIN $tbl_course c
4717
                    ON (c.id = cu.c_id)
4718
                    $extraInnerJoin
4719
                    WHERE
4720
                        cu.user_id = $user_id
4721
                        $sessionCondition
4722
                    $orderBy ";
4723
        }
4724
4725
        $rs = Database::query($sql);
4726
        $simple_session_array = [];
4727
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4728
            $course_info = api_get_course_info($row['code']);
4729
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4730
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
4731
            $simple_session_array[$row['session_id']] = $row['name'];
4732
        }
4733
4734
        foreach ($simple_session_array as $my_session_id => $session_name) {
4735
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4736
            $my_course_data = [];
4737
            foreach ($course_list as $courseId => $course_data) {
4738
                $my_course_data[$courseId] = $course_data['title'];
4739
            }
4740
4741
            if (empty($session_id)) {
4742
                $my_course_data = utf8_sort($my_course_data);
4743
            }
4744
4745
            $final_course_data = [];
4746
            foreach ($my_course_data as $course_id => $value) {
4747
                if (isset($course_list[$course_id])) {
4748
                    $final_course_data[$course_id] = $course_list[$course_id];
4749
                }
4750
            }
4751
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4752
            $course_in_session[$my_session_id]['name'] = $session_name;
4753
        }
4754
4755
        if ($returnArray) {
4756
            $course_in_session[0] = $courseIdList;
4757
4758
            return $course_in_session;
4759
        }
4760
4761
        $html = '';
4762
        // Course list
4763
        if ($show_courses) {
4764
            if (!empty($courses)) {
4765
                $html .= Display::page_subheader(
4766
                    Display::return_icon(
4767
                        'course.png',
4768
                        get_lang('My courses'),
4769
                        [],
4770
                        ICON_SIZE_SMALL
4771
                    ).' '.get_lang('My courses')
4772
                );
4773
4774
                $columns = [
4775
                    'course_title' => get_lang('Course'),
4776
                    'time_spent' => get_lang('Time spent in the course'),
4777
                    'progress' => get_lang('Progress'),
4778
                    'best_score_in_lp' => get_lang('Best score in learning path'),
4779
                    'best_score_not_in_lp' => get_lang('Best score not in learning path'),
4780
                    'latest_login' => get_lang('Latest login'),
4781
                    'details' => get_lang('Details'),
4782
                ];
4783
                $availableColumns = [];
4784
                if (isset($trackingColumns['my_progress_courses'])) {
4785
                    $availableColumns = $trackingColumns['my_progress_courses'];
4786
                }
4787
                $html .= '<div class="table-responsive">';
4788
                $html .= '<table class="table table-striped table-hover">';
4789
                $html .= '<thead><tr>';
4790
                foreach ($columns as $columnKey => $name) {
4791
                    if (!empty($availableColumns)) {
4792
                        if (isset($availableColumns[$columnKey]) && false == $availableColumns[$columnKey]) {
4793
                            continue;
4794
                        }
4795
                    }
4796
                    $html .= Display::tag('th', $name);
4797
                }
4798
                $html .= '</tr></thead><tbody>';
4799
4800
                foreach ($courses as $course_code => $course_title) {
4801
                    $courseInfo = api_get_course_info($course_code);
4802
                    $courseId = $courseInfo['real_id'];
4803
4804
                    $total_time_login = self::get_time_spent_on_the_course(
4805
                        $user_id,
4806
                        $courseId
4807
                    );
4808
                    $time = api_time_to_hms($total_time_login);
4809
                    $progress = self::get_avg_student_progress(
4810
                        $user_id,
4811
                        $course_code
4812
                    );
4813
                    $bestScore = self::get_avg_student_score(
4814
                        $user_id,
4815
                        $course_code,
4816
                        [],
4817
                        null,
4818
                        false,
4819
                        false,
4820
                        true
4821
                    );
4822
4823
                    $exerciseList = ExerciseLib::get_all_exercises(
4824
                        $courseInfo,
4825
                        0,
4826
                        false,
4827
                        null,
4828
                        false,
4829
                        1
4830
                    );
4831
4832
                    $bestScoreAverageNotInLP = 0;
4833
                    if (!empty($exerciseList)) {
4834
                        foreach ($exerciseList as $exerciseData) {
4835
                            $results = Event::get_best_exercise_results_by_user(
4836
                                $exerciseData['id'],
4837
                                $courseInfo['real_id'],
4838
                                0,
4839
                                $user_id
4840
                            );
4841
                            $best = 0;
4842
                            if (!empty($results)) {
4843
                                foreach ($results as $result) {
4844
                                    if (!empty($result['max_score'])) {
4845
                                        $score = $result['score'] / $result['max_score'];
4846
                                        if ($score > $best) {
4847
                                            $best = $score;
4848
                                        }
4849
                                    }
4850
                                }
4851
                            }
4852
                            $bestScoreAverageNotInLP += $best;
4853
                        }
4854
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
4855
                    }
4856
4857
                    $last_connection = self::get_last_connection_date_on_the_course(
4858
                        $user_id,
4859
                        $courseInfo
4860
                    );
4861
4862
                    if (is_null($progress) || empty($progress)) {
4863
                        $progress = '0%';
4864
                    } else {
4865
                        $progress = $progress.'%';
4866
                    }
4867
4868
                    if (isset($_GET['course']) &&
4869
                        $course_code == $_GET['course'] &&
4870
                        empty($_GET['session_id'])
4871
                    ) {
4872
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D">';
4873
                    } else {
4874
                        $html .= '<tr class="row_even">';
4875
                    }
4876
                    $url = api_get_course_url($course_code, $session_id);
4877
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
4878
                    $bestScoreResult = '';
4879
                    if (empty($bestScore)) {
4880
                        $bestScoreResult = '-';
4881
                    } else {
4882
                        $bestScoreResult = $bestScore.'%';
4883
                    }
4884
                    $bestScoreNotInLP = '';
4885
                    if (empty($bestScoreAverageNotInLP)) {
4886
                        $bestScoreNotInLP = '-';
4887
                    } else {
4888
                        $bestScoreNotInLP = $bestScoreAverageNotInLP.'%';
4889
                    }
4890
4891
                    $detailsLink = '';
4892
                    if (isset($_GET['course']) &&
4893
                        $course_code == $_GET['course'] &&
4894
                        empty($_GET['session_id'])
4895
                    ) {
4896
                        $detailsLink .= '<a href="#course_session_header">';
4897
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4898
                        $detailsLink .= '</a>';
4899
                    } else {
4900
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_header">';
4901
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4902
                        $detailsLink .= '</a>';
4903
                    }
4904
4905
                    $result = [
4906
                        'course_title' => $course_url,
4907
                        'time_spent' => $time,
4908
                        'progress' => $progress,
4909
                        'best_score_in_lp' => $bestScoreResult,
4910
                        'best_score_not_in_lp' => $bestScoreNotInLP,
4911
                        'latest_login' => $last_connection,
4912
                        'details' => $detailsLink,
4913
                    ];
4914
4915
                    foreach ($result as $columnKey => $data) {
4916
                        if (!empty($availableColumns)) {
4917
                            if (isset($availableColumns[$columnKey]) && false == $availableColumns[$columnKey]) {
4918
                                continue;
4919
                            }
4920
                        }
4921
                        $html .= '<td>'.$data.'</td>';
4922
                    }
4923
4924
                    $html .= '</tr>';
4925
                }
4926
                $html .= '</tbody></table>';
4927
                $html .= '</div>';
4928
            }
4929
        }
4930
4931
        // Session list
4932
        if (!empty($course_in_session)) {
4933
            $main_session_graph = '';
4934
            // Load graphics only when calling to an specific session
4935
            $all_exercise_graph_name_list = [];
4936
            $my_results = [];
4937
            $all_exercise_graph_list = [];
4938
            $all_exercise_start_time = [];
4939
            foreach ($course_in_session as $my_session_id => $session_data) {
4940
                $course_list = $session_data['course_list'];
4941
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
4942
                $exercise_graph_name_list = [];
4943
                $exercise_graph_list = [];
4944
4945
                foreach ($course_list as $course_data) {
4946
                    $exercise_list = ExerciseLib::get_all_exercises(
4947
                        $course_data,
4948
                        $my_session_id,
4949
                        false,
4950
                        null,
4951
                        false,
4952
                        1
4953
                    );
4954
4955
                    foreach ($exercise_list as $exercise_data) {
4956
                        $exercise_obj = new Exercise($course_data['real_id']);
4957
                        $exercise_obj->read($exercise_data['id']);
4958
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
4959
                        //$visible_return = $exercise_obj->is_visible();
4960
                        if (0 == $exercise_data['results_disabled'] || 2 == $exercise_data['results_disabled']) {
4961
                            $best_average = (int)
4962
                                ExerciseLib::get_best_average_score_by_exercise(
4963
                                    $exercise_data['id'],
4964
                                    $course_data['real_id'],
4965
                                    $my_session_id,
4966
                                    $user_count
4967
                                )
4968
                            ;
4969
4970
                            $exercise_graph_list[] = $best_average;
4971
                            $all_exercise_graph_list[] = $best_average;
4972
4973
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
4974
                                api_get_user_id(),
4975
                                $exercise_data['id'],
4976
                                $course_data['real_id'],
4977
                                $my_session_id
4978
                            );
4979
4980
                            $score = 0;
4981
                            if (!empty($user_result_data['max_score']) && 0 != intval($user_result_data['max_score'])) {
4982
                                $score = intval($user_result_data['score'] / $user_result_data['max_score'] * 100);
4983
                            }
4984
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
4985
                            $all_exercise_start_time[] = $time;
4986
                            $my_results[] = $score;
4987
                            if (count($exercise_list) <= 10) {
4988
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
4989
                                $exercise_graph_name_list[] = $title;
4990
                                $all_exercise_graph_name_list[] = $title;
4991
                            } else {
4992
                                // if there are more than 10 results, space becomes difficult to find,
4993
                                // so only show the title of the exercise, not the tool
4994
                                $title = cut($exercise_data['title'], 30);
4995
                                $exercise_graph_name_list[] = $title;
4996
                                $all_exercise_graph_name_list[] = $title;
4997
                            }
4998
                        }
4999
                    }
5000
                }
5001
            }
5002
5003
            // Complete graph
5004
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
5005
                asort($all_exercise_start_time);
5006
5007
                //Fix exams order
5008
                $final_all_exercise_graph_name_list = [];
5009
                $my_results_final = [];
5010
                $final_all_exercise_graph_list = [];
5011
5012
                foreach ($all_exercise_start_time as $key => $time) {
5013
                    $label_time = '';
5014
                    if (!empty($time)) {
5015
                        $label_time = date('d-m-y', $time);
5016
                    }
5017
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
5018
                    $my_results_final[] = $my_results[$key];
5019
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
5020
                }
5021
                $main_session_graph = self::generate_session_exercise_graph(
5022
                    $final_all_exercise_graph_name_list,
5023
                    $my_results_final,
5024
                    $final_all_exercise_graph_list
5025
                );
5026
            }
5027
5028
            $sessionIcon = Display::return_icon(
5029
                'session.png',
5030
                get_lang('Course sessions'),
5031
                [],
5032
                ICON_SIZE_SMALL
5033
            );
5034
5035
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5036
            $html .= $anchor.Display::page_subheader(
5037
                $sessionIcon.' '.get_lang('Course sessions')
5038
            );
5039
5040
            $html .= '<div class="table-responsive">';
5041
            $html .= '<table class="table table-striped table-hover">';
5042
            $html .= '<thead>';
5043
            $html .= '<tr>
5044
                  '.Display::tag('th', get_lang('Session'), ['width' => '300px']).'
5045
                  '.Display::tag('th', get_lang('Tests available'), ['width' => '300px']).'
5046
                  '.Display::tag('th', get_lang('New exercises')).'
5047
                  '.Display::tag('th', get_lang('Average exercise result')).'
5048
                  '.Display::tag('th', get_lang('Details')).'
5049
                  </tr>';
5050
            $html .= '</thead>';
5051
            $html .= '<tbody>';
5052
5053
            foreach ($course_in_session as $my_session_id => $session_data) {
5054
                $course_list = $session_data['course_list'];
5055
                $session_name = $session_data['name'];
5056
                if (false == $showAllSessions) {
5057
                    if (isset($session_id) && !empty($session_id)) {
5058
                        if ($session_id != $my_session_id) {
5059
                            continue;
5060
                        }
5061
                    }
5062
                }
5063
5064
                $all_exercises = 0;
5065
                $all_unanswered_exercises_by_user = 0;
5066
                $all_average = 0;
5067
                $stats_array = [];
5068
5069
                foreach ($course_list as $course_data) {
5070
                    // All exercises in the course @todo change for a real count
5071
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5072
                    $count_exercises = 0;
5073
                    if (is_array($exercises) && !empty($exercises)) {
5074
                        $count_exercises = count($exercises);
5075
                    }
5076
5077
                    // Count of user results
5078
                    $done_exercises = null;
5079
                    $courseInfo = api_get_course_info($course_data['code']);
5080
5081
                    $answered_exercises = 0;
5082
                    if (!empty($exercises)) {
5083
                        foreach ($exercises as $exercise_item) {
5084
                            $attempts = Event::count_exercise_attempts_by_user(
5085
                                api_get_user_id(),
5086
                                $exercise_item['id'],
5087
                                $courseInfo['real_id'],
5088
                                $my_session_id
5089
                            );
5090
                            if ($attempts > 1) {
5091
                                $answered_exercises++;
5092
                            }
5093
                        }
5094
                    }
5095
5096
                    // Average
5097
                    $average = ExerciseLib::get_average_score_by_course(
5098
                        $courseInfo['real_id'],
5099
                        $my_session_id
5100
                    );
5101
                    $all_exercises += $count_exercises;
5102
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5103
                    $all_average += $average;
5104
                }
5105
5106
                if (!empty($course_list)) {
5107
                    $all_average = $all_average / count($course_list);
5108
                }
5109
5110
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5111
                    $html .= '<tr style="background-color:#FBF09D">';
5112
                } else {
5113
                    $html .= '<tr>';
5114
                }
5115
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5116
5117
                $html .= Display::tag('td', Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]));
5118
                $html .= Display::tag('td', $all_exercises);
5119
                $html .= Display::tag('td', $all_unanswered_exercises_by_user);
5120
                $html .= Display::tag('td', ExerciseLib::convert_to_percentage($all_average));
5121
5122
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5123
                    $icon = Display::url(
5124
                        Display::return_icon(
5125
                            '2rightarrow_na.png',
5126
                            get_lang('Details')
5127
                        ),
5128
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5129
                    );
5130
                } else {
5131
                    $icon = Display::url(
5132
                        Display::return_icon(
5133
                            '2rightarrow.png',
5134
                            get_lang('Details')
5135
                        ),
5136
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5137
                    );
5138
                }
5139
                $html .= Display::tag('td', $icon);
5140
                $html .= '</tr>';
5141
            }
5142
            $html .= '</tbody>';
5143
            $html .= '</table></div><br />';
5144
            $html .= Display::div(
5145
                $main_session_graph,
5146
                [
5147
                    'id' => 'session_graph',
5148
                    'class' => 'chart-session',
5149
                    'style' => 'position:relative; text-align: center;',
5150
                ]
5151
            );
5152
5153
            // Checking selected session.
5154
            if (isset($_GET['session_id'])) {
5155
                $session_id_from_get = (int) $_GET['session_id'];
5156
                $session_data = $course_in_session[$session_id_from_get];
5157
                $course_list = $session_data['course_list'];
5158
5159
                $html .= '<a name= "course_session_list"></a>';
5160
                $html .= Display::tag('h3', $session_data['name'].' - '.get_lang('Course list'));
5161
5162
                $html .= '<div class="table-responsive">';
5163
                $html .= '<table class="table table-hover table-striped">';
5164
5165
                $columnHeaders = [
5166
                    'course_title' => [
5167
                        get_lang('Course'),
5168
                        ['width' => '300px'],
5169
                    ],
5170
                    'published_exercises' => [
5171
                        get_lang('Tests available'),
5172
                    ],
5173
                    'new_exercises' => [
5174
                        get_lang('New exercises'),
5175
                    ],
5176
                    'my_average' => [
5177
                        get_lang('My average'),
5178
                    ],
5179
                    'average_exercise_result' => [
5180
                        get_lang('Average exercise result'),
5181
                    ],
5182
                    'time_spent' => [
5183
                        get_lang('Time spent in the course'),
5184
                    ],
5185
                    'lp_progress' => [
5186
                        get_lang('Learning path progress'),
5187
                    ],
5188
                    'score' => [
5189
                        get_lang('Score').
5190
                        Display::return_icon(
5191
                            'info3.gif',
5192
                            get_lang('Average of tests in Learning Paths'),
5193
                            ['align' => 'absmiddle', 'hspace' => '3px']
5194
                        ),
5195
                    ],
5196
                    'best_score' => [
5197
                        get_lang('Best score'),
5198
                    ],
5199
                    'last_connection' => [
5200
                        get_lang('Latest login'),
5201
                    ],
5202
                    'details' => [
5203
                        get_lang('Details'),
5204
                    ],
5205
                ];
5206
5207
                $html .= '<thead><tr>';
5208
                foreach ($columnHeaders as $key => $columnSetting) {
5209
                    if (isset($trackingColumns['course_session']) &&
5210
                        in_array($key, $trackingColumns['course_session']) &&
5211
                        $trackingColumns['course_session'][$key]
5212
                    ) {
5213
                        $settings = isset($columnSetting[1]) ? $columnSetting[1] : [];
5214
                        $html .= Display::tag(
5215
                             'th',
5216
                             $columnSetting[0],
5217
                             $settings
5218
                         );
5219
                    }
5220
                }
5221
5222
                $html .= '</tr>
5223
                    </thead>
5224
                    <tbody>';
5225
5226
                foreach ($course_list as $course_data) {
5227
                    $course_code = $course_data['code'];
5228
                    $course_title = $course_data['title'];
5229
                    $courseId = $course_data['real_id'];
5230
5231
                    // All exercises in the course @todo change for a real count
5232
                    $exercises = ExerciseLib::get_all_exercises(
5233
                        $course_data,
5234
                        $session_id_from_get
5235
                    );
5236
                    $count_exercises = 0;
5237
                    if (!empty($exercises)) {
5238
                        $count_exercises = count($exercises);
5239
                    }
5240
                    $answered_exercises = 0;
5241
                    foreach ($exercises as $exercise_item) {
5242
                        $attempts = Event::count_exercise_attempts_by_user(
5243
                            api_get_user_id(),
5244
                            $exercise_item['id'],
5245
                            $courseId,
5246
                            $session_id_from_get
5247
                        );
5248
                        if ($attempts > 1) {
5249
                            $answered_exercises++;
5250
                        }
5251
                    }
5252
5253
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5254
5255
                    // Average
5256
                    $average = ExerciseLib::get_average_score_by_course(
5257
                        $courseId,
5258
                        $session_id_from_get
5259
                    );
5260
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5261
                        api_get_user_id(),
5262
                        $courseId,
5263
                        $session_id_from_get
5264
                    );
5265
5266
                    $bestScore = self::get_avg_student_score(
5267
                        $user_id,
5268
                        $course_code,
5269
                        [],
5270
                        $session_id_from_get,
5271
                        false,
5272
                        false,
5273
                        true
5274
                    );
5275
5276
                    $stats_array[$course_code] = [
5277
                        'exercises' => $count_exercises,
5278
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5279
                        'done_exercises' => $done_exercises,
5280
                        'average' => $average,
5281
                        'my_average' => $my_average,
5282
                        'best_score' => $bestScore,
5283
                    ];
5284
5285
                    $last_connection = self::get_last_connection_date_on_the_course(
5286
                        $user_id,
5287
                        $course_data,
5288
                        $session_id_from_get
5289
                    );
5290
5291
                    $progress = self::get_avg_student_progress(
5292
                        $user_id,
5293
                        $course_code,
5294
                        [],
5295
                        $session_id_from_get
5296
                    );
5297
5298
                    $total_time_login = self::get_time_spent_on_the_course(
5299
                        $user_id,
5300
                        $courseId,
5301
                        $session_id_from_get
5302
                    );
5303
                    $time = api_time_to_hms($total_time_login);
5304
5305
                    $percentage_score = self::get_avg_student_score(
5306
                        $user_id,
5307
                        $course_code,
5308
                        [],
5309
                        $session_id_from_get
5310
                    );
5311
                    $courseCodeFromGet = isset($_GET['course']) ? $_GET['course'] : null;
5312
5313
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
5314
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D" >';
5315
                    } else {
5316
                        $html .= '<tr class="row_even">';
5317
                    }
5318
5319
                    $url = api_get_course_url($course_code, $session_id_from_get);
5320
                    $course_url = Display::url(
5321
                        $course_title,
5322
                        $url,
5323
                        ['target' => SESSION_LINK_TARGET]
5324
                    );
5325
5326
                    if (is_numeric($progress)) {
5327
                        $progress = $progress.'%';
5328
                    } else {
5329
                        $progress = '0%';
5330
                    }
5331
                    if (is_numeric($percentage_score)) {
5332
                        $percentage_score = $percentage_score.'%';
5333
                    } else {
5334
                        $percentage_score = '0%';
5335
                    }
5336
5337
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5338
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5339
                    } else {
5340
                        $bestScore = '-';
5341
                    }
5342
5343
                    if (empty($last_connection) || is_bool($last_connection)) {
5344
                        $last_connection = '';
5345
                    }
5346
5347
                    if ($course_code == $courseCodeFromGet &&
5348
                        $_GET['session_id'] == $session_id_from_get
5349
                    ) {
5350
                        $details = Display::url(
5351
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
5352
                        '#course_session_data'
5353
                        );
5354
                    } else {
5355
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5356
                        $details = Display::url(
5357
                            Display::return_icon(
5358
                                '2rightarrow.png',
5359
                                get_lang('Details')
5360
                            ),
5361
                            $url
5362
                        );
5363
                    }
5364
                    $details .= '</a>';
5365
5366
                    $data = [
5367
                        'course_title' => $course_url,
5368
                        'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5369
                        'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5370
                        'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5371
                        'average_exercise_result' => 0 == $stats_array[$course_code]['average'] ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5372
                        'time_spent' => $time,
5373
                        'lp_progress' => $progress,
5374
                        'score' => $percentage_score,
5375
                        'best_score' => $bestScore,
5376
                        'last_connection' => $last_connection,
5377
                        'details' => $details,
5378
                    ];
5379
5380
                    foreach ($data as $key => $value) {
5381
                        if (in_array($key, $trackingColumns['course_session'])
5382
                            && $trackingColumns['course_session'][$key]
5383
                        ) {
5384
                            $html .= Display::tag('td', $value);
5385
                        }
5386
                    }
5387
                    $html .= '</tr>';
5388
                }
5389
                $html .= '</tbody></table></div>';
5390
            }
5391
        }
5392
5393
        $pluginCalendar = 'true' === api_get_plugin_setting('learning_calendar', 'enabled');
5394
        if ($pluginCalendar) {
5395
            $course_in_session[0] = $courseIdList;
5396
            $plugin = LearningCalendarPlugin::create();
5397
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
5398
        }
5399
5400
        return $html;
5401
    }
5402
5403
    /**
5404
     * Shows the user detail progress (when clicking in the details link).
5405
     *
5406
     * @param int    $user_id
5407
     * @param string $course_code
5408
     * @param int    $session_id
5409
     *
5410
     * @return string html code
5411
     */
5412
    public static function show_course_detail($user_id, $course_code, $session_id)
5413
    {
5414
        $html = '';
5415
        if (isset($course_code)) {
5416
            $user_id = (int) $user_id;
5417
            $session_id = (int) $session_id;
5418
            $course = Database::escape_string($course_code);
5419
            $course_info = api_get_course_info($course);
5420
            if (empty($course_info)) {
5421
                return '';
5422
            }
5423
5424
            $html .= '<a name="course_session_data"></a>';
5425
            $html .= Display::page_subheader($course_info['title']);
5426
            $html .= '<div class="table-responsive">';
5427
            $html .= '<table class="table table-striped table-hover">';
5428
5429
            // Course details
5430
            $html .= '
5431
                <thead>
5432
                <tr>
5433
                <th>'.get_lang('Tests').'</th>
5434
                <th>'.get_lang('Attempts').'</th>
5435
                <th>'.get_lang('Best attempt').'</th>
5436
                <th>'.get_lang('Ranking').'</th>
5437
                <th>'.get_lang('Best result in course').'</th>
5438
                <th>'.get_lang('Statistics').' '.Display::return_icon('info3.gif', get_lang('In case of multiple attempts, only shows the best result of each learner'), ['align' => 'absmiddle', 'hspace' => '3px']).'</th>
5439
                </tr>
5440
                </thead>
5441
                <tbody>';
5442
5443
            if (empty($session_id)) {
5444
                $user_list = CourseManager::get_user_list_from_course_code(
5445
                    $course,
5446
                    $session_id,
5447
                    null,
5448
                    null,
5449
                    STUDENT
5450
                );
5451
            } else {
5452
                $user_list = CourseManager::get_user_list_from_course_code(
5453
                    $course,
5454
                    $session_id,
5455
                    null,
5456
                    null,
5457
                    0
5458
                );
5459
            }
5460
5461
            // Show exercise results of invisible exercises? see BT#4091
5462
            $exercise_list = ExerciseLib::get_all_exercises(
5463
                $course_info,
5464
                $session_id,
5465
                false,
5466
                null,
5467
                false,
5468
                2
5469
            );
5470
5471
            $to_graph_exercise_result = [];
5472
            if (!empty($exercise_list)) {
5473
                $score = $weighting = $exe_id = 0;
5474
                foreach ($exercise_list as $exercices) {
5475
                    $exercise_obj = new Exercise($course_info['real_id']);
5476
                    $exercise_obj->read($exercices['id']);
5477
                    $visible_return = $exercise_obj->is_visible();
5478
                    $score = $weighting = $attempts = 0;
5479
5480
                    // Getting count of attempts by user
5481
                    $attempts = Event::count_exercise_attempts_by_user(
5482
                        api_get_user_id(),
5483
                        $exercices['id'],
5484
                        $course_info['real_id'],
5485
                        $session_id
5486
                    );
5487
5488
                    $html .= '<tr class="row_even">';
5489
                    $url = api_get_path(WEB_CODE_PATH)."exercise/overview.php?cidReq={$course_info['code']}&id_session=$session_id&exerciseId={$exercices['id']}";
5490
5491
                    if (true == $visible_return['value']) {
5492
                        $exercices['title'] = Display::url(
5493
                            $exercices['title'],
5494
                            $url,
5495
                            ['target' => SESSION_LINK_TARGET]
5496
                        );
5497
                    } elseif (-1 == $exercices['active']) {
5498
                        $exercices['title'] = sprintf(get_lang('%s (deleted)'), $exercices['title']);
5499
                    }
5500
5501
                    $html .= Display::tag('td', $exercices['title']);
5502
5503
                    // Exercise configuration show results or show only score
5504
                    if (0 == $exercices['results_disabled'] || 2 == $exercices['results_disabled']) {
5505
                        //For graphics
5506
                        $best_exercise_stats = Event::get_best_exercise_results_by_user(
5507
                            $exercices['id'],
5508
                            $course_info['real_id'],
5509
                            $session_id
5510
                        );
5511
5512
                        $to_graph_exercise_result[$exercices['id']] = [
5513
                            'title' => $exercices['title'],
5514
                            'data' => $best_exercise_stats,
5515
                        ];
5516
5517
                        $latest_attempt_url = '';
5518
                        $best_score = $position = $percentage_score_result = '-';
5519
                        $graph = $normal_graph = null;
5520
5521
                        // Getting best results
5522
                        $best_score_data = ExerciseLib::get_best_attempt_in_course(
5523
                            $exercices['id'],
5524
                            $course_info['real_id'],
5525
                            $session_id
5526
                        );
5527
5528
                        $best_score = '';
5529
                        if (!empty($best_score_data)) {
5530
                            $best_score = ExerciseLib::show_score(
5531
                                $best_score_data['score'],
5532
                                $best_score_data['max_score']
5533
                            );
5534
                        }
5535
5536
                        if ($attempts > 0) {
5537
                            $exercise_stat = ExerciseLib::get_best_attempt_by_user(
5538
                                api_get_user_id(),
5539
                                $exercices['id'],
5540
                                $course_info['real_id'],
5541
                                $session_id
5542
                            );
5543
                            if (!empty($exercise_stat)) {
5544
                                // Always getting the BEST attempt
5545
                                $score = $exercise_stat['score'];
5546
                                $weighting = $exercise_stat['max_score'];
5547
                                $exe_id = $exercise_stat['exe_id'];
5548
5549
                                $latest_attempt_url .= api_get_path(WEB_CODE_PATH).'exercise/result.php?id='.$exe_id.'&cidReq='.$course_info['code'].'&show_headers=1&id_session='.$session_id;
5550
                                $percentage_score_result = Display::url(
5551
                                    ExerciseLib::show_score($score, $weighting),
5552
                                    $latest_attempt_url
5553
                                );
5554
                                $my_score = 0;
5555
                                if (!empty($weighting) && 0 != intval($weighting)) {
5556
                                    $my_score = $score / $weighting;
5557
                                }
5558
                                //@todo this function slows the page
5559
                                if (is_int($user_list)) {
5560
                                    $user_list = [$user_list];
5561
                                }
5562
                                $position = ExerciseLib::get_exercise_result_ranking(
5563
                                    $my_score,
5564
                                    $exe_id,
5565
                                    $exercices['id'],
5566
                                    $course_info['code'],
5567
                                    $session_id,
5568
                                    $user_list
5569
                                );
5570
5571
                                $graph = self::generate_exercise_result_thumbnail_graph(
5572
                                    $to_graph_exercise_result[$exercices['id']]
5573
                                );
5574
                                $normal_graph = self::generate_exercise_result_graph(
5575
                                    $to_graph_exercise_result[$exercices['id']]
5576
                                );
5577
                            }
5578
                        }
5579
                        $html .= Display::div(
5580
                            $normal_graph,
5581
                            [
5582
                                'id' => 'main_graph_'.$exercices['id'],
5583
                                'class' => 'dialog',
5584
                                'style' => 'display:none',
5585
                            ]
5586
                        );
5587
5588
                        if (empty($graph)) {
5589
                            $graph = '-';
5590
                        } else {
5591
                            $graph = Display::url(
5592
                                '<img src="'.$graph.'" >',
5593
                                $normal_graph,
5594
                                [
5595
                                    'id' => $exercices['id'],
5596
                                    'class' => 'expand-image',
5597
                                ]
5598
                            );
5599
                        }
5600
5601
                        $html .= Display::tag('td', $attempts);
5602
                        $html .= Display::tag('td', $percentage_score_result);
5603
                        $html .= Display::tag('td', $position);
5604
                        $html .= Display::tag('td', $best_score);
5605
                        $html .= Display::tag('td', $graph);
5606
                    } else {
5607
                        // Exercise configuration NO results
5608
                        $html .= Display::tag('td', $attempts);
5609
                        $html .= Display::tag('td', '-');
5610
                        $html .= Display::tag('td', '-');
5611
                        $html .= Display::tag('td', '-');
5612
                        $html .= Display::tag('td', '-');
5613
                    }
5614
                    $html .= '</tr>';
5615
                }
5616
            } else {
5617
                $html .= '<tr><td colspan="5">'.get_lang('There is no test for the moment').'</td></tr>';
5618
            }
5619
            $html .= '</tbody></table></div>';
5620
5621
            $columnHeaders = [
5622
                'lp' => get_lang('Learning paths'),
5623
                'time' => get_lang('Time spent'),
5624
                'progress' => get_lang('Progress'),
5625
                'score' => get_lang('Score'),
5626
                'best_score' => get_lang('Best score'),
5627
                'last_connection' => get_lang('Latest login'),
5628
            ];
5629
5630
            $headers = '';
5631
            $trackingColumns = api_get_configuration_value('tracking_columns');
5632
            if (isset($trackingColumns['my_progress_lp'])) {
5633
                foreach ($columnHeaders as $key => $value) {
5634
                    if (!isset($trackingColumns['my_progress_lp'][$key]) ||
5635
                        false == $trackingColumns['my_progress_lp'][$key]
5636
                    ) {
5637
                        unset($columnHeaders[$key]);
5638
                    }
5639
                }
5640
            }
5641
5642
            $columnHeadersKeys = array_keys($columnHeaders);
5643
            foreach ($columnHeaders as $key => $columnName) {
5644
                $headers .= Display::tag(
5645
                    'th',
5646
                    $columnName
5647
                );
5648
            }
5649
5650
            // LP table results
5651
            $html .= '<div class="table-responsive">';
5652
            $html .= '<table class="table table-striped table-hover">';
5653
            $html .= '<thead><tr>';
5654
            $html .= $headers;
5655
            $html .= '</tr></thead><tbody>';
5656
5657
            $list = new LearnpathList(
5658
                api_get_user_id(),
5659
                $course_info,
5660
                $session_id,
5661
                'lp.publicatedOn ASC',
5662
                true,
5663
                null,
5664
                true
5665
            );
5666
5667
            $lp_list = $list->get_flat_list();
5668
5669
            if (!empty($lp_list)) {
5670
                foreach ($lp_list as $lp_id => $learnpath) {
5671
                    if (!$learnpath['lp_visibility']) {
5672
                        continue;
5673
                    }
5674
5675
                    $progress = self::get_avg_student_progress(
5676
                        $user_id,
5677
                        $course,
5678
                        [$lp_id],
5679
                        $session_id
5680
                    );
5681
                    $last_connection_in_lp = self::get_last_connection_time_in_lp(
5682
                        $user_id,
5683
                        $course,
5684
                        $lp_id,
5685
                        $session_id
5686
                    );
5687
5688
                    $time_spent_in_lp = self::get_time_spent_in_lp(
5689
                        $user_id,
5690
                        $course,
5691
                        [$lp_id],
5692
                        $session_id
5693
                    );
5694
                    $percentage_score = self::get_avg_student_score(
5695
                        $user_id,
5696
                        $course,
5697
                        [$lp_id],
5698
                        $session_id
5699
                    );
5700
5701
                    $bestScore = self::get_avg_student_score(
5702
                        $user_id,
5703
                        $course,
5704
                        [$lp_id],
5705
                        $session_id,
5706
                        false,
5707
                        false,
5708
                        true
5709
                    );
5710
5711
                    if (is_numeric($progress)) {
5712
                        $progress = $progress.'%';
5713
                    }
5714
                    if (is_numeric($percentage_score)) {
5715
                        $percentage_score = $percentage_score.'%';
5716
                    } else {
5717
                        $percentage_score = '0%';
5718
                    }
5719
5720
                    if (is_numeric($bestScore)) {
5721
                        $bestScore = $bestScore.'%';
5722
                    } else {
5723
                        $bestScore = '-';
5724
                    }
5725
5726
                    $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
5727
                    $last_connection = '-';
5728
                    if (!empty($last_connection_in_lp)) {
5729
                        $last_connection = api_convert_and_format_date(
5730
                            $last_connection_in_lp,
5731
                            DATE_TIME_FORMAT_LONG
5732
                        );
5733
                    }
5734
5735
                    $url = api_get_path(WEB_CODE_PATH)."lp/lp_controller.php?cidReq={$course_code}&id_session=$session_id&lp_id=$lp_id&action=view";
5736
                    $html .= '<tr class="row_even">';
5737
5738
                    if (in_array('lp', $columnHeadersKeys)) {
5739
                        if (0 == $learnpath['lp_visibility']) {
5740
                            $html .= Display::tag('td', $learnpath['lp_name']);
5741
                        } else {
5742
                            $html .= Display::tag(
5743
                                'td',
5744
                                Display::url(
5745
                                    $learnpath['lp_name'],
5746
                                    $url,
5747
                                    ['target' => SESSION_LINK_TARGET]
5748
                                )
5749
                            );
5750
                        }
5751
                    }
5752
5753
                    if (in_array('time', $columnHeadersKeys)) {
5754
                        $html .= Display::tag(
5755
                            'td',
5756
                            $time_spent_in_lp
5757
                        );
5758
                    }
5759
5760
                    if (in_array('progress', $columnHeadersKeys)) {
5761
                        $html .= Display::tag(
5762
                            'td',
5763
                            $progress
5764
                        );
5765
                    }
5766
5767
                    if (in_array('score', $columnHeadersKeys)) {
5768
                        $html .= Display::tag('td', $percentage_score);
5769
                    }
5770
                    if (in_array('best_score', $columnHeadersKeys)) {
5771
                        $html .= Display::tag('td', $bestScore);
5772
                    }
5773
5774
                    if (in_array('last_connection', $columnHeadersKeys)) {
5775
                        $html .= Display::tag('td', $last_connection, ['width' => '180px']);
5776
                    }
5777
                    $html .= '</tr>';
5778
                }
5779
            } else {
5780
                $html .= '<tr>
5781
                        <td colspan="4" align="center">
5782
                            '.get_lang('No learning path').'
5783
                        </td>
5784
                      </tr>';
5785
            }
5786
            $html .= '</tbody></table></div>';
5787
5788
            $html .= self::displayUserSkills($user_id, $course_info['id'], $session_id);
5789
        }
5790
5791
        return $html;
5792
    }
5793
5794
    /**
5795
     * Generates an histogram.
5796
     *
5797
     * @param array $names      list of exercise names
5798
     * @param array $my_results my results 0 to 100
5799
     * @param array $average    average scores 0-100
5800
     *
5801
     * @return string
5802
     */
5803
    public static function generate_session_exercise_graph($names, $my_results, $average)
5804
    {
5805
        $html = api_get_js('chartjs/Chart.js');
5806
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
5807
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
5808
        $jsStr = " var data = {
5809
                       labels:".json_encode($names).",
5810
                       datasets: [
5811
                       {
5812
                         label: '".get_lang('My results')."',
5813
                         backgroundColor: 'rgb(255, 99, 132)',
5814
                         stack: 'Stack1',
5815
                         data: ".json_encode($my_results).",
5816
                        },
5817
                        {
5818
                         label: '".get_lang('Average score')."',
5819
                         backgroundColor: 'rgb(75, 192, 192)',
5820
                         stack: 'Stack2',
5821
                         data: ".json_encode($average).",
5822
                        },
5823
                        ],
5824
                    };
5825
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
5826
                    var myBarChart = new Chart(ctx, {
5827
                    type: 'bar',
5828
                    data: data,
5829
                    options: {
5830
                            title: {
5831
                                    display: true,
5832
                                    text: '".get_lang('TestsInTimeProgressChart')."'
5833
                            },
5834
                            tooltips: {
5835
                                    mode: 'index',
5836
                                    intersect: false
5837
                            },
5838
                            responsive: true,
5839
                            scales: {
5840
                                yAxes: [{
5841
                                    ticks: {
5842
                                        // Include a dollar sign in the ticks
5843
                                        callback: function(value, index, values) {
5844
                                            return value + '%';
5845
                                        }
5846
                                    }
5847
                                }]
5848
                            }
5849
                    }
5850
                });";
5851
        $html .= Display::tag('script', $jsStr);
5852
5853
        return $html;
5854
    }
5855
5856
    /**
5857
     * Returns a thumbnail of the function generate_exercise_result_graph.
5858
     *
5859
     * @param array $attempts
5860
     */
5861
    public static function generate_exercise_result_thumbnail_graph($attempts)
5862
    {
5863
        //$exercise_title = $attempts['title'];
5864
        $attempts = $attempts['data'];
5865
        $my_exercise_result_array = $exercise_result = [];
5866
        if (empty($attempts)) {
5867
            return null;
5868
        }
5869
5870
        foreach ($attempts as $attempt) {
5871
            if (api_get_user_id() == $attempt['exe_user_id']) {
5872
                if (0 != $attempt['max_score']) {
5873
                    $my_exercise_result_array[] = $attempt['score'] / $attempt['max_score'];
5874
                }
5875
            } else {
5876
                if (0 != $attempt['max_score']) {
5877
                    $exercise_result[] = $attempt['score'] / $attempt['max_score'];
5878
                }
5879
            }
5880
        }
5881
5882
        // Getting best result
5883
        rsort($my_exercise_result_array);
5884
        $my_exercise_result = 0;
5885
        if (isset($my_exercise_result_array[0])) {
5886
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5887
        }
5888
5889
        $max = 100;
5890
        $pieces = 5;
5891
        $part = round($max / $pieces);
5892
        $x_axis = [];
5893
        $final_array = [];
5894
        $my_final_array = [];
5895
5896
        for ($i = 1; $i <= $pieces; $i++) {
5897
            $sum = 1;
5898
            if (1 == $i) {
5899
                $sum = 0;
5900
            }
5901
            $min = ($i - 1) * $part + $sum;
5902
            $max = ($i) * $part;
5903
            $x_axis[] = $min." - ".$max;
5904
            $count = 0;
5905
            foreach ($exercise_result as $result) {
5906
                $percentage = $result * 100;
5907
                if ($percentage >= $min && $percentage <= $max) {
5908
                    //echo ' is > ';
5909
                    $count++;
5910
                }
5911
            }
5912
            //echo '<br />';
5913
            $final_array[] = $count;
5914
5915
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5916
                $my_final_array[] = 1;
5917
            } else {
5918
                $my_final_array[] = 0;
5919
            }
5920
        }
5921
5922
        // Fix to remove the data of the user with my data
5923
        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...
5924
            if (!empty($my_final_array[$i])) {
5925
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5926
                $final_array[$i] = 0;
5927
            }
5928
        }
5929
5930
        // Dataset definition
5931
        $dataSet = new pData();
5932
        $dataSet->addPoints($final_array, 'Serie1');
5933
        $dataSet->addPoints($my_final_array, 'Serie2');
5934
        $dataSet->normalize(100, "%");
5935
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5936
5937
        // Cache definition
5938
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5939
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5940
        $chartHash = $myCache->getHash($dataSet);
5941
        if ($myCache->isInCache($chartHash)) {
5942
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5943
            $myCache->saveFromCache($chartHash, $imgPath);
5944
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5945
        } else {
5946
            /* Create the pChart object */
5947
            $widthSize = 80;
5948
            $heightSize = 35;
5949
            $fontSize = 2;
5950
5951
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5952
5953
            /* Turn of Antialiasing */
5954
            $myPicture->Antialias = false;
5955
5956
            /* Add a border to the picture */
5957
            $myPicture->drawRectangle(
5958
                0,
5959
                0,
5960
                $widthSize - 1,
5961
                $heightSize - 1,
5962
                ['R' => 0, 'G' => 0, 'B' => 0]
5963
            );
5964
5965
            /* Set the default font */
5966
            $myPicture->setFontProperties(
5967
                [
5968
                    'FontName' => api_get_path(
5969
                            SYS_FONTS_PATH
5970
                        ).'opensans/OpenSans-Regular.ttf',
5971
                    'FontSize' => $fontSize,
5972
                ]
5973
            );
5974
5975
            /* Do not write the chart title */
5976
            /* Define the chart area */
5977
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
5978
5979
            /* Draw the scale */
5980
            $scaleSettings = [
5981
                'GridR' => 200,
5982
                'GridG' => 200,
5983
                'GridB' => 200,
5984
                'DrawSubTicks' => true,
5985
                'CycleBackground' => true,
5986
                'Mode' => SCALE_MODE_MANUAL,
5987
                'ManualScale' => [
5988
                    '0' => [
5989
                        'Min' => 0,
5990
                        'Max' => 100,
5991
                    ],
5992
                ],
5993
            ];
5994
            $myPicture->drawScale($scaleSettings);
5995
5996
            /* Turn on shadow computing */
5997
            $myPicture->setShadow(
5998
                true,
5999
                [
6000
                    'X' => 1,
6001
                    'Y' => 1,
6002
                    'R' => 0,
6003
                    'G' => 0,
6004
                    'B' => 0,
6005
                    'Alpha' => 10,
6006
                ]
6007
            );
6008
6009
            /* Draw the chart */
6010
            $myPicture->setShadow(
6011
                true,
6012
                [
6013
                    'X' => 1,
6014
                    'Y' => 1,
6015
                    'R' => 0,
6016
                    'G' => 0,
6017
                    'B' => 0,
6018
                    'Alpha' => 10,
6019
                ]
6020
            );
6021
            $settings = [
6022
                'DisplayValues' => true,
6023
                'DisplaySize' => $fontSize,
6024
                'DisplayR' => 0,
6025
                'DisplayG' => 0,
6026
                'DisplayB' => 0,
6027
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6028
                'Gradient' => false,
6029
                'Surrounding' => 5,
6030
                'InnerSurrounding' => 5,
6031
            ];
6032
            $myPicture->drawStackedBarChart($settings);
6033
6034
            /* Save and write in cache */
6035
            $myCache->writeToCache($chartHash, $myPicture);
6036
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6037
            $myCache->saveFromCache($chartHash, $imgPath);
6038
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6039
        }
6040
6041
        return $imgPath;
6042
    }
6043
6044
    /**
6045
     * Generates a big graph with the number of best results.
6046
     *
6047
     * @param	array
6048
     */
6049
    public static function generate_exercise_result_graph($attempts)
6050
    {
6051
        $exercise_title = strip_tags($attempts['title']);
6052
        $attempts = $attempts['data'];
6053
        $my_exercise_result_array = $exercise_result = [];
6054
        if (empty($attempts)) {
6055
            return null;
6056
        }
6057
        foreach ($attempts as $attempt) {
6058
            if (api_get_user_id() == $attempt['exe_user_id']) {
6059
                if (0 != $attempt['max_score']) {
6060
                    $my_exercise_result_array[] = $attempt['score'] / $attempt['max_score'];
6061
                }
6062
            } else {
6063
                if (0 != $attempt['max_score']) {
6064
                    $exercise_result[] = $attempt['score'] / $attempt['max_score'];
6065
                }
6066
            }
6067
        }
6068
6069
        //Getting best result
6070
        rsort($my_exercise_result_array);
6071
        $my_exercise_result = 0;
6072
        if (isset($my_exercise_result_array[0])) {
6073
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6074
        }
6075
6076
        $max = 100;
6077
        $pieces = 5;
6078
        $part = round($max / $pieces);
6079
        $x_axis = [];
6080
        $final_array = [];
6081
        $my_final_array = [];
6082
6083
        for ($i = 1; $i <= $pieces; $i++) {
6084
            $sum = 1;
6085
            if (1 == $i) {
6086
                $sum = 0;
6087
            }
6088
            $min = ($i - 1) * $part + $sum;
6089
            $max = ($i) * $part;
6090
            $x_axis[] = $min." - ".$max;
6091
            $count = 0;
6092
            foreach ($exercise_result as $result) {
6093
                $percentage = $result * 100;
6094
                if ($percentage >= $min && $percentage <= $max) {
6095
                    $count++;
6096
                }
6097
            }
6098
            $final_array[] = $count;
6099
6100
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6101
                $my_final_array[] = 1;
6102
            } else {
6103
                $my_final_array[] = 0;
6104
            }
6105
        }
6106
6107
        //Fix to remove the data of the user with my data
6108
6109
        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...
6110
            if (!empty($my_final_array[$i])) {
6111
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6112
                $final_array[$i] = 0;
6113
            }
6114
        }
6115
6116
        // Dataset definition
6117
        $dataSet = new pData();
6118
        $dataSet->addPoints($final_array, 'Serie1');
6119
        $dataSet->addPoints($my_final_array, 'Serie2');
6120
        $dataSet->addPoints($x_axis, 'Serie3');
6121
6122
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
6123
        $dataSet->setSerieDescription('Serie2', get_lang('My results'));
6124
        $dataSet->setAbscissa('Serie3');
6125
6126
        $dataSet->setXAxisName(get_lang('Score'));
6127
        $dataSet->normalize(100, "%");
6128
6129
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6130
6131
        // Cache definition
6132
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6133
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6134
        $chartHash = $myCache->getHash($dataSet);
6135
6136
        if ($myCache->isInCache($chartHash)) {
6137
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6138
            $myCache->saveFromCache($chartHash, $imgPath);
6139
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6140
        } else {
6141
            /* Create the pChart object */
6142
            $widthSize = 480;
6143
            $heightSize = 250;
6144
            $fontSize = 8;
6145
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6146
6147
            /* Turn of Antialiasing */
6148
            $myPicture->Antialias = false;
6149
6150
            /* Add a border to the picture */
6151
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
6152
6153
            /* Set the default font */
6154
            $myPicture->setFontProperties(
6155
                [
6156
                    'FontName' => api_get_path(
6157
                            SYS_FONTS_PATH
6158
                        ).'opensans/OpenSans-Regular.ttf',
6159
                    'FontSize' => 10,
6160
                ]
6161
            );
6162
6163
            /* Write the chart title */
6164
            $myPicture->drawText(
6165
                250,
6166
                20,
6167
                $exercise_title,
6168
                [
6169
                    'FontSize' => 12,
6170
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
6171
                ]
6172
            );
6173
6174
            /* Define the chart area */
6175
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
6176
6177
            /* Draw the scale */
6178
            $scaleSettings = [
6179
                'GridR' => 200,
6180
                'GridG' => 200,
6181
                'GridB' => 200,
6182
                'DrawSubTicks' => true,
6183
                'CycleBackground' => true,
6184
                'Mode' => SCALE_MODE_MANUAL,
6185
                'ManualScale' => [
6186
                    '0' => [
6187
                        'Min' => 0,
6188
                        'Max' => 100,
6189
                    ],
6190
                ],
6191
            ];
6192
            $myPicture->drawScale($scaleSettings);
6193
6194
            /* Turn on shadow computing */
6195
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6196
6197
            /* Draw the chart */
6198
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6199
            $settings = [
6200
                'DisplayValues' => true,
6201
                'DisplaySize' => $fontSize,
6202
                'DisplayR' => 0,
6203
                'DisplayG' => 0,
6204
                'DisplayB' => 0,
6205
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6206
                'Gradient' => false,
6207
                'Surrounding' => 30,
6208
                'InnerSurrounding' => 25,
6209
            ];
6210
            $myPicture->drawStackedBarChart($settings);
6211
6212
            $legendSettings = [
6213
                'Mode' => LEGEND_HORIZONTAL,
6214
                'Style' => LEGEND_NOBORDER,
6215
            ];
6216
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
6217
6218
            /* Write and save into cache */
6219
            $myCache->writeToCache($chartHash, $myPicture);
6220
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6221
            $myCache->saveFromCache($chartHash, $imgPath);
6222
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6223
        }
6224
6225
        return $imgPath;
6226
    }
6227
6228
    /**
6229
     * @param FormValidator $form
6230
     *
6231
     * @return mixed
6232
     */
6233
    public static function setUserSearchForm($form)
6234
    {
6235
        global $_configuration;
6236
        $form->addElement('text', 'keyword', get_lang('Keyword'));
6237
        $form->addElement(
6238
            'select',
6239
            'active',
6240
            get_lang('Status'),
6241
            [1 => get_lang('active'), 0 => get_lang('inactive')]
6242
        );
6243
6244
        $form->addElement(
6245
            'select',
6246
            'sleeping_days',
6247
            get_lang('inactiveDays'),
6248
            [
6249
                '',
6250
                1 => 1,
6251
                5 => 5,
6252
                15 => 15,
6253
                30 => 30,
6254
                60 => 60,
6255
                90 => 90,
6256
                120 => 120,
6257
            ]
6258
        );
6259
6260
        $form->addButtonSearch(get_lang('Search'));
6261
6262
        return $form;
6263
    }
6264
6265
    /**
6266
     * Get the progress of a exercise.
6267
     *
6268
     * @param int    $sessionId  The session ID (session.id)
6269
     * @param int    $courseId   The course ID (course.id)
6270
     * @param int    $exerciseId The quiz ID (c_quiz.id)
6271
     * @param string $date_from
6272
     * @param string $date_to
6273
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
6274
     *
6275
     * @return array An array with the data of exercise(s) progress
6276
     */
6277
    public static function get_exercise_progress(
6278
        $sessionId = 0,
6279
        $courseId = 0,
6280
        $exerciseId = 0,
6281
        $date_from = null,
6282
        $date_to = null,
6283
        $options = []
6284
    ) {
6285
        $sessionId = intval($sessionId);
6286
        $courseId = intval($courseId);
6287
        $exerciseId = intval($exerciseId);
6288
        $date_from = Database::escape_string($date_from);
6289
        $date_to = Database::escape_string($date_to);
6290
        /*
6291
         * This method gets the data by blocks, as previous attempts at one single
6292
         * query made it take ages. The logic of query division is described below
6293
         */
6294
        // Get tables names
6295
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6296
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6297
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6298
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6299
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6300
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6301
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6302
6303
        $sessions = [];
6304
        $courses = [];
6305
        // if session ID is defined but course ID is empty, get all the courses
6306
        // from that session
6307
        if (!empty($sessionId) && empty($courseId)) {
6308
            // $courses is an array of course int id as index and course details hash as value
6309
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6310
            $sessions[$sessionId] = api_get_session_info($sessionId);
6311
        } elseif (empty($sessionId) && !empty($courseId)) {
6312
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6313
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
6314
            $course = api_get_course_info_by_id($courseId);
6315
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6316
            $courses[$courseId] = $course;
6317
            foreach ($sessionsTemp as $sessionItem) {
6318
                $sessions[$sessionItem['id']] = $sessionItem;
6319
            }
6320
        } elseif (!empty($courseId) && !empty($sessionId)) {
6321
            //none is empty
6322
            $course = api_get_course_info_by_id($courseId);
6323
            $courses[$courseId] = [$course['code']];
6324
            $courses[$courseId]['code'] = $course['code'];
6325
            $sessions[$sessionId] = api_get_session_info($sessionId);
6326
        } else {
6327
            //both are empty, not enough data, return an empty array
6328
            return [];
6329
        }
6330
        // Now we have two arrays of courses and sessions with enough data to proceed
6331
        // If no course could be found, we shouldn't return anything.
6332
        // Course sessions can be empty (then we only return the pure-course-context results)
6333
        if (count($courses) < 1) {
6334
            return [];
6335
        }
6336
6337
        $data = [];
6338
        // The following loop is less expensive than what it seems:
6339
        // - if a course was defined, then we only loop through sessions
6340
        // - if a session was defined, then we only loop through courses
6341
        // - if a session and a course were defined, then we only loop once
6342
        foreach ($courses as $courseIdx => $courseData) {
6343
            $where = '';
6344
            $whereParams = [];
6345
            $whereSessionParams = '';
6346
            if (count($sessions > 0)) {
6347
                foreach ($sessions as $sessionIdx => $sessionData) {
6348
                    if (!empty($sessionIdx)) {
6349
                        $whereSessionParams .= $sessionIdx.',';
6350
                    }
6351
                }
6352
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6353
            }
6354
6355
            if (!empty($exerciseId)) {
6356
                $exerciseId = intval($exerciseId);
6357
                $where .= ' AND q.id = %d ';
6358
                $whereParams[] = $exerciseId;
6359
            }
6360
6361
            /*
6362
             * This feature has been disabled for now, to avoid having to
6363
             * join two very large tables
6364
            //2 = show all questions (wrong and correct answered)
6365
            if ($answer != 2) {
6366
                $answer = intval($answer);
6367
                //$where .= ' AND qa.correct = %d';
6368
                //$whereParams[] = $answer;
6369
            }
6370
            */
6371
6372
            $limit = '';
6373
            if (!empty($options['limit'])) {
6374
                $limit = " LIMIT ".$options['limit'];
6375
            }
6376
6377
            if (!empty($options['where'])) {
6378
                $where .= ' AND '.Database::escape_string($options['where']);
6379
            }
6380
6381
            $order = '';
6382
            if (!empty($options['order'])) {
6383
                $order = " ORDER BY ".$options['order'];
6384
            }
6385
6386
            if (!empty($date_to) && !empty($date_from)) {
6387
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6388
            }
6389
6390
            $sql = "SELECT
6391
                te.session_id,
6392
                ta.id as attempt_id,
6393
                te.exe_user_id as user_id,
6394
                te.exe_id as exercise_attempt_id,
6395
                ta.question_id,
6396
                ta.answer as answer_id,
6397
                ta.tms as time,
6398
                te.exe_exo_id as quiz_id,
6399
                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
6400
                q.title as quiz_title,
6401
                qq.description as description
6402
                FROM $ttrack_exercises te
6403
                INNER JOIN $ttrack_attempt ta
6404
                ON ta.exe_id = te.exe_id
6405
                INNER JOIN $tquiz q
6406
                ON q.id = te.exe_exo_id
6407
                INNER JOIN $tquiz_rel_question rq
6408
                ON rq.exercice_id = q.id AND rq.c_id = q.c_id
6409
                INNER JOIN $tquiz_question qq
6410
                ON
6411
                    qq.id = rq.question_id AND
6412
                    qq.c_id = rq.c_id AND
6413
                    qq.position = rq.question_order AND
6414
                    ta.question_id = rq.question_id
6415
                WHERE
6416
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6417
                    AND q.c_id = $courseIdx
6418
                    $where $order $limit";
6419
            $sql_query = vsprintf($sql, $whereParams);
6420
6421
            // Now browse through the results and get the data
6422
            $rs = Database::query($sql_query);
6423
            $userIds = [];
6424
            $questionIds = [];
6425
            $answerIds = [];
6426
            while ($row = Database::fetch_array($rs)) {
6427
                //only show if exercise is visible
6428
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6429
                    $userIds[$row['user_id']] = $row['user_id'];
6430
                    $questionIds[$row['question_id']] = $row['question_id'];
6431
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6432
                    $row['session'] = $sessions[$row['session_id']];
6433
                    $data[] = $row;
6434
                }
6435
            }
6436
            // Now fill questions data. Query all questions and answers for this test to avoid
6437
            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
6438
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
6439
                            FROM $tquiz_question tq, $tquiz_answer tqa
6440
                            WHERE
6441
                                tqa.question_id = tq.id AND
6442
                                tqa.c_id = tq.c_id AND
6443
                                tq.c_id = $courseIdx AND
6444
                                tq.id IN (".implode(',', $questionIds).")";
6445
6446
            $resQuestions = Database::query($sqlQuestions);
6447
            $answer = [];
6448
            $question = [];
6449
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6450
                $questionId = $rowQuestion['question_id'];
6451
                $answerId = $rowQuestion['answer_id'];
6452
                $answer[$questionId][$answerId] = [
6453
                    'position' => $rowQuestion['position'],
6454
                    'question' => $rowQuestion['question'],
6455
                    'answer' => $rowQuestion['answer'],
6456
                    'correct' => $rowQuestion['correct'],
6457
                ];
6458
                $question[$questionId]['question'] = $rowQuestion['question'];
6459
            }
6460
6461
            // Now fill users data
6462
            $sqlUsers = "SELECT user_id, username, lastname, firstname
6463
                         FROM $tuser
6464
                         WHERE user_id IN (".implode(',', $userIds).")";
6465
            $resUsers = Database::query($sqlUsers);
6466
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6467
                $users[$rowUser['user_id']] = $rowUser;
6468
            }
6469
6470
            foreach ($data as $id => $row) {
6471
                $rowQuestId = $row['question_id'];
6472
                $rowAnsId = $row['answer_id'];
6473
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
6474
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
6475
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6476
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6477
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6478
                $data[$id]['correct'] = (0 == $answer[$rowQuestId][$rowAnsId]['correct'] ? get_lang('No') : get_lang('Yes'));
6479
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6480
                $data[$id]['question_id'] = $rowQuestId;
6481
                $data[$id]['description'] = $row['description'];
6482
            }
6483
6484
            /*
6485
            The minimum expected array structure at the end is:
6486
            attempt_id,
6487
            session name,
6488
            exercise_id,
6489
            quiz_title,
6490
            username,
6491
            lastname,
6492
            firstname,
6493
            time,
6494
            question_id,
6495
            question,
6496
            answer,
6497
            */
6498
        }
6499
6500
        return $data;
6501
    }
6502
6503
    /**
6504
     * @param string              $tool
6505
     * @param sessionEntity |null $session Optional
6506
     *
6507
     * @throws \Doctrine\ORM\NonUniqueResultException
6508
     *
6509
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
6510
     */
6511
    public static function getLastStudentPublication(
6512
        User $user,
6513
        $tool,
6514
        Course $course,
6515
        SessionEntity $session = null
6516
    ) {
6517
        return Database::getManager()
6518
            ->createQuery("
6519
                SELECT csp
6520
                FROM ChamiloCourseBundle:CStudentPublication csp
6521
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6522
                    WITH (
6523
                        csp.iid = cip.ref AND
6524
                        csp.session = cip.session AND
6525
                        csp.cId = cip.course AND
6526
                        csp.userId = cip.lasteditUserId
6527
                    )
6528
                WHERE
6529
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6530
                ORDER BY csp.iid DESC
6531
            ")
6532
            ->setMaxResults(1)
6533
            ->setParameters([
6534
                'tool' => $tool,
6535
                'session' => $session,
6536
                'course' => $course,
6537
                'user' => $user,
6538
            ])
6539
            ->getOneOrNullResult();
6540
    }
6541
6542
    /**
6543
     * Get the HTML code for show a block with the achieved user skill on course/session.
6544
     *
6545
     * @param int  $userId
6546
     * @param int  $courseId
6547
     * @param int  $sessionId
6548
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6549
     *
6550
     * @return string
6551
     */
6552
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6553
    {
6554
        if (false === Skill::isAllowed($userId, false) && false == $forceView) {
6555
            return '';
6556
        }
6557
        $skillManager = new Skill();
6558
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6559
6560
        return $html;
6561
    }
6562
6563
    /**
6564
     * @param int $userId
6565
     * @param int $courseId
6566
     * @param int $sessionId
6567
     *
6568
     * @return array
6569
     */
6570
    public static function getCalculateTime($userId, $courseId, $sessionId)
6571
    {
6572
        $userId = (int) $userId;
6573
        $courseId = (int) $courseId;
6574
        $sessionId = (int) $sessionId;
6575
6576
        if (empty($userId) || empty($courseId)) {
6577
            return [];
6578
        }
6579
6580
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6581
                FROM track_e_access_complete
6582
                WHERE
6583
                    user_id = $userId AND
6584
                    c_id = $courseId AND
6585
                    session_id = $sessionId AND
6586
                    login_as = 0
6587
                ORDER BY date_reg ASC
6588
                LIMIT 1";
6589
        $rs = Database::query($sql);
6590
6591
        $firstConnection = '';
6592
        $lastConnection = '';
6593
        if (Database::num_rows($rs) > 0) {
6594
            $value = Database::fetch_array($rs);
6595
            $firstConnection = $value['min'];
6596
            $lastConnection = $value['max'];
6597
        }
6598
6599
        $sql = "SELECT * FROM track_e_access_complete
6600
                WHERE
6601
                    user_id = $userId AND
6602
                    c_id = $courseId AND
6603
                    session_id = $sessionId AND
6604
                    login_as = 0 AND current_id <> 0";
6605
6606
        $res = Database::query($sql);
6607
        $reg = [];
6608
        while ($row = Database::fetch_assoc($res)) {
6609
            $reg[$row['id']] = $row;
6610
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6611
        }
6612
6613
        $sessions = [];
6614
        foreach ($reg as $key => $value) {
6615
            $sessions[$value['current_id']][$value['tool']][] = $value;
6616
        }
6617
6618
        $quizTime = 0;
6619
        $result = [];
6620
        $totalTime = 0;
6621
        $lpTime = [];
6622
        $lpDetailTime = [];
6623
        foreach ($sessions as $listPerTool) {
6624
            $min = 0;
6625
            $max = 0;
6626
            $sessionDiff = 0;
6627
            foreach ($listPerTool as $tool => $results) {
6628
                $beforeItem = [];
6629
                foreach ($results as $item) {
6630
                    if (empty($beforeItem)) {
6631
                        $beforeItem = $item;
6632
                        if (empty($min)) {
6633
                            $min = $item['date_reg'];
6634
                        }
6635
6636
                        if (empty($max)) {
6637
                            $max = $item['date_reg'];
6638
                        }
6639
                        continue;
6640
                    }
6641
6642
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6643
                    if ($item['date_reg'] > $max) {
6644
                        $max = $item['date_reg'];
6645
                    }
6646
6647
                    if (empty($min)) {
6648
                        $min = $item['date_reg'];
6649
                    }
6650
6651
                    if ($item['date_reg'] < $min) {
6652
                        $min = $item['date_reg'];
6653
                    }
6654
6655
                    switch ($tool) {
6656
                        case TOOL_AGENDA:
6657
                        case TOOL_FORUM:
6658
                        case TOOL_ANNOUNCEMENT:
6659
                        case TOOL_COURSE_DESCRIPTION:
6660
                        case TOOL_SURVEY:
6661
                        case TOOL_NOTEBOOK:
6662
                        case TOOL_GRADEBOOK:
6663
                        case TOOL_DROPBOX:
6664
                        case 'Reports':
6665
                        case 'Videoconference':
6666
                        case TOOL_LINK:
6667
                        case TOOL_CHAT:
6668
                        case 'course-main':
6669
                            if (!isset($result[$tool])) {
6670
                                $result[$tool] = 0;
6671
                            }
6672
                            $result[$tool] += $partialTime;
6673
                            break;
6674
                        case TOOL_LEARNPATH:
6675
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6676
                                break;
6677
                            }
6678
                            if (!isset($lpTime[$item['tool_id']])) {
6679
                                $lpTime[$item['tool_id']] = 0;
6680
                            }
6681
6682
                            // Saving the attempt id "action_details"
6683
                            if (!empty($item['tool_id'])) {
6684
                                if (!empty($item['tool_id_detail'])) {
6685
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6686
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6687
                                    }
6688
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6689
                                }
6690
                                $lpTime[$item['tool_id']] += $partialTime;
6691
                            }
6692
                            break;
6693
                        case TOOL_QUIZ:
6694
                            if (!isset($lpTime[$item['action_details']])) {
6695
                                $lpTime[$item['action_details']] = 0;
6696
                            }
6697
                            if ('learnpath_id' === $beforeItem['action']) {
6698
                                $lpTime[$item['action_details']] += $partialTime;
6699
                            } else {
6700
                                $quizTime += $partialTime;
6701
                            }
6702
                            break;
6703
                    }
6704
                    $beforeItem = $item;
6705
                }
6706
            }
6707
6708
            $sessionDiff += $max - $min;
6709
            if ($sessionDiff > 0) {
6710
                $totalTime += $sessionDiff;
6711
            }
6712
        }
6713
6714
        $totalLp = 0;
6715
        foreach ($lpTime as $value) {
6716
            $totalLp += $value;
6717
        }
6718
6719
        $result['learnpath_detailed'] = $lpDetailTime;
6720
        $result[TOOL_LEARNPATH] = $lpTime;
6721
        $result[TOOL_QUIZ] = $quizTime;
6722
        $result['total_learnpath'] = $totalLp;
6723
        $result['total_time'] = $totalTime;
6724
        $result['number_connections'] = count($sessions);
6725
        $result['first'] = $firstConnection;
6726
        $result['last'] = $lastConnection;
6727
6728
        return $result;
6729
    }
6730
6731
    /**
6732
     * Gets the IP of a given user, using the last login before the given date.
6733
     *
6734
     * @param int User ID
6735
     * @param string Datetime
6736
     * @param bool Whether to return the IP as a link or just as an IP
6737
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6738
     *
6739
     * @return string IP address (or false on error)
6740
     * @assert (0,0) === false
6741
     */
6742
    public static function get_ip_from_user_event(
6743
        $user_id,
6744
        $event_date,
6745
        $return_as_link = false,
6746
        $body_replace = null
6747
    ) {
6748
        if (empty($user_id) || empty($event_date)) {
6749
            return false;
6750
        }
6751
        $user_id = intval($user_id);
6752
        $event_date = Database::escape_string($event_date);
6753
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6754
        $sql_ip = "SELECT login_date, user_ip
6755
                   FROM $table_login
6756
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6757
                   ORDER BY login_date DESC LIMIT 1";
6758
        $ip = '';
6759
        $res_ip = Database::query($sql_ip);
6760
        if (false !== $res_ip && Database::num_rows($res_ip) > 0) {
6761
            $row_ip = Database::fetch_row($res_ip);
6762
            if ($return_as_link) {
6763
                $ip = Display::url(
6764
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6765
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6766
                    ['title' => get_lang('Trace IP'), 'target' => '_blank']
6767
                );
6768
            } else {
6769
                $ip = $row_ip[1];
6770
            }
6771
        }
6772
6773
        return $ip;
6774
    }
6775
6776
    /**
6777
     * @param int   $userId
6778
     * @param array $courseInfo
6779
     * @param int   $sessionId
6780
     *
6781
     * @return array
6782
     */
6783
    public static function getToolInformation(
6784
        $userId,
6785
        $courseInfo,
6786
        $sessionId = 0
6787
    ) {
6788
        $csvContent = [];
6789
        $courseToolInformation = '';
6790
        $headerTool = [
6791
            [get_lang('Title')],
6792
            [get_lang('Created at')],
6793
            [get_lang('Updated at')],
6794
        ];
6795
6796
        $headerListForCSV = [];
6797
        foreach ($headerTool as $item) {
6798
            $headerListForCSV[] = $item[0];
6799
        }
6800
6801
        $courseForumInformationArray = getForumCreatedByUser(
6802
            $userId,
6803
            $courseInfo,
6804
            $sessionId
6805
        );
6806
6807
        if (!empty($courseForumInformationArray)) {
6808
            $csvContent[] = [];
6809
            $csvContent[] = [get_lang('Forums')];
6810
            $csvContent[] = $headerListForCSV;
6811
            foreach ($courseForumInformationArray as $row) {
6812
                $csvContent[] = $row;
6813
            }
6814
6815
            $courseToolInformation .= Display::page_subheader2(
6816
                get_lang('Forums')
6817
            );
6818
            $courseToolInformation .= Display::return_sortable_table(
6819
                $headerTool,
6820
                $courseForumInformationArray
6821
            );
6822
        }
6823
6824
        $courseWorkInformationArray = getWorkCreatedByUser(
6825
            $userId,
6826
            $courseInfo['real_id'],
6827
            $sessionId
6828
        );
6829
6830
        if (!empty($courseWorkInformationArray)) {
6831
            $csvContent[] = null;
6832
            $csvContent[] = [get_lang('Assignments')];
6833
            $csvContent[] = $headerListForCSV;
6834
6835
            foreach ($courseWorkInformationArray as $row) {
6836
                $csvContent[] = $row;
6837
            }
6838
            $csvContent[] = null;
6839
6840
            $courseToolInformation .= Display::page_subheader2(
6841
                get_lang('Assignments')
6842
            );
6843
            $courseToolInformation .= Display::return_sortable_table(
6844
                $headerTool,
6845
                $courseWorkInformationArray
6846
            );
6847
        }
6848
6849
        $courseToolInformationTotal = null;
6850
        if (!empty($courseToolInformation)) {
6851
            $sessionTitle = null;
6852
            if (!empty($sessionId)) {
6853
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6854
            }
6855
6856
            $courseToolInformationTotal .= Display::page_subheader(
6857
                $courseInfo['title'].$sessionTitle
6858
            );
6859
            $courseToolInformationTotal .= $courseToolInformation;
6860
        }
6861
6862
        return [
6863
            'array' => $csvContent,
6864
            'html' => $courseToolInformationTotal,
6865
        ];
6866
    }
6867
6868
    /**
6869
     * @param int $sessionId
6870
     *
6871
     * @return bool
6872
     */
6873
    public static function isAllowToTrack($sessionId)
6874
    {
6875
        return
6876
            api_is_platform_admin(true, true) ||
6877
            SessionManager::user_is_general_coach(api_get_user_id(), $sessionId) ||
6878
            api_is_allowed_to_create_course() ||
6879
            api_is_course_tutor() ||
6880
            api_is_course_admin();
6881
    }
6882
6883
    public static function getCourseLpProgress($userId, $sessionId)
6884
    {
6885
        $controller = new IndexManager(get_lang('MyCourses'));
6886
        $data = $controller->returnCoursesAndSessions($userId);
6887
        $courseList = $data['courses'];
6888
        $result = [];
6889
        if ($courseList) {
6890
            //$counter = 1;
6891
            foreach ($courseList as $course) {
6892
                $courseId = $course['course_id'];
6893
                $courseInfo = api_get_course_info_by_id($courseId);
6894
                if (empty($courseInfo)) {
6895
                    continue;
6896
                }
6897
                $courseCode = $courseInfo['code'];
6898
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
6899
6900
                // total progress
6901
                $list = new LearnpathList(
6902
                    $userId,
6903
                     $courseInfo,
6904
                    0,
6905
                    'lp.publicatedOn ASC',
6906
                    true,
6907
                    null,
6908
                    true
6909
                );
6910
6911
                $list = $list->get_flat_list();
6912
                $totalProgress = 0;
6913
                $totalTime = 0;
6914
                if (!empty($list)) {
6915
                    foreach ($list as $lp_id => $learnpath) {
6916
                        if (!$learnpath['lp_visibility']) {
6917
                            continue;
6918
                        }
6919
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
6920
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
6921
                        if ($lpProgress == 100) {
6922
                            if (!empty($time)) {
6923
                                $timeInMinutes = $time / 60;
6924
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
6925
                                if ($timeInMinutes >= $min) {
6926
                                    $totalProgress++;
6927
                                }
6928
                            }
6929
                        }
6930
                        $totalTime += $time;
6931
                    }
6932
6933
                    if (!empty($totalProgress)) {
6934
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
6935
                    }
6936
                }
6937
6938
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
6939
6940
                $result[] = [
6941
                    'module' => $courseInfo['name'],
6942
                    'progress' => $progress,
6943
                    'qualification' => $totalProgress,
6944
                    'activeTime' => $totalTime,
6945
                ];
6946
            }
6947
        }
6948
6949
        return $result;
6950
    }
6951
6952
    /**
6953
     * @param int $userId
6954
     * @param int $courseId
6955
     * @param int $sessionId
6956
     *
6957
     * @return int
6958
     */
6959
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
6960
    {
6961
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6962
        $sessionCondition = api_get_session_condition($sessionId);
6963
        $courseId = (int) $courseId;
6964
        $userId = (int) $userId;
6965
6966
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
6967
            FROM $tblTrackCourseAccess
6968
            WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
6969
6970
        $result = Database::fetch_assoc(Database::query($sql));
6971
6972
        return (int) $result['c'];
6973
    }
6974
}
6975
6976
/**
6977
 * @todo move into a proper file
6978
 */
6979
class TrackingCourseLog
6980
{
6981
    /**
6982
     * @return mixed
6983
     */
6984
    public static function count_item_resources()
6985
    {
6986
        $session_id = api_get_session_id();
6987
        $course_id = api_get_course_int_id();
6988
6989
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
6990
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6991
6992
        $sql = "SELECT count(tool) AS total_number_of_items
6993
                FROM $table_item_property track_resource, $table_user user
6994
                WHERE
6995
                    track_resource.c_id = $course_id AND
6996
                    track_resource.insert_user_id = user.user_id AND
6997
                    session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
6998
6999
        if (isset($_GET['keyword'])) {
7000
            $keyword = Database::escape_string(trim($_GET['keyword']));
7001
            $sql .= " AND (
7002
                        user.username LIKE '%".$keyword."%' OR
7003
                        lastedit_type LIKE '%".$keyword."%' OR
7004
                        tool LIKE '%".$keyword."%'
7005
                    )";
7006
        }
7007
7008
        $sql .= " AND tool IN (
7009
                    'document',
7010
                    'learnpath',
7011
                    'quiz',
7012
                    'glossary',
7013
                    'link',
7014
                    'course_description',
7015
                    'announcement',
7016
                    'thematic',
7017
                    'thematic_advance',
7018
                    'thematic_plan'
7019
                )";
7020
        $res = Database::query($sql);
7021
        $obj = Database::fetch_object($res);
7022
7023
        return $obj->total_number_of_items;
7024
    }
7025
7026
    /**
7027
     * @param $from
7028
     * @param $number_of_items
7029
     * @param $column
7030
     * @param $direction
7031
     *
7032
     * @return array
7033
     */
7034
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
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
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
7042
        $session_id = intval($session_id);
7043
7044
        $sql = "SELECT
7045
                    tool as col0,
7046
                    lastedit_type as col1,
7047
                    ref as ref,
7048
                    user.username as col3,
7049
                    insert_date as col6,
7050
                    visibility as col7,
7051
                    user.user_id as user_id
7052
                FROM $table_item_property track_resource, $table_user user
7053
                WHERE
7054
                  track_resource.c_id = $course_id AND
7055
                  track_resource.insert_user_id = user.user_id AND
7056
                  session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
7057
7058
        if (isset($_GET['keyword'])) {
7059
            $keyword = Database::escape_string(trim($_GET['keyword']));
7060
            $sql .= " AND (
7061
                        user.username LIKE '%".$keyword."%' OR
7062
                        lastedit_type LIKE '%".$keyword."%' OR
7063
                        tool LIKE '%".$keyword."%'
7064
                     ) ";
7065
        }
7066
7067
        $sql .= " AND tool IN (
7068
                    'document',
7069
                    'learnpath',
7070
                    'quiz',
7071
                    'glossary',
7072
                    'link',
7073
                    'course_description',
7074
                    'announcement',
7075
                    'thematic',
7076
                    'thematic_advance',
7077
                    'thematic_plan'
7078
                )";
7079
7080
        if (0 == $column) {
7081
            $column = '0';
7082
        }
7083
        if ('' != $column && '' != $direction) {
7084
            if (2 != $column && 4 != $column) {
7085
                $sql .= " ORDER BY col$column $direction";
7086
            }
7087
        } else {
7088
            $sql .= " ORDER BY col6 DESC ";
7089
        }
7090
7091
        $from = intval($from);
7092
        if ($from) {
7093
            $number_of_items = intval($number_of_items);
7094
            $sql .= " LIMIT $from, $number_of_items ";
7095
        }
7096
7097
        $res = Database::query($sql);
7098
        $resources = [];
7099
        $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan'];
7100
        while ($row = Database::fetch_array($res)) {
7101
            $ref = $row['ref'];
7102
            $table_name = self::get_tool_name_table($row['col0']);
7103
            $table_tool = Database::get_course_table($table_name['table_name']);
7104
7105
            $id = $table_name['id_tool'];
7106
            $recorset = false;
7107
7108
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
7109
                $tbl_thematic = Database::get_course_table(TABLE_THEMATIC);
7110
                $sql = "SELECT thematic_id FROM $table_tool
7111
                        WHERE c_id = $course_id AND id = $ref";
7112
                $rs_thematic = Database::query($sql);
7113
                if (Database::num_rows($rs_thematic)) {
7114
                    $row_thematic = Database::fetch_array($rs_thematic);
7115
                    $thematic_id = $row_thematic['thematic_id'];
7116
7117
                    $sql = "SELECT session.id, session.name, user.username
7118
                            FROM $tbl_thematic t, $table_session session, $table_user user
7119
                            WHERE
7120
                              t.c_id = $course_id AND
7121
                              t.session_id = session.id AND
7122
                              session.id_coach = user.user_id AND
7123
                              t.id = $thematic_id";
7124
                    $recorset = Database::query($sql);
7125
                }
7126
            } else {
7127
                $sql = "SELECT session.id, session.name, user.username
7128
                          FROM $table_tool tool, $table_session session, $table_user user
7129
                          WHERE
7130
                              tool.c_id = $course_id AND
7131
                              tool.session_id = session.id AND
7132
                              session.id_coach = user.user_id AND
7133
                              tool.$id = $ref";
7134
                $recorset = Database::query($sql);
7135
            }
7136
7137
            if (!empty($recorset)) {
7138
                $obj = Database::fetch_object($recorset);
7139
7140
                $name_session = '';
7141
                $coach_name = '';
7142
                if (!empty($obj)) {
7143
                    $name_session = $obj->name;
7144
                    $coach_name = $obj->username;
7145
                }
7146
7147
                $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
7148
                $row[0] = '';
7149
                if (2 != $row['col6']) {
7150
                    if (in_array($row['col0'], $thematic_tools)) {
7151
                        $exp_thematic_tool = explode('_', $row['col0']);
7152
                        $thematic_tool_title = '';
7153
                        if (is_array($exp_thematic_tool)) {
7154
                            foreach ($exp_thematic_tool as $exp) {
7155
                                $thematic_tool_title .= api_ucfirst($exp);
7156
                            }
7157
                        } else {
7158
                            $thematic_tool_title = api_ucfirst($row['col0']);
7159
                        }
7160
7161
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
7162
                    } else {
7163
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
7164
                    }
7165
                } else {
7166
                    $row[0] = api_ucfirst($row['col0']);
7167
                }
7168
                $row[1] = get_lang($row[1]);
7169
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
7170
                $row[5] = '';
7171
                //@todo Improve this code please
7172
                switch ($table_name['table_name']) {
7173
                    case 'document':
7174
                        $sql = "SELECT tool.title as title FROM $table_tool tool
7175
                                WHERE c_id = $course_id AND id = $ref";
7176
                        $rs_document = Database::query($sql);
7177
                        $obj_document = Database::fetch_object($rs_document);
7178
                        if ($obj_document) {
7179
                            $row[5] = $obj_document->title;
7180
                        }
7181
                        break;
7182
                    case 'announcement':
7183
                        $sql = "SELECT title FROM $table_tool
7184
                                WHERE c_id = $course_id AND id = $ref";
7185
                        $rs_document = Database::query($sql);
7186
                        $obj_document = Database::fetch_object($rs_document);
7187
                        if ($obj_document) {
7188
                            $row[5] = $obj_document->title;
7189
                        }
7190
                        break;
7191
                    case 'glossary':
7192
                        $sql = "SELECT name FROM $table_tool
7193
                                WHERE c_id = $course_id AND glossary_id = $ref";
7194
                        $rs_document = Database::query($sql);
7195
                        $obj_document = Database::fetch_object($rs_document);
7196
                        if ($obj_document) {
7197
                            $row[5] = $obj_document->name;
7198
                        }
7199
                        break;
7200
                    case 'lp':
7201
                        $sql = "SELECT name
7202
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
7203
                        $rs_document = Database::query($sql);
7204
                        $obj_document = Database::fetch_object($rs_document);
7205
                        $row[5] = $obj_document->name;
7206
                        break;
7207
                    case 'quiz':
7208
                        $sql = "SELECT title FROM $table_tool
7209
                                WHERE c_id = $course_id AND id = $ref";
7210
                        $rs_document = Database::query($sql);
7211
                        $obj_document = Database::fetch_object($rs_document);
7212
                        if ($obj_document) {
7213
                            $row[5] = $obj_document->title;
7214
                        }
7215
                        break;
7216
                    case 'course_description':
7217
                        $sql = "SELECT title FROM $table_tool
7218
                                WHERE c_id = $course_id AND id = $ref";
7219
                        $rs_document = Database::query($sql);
7220
                        $obj_document = Database::fetch_object($rs_document);
7221
                        if ($obj_document) {
7222
                            $row[5] = $obj_document->title;
7223
                        }
7224
                        break;
7225
                    case 'thematic':
7226
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7227
                        if (Database::num_rows($rs) > 0) {
7228
                            $obj = Database::fetch_object($rs);
7229
                            if ($obj) {
7230
                                $row[5] = $obj->title;
7231
                            }
7232
                        }
7233
                        break;
7234
                    case 'thematic_advance':
7235
                        $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7236
                        if (Database::num_rows($rs) > 0) {
7237
                            $obj = Database::fetch_object($rs);
7238
                            if ($obj) {
7239
                                $row[5] = $obj->content;
7240
                            }
7241
                        }
7242
                        break;
7243
                    case 'thematic_plan':
7244
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7245
                        if (Database::num_rows($rs) > 0) {
7246
                            $obj = Database::fetch_object($rs);
7247
                            if ($obj) {
7248
                                $row[5] = $obj->title;
7249
                            }
7250
                        }
7251
                        break;
7252
                    default:
7253
                        break;
7254
                }
7255
7256
                $row2 = $name_session;
7257
                if (!empty($coach_name)) {
7258
                    $row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
7259
                }
7260
                $row[2] = $row2;
7261
                if (!empty($row['col3'])) {
7262
                    $userInfo = api_get_user_info($row['user_id']);
7263
                    $row['col3'] = Display::url(
7264
                        $row['col3'],
7265
                        $userInfo['profile_url']
7266
                    );
7267
                    $row[3] = $row['col3'];
7268
7269
                    $ip = Tracking::get_ip_from_user_event(
7270
                        $row['user_id'],
7271
                        $row['col6'],
7272
                        true
7273
                    );
7274
                    if (empty($ip)) {
7275
                        $ip = get_lang('Unknown');
7276
                    }
7277
                    $row[4] = $ip;
7278
                }
7279
7280
                $resources[] = $row;
7281
            }
7282
        }
7283
7284
        return $resources;
7285
    }
7286
7287
    /**
7288
     * @param string $tool
7289
     *
7290
     * @return array
7291
     */
7292
    public static function get_tool_name_table($tool)
7293
    {
7294
        switch ($tool) {
7295
            case 'document':
7296
                $table_name = TABLE_DOCUMENT;
7297
                $link_tool = 'document/document.php';
7298
                $id_tool = 'id';
7299
                break;
7300
            case 'learnpath':
7301
                $table_name = TABLE_LP_MAIN;
7302
                $link_tool = 'lp/lp_controller.php';
7303
                $id_tool = 'id';
7304
                break;
7305
            case 'quiz':
7306
                $table_name = TABLE_QUIZ_TEST;
7307
                $link_tool = 'exercise/exercise.php';
7308
                $id_tool = 'id';
7309
                break;
7310
            case 'glossary':
7311
                $table_name = TABLE_GLOSSARY;
7312
                $link_tool = 'glossary/index.php';
7313
                $id_tool = 'glossary_id';
7314
                break;
7315
            case 'link':
7316
                $table_name = TABLE_LINK;
7317
                $link_tool = 'link/link.php';
7318
                $id_tool = 'id';
7319
                break;
7320
            case 'course_description':
7321
                $table_name = TABLE_COURSE_DESCRIPTION;
7322
                $link_tool = 'course_description/';
7323
                $id_tool = 'id';
7324
                break;
7325
            case 'announcement':
7326
                $table_name = TABLE_ANNOUNCEMENT;
7327
                $link_tool = 'announcements/announcements.php';
7328
                $id_tool = 'id';
7329
                break;
7330
            case 'thematic':
7331
                $table_name = TABLE_THEMATIC;
7332
                $link_tool = 'course_progress/index.php';
7333
                $id_tool = 'id';
7334
                break;
7335
            case 'thematic_advance':
7336
                $table_name = TABLE_THEMATIC_ADVANCE;
7337
                $link_tool = 'course_progress/index.php';
7338
                $id_tool = 'id';
7339
                break;
7340
            case 'thematic_plan':
7341
                $table_name = TABLE_THEMATIC_PLAN;
7342
                $link_tool = 'course_progress/index.php';
7343
                $id_tool = 'id';
7344
                break;
7345
            default:
7346
                $table_name = $tool;
7347
            break;
7348
        }
7349
7350
        return [
7351
            'table_name' => $table_name,
7352
            'link_tool' => $link_tool,
7353
            'id_tool' => $id_tool,
7354
        ];
7355
    }
7356
7357
    /**
7358
     * @return string
7359
     */
7360
    public static function display_additional_profile_fields()
7361
    {
7362
        // getting all the extra profile fields that are defined by the platform administrator
7363
        $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
7364
7365
        // creating the form
7366
        $return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
7367
7368
        // the select field with the additional user profile fields (= this is where we select the field of which we want to see
7369
        // the information the users have entered or selected.
7370
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
7371
        $return .= '<option value="-">'.get_lang('Select user profile field to add').'</option>';
7372
        $extra_fields_to_show = 0;
7373
        foreach ($extra_fields as $key => $field) {
7374
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
7375
            if (1 == $field[6] && 1 == $field[8]) {
7376
                if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field']) {
7377
                    $selected = 'selected="selected"';
7378
                } else {
7379
                    $selected = '';
7380
                }
7381
                $extra_fields_to_show++;
7382
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
7383
            }
7384
        }
7385
        $return .= '</select>';
7386
7387
        // the form elements for the $_GET parameters (because the form is passed through GET
7388
        foreach ($_GET as $key => $value) {
7389
            if ('additional_profile_field' != $key) {
7390
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
7391
            }
7392
        }
7393
        // the submit button
7394
        $return .= '<button class="save" type="submit">'.get_lang('Add user profile field').'</button>';
7395
        $return .= '</form>';
7396
        if ($extra_fields_to_show > 0) {
7397
            return $return;
7398
        } else {
7399
            return '';
7400
        }
7401
    }
7402
7403
    /**
7404
     * This function gets all the information of a certrain ($field_id)
7405
     * additional profile field for a specific list of users is more efficent
7406
     * than get_addtional_profile_information_of_field() function
7407
     * It gets the information of all the users so that it can be displayed
7408
     * in the sortable table or in the csv or xls export.
7409
     *
7410
     * @author    Julio Montoya <[email protected]>
7411
     *
7412
     * @param    int field id
7413
     * @param    array list of user ids
7414
     *
7415
     * @return array
7416
     *
7417
     * @since    Nov 2009
7418
     *
7419
     * @version    1.8.6.2
7420
     */
7421
    public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users)
7422
    {
7423
        // Database table definition
7424
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7425
        $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
7426
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
7427
        $result_extra_field = UserManager::get_extra_field_information($field_id);
7428
        $return = [];
7429
        if (!empty($users)) {
7430
            if (UserManager::USER_FIELD_TYPE_TAG == $result_extra_field['field_type']) {
7431
                foreach ($users as $user_id) {
7432
                    $user_result = UserManager::get_user_tags($user_id, $field_id);
7433
                    $tag_list = [];
7434
                    foreach ($user_result as $item) {
7435
                        $tag_list[] = $item['tag'];
7436
                    }
7437
                    $return[$user_id][] = implode(', ', $tag_list);
7438
                }
7439
            } else {
7440
                $new_user_array = [];
7441
                foreach ($users as $user_id) {
7442
                    $new_user_array[] = "'".$user_id."'";
7443
                }
7444
                $users = implode(',', $new_user_array);
7445
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
7446
                // Selecting only the necessary information NOT ALL the user list
7447
                $sql = "SELECT user.user_id, v.value
7448
                        FROM $table_user user
7449
                        INNER JOIN $table_user_field_values v
7450
                        ON (user.user_id = v.item_id)
7451
                        INNER JOIN $extraField f
7452
                        ON (f.id = v.field_id)
7453
                        WHERE
7454
                            f.extra_field_type = $extraFieldType AND
7455
                            v.field_id=".intval($field_id)." AND
7456
                            user.user_id IN ($users)";
7457
7458
                $result = Database::query($sql);
7459
                while ($row = Database::fetch_array($result)) {
7460
                    // get option value for field type double select by id
7461
                    if (!empty($row['value'])) {
7462
                        if (ExtraField::FIELD_TYPE_DOUBLE_SELECT ==
7463
                            $result_extra_field['field_type']
7464
                        ) {
7465
                            $id_double_select = explode(';', $row['value']);
7466
                            if (is_array($id_double_select)) {
7467
                                $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
7468
                                $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
7469
                                $row['value'] = ($value1.';'.$value2);
7470
                            }
7471
                        }
7472
7473
                        if (ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD == $result_extra_field['field_type']) {
7474
                            $parsedValue = explode('::', $row['value']);
7475
7476
                            if ($parsedValue) {
7477
                                $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text'];
7478
                                $value2 = $parsedValue[1];
7479
7480
                                $row['value'] = "$value1: $value2";
7481
                            }
7482
                        }
7483
7484
                        if (ExtraField::FIELD_TYPE_TRIPLE_SELECT == $result_extra_field['field_type']) {
7485
                            list($level1, $level2, $level3) = explode(';', $row['value']);
7486
7487
                            $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / ';
7488
                            $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / ';
7489
                            $row['value'] .= $result_extra_field['options'][$level3]['display_text'];
7490
                        }
7491
                    }
7492
                    // get other value from extra field
7493
                    $return[$row['user_id']][] = $row['value'];
7494
                }
7495
            }
7496
        }
7497
7498
        return $return;
7499
    }
7500
7501
    /**
7502
     * count the number of students in this course (used for SortableTable)
7503
     * Deprecated.
7504
     */
7505
    public function count_student_in_course()
7506
    {
7507
        global $nbStudents;
7508
7509
        return $nbStudents;
7510
    }
7511
7512
    public function sort_users($a, $b)
7513
    {
7514
        $tracking = Session::read('tracking_column');
7515
7516
        return strcmp(
7517
            trim(api_strtolower($a[$tracking])),
7518
            trim(api_strtolower($b[$tracking]))
7519
        );
7520
    }
7521
7522
    public function sort_users_desc($a, $b)
7523
    {
7524
        $tracking = Session::read('tracking_column');
7525
7526
        return strcmp(
7527
            trim(api_strtolower($b[$tracking])),
7528
            trim(api_strtolower($a[$tracking]))
7529
        );
7530
    }
7531
7532
    /**
7533
     * Get number of users for sortable with pagination.
7534
     *
7535
     * @return int
7536
     */
7537
    public static function get_number_of_users($conditions)
7538
    {
7539
        $conditions['get_count'] = true;
7540
7541
        return self::get_user_data(null, null, null, null, $conditions);
7542
    }
7543
7544
    /**
7545
     * Get data for users list in sortable with pagination.
7546
     *
7547
     * @param int $from
7548
     * @param int $number_of_items
7549
     * @param $column
7550
     * @param $direction
7551
     * @param $conditions
7552
     *
7553
     * @return array
7554
     */
7555
    public static function get_user_data(
7556
        $from,
7557
        $number_of_items,
7558
        $column,
7559
        $direction,
7560
        $conditions = []
7561
    ) {
7562
        global $user_ids, $course_code, $export_csv, $session_id;
7563
        $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users
7564
        $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false;
7565
7566
        $csv_content = [];
7567
        $course_code = Database::escape_string($course_code);
7568
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
7569
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
7570
        $access_url_id = api_get_current_access_url_id();
7571
7572
        // get all users data from a course for sortable with limit
7573
        if (is_array($user_ids)) {
7574
            $user_ids = array_map('intval', $user_ids);
7575
            $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") ";
7576
        } else {
7577
            $user_ids = (int) $user_ids;
7578
            $condition_user = " WHERE user.id = $user_ids ";
7579
        }
7580
7581
        if (!empty($_GET['user_keyword'])) {
7582
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
7583
            $condition_user .= " AND (
7584
                user.firstname LIKE '%".$keyword."%' OR
7585
                user.lastname LIKE '%".$keyword."%'  OR
7586
                user.username LIKE '%".$keyword."%'  OR
7587
                user.email LIKE '%".$keyword."%'
7588
             ) ";
7589
        }
7590
7591
        $url_table = '';
7592
        $url_condition = '';
7593
        if (api_is_multiple_url_enabled()) {
7594
            $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)";
7595
            $url_condition = " AND access_url_id = '$access_url_id'";
7596
        }
7597
7598
        $invitedUsersCondition = '';
7599
        if (!$includeInvitedUsers) {
7600
            $invitedUsersCondition = " AND user.status != ".INVITEE;
7601
        }
7602
7603
        $select = '
7604
                SELECT user.id as user_id,
7605
                    user.official_code  as col0,
7606
                    user.lastname       as col1,
7607
                    user.firstname      as col2,
7608
                    user.username       as col3';
7609
        if ($getCount) {
7610
            $select = ' SELECT COUNT(distinct(user.id)) as count ';
7611
        }
7612
7613
        $sqlInjectJoins = '';
7614
        $where = 'AND 1 = 1 ';
7615
        $sqlInjectWhere = '';
7616
        if (!empty($conditions)) {
7617
            if (isset($conditions['inject_joins'])) {
7618
                $sqlInjectJoins = $conditions['inject_joins'];
7619
            }
7620
            if (isset($conditions['where'])) {
7621
                $where = $conditions['where'];
7622
            }
7623
            if (isset($conditions['inject_where'])) {
7624
                $sqlInjectWhere = $conditions['inject_where'];
7625
            }
7626
            $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1;
7627
            $injectExtraFields = rtrim($injectExtraFields, ', ');
7628
            if (false === $getCount) {
7629
                $select .= " , $injectExtraFields";
7630
            }
7631
        }
7632
7633
        $sql = "$select
7634
                FROM $tbl_user as user
7635
                $url_table
7636
                $sqlInjectJoins
7637
                $condition_user
7638
                $url_condition
7639
                $invitedUsersCondition
7640
                $where
7641
                $sqlInjectWhere
7642
                ";
7643
7644
        if (!in_array($direction, ['ASC', 'DESC'])) {
7645
            $direction = 'ASC';
7646
        }
7647
7648
        $column = (int) $column;
7649
        $from = (int) $from;
7650
        $number_of_items = (int) $number_of_items;
7651
7652
        if ($getCount) {
7653
            $res = Database::query($sql);
7654
            $row = Database::fetch_array($res);
7655
7656
            return $row['count'];
7657
        }
7658
7659
        $sql .= " ORDER BY col$column $direction ";
7660
        $sql .= " LIMIT $from, $number_of_items";
7661
7662
        $res = Database::query($sql);
7663
        $users = [];
7664
7665
        $courseInfo = api_get_course_info($course_code);
7666
        $courseId = $courseInfo['real_id'];
7667
        $courseCode = $courseInfo['code'];
7668
7669
        $total_surveys = 0;
7670
        $total_exercises = ExerciseLib::get_all_exercises(
7671
            $courseInfo,
7672
            $session_id,
7673
            false,
7674
            null,
7675
            false,
7676
            3
7677
        );
7678
7679
        if (empty($session_id)) {
7680
            $survey_user_list = [];
7681
            $surveyList = SurveyManager::get_surveys($course_code, $session_id);
7682
            if ($surveyList) {
7683
                $total_surveys = count($surveyList);
7684
                foreach ($surveyList as $survey) {
7685
                $user_list = SurveyManager::get_people_who_filled_survey(
7686
                    $survey['survey_id'],
7687
                    false,
7688
                        $courseId
7689
                );
7690
7691
                foreach ($user_list as $user_id) {
7692
                    isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
7693
                    }
7694
                }
7695
            }
7696
        }
7697
7698
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode.
7699
            '&course='.$course_code.'&origin=tracking_course&id_session='.$session_id;
7700
7701
        $sortByFirstName = api_sort_by_first_name();
7702
        Session::write('user_id_list', []);
7703
        $userIdList = [];
7704
        while ($user = Database::fetch_array($res, 'ASSOC')) {
7705
            $userIdList[] = $user['user_id'];
7706
            $user['official_code'] = $user['col0'];
7707
            $user['username'] = $user['col3'];
7708
            $user['time'] = api_time_to_hms(
7709
                Tracking::get_time_spent_on_the_course(
7710
                    $user['user_id'],
7711
                    $courseId,
7712
                    $session_id
7713
                )
7714
            );
7715
7716
            $avg_student_score = Tracking::get_avg_student_score(
7717
                $user['user_id'],
7718
                $course_code,
7719
                [],
7720
                $session_id
7721
            );
7722
7723
            $avg_student_progress = Tracking::get_avg_student_progress(
7724
                $user['user_id'],
7725
                $course_code,
7726
                [],
7727
                $session_id
7728
            );
7729
7730
            if (empty($avg_student_progress)) {
7731
                $avg_student_progress = 0;
7732
            }
7733
            $user['average_progress'] = $avg_student_progress.'%';
7734
7735
            $total_user_exercise = Tracking::get_exercise_student_progress(
7736
                $total_exercises,
7737
                $user['user_id'],
7738
                $courseId,
7739
                $session_id
7740
            );
7741
7742
            $user['exercise_progress'] = $total_user_exercise;
7743
7744
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
7745
                $total_exercises,
7746
                $user['user_id'],
7747
                $courseId,
7748
                $session_id
7749
            );
7750
7751
            $user['exercise_average_best_attempt'] = $total_user_exercise;
7752
7753
            if (is_numeric($avg_student_score)) {
7754
                $user['student_score'] = $avg_student_score.'%';
7755
            } else {
7756
                $user['student_score'] = $avg_student_score;
7757
            }
7758
7759
            $user['count_assignments'] = Tracking::count_student_assignments(
7760
                $user['user_id'],
7761
                $course_code,
7762
                $session_id
7763
            );
7764
            $user['count_messages'] = Tracking::count_student_messages(
7765
                $user['user_id'],
7766
                $course_code,
7767
                $session_id
7768
            );
7769
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
7770
                $user['user_id'],
7771
                $courseId,
7772
                $session_id,
7773
                false === $export_csv
7774
            );
7775
7776
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
7777
                $user['user_id'],
7778
                $courseInfo,
7779
                $session_id,
7780
                false === $export_csv
7781
            );
7782
7783
            if ($export_csv) {
7784
                if (!empty($user['first_connection'])) {
7785
                    $user['first_connection'] = api_get_local_time($user['first_connection']);
7786
                } else {
7787
                    $user['first_connection'] = '-';
7788
                }
7789
                if (!empty($user['last_connection'])) {
7790
                    $user['last_connection'] = api_get_local_time($user['last_connection']);
7791
                } else {
7792
                    $user['last_connection'] = '-';
7793
                }
7794
            }
7795
7796
            if (empty($session_id)) {
7797
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys;
7798
            }
7799
7800
            $url = $urlBase.'&student='.$user['user_id'];
7801
7802
            $user['link'] = '<center><a href="'.$url.'">
7803
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
7804
                             </a></center>';
7805
7806
            // store columns in array $users
7807
            $user_row = [];
7808
            $user_row['official_code'] = $user['official_code']; //0
7809
            if ($sortByFirstName) {
7810
                $user_row['firstname'] = $user['col2'];
7811
                $user_row['lastname'] = $user['col1'];
7812
            } else {
7813
                $user_row['lastname'] = $user['col1'];
7814
                $user_row['firstname'] = $user['col2'];
7815
            }
7816
            $user_row['username'] = $user['username'];
7817
            $user_row['time'] = $user['time'];
7818
            $user_row['average_progress'] = $user['average_progress'];
7819
            $user_row['exercise_progress'] = $user['exercise_progress'];
7820
            $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
7821
            $user_row['student_score'] = $user['student_score'];
7822
            $user_row['count_assignments'] = $user['count_assignments'];
7823
            $user_row['count_messages'] = $user['count_messages'];
7824
7825
            $userGroupManager = new UserGroup();
7826
            $user_row['classes'] = $userGroupManager->getLabelsFromNameList($user['user_id'], UserGroup::NORMAL_CLASS);
7827
7828
            if (empty($session_id)) {
7829
                $user_row['survey'] = $user['survey'];
7830
            } else {
7831
                $userSession = SessionManager::getUserSession($user['user_id'], $session_id);
7832
                $user_row['registered_at'] = '';
7833
                if ($userSession) {
7834
                    $user_row['registered_at'] = api_get_local_time($userSession['registered_at']);
7835
                }
7836
            }
7837
7838
            $user_row['first_connection'] = $user['first_connection'];
7839
            $user_row['last_connection'] = $user['last_connection'];
7840
7841
            // we need to display an additional profile field
7842
            if (isset($_GET['additional_profile_field'])) {
7843
                $data = Session::read('additional_user_profile_info');
7844
7845
                $extraFieldInfo = Session::read('extra_field_info');
7846
                foreach ($_GET['additional_profile_field'] as $fieldId) {
7847
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
7848
                        if (is_array($data[$fieldId][$user['user_id']])) {
7849
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = implode(
7850
                                ', ',
7851
                                $data[$fieldId][$user['user_id']]
7852
                            );
7853
                        } else {
7854
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
7855
                        }
7856
                    } else {
7857
                        $user_row[$extraFieldInfo[$fieldId]['variable']] = '';
7858
                    }
7859
                }
7860
            }
7861
7862
            $user_row['link'] = $user['link'];
7863
7864
            if ($export_csv) {
7865
                if (empty($session_id)) {
7866
                    unset($user_row['classes']);
7867
                    unset($user_row['link']);
7868
                } else {
7869
                    unset($user_row['classes']);
7870
                    unset($user_row['link']);
7871
                }
7872
7873
                $csv_content[] = $user_row;
7874
            }
7875
            $users[] = array_values($user_row);
7876
        }
7877
7878
        if ($export_csv) {
7879
            Session::write('csv_content', $csv_content);
7880
        }
7881
7882
        Session::erase('additional_user_profile_info');
7883
        Session::erase('extra_field_info');
7884
        Session::write('user_id_list', $userIdList);
7885
7886
        return $users;
7887
    }
7888
7889
    /**
7890
     * Get data for users list in sortable with pagination.
7891
     *
7892
     * @param $from
7893
     * @param $number_of_items
7894
     * @param $column
7895
     * @param $direction
7896
     * @param $includeInvitedUsers boolean Whether include the invited users
7897
     *
7898
     * @return array
7899
     */
7900
    public static function getTotalTimeReport(
7901
        $from,
7902
        $number_of_items,
7903
        $column,
7904
        $direction,
7905
        $includeInvitedUsers = false
7906
    ) {
7907
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
7908
7909
        $course_code = Database::escape_string($course_code);
7910
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
7911
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
7912
        $access_url_id = api_get_current_access_url_id();
7913
7914
        // get all users data from a course for sortable with limit
7915
        if (is_array($user_ids)) {
7916
            $user_ids = array_map('intval', $user_ids);
7917
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
7918
        } else {
7919
            $user_ids = intval($user_ids);
7920
            $condition_user = " WHERE user.user_id = $user_ids ";
7921
        }
7922
7923
        $url_table = null;
7924
        $url_condition = null;
7925
        if (api_is_multiple_url_enabled()) {
7926
            $url_table = ", ".$tbl_url_rel_user." as url_users";
7927
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
7928
        }
7929
7930
        $invitedUsersCondition = '';
7931
        if (!$includeInvitedUsers) {
7932
            $invitedUsersCondition = " AND user.status != ".INVITEE;
7933
        }
7934
7935
        $sql = "SELECT  user.user_id as user_id,
7936
                    user.official_code  as col0,
7937
                    user.lastname       as col1,
7938
                    user.firstname      as col2,
7939
                    user.username       as col3
7940
                FROM $tbl_user as user $url_table
7941
                $condition_user $url_condition $invitedUsersCondition";
7942
7943
        if (!in_array($direction, ['ASC', 'DESC'])) {
7944
            $direction = 'ASC';
7945
        }
7946
7947
        $column = (int) $column;
7948
        $from = (int) $from;
7949
        $number_of_items = (int) $number_of_items;
7950
7951
        $sql .= " ORDER BY col$column $direction ";
7952
        $sql .= " LIMIT $from,$number_of_items";
7953
7954
        $res = Database::query($sql);
7955
        $users = [];
7956
7957
        $sortByFirstName = api_sort_by_first_name();
7958
        $courseInfo = api_get_course_info($course_code);
7959
        $courseId = $courseInfo['real_id'];
7960
7961
        while ($user = Database::fetch_array($res, 'ASSOC')) {
7962
            $user['official_code'] = $user['col0'];
7963
            $user['lastname'] = $user['col1'];
7964
            $user['firstname'] = $user['col2'];
7965
            $user['username'] = $user['col3'];
7966
7967
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
7968
                $user['user_id'],
7969
                $courseId,
7970
                $session_id
7971
            );
7972
7973
            $user['time'] = api_time_to_hms($totalCourseTime);
7974
            $totalLpTime = Tracking::get_time_spent_in_lp(
7975
                $user['user_id'],
7976
                $course_code,
7977
                [],
7978
                $session_id
7979
            );
7980
7981
            $user['total_lp_time'] = $totalLpTime;
7982
            $warning = '';
7983
            if ($totalLpTime > $totalCourseTime) {
7984
                $warning = '&nbsp;'.Display::label(get_lang('Time difference'), 'danger');
7985
            }
7986
7987
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
7988
7989
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
7990
                $user['user_id'],
7991
                $courseId,
7992
                $session_id
7993
            );
7994
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
7995
                $user['user_id'],
7996
                $courseInfo,
7997
                $session_id,
7998
                false === $export_csv
7999
            );
8000
8001
            $user['link'] = '<center>
8002
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
8003
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
8004
                             </a>
8005
                         </center>';
8006
8007
            // store columns in array $users
8008
            $user_row = [];
8009
            $user_row['official_code'] = $user['official_code']; //0
8010
            if ($sortByFirstName) {
8011
                $user_row['firstname'] = $user['firstname'];
8012
                $user_row['lastname'] = $user['lastname'];
8013
            } else {
8014
                $user_row['lastname'] = $user['lastname'];
8015
                $user_row['firstname'] = $user['firstname'];
8016
            }
8017
            $user_row['username'] = $user['username'];
8018
            $user_row['time'] = $user['time'];
8019
            $user_row['total_lp_time'] = $user['total_lp_time'];
8020
            $user_row['first_connection'] = $user['first_connection'];
8021
            $user_row['last_connection'] = $user['last_connection'];
8022
8023
            $user_row['link'] = $user['link'];
8024
            $users[] = array_values($user_row);
8025
        }
8026
8027
        return $users;
8028
    }
8029
8030
    /**
8031
     * @param string $current
8032
     */
8033
    public static function actionsLeft($current, $sessionId = 0)
8034
    {
8035
        $usersLink = Display::url(
8036
            Display::return_icon('user.png', get_lang('Report on learners'), [], ICON_SIZE_MEDIUM),
8037
            'courseLog.php?'.api_get_cidreq(true, false)
8038
        );
8039
8040
        $groupsLink = Display::url(
8041
            Display::return_icon('group.png', get_lang('Group reporting'), [], ICON_SIZE_MEDIUM),
8042
            'course_log_groups.php?'.api_get_cidreq()
8043
        );
8044
8045
        $resourcesLink = Display::url(
8046
            Display::return_icon('tools.png', get_lang('Report on resource'), [], ICON_SIZE_MEDIUM),
8047
            'course_log_resources.php?'.api_get_cidreq(true, false)
8048
        );
8049
8050
        $courseLink = Display::url(
8051
            Display::return_icon('course.png', get_lang('Course report'), [], ICON_SIZE_MEDIUM),
8052
            'course_log_tools.php?'.api_get_cidreq(true, false)
8053
        );
8054
8055
        $examLink = Display::url(
8056
            Display::return_icon('quiz.png', get_lang('Exam tracking'), [], ICON_SIZE_MEDIUM),
8057
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
8058
        );
8059
8060
        $eventsLink = Display::url(
8061
            Display::return_icon('security.png', get_lang('Audit report'), [], ICON_SIZE_MEDIUM),
8062
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
8063
        );
8064
8065
        $lpLink = Display::url(
8066
            Display::return_icon('scorms.png', get_lang('CourseLPsGenericStats'), [], ICON_SIZE_MEDIUM),
8067
            api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq()
8068
        );
8069
8070
        $attendanceLink = '';
8071
        if (!empty($sessionId)) {
8072
            $attendanceLink = Display::url(
8073
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8074
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
8075
            );
8076
        }
8077
8078
        switch ($current) {
8079
            case 'users':
8080
                $usersLink = Display::url(
8081
                        Display::return_icon(
8082
                        'user_na.png',
8083
                        get_lang('Report on learners'),
8084
                        [],
8085
                        ICON_SIZE_MEDIUM
8086
                    ),
8087
                    '#'
8088
                );
8089
                break;
8090
            case 'groups':
8091
                $groupsLink = Display::url(
8092
                    Display::return_icon('group_na.png', get_lang('Group reporting'), [], ICON_SIZE_MEDIUM),
8093
                    '#'
8094
                );
8095
                break;
8096
            case 'courses':
8097
                $courseLink = Display::url(
8098
                    Display::return_icon('course_na.png', get_lang('Course report'), [], ICON_SIZE_MEDIUM),
8099
                    '#'
8100
                );
8101
                break;
8102
            case 'resources':
8103
                $resourcesLink = Display::url(
8104
                    Display::return_icon(
8105
                    'tools_na.png',
8106
                    get_lang('Report on resource'),
8107
                    [],
8108
                    ICON_SIZE_MEDIUM
8109
                    ),
8110
                    '#'
8111
                );
8112
                break;
8113
            case 'exams':
8114
                $examLink = Display::url(
8115
                    Display::return_icon('quiz_na.png', get_lang('Exam tracking'), [], ICON_SIZE_MEDIUM),
8116
                    '#'
8117
                );
8118
                break;
8119
            case 'logs':
8120
                $eventsLink = Display::url(
8121
                    Display::return_icon('security_na.png', get_lang('Audit report'), [], ICON_SIZE_MEDIUM),
8122
                    '#'
8123
                );
8124
                break;
8125
            case 'attendance':
8126
                if (!empty($sessionId)) {
8127
                    $attendanceLink = Display::url(
8128
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
8129
                        '#'
8130
                    );
8131
                }
8132
                break;
8133
            case 'lp':
8134
                $lpLink = Display::url(
8135
                    Display::return_icon('scorms_na.png', get_lang('CourseLPsGenericStats'), [], ICON_SIZE_MEDIUM),
8136
                    '#'
8137
                );
8138
                break;
8139
        }
8140
8141
        $items = [
8142
            $usersLink,
8143
            $groupsLink,
8144
            $courseLink,
8145
            $resourcesLink,
8146
            $examLink,
8147
            $eventsLink,
8148
            $lpLink,
8149
            $attendanceLink,
8150
        ];
8151
8152
        return implode('', $items).'&nbsp;';
8153
    }
8154
}
8155