Passed
Push — master ( 15378b...a2dd4f )
by Julito
09:24
created

Tracking::generate_session_exercise_graph()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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