Tracking::getCalculateTime()   F
last analyzed

Complexity

Conditions 40
Paths 10449

Size

Total Lines 155
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 40
eloc 105
nc 10449
nop 3
dl 0
loc 155
rs 0
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\TrackEAttemptQualify;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
8
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
9
use Chamilo\CoreBundle\Entity\TrackEDownloads;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Entity\Usergroup;
12
use Chamilo\CoreBundle\Framework\Container;
13
use Chamilo\CourseBundle\Entity\CLp;
14
use Chamilo\CourseBundle\Entity\CQuiz;
15
use Chamilo\CourseBundle\Entity\CStudentPublication;
16
use ChamiloSession as Session;
17
use CpChart\Cache as pCache;
18
use CpChart\Data as pData;
19
use CpChart\Image as pImage;
20
use ExtraField as ExtraFieldModel;
21
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
22
use Chamilo\CoreBundle\Component\Utils\StateIcon;
23
24
/**
25
 *  Class Tracking.
26
 *
27
 *  @author  Julio Montoya <[email protected]>
28
 */
29
class Tracking
30
{
31
    /**
32
     * Get group reporting.
33
     *
34
     * @param int    $course_id
35
     * @param int    $sessionId
36
     * @param int    $group_id
37
     * @param string $type
38
     * @param int    $start
39
     * @param int    $limit
40
     * @param int    $sidx
41
     * @param string $sord
42
     * @param array  $where_condition
43
     *
44
     * @return array|null
45
     */
46
    public static function get_group_reporting(
47
        $courseId,
48
        $sessionId = 0,
49
        $group_id = 0,
50
        $type = 'all',
51
        $start = 0,
52
        $limit = 1000,
53
        $sidx = 1,
54
        $sord = 'desc',
55
        $where_condition = []
56
    ) {
57
        $courseId = (int) $courseId;
58
        $sessionId = (int) $sessionId;
59
60
        if (empty($courseId)) {
61
            return null;
62
        }
63
        $course = api_get_course_entity($courseId);
64
65
        $session = api_get_session_entity($sessionId);
66
        if ('count' === $type) {
67
            return GroupManager::get_group_list(null, $course, null, $sessionId, true);
68
        }
69
70
        $groupList = GroupManager::get_group_list(null, $course, null, $sessionId, false, null, true);
71
        $parsedResult = [];
72
        if (!empty($groupList)) {
73
            foreach ($groupList as $group) {
74
                $users = GroupManager::get_users($group->getIid(), true, null, null, false, $courseId);
75
                $time = 0;
76
                $avg_student_score = 0;
77
                $avg_student_progress = 0;
78
                $work = 0;
79
                $messages = 0;
80
                foreach ($users as $user_data) {
81
                    $user = api_get_user_entity($user_data['user_id']);
82
                    $time += self::get_time_spent_on_the_course(
83
                        $user_data['user_id'],
84
                        $courseId,
85
                        $sessionId
86
                    );
87
                    $average = self::get_avg_student_score(
88
                        $user_data['user_id'],
89
                        $course,
90
                        [],
91
                        $session
92
                    );
93
                    if (is_numeric($average)) {
94
                        $avg_student_score += $average;
95
                    }
96
                    $avg_student_progress += self::get_avg_student_progress(
97
                        $user_data['user_id'],
98
                        $course,
99
                        [],
100
                        $session
101
                    );
102
                    $work += Container::getStudentPublicationRepository()->countUserPublications(
103
                        $user,
104
                        $course,
105
                        $session
106
                    );
107
                    $messages += Container::getForumPostRepository()->countUserForumPosts($user, $course, $session);
108
                }
109
110
                $countUsers = count($users);
111
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
112
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
113
114
                $groupItem = [
115
                    'id' => $group->getIid(),
116
                    'title' => $group->getTitle(),
117
                    'time' => api_time_to_hms($time),
118
                    'progress' => $averageProgress,
119
                    'score' => $averageScore,
120
                    'works' => $work,
121
                    'messages' => $messages,
122
                ];
123
                $parsedResult[] = $groupItem;
124
            }
125
        }
126
127
        return $parsedResult;
128
    }
129
130
    /**
131
     * @param int    $session_id
132
     * @param string $origin
133
     * @param bool   $export_csv
134
     * @param int    $lp_id
135
     * @param int    $lp_item_id
136
     * @param int    $extendId
137
     * @param int    $extendAttemptId
138
     * @param string $extendedAttempt
139
     * @param string $extendedAll
140
     * @param string $type            classic or simple
141
     * @param bool   $allowExtend     Optional. Allow or not extend te results
142
     *
143
     * @return string
144
     */
145
    public static function getLpStats(
146
        int $user_id,
147
        Course $course,
148
        ?SessionEntity $session,
149
        $origin,
150
        $export_csv,
151
        $lp_id,
152
        $lp_item_id = null,
153
        $extendId = null,
154
        $extendAttemptId = null,
155
        $extendedAttempt = null,
156
        $extendedAll = null,
157
        $type = 'classic',
158
        $allowExtend = true
159
    ) {
160
        if (empty($lp_id)) {
161
            return '';
162
        }
163
164
        $hideTime = ('true' === api_get_setting('lp.hide_lp_time'));
165
        $lp_id = (int) $lp_id;
166
167
        $lp_item_id = (int) $lp_item_id;
168
        $user_id = (int) $user_id;
169
        $sessionId = $session ? $session->getId() : 0;
170
        $origin = Security::remove_XSS($origin);
171
        $lp = Container::getLpRepository()->find($lp_id);
172
        $list = learnpath::get_flat_ordered_items_list($lp);
173
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
174
        $courseId = $course->getId();
175
        $session_condition = api_get_session_condition($sessionId);
176
177
        // Extend all button
178
        $output = '';
179
        $extra = '<script>
180
        $(function() {
181
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
182
            $( "#dialog-confirm" ).dialog({
183
                autoOpen: false,
184
                show: "blind",
185
                resizable: false,
186
                height:300,
187
                modal: true
188
            });
189
190
            $(".export").click(function() {
191
                var targetUrl = $(this).attr("href");
192
                $( "#dialog-confirm" ).dialog({
193
                    width:400,
194
                    height:300,
195
                    buttons: {
196
                        "'.addslashes(get_lang('Download')).'": function() {
197
                            var option = $("input[name=add_logo]:checked").val();
198
                            location.href = targetUrl+"&add_logo="+option;
199
                            $(this).dialog("close");
200
                        }
201
                    }
202
                });
203
                $("#dialog-confirm").dialog("open");
204
205
                return false;
206
            });
207
        });
208
        </script>';
209
210
        $extra .= '<div id="dialog-confirm" title="'.get_lang('Please confirm your choice').'">';
211
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
212
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
213
        $extra .= $form->returnForm();
214
        $extra .= '</div>';
215
        $output .= $extra;
216
217
        $url_suffix = '&lp_id='.$lp_id;
218
        if ('tracking' === $origin) {
219
            $url_suffix = '&sid='.$sessionId.'&cid='.$courseId.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
220
        }
221
222
        $extend_all = 0;
223
        if (!empty($extendedAll)) {
224
            $extend_all_link = Display::url(
225
                Display::getMdiIcon(
226
                    ActionIcon::VIEW_LESS,
227
                    'ch-tool-icon',
228
                    null,
229
                    ICON_SIZE_SMALL,
230
                    get_lang('Hide all attempts')
231
                ),
232
                api_get_self().'?action=stats'.$url_suffix
233
            );
234
            $extend_all = 1;
235
        } else {
236
            $extend_all_link = Display::url(
237
                Display::getMdiIcon(
238
                    ActionIcon::VIEW_MORE,
239
                    'ch-tool-icon',
240
                    null,
241
                    ICON_SIZE_SMALL,
242
                    get_lang('Show all attempts')
243
                ),
244
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
245
            );
246
        }
247
248
        if ('tracking' !== $origin) {
249
            $output .= '<div class="section-status">';
250
            $output .= Display::page_header(get_lang('My progress'));
251
            $output .= '</div>';
252
        }
253
254
        $actionColumn = null;
255
        if ('classic' === $type) {
256
            $actionColumn = ' <th>'.get_lang('Detail').'</th>';
257
        }
258
259
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('Time').'</th>';
260
        if ($hideTime) {
261
            $timeHeader = '';
262
        }
263
        $output .= '<div class="table-responsive">';
264
        $output .= '<table id="lp_tracking" class="table tracking">
265
            <thead>
266
            <tr class="table-header">
267
                <th width="16">'.(true === $allowExtend ? $extend_all_link : '&nbsp;').'</th>
268
                <th colspan="4">
269
                    '.get_lang('Learning object name').'
270
                </th>
271
                <th colspan="2">
272
                    '.get_lang('Status').'
273
                </th>
274
                <th colspan="2">
275
                    '.get_lang('Score').'
276
                </th>
277
                '.$timeHeader.'
278
                '.$actionColumn.'
279
                </tr>
280
            </thead>
281
            <tbody>
282
        ';
283
284
        // Going through the items using the $items[] array instead of the database order ensures
285
        // we get them in the same order as in the imsmanifest file, which is rather random when using
286
        // the database table.
287
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
288
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
289
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
290
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
291
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
292
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
293
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
294
295
        $sql = "SELECT max(view_count)
296
                FROM $TBL_LP_VIEW
297
                WHERE
298
                    c_id = $courseId AND
299
                    lp_id = $lp_id AND
300
                    user_id = $user_id
301
                    $session_condition";
302
        $res = Database::query($sql);
303
        $view = 0;
304
        if (Database::num_rows($res) > 0) {
305
            $myrow = Database::fetch_array($res);
306
            $view = (int) $myrow[0];
307
        }
308
309
        $counter = 0;
310
        $total_time = 0;
311
        $h = get_lang('h');
312
313
        if (!empty($export_csv)) {
314
            $csvHeaders = [
315
                get_lang('Learning object name'),
316
                get_lang('Status'),
317
                get_lang('Score'),
318
            ];
319
320
            if (false === $hideTime) {
321
                $csvHeaders[] = get_lang('Time');
322
            }
323
            $csv_content[] = $csvHeaders;
324
        }
325
326
        $result_disabled_ext_all = true;
327
        $chapterTypes = learnpath::getChapterTypes();
328
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
329
330
        $minimumAvailable = self::minimumTimeAvailable($sessionId, $courseId);
331
        $timeCourse = [];
332
        if ($minimumAvailable) {
333
            $timeCourse = self::getCalculateTime($user_id, $courseId, $sessionId);
334
            Session::write('trackTimeCourse', $timeCourse);
335
        }
336
337
        // Show lp items
338
        if (is_array($list) && count($list) > 0) {
339
            foreach ($list as $my_item_id) {
340
                $extend_this = 0;
341
                $order = 'DESC';
342
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
343
                    $extend_this = 1;
344
                    $order = 'ASC';
345
                }
346
347
                // Prepare statement to go through each attempt.
348
                $viewCondition = null;
349
                if (!empty($view)) {
350
                    $viewCondition = " AND v.view_count = $view  ";
351
                }
352
353
                $sql = "SELECT
354
                    iv.status as mystatus,
355
                    v.view_count as mycount,
356
                    iv.score as myscore,
357
                    iv.total_time as mytime,
358
                    i.iid as myid,
359
                    i.lp_id as mylpid,
360
                    iv.lp_view_id as mylpviewid,
361
                    i.title as mytitle,
362
                    i.max_score as mymaxscore,
363
                    iv.max_score as myviewmaxscore,
364
                    i.item_type as item_type,
365
                    iv.view_count as iv_view_count,
366
                    iv.iid as iv_id,
367
                    path
368
                FROM $TBL_LP_ITEM as i
369
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
370
                ON (i.iid = iv.lp_item_id)
371
                INNER JOIN $TBL_LP_VIEW as v
372
                ON (iv.lp_view_id = v.iid)
373
                WHERE
374
                    i.iid = $my_item_id AND
375
                    i.lp_id = $lp_id  AND
376
                    v.user_id = $user_id
377
                    $session_condition
378
                    $viewCondition
379
                ORDER BY iv.view_count $order ";
380
381
                $result = Database::query($sql);
382
                $num = Database::num_rows($result);
383
                $time_for_total = 0;
384
                $attemptResult = 0;
385
386
                if ($timeCourse) {
387
                    if (isset($timeCourse['learnpath_detailed']) &&
388
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
389
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
390
                    ) {
391
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
392
                    }
393
                }
394
395
                // Extend all
396
                if (($extend_this || $extend_all) && $num > 0) {
397
                    $row = Database::fetch_array($result);
398
                    $result_disabled_ext_all = false;
399
                    if ('quiz' === $row['item_type']) {
400
                        // Check results_disabled in quiz table.
401
                        $my_path = Database::escape_string($row['path']);
402
                        $sql = "SELECT results_disabled
403
                                FROM $TBL_QUIZ
404
                                WHERE
405
                                    iid ='".$my_path."'";
406
                        $res_result_disabled = Database::query($sql);
407
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
408
409
                        if (Database::num_rows($res_result_disabled) > 0 &&
410
                            1 === (int) $row_result_disabled[0]
411
                        ) {
412
                            $result_disabled_ext_all = true;
413
                        }
414
                    }
415
416
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
417
                    $oddclass = 'row_even';
418
                    if (0 === ($counter % 2)) {
419
                        $oddclass = 'row_odd';
420
                    }
421
                    $extend_link = '';
422
                    if (!empty($inter_num)) {
423
                        $extend_link = Display::url(
424
                            Display::getMdiIcon(
425
                                ActionIcon::VISIBLE,
426
                                'ch-tool-icon',
427
                                null,
428
                                ICON_SIZE_SMALL,
429
                                get_lang('Hide attempt view')
430
                            ),
431
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
432
                        );
433
                    }
434
                    $title = $row['mytitle'];
435
436
                    if (empty($title)) {
437
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
438
                    }
439
440
                    if (in_array($row['item_type'], $chapterTypes)) {
441
                        $title = "<h4> $title </h4>";
442
                    }
443
                    $lesson_status = $row['mystatus'];
444
                    $title = Security::remove_XSS($title);
445
                    $counter++;
446
447
                    $action = null;
448
                    if ('classic' === $type) {
449
                        $action = '<td></td>';
450
                    }
451
452
                    if (in_array($row['item_type'], $chapterTypes)) {
453
                        $output .= '<tr class="'.$oddclass.'">
454
                                <td>'.$extend_link.'</td>
455
                                <td colspan="4">
456
                                   '.$title.'
457
                                </td>
458
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
459
                                <td colspan="2"></td>
460
                                <td colspan="2"></td>
461
                                '.$action.'
462
                            </tr>';
463
                        continue;
464
                    } else {
465
                        $output .= '<tr class="'.$oddclass.'">
466
                                <td>'.$extend_link.'</td>
467
                                <td colspan="4">'.$title.'</td>
468
                                <td colspan="2"></td>
469
                                <td colspan="2"></td>
470
                                <td colspan="2"></td>
471
                                '.$action.'
472
                            </tr>';
473
                    }
474
475
                    $attemptCount = 1;
476
                    do {
477
                        // Check if there are interactions below.
478
                        $extend_attempt_link = '';
479
                        $extend_this_attempt = 0;
480
481
                        if ($timeCourse) {
482
                            //$attemptResult = 0;
483
                            if (isset($timeCourse['learnpath_detailed']) &&
484
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
485
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
486
                            ) {
487
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
488
                            }
489
                        }
490
                        if ((
491
                            learnpath::get_interactions_count_from_db($row['iv_id'], $courseId) > 0 ||
492
                            learnpath::get_objectives_count_from_db($row['iv_id'], $courseId) > 0
493
                            ) &&
494
                            !$extend_all
495
                        ) {
496
                            if ($extendAttemptId == $row['iv_id']) {
497
                                // The extend button for this attempt has been clicked.
498
                                $extend_this_attempt = 1;
499
                                $extend_attempt_link = Display::url(
500
                                    Display::getMdiIcon(
501
                                        ActionIcon::VISIBLE,
502
                                        'ch-tool-icon',
503
                                        null,
504
                                        ICON_SIZE_SMALL,
505
                                        get_lang('Hide attempt view')
506
                                    ),
507
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
508
                                );
509
                                if ($accessToPdfExport) {
510
                                    $extend_attempt_link .= '&nbsp;'.
511
                                        Display::url(
512
                                            Display::getMdiIcon(
513
                                                ActionIcon::EXPORT_PDF,
514
                                                'ch-tool-icon',
515
                                                null,
516
                                                ICON_SIZE_SMALL,
517
                                                get_lang('Export to PDF')
518
                                            ),
519
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
520
                                            ['class' => 'export']
521
                                        );
522
                                }
523
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
524
                                // The "extend" button for this attempt has not been clicked.
525
                                $extend_attempt_link = Display::url(
526
                                    Display::getMdiIcon(
527
                                        ActionIcon::INVISIBLE,
528
                                        'ch-tool-icon',
529
                                        null,
530
                                        ICON_SIZE_SMALL,
531
                                        get_lang('Extend attempt view')
532
                                    ),
533
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
534
                                );
535
                                if ($accessToPdfExport) {
536
                                    $extend_attempt_link .= '&nbsp;'.
537
                                        Display::url(
538
                                            Display::getMdiIcon(
539
                                                ActionIcon::EXPORT_PDF,
540
                                                'ch-tool-icon',
541
                                                null,
542
                                                ICON_SIZE_SMALL,
543
                                                get_lang('Export to PDF')
544
                                            ),
545
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
546
                                            ['class' => 'export']
547
                                        );
548
                                }
549
                            }
550
                        }
551
552
                        $oddclass = 'row_even';
553
                        if (0 == ($counter % 2)) {
554
                            $oddclass = 'row_odd';
555
                        }
556
557
                        $lesson_status = $row['mystatus'];
558
                        $score = $row['myscore'];
559
                        $time_for_total += $row['mytime'];
560
                        $attemptTime = $row['mytime'];
561
562
                        if ($minimumAvailable) {
563
                            $lp_time = $timeCourse[TOOL_LEARNPATH];
564
                            $lpTime = null;
565
                            if (isset($lp_time[$lp_id])) {
566
                                $lpTime = (int) $lp_time[$lp_id];
567
                            }
568
                            $time_for_total = $lpTime;
569
570
                            if ($timeCourse) {
571
                                $time_for_total = (int) $attemptResult;
572
                                $attemptTime = (int) $attemptResult;
573
                            }
574
                        }
575
576
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
577
578
                        if (0 == $score) {
579
                            $maxscore = $row['mymaxscore'];
580
                        } else {
581
                            if ('sco' === $row['item_type']) {
582
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
583
                                    $maxscore = $row['myviewmaxscore'];
584
                                } elseif ('' === $row['myviewmaxscore']) {
585
                                    $maxscore = 0;
586
                                } else {
587
                                    $maxscore = $row['mymaxscore'];
588
                                }
589
                            } else {
590
                                $maxscore = $row['mymaxscore'];
591
                            }
592
                        }
593
594
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
595
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
596
597
                        if ('dir' !== $row['item_type']) {
598
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
599
                                $view_score = Display::getMdiIcon(
600
                                    ActionIcon::INVISIBLE,
601
                                    'ch-tool-icon',
602
                                    null,
603
                                    ICON_SIZE_SMALL,
604
                                    get_lang('Results hidden by the exercise setting')
605
                                );
606
                            } else {
607
                                switch ($row['item_type']) {
608
                                    case 'sco':
609
                                        if (0 == $maxscore) {
610
                                            $view_score = $score;
611
                                        } else {
612
                                            $view_score = ExerciseLib::show_score(
613
                                                $score,
614
                                                $maxscore,
615
                                                false
616
                                            );
617
                                        }
618
                                        break;
619
                                    case 'document':
620
                                        $view_score = (0 == $score ? '/' : ExerciseLib::show_score($score, $maxscore, false));
621
                                        break;
622
                                    default:
623
                                        $view_score = ExerciseLib::show_score(
624
                                            $score,
625
                                            $maxscore,
626
                                            false
627
                                        );
628
                                        break;
629
                                }
630
                            }
631
632
                            $action = null;
633
                            if ('classic' === $type) {
634
                                $action = '<td></td>';
635
                            }
636
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
637
                            if ($hideTime) {
638
                                $timeRow = '';
639
                            }
640
                            $output .= '<tr class="'.$oddclass.'">
641
                                    <td></td>
642
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
643
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
644
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
645
                                    <td colspan="2">'.$view_score.'</td>
646
                                    '.$timeRow.'
647
                                    '.$action.'
648
                                </tr>';
649
                            $attemptCount++;
650
                            if (!empty($export_csv)) {
651
                                $temp = [];
652
                                $temp[] = $title = Security::remove_XSS($title);
653
                                $temp[] = Security::remove_XSS(
654
                                    learnpathItem::humanize_status($lesson_status, false, $type)
655
                                );
656
657
                                if ('quiz' === $row['item_type']) {
658
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
659
                                        $temp[] = '/';
660
                                    } else {
661
                                        $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
662
                                    }
663
                                } else {
664
                                    $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
665
                                }
666
667
                                if (false === $hideTime) {
668
                                    $temp[] = $time;
669
                                }
670
                                $csv_content[] = $temp;
671
                            }
672
                        }
673
674
                        $counter++;
675
                        $action = null;
676
                        if ('classic' === $type) {
677
                            $action = '<td></td>';
678
                        }
679
680
                        if ($extend_this_attempt || $extend_all) {
681
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
682
                            foreach ($list1 as $id => $interaction) {
683
                                $oddclass = 'row_even';
684
                                if (0 == ($counter % 2)) {
685
                                    $oddclass = 'row_odd';
686
                                }
687
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
688
                                if ($hideTime) {
689
                                    $timeRow = '';
690
                                }
691
692
                                $output .= '<tr class="'.$oddclass.'">
693
                                        <td></td>
694
                                        <td></td>
695
                                        <td></td>
696
                                        <td>'.$interaction['order_id'].'</td>
697
                                        <td>'.$interaction['id'].'</td>';
698
699
                                $output .= '
700
                                        <td colspan="2">'.$interaction['type'].'</td>
701
                                        <td>'.$interaction['student_response_formatted'].'</td>
702
                                        <td>'.$interaction['result'].'</td>
703
                                        <td>'.$interaction['latency'].'</td>
704
                                        '.$timeRow.'
705
                                        '.$action.'
706
                                    </tr>';
707
                                $counter++;
708
                            }
709
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
710
                            foreach ($list2 as $id => $interaction) {
711
                                $oddclass = 'row_even';
712
                                if (0 === ($counter % 2)) {
713
                                    $oddclass = 'row_odd';
714
                                }
715
                                $output .= '<tr class="'.$oddclass.'">
716
                                        <td></td>
717
                                        <td></td>
718
                                        <td></td>
719
                                        <td>'.$interaction['order_id'].'</td>
720
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
721
                                        <td colspan="2">'.$interaction['status'].'</td>
722
                                        <td>'.$interaction['score_raw'].'</td>
723
                                        <td>'.$interaction['score_max'].'</td>
724
                                        <td>'.$interaction['score_min'].'</td>
725
                                        '.$action.'
726
                                     </tr>';
727
                                $counter++;
728
                            }
729
                        }
730
                    } while ($row = Database::fetch_array($result));
731
                } elseif ($num > 0) {
732
                    // Not extended.
733
                    $row = Database::fetch_assoc($result);
734
                    $my_id = $row['myid'];
735
                    $my_lp_id = $row['mylpid'];
736
                    $my_lp_view_id = $row['mylpviewid'];
737
                    $my_path = $row['path'];
738
                    $result_disabled_ext_all = false;
739
                    if ('quiz' === $row['item_type']) {
740
                        // Check results_disabled in quiz table.
741
                        $my_path = Database::escape_string($my_path);
742
                        $sql = "SELECT results_disabled
743
                                FROM $TBL_QUIZ
744
                                WHERE iid = '$my_path' ";
745
                        $res_result_disabled = Database::query($sql);
746
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
747
748
                        if (Database::num_rows($res_result_disabled) > 0 &&
749
                            1 === (int) $row_result_disabled[0]
750
                        ) {
751
                            $result_disabled_ext_all = true;
752
                        }
753
                    }
754
755
                    // Check if there are interactions below
756
                    $extend_this_attempt = 0;
757
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $courseId);
758
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $courseId);
759
                    $extend_attempt_link = '';
