Issues (1870)

public/main/inc/lib/tracking.lib.php (7 issues)

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

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
6541
            ->createQuery("
6542
                SELECT csp
6543
                FROM ChamiloCourseBundle:CStudentPublication csp
6544
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6545
                    WITH (
6546
                        csp.iid = cip.ref AND
6547
                        csp.session = cip.session AND
6548
                        csp.cId = cip.course AND
6549
                        csp.userId = cip.lasteditUserId
6550
                    )
6551
                WHERE
6552
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6553
                ORDER BY csp.iid DESC
6554
            ")
6555
            ->setMaxResults(1)
6556
            ->setParameters([
6557
                'tool' => $tool,
6558
                'session' => $session,
6559
                'course' => $course,
6560
                'user' => $user,
6561
            ])
6562
            ->getOneOrNullResult();
6563
    }
6564
6565
    /**
6566
     * Get the HTML code for show a block with the achieved user skill on course/session.
6567
     *
6568
     * @param int  $userId
6569
     * @param ?int  $courseId
6570
     * @param ?int  $sessionId
6571
     * @param ?bool $forceView forces the view of the skills, not checking for deeper access
6572
     *
6573
     * @return string
6574
     */
6575
    public static function displayUserSkills(int $userId, ?int $courseId = 0, ?int $sessionId = 0, ?bool $forceView = false): string
6576
    {
6577
        if (false === SkillModel::isAllowed($userId, false) && !$forceView) {
6578
            return '';
6579
        }
6580
        $skillManager = new SkillModel();
6581
6582
        return $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6583
    }
6584
6585
    /**
6586
     * Return time spent by the given user in the given course/session, from the track_e_access_complete table, and
6587
     * breakdown time items in different array elements
6588
     * @param int $userId
6589
     * @param int $courseId
6590
     * @param int $sessionId
6591
     *
6592
     * @return array
6593
     * @throws \Doctrine\DBAL\Exception
6594
     */
6595
    public static function getCalculateTime(int $userId, int $courseId, int $sessionId): array
6596
    {
6597
        if (empty($userId) || empty($courseId)) {
6598
            return [];
6599
        }
6600
6601
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6602
                FROM track_e_access_complete
6603
                WHERE
6604
                    user_id = $userId AND
6605
                    c_id = $courseId AND
6606
                    session_id = $sessionId AND
6607
                    login_as = 0
6608
                ORDER BY date_reg ASC
6609
                LIMIT 1";
6610
        $rs = Database::query($sql);
6611
6612
        $firstConnection = '';
6613
        $lastConnection = '';
6614
        if (Database::num_rows($rs) > 0) {
6615
            $value = Database::fetch_array($rs);
6616
            $firstConnection = $value['min'];
6617
            $lastConnection = $value['max'];
6618
        }
6619
6620
        $sql = "SELECT * FROM track_e_access_complete
6621
                WHERE
6622
                    user_id = $userId AND
6623
                    c_id = $courseId AND
6624
                    session_id = $sessionId AND
6625
                    login_as = 0 AND current_id <> 0";
6626
6627
        $res = Database::query($sql);
6628
        $reg = [];
6629
        while ($row = Database::fetch_assoc($res)) {
6630
            $reg[$row['id']] = $row;
6631
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6632
        }
6633
6634
        $sessions = [];
6635
        foreach ($reg as $key => $value) {
6636
            $sessions[$value['current_id']][$value['tool']][] = $value;
6637
        }
6638
6639
        $quizTime = 0;
6640
        $result = [];
6641
        $totalTime = 0;
6642
        $lpTime = [];
6643
        $lpDetailTime = [];
6644
        foreach ($sessions as $listPerTool) {
6645
            $min = 0;
6646
            $max = 0;
6647
            $sessionDiff = 0;
6648
            foreach ($listPerTool as $tool => $results) {
6649
                $beforeItem = [];
6650
                foreach ($results as $item) {
6651
                    if (empty($beforeItem)) {
6652
                        $beforeItem = $item;
6653
                        if (empty($min)) {
6654
                            $min = $item['date_reg'];
6655
                        }
6656
6657
                        if (empty($max)) {
6658
                            $max = $item['date_reg'];
6659
                        }
6660
                        continue;
6661
                    }
6662
6663
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6664
                    if ($item['date_reg'] > $max) {
6665
                        $max = $item['date_reg'];
6666
                    }
6667
6668
                    if (empty($min)) {
6669
                        $min = $item['date_reg'];
6670
                    }
6671
6672
                    if ($item['date_reg'] < $min) {
6673
                        $min = $item['date_reg'];
6674
                    }
6675
6676
                    switch ($tool) {
6677
                        case TOOL_AGENDA:
6678
                        case TOOL_FORUM:
6679
                        case TOOL_ANNOUNCEMENT:
6680
                        case TOOL_COURSE_DESCRIPTION:
6681
                        case TOOL_SURVEY:
6682
                        case TOOL_NOTEBOOK:
6683
                        case TOOL_GRADEBOOK:
6684
                        case TOOL_DROPBOX:
6685
                        case 'Reports':
6686
                        case 'Videoconference':
6687
                        case TOOL_LINK:
6688
                        case TOOL_CHAT:
6689
                        case 'course-main':
6690
                            if (!isset($result[$tool])) {
6691
                                $result[$tool] = 0;
6692
                            }
6693
                            $result[$tool] += $partialTime;
6694
                            break;
6695
                        case TOOL_LEARNPATH:
6696
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6697
                                break;
6698
                            }
6699
                            if (!isset($lpTime[$item['tool_id']])) {
6700
                                $lpTime[$item['tool_id']] = 0;
6701
                            }
6702
6703
                            // Saving the attempt id "action_details"
6704
                            if (!empty($item['tool_id'])) {
6705
                                if (!empty($item['tool_id_detail'])) {
6706
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6707
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6708
                                    }
6709
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6710
                                }
6711
                                $lpTime[$item['tool_id']] += $partialTime;
6712
                            }
6713
                            break;
6714
                        case TOOL_QUIZ:
6715
                            if (!isset($lpTime[$item['action_details']])) {
6716
                                $lpTime[$item['action_details']] = 0;
6717
                            }
6718
                            if ('learnpath_id' === $beforeItem['action']) {
6719
                                $lpTime[$item['action_details']] += $partialTime;
6720
                            } else {
6721
                                $quizTime += $partialTime;
6722
                            }
6723
                            break;
6724
                    }
6725
                    $beforeItem = $item;
6726
                }
6727
            }
6728
6729
            $sessionDiff += $max - $min;
6730
            if ($sessionDiff > 0) {
6731
                $totalTime += $sessionDiff;
6732
            }
6733
        }
6734
6735
        $totalLp = 0;
6736
        foreach ($lpTime as $value) {
6737
            $totalLp += $value;
6738
        }
6739
6740
        $result['learnpath_detailed'] = $lpDetailTime;
6741
        $result[TOOL_LEARNPATH] = $lpTime;
