Completed
Push — master ( 8b2aa0...562606 )
by Julito
09:06
created

Tracking::get_courses_followed_by_coach()   F

Complexity

Conditions 12
Paths 576

Size

Total Lines 90
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 54
c 0
b 0
f 0
nc 576
nop 2
dl 0
loc 90
rs 3.3888

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Course;
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
7
use Chamilo\UserBundle\Entity\User;
8
use ChamiloSession as Session;
9
use CpChart\Cache as pCache;
10
use CpChart\Data as pData;
11
use CpChart\Image as pImage;
12
use ExtraField as ExtraFieldModel;
13
14
/**
15
 *  Class Tracking.
16
 *
17
 *  @author  Julio Montoya <[email protected]>
18
 *
19
 *  @package chamilo.library
20
 */
21
class Tracking
22
{
23
    /**
24
     * Get group reporting.
25
     *
26
     * @param int    $course_id
27
     * @param int    $sessionId
28
     * @param int    $group_id
29
     * @param string $type
30
     * @param int    $start
31
     * @param int    $limit
32
     * @param int    $sidx
33
     * @param string $sord
34
     * @param array  $where_condition
35
     *
36
     * @return array|null
37
     */
38
    public static function get_group_reporting(
39
        $course_id,
40
        $sessionId = 0,
41
        $group_id = 0,
42
        $type = 'all',
43
        $start = 0,
44
        $limit = 1000,
45
        $sidx = 1,
46
        $sord = 'desc',
47
        $where_condition = []
48
    ) {
49
        $course_id = (int) $course_id;
50
        $sessionId = (int) $sessionId;
51
52
        if (empty($course_id)) {
53
            return null;
54
        }
55
        $courseInfo = api_get_course_info_by_id($course_id);
56
        if ($type == 'count') {
57
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
58
        }
59
60
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
61
        $parsedResult = [];
62
        if (!empty($groupList)) {
63
            foreach ($groupList as $group) {
64
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
65
                $time = 0;
66
                $avg_student_score = 0;
67
                $avg_student_progress = 0;
68
                $work = 0;
69
                $messages = 0;
70
71
                foreach ($users as $user_data) {
72
                    $time += self::get_time_spent_on_the_course(
73
                        $user_data['user_id'],
74
                        $courseInfo['real_id'],
75
                        $sessionId
76
                    );
77
                    $average = self::get_avg_student_score(
78
                        $user_data['user_id'],
79
                        $courseInfo['code'],
80
                        [],
81
                        $sessionId
82
                    );
83
                    if (is_numeric($average)) {
84
                        $avg_student_score += $average;
85
                    }
86
                    $avg_student_progress += self::get_avg_student_progress(
87
                        $user_data['user_id'],
88
                        $courseInfo['code'],
89
                        [],
90
                        $sessionId
91
                    );
92
                    $work += self::count_student_assignments(
93
                        $user_data['user_id'],
94
                        $courseInfo['code'],
95
                        $sessionId
96
                    );
97
                    $messages += self::count_student_messages(
98
                        $user_data['user_id'],
99
                        $courseInfo['code'],
100
                        $sessionId
101
                    );
102
                }
103
104
                $countUsers = count($users);
105
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
106
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
107
108
                $groupItem = [
109
                    'id' => $group['id'],
110
                    'name' => $group['name'],
111
                    'time' => api_time_to_hms($time),
112
                    'progress' => $averageProgress,
113
                    'score' => $averageScore,
114
                    'works' => $work,
115
                    'messages' => $messages,
116
                ];
117
                $parsedResult[] = $groupItem;
118
            }
119
        }
120
121
        return $parsedResult;
122
    }
123
124
    /**
125
     * @param int    $user_id
126
     * @param array  $courseInfo
127
     * @param int    $session_id
128
     * @param string $origin
129
     * @param bool   $export_csv
130
     * @param int    $lp_id
131
     * @param int    $lp_item_id
132
     * @param int    $extendId
133
     * @param int    $extendAttemptId
134
     * @param string $extendedAttempt
135
     * @param string $extendedAll
136
     * @param string $type            classic or simple
137
     * @param bool   $allowExtend     Optional. Allow or not extend te results
138
     *
139
     * @return string
140
     */
141
    public static function getLpStats(
142
        $user_id,
143
        $courseInfo,
144
        $session_id,
145
        $origin,
146
        $export_csv,
147
        $lp_id,
148
        $lp_item_id = null,
149
        $extendId = null,
150
        $extendAttemptId = null,
151
        $extendedAttempt = null,
152
        $extendedAll = null,
153
        $type = 'classic',
154
        $allowExtend = true
155
    ) {
156
        if (empty($courseInfo) || empty($lp_id)) {
157
            return '';
158
        }
159
160
        $hideTime = api_get_configuration_value('hide_lp_time');
161
        $allowNewTracking = api_get_configuration_value('use_new_tracking_in_lp_item');
162
163
        $lp_id = (int) $lp_id;
164
165
        if ($allowNewTracking) {
166
            $extraField = new ExtraFieldValue('lp');
167
            $result = $extraField->get_values_by_handler_and_field_variable($lp_id, 'track_lp_item');
168
            if (empty($result)) {
169
                $allowNewTracking = false;
170
            } else {
171
                if (isset($result['value']) && $result['value'] == 1) {
172
                    $allowNewTracking = true;
173
                }
174
            }
175
        }
176
177
        $lp_item_id = (int) $lp_item_id;
178
        $user_id = (int) $user_id;
179
        $session_id = (int) $session_id;
180
        $origin = Security::remove_XSS($origin);
181
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
182
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
183
        $course_id = $courseInfo['real_id'];
184
        $courseCode = $courseInfo['code'];
185
        $session_condition = api_get_session_condition($session_id);
186
187
        // Extend all button
188
        $output = '';
189
        $url_suffix = '&lp_id='.$lp_id;
190
        if ($origin === 'tracking') {
191
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
192
        }
193
194
        $extend_all = 0;
195
        if (!empty($extendedAll)) {
196
            $extend_all_link = Display::url(
197
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
198
                api_get_self().'?action=stats'.$url_suffix
199
            );
200
            $extend_all = 1;
201
        } else {
202
            $extend_all_link = Display::url(
203
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
204
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
205
            );
206
        }
207
208
        if ($origin != 'tracking') {
209
            $output .= '<div class="section-status">';
210
            $output .= Display::page_header(get_lang('ScormMystatus'));
211
            $output .= '</div>';
212
        }
213
214
        $actionColumn = null;
215
        if ($type === 'classic') {
216
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
217
        }
218
219
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
220
        if ($hideTime) {
221
            $timeHeader = '';
222
        }
223
        $output .= '<div class="table-responsive">';
224
        $output .= '<table id="lp_tracking" class="table tracking">
225
            <thead>
226
            <tr class="table-header">
227
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
228
                <th colspan="4">
229
                    '.get_lang('ScormLessonTitle').'
230
                </th>
231
                <th colspan="2">
232
                    '.get_lang('ScormStatus').'
233
                </th>
234
                <th colspan="2">
235
                    '.get_lang('ScormScore').'
236
                </th>
237
                '.$timeHeader.'
238
                '.$actionColumn.'
239
                </tr>
240
            </thead>
241
            <tbody>
242
        ';
243
244
        // Going through the items using the $items[] array instead of the database order ensures
245
        // we get them in the same order as in the imsmanifest file, which is rather random when using
246
        // the database table.
247
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
248
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
249
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
250
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
251
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
252
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
253
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
254
255
        $sql = "SELECT max(view_count)
256
                FROM $TBL_LP_VIEW
257
                WHERE
258
                    c_id = $course_id AND
259
                    lp_id = $lp_id AND
260
                    user_id = $user_id
261
                    $session_condition";
262
        $res = Database::query($sql);
263
        $view = 0;
264
        if (Database::num_rows($res) > 0) {
265
            $myrow = Database::fetch_array($res);
266
            $view = (int) $myrow[0];
267
        }
268
269
        $counter = 0;
270
        $total_time = 0;
271
        $h = get_lang('h');
272
273
        if (!empty($export_csv)) {
274
            $csvHeaders = [
275
                get_lang('ScormLessonTitle'),
276
                get_lang('ScormStatus'),
277
                get_lang('ScormScore'),
278
            ];
279
280
            if ($hideTime === false) {
281
                $csvHeaders[] = get_lang('ScormTime');
282
            }
283
284
            $csv_content[] = $csvHeaders;
285
        }
286
287
        $result_disabled_ext_all = true;
288
        $chapterTypes = learnpath::getChapterTypes();
289
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
290
291
        $minimunAvailable = self::minimumTimeAvailable($session_id, $course_id);
292
        $timeCourse = [];
293
        if ($minimunAvailable) {
294
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
295
            Session::write('trackTimeCourse', $timeCourse);
296
        }
297
298
        // Show lp items
299
        if (is_array($list) && count($list) > 0) {
300
            foreach ($list as $my_item_id) {
301
                $extend_this = 0;
302
                $order = 'DESC';
303
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
304
                    $extend_this = 1;
305
                    $order = 'ASC';
306
                }
307
308
                // Prepare statement to go through each attempt.
309
                $viewCondition = null;
310
                if (!empty($view)) {
311
                    $viewCondition = " AND v.view_count = $view  ";
312
                }
313
314
                $sql = "SELECT
315
                    iv.status as mystatus,
316
                    v.view_count as mycount,
317
                    iv.score as myscore,
318
                    iv.total_time as mytime,
319
                    i.iid as myid,
320
                    i.lp_id as mylpid,
321
                    iv.lp_view_id as mylpviewid,
322
                    i.title as mytitle,
323
                    i.max_score as mymaxscore,
324
                    iv.max_score as myviewmaxscore,
325
                    i.item_type as item_type,
326
                    iv.view_count as iv_view_count,
327
                    iv.id as iv_id,
328
                    path
329
                FROM $TBL_LP_ITEM as i
330
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
331
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
332
                INNER JOIN $TBL_LP_VIEW as v
333
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
334
                WHERE
335
                    v.c_id = $course_id AND
336
                    i.iid = $my_item_id AND
337
                    i.lp_id = $lp_id  AND
338
                    v.user_id = $user_id AND
339
                    v.session_id = $session_id
340
                    $viewCondition
341
                ORDER BY iv.view_count $order ";
342
343
                $result = Database::query($sql);
344
                $num = Database::num_rows($result);
345
                $time_for_total = 0;
346
                $attemptResult = 0;
347
348
                if ($allowNewTracking && $timeCourse) {
349
                    if (isset($timeCourse['learnpath_detailed']) &&
350
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
351
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
352
                    ) {
353
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
354
                    }
355
                }
356
357
                // Extend all
358
                if (($extend_this || $extend_all) && $num > 0) {
359
                    $row = Database::fetch_array($result);
360
                    $result_disabled_ext_all = false;
361
                    if ($row['item_type'] === 'quiz') {
362
                        // Check results_disabled in quiz table.
363
                        $my_path = Database::escape_string($row['path']);
364
                        $sql = "SELECT results_disabled
365
                                FROM $TBL_QUIZ
366
                                WHERE
367
                                    c_id = $course_id AND
368
                                    id ='".$my_path."'";
369
                        $res_result_disabled = Database::query($sql);
370
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
371
372
                        if (Database::num_rows($res_result_disabled) > 0 &&
373
                            (int) $row_result_disabled[0] === 1
374
                        ) {
375
                            $result_disabled_ext_all = true;
376
                        }
377
                    }
378
379
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
380
                    $oddclass = 'row_even';
381
                    if (($counter % 2) === 0) {
382
                        $oddclass = 'row_odd';
383
                    }
384
                    $extend_link = '';
385
                    if (!empty($inter_num)) {
386
                        $extend_link = Display::url(
387
                            Display::return_icon(
388
                                'visible.png',
389
                                get_lang('HideAttemptView')
390
                            ),
391
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
392
                        );
393
                    }
394
                    $title = $row['mytitle'];
395
396
                    if (empty($title)) {
397
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
398
                    }
399
400
                    if (in_array($row['item_type'], $chapterTypes)) {
401
                        $title = "<h4> $title </h4>";
402
                    }
403
                    $lesson_status = $row['mystatus'];
404
                    $title = Security::remove_XSS($title);
405
                    $counter++;
406
407
                    $action = null;
408
                    if ($type === 'classic') {
409
                        $action = '<td></td>';
410
                    }
411
412
                    if (in_array($row['item_type'], $chapterTypes)) {
413
                        $output .= '<tr class="'.$oddclass.'">
414
                                <td>'.$extend_link.'</td>
415
                                <td colspan="4">
416
                                   '.$title.'
417
                                </td>
418
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
419
                                <td colspan="2"></td>
420
                                <td colspan="2"></td>
421
                                '.$action.'
422
                            </tr>';
423
                        continue;
424
                    } else {
425
                        $output .= '<tr class="'.$oddclass.'">
426
                                <td>'.$extend_link.'</td>
427
                                <td colspan="4">'.$title.'</td>
428
                                <td colspan="2"></td>
429
                                <td colspan="2"></td>
430
                                <td colspan="2"></td>
431
                                '.$action.'
432
                            </tr>';
433
                    }
434
435
                    $attemptCount = 1;
436
                    do {
437
                        // Check if there are interactions below.
438
                        $extend_attempt_link = '';
439
                        $extend_this_attempt = 0;
440
441
                        if ($allowNewTracking && $timeCourse) {
442
                            //$attemptResult = 0;
443
                            if (isset($timeCourse['learnpath_detailed']) &&
444
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
445
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
446
                            ) {
447
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
448
                            }
449
                        }
450
                        if ((
451
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
452
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
453
                            ) &&
454
                            !$extend_all
455
                        ) {
456
                            if ($extendAttemptId == $row['iv_id']) {
457
                                // The extend button for this attempt has been clicked.
458
                                $extend_this_attempt = 1;
459
                                $extend_attempt_link = Display::url(
460
                                    Display::return_icon('visible.png', get_lang('HideAttemptView')),
461
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
462
                                );
463
                                if ($accessToPdfExport) {
464
                                    $extend_attempt_link .= '&nbsp;'.
465
                                        Display::url(
466
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
467
                                            api_get_self(
468
                                            ).'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
469
                                        );
470
                                }
471
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
472
                                // The extend button for this attempt has not been clicked.
473
                                $extend_attempt_link = Display::url(
474
                                    Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
475
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
476
                                );
477
                                if ($accessToPdfExport) {
478
                                    $extend_attempt_link .= '&nbsp;'.
479
                                        Display::url(
480
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
481
                                            api_get_self(
482
                                            ).'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
483
                                        );
484
                                }
485
                            }
486
                        }
487
488
                        $oddclass = 'row_even';
489
                        if (($counter % 2) == 0) {
490
                            $oddclass = 'row_odd';
491
                        }
492
493
                        $lesson_status = $row['mystatus'];
494
                        $score = $row['myscore'];
495
                        $time_for_total = $row['mytime'];
496
                        $attemptTime = $row['mytime'];
497
498
                        if ($minimunAvailable) {
499
                            $lp_time = $timeCourse[TOOL_LEARNPATH];
500
                            $lpTime = null;
501
                            if (isset($lp_time[$lp_id])) {
502
                                $lpTime = (int) $lp_time[$lp_id];
503
                            }
504
                            $time_for_total = $lpTime;
505
506
                            if ($allowNewTracking) {
507
                                $time_for_total = (int) $attemptResult;
508
                                $attemptTime = (int) $attemptResult;
509
                            }
510
                        }
511
512
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
513
514
                        if ($score == 0) {
515
                            $maxscore = $row['mymaxscore'];
516
                        } else {
517
                            if ($row['item_type'] === 'sco') {
518
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
519
                                    $maxscore = $row['myviewmaxscore'];
520
                                } elseif ($row['myviewmaxscore'] === '') {
521
                                    $maxscore = 0;
522
                                } else {
523
                                    $maxscore = $row['mymaxscore'];
524
                                }
525
                            } else {
526
                                $maxscore = $row['mymaxscore'];
527
                            }
528
                        }
529
530
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
531
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
532
533
                        if ($row['item_type'] !== 'dir') {
534
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
535
                                $view_score = Display::return_icon(
536
                                    'invisible.png',
537
                                    get_lang('ResultsHiddenByExerciseSetting')
538
                                );
539
                            } else {
540
                                switch ($row['item_type']) {
541
                                    case 'sco':
542
                                        if ($maxscore == 0) {
543
                                            $view_score = $score;
544
                                        } else {
545
                                            $view_score = ExerciseLib::show_score(
546
                                                $score,
547
                                                $maxscore,
548
                                                false
549
                                            );
550
                                        }
551
                                        break;
552
                                    case 'document':
553
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
554
                                        break;
555
                                    default:
556
                                        $view_score = ExerciseLib::show_score(
557
                                            $score,
558
                                            $maxscore,
559
                                            false
560
                                        );
561
                                        break;
562
                                }
563
                            }
564
565
                            $action = null;
566
                            if ($type == 'classic') {
567
                                $action = '<td></td>';
568
                            }
569
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
570
                            if ($hideTime) {
571
                                $timeRow = '';
572
                            }
573
                            $output .= '<tr class="'.$oddclass.'">
574
                                    <td></td>
575
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
576
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
577
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
578
                                    <td colspan="2">'.$view_score.'</td>
579
                                    '.$timeRow.'
580
                                    '.$action.'
581
                                </tr>';
582
                            $attemptCount++;
583
                            if (!empty($export_csv)) {
584
                                $temp = [];
585
                                $temp[] = $title = Security::remove_XSS($title);
586
                                $temp[] = Security::remove_XSS(
587
                                    learnpathItem::humanize_status($lesson_status, false, $type)
588
                                );
589
590
                                if ($row['item_type'] === 'quiz') {
591
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
592
                                        $temp[] = '/';
593
                                    } else {
594
                                        $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
595
                                    }
596
                                } else {
597
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
598
                                }
599
600
                                if ($hideTime === false) {
601
                                    $temp[] = $time;
602
                                }
603
                                $csv_content[] = $temp;
604
                            }
605
                        }
606
607
                        $counter++;
608
                        $action = null;
609
                        if ($type === 'classic') {
610
                            $action = '<td></td>';
611
                        }
612
613
                        if ($extend_this_attempt || $extend_all) {
614
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
615
                            foreach ($list1 as $id => $interaction) {
616
                                $oddclass = 'row_even';
617
                                if (($counter % 2) == 0) {
618
                                    $oddclass = 'row_odd';
619
                                }
620
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
621
                                if ($hideTime) {
622
                                    $timeRow = '';
623
                                }
624
625
                                $output .= '<tr class="'.$oddclass.'">
626
                                        <td></td>
627
                                        <td></td>
628
                                        <td></td>
629
                                        <td>'.$interaction['order_id'].'</td>
630
                                        <td>'.$interaction['id'].'</td>';
631
632
                                $output .= '
633
                                        <td colspan="2">'.$interaction['type'].'</td>
634
                                        <td>'.$interaction['student_response_formatted'].'</td>
635
                                        <td>'.$interaction['result'].'</td>
636
                                        <td>'.$interaction['latency'].'</td>
637
                                        '.$timeRow.'
638
                                        '.$action.'
639
                                    </tr>';
640
                                $counter++;
641
                            }
642
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
643
                            foreach ($list2 as $id => $interaction) {
644
                                $oddclass = 'row_even';
645
                                if (($counter % 2) === 0) {
646
                                    $oddclass = 'row_odd';
647
                                }
648
                                $output .= '<tr class="'.$oddclass.'">
649
                                        <td></td>
650
                                        <td></td>
651
                                        <td></td>
652
                                        <td>'.$interaction['order_id'].'</td>
653
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
654
                                        <td colspan="2">'.$interaction['status'].'</td>
655
                                        <td>'.$interaction['score_raw'].'</td>
656
                                        <td>'.$interaction['score_max'].'</td>
657
                                        <td>'.$interaction['score_min'].'</td>
658
                                        '.$action.'
659
                                     </tr>';
660
                                $counter++;
661
                            }
662
                        }
663
                    } while ($row = Database::fetch_array($result));
664
                } elseif ($num > 0) {
665
                    // Not extended.
666
                    $row = Database::fetch_array($result, 'ASSOC');
667
                    $my_id = $row['myid'];
668
                    $my_lp_id = $row['mylpid'];
669
                    $my_lp_view_id = $row['mylpviewid'];
670
                    $my_path = $row['path'];
671
                    $result_disabled_ext_all = false;
672
                    if ($row['item_type'] === 'quiz') {
673
                        // Check results_disabled in quiz table.
674
                        $my_path = Database::escape_string($my_path);
675
                        $sql = "SELECT results_disabled
676
                                FROM $TBL_QUIZ
677
                                WHERE c_id = $course_id AND id = '$my_path' ";
678
                        $res_result_disabled = Database::query($sql);
679
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
680
681
                        if (Database::num_rows($res_result_disabled) > 0 &&
682
                            (int) $row_result_disabled[0] === 1
683
                        ) {
684
                            $result_disabled_ext_all = true;
685
                        }
686
                    }
687
688
                    // Check if there are interactions below
689
                    $extend_this_attempt = 0;
690
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
691
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
692
                    $extend_attempt_link = '';
693
                    if ($inter_num > 0 || $objec_num > 0) {
694
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
695
                            // The extend button for this attempt has been clicked.
696
                            $extend_this_attempt = 1;
697
                            $extend_attempt_link = Display::url(
698
                                Display::return_icon('visible.png', get_lang('HideAttemptView')),
699
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
700
                            );
701
                        } else {
702
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
703
                            // The extend button for this attempt has not been clicked.
704
                            $extend_attempt_link = Display::url(
705
                                Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
706
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
707
                            );
708
                        }
709
                    }