760
                    if ($inter_num > 0 || $objec_num > 0) {
761
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
762
                            // The extend button for this attempt has been clicked.
763
                            $extend_this_attempt = 1;
764
                            $extend_attempt_link = Display::url(
765
                                Display::getMdiIcon(
766
                                    ActionIcon::VISIBLE,
767
                                    'ch-tool-icon',
768
                                    null,
769
                                    ICON_SIZE_SMALL,
770
                                    get_lang('Hide attempt view')
771
                                ),
772
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
773
                            );
774
                        } else {
775
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
776
                            // The "Extend" button for this attempt has not been clicked.
777
                            $extend_attempt_link = Display::url(
778
                                Display::getMdiIcon(
779
                                    ActionIcon::INVISIBLE,
780
                                    'ch-tool-icon',
781
                                    null,
782
                                    ICON_SIZE_SMALL,
783
                                    get_lang('Extend attempt view')
784
                                ),
785
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
786
                            );
787
                        }
788
                    }
789
790
                    $oddclass = 'row_even';
791
                    if (0 == ($counter % 2)) {
792
                        $oddclass = 'row_odd';
793
                    }
794
795
                    $extend_link = '';
796
                    if ($inter_num > 1) {
797
                        $extend_link = Display::url(
798
                            Display::getMdiIcon(
799
                                ActionIcon::INVISIBLE,
800
                                'ch-tool-icon',
801
                                null,
802
                                ICON_SIZE_SMALL,
803
                                get_lang('Extend attempt view')
804
                            ),
805
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
806
                        );
807
                    }
