Passed
Push — master ( 1621b1...72e552 )
by Yannick
06:59 queued 13s
created

TrackingCourseLog::get_tool_name_table()   C

Complexity

Conditions 11
Paths 11

Size

Total Lines 62
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 58
nc 11
nop 1
dl 0
loc 62
rs 6.7696
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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