710
711
                    $oddclass = 'row_even';
712
                    if (($counter % 2) == 0) {
713
                        $oddclass = 'row_odd';
714
                    }
715
716
                    $extend_link = '';
717
                    if ($inter_num > 1) {
718
                        $extend_link = Display::url(
719
                            Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
720
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
721
                        );
722
                    }
723
724
                    $lesson_status = $row['mystatus'];
725
                    $score = $row['myscore'];
726
                    $subtotal_time = $row['mytime'];
727
                    while ($tmp_row = Database::fetch_array($result)) {
728
                        $subtotal_time += $tmp_row['mytime'];
729
                    }
730
731
                    if ($allowNewTracking) {
732
                        $subtotal_time = $attemptResult;
733
                    }
734
735
                    $title = $row['mytitle'];
736
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
737
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
738
                            WHERE
739
                                exe_exo_id="'.$row['path'].'" AND
740
                                exe_user_id="'.$user_id.'" AND
741
                                orig_lp_id = "'.$lp_id.'" AND
742
                                orig_lp_item_id = "'.$row['myid'].'" AND
743
                                c_id = '.$course_id.' AND
744
                                status <> "incomplete" AND
745
                                session_id = '.$session_id.'
746
                             ORDER BY exe_date DESC
747
                             LIMIT 1';