6742
        $result[TOOL_QUIZ] = $quizTime;
6743
        $result['total_learnpath'] = $totalLp;
6744
        $result['total_time'] = $totalTime;
6745
        $result['number_connections'] = count($sessions);
6746
        $result['first'] = $firstConnection;
6747
        $result['last'] = $lastConnection;
6748
6749
        return $result;
6750
    }
6751
6752
    /**
6753
     * Gets the IP of a given user, using the last login before the given date.
6754
     *
6755
     * @param int User ID
6756
     * @param string Datetime
6757
     * @param ?bool Whether to return the IP as a link or just as an IP
6758
     * @param ?string If defined and return_as_link if true, will be used as the text to be shown as the link
6759
     *
6760
     * @return string IP address (or false on error)
6761
     * @assert (0,0) === false
6762
     * @throws \Doctrine\DBAL\Exception
6763
     */
6764
    public static function get_ip_from_user_event(
6765
        int $user_id,
6766
        string $event_date,
6767
        ?bool $return_as_link = false,
6768
        ?string $body_replace = null
6769
    ): mixed {
6770
        if (empty($user_id) || empty($event_date)) {
6771
            return false;
6772
        }
6773
        $user_id = intval($user_id);
6774
        $event_date = Database::escape_string($event_date);
6775
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6776
        $sql_ip = "SELECT login_date, user_ip
6777
                   FROM $table_login
6778
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6779
                   ORDER BY login_date DESC LIMIT 1";
6780
        $ip = '';
6781
        $res_ip = Database::query($sql_ip);
6782
        if (false !== $res_ip && Database::num_rows($res_ip) > 0) {
6783
            $row_ip = Database::fetch_row($res_ip);
6784
            if ($return_as_link) {
6785
                $ip = Display::url(
6786
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6787
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6788
                    ['title' => get_lang('Trace IP'), 'target' => '_blank']
6789
                );
6790
            } else {
6791
                $ip = $row_ip[1];
6792
            }
6793
        }
6794
6795
        return $ip;
6796
    }
6797
6798
    /**
6799
     * @param int   $userId
6800
     * @param array $courseInfo
6801
     * @param ?int   $sessionId
6802
     *
6803
     * @return array
6804
     */
6805
    public static function getToolInformation(
6806
        int $userId,
6807
        array $courseInfo,
6808
        ?int $sessionId = 0
6809
    ): array {
6810
        $csvContent = [];
6811
        $courseToolInformation = '';
6812
        $headerTool = [
6813
            [get_lang('Title')],
6814
            [get_lang('Created at')],
6815
            [get_lang('Updated at')],
6816
        ];
6817
6818
        $headerListForCSV = [];
6819
        foreach ($headerTool as $item) {
6820
            $headerListForCSV[] = $item[0];
6821
        }
6822
6823
        $courseForumInformationArray = getForumCreatedByUser(
6824
            $userId,
6825
            $courseInfo,
6826
            $sessionId
6827
        );
6828
6829
        if (!empty($courseForumInformationArray)) {
6830
            $csvContent[] = [];
6831
            $csvContent[] = [get_lang('Forums')];
6832
            $csvContent[] = $headerListForCSV;
6833
            foreach ($courseForumInformationArray as $row) {
6834
                $csvContent[] = $row;
6835
            }
6836
6837
            $courseToolInformation .= Display::page_subheader2(
6838
                get_lang('Forums')
6839
            );
6840
            $courseToolInformation .= Display::return_sortable_table(
6841
                $headerTool,
6842
                $courseForumInformationArray
6843
            );
6844
        }
6845
6846
        $courseWorkInformationArray = getWorkCreatedByUser(
6847
            $userId,
6848
            $courseInfo['real_id'],
6849
            $sessionId
6850
        );
6851
6852
        if (!empty($courseWorkInformationArray)) {
6853
            $csvContent[] = null;
6854
            $csvContent[] = [get_lang('Assignments')];
6855
            $csvContent[] = $headerListForCSV;
6856
6857
            foreach ($courseWorkInformationArray as $row) {
6858
                $csvContent[] = $row;
6859
            }
6860
            $csvContent[] = null;
6861
6862
            $courseToolInformation .= Display::page_subheader2(
6863
                get_lang('Assignments')
6864
            );
6865
            $courseToolInformation .= Display::return_sortable_table(
6866
                $headerTool,
6867
                $courseWorkInformationArray
6868
            );
6869
        }
6870
6871
        $courseToolInformationTotal = null;
6872
        if (!empty($courseToolInformation)) {
6873
            $sessionTitle = null;
6874
            if (!empty($sessionId)) {
6875
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6876
            }
6877
6878
            $courseToolInformationTotal .= Display::page_subheader(
6879
                $courseInfo['title'].$sessionTitle
6880
            );
6881
            $courseToolInformationTotal .= $courseToolInformation;
6882
        }
6883
6884
        return [
6885
            'array' => $csvContent,
6886
            'html' => $courseToolInformationTotal,
6887
        ];
6888
    }
6889
6890
    /**
6891
     * @param int $sessionId
6892
     *
6893
     * @return bool
6894
     */
6895
    public static function isAllowToTrack($sessionId)
6896
    {
6897
        return
6898
            api_is_platform_admin(true, true) ||
6899
            (!empty($sessionId) && api_get_session_entity($sessionId)->hasUserAsGeneralCoach(api_get_user_entity())) ||
6900
            api_is_allowed_to_create_course() ||
6901
            api_is_course_tutor() ||
6902
            api_is_course_admin();
6903
    }
6904
6905
    public static function getCourseLpProgress($userId, $sessionId)
6906
    {
6907
        $controller = new IndexManager(get_lang('MyCourses'));
6908
        $data = $controller->returnCoursesAndSessions($userId);
6909
        $courseList = $data['courses'];
6910
        $result = [];
6911
        if ($courseList) {
6912
            //$counter = 1;
6913
            foreach ($courseList as $course) {
6914
                $courseId = $course['course_id'];
6915
                $courseInfo = api_get_course_info_by_id($courseId);
6916
                if (empty($courseInfo)) {
6917
                    continue;
6918
                }
6919
                $courseCode = $courseInfo['code'];
6920
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
6921
6922
                // total progress
6923
                $list = new LearnpathList(
6924
                    $userId,
6925
                     $courseInfo,
6926
                    0,
6927
                    'resource.publishedOn ASC',
6928
                    true,
6929
                    null,
6930
                    true
6931
                );
6932
6933
                $list = $list->get_flat_list();
6934
                $totalProgress = 0;
6935
                $totalTime = 0;
6936
                if (!empty($list)) {
6937
                    foreach ($list as $lp_id => $learnpath) {
6938
                        if (!$learnpath['lp_visibility']) {
6939
                            continue;
6940
                        }
6941
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
6942
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
6943
                        if (100 == $lpProgress) {
6944
                            if (!empty($time)) {
6945
                                $timeInMinutes = $time / 60;
6946
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
6947
                                if ($timeInMinutes >= $min) {
6948
                                    $totalProgress++;
6949
                                }
6950
                            }
6951
                        }
6952
                        $totalTime += $time;
6953
                    }
6954
6955
                    if (!empty($totalProgress)) {
6956
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
6957
                    }
6958
                }
6959
6960
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
6961
6962
                $result[] = [
6963
                    'module' => $courseInfo['name'],
6964
                    'progress' => $progress,
6965
                    'qualification' => $totalProgress,
6966
                    'activeTime' => $totalTime,
6967
                ];
6968
            }
6969
        }
6970
6971
        return $result;
6972
    }
