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

TrackingCourseLog::display_additional_profile_fields()   B

Complexity

Conditions 9
Paths 24

Size

Total Lines 40
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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