Passed
Push — dependabot/composer/symfony/se... ( 6a3ee0...ae0984 )
by
unknown
28:58 queued 18:58
created

TrackingCourseLog::get_user_data()   F

Complexity

Conditions 51
Paths > 20000

Size

Total Lines 405
Code Lines 265

Duplication

Lines 0
Ratio 0 %

Importance

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