6973
6974
    /**
6975
     * @param int $userId
6976
     * @param int $courseId
6977
     * @param int $sessionId
6978
     *
6979
     * @return int
6980
     */
6981
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
6982
    {
6983
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6984
        $sessionCondition = api_get_session_condition($sessionId);
6985
        $courseId = (int) $courseId;
6986
        $userId = (int) $userId;
6987
6988
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
6989
            FROM $tblTrackCourseAccess
6990
            WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
6991
6992
        $result = Database::fetch_assoc(Database::query($sql));
6993
6994
        return (int) $result['c'];
6995
    }
6996
6997
    public static function processUserDataMove(
6998
        $user_id,
6999
        $course_info,
7000
        $origin_session_id,
7001
        $new_session_id,
7002
        $update_database,
7003
        $debug = false
7004
    ) {
7005
        // Begin with the import process
7006
        $origin_course_code = $course_info['code'];
7007
        $course_id = $course_info['real_id'];
7008
        $user_id = (int) $user_id;
7009
        $origin_session_id = (int) $origin_session_id;
7010
        $new_session_id = (int) $new_session_id;
7011
        $session = api_get_session_entity($new_session_id);
7012
        $em = Database::getManager();
7013
7014
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7015
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7016
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7017
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
7018
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
7019
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
7020
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
7021
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
7022
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
7023
7024
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
7025
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
7026
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
7027
7028
        //1. track_e_exercises
7029
        //ORIGINAL COURSE
7030
        $sessionCondition = api_get_session_condition($origin_session_id);
7031
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7032
                WHERE c_id = $course_id AND exe_user_id = $user_id  $sessionCondition";
7033
        $res = Database::query($sql);
7034
        $list = [];
7035
        while ($row = Database::fetch_assoc($res)) {
7036
            $list[$row['exe_id']] = $row;
7037
        }
7038
7039
        $result_message = [];
7040
        $result_message_compare = [];
7041
        if (!empty($list)) {
7042
            foreach ($list as $exe_id => $data) {
7043
                if ($update_database) {
7044
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7045
                    Database::query($sql);
7046
7047
                    //$sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7048
                    //Database::query($sql);
7049
7050
                    $repoTrackQualify = $em->getRepository(TrackEAttemptQualify::class);
7051
                    /** @var TrackEAttemptQualify $trackQualify */
7052
                    $trackQualify = $repoTrackQualify->findBy([
7053
                        'exeId' => $exe_id
7054
                    ]);
7055
                    if ($trackQualify) {
7056
                        $trackQualify->setSessionId($new_session_id);
7057
                        $em->persist($trackQualify);
7058
                        $em->flush();
7059
                    }
7060
7061
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
7062
                        $result_message[$TABLETRACK_EXERCICES] = 0;
7063
                    }
7064
                    $result_message[$TABLETRACK_EXERCICES]++;
7065
                } else {
7066
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7067
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
7068
                    } else {
7069
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7070
                    }
7071
                }
7072
            }
7073
        }
7074
7075
        // DESTINY COURSE
7076
        if (!$update_database) {
7077
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7078
                    WHERE
7079
                        c_id = $course_id AND
7080
                        session_id = $new_session_id AND
7081
                        exe_user_id = $user_id ";
7082
            $res = Database::query($sql);
7083
            $list = [];
7084
            while ($row = Database::fetch_assoc($res)) {
7085
                $list[$row['exe_id']] = $row;
7086
            }
7087
7088
            if (!empty($list)) {
7089
                foreach ($list as $exe_id => $data) {
7090
                    if ($update_database) {
7091
                        $sql = "UPDATE $TABLETRACK_EXERCICES
7092
                                SET session_id = '$new_session_id'
7093
                                WHERE exe_id = $exe_id";
7094
                        Database::query($sql);
7095
                        $result_message[$TABLETRACK_EXERCICES]++;
7096
                    } else {
7097
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7098
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
7099
                        } else {
7100
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7101
                        }
7102
                    }
7103
                }
7104
            }
7105
        }
7106
7107
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
7108
        // Nothing to do because there are not relationship with a session
7109
        // 3. track_e_course_access
7110
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
7111
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
7112
        $res = Database::query($sql);
7113
        $list = [];
7114
        while ($row = Database::fetch_assoc($res)) {
7115
            $list[$row['course_access_id']] = $row;
7116
        }
7117
7118
        if (!empty($list)) {
7119
            foreach ($list as $id => $data) {
7120
                if ($update_database) {
7121
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
7122
                            SET session_id = $new_session_id
7123
                            WHERE course_access_id = $id";
7124
                    if ($debug) {
7125
                        echo $sql;
7126
                    }
7127
                    Database::query($sql);
7128
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
7129
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
7130
                    }
7131
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
7132
                }
7133
            }
7134
        }
7135
7136
        // 4. track_e_lastaccess
7137
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
7138
                WHERE
7139
                    c_id = $course_id AND
7140
                    session_id = $origin_session_id AND
7141
                    access_user_id = $user_id ";
7142
        $res = Database::query($sql);
7143
        $list = [];
7144
        while ($row = Database::fetch_assoc($res)) {
7145
            $list[] = $row['access_id'];
7146
        }
7147
7148
        if (!empty($list)) {
7149
            foreach ($list as $id) {
7150
                if ($update_database) {
7151
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
7152
                            SET session_id = $new_session_id
7153
                            WHERE access_id = $id";
7154
                    if ($debug) {
7155
                        echo $sql;
7156
                    }
7157
                    Database::query($sql);
7158
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
7159
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
7160
                    }
7161
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
7162
                }
7163
            }
7164
        }
7165
7166
        // 5. lp_item_view
7167
        // CHECK ORIGIN
7168
        $sql = "SELECT * FROM $TBL_LP_VIEW
7169
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
7170
        $res = Database::query($sql);
7171
7172
        // Getting the list of LPs in the new session
7173
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
7174
        $flat_list = $lp_list->get_flat_list();
7175
        $list = [];
7176
        while ($row = Database::fetch_assoc($res)) {
7177
            // Checking if the LP exist in the new session
7178
            //if (in_array($row['lp_id'], array_keys($flat_list))) {
7179
            $list[$row['id']] = $row;
7180
            //}
7181
        }
