Passed
Push — master ( f437d8...92f70a )
by Julito
10:14
created

Tracking::get_last_connection_date_on_the_course()   C

Complexity

Conditions 15
Paths 18

Size

Total Lines 115
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 61
nc 18
nop 4
dl 0
loc 115
rs 5.9166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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