808
809
                    $lesson_status = $row['mystatus'];
810
                    $score = $row['myscore'];
811
                    $subtotal_time = $row['mytime'];
812
                    while ($tmp_row = Database::fetch_array($result)) {
813
                        $subtotal_time += $tmp_row['mytime'];
814
                    }
815
816
                    $title = $row['mytitle'];
817
                    $sessionCondition = api_get_session_condition($sessionId);
818
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
819
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
820
                            WHERE
821
                                exe_exo_id="'.$row['path'].'" AND
822
                                exe_user_id="'.$user_id.'" AND
823
                                orig_lp_id = "'.$lp_id.'" AND
824
                                orig_lp_item_id = "'.$row['myid'].'" AND
825
                                c_id = '.$courseId.' AND
826
                                status <> "incomplete"
827
                                '.$sessionCondition.'
828
                             ORDER BY exe_date DESC
829
                             LIMIT 1';
830
831
                    $resultLastAttempt = Database::query($sql);
832
                    $num = Database::num_rows($resultLastAttempt);
833
                    $id_last_attempt = null;
834
                    if ($num > 0) {
835
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
836
                            $id_last_attempt = $rowLA['exe_id'];
837
                        }
838
                    }
839
840
                    switch ($row['item_type']) {
841
                        case 'sco':
842
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
843
                                $maxscore = $row['myviewmaxscore'];
844
                            } elseif ('' === $row['myviewmaxscore']) {
845
                                $maxscore = 0;
846
                            } else {
847
                                $maxscore = $row['mymaxscore'];
848
                            }