7182
7183
        if (!empty($list)) {
7184
            foreach ($list as $id => $data) {
7185
                if ($update_database) {
7186
                    $sql = "UPDATE $TBL_LP_VIEW
7187
                            SET session_id = $new_session_id
7188
                            WHERE c_id = $course_id AND iid = $id ";
7189
                    if ($debug) {
7190
                        var_dump($sql);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($sql) looks like debug code. Are you sure you do not want to remove it?
Loading history...
7191
                    }
7192
                    $res = Database::query($sql);
7193
                    if ($debug) {
7194
                        var_dump($res);
7195
                    }
7196
                    if (!isset($result_message[$TBL_LP_VIEW])) {
7197
                        $result_message[$TBL_LP_VIEW] = 0;
7198
                    }
7199
                    $result_message[$TBL_LP_VIEW]++;
7200
                } else {
7201
                    // Getting all information of that lp_item_id
7202
                    $score = self::get_avg_student_score(
7203
                        $user_id,
7204
                        $origin_course_code,
7205
                        [$data['lp_id']],
7206
                        $origin_session_id
7207
                    );
7208
                    $progress = self::get_avg_student_progress(
7209
                        $user_id,
7210
                        $origin_course_code,
7211
                        [$data['lp_id']],
7212
                        $origin_session_id
7213
                    );
7214
                    $result_message['LP_VIEW'][$data['lp_id']] = [
7215
                        'score' => $score,
7216
                        'progress' => $progress,
7217
                    ];
7218
                }
7219
            }
7220
        }
7221
7222
        // Check destination.
7223
        if (!$update_database) {
7224
            $sql = "SELECT * FROM $TBL_LP_VIEW
7225
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
7226
            $res = Database::query($sql);
7227
7228
            // Getting the list of LPs in the new session
7229
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
7230
            $flat_list = $lp_list->get_flat_list();
7231
7232
            $list = [];
7233
            while ($row = Database::fetch_assoc($res)) {
7234
                //Checking if the LP exist in the new session
7235
                //if (in_array($row['lp_id'], array_keys($flat_list))) {
7236
                $list[$row['id']] = $row;
7237
                //}
7238
            }
7239
7240
            if (!empty($list)) {
7241
                foreach ($list as $id => $data) {
7242
                    // Getting all information of that lp_item_id
7243
                    $score = self::get_avg_student_score(
7244
                        $user_id,
7245
                        $origin_course_code,
7246
                        [$data['lp_id']],
7247
                        $new_session_id
7248
                    );
7249
                    $progress = self::get_avg_student_progress(
7250
                        $user_id,
7251
                        $origin_course_code,
7252
                        [$data['lp_id']],
7253
                        $new_session_id
7254
                    );
7255
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
7256
                        'score' => $score,
7257
                        'progress' => $progress,
7258
                    ];
7259
                }
7260
            }
7261
        }
7262
7263
        // 6. Agenda
7264
        // calendar_event_attachment no problems no session_id
7265
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
7266
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
7267
        $res = Database::query($sql);
7268
        while ($row = Database::fetch_assoc($res)) {
7269
            $id = $row['ref'];
7270
            if ($update_database) {
7271
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND iid = $id ";
7272
                if ($debug) {
7273
                    var_dump($sql);
7274
                }
7275
                $res_update = Database::query($sql);
7276
                if ($debug) {
7277
                    var_dump($res_update);
7278
                }
7279
                if (!isset($result_message['agenda'])) {
7280
                    $result_message['agenda'] = 0;
7281
                }
7282
                $result_message['agenda']++;
7283
            }
7284
        }
7285
7286
        // 7. Forum ?? So much problems when trying to import data
7287
        // 8. Student publication - Works
7288
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
7289
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
7290
        if ($debug) {
7291
            echo $sql;
7292
        }
7293
        $res = Database::query($sql);
7294
        while ($row = Database::fetch_assoc($res)) {
7295
            $id = $row['ref'];
7296
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7297
                    WHERE iid = $id AND session_id = $origin_session_id AND c_id = $course_id";
7298
            $sub_res = Database::query($sql);
7299
            if (Database::num_rows($sub_res) > 0) {
7300
                $data = Database::fetch_assoc($sub_res);
7301
                if ($debug) {
7302
                    var_dump($data);
7303
                }
7304
                $parent_id = $data['parent_id'];
7305
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
7306
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7307
                            WHERE iid = $parent_id AND c_id = $course_id";
7308
                    $select_res = Database::query($sql);
7309
                    $parent_data = Database::fetch_assoc($select_res);
7310
                    if ($debug) {
7311
                        var_dump($parent_data);
7312
                    }
7313
7314
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
7315
                    $course_dir = $sys_course_path.$course_info['path'];
7316
                    $base_work_dir = $course_dir.'/work';
7317
7318
                    // Creating the parent folder in the session if does not exists already
7319
                    //@todo ugly fix
7320
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
7321
                    $search_this2 = $parent_data['url'];
7322
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7323
                            WHERE description like '%$search_this%' AND
7324
                                  url LIKE '%$search_this2%' AND
7325
                                  session_id = $new_session_id AND
7326
                                  c_id = $course_id
7327
                            ORDER BY id desc  LIMIT 1";
7328
                    if ($debug) {
7329
                        echo $sql;
7330
                    }
7331
                    $sub_res = Database::query($sql);
7332
                    $num_rows = Database::num_rows($sub_res);
7333
7334
                    $new_parent_id = 0;
7335
                    if ($num_rows > 0) {
7336
                        $new_result = Database::fetch_assoc($sub_res);
7337
                        $created_dir = $new_result['url'];
7338
                        $new_parent_id = $new_result['id'];
7339
                    } else {
7340
                        if ($update_database) {
7341
                            $dir_name = substr($parent_data['url'], 1);
7342
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
7343
                            $created_dir = '/'.$created_dir;
7344
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
7345
                            // Creating directory
7346
                            $publication = (new CStudentPublication())
7347
                                ->setTitle($parent_data['title'])
7348
                                ->setDescription(
7349
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
7350
                                )
7351
                                ->setActive(false)
7352
                                ->setAccepted(true)
7353
                                ->setPostGroupId(0)
7354
                                ->setHasProperties($parent_data['has_properties'])
7355
                                ->setWeight($parent_data['weight'])
7356
                                ->setContainsFile($parent_data['contains_file'])
7357
                                ->setFiletype('folder')
7358
                                ->setSentDate($now)
7359
                                ->setQualification($parent_data['qualification'])
7360
                                ->setParentId(0)
7361
                                ->setQualificatorId(0)
7362
                                ->setUserId($parent_data['user_id'])
7363
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
7364
                                ->setSession($session);
7365
7366
                            $publication->setDocumentId($parent_data['document_id']);
7367
7368
                            Database::getManager()->persist($publication);
7369
                            Database::getManager()->flush();
7370
                            $id = $publication->getIid();
7371
                            //Folder created
7372
                            //api_item_property_update($course_info, 'work', $id, 'DirectoryCreated', api_get_user_id());
7373
                            $new_parent_id = $id;
7374
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
7375
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
7376
                            }
7377
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
7378
                        }
7379
                    }
7380
7381
                    //Creating student_publication_assignment if exists
7382
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
7383
                            WHERE publication_id = $parent_id AND c_id = $course_id";
7384
                    if ($debug) {
7385
                        var_dump($sql);
7386
                    }
7387
                    $rest_select = Database::query($sql);
7388
                    if (Database::num_rows($rest_select) > 0) {
7389
                        if ($update_database && $new_parent_id) {
7390
                            $assignment_data = Database::fetch_assoc($rest_select);
7391
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
7392
                                    	c_id = '$course_id',
7393
                                       expires_on          = '".$assignment_data['expires_on']."',
7394
                                       ends_on              = '".$assignment_data['ends_on']."',
7395
                                       add_to_calendar      = '".$assignment_data['add_to_calendar']."',
7396
                                       enable_qualification = '".$assignment_data['enable_qualification']."',
7397
                                       publication_id       = '".$new_parent_id."'";
7398
                            if ($debug) {
7399
                                echo $sql_add_publication;
7400
                            }
7401
                            Database::query($sql_add_publication);
7402
                            $id = (int) Database::insert_id();
7403
                            if ($id) {
7404
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
7405
                                           SET  has_properties = '".$id."',
7406
                                                view_properties = '1'
7407
                                           WHERE iid = ".$new_parent_id;
7408
                                if ($debug) {
7409
                                    echo $sql_update;
7410
                                }
7411
                                Database::query($sql_update);
7412
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
7413
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
7414
                                }
7415
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
7416
                            }
7417
                        }
7418
                    }