748
749
                    $resultLastAttempt = Database::query($sql);
750
                    $num = Database::num_rows($resultLastAttempt);
751
                    $id_last_attempt = null;
752
                    if ($num > 0) {
753
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
754
                            $id_last_attempt = $rowLA['exe_id'];
755
                        }
756
                    }
757
758
                    switch ($row['item_type']) {
759
                        case 'sco':
760
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
761
                                $maxscore = $row['myviewmaxscore'];
762
                            } elseif ($row['myviewmaxscore'] === '') {
763
                                $maxscore = 0;
764
                            } else {
765
                                $maxscore = $row['mymaxscore'];
766
                            }
767
                            break;
768
                        case 'quiz':
769
                            // Get score and total time from last attempt of a exercise en lp.
770
                            $sql = "SELECT iid, score
771
                                    FROM $TBL_LP_ITEM_VIEW
772
                                    WHERE
773
                                        c_id = $course_id AND
774
                                        lp_item_id = '".(int) $my_id."' AND
775
                                        lp_view_id = '".(int) $my_lp_view_id."'
776
                                    ORDER BY view_count DESC 
777
                                    LIMIT 1";
778
                            $res_score = Database::query($sql);
779
                            $row_score = Database::fetch_array($res_score);
780
781
                            $sql = "SELECT SUM(total_time) as total_time
