Completed
Push — master ( 23e37a...6a2455 )
by Julito
13:06 queued 16s
created

Tracking::get_avg_student_score()   F

Complexity

Conditions 57
Paths 18498

Size

Total Lines 373
Code Lines 206

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 57
eloc 206
nc 18498
nop 7
dl 0
loc 373
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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