Passed
Pull Request — master (#5853)
by
unknown
07:37
created

get_exercise_student_average_best_attempt()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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