849
                            break;
850
                        case 'quiz':
851
                            // Get score and total time from last attempt of a exercise en lp.
852
                            $sql = "SELECT iid, score
853
                                    FROM $TBL_LP_ITEM_VIEW
854
                                    WHERE
855
                                        lp_item_id = '".(int) $my_id."' AND
856
                                        lp_view_id = '".(int) $my_lp_view_id."'
857
                                    ORDER BY view_count DESC
858
                                    LIMIT 1";
859
                            $res_score = Database::query($sql);
860
                            $row_score = Database::fetch_array($res_score);
861
862
                            $sql = "SELECT SUM(total_time) as total_time
863
                                    FROM $TBL_LP_ITEM_VIEW
864
                                    WHERE
865
                                        lp_item_id = '".(int) $my_id."' AND
866
                                        lp_view_id = '".(int) $my_lp_view_id."'";
867
                            $res_time = Database::query($sql);
868
                            $row_time = Database::fetch_array($res_time);
869
870
                            $score = 0;
871
                            $subtotal_time = 0;
872
                            if (Database::num_rows($res_score) > 0 &&
873
                                Database::num_rows($res_time) > 0
874
                            ) {
875
                                $score = (float) $row_score['score'];
876
                                $subtotal_time = (int) $row_time['total_time'];
877
                            }
