Passed
Push — master ( bdd920...c30c5e )
by Yannick
19:04 queued 09:38
created

Tracking::generate_session_exercise_graph()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 13
c 1
b 0
f 1
nc 1
nop 3
dl 0
loc 51
rs 9.8333

How to fix   Long Method   

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