Passed
Push — master ( 65d552...4599b6 )
by Angel Fernando Quiroz
10:41
created

Tracking::getLastStudentPublication()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
c 0
b 0
f 0
nc 1
nop 4
dl 0
loc 32
rs 9.9332
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\TrackEAttemptQualify;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
8
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
9
use Chamilo\CoreBundle\Entity\TrackEDownloads;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Entity\Usergroup;
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
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
22
use Chamilo\CoreBundle\Component\Utils\StateIcon;
23
24
/**
25
 *  Class Tracking.
26
 *
27
 *  @author  Julio Montoya <[email protected]>
28
 */
29
class Tracking
30
{
31
    /**
32
     * Get group reporting.
33
     *
34
     * @param int    $course_id
35
     * @param int    $sessionId
36
     * @param int    $group_id
37
     * @param string $type
38
     * @param int    $start
39
     * @param int    $limit
40
     * @param int    $sidx
41
     * @param string $sord
42
     * @param array  $where_condition
43
     *
44
     * @return array|null
45
     */
46
    public static function get_group_reporting(
47
        $courseId,
48
        $sessionId = 0,
49
        $group_id = 0,
50
        $type = 'all',
51
        $start = 0,
52
        $limit = 1000,
53
        $sidx = 1,
54
        $sord = 'desc',
55
        $where_condition = []
56
    ) {
57
        $courseId = (int) $courseId;
58
        $sessionId = (int) $sessionId;
59
60
        if (empty($courseId)) {
61
            return null;
62
        }
63
        $course = api_get_course_entity($courseId);
64
65
        $session = api_get_session_entity($sessionId);
66
        if ('count' === $type) {
67
            return GroupManager::get_group_list(null, $course, null, $sessionId, true);
68
        }
69
70
        $groupList = GroupManager::get_group_list(null, $course, null, $sessionId, false, null, true);
71
        $parsedResult = [];
72
        if (!empty($groupList)) {
73
            foreach ($groupList as $group) {
74
                $users = GroupManager::get_users($group->getIid(), true, null, null, false, $courseId);
75
                $time = 0;
76
                $avg_student_score = 0;
77
                $avg_student_progress = 0;
78
                $work = 0;
79
                $messages = 0;
80
                foreach ($users as $user_data) {
81
                    $user = api_get_user_entity($user_data['user_id']);
82
                    $time += self::get_time_spent_on_the_course(
83
                        $user_data['user_id'],
84
                        $courseId,
85
                        $sessionId
86
                    );
87
                    $average = self::get_avg_student_score(
88
                        $user_data['user_id'],
89
                        $course,
90
                        [],
91
                        $session
92
                    );
93
                    if (is_numeric($average)) {
94
                        $avg_student_score += $average;
95
                    }
96
                    $avg_student_progress += self::get_avg_student_progress(
97
                        $user_data['user_id'],
98
                        $course,
99
                        [],
100
                        $session
101
                    );
102
                    $work += Container::getStudentPublicationRepository()->countUserPublications(
103
                        $user,
104
                        $course,
105
                        $session
106
                    );
107
                    $messages += Container::getForumPostRepository()->countUserForumPosts($user, $course, $session);
108
                }
109
110
                $countUsers = count($users);
111
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
112
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
113
114
                $groupItem = [
115
                    'id' => $group->getIid(),
116
                    'title' => $group->getTitle(),
117
                    'time' => api_time_to_hms($time),
118
                    'progress' => $averageProgress,
119
                    'score' => $averageScore,
120
                    'works' => $work,
121
                    'messages' => $messages,
122
                ];
123
                $parsedResult[] = $groupItem;
124
            }
125
        }
126
127
        return $parsedResult;
128
    }
129
130
    /**
131
     * @param int    $session_id
132
     * @param string $origin
133
     * @param bool   $export_csv
134
     * @param int    $lp_id
135
     * @param int    $lp_item_id
136
     * @param int    $extendId
137
     * @param int    $extendAttemptId
138
     * @param string $extendedAttempt
139
     * @param string $extendedAll
140
     * @param string $type            classic or simple
141
     * @param bool   $allowExtend     Optional. Allow or not extend te results
142
     *
143
     * @return string
144
     */
145
    public static function getLpStats(
146
        int $user_id,
147
        Course $course,
148
        ?SessionEntity $session,
149
        $origin,
150
        $export_csv,
151
        $lp_id,
152
        $lp_item_id = null,
153
        $extendId = null,
154
        $extendAttemptId = null,
155
        $extendedAttempt = null,
156
        $extendedAll = null,
157
        $type = 'classic',
158
        $allowExtend = true
159
    ) {
160
        if (empty($lp_id)) {
161
            return '';
162
        }
163
164
        $hideTime = ('true' === api_get_setting('lp.hide_lp_time'));
165
        $lp_id = (int) $lp_id;
166
167
        $lp_item_id = (int) $lp_item_id;
168
        $user_id = (int) $user_id;
169
        $sessionId = $session ? $session->getId() : 0;
170
        $origin = Security::remove_XSS($origin);
171
        $lp = Container::getLpRepository()->find($lp_id);
172
        $list = learnpath::get_flat_ordered_items_list($lp);
173
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
174
        $courseId = $course->getId();
175
        $session_condition = api_get_session_condition($sessionId);
176
177
        // Extend all button
178
        $output = '';
179
        $extra = '<script>
180
        $(function() {
181
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
182
            $( "#dialog-confirm" ).dialog({
183
                autoOpen: false,
184
                show: "blind",
185
                resizable: false,
186
                height:300,
187
                modal: true
188
            });
189
190
            $(".export").click(function() {
191
                var targetUrl = $(this).attr("href");
192
                $( "#dialog-confirm" ).dialog({
193
                    width:400,
194
                    height:300,
195
                    buttons: {
196
                        "'.addslashes(get_lang('Download')).'": function() {
197
                            var option = $("input[name=add_logo]:checked").val();
198
                            location.href = targetUrl+"&add_logo="+option;
199
                            $(this).dialog("close");
200
                        }
201
                    }
202
                });
203
                $("#dialog-confirm").dialog("open");
204
205
                return false;
206
            });
207
        });
208
        </script>';
209
210
        $extra .= '<div id="dialog-confirm" title="'.get_lang('Please confirm your choice').'">';
211
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
212
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
213
        $extra .= $form->returnForm();
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
Best Practice introduced by
Using exit here is not recommended.

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

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