878
                            // Selecting the max score from an attempt.
879
                            $sql = "SELECT SUM(t.ponderation) as maxscore
880
                                    FROM (
881
                                        SELECT DISTINCT
882
                                            question_id, marks, ponderation
883
                                        FROM $tbl_stats_attempts as at
884
                                        INNER JOIN $tbl_quiz_questions as q
885
                                        ON (q.iid = at.question_id)
886
                                        WHERE exe_id ='$id_last_attempt'
887
                                    ) as t";
888
889
                            $result = Database::query($sql);
890
                            $row_max_score = Database::fetch_array($result);
891
                            $maxscore = $row_max_score['maxscore'];
892
893
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
894
                            $sql = 'SELECT SUM(exe_duration) exe_duration
895
                                    FROM '.$tbl_stats_exercices.'
896
                                    WHERE
897
                                        exe_exo_id="'.$row['path'].'" AND
898
                                        exe_user_id="'.$user_id.'" AND
899
                                        orig_lp_id = "'.$lp_id.'" AND
900
                                        orig_lp_item_id = "'.$row['myid'].'" AND
901
                                        c_id = '.$courseId.' AND
902
                                        status <> "incomplete" AND
903
                                        session_id = '.$sessionId.'
904
                                     ORDER BY exe_date DESC ';