7419
7420
                    $doc_url = $data['url'];
7421
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
7422
7423
                    if ($update_database) {
7424
                        // Creating a new work
7425
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
7426
7427
                        $data['post_group_id'] = (int) $data['post_group_id'];
7428
                        $publication = (new CStudentPublication())
7429
                            ->setTitle($data['title'])
7430
                            ->setDescription($data['description'].' file moved')
7431
                            ->setActive($data['active'])
7432
                            ->setAccepted($data['accepted'])
7433
                            ->setPostGroupId($data['post_group_id'])
7434
                            ->setSentDate($data['sent_date'])
7435
                            ->setParentId($new_parent_id)
7436
                            ->setWeight($data['weight'])
7437
                            ->setHasProperties(0)
7438
                            ->setWeight($data['weight'])
7439
                            ->setContainsFile($data['contains_file'])
7440
                            ->setSession($session)
7441
                            ->setUserId($data['user_id'])
7442
                            ->setFiletype('file')
7443
                            ->setDocumentId(0)
7444
                        ;
7445
7446
                        $em->persist($publication);
7447
                        $em->flush();
7448
7449
                        $id = $publication->getIid();
7450
                        /*api_item_property_update(
7451
                            $course_info,
7452
                            'work',
7453
                            $id,
7454
                            'DocumentAdded',
7455
                            $user_id,
7456
                            null,
7457
                            null,
7458
                            null,
7459
                            null,
7460
                            $new_session_id
7461
                        );*/
7462
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
7463
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
7464
                        }
7465
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
7466
                        $full_file_name = $course_dir.'/'.$doc_url;
7467
                        $new_file = $course_dir.'/'.$new_url;
7468
7469
                        if (file_exists($full_file_name)) {
7470
                            // deleting old assignment
7471
                            $result = copy($full_file_name, $new_file);
7472
                            if ($result) {
7473
                                unlink($full_file_name);
7474
                                if (isset($data['id'])) {
7475
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
7476
                                    if ($debug) {
7477
                                        var_dump($sql);
7478
                                    }
7479
                                    Database::query($sql);
7480
                                }
7481
                                api_item_property_update(
7482
                                    $course_info,
7483
                                    'work',
7484
                                    $data['id'],
7485
                                    'DocumentDeleted',
7486
                                    api_get_user_id()
7487
                                );
7488
                            }
7489
                        }
7490
                    }
7491
                }
7492
            }
7493
        }
7494
7495
        //9. Survey   Pending
7496
        //10. Dropbox - not neccesary to move categories (no presence of session_id)
7497
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
7498
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
7499
        if ($debug) {
7500
            var_dump($sql);
7501
        }
7502
        $res = Database::query($sql);
7503
        while ($row = Database::fetch_assoc($res)) {
7504
            $id = (int) $row['id'];
7505
            if ($update_database) {
7506
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND iid = $id";
7507
                if ($debug) {
7508
                    var_dump($sql);
7509
                }
7510
                Database::query($sql);
7511
                if ($debug) {
7512
                    var_dump($res);
7513
                }
7514
7515
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
7516
                if ($debug) {
7517
                    var_dump($sql);
7518
                }
7519
                Database::query($sql);
7520
                if ($debug) {
7521
                    var_dump($res);
7522
                }
7523
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
7524
                    $result_message[$TBL_DROPBOX_FILE] = 0;
7525
                }
7526
                $result_message[$TBL_DROPBOX_FILE]++;
7527
            }
7528
        }
7529
7530
        // 11. Notebook
7531
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
7532
                WHERE
7533
                    user_id = $user_id AND
7534
                    session_id = $origin_session_id AND
7535
                    course = '$origin_course_code' AND
7536
                    c_id = $course_id";
7537
        if ($debug) {
7538
            var_dump($sql);
7539
        }
7540
        $res = Database::query($sql);
7541
        while ($row = Database::fetch_assoc($res)) {
7542
            $id = $row['notebook_id'];
7543
            if ($update_database) {
7544
                $sql = "UPDATE $TBL_NOTEBOOK
7545
                        SET session_id = $new_session_id
7546
                        WHERE c_id = $course_id AND notebook_id = $id";
7547
                if ($debug) {
7548
                    var_dump($sql);
7549
                }
7550
                $res = Database::query($sql);
7551
                if ($debug) {
7552
                    var_dump($res);
7553
                }
7554
            }
7555
        }*/
7556
7557
        if ($update_database) {
7558
            echo Display::return_message(get_lang('StatsMoved'));
7559
            if (is_array($result_message)) {
7560
                foreach ($result_message as $table => $times) {
7561
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
7562
                }
7563
            }
7564
        } else {
7565
            echo '<h4>'.get_lang('UserInformationOfThisCourse').'</h4>';
7566
            echo '<br />';
7567
            echo '<table class="table" width="100%">';
7568
            echo '<tr>';
7569
            echo '<td width="50%" valign="top">';
7570
7571
            if (0 == $origin_session_id) {
7572
                echo '<h5>'.get_lang('OriginCourse').'</h5>';
7573
            } else {
7574
                echo '<h5>'.get_lang('OriginSession').' #'.$origin_session_id.'</h5>';
7575
            }
7576
            self::compareUserData($result_message);
7577
            echo '</td>';
7578
            echo '<td width="50%" valign="top">';
7579
            if (0 == $new_session_id) {
7580
                echo '<h5>'.get_lang('DestinyCourse').'</h5>';
7581
            } else {
7582
                echo '<h5>'.get_lang('DestinySession').' #'.$new_session_id.'</h5>';
7583
            }
7584
            self::compareUserData($result_message_compare);
7585
            echo '</td>';
7586
            echo '</tr>';
7587
            echo '</table>';
7588
        }
