Passed
Pull Request — master (#5678)
by Angel Fernando Quiroz
13:13 queued 05:29
created

Tracking::getFirstConnectionTimeInLp()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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