782
                                    FROM $TBL_LP_ITEM_VIEW
783
                                    WHERE
784
                                        c_id = $course_id AND
785
                                        lp_item_id = '".(int) $my_id."' AND
786
                                        lp_view_id = '".(int) $my_lp_view_id."'";
787
                            $res_time = Database::query($sql);
788
                            $row_time = Database::fetch_array($res_time);
789
790
                            $score = 0;
791
                            $subtotal_time = 0;
792
                            if (Database::num_rows($res_score) > 0 &&
793
                                Database::num_rows($res_time) > 0
794
                            ) {
795
                                $score = (float) $row_score['score'];
796
                                $subtotal_time = (int) $row_time['total_time'];
797
                            }
798
                            // Selecting the max score from an attempt.
799
                            $sql = "SELECT SUM(t.ponderation) as maxscore
800
                                    FROM (
801
                                        SELECT DISTINCT
802
                                            question_id, marks, ponderation
803
                                        FROM $tbl_stats_attempts as at
804
                                        INNER JOIN $tbl_quiz_questions as q
805
                                        ON (q.id = at.question_id AND q.c_id = $course_id)
806
                                        WHERE exe_id ='$id_last_attempt'
807
                                    ) as t";
808
809
                            $result = Database::query($sql);
810
                            $row_max_score = Database::fetch_array($result);