905
                            $sumScoreResult = Database::query($sql);
906
                            $durationRow = Database::fetch_assoc($sumScoreResult);
907
                            if (!empty($durationRow['exe_duration'])) {
908
                                $exeDuration = $durationRow['exe_duration'];
909
                                if ($exeDuration != $subtotal_time &&
910
                                    !empty($row_score['iid']) &&
911
                                    !empty($exeDuration)
912
                                ) {
913
                                    $subtotal_time = $exeDuration;
914
                                    // Update c_lp_item_view.total_time
915
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
916
                                                  WHERE iid = ".$row_score['iid'];
917
                                    Database::query($sqlUpdate);
918
                                }
919
                            }
920
                            break;
921
                        default:
922
                            $maxscore = $row['mymaxscore'];
923
                            break;
924
                    }
925
926
                    $time_for_total = $subtotal_time;
927
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
928
                    if (empty($title)) {
929
                        $title = learnpath::rl_get_resource_name(
930
                            $courseInfo['code'],
931
                            $lp_id,
932
                            $row['myid']
933
                        );
934
                    }
935
936
                    $action = null;
937
                    if ('classic' === $type) {
938
                        $action = '<td></td>';
939
                    }
940
941
                    if (in_array($row['item_type'], $chapterTypes)) {
942
                        $title = Security::remove_XSS($title);
943
                        $output .= '<tr class="'.$oddclass.'">
944
                                <td>'.$extend_link.'</td>
945
                                <td colspan="10">
946
                                <h4>'.$title.'</h4>
947
                                </td>
948
                                '.$action.'
949
                            </tr>';
950
                    } else {
951
                        $correct_test_link = '-';
952
                        $showRowspan = false;
953
                        if ('quiz' === $row['item_type']) {
954
                            $my_url_suffix = '&cid='.$courseId.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
955
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
956
                                     WHERE
957
                                        exe_exo_id="'.$row['path'].'" AND
958
                                        exe_user_id="'.$user_id.'" AND
959
                                        orig_lp_id = "'.$lp_id.'" AND
960
                                        orig_lp_item_id = "'.$row['myid'].'" AND
961
                                        c_id = '.$courseId.' AND
962
                                        status <> "incomplete" AND
963
                                        session_id = '.$sessionId.'
964
                                     ORDER BY exe_date DESC ';
965
966
                            $resultLastAttempt = Database::query($sql);
967
                            $num = Database::num_rows($resultLastAttempt);
968
                            $showRowspan = false;
969
                            if ($num > 0) {
970
                                $linkId = 'link_'.$my_id;
971
                                if (1 == $extendedAttempt &&
972
                                    $lp_id == $my_lp_id &&
973
                                    $lp_item_id == $my_id
974
                                ) {
975
                                    $showRowspan = true;
976
                                    $correct_test_link = Display::url(
977
                                        Display::getMdiIcon(
978
                                            ActionIcon::VIEW_LESS,
979
                                            'ch-tool-icon',
980
                                            null,
981
                                            ICON_SIZE_SMALL,
982
                                            get_lang('Hide all attempts')
983
                                        ),
984
                                        api_get_self().'?action=stats'.$my_url_suffix.'&sid='.$sessionId.'&lp_item_id='.$my_id.'#'.$linkId,
985
                                        ['id' => $linkId]
986
                                    );
987
                                } else {
988
                                    $correct_test_link = Display::url(
989
                                        Display::getMdiIcon(
990
                                            ActionIcon::VIEW_MORE,
991
                                            'ch-tool-icon',
992
                                            null,
993
                                            ICON_SIZE_SMALL,
994
                                            get_lang(
995
                                                'Show all attemptsByExercise'
996
                                            )
997
                                        ),
998
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&sid='.$sessionId.'&lp_item_id='.$my_id.'#'.$linkId,
999
                                        ['id' => $linkId]
1000
                                    );
1001
                                }
1002
                            }
1003
                        }
1004
1005
                        $title = Security::remove_XSS($title);
1006
                        $action = null;
1007
                        if ('classic' === $type) {
1008
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
1009
                        }
1010
1011
                        if ($lp_id == $my_lp_id && false) {
1012
                            $output .= '<tr class ='.$oddclass.'>
1013
                                    <td>'.$extend_link.'</td>
1014
                                    <td colspan="4">'.$title.'</td>
1015
                                    <td colspan="2">&nbsp;</td>
1016
                                    <td colspan="2">&nbsp;</td>
1017
                                    <td colspan="2">&nbsp;</td>
1018
                                    '.$action.'
1019
                                </tr>';
1020
                            $output .= '</tr>';
1021
                        } else {
1022
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
1023
                                $output .= "<tr class='$oddclass'>";
1024
                            } else {
1025
                                $output .= "<tr class='$oddclass'>";
1026
                            }
1027
1028
                            $scoreItem = null;
1029
                            if ('quiz' === $row['item_type']) {
1030
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1031
                                    $scoreItem .= Display::getMdiIcon(
1032
                                        ActionIcon::INVISIBLE,
1033
                                        'ch-tool-icon',
1034
                                        null,
1035
                                        ICON_SIZE_SMALL,
1036
                                        get_lang('Results hidden by the exercise setting')
1037
                                    );
1038
                                } else {
1039
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
1040
                                }
1041
                            } else {
1042
                                $scoreItem .= 0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.$maxscore);