7589
    }
7590
7591
    public static function compareUserData($result_message)
7592
    {
7593
        foreach ($result_message as $table => $data) {
7594
            $title = $table;
7595
            if ('TRACK_E_EXERCISES' === $table) {
7596
                $title = get_lang('Exercises');
7597
            } elseif ('TRACK_E_EXERCISES_IN_LP' === $table) {
7598
                $title = get_lang('ExercisesInLp');
7599
            } elseif ('LP_VIEW' === $table) {
7600
                $title = get_lang('LearningPaths');
7601
            }
7602
            echo '<br / ><h3>'.get_lang($title).' </h3><hr />';
7603
7604
            if (is_array($data)) {
7605
                foreach ($data as $id => $item) {
7606
                    if ('TRACK_E_EXERCISES' === $table || 'TRACK_E_EXERCISES_IN_LP' === $table) {
7607
                        echo "<br /><h3>".get_lang('Attempt')." #$id</h3>";
7608
                        echo '<h3>';
7609
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
7610
                        echo '</h3>';
7611
                        if (!empty($item['orig_lp_id'])) {
7612
                            echo '<h3>';
7613
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
7614
                            echo '</h3>';
7615
                        }
7616
                        // Process data.
7617
                        $array = [
7618
                            'exe_date' => get_lang('Date'),
7619
                            'score' => get_lang('Score'),
7620
                            'max_score' => get_lang('Weighting'),
7621
                        ];
7622
                        foreach ($item as $key => $value) {
7623
                            if (in_array($key, array_keys($array))) {
7624
                                $key = $array[$key];
7625
                                echo "$key =  $value <br />";
7626
                            }
7627
                        }
7628
                    } else {
7629
                        echo "<br /><h3>".get_lang('Id')." #$id</h3>";
7630
                        // process data
7631
                        foreach ($item as $key => $value) {
7632
                            echo "$key =  $value <br />";
7633
                        }
7634
                    }
7635
                }
7636
            } else {
7637
                echo get_lang('NoResults');
7638
            }
7639
        }
7640
    }
7641
7642
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
7643
    {
7644
        if (empty($sessionId)) {
7645
            $userList = CourseManager::get_user_list_from_course_code(
7646
                $courseInfo['code'],
7647
                $sessionId,
7648
                null,
7649
                null,
7650
                STUDENT
7651
            );
7652
        } else {
7653
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
7654
        }
7655
7656
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null);
7657
7658
        if (empty($exerciseList)) {
7659
            return Display::return_message(get_lang('NoEx'));
7660
        }
7661
7662
        $toGraphExerciseResult = [];
7663
7664
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
7665
        $quizzesTable->setHeaders(
7666
            [
7667
                get_lang('Exercises'),
7668
                get_lang('Attempts'),
7669
                get_lang('BestAttempt'),
7670
                get_lang('Ranking'),
7671
                get_lang('BestResultInCourse'),
7672
                get_lang('Statistics').Display::getMdiIcon(
7673
                    ActionIcon::INFORMATION,
7674
                    'ch-tool-icon',
7675
                    null,
7676
                    ICON_SIZE_SMALL,
7677
                    get_lang('OnlyBestResultsPerStudent')
7678
                ),
7679
            ]
7680
        );
7681
7682
        $webCodePath = api_get_path(WEB_CODE_PATH);
7683
7684
        foreach ($exerciseList as $exercices) {
7685
            $objExercise = new Exercise($courseInfo['real_id']);
7686
            $objExercise->read($exercices['id']);
7687
            $visibleReturn = $objExercise->is_visible();
7688
7689
            // Getting count of attempts by user
7690
            $attempts = Event::count_exercise_attempts_by_user(
7691
                api_get_user_id(),
7692
                $exercices['id'],
7693
                $courseInfo['real_id'],
7694
                $sessionId
7695
            );
7696
7697
            $url = $webCodePath.'exercise/overview.php?'
7698
                .http_build_query(
7699
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['id']]
7700
                );
7701
7702
            if (true == $visibleReturn['value']) {
7703
                $exercices['title'] = Display::url(
7704
                    $exercices['title'],
7705
                    $url,
7706
                    ['target' => SESSION_LINK_TARGET]
7707
                );
7708
            } elseif (-1 == $exercices['active']) {
7709
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
7710
            }
7711
7712
            $quizData = [
7713
                $exercices['title'],
7714
                $attempts,
7715
                '-',
7716
                '-',
7717
                '-',
7718
                '-',
7719
            ];
7720
7721
            // Exercise configuration show results or show only score
7722
            if (!in_array($exercices['results_disabled'], [0, 2])
7723
                || empty($attempts)
7724
            ) {
7725
                $quizzesTable->addRow($quizData);
7726
7727
                continue;
7728
            }
7729
7730
            //For graphics
7731
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
7732
                $exercices['id'],
7733
                $courseInfo['real_id'],
7734
                $sessionId
7735
            );
7736
7737
            $toGraphExerciseResult[$exercices['id']] = [
7738
                'title' => $exercices['title'],
7739
                'data' => $bestExerciseAttempts,
7740
            ];
7741
7742
            // Getting best results
7743
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
7744
                $exercices['id'],
7745
                $courseInfo['real_id'],
7746
                $sessionId
7747
            );
7748
7749
            if (!empty($bestScoreData)) {
7750
                $quizData[5] = ExerciseLib::show_score(
7751
                    $bestScoreData['score'],
7752
                    $bestScoreData['max_score']
7753
                );
7754
            }
7755
7756
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
7757
                api_get_user_id(),
7758
                $exercices['id'],
7759
                $courseInfo['real_id'],
7760
                $sessionId
7761
            );
7762
7763
            if (!empty($exerciseAttempt)) {
7764
                // Always getting the BEST attempt
7765
                $score = $exerciseAttempt['score'];
7766
                $weighting = $exerciseAttempt['max_score'];
7767
                $exeId = $exerciseAttempt['exe_id'];
7768
7769
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
7770
                    .http_build_query(
7771
                        [
7772
                            'id' => $exeId,
7773
                            'cidReq' => $courseInfo['code'],
7774
                            'show_headers' => 1,
7775
                            'id_session' => $sessionId,
7776
                        ]
7777
                    );
7778
7779
                $quizData[3] = Display::url(
7780
                    ExerciseLib::show_score($score, $weighting),
7781
                    $latestAttemptUrl
7782
                );
7783
7784
                $myScore = !empty($weighting) && 0 != intval($weighting) ? $score / $weighting : 0;
7785
7786
                //@todo this function slows the page
7787
                if (is_int($userList)) {
7788
                    $userList = [$userList];
7789
                }
7790
7791
                $quizData[4] = ExerciseLib::get_exercise_result_ranking(
7792
                    $myScore,
7793
                    $exeId,
7794
                    $exercices['id'],
7795
                    $courseInfo['code'],
7796
                    $sessionId,
7797
                    $userList
7798
                );
7799
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['id']]);
7800
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['id']]);
7801
7802
                $quizData[6] = Display::url(
7803
                    Display::img($graph, '', [], false),
7804
                    $normalGraph,
7805
                    ['id' => $exercices['id'], 'class' => 'expand-image']
7806
                );