811
                            $maxscore = $row_max_score['maxscore'];
812
813
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
814
                            $sql = 'SELECT SUM(exe_duration) exe_duration
815
                                    FROM '.$tbl_stats_exercices.'
816
                                    WHERE
817
                                        exe_exo_id="'.$row['path'].'" AND
818
                                        exe_user_id="'.$user_id.'" AND
819
                                        orig_lp_id = "'.$lp_id.'" AND
820
                                        orig_lp_item_id = "'.$row['myid'].'" AND
821
                                        c_id = '.$course_id.' AND
822
                                        status <> "incomplete" AND
823
                                        session_id = '.$session_id.'
824
                                     ORDER BY exe_date DESC ';
825
                            $sumScoreResult = Database::query($sql);
826
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
827
                            if (!empty($durationRow['exe_duration'])) {
828
                                $exeDuration = $durationRow['exe_duration'];
829
                                if ($exeDuration != $subtotal_time &&
830
                                    !empty($row_score['iid']) &&
831
                                    !empty($exeDuration)
832
                                ) {
833
                                    $subtotal_time = $exeDuration;
834
                                    // Update c_lp_item_view.total_time
835
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration' 
836
                                                  WHERE iid = ".$row_score['iid'];
837
                                    Database::query($sqlUpdate);
838
                                }
839
                            }