1043
                            }
1044
1045
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1046
                            if ($hideTime) {
1047
                                $timeRow = '';
1048
                            }
1049
1050
                            $output .= '
1051
                                <td>'.$extend_link.'</td>
1052
                                <td colspan="4">'.$title.'</td>
1053
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1054
                                <td colspan="2">'.$scoreItem.'</td>
1055
                                '.$timeRow.'
1056
                                '.$action.'
1057
                             ';
1058
                            $output .= '</tr>';
1059
                        }
1060
1061
                        if (!empty($export_csv)) {
1062
                            $temp = [];
1063
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1064
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1065
                            if ('quiz' === $row['item_type']) {
1066
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1067
                                    $temp[] = '/';
1068
                                } else {
1069
                                    $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1070
                                }
1071
                            } else {
1072
                                $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1073
                            }
1074
1075
                            if (false === $hideTime) {
1076
                                $temp[] = $time;
1077
                            }
1078
                            $csv_content[] = $temp;
1079
                        }
1080
                    }
1081
1082
                    $counter++;
1083
                    $action = null;
1084
                    if ('classic' === $type) {
1085
                        $action = '<td></td>';
1086
                    }
1087
1088
                    if ($extend_this_attempt || $extend_all) {
1089
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
1090
                        foreach ($list1 as $id => $interaction) {
1091
                            $oddclass = 'row_even';
1092
                            if (0 == ($counter % 2)) {
1093
                                $oddclass = 'row_odd';
1094
                            }
1095
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1096
                            if ($hideTime) {
1097
                                $timeRow = '';
1098
                            }
1099
1100
                            $output .= '<tr class="'.$oddclass.'">
1101
                                    <td></td>
1102
                                    <td></td>
1103
                                    <td></td>
1104
                                    <td>'.$interaction['order_id'].'</td>
1105
                                    <td>'.$interaction['id'].'</td>
1106
                                    <td colspan="2">'.$interaction['type'].'</td>
1107
                                    <td>'.urldecode($interaction['student_response']).'</td>
1108
                                    <td>'.$interaction['result'].'</td>
1109
                                    <td>'.$interaction['latency'].'</td>
1110
                                    '.$timeRow.'
1111
                                    '.$action.'
1112
                               </tr>';
1113
                            $counter++;
1114
                        }
1115
1116
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
1117
                        foreach ($list2 as $id => $interaction) {
1118
                            $oddclass = 'row_even';
1119
                            if (0 == ($counter % 2)) {
1120
                                $oddclass = 'row_odd';
1121
                            }
1122
                            $output .= '<tr class="'.$oddclass.'">
1123
                                    <td></td>
1124
                                    <td></td>
1125
                                    <td></td>
1126
                                    <td>'.$interaction['order_id'].'</td>
1127
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1128
                                    <td colspan="2">'.$interaction['status'].'</td>
1129
                                    <td>'.$interaction['score_raw'].'</td>
1130
                                    <td>'.$interaction['score_max'].'</td>
1131
                                    <td>'.$interaction['score_min'].'</td>
1132
                                    '.$action.'
1133
                               </tr>';
1134
                            $counter++;
1135
                        }
1136
                    }
1137
1138
                    // Attempts listing by exercise.
1139
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1140
                        // Get attempts of a exercise.
1141
                        if (!empty($lp_id) &&
1142
                            !empty($lp_item_id) &&
1143
                            'quiz' === $row['item_type']
1144
                        ) {
1145
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1146
                                    WHERE
1147
                                        iid = '$lp_item_id' AND
1148
                                        lp_id = '$lp_id'";
1149
                            $res_path = Database::query($sql);
1150
                            $row_path = Database::fetch_array($res_path);
1151
1152
                            if (Database::num_rows($res_path) > 0) {
1153
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1154
                                        WHERE
1155
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1156
                                            status <> "incomplete" AND
1157
                                            exe_user_id="'.$user_id.'" AND
1158
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1159
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1160
                                            c_id = '.$courseId.'  AND
1161
                                            session_id = '.$sessionId.'
1162
                                        ORDER BY exe_date';
1163
                                $res_attempts = Database::query($sql);
1164
                                $num_attempts = Database::num_rows($res_attempts);
1165
                                if ($num_attempts > 0) {
1166
                                    $n = 1;
1167
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1168
                                        $my_score = $row_attempts['score'];
1169
                                        $my_maxscore = $row_attempts['max_score'];
1170
                                        $my_exe_id = $row_attempts['exe_id'];
1171
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1172
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1173
                                        $time_attemp = ' - ';
1174
                                        if ($mktime_start_date && $mktime_exe_date) {
1175
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1176
                                        }
1177
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1178
                                            $view_score = Display::getMdiIcon(
1179
                                                ActionIcon::INVISIBLE,
1180
                                                'ch-tool-icon',
1181
                                                null,
1182
                                                ICON_SIZE_SMALL,
1183
                                                get_lang('Results hidden by the exercise setting')
1184
                                            );
1185
                                        } else {
1186
                                            // Show only float when need it
1187
                                            if (0 == $my_score) {
1188
                                                $view_score = ExerciseLib::show_score(
1189
                                                    0,
1190
                                                    $my_maxscore,
1191
                                                    false
1192
                                                );
1193
                                            } else {
1194
                                                if (0 == $my_maxscore) {
1195
                                                    $view_score = $my_score;
1196
                                                } else {
1197
                                                    $view_score = ExerciseLib::show_score(
1198
                                                        $my_score,
1199
                                                        $my_maxscore,
1200
                                                        false
1201
                                                    );
1202
                                                }
1203
                                            }
1204
                                        }
1205
                                        $my_lesson_status = $row_attempts['status'];
1206
                                        if ('' === $my_lesson_status) {
1207
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1208
                                        } elseif ('incomplete' === $my_lesson_status) {
1209
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1210
                                        }
1211
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1212
                                        if ($hideTime) {
1213
                                            $timeRow = '';
1214
                                        }
1215
1216
                                        $output .= '<tr class="'.$oddclass.'" >
1217
                                        <td></td>
1218
                                        <td>'.$extend_attempt_link.'</td>
1219
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1220
                                        <td colspan="2">'.$my_lesson_status.'</td>
1221
                                        <td colspan="2">'.$view_score.'</td>
1222
                                        '.$timeRow;
1223
1224
                                        if ('classic' === $action) {
1225
                                            if ('tracking' !== $origin) {
1226
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1227
                                                    $output .= '<td>
1228
                                                            <img
1229
                                                                src="'.Display::returnIconPath('quiz_na.gif').'"
1230
                                                                alt="'.get_lang('Show attempt').'"
1231
                                                                title="'.get_lang('Show attempt').'" />
1232
                                                            </td>';
1233
                                                } else {
1234
                                                    $output .= '<td>
1235
                                                            <a
1236
                                                                href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cid='.$courseId.'"
1237
                                                                target="_parent">
1238
                                                            <img
1239
                                                                src="'.Display::returnIconPath('quiz.png').'"
1240
                                                                alt="'.get_lang('Show attempt').'"
1241
                                                                title="'.get_lang('Show attempt').'" />
1242
                                                            </a></td>';
1243
                                                }
1244
                                            } else {
1245
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1246
                                                    $output .= '<td>
1247
                                                                <img
1248
                                                                    src="'.Display::returnIconPath('quiz_na.gif').'"
1249
                                                                    alt="'.get_lang('Show and grade attempt').'"
1250
                                                                    title="'.get_lang('Show and grade attempt').'" />
1251
                                                                </td>';
1252
                                                } else {
1253
                                                    $output .= '<td>
1254
                                                                    <a
1255
                                                                        href="../exercise/exercise_show.php?cid='.$courseId.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'"
1256
                                                                        target="_parent">
1257
                                                                    <img
1258
                                                                        src="'.Display::returnIconPath('quiz.gif').'"
1259
                                                                        alt="'.get_lang('Show and grade attempt').'"
1260
                                                                        title="'.get_lang('Show and grade attempt').'">
1261
                                                                    </a>
1262
                                                                    </td>';
1263
                                                }
1264
                                            }
1265
                                        }