7807
            }
7808
7809
            $quizzesTable->addRow($quizData);
7810
        }
7811
7812
        return Display::div(
7813
            $quizzesTable->toHtml(),
7814
            ['class' => 'table-responsive']
7815
        );
7816
    }
7817
7818
    private static function generateLearningPathsTable(int $userId, array $courseInfo, int $sessionId = 0): string
7819
    {
7820
        $columnHeaders = [
7821
            'lp' => get_lang('LearningPath'),
7822
            'time' => get_lang('LatencyTimeSpent'),
7823
            'progress' => get_lang('Progress'),
7824
            'score' => get_lang('Score'),
7825
            'best_score' => get_lang('BestScore'),
7826
            'last_connection' => get_lang('LastConnexion'),
7827
        ];
7828
7829
        $trackingColumns = api_get_setting('session.tracking_columns', true);
7830
7831
        if (isset($trackingColumns['my_progress_lp'])) {
7832
            $columnHeaders = array_filter(
7833
                $columnHeaders,
7834
                function ($columHeader, $key) use ($trackingColumns) {
7835
                    if (!isset($trackingColumns['my_progress_lp'][$key])
7836
                        || false == $trackingColumns['my_progress_lp'][$key]
7837
                    ) {
7838
                        return false;
7839
                    }
7840
7841
                    return true;
7842
                },
7843
                ARRAY_FILTER_USE_BOTH
7844
            );
7845
        }
7846
7847
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
7848
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
7849
        }
7850
7851
        if ('true' === api_get_setting('lp.student_follow_page_add_lp_acquisition_info')) {
7852
            $columnHeaders['student_follow_page_add_lp_acquisition_info'] = get_lang('Acquisition');
7853
        }
7854
7855
        $addLpInvisibleCheckbox = api_get_setting('lp.student_follow_page_add_lp_invisible_checkbox');
7856
7857
        $columnHeadersKeys = array_keys($columnHeaders);
7858
7859
        $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
7860
        $learningpathsTable->setHeaders($columnHeaders);
7861
7862
        // LP table results
7863
        $list = new LearnpathList(
7864
            api_get_user_id(),
7865
            $courseInfo,
7866
            $sessionId,
7867
            'resource.publishedOn ASC',
7868
            true,
7869
            null,
7870
            true
7871
        );
7872
7873
        $lpList = $list->get_flat_list();
7874
7875
        if (empty($lpList)) {
7876
            return Display::return_message(get_lang('NoLearnpath'));
7877
        }
7878
7879
        $webCodePath = api_get_path(WEB_CODE_PATH);
7880
7881
        foreach ($lpList as $lpId => $learnpath) {
7882
            $learningpathData = [];
7883
7884
            if (!$learnpath['lp_visibility']) {
7885
                continue;
7886
            }
7887
7888
            if ($addLpInvisibleCheckbox) {
7889
                if (!StudentFollowPage::isViewVisible($lpId, $userId, $courseInfo['real_id'], $sessionId)) {
7890
                    continue;
7891
                }
7892
            }
7893
7894
            $url = $webCodePath.'lp/lp_controller.php?'
7895
                .http_build_query(
7896
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
7897
                );
7898
7899
            if (in_array('lp', $columnHeadersKeys)) {
7900
                if (0 == $learnpath['lp_visibility']) {
7901
                    $learningpathData[] = $learnpath['lp_name'];
7902
                } else {
7903
                    $learningpathData[] = Display::url(
7904
                        $learnpath['lp_name'],
7905
                        $url,
7906
                        ['target' => SESSION_LINK_TARGET]
7907
                    );
7908
                }
7909
            }
7910
7911
            if (in_array('time', $columnHeadersKeys)) {
7912
                $time_spent_in_lp = self::get_time_spent_in_lp(
7913
                    $userId,
7914
                    $courseInfo['code'],
7915
                    [$lpId],
7916
                    $sessionId
7917
                );
7918
7919
                $learningpathData[] = api_time_to_hms($time_spent_in_lp);
7920
            }
7921
7922
            if (in_array('progress', $columnHeadersKeys)) {
7923
                $progress = self::get_avg_student_progress(
7924
                    $userId,
7925
                    $courseInfo['code'],
7926
                    [$lpId],
7927
                    $sessionId
7928
                );
7929
7930
                if (is_numeric($progress)) {
7931
                    $progress = sprintf(get_lang('XPercent'), $progress);
7932
                }
7933
7934
                $learningpathData[] = $progress;
7935
            }
7936
7937
            if (in_array('score', $columnHeadersKeys)) {
7938
                $percentage_score = self::get_avg_student_score(
7939
                    $userId,
7940
                    $courseInfo['code'],
7941
                    [$lpId],
7942
                    $sessionId
7943
                );
7944
7945
                if (is_numeric($percentage_score)) {
7946
                    $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
7947
                } else {
7948
                    $percentage_score = sprintf(get_lang('XPercent'), 0);
7949
                }
7950
7951
                $learningpathData[] = $percentage_score;
7952
            }
7953
7954
            if (in_array('best_score', $columnHeadersKeys)) {
7955
                $bestScore = self::get_avg_student_score(
7956
                    $userId,
7957
                    $courseInfo['code'],
7958
                    [$lpId],
7959
                    $sessionId,
7960
                    false,
7961
                    false,
7962
                    true
7963
                );
7964
7965
                if (is_numeric($bestScore)) {
7966
                    $bestScore = sprintf(get_lang('XPercent'), $bestScore);
7967
                } else {
7968
                    $bestScore = '-';
7969
                }
7970
7971
                $learningpathData[] = $bestScore;
7972
            }
7973
7974
            if (in_array('last_connection', $columnHeadersKeys)) {
7975
                $lastConnectionInLp = self::get_last_connection_time_in_lp(
7976
                    $userId,
7977
                    $courseInfo['code'],
7978
                    $lpId,
7979
                    $sessionId
7980
                );
7981
7982
                $lastConnection = '-';
7983
7984
                if (!empty($lastConnectionInLp)) {
7985
                    $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
7986
                }
7987
7988
                $learningpathData[] = $lastConnection;
7989
            }
7990
7991
            if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
7992
                $learningpathData[] = StudentFollowPage::getLpSubscription(
7993
                    $learnpath,
7994
                    $userId,
7995
                    $courseInfo['real_id'],
7996
                    $sessionId
7997
                );
7998
            }
