Passed
Push — master ( 75cb6d...4670ec )
by Julito
11:34
created

Tracking::generateQuizzesTable()   C

Complexity

Conditions 13
Paths 118

Size

Total Lines 167
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 105
nc 118
nop 2
dl 0
loc 167
rs 5.1733
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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