840
                            break;
841
                        default:
842
                            $maxscore = $row['mymaxscore'];
843
                            break;
844
                    }
845
846
                    $time_for_total = $subtotal_time;
847
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
848
                    if (empty($title)) {
849
                        $title = learnpath::rl_get_resource_name(
850
                            $courseInfo['code'],
851
                            $lp_id,
852
                            $row['myid']
853
                        );
854
                    }
855
856
                    $action = null;
857
                    if ($type == 'classic') {
858
                        $action = '<td></td>';
859
                    }
860
861
                    if (in_array($row['item_type'], $chapterTypes)) {
862
                        $title = Security::remove_XSS($title);
863
                        $output .= '<tr class="'.$oddclass.'">
864
                                <td>'.$extend_link.'</td>
865
                                <td colspan="4">
866
                                <h4>'.$title.'</h4>
867
                                </td>
868
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
869
                                <td colspan="2"></td>
870
                                <td colspan="2"></td>
871
                                '.$action.'
872
                            </tr>';
873
                    } else {
874
                        $correct_test_link = '-';
875
                        $showRowspan = false;
876
                        if ($row['item_type'] === 'quiz') {
877
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
878
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
879
                                     WHERE
880
                                        exe_exo_id="'.$row['path'].'" AND
881
                                        exe_user_id="'.$user_id.'" AND
882
                                        orig_lp_id = "'.$lp_id.'" AND
883
                                        orig_lp_item_id = "'.$row['myid'].'" AND
884
                                        c_id = '.$course_id.' AND
885
                                        status <> "incomplete" AND
886
                                        session_id = '.$session_id.'
887
                                     ORDER BY exe_date DESC ';
888
889
                            $resultLastAttempt = Database::query($sql);
890
                            $num = Database::num_rows($resultLastAttempt);
891
                            $showRowspan = false;
892
                            if ($num > 0) {
893
                                $linkId = 'link_'.$my_id;
894
                                if ($extendedAttempt == 1 &&
895
                                    $lp_id == $my_lp_id &&
896
                                    $lp_item_id == $my_id
897
                                ) {
898
                                    $showRowspan = true;
899
                                    $correct_test_link = Display::url(
900
                                        Display::return_icon(
901
                                            'view_less_stats.gif',
902
                                            get_lang('HideAllAttempts')
903
                                        ),
904
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
905
                                        ['id' => $linkId]
906
                                    );
907
                                } else {
908
                                    $correct_test_link = Display::url(
909
                                        Display::return_icon(
910
                                            'view_more_stats.gif',
911
                                            get_lang(
912
                                                'ShowAllAttemptsByExercise'
913
                                            )
914
                                        ),
915
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
916
                                        ['id' => $linkId]
917
                                    );
918
                                }
919
                            }
920
                        }
921
922
                        $title = Security::remove_XSS($title);
923
                        $action = null;
924
                        if ($type === 'classic') {
925
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
926
                        }
927
928
                        if ($lp_id == $my_lp_id && false) {
929
                            $output .= '<tr class ='.$oddclass.'>
930
                                    <td>'.$extend_link.'</td>
931
                                    <td colspan="4">'.$title.'</td>
932
                                    <td colspan="2">&nbsp;</td>
933
                                    <td colspan="2">&nbsp;</td>
934
                                    <td colspan="2">&nbsp;</td>
935
                                    '.$action.'
936
                                </tr>';
937
                            $output .= '</tr>';
938
                        } else {
939
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
940
                                $output .= "<tr class='$oddclass'>";
941
                            } else {
942
                                $output .= "<tr class='$oddclass'>";
943
                            }
944
945
                            $scoreItem = null;
946
                            if ($row['item_type'] == 'quiz') {
947
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
948
                                    $scoreItem .= Display::return_icon(
949
                                        'invisible.png',
950
                                        get_lang('ResultsHiddenByExerciseSetting')
951
                                    );
952
                                } else {
953
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
954
                                }
955
                            } else {
956
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
957
                            }