7999
8000
            if (in_array('student_follow_page_add_lp_acquisition_info', $columnHeadersKeys)) {
8001
                $learningpathData[] = StudentFollowPage::getLpAcquisition(
8002
                    $learnpath,
8003
                    $userId,
8004
                    $courseInfo['real_id'],
8005
                    $sessionId
8006
                );
8007
            }
8008
8009
            $learningpathsTable->addRow($learningpathData);
8010
        }
8011
8012
        return Display::div(
8013
            $learningpathsTable->toHtml(),
8014
            ['class' => 'table-responsive']
8015
        );
8016
    }
8017
8018
    /**
8019
     * Counts the number of student publications for a given course, session, and group.
8020
     *
8021
     * @param int $courseId
8022
     * @param int|null $sessionId
8023
     * @param int|null $groupId
8024
     *
8025
     * @return int The number of student publications.
8026
     */
8027
    public static function countStudentPublications(int $courseId, ?int $sessionId = null, ?int $groupId = null): int
8028
    {
8029
        $repo = Container::getStudentPublicationRepository();
8030
8031
        $course = api_get_course_entity($courseId);
8032
        $session = api_get_session_entity($sessionId);
8033
        $group = api_get_group_entity($groupId);
8034
8035
        $qb = $repo->getResourcesByCourse($course, $session, $group);
8036
        $qb->select('COUNT(resource.iid)');
8037
8038
        return (int) $qb->getQuery()->getSingleScalarResult();
8039
    }
8040
8041
    /**
8042
     * Counts the number of forum posts for a given course, session, and group.
8043
     *
8044
     * @param int $courseId
8045
     * @param int|null $sessionId
8046
     * @param int|null $groupId
8047
     *
8048
     * @return int The number of forum posts.
8049
     */
8050
    public static function countStudentMessages(int $courseId, ?int $sessionId = null, ?int $groupId = null): int
8051
    {
8052
        $repo = Container::getForumPostRepository();
8053
8054
        $course = api_get_course_entity($courseId);
8055
        $session = api_get_session_entity($sessionId);
8056
        $group = api_get_group_entity($groupId);
8057
8058
        $qb = $repo->getResourcesByCourse($course, $session, $group);
8059
        $qb->select('COUNT(resource.iid)');
8060
8061
        return (int) $qb->getQuery()->getSingleScalarResult();
8062
    }
8063
8064
    /**
8065
     * It gets the last finalization date of learnpaths in a course.
8066
     *
8067
     * @return string finalization date formatted or false if it is empty.
8068
     */
8069
    public static function getCourseLpFinalizationDate(
8070
        int $userId,
8071
        int $courseId,
8072
        int $sessionId,
8073
        bool $convertDate = true
8074
    ) {
8075
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
8076
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
8077
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
8078
8079
        $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date
8080
                FROM $tblLpItemView liv
8081
                INNER JOIN
8082
                    $tblLpView lv ON lv.iid = liv.lp_view_id
8083
                INNER JOIN
8084
                    $tblLpItem li ON li.iid = liv.lp_item_id
8085
                WHERE
8086
                    lv.user_id = $userId AND
8087
                    lv.c_id = $courseId AND
8088
                    lv.session_id = $sessionId AND
8089
                    li.item_type = '".TOOL_LP_FINAL_ITEM."' AND
8090
                    liv.status = 'completed'
8091
                ORDER BY start_date DESC
8092
                LIMIT 1";
8093
8094
        $rs = Database::query($sql);
8095
        $lpFinalDate = Database::result($rs, 0, 0);
8096
8097
        if (empty($lpFinalDate)) {
8098
            return false;
8099
        }
8100
8101
        if ($convertDate) {
8102
            return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT);
8103
        }
8104
8105
        return $lpFinalDate;
8106
    }
8107
8108
    /**
8109
     * It gets the last finalization date of exercises in a course.
8110
     *
8111
     * @return string finalization date formatted or false if it is empty.
8112
     */
8113
    public static function getCourseQuizLastFinalizationDate(
8114
        int $userId,
8115
        int $courseId,
8116
        int $sessionId,
8117
        bool $convertDate = true
8118
    ) {
8119
        $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
8120
8121
        $sql = "SELECT ex.exe_date
8122
                FROM $tblTrackExercise AS ex
8123
                WHERE
8124
                    ex.c_id = $courseId AND
8125
                    ex.session_id  = $sessionId AND
8126
                    ex.exe_user_id = $userId AND
8127
                    ex.status = ''
8128
                ORDER BY ex.exe_date DESC
8129
                LIMIT 1";
8130
        $rs = Database::query($sql);
8131
        $exeDate = Database::result($rs, 0, 0);
8132
8133
        if (empty($exeDate)) {
8134
            return false;
8135
        }
8136
8137
        if ($convertDate) {
8138
            return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT);
8139
        }
8140
8141
        return $exeDate;
8142
    }
8143
8144
    /**
8145
     * Return the total time spent in courses (no the total in platform).
8146
     *
8147
     * @return int
8148
     * @throws \Doctrine\DBAL\Exception
8149
     * @throws Exception
8150
     */
8151
    public static function getTotalTimeSpentInCourses(
8152
        string $dateFrom = '',
8153
        string $dateUntil = ''
8154
    ): int {
8155
        $tableTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
8156
        $tableUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8157
        $tableUrl = null;
8158
        $urlCondition = null;
8159
        $conditionTime = null;
8160
        $accessUrlUtil = Container::getAccessUrlUtil();
8161
        if ($accessUrlUtil->isMultiple()) {
8162
            $accessUrlId = $accessUrlUtil->getCurrent()->getId();
8163
            $tableUrl = ", ".$tableUrlRelUser." as url_users";
8164
            $urlCondition = " AND u.user_id = url_users.user_id AND access_url_id = $accessUrlId";
8165
        }
8166
        if (!empty($dateFrom) && !empty($dateUntil)) {
8167
            $dateFrom = Database::escape_string($dateFrom);
8168
            $dateUntil = Database::escape_string($dateUntil);
8169
            $conditionTime = " (login_course_date >= '$dateFrom' AND logout_course_date <= '$dateUntil' ) ";
8170
        }
8171
        $sql = "SELECT SUM(TIMESTAMPDIFF(HOUR, login_course_date, logout_course_date)) diff
8172
    	        FROM $tableTrackLogin u $tableUrl
8173
                WHERE $conditionTime $urlCondition";
8174
        $rs = Database::query($sql);
8175
        $row = Database::fetch_array($rs, 'ASSOC');
8176
        $diff = $row['diff'];
8177
        if ($diff >= 0 and !empty($diff)) {
8178
            return $diff;
8179
        }
8180
        return 0;
8181
    }
8182
}
8183