1266
                                        $output .= '</tr>';
1267
                                        $n++;
1268
                                    }
1269
                                }
1270
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1271
                            }
1272
                        }
1273
                    }
1274
                }
1275
1276
                $total_time += $time_for_total;
1277
                // QUIZZ IN LP
1278
                $a_my_id = [];
1279
                if (!empty($my_lp_id)) {
1280
                    $a_my_id[] = $my_lp_id;
1281
                }
1282
            }
1283
        }
1284
1285
        // NOT Extend all "left green cross"
1286
        if (!empty($a_my_id)) {
1287
            if ($extendedAttempt) {
1288
                // "Right green cross" extended
1289
                $total_score = self::get_avg_student_score(
1290
                    $user_id,
1291
                    $course,
1292
                    $a_my_id,
1293
                    $session,
1294
                    false,
1295
                    false
1296
                );
1297
            } else {
1298
                // "Left green cross" extended
1299
                $total_score = self::get_avg_student_score(
1300
                    $user_id,
1301
                    $course,
1302
                    $a_my_id,
1303
                    $session,
1304
                    false,
1305
                    true
1306
                );
1307
            }
1308
        } else {
1309
            // Extend all "left green cross"
1310
            $total_score = self::get_avg_student_score(
1311
                $user_id,
1312
                $course,
1313
                [$lp_id],
1314
                $session,
1315
                false,
1316
                false
1317
            );
1318
        }
1319
1320
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1321
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1322
1323
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1324
            $final_score = Display::getMdiIcon(
1325
                ActionIcon::INVISIBLE,
1326
                'ch-tool-icon',
1327
                null,
1328
                ICON_SIZE_SMALL,
1329
                get_lang('Results hidden by the exercise setting')
1330
            );
1331
            $finalScoreToCsv = get_lang('Results hidden by the exercise setting');
1332
        } else {
1333
            if (is_numeric($total_score)) {
1334
                $final_score = $total_score.'%';
1335
            } else {
1336
                $final_score = $total_score;
1337
            }
1338
            $finalScoreToCsv = $final_score;
1339
        }
1340
        $progress = learnpath::getProgress($lp_id, $user_id, $courseId, $sessionId);
1341
1342
        $oddclass = 'row_even';
1343
        if (0 == ($counter % 2)) {
1344
            $oddclass = 'row_odd';
1345
        }
1346
1347
        $action = null;
1348
        if ('classic' === $type) {
1349
            $action = '<td></td>';
1350
        }
1351
1352
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1353
        if ($hideTime) {
1354
            $timeTotal = '';
1355
        }
1356
1357
        $output .= '<tr class="'.$oddclass.'">
1358
                <td></td>
1359
                <td colspan="4">
1360
                    <i>'.get_lang('Total of completed learning objects').'</i>
1361
                </td>
1362
                <td colspan="2">'.$progress.'%</td>
1363
                <td colspan="2">'.$final_score.'</td>
1364
                '.$timeTotal.'
1365
                '.$action.'
1366
           </tr>';
1367
1368
        $output .= '
1369
                    </tbody>
1370
                </table>
1371
            </div>
1372
        ';
1373
1374
        if (!empty($export_csv)) {
1375
            $temp = [
1376
                '',
1377
                '',
1378
                '',
1379
                '',
1380
            ];
1381
            $csv_content[] = $temp;
1382
            $temp = [
1383
                get_lang('Total of completed learning objects'),
1384
                '',
1385
                $finalScoreToCsv,
1386
            ];
1387
1388
            if (false === $hideTime) {
1389
                $temp[] = $total_time;
1390
            }
1391
1392
            $csv_content[] = $temp;
1393
            ob_end_clean();
1394
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1395
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

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