958
959
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
960
                            if ($hideTime) {
961
                                $timeRow = '';
962
                            }
963
964
                            $output .= '
965
                                <td>'.$extend_link.'</td>
966
                                <td colspan="4">'.$title.'</td>
967
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
968
                                <td colspan="2">'.$scoreItem.'</td>
969
                                '.$timeRow.'
970
                                '.$action.'
971
                             ';
972
                            $output .= '</tr>';
973
                        }
974
975
                        if (!empty($export_csv)) {
976
                            $temp = [];
977
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
978
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
979
                            if ($row['item_type'] === 'quiz') {
980
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
981
                                    $temp[] = '/';
982
                                } else {
983
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
984
                                }
985
                            } else {
986
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
987
                            }
988
989
                            if ($hideTime === false) {
990
                                $temp[] = $time;
991
                            }
992
                            $csv_content[] = $temp;
993
                        }
994
                    }
995
996
                    $counter++;
997
                    $action = null;
998
                    if ($type === 'classic') {
999
                        $action = '<td></td>';
1000
                    }
1001
1002
                    if ($extend_this_attempt || $extend_all) {
1003
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1004
                        foreach ($list1 as $id => $interaction) {
1005
                            if (($counter % 2) == 0) {
1006
                                $oddclass = 'row_odd';
1007
                            } else {
1008
                                $oddclass = 'row_even';
1009
                            }
1010
1011
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1012
                            if ($hideTime) {
1013
                                $timeRow = '';
1014
                            }
1015
1016
                            $output .= '<tr class="'.$oddclass.'">
1017
                                    <td></td>
1018
                                    <td></td>
1019
                                    <td></td>
1020
                                    <td>'.$interaction['order_id'].'</td>
1021
                                    <td>'.$interaction['id'].'</td>
1022
                                    <td colspan="2">'.$interaction['type'].'</td>
1023
                                    <td>'.urldecode($interaction['student_response']).'</td>
1024
                                    <td>'.$interaction['result'].'</td>
1025
                                    <td>'.$interaction['latency'].'</td>
1026
                                    '.$timeRow.'
1027
                                    '.$action.'
1028
                               </tr>';
1029
                            $counter++;
1030
                        }
1031
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1032
1033
                        foreach ($list2 as $id => $interaction) {
1034
                            if (($counter % 2) == 0) {
1035
                                $oddclass = 'row_odd';
1036
                            } else {
1037
                                $oddclass = 'row_even';
1038
                            }
1039
                            $output .= '<tr class="'.$oddclass.'">
1040
                                    <td></td>
1041
                                    <td></td>
1042
                                    <td></td>
1043
                                    <td>'.$interaction['order_id'].'</td>
1044
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1045
                                    <td colspan="2">'.$interaction['status'].'</td>
1046
                                    <td>'.$interaction['score_raw'].'</td>
1047
                                    <td>'.$interaction['score_max'].'</td>
1048
                                    <td>'.$interaction['score_min'].'</td>
1049
                                    '.$action.'
1050
                               </tr>';
1051
                            $counter++;
1052
                        }
1053
                    }
1054
1055
                    // Attempts listing by exercise.
1056
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1057
                        // Get attempts of a exercise.
