Issues (1886)

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