Passed
Push — master ( 6f8cb0...cccadf )
by Julito
09:47
created

Tracking::getTotalTimeSpentOnThePlatform()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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