1058
                        if (!empty($lp_id) &&
1059
                            !empty($lp_item_id) &&
1060
                            $row['item_type'] === 'quiz'
1061
                        ) {
1062
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1063
                                    WHERE
1064
                                        c_id = $course_id AND
1065
                                        iid = '$lp_item_id' AND
1066
                                        lp_id = '$lp_id'";
1067
                            $res_path = Database::query($sql);
1068
                            $row_path = Database::fetch_array($res_path);
1069
1070
                            if (Database::num_rows($res_path) > 0) {
1071
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1072
                                        WHERE
1073
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1074
                                            status <> "incomplete" AND
1075
                                            exe_user_id="'.$user_id.'" AND
1076
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1077
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1078
                                            c_id = '.$course_id.'  AND
1079
                                            session_id = '.$session_id.'
1080
                                        ORDER BY exe_date';
1081
                                $res_attempts = Database::query($sql);
1082
                                $num_attempts = Database::num_rows($res_attempts);
1083
                                if ($num_attempts > 0) {
1084
                                    $n = 1;
1085
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1086
                                        $my_score = $row_attempts['score'];
1087
                                        $my_maxscore = $row_attempts['max_score'];
1088
                                        $my_exe_id = $row_attempts['exe_id'];
1089
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1090
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1091
                                        $time_attemp = ' - ';
1092
                                        if ($mktime_start_date && $mktime_exe_date) {
1093
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1094
                                        }
1095
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1096
                                            $view_score = Display::return_icon(
1097
                                                'invisible.png',
1098
                                                get_lang(
1099
                                                    'ResultsHiddenByExerciseSetting'
1100
                                                )
1101
                                            );
1102
                                        } else {
1103
                                            // Show only float when need it
1104
                                            if ($my_score == 0) {
1105
                                                $view_score = ExerciseLib::show_score(
1106
                                                    0,
1107
                                                    $my_maxscore,
1108
                                                    false
1109
                                                );
1110
                                            } else {
1111
                                                if ($my_maxscore == 0) {
1112
                                                    $view_score = $my_score;
1113
                                                } else {
1114
                                                    $view_score = ExerciseLib::show_score(
1115
                                                        $my_score,
1116
                                                        $my_maxscore,
1117
                                                        false
1118
                                                    );
1119
                                                }
1120
                                            }
1121
                                        }
1122
                                        $my_lesson_status = $row_attempts['status'];
1123
                                        if ($my_lesson_status == '') {
1124
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1125
                                        } elseif ($my_lesson_status == 'incomplete') {
1126
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1127
                                        }
1128
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1129
                                        if ($hideTime) {
1130
                                            $timeRow = '';
1131
                                        }
1132
1133
                                        $output .= '<tr class="'.$oddclass.'" >
1134
                                        <td></td>
1135
                                        <td>'.$extend_attempt_link.'</td>
1136
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1137
                                        <td colspan="2">'.$my_lesson_status.'</td>
1138
                                        <td colspan="2">'.$view_score.'</td>
1139
                                        '.$timeRow;
1140
1141
                                        if ($action == 'classic') {
1142
                                            if ($origin != 'tracking') {
1143
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1144
                                                    $output .= '<td>
1145
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1146
                                                            </td>';
1147
                                                } else {
1148
                                                    $output .= '<td>
1149
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1150
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1151
                                                            </a></td>';
1152
                                                }
1153
                                            } else {
1154
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1155
                                                    $output .= '<td>
1156
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
1157
                                                } else {
1158
                                                    $output .= '<td>
1159
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1160
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
1161
                                                }
1162
                                            }
1163
                                        }
1164
                                        $output .= '</tr>';
1165
                                        $n++;
1166
                                    }
1167
                                }
1168
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1169
                            }
1170
                        }
1171
                    }
1172
                }
1173
1174
                $total_time += $time_for_total;
1175
                // QUIZZ IN LP
1176
                $a_my_id = [];
1177
                if (!empty($my_lp_id)) {
1178
                    $a_my_id[] = $my_lp_id;
1179
                }
1180
            }
1181
        }
1182
1183
        // NOT Extend all "left green cross"
1184
        if (!empty($a_my_id)) {
1185
            if ($extendedAttempt) {
1186
                // "Right green cross" extended
1187
                $total_score = self::get_avg_student_score(
1188
                    $user_id,
1189
                    $course_id,
1190
                    $a_my_id,
1191
                    $session_id,
1192
                    false,
1193
                    false
1194
                );
1195
            } else {
1196
                // "Left green cross" extended
1197
                $total_score = self::get_avg_student_score(
1198
                    $user_id,
1199
                    $course_id,
1200
                    $a_my_id,
1201
                    $session_id,
1202
                    false,
1203
                    true
1204
                );
1205
            }
1206
        } else {
1207
            // Extend all "left green cross"
1208
            $total_score = self::get_avg_student_score(
1209
                $user_id,
1210
                $course_id,
1211
                [$lp_id],
1212
                $session_id,
1213
                false,
1214
                false
1215
            );
1216
        }
1217
1218
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1219
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1220
1221
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1222
            $final_score = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
1223
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
1224
        } else {
1225
            if (is_numeric($total_score)) {
1226
                $final_score = $total_score.'%';
1227
            } else {
1228
                $final_score = $total_score;
1229
            }
1230
            $finalScoreToCsv = $final_score;
1231
        }
1232
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1233
1234
        $oddclass = 'row_even';
1235
        if (($counter % 2) == 0) {
1236
            $oddclass = 'row_odd';
1237
        }
1238
1239
        $action = null;
1240
        if ($type === 'classic') {
1241
            $action = '<td></td>';
1242
        }
1243
1244
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1245
        if ($hideTime) {
1246
            $timeTotal = '';
1247
        }
1248
1249
        $output .= '<tr class="'.$oddclass.'">
1250
                <td></td>
1251
                <td colspan="4">
1252
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
1253
                </td>
1254
                <td colspan="2">'.$progress.'%</td>
1255
                <td colspan="2">'.$final_score.'</td>
1256
                '.$timeTotal.'
1257
                '.$action.'
1258
           </tr>';
1259
1260
        $output .= '
1261
                    </tbody>
1262
                </table>
1263
            </div>
1264
        ';
1265
1266
        if (!empty($export_csv)) {
1267
            $temp = [
1268
                '',
1269
                '',
1270
                '',
1271
                '',
1272
            ];
1273
            $csv_content[] = $temp;
1274
            $temp = [
1275
                get_lang('AccomplishedStepsTotal'),
1276
                '',
1277
                $finalScoreToCsv,
1278
            ];
1279
1280
            if ($hideTime === false) {
1281
                $temp[] = $total_time;
1282
            }
1283
1284
            $csv_content[] = $temp;
1285
            ob_end_clean();
1286
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1287
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

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