Completed
Push — master ( 9ea0a4...4aa6a0 )
by Julito
45:51 queued 25:47
created

Tracking::count_login_per_student()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 12
nc 1
nop 3
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Course;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Course. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
5
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
6
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
7
use Chamilo\UserBundle\Entity\User;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, User. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use ChamiloSession as Session;
9
use CpChart\Cache as pCache;
10
use CpChart\Data as pData;
11
use CpChart\Image as pImage;
12
13
/**
14
 *  Class Tracking.
15
 *
16
 *  @author  Julio Montoya <[email protected]>
17
 *
18
 *  @package chamilo.library
19
 */
20
class Tracking
21
{
22
    /**
23
     * Get group reporting.
24
     *
25
     * @param int    $course_id
26
     * @param int    $sessionId
27
     * @param int    $group_id
28
     * @param string $type
29
     * @param int    $start
30
     * @param int    $limit
31
     * @param int    $sidx
32
     * @param string $sord
33
     * @param array  $where_condition
34
     *
35
     * @return array|null
36
     */
37
    public static function get_group_reporting(
38
        $course_id,
39
        $sessionId = 0,
40
        $group_id = 0,
0 ignored issues
show
Unused Code introduced by
The parameter $group_id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

40
        /** @scrutinizer ignore-unused */ $group_id = 0,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
41
        $type = 'all',
42
        $start = 0,
0 ignored issues
show
Unused Code introduced by
The parameter $start is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

42
        /** @scrutinizer ignore-unused */ $start = 0,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
43
        $limit = 1000,
0 ignored issues
show
Unused Code introduced by
The parameter $limit is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

43
        /** @scrutinizer ignore-unused */ $limit = 1000,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
44
        $sidx = 1,
0 ignored issues
show
Unused Code introduced by
The parameter $sidx is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

44
        /** @scrutinizer ignore-unused */ $sidx = 1,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
45
        $sord = 'desc',
0 ignored issues
show
Unused Code introduced by
The parameter $sord is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

45
        /** @scrutinizer ignore-unused */ $sord = 'desc',

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
46
        $where_condition = []
0 ignored issues
show
Unused Code introduced by
The parameter $where_condition is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

46
        /** @scrutinizer ignore-unused */ $where_condition = []

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
47
    ) {
48
        $course_id = (int) $course_id;
49
        $sessionId = (int) $sessionId;
50
51
        if (empty($course_id)) {
52
            return null;
53
        }
54
        $courseInfo = api_get_course_info_by_id($course_id);
55
        if ($type == 'count') {
56
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
57
        }
58
59
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
60
        $parsedResult = [];
61
        if (!empty($groupList)) {
62
            foreach ($groupList as $group) {
63
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
64
                $time = 0;
65
                $avg_student_score = 0;
66
                $avg_student_progress = 0;
67
                $work = 0;
68
                $messages = 0;
69
70
                foreach ($users as $user_data) {
71
                    $time += self::get_time_spent_on_the_course(
72
                        $user_data['user_id'],
73
                        $courseInfo['real_id'],
74
                        $sessionId
75
                    );
76
                    $average = self::get_avg_student_score(
77
                        $user_data['user_id'],
78
                        $courseInfo['code'],
79
                        [],
80
                        $sessionId
81
                    );
82
                    if (is_numeric($average)) {
83
                        $avg_student_score += $average;
84
                    }
85
                    $avg_student_progress += self::get_avg_student_progress(
86
                        $user_data['user_id'],
87
                        $courseInfo['code'],
88
                        [],
89
                        $sessionId
90
                    );
91
                    $work += self::count_student_assignments(
92
                        $user_data['user_id'],
93
                        $courseInfo['code'],
94
                        $sessionId
95
                    );
96
                    $messages += self::count_student_messages(
97
                        $user_data['user_id'],
98
                        $courseInfo['code'],
99
                        $sessionId
100
                    );
101
                }
102
103
                $countUsers = count($users);
104
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
105
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
106
107
                $groupItem = [
108
                    'id' => $group['id'],
109
                    'name' => $group['name'],
110
                    'time' => api_time_to_hms($time),
111
                    'progress' => $averageProgress,
112
                    'score' => $averageScore,
113
                    'works' => $work,
114
                    'messages' => $messages,
115
                ];
116
                $parsedResult[] = $groupItem;
117
            }
118
        }
119
120
        return $parsedResult;
121
    }
122
123
    /**
124
     * @param int    $user_id
125
     * @param array  $courseInfo
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 null|string
139
     */
140
    public static function getLpStats(
141
        $user_id,
142
        $courseInfo,
143
        $session_id,
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($courseInfo) || empty($lp_id)) {
156
            return null;
157
        }
158
159
        $hideTime = api_get_configuration_value('hide_lp_time');
160
161
        $lp_id = intval($lp_id);
162
        $lp_item_id = intval($lp_item_id);
163
        $user_id = intval($user_id);
164
        $session_id = intval($session_id);
165
        $origin = Security::remove_XSS($origin);
166
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
167
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
168
        $course_id = $courseInfo['real_id'];
169
        $courseCode = $courseInfo['code'];
170
        $session_condition = api_get_session_condition($session_id);
171
172
        // Extend all button
173
        $output = null;
174
        $extend_all = 0;
175
        if ($origin == 'tracking') {
176
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
177
        } else {
178
            $url_suffix = '&lp_id='.$lp_id;
179
        }
180
181
        if (!empty($extendedAll)) {
182
            $extend_all_link = Display::url(
183
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
184
                api_get_self().'?action=stats'.$url_suffix
185
            );
186
            $extend_all = 1;
187
        } else {
188
            $extend_all_link = Display::url(
189
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
190
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
191
            );
192
        }
193
194
        if ($origin != 'tracking') {
195
            $output .= '<div class="section-status">';
196
            $output .= Display::page_header(get_lang('ScormMystatus'));
197
            $output .= '</div>';
198
        }
199
200
        $actionColumn = null;
201
        if ($type == 'classic') {
202
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
203
        }
204
205
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
206
        if ($hideTime) {
207
            $timeHeader = '';
208
        }
209
        $output .= '<div class="table-responsive">';
210
        $output .= '<table id="lp_tracking" class="table tracking">
211
            <thead>
212
            <tr class="table-header">
213
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
214
                <th colspan="4">
215
                    '.get_lang('ScormLessonTitle').'
216
                </th>
217
                <th colspan="2">
218
                    '.get_lang('ScormStatus').'
219
                </th>
220
                <th colspan="2">
221
                    '.get_lang('ScormScore').'
222
                </th>
223
                '.$timeHeader.'
224
                '.$actionColumn.'
225
                </tr>
226
            </thead>
227
            <tbody>
228
        ';
229
230
        // Going through the items using the $items[] array instead of the database order ensures
231
        // we get them in the same order as in the imsmanifest file, which is rather random when using
232
        // the database table.
233
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
234
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
235
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
236
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
237
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
238
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
239
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
240
241
        $sql = "SELECT max(view_count)
242
                FROM $TBL_LP_VIEW
243
                WHERE
244
                    c_id = $course_id AND
245
                    lp_id = $lp_id AND
246
                    user_id = $user_id
247
                    $session_condition";
248
        $res = Database::query($sql);
249
        $view = '';
250
        if (Database::num_rows($res) > 0) {
251
            $myrow = Database::fetch_array($res);
252
            $view = $myrow[0];
253
        }
254
255
        $counter = 0;
256
        $total_time = 0;
257
        $h = get_lang('h');
258
259
        if (!empty($export_csv)) {
260
            $csvHeaders = [
261
                get_lang('ScormLessonTitle'),
262
                get_lang('ScormStatus'),
263
                get_lang('ScormScore'),
264
            ];
265
266
            if ($hideTime === false) {
267
                $csvHeaders[] = get_lang('ScormTime');
268
            }
269
270
            $csv_content[] = $csvHeaders;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$csv_content was never initialized. Although not strictly required by PHP, it is generally a good practice to add $csv_content = array(); before regardless.
Loading history...
271
        }
272
273
        $result_disabled_ext_all = true;
274
        $chapterTypes = learnpath::getChapterTypes();
275
276
        // Show lp items
277
        if (is_array($list) && count($list) > 0) {
278
            foreach ($list as $my_item_id) {
279
                $extend_this = 0;
280
                $order = 'DESC';
281
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
282
                    $extend_this = 1;
283
                    $order = 'ASC';
284
                }
285
286
                // Prepare statement to go through each attempt.
287
                $viewCondition = null;
288
                if (!empty($view)) {
289
                    $viewCondition = " AND v.view_count = $view  ";
290
                }
291
292
                $sql = "SELECT
293
                    iv.status as mystatus,
294
                    v.view_count as mycount,
295
                    iv.score as myscore,
296
                    iv.total_time as mytime,
297
                    i.id as myid,
298
                    i.lp_id as mylpid,
299
                    iv.lp_view_id as mylpviewid,
300
                    i.title as mytitle,
301
                    i.max_score as mymaxscore,
302
                    iv.max_score as myviewmaxscore,
303
                    i.item_type as item_type,
304
                    iv.view_count as iv_view_count,
305
                    iv.id as iv_id,
306
                    path
307
                FROM $TBL_LP_ITEM as i
308
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
309
                ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
310
                INNER JOIN $TBL_LP_VIEW as v
311
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
312
                WHERE
313
                    v.c_id = $course_id AND
314
                    i.id = $my_item_id AND
315
                    i.lp_id = $lp_id  AND
316
                    v.user_id = $user_id AND
317
                    v.session_id = $session_id
318
                    $viewCondition
319
                ORDER BY iv.view_count $order ";
320
321
                $result = Database::query($sql);
322
                $num = Database::num_rows($result);
323
                $time_for_total = 0;
324
325
                // Extend all
326
                if (($extend_this || $extend_all) && $num > 0) {
327
                    $row = Database::fetch_array($result);
328
                    $result_disabled_ext_all = false;
329
                    if ($row['item_type'] == 'quiz') {
330
                        // Check results_disabled in quiz table.
331
                        $my_path = Database::escape_string($row['path']);
332
333
                        $sql = "SELECT results_disabled
334
                                FROM $TBL_QUIZ
335
                                WHERE
336
                                    c_id = $course_id AND
337
                                    id ='".$my_path."'";
338
                        $res_result_disabled = Database::query($sql);
339
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
340
341
                        if (Database::num_rows($res_result_disabled) > 0 &&
342
                            (int) $row_result_disabled[0] === 1
343
                        ) {
344
                            $result_disabled_ext_all = true;
345
                        }
346
                    }
347
348
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
349
                    if (($counter % 2) == 0) {
350
                        $oddclass = 'row_odd';
351
                    } else {
352
                        $oddclass = 'row_even';
353
                    }
354
355
                    $extend_link = '';
356
                    if (!empty($inter_num)) {
357
                        $extend_link = Display::url(
358
                            Display::return_icon(
359
                                'visible.gif',
360
                                get_lang('HideAttemptView')
361
                            ),
362
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
363
                        );
364
                    }
365
                    $title = $row['mytitle'];
366
367
                    if (empty($title)) {
368
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
369
                    }
370
371
                    if (in_array($row['item_type'], $chapterTypes)) {
372
                        $title = "<h4> $title </h4>";
373
                    }
374
                    $lesson_status = $row['mystatus'];
375
                    $title = Security::remove_XSS($title);
376
                    $counter++;
377
378
                    $action = null;
379
                    if ($type == 'classic') {
380
                        $action = '<td></td>';
381
                    }
382
383
                    if (in_array($row['item_type'], $chapterTypes)) {
384
                        $output .= '<tr class="'.$oddclass.'">
385
                                <td>'.$extend_link.'</td>
386
                                <td colspan="4">
387
                                   '.$title.'
388
                                </td>
389
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
390
                                <td colspan="2"></td>
391
                                <td colspan="2"></td>
392
                                '.$action.'
393
                            </tr>';
394
                        continue;
395
                    } else {
396
                        $output .= '<tr class="'.$oddclass.'">
397
                                <td>'.$extend_link.'</td>
398
                                <td colspan="4">
399
                                   '.$title.'
400
                                </td>
401
                                <td colspan="2"></td>
402
                                <td colspan="2"></td>
403
                                <td colspan="2"></td>
404
                                '.$action.'
405
                            </tr>';
406
                    }
407
408
                    $attemptCount = 1;
409
                    do {
410
                        // Check if there are interactions below.
411
                        $extend_attempt_link = '';
412
                        $extend_this_attempt = 0;
413
414
                        if ((learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
415
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0) &&
416
                            !$extend_all
417
                        ) {
418
                            if ($extendAttemptId == $row['iv_id']) {
419
                                // The extend button for this attempt has been clicked.
420
                                $extend_this_attempt = 1;
421
                                $extend_attempt_link = Display::url(
422
                                    Display::return_icon('visible.gif', get_lang('HideAttemptView')),
423
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
424
                                );
425
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
426
                                // The extend button for this attempt has not been clicked.
427
                                $extend_attempt_link = Display::url(
428
                                    Display::return_icon('invisible.gif', get_lang('ExtendAttemptView')),
429
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
430
                                );
431
                            }
432
                        }
433
434
                        if (($counter % 2) == 0) {
435
                            $oddclass = 'row_odd';
436
                        } else {
437
                            $oddclass = 'row_even';
438
                        }
439
440
                        $lesson_status = $row['mystatus'];
441
                        $score = $row['myscore'];
442
                        $time_for_total = $row['mytime'];
443
                        $time = learnpathItem::getScormTimeFromParameter('js', $row['mytime']);
444
445
                        if ($score == 0) {
446
                            $maxscore = $row['mymaxscore'];
447
                        } else {
448
                            if ($row['item_type'] == 'sco') {
449
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
450
                                    $maxscore = $row['myviewmaxscore'];
451
                                } elseif ($row['myviewmaxscore'] === '') {
452
                                    $maxscore = 0;
453
                                } else {
454
                                    $maxscore = $row['mymaxscore'];
455
                                }
456
                            } else {
457
                                $maxscore = $row['mymaxscore'];
458
                            }
459
                        }
460
461
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
462
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
463
464
                        if ($row['item_type'] != 'dir') {
465
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
466
                                $view_score = Display::return_icon(
467
                                    'invisible.gif',
468
                                    get_lang('ResultsHiddenByExerciseSetting')
469
                                );
470
                            } else {
471
                                switch ($row['item_type']) {
472
                                    case 'sco':
473
                                        if ($maxscore == 0) {
474
                                            $view_score = $score;
475
                                        } else {
476
                                            $view_score = ExerciseLib::show_score(
477
                                                $score,
478
                                                $maxscore,
479
                                                false
480
                                            );
481
                                        }
482
                                        break;
483
                                    case 'document':
484
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
485
                                        break;
486
                                    default:
487
                                        $view_score = ExerciseLib::show_score(
488
                                            $score,
489
                                            $maxscore,
490
                                            false
491
                                        );
492
                                        break;
493
                                }
494
                            }
495
496
                            $action = null;
497
                            if ($type == 'classic') {
498
                                $action = '<td></td>';
499
                            }
500
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
501
                            if ($hideTime) {
502
                                $timeRow = '';
503
                            }
504
                            $output .= '<tr class="'.$oddclass.'">
505
                                    <td></td>
506
                                    <td>'.$extend_attempt_link.'</td>
507
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
508
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
509
                                    <td colspan="2">'.$view_score.'</td>
510
                                    '.$timeRow.'
511
                                    '.$action.'
512
                                </tr>';
513
                            $attemptCount++;
514
                            if (!empty($export_csv)) {
515
                                $temp = [];
516
                                $temp[] = $title = Security::remove_XSS($title);
517
                                $temp[] = Security::remove_XSS(
518
                                    learnpathItem::humanize_status($lesson_status, false, $type)
519
                                );
520
521
                                if ($row['item_type'] == 'quiz') {
522
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
523
                                        $temp[] = '/';
524
                                    } else {
525
                                        $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
526
                                    }
527
                                } else {
528
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
529
                                }
530
531
                                if ($hideTime === false) {
532
                                    $temp[] = $time;
533
                                }
534
                                $csv_content[] = $temp;
535
                            }
536
                        }
537
538
                        $counter++;
539
                        $action = null;
540
                        if ($type == 'classic') {
541
                            $action = '<td></td>';
542
                        }
543
544
                        if ($extend_this_attempt || $extend_all) {
545
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id']);
546
                            foreach ($list1 as $id => $interaction) {
547
                                if (($counter % 2) == 0) {
548
                                    $oddclass = 'row_odd';
549
                                } else {
550
                                    $oddclass = 'row_even';
551
                                }
552
                                $student_response = urldecode($interaction['student_response']);
553
                                $content_student_response = explode('__|', $student_response);
554
555
                                if (count($content_student_response) > 0) {
556
                                    if (count($content_student_response) >= 3) {
557
                                        // Pop the element off the end of array.
558
                                        array_pop($content_student_response);
559
                                    }
560
                                    $student_response = implode(',', $content_student_response);
561
                                }
562
563
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
564
                                if ($hideTime) {
565
                                    $timeRow = '';
566
                                }
567
568
                                $output .= '<tr class="'.$oddclass.'">
569
                                        <td></td>
570
                                        <td></td>
571
                                        <td></td>
572
                                        <td>'.$interaction['order_id'].'</td>
573
                                        <td>'.$interaction['id'].'</td>
574
                                        <td colspan="2">'.$interaction['type'].'</td>
575
                                        <td>'.$student_response.'</td>
576
                                        <td>'.$interaction['result'].'</td>
577
                                        <td>'.$interaction['latency'].'</td>
578
                                        '.$timeRow.'
579
                                        '.$action.'
580
                                    </tr>';
581
                                $counter++;
582
                            }
583
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id']);
584
                            foreach ($list2 as $id => $interaction) {
585
                                if (($counter % 2) == 0) {
586
                                    $oddclass = 'row_odd';
587
                                } else {
588
                                    $oddclass = 'row_even';
589
                                }
590
                                $output .= '<tr class="'.$oddclass.'">
591
                                        <td></td>
592
                                        <td></td>
593
                                        <td></td>
594
                                        <td>'.$interaction['order_id'].'</td>
595
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
596
                                        <td colspan="2">'.$interaction['status'].'</td>
597
                                        <td>'.$interaction['score_raw'].'</td>
598
                                        <td>'.$interaction['score_max'].'</td>
599
                                        <td>'.$interaction['score_min'].'</td>
600
                                        '.$action.'
601
                                     </tr>';
602
                                $counter++;
603
                            }
604
                        }
605
                    } while ($row = Database::fetch_array($result));
606
                } elseif ($num > 0) {
607
                    // Not extended.
608
                    $row = Database::fetch_array($result, 'ASSOC');
609
                    $my_id = $row['myid'];
610
                    $my_lp_id = $row['mylpid'];
611
                    $my_lp_view_id = $row['mylpviewid'];
612
                    $my_path = $row['path'];
613
                    $result_disabled_ext_all = false;
614
                    if ($row['item_type'] == 'quiz') {
615
                        // Check results_disabled in quiz table.
616
                        $my_path = Database::escape_string($my_path);
617
                        $sql = "SELECT results_disabled
618
                                FROM $TBL_QUIZ
619
                                WHERE c_id = $course_id AND id ='".$my_path."'";
620
                        $res_result_disabled = Database::query($sql);
621
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
622
623
                        if (Database::num_rows($res_result_disabled) > 0 &&
624
                            (int) $row_result_disabled[0] === 1
625
                        ) {
626
                            $result_disabled_ext_all = true;
627
                        }
628
                    }
629
630
                    // Check if there are interactions below
631
                    $extend_this_attempt = 0;
632
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
633
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
634
                    $extend_attempt_link = '';
635
                    if ($inter_num > 0 || $objec_num > 0) {
636
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
637
                            // The extend button for this attempt has been clicked.
638
                            $extend_this_attempt = 1;
639
                            $extend_attempt_link = Display::url(
640
                                Display::return_icon('visible.gif', get_lang('HideAttemptView')),
641
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
642
                            );
643
                        } else {
644
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
645
                            // The extend button for this attempt has not been clicked.
646
                            $extend_attempt_link = Display::url(
647
                                Display::return_icon('invisible.gif', get_lang('ExtendAttemptView')),
648
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
649
                            );
650
                        }
651
                    }
652
653
                    if (($counter % 2) == 0) {
654
                        $oddclass = 'row_odd';
655
                    } else {
656
                        $oddclass = 'row_even';
657
                    }
658
659
                    $extend_link = '';
660
                    if ($inter_num > 1) {
661
                        $extend_link = Display::url(
662
                            Display::return_icon('invisible.gif', get_lang('ExtendAttemptView')),
663
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
664
                        );
665
                    }
666
667
                    $lesson_status = $row['mystatus'];
668
                    $score = $row['myscore'];
669
                    $subtotal_time = $row['mytime'];
670
671
                    while ($tmp_row = Database::fetch_array($result)) {
672
                        $subtotal_time += $tmp_row['mytime'];
673
                    }
674
                    $title = $row['mytitle'];
675
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
676
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
677
                            WHERE
678
                                exe_exo_id="'.$row['path'].'" AND
679
                                exe_user_id="'.$user_id.'" AND
680
                                orig_lp_id = "'.$lp_id.'" AND
681
                                orig_lp_item_id = "'.$row['myid'].'" AND
682
                                c_id = '.$course_id.' AND
683
                                status <> "incomplete" AND
684
                                session_id = '.$session_id.'
685
                             ORDER BY exe_date DESC
686
                             LIMIT 1';
687
688
                    $resultLastAttempt = Database::query($sql);
689
                    $num = Database::num_rows($resultLastAttempt);
690
                    $id_last_attempt = null;
691
                    if ($num > 0) {
692
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
693
                            $id_last_attempt = $rowLA['exe_id'];
694
                        }
695
                    }
696
697
                    switch ($row['item_type']) {
698
                        case 'sco':
699
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
700
                                $maxscore = $row['myviewmaxscore'];
701
                            } elseif ($row['myviewmaxscore'] === '') {
702
                                $maxscore = 0;
703
                            } else {
704
                                $maxscore = $row['mymaxscore'];
705
                            }
706
                            break;
707
                        case 'quiz':
708
                            // Get score and total time from last attempt of a exercise en lp.
709
                            $sql = "SELECT iid, score
710
                                    FROM $TBL_LP_ITEM_VIEW
711
                                    WHERE
712
                                        c_id = $course_id AND
713
                                        lp_item_id = '".(int) $my_id."' AND
714
                                        lp_view_id = '".(int) $my_lp_view_id."'
715
                                    ORDER BY view_count DESC 
716
                                    LIMIT 1";
717
                            $res_score = Database::query($sql);
718
                            $row_score = Database::fetch_array($res_score);
719
720
                            $sql = "SELECT SUM(total_time) as total_time
721
                                    FROM $TBL_LP_ITEM_VIEW
722
                                    WHERE
723
                                        c_id = $course_id AND
724
                                        lp_item_id = '".(int) $my_id."' AND
725
                                        lp_view_id = '".(int) $my_lp_view_id."'";
726
                            $res_time = Database::query($sql);
727
                            $row_time = Database::fetch_array($res_time);
728
729
                            $score = 0;
730
                            $subtotal_time = 0;
731
                            if (Database::num_rows($res_score) > 0 &&
732
                                Database::num_rows($res_time) > 0
733
                            ) {
734
                                $score = (float) $row_score['score'];
735
                                $subtotal_time = (int) $row_time['total_time'];
736
                            }
737
                            // Selecting the max score from an attempt.
738
                            $sql = "SELECT SUM(t.ponderation) as maxscore
739
                                    FROM (
740
                                        SELECT DISTINCT
741
                                            question_id, marks, ponderation
742
                                        FROM $tbl_stats_attempts as at
743
                                        INNER JOIN $tbl_quiz_questions as q
744
                                        ON (q.id = at.question_id AND q.c_id = $course_id)
745
                                        WHERE exe_id ='$id_last_attempt'
746
                                    ) as t";
747
748
                            $result = Database::query($sql);
749
                            $row_max_score = Database::fetch_array($result);
750
                            $maxscore = $row_max_score['maxscore'];
751
752
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
753
                            $sql = 'SELECT SUM(exe_duration) exe_duration 
754
                                    FROM '.$tbl_stats_exercices.'
755
                                    WHERE
756
                                        exe_exo_id="'.$row['path'].'" AND
757
                                        exe_user_id="'.$user_id.'" AND
758
                                        orig_lp_id = "'.$lp_id.'" AND
759
                                        orig_lp_item_id = "'.$row['myid'].'" AND
760
                                        c_id = '.$course_id.' AND
761
                                        status <> "incomplete" AND
762
                                        session_id = '.$session_id.'
763
                                     ORDER BY exe_date DESC ';
764
                            $sumScoreResult = Database::query($sql);
765
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
766
                            if (!empty($durationRow['exe_duration'])) {
767
                                $exeDuration = $durationRow['exe_duration'];
768
                                if ($exeDuration != $subtotal_time &&
769
                                    !empty($row_score['iid']) &&
770
                                    !empty($exeDuration)
771
                                ) {
772
                                    $subtotal_time = $exeDuration;
773
                                    // Update c_lp_item_view.total_time
774
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration' 
775
                                                  WHERE iid = ".$row_score['iid'];
776
                                    Database::query($sqlUpdate);
777
                                }
778
                            }
779
                            break;
780
                        default:
781
                            $maxscore = $row['mymaxscore'];
782
                            break;
783
                    }
784
785
                    $time_for_total = $subtotal_time;
786
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
787
                    if (empty($title)) {
788
                        $title = learnpath::rl_get_resource_name(
789
                            $courseInfo['code'],
790
                            $lp_id,
791
                            $row['myid']
792
                        );
793
                    }
794
795
                    $action = null;
796
                    if ($type == 'classic') {
797
                        $action = '<td></td>';
798
                    }
799
800
                    if (in_array($row['item_type'], $chapterTypes)) {
801
                        $title = Security::remove_XSS($title);
802
                        $output .= '<tr class="'.$oddclass.'">
803
                                <td>'.$extend_link.'</td>
804
                                <td colspan="4">
805
                                <h4>'.$title.'</h4>
806
                                </td>
807
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
808
                                <td colspan="2"></td>
809
                                <td colspan="2"></td>
810
                                '.$action.'
811
                            </tr>';
812
                    } else {
813
                        $correct_test_link = '-';
814
                        $showRowspan = false;
815
                        if ($row['item_type'] == 'quiz') {
816
                            $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
817
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
818
                                     WHERE
819
                                        exe_exo_id="'.$row['path'].'" AND
820
                                        exe_user_id="'.$user_id.'" AND
821
                                        orig_lp_id = "'.$lp_id.'" AND
822
                                        orig_lp_item_id = "'.$row['myid'].'" AND
823
                                        c_id = '.$course_id.' AND
824
                                        status <> "incomplete" AND
825
                                        session_id = '.$session_id.'
826
                                     ORDER BY exe_date DESC ';
827
828
                            $resultLastAttempt = Database::query($sql);
829
                            $num = Database::num_rows($resultLastAttempt);
830
                            $showRowspan = false;
831
                            if ($num > 0) {
832
                                $linkId = 'link_'.$my_id;
833
                                if ($extendedAttempt == 1 &&
834
                                    $lp_id == $my_lp_id &&
835
                                    $lp_item_id == $my_id
836
                                ) {
837
                                    $showRowspan = true;
838
                                    $correct_test_link = Display::url(
839
                                        Display::return_icon(
840
                                            'view_less_stats.gif',
841
                                            get_lang('HideAllAttempts')
842
                                        ),
843
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
844
                                        ['id' => $linkId]
845
                                    );
846
                                } else {
847
                                    $correct_test_link = Display::url(
848
                                        Display::return_icon(
849
                                            'view_more_stats.gif',
850
                                            get_lang(
851
                                                'ShowAllAttemptsByExercise'
852
                                            )
853
                                        ),
854
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
855
                                        ['id' => $linkId]
856
                                    );
857
                                }
858
                            }
859
                        }
860
861
                        $title = Security::remove_XSS($title);
862
                        $action = null;
863
                        if ($type == 'classic') {
864
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
865
                        }
866
867
                        if ($lp_id == $my_lp_id && false) {
868
                            $output .= '<tr class ='.$oddclass.'>
869
                                    <td>'.$extend_link.'</td>
870
                                    <td colspan="4">'.$title.'</td>
871
                                    <td colspan="2">&nbsp;</td>
872
                                    <td colspan="2">&nbsp;</td>
873
                                    <td colspan="2">&nbsp;</td>
874
                                    '.$action.'
875
                                </tr>';
876
                            $output .= '</tr>';
877
                        } else {
878
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
879
                                $output .= "<tr class='$oddclass'>";
880
                            } else {
881
                                $output .= "<tr class='$oddclass'>";
882
                            }
883
884
                            $scoreItem = null;
885
                            if ($row['item_type'] == 'quiz') {
886
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
887
                                    $scoreItem .= Display::return_icon(
888
                                        'invisible.gif',
889
                                        get_lang('ResultsHiddenByExerciseSetting')
890
                                    );
891
                                } else {
892
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
893
                                }
894
                            } else {
895
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
896
                            }
897
898
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
899
                            if ($hideTime) {
900
                                $timeRow = '';
901
                            }
902
903
                            $output .= '
904
                                <td>'.$extend_link.'</td>
905
                                <td colspan="4">'.$title.'</td>
906
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
907
                                <td colspan="2">'.$scoreItem.'</td>
908
                                '.$timeRow.'
909
                                '.$action.'
910
                             ';
911
                            $output .= '</tr>';
912
                        }
913
914
                        if (!empty($export_csv)) {
915
                            $temp = [];
916
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
917
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
918
919
                            if ($row['item_type'] == 'quiz') {
920
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
921
                                    $temp[] = '/';
922
                                } else {
923
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
924
                                }
925
                            } else {
926
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
927
                            }
928
929
                            if ($hideTime === false) {
930
                                $temp[] = $time;
931
                            }
932
                            $csv_content[] = $temp;
933
                        }
934
                    }
935
936
                    $counter++;
937
                    $action = null;
938
                    if ($type == 'classic') {
939
                        $action = '<td></td>';
940
                    }
941
942
                    if ($extend_this_attempt || $extend_all) {
943
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id']);
944
                        foreach ($list1 as $id => $interaction) {
945
                            if (($counter % 2) == 0) {
946
                                $oddclass = 'row_odd';
947
                            } else {
948
                                $oddclass = 'row_even';
949
                            }
950
951
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
952
                            if ($hideTime) {
953
                                $timeRow = '';
954
                            }
955
956
                            $output .= '<tr class="'.$oddclass.'">
957
                                    <td></td>
958
                                    <td></td>
959
                                    <td></td>
960
                                    <td>'.$interaction['order_id'].'</td>
961
                                    <td>'.$interaction['id'].'</td>
962
                                    <td colspan="2">'.$interaction['type'].'</td>
963
                                    <td>'.urldecode($interaction['student_response']).'</td>
964
                                    <td>'.$interaction['result'].'</td>
965
                                    <td>'.$interaction['latency'].'</td>
966
                                    '.$timeRow.'
967
                                    '.$action.'
968
                               </tr>';
969
                            $counter++;
970
                        }
971
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id']);
972
                        foreach ($list2 as $id => $interaction) {
973
                            if (($counter % 2) == 0) {
974
                                $oddclass = 'row_odd';
975
                            } else {
976
                                $oddclass = 'row_even';
977
                            }
978
                            $output .= '<tr class="'.$oddclass.'">
979
                                    <td></td>
980
                                    <td></td>
981
                                    <td></td>
982
                                    <td>'.$interaction['order_id'].'</td>
983
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
984
                                    <td colspan="2">'.$interaction['status'].'</td>
985
                                    <td>'.$interaction['score_raw'].'</td>
986
                                    <td>'.$interaction['score_max'].'</td>
987
                                    <td>'.$interaction['score_min'].'</td>
988
                                    '.$action.'
989
                               </tr>';
990
                            $counter++;
991
                        }
992
                    }
993
994
                    // Attempts listing by exercise.
995
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
996
                        // Get attempts of a exercise.
997
                        if (!empty($lp_id) &&
998
                            !empty($lp_item_id) &&
999
                            $row['item_type'] === 'quiz'
1000
                        ) {
1001
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1002
                                    WHERE
1003
                                        c_id = $course_id AND
1004
                                        id = '$lp_item_id' AND
1005
                                        lp_id = '$lp_id'";
1006
                            $res_path = Database::query($sql);
1007
                            $row_path = Database::fetch_array($res_path);
1008
1009
                            if (Database::num_rows($res_path) > 0) {
1010
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1011
                                        WHERE
1012
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1013
                                            status <> "incomplete" AND
1014
                                            exe_user_id="'.$user_id.'" AND
1015
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1016
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1017
                                            c_id = '.$course_id.'  AND
1018
                                            session_id = '.$session_id.'
1019
                                        ORDER BY exe_date';
1020
                                $res_attempts = Database::query($sql);
1021
                                $num_attempts = Database::num_rows($res_attempts);
1022
                                if ($num_attempts > 0) {
1023
                                    $n = 1;
1024
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1025
                                        $my_score = $row_attempts['exe_result'];
1026
                                        $my_maxscore = $row_attempts['exe_weighting'];
1027
                                        $my_exe_id = $row_attempts['exe_id'];
1028
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1029
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1030
                                        $time_attemp = ' - ';
1031
                                        if ($mktime_start_date && $mktime_exe_date) {
1032
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1033
                                        }
1034
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1035
                                            $view_score = Display::return_icon(
1036
                                                'invisible.gif',
1037
                                                get_lang(
1038
                                                    'ResultsHiddenByExerciseSetting'
1039
                                                )
1040
                                            );
1041
                                        } else {
1042
                                            // Show only float when need it
1043
                                            if ($my_score == 0) {
1044
                                                $view_score = ExerciseLib::show_score(
1045
                                                    0,
1046
                                                    $my_maxscore,
1047
                                                    false
1048
                                                );
1049
                                            } else {
1050
                                                if ($my_maxscore == 0) {
1051
                                                    $view_score = $my_score;
1052
                                                } else {
1053
                                                    $view_score = ExerciseLib::show_score(
1054
                                                        $my_score,
1055
                                                        $my_maxscore,
1056
                                                        false
1057
                                                    );
1058
                                                }
1059
                                            }
1060
                                        }
1061
                                        $my_lesson_status = $row_attempts['status'];
1062
                                        if ($my_lesson_status == '') {
1063
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1064
                                        } elseif ($my_lesson_status == 'incomplete') {
1065
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1066
                                        }
1067
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1068
                                        if ($hideTime) {
1069
                                            $timeRow = '';
1070
                                        }
1071
1072
                                        $output .= '<tr class="'.$oddclass.'" >
1073
                                        <td></td>
1074
                                        <td>'.$extend_attempt_link.'</td>
1075
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1076
                                        <td colspan="2">'.$my_lesson_status.'</td>
1077
                                        <td colspan="2">'.$view_score.'</td>
1078
                                        '.$timeRow;
1079
1080
                                        if ($action == 'classic') {
1081
                                            if ($origin != 'tracking') {
1082
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1083
                                                    $output .= '<td>
1084
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1085
                                                            </td>';
1086
                                                } else {
1087
                                                    $output .= '<td>
1088
                                                            <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
1089
                                                            <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
1090
                                                            </a></td>';
1091
                                                }
1092
                                            } else {
1093
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1094
                                                    $output .= '<td>
1095
                                                                <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
1096
                                                } else {
1097
                                                    $output .= '<td>
1098
                                                                    <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
1099
                                                                    <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
1100
                                                }
1101
                                            }
1102
                                        }
1103
                                        $output .= '</tr>';
1104
                                        $n++;
1105
                                    }
1106
                                }
1107
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1108
                            }
1109
                        }
1110
                    }
1111
                }
1112
1113
                $total_time += $time_for_total;
1114
                // QUIZZ IN LP
1115
                $a_my_id = [];
1116
                if (!empty($my_lp_id)) {
1117
                    $a_my_id[] = $my_lp_id;
1118
                }
1119
            }
1120
        }
1121
1122
        // NOT Extend all "left green cross"
1123
        if (!empty($a_my_id)) {
1124
            if ($extendedAttempt) {
1125
                // "Right green cross" extended
1126
                $total_score = self::get_avg_student_score(
1127
                    $user_id,
1128
                    $course_id,
1129
                    $a_my_id,
1130
                    $session_id,
1131
                    false,
1132
                    false
1133
                );
1134
            } else {
1135
                // "Left green cross" extended
1136
                $total_score = self::get_avg_student_score(
1137
                    $user_id,
1138
                    $course_id,
1139
                    $a_my_id,
1140
                    $session_id,
1141
                    false,
1142
                    true
1143
                );
1144
            }
1145
        } else {
1146
            // Extend all "left green cross"
1147
            $total_score = self::get_avg_student_score(
1148
                $user_id,
1149
                $course_id,
1150
                [$lp_id],
1151
                $session_id,
1152
                false,
1153
                false
1154
            );
1155
        }
1156
1157
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1158
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1159
1160
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1161
            $final_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
1162
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
1163
        } else {
1164
            if (is_numeric($total_score)) {
1165
                $final_score = $total_score.'%';
1166
            } else {
1167
                $final_score = $total_score;
1168
            }
1169
            $finalScoreToCsv = $final_score;
1170
        }
1171
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1172
1173
        if (($counter % 2) == 0) {
1174
            $oddclass = 'row_odd';
1175
        } else {
1176
            $oddclass = 'row_even';
1177
        }
1178
1179
        $action = null;
1180
        if ($type == 'classic') {
1181
            $action = '<td></td>';
1182
        }
1183
1184
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1185
        if ($hideTime) {
1186
            $timeTotal = '';
1187
        }
1188
1189
        $output .= '<tr class="'.$oddclass.'">
1190
                <td></td>
1191
                <td colspan="4">
1192
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
1193
                </td>
1194
                <td colspan="2">'.$progress.'%</td>
1195
                <td colspan="2">'.$final_score.'</td>
1196
                '.$timeTotal.'
1197
                '.$action.'
1198
           </tr>';
1199
1200
        $output .= '
1201
                    </tbody>
1202
                </table>
1203
            </div>
1204
        ';
1205
1206
        if (!empty($export_csv)) {
1207
            $temp = [
1208
                '',
1209
                '',
1210
                '',
1211
                '',
1212
            ];
1213
            $csv_content[] = $temp;
1214
            $temp = [
1215
                get_lang('AccomplishedStepsTotal'),
1216
                '',
1217
                $finalScoreToCsv,
1218
            ];
1219
1220
            if ($hideTime === false) {
1221
                $temp[] = $total_time;
1222
            }
1223
1224
            $csv_content[] = $temp;
1225
            ob_end_clean();
1226
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1227
            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...
1228
        }
1229
1230
        return $output;
1231
    }
1232
1233
    /**
1234
     * @param int  $userId
1235
     * @param bool $getCount
1236
     *
1237
     * @return array
1238
     */
1239
    public static function getStats($userId, $getCount = false)
1240
    {
1241
        $courses = [];
1242
        $assignedCourses = [];
1243
        $drhCount = 0;
1244
        $teachersCount = 0;
1245
        $studentsCount = 0;
1246
        $studentBossCount = 0;
1247
        $courseCount = 0;
1248
        $sessionCount = 0;
1249
        $assignedCourseCount = 0;
1250
1251
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1252
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1253
                'drh_all',
1254
                $userId,
1255
                false,
1256
                null,
1257
                null,
1258
                null,
1259
                null,
1260
                null,
1261
                null,
1262
                null,
1263
                [],
1264
                [],
1265
                STUDENT
1266
            );
1267
1268
            $students = [];
1269
            if (is_array($studentList)) {
1270
                foreach ($studentList as $studentData) {
1271
                    $students[] = $studentData['user_id'];
1272
                }
1273
            }
1274
1275
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1276
                'drh_all',
1277
                $userId,
1278
                $getCount,
1279
                null,
1280
                null,
1281
                null,
1282
                null,
1283
                null,
1284
                null,
1285
                null,
1286
                [],
1287
                [],
1288
                STUDENT_BOSS
1289
            );
1290
1291
            if ($getCount) {
1292
                $studentBossCount = $studentBossesList;
1293
            } else {
1294
                $studentBosses = [];
1295
                if (is_array($studentBossesList)) {
1296
                    foreach ($studentBossesList as $studentBossData) {
1297
                        $studentBosses[] = $studentBossData['user_id'];
1298
                    }
1299
                }
1300
            }
1301
1302
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1303
                'drh_all',
1304
                $userId,
1305
                $getCount,
1306
                null,
1307
                null,
1308
                null,
1309
                null,
1310
                null,
1311
                null,
1312
                null,
1313
                [],
1314
                [],
1315
                COURSEMANAGER
1316
            );
1317
1318
            if ($getCount) {
1319
                $teachersCount = $teacherList;
1320
            } else {
1321
                $teachers = [];
1322
                foreach ($teacherList as $teacherData) {
1323
                    $teachers[] = $teacherData['user_id'];
1324
                }
1325
            }
1326
1327
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1328
                'drh_all',
1329
                $userId,
1330
                $getCount,
1331
                null,
1332
                null,
1333
                null,
1334
                null,
1335
                null,
1336
                null,
1337
                null,
1338
                [],
1339
                [],
1340
                DRH
1341
            );
1342
1343
            if ($getCount) {
1344
                $drhCount = $humanResources;
1345
            } else {
1346
                $humanResourcesList = [];
1347
                if (is_array($humanResources)) {
1348
                    foreach ($humanResources as $item) {
1349
                        $humanResourcesList[] = $item['user_id'];
1350
                    }
1351
                }
1352
            }
1353
1354
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1355
                $userId,
1356
                null,
1357
                null,
1358
                null,
1359
                null,
1360
                null,
1361
                $getCount
1362
            );
1363
1364
            if ($getCount) {
1365
                $courseCount = $platformCourses;
1366
            } else {
1367
                foreach ($platformCourses as $course) {
1368
                    $courses[$course['code']] = $course['code'];
1369
                }
1370
            }
1371
1372
            $sessions = SessionManager::get_sessions_followed_by_drh(
1373
                $userId,
1374
                null,
1375
                null,
1376
                false
1377
            );
1378
        } else {
1379
            $studentList = UserManager::getUsersFollowedByUser(
1380
                $userId,
1381
                STUDENT,
1382
                false,
1383
                false,
1384
                false,
1385
                null,
1386
                null,
1387
                null,
1388
                null,
1389
                null,
1390
                null,
1391
                COURSEMANAGER
1392
            );
1393
1394
            $students = [];
1395
            if (is_array($studentList)) {
1396
                foreach ($studentList as $studentData) {
1397
                    $students[] = $studentData['user_id'];
1398
                }
1399
            }
1400
1401
            $studentBossesList = UserManager::getUsersFollowedByUser(
1402
                $userId,
1403
                STUDENT_BOSS,
1404
                false,
1405
                false,
1406
                $getCount,
1407
                null,
1408
                null,
1409
                null,
1410
                null,
1411
                null,
1412
                null,
1413
                COURSEMANAGER
1414
            );
1415
1416
            if ($getCount) {
1417
                $studentBossCount = $studentBossesList;
1418
            } else {
1419
                $studentBosses = [];
1420
                if (is_array($studentBossesList)) {
1421
                    foreach ($studentBossesList as $studentBossData) {
1422
                        $studentBosses[] = $studentBossData['user_id'];
1423
                    }
1424
                }
1425
            }
1426
1427
            $teacherList = UserManager::getUsersFollowedByUser(
1428
                $userId,
1429
                COURSEMANAGER,
1430
                false,
1431
                false,
1432
                $getCount,
1433
                null,
1434
                null,
1435
                null,
1436
                null,
1437
                null,
1438
                null,
1439
                COURSEMANAGER
1440
            );
1441
1442
            if ($getCount) {
1443
                $teachersCount = $teacherList;
1444
            } else {
1445
                $teachers = [];
1446
                foreach ($teacherList as $teacherData) {
1447
                    $teachers[] = $teacherData['user_id'];
1448
                }
1449
            }
1450
1451
            $humanResources = UserManager::getUsersFollowedByUser(
1452
                $userId,
1453
                DRH,
1454
                false,
1455
                false,
1456
                $getCount,
1457
                null,
1458
                null,
1459
                null,
1460
                null,
1461
                null,
1462
                null,
1463
                COURSEMANAGER
1464
            );
1465
1466
            if ($getCount) {
1467
                $drhCount = $humanResources;
1468
            } else {
1469
                $humanResourcesList = [];
1470
                foreach ($humanResources as $item) {
1471
                    $humanResourcesList[] = $item['user_id'];
1472
                }
1473
            }
1474
1475
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1476
                $userId,
1477
                COURSEMANAGER,
1478
                null,
1479
                null,
1480
                null,
1481
                null,
1482
                $getCount,
1483
                null,
1484
                null,
1485
                true
1486
            );
1487
1488
            if ($getCount) {
1489
                $assignedCourseCount = $platformCourses;
1490
            } else {
1491
                foreach ($platformCourses as $course) {
1492
                    $assignedCourses[$course['code']] = $course['code'];
1493
                }
1494
            }
1495
1496
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1497
                $userId,
1498
                COURSEMANAGER,
1499
                null,
1500
                null,
1501
                null,
1502
                null,
1503
                $getCount
1504
            );
1505
1506
            if ($getCount) {
1507
                $courseCount = $platformCourses;
1508
            } else {
1509
                foreach ($platformCourses as $course) {
1510
                    $courses[$course['code']] = $course['code'];
1511
                }
1512
            }
1513
1514
            $sessions = SessionManager::getSessionsFollowedByUser(
1515
                $userId,
1516
                COURSEMANAGER,
1517
                null,
1518
                null,
1519
                false
1520
            );
1521
        }
1522
1523
        if ($getCount) {
1524
            return [
1525
                'drh' => $drhCount,
1526
                'teachers' => $teachersCount,
1527
                'student_count' => count($students),
1528
                'student_list' => $students,
1529
                'student_bosses' => $studentBossCount,
1530
                'courses' => $courseCount,
1531
                'session_count' => count($sessions),
1532
                'session_list' => $sessions,
1533
                'assigned_courses' => $assignedCourseCount,
1534
            ];
1535
        }
1536
1537
        return [
1538
            'drh' => $humanResourcesList,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $humanResourcesList does not seem to be defined for all execution paths leading up to this point.
Loading history...
1539
            'teachers' => $teachers,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $teachers does not seem to be defined for all execution paths leading up to this point.
Loading history...
1540
            'student_list' => $students,
1541
            'student_bosses' => $studentBosses,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $studentBosses does not seem to be defined for all execution paths leading up to this point.
Loading history...
1542
            'courses' => $courses,
1543
            'sessions' => $sessions,
1544
            'assigned_courses' => $assignedCourses,
1545
        ];
1546
    }
1547
1548
    /**
1549
     * Calculates the time spent on the platform by a user.
1550
     *
1551
     * @param   int|array User id
1552
     * @param string $timeFilter type of time filter: 'last_week' or 'custom'
1553
     * @param string $start_date start date date('Y-m-d H:i:s')
1554
     * @param string $end_date   end date date('Y-m-d H:i:s')
1555
     *
1556
     * @return int $nb_seconds
1557
     */
1558
    public static function get_time_spent_on_the_platform(
1559
        $userId,
1560
        $timeFilter = 'last_7_days',
1561
        $start_date = null,
1562
        $end_date = null
1563
    ) {
1564
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1565
        $condition_time = '';
1566
1567
        if (is_array($userId)) {
1568
            $userList = array_map('intval', $userId);
1569
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1570
        } else {
1571
            $userCondition = " login_user_id = ".intval($userId);
1572
        }
1573
1574
        if (empty($timeFilter)) {
1575
            $timeFilter = 'last_week';
1576
        }
1577
1578
        $today = new DateTime('now', new DateTimeZone('UTC'));
1579
1580
        switch ($timeFilter) {
1581
            case 'last_7_days':
1582
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1583
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1584
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1585
                break;
1586
            case 'last_30_days':
1587
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1588
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1589
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1590
                break;
1591
            case 'custom':
1592
                if (!empty($start_date) && !empty($end_date)) {
1593
                    $start_date = Database::escape_string($start_date);
1594
                    $end_date = Database::escape_string($end_date);
1595
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1596
                }
1597
                break;
1598
        }
1599
1600
        $sql = 'SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1601
    	        FROM '.$tbl_track_login.'
1602
                WHERE '.$userCondition.$condition_time;
1603
        $rs = Database::query($sql);
1604
        $row = Database::fetch_array($rs, 'ASSOC');
1605
        $diff = $row['diff'];
1606
1607
        if ($diff >= 0) {
1608
            return $diff;
1609
        } else {
1610
            return -1;
1611
        }
1612
    }
1613
1614
    /**
1615
     * Calculates the time spent on the course.
1616
     *
1617
     * @param int $user_id
1618
     * @param int $courseId
1619
     * @param int Session id (optional)
1620
     *
1621
     * @return int Time in seconds
1622
     */
1623
    public static function get_time_spent_on_the_course(
1624
        $user_id,
1625
        $courseId,
1626
        $session_id = 0
1627
    ) {
1628
        $courseId = intval($courseId);
1629
1630
        if (empty($courseId) || empty($user_id)) {
1631
            return 0;
1632
        }
1633
1634
        $session_id = intval($session_id);
1635
        if (is_array($user_id)) {
1636
            $user_id = array_map('intval', $user_id);
1637
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
1638
        } else {
1639
            $user_id = intval($user_id);
1640
            $conditionUser = " AND user_id = $user_id ";
1641
        }
1642
1643
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1644
        $sql = "SELECT
1645
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1646
                FROM $table
1647
                WHERE 
1648
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND 
1649
                    c_id = '$courseId' ";
1650
1651
        if ($session_id != -1) {
1652
            $sql .= "AND session_id = '$session_id' ";
1653
        }
1654
1655
        $sql .= $conditionUser;
1656
        $rs = Database::query($sql);
1657
        $row = Database::fetch_array($rs);
1658
1659
        return $row['nb_seconds'];
1660
    }
1661
1662
    /**
1663
     * Get first connection date for a student.
1664
     *
1665
     * @param int $student_id
1666
     *
1667
     * @return string|bool Date format long without day or false if there are no connections
1668
     */
1669
    public static function get_first_connection_date($student_id)
1670
    {
1671
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1672
        $sql = 'SELECT login_date
1673
                FROM '.$table.'
1674
                WHERE login_user_id = '.intval($student_id).'
1675
                ORDER BY login_date ASC
1676
                LIMIT 0,1';
1677
1678
        $rs = Database::query($sql);
1679
        if (Database::num_rows($rs) > 0) {
1680
            if ($first_login_date = Database::result($rs, 0, 0)) {
1681
                return api_convert_and_format_date(
1682
                    $first_login_date,
1683
                    DATE_FORMAT_SHORT,
1684
                    date_default_timezone_get()
1685
                );
1686
            }
1687
        }
1688
1689
        return false;
1690
    }
1691
1692
    /**
1693
     * Get las connection date for a student.
1694
     *
1695
     * @param int  $student_id
1696
     * @param bool $warning_message  Show a warning message (optional)
1697
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1698
     *
1699
     * @return string|int|bool Date format long without day, false if there are no connections or
1700
     *                         timestamp if parameter $return_timestamp is true
1701
     */
1702
    public static function get_last_connection_date(
1703
        $student_id,
1704
        $warning_message = false,
1705
        $return_timestamp = false
1706
    ) {
1707
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1708
        $sql = 'SELECT login_date
1709
                FROM '.$table.'
1710
                WHERE login_user_id = '.intval($student_id).'
1711
                ORDER BY login_date
1712
                DESC LIMIT 0,1';
1713
1714
        $rs = Database::query($sql);
1715
        if (Database::num_rows($rs) > 0) {
1716
            if ($last_login_date = Database::result($rs, 0, 0)) {
1717
                $last_login_date = api_get_local_time($last_login_date);
1718
                if ($return_timestamp) {
1719
                    return api_strtotime($last_login_date, 'UTC');
1720
                } else {
1721
                    if (!$warning_message) {
1722
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1723
                    } else {
1724
                        $timestamp = api_strtotime($last_login_date, 'UTC');
1725
                        $currentTimestamp = time();
1726
1727
                        //If the last connection is > than 7 days, the text is red
1728
                        //345600 = 7 days in seconds
1729
                        if ($currentTimestamp - $timestamp > 604800) {
1730
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</span>';
1731
                        } else {
1732
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1733
                        }
1734
                    }
1735
                }
1736
            }
1737
        }
1738
1739
        return false;
1740
    }
1741
1742
    /**
1743
     * Get las connection date for a student.
1744
     *
1745
     * @param array $studentList Student id array
1746
     * @param int   $days
1747
     * @param bool  $getCount
1748
     *
1749
     * @return int
1750
     */
1751
    public static function getInactiveUsers($studentList, $days, $getCount = true)
1752
    {
1753
        if (empty($studentList)) {
1754
            return 0;
1755
        }
1756
        $days = intval($days);
1757
        $date = api_get_utc_datetime(strtotime($days.' days ago'));
1758
        $studentList = array_map('intval', $studentList);
1759
1760
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1761
        $select = " SELECT login_user_id ";
1762
        if ($getCount) {
1763
            $select = " SELECT count(DISTINCT login_user_id) as count";
1764
        }
1765
        $sql = "$select
1766
                FROM $tbl_track_login
1767
                WHERE
1768
                    login_user_id IN (' ".implode("','", $studentList)."' ) AND
1769
                    login_date < '$date'
1770
                ";
1771
        $rs = Database::query($sql);
1772
        if (Database::num_rows($rs) > 0) {
1773
            if ($getCount) {
1774
                $count = Database::fetch_array($rs);
1775
1776
                return $count['count'];
1777
            }
1778
1779
            return Database::store_result($rs, 'ASSOC');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Database::store_result($rs, 'ASSOC') returns the type array which is incompatible with the documented return type integer.
Loading history...
1780
        }
1781
1782
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
1783
    }
1784
1785
    /**
1786
     * Get first user's connection date on the course.
1787
     *
1788
     * @param int User id
1789
     * @param int $courseId
1790
     * @param int Session id (optional, default=0)
1791
     * @param bool $convert_date
1792
     *
1793
     * @return string|bool Date with format long without day or false if there is no date
1794
     */
1795
    public static function get_first_connection_date_on_the_course(
1796
        $student_id,
1797
        $courseId,
1798
        $session_id = 0,
1799
        $convert_date = true
1800
    ) {
1801
        $student_id = intval($student_id);
1802
        $courseId = intval($courseId);
1803
        $session_id = intval($session_id);
1804
1805
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1806
        $sql = 'SELECT login_course_date
1807
                FROM '.$tbl_track_login.'
1808
                WHERE
1809
                    user_id = '.$student_id.' AND
1810
                    c_id = '.$courseId.' AND
1811
                    session_id = '.$session_id.'
1812
                ORDER BY login_course_date ASC 
1813
                LIMIT 0,1';
1814
        $rs = Database::query($sql);
1815
        if (Database::num_rows($rs) > 0) {
1816
            if ($first_login_date = Database::result($rs, 0, 0)) {
1817
                if ($convert_date) {
1818
                    return api_convert_and_format_date(
1819
                        $first_login_date,
1820
                        DATE_FORMAT_SHORT
1821
                    );
1822
                } else {
1823
                    return $first_login_date;
1824
                }
1825
            }
1826
        }
1827
1828
        return false;
1829
    }
1830
1831
    /**
1832
     * Get last user's connection date on the course.
1833
     *
1834
     * @param     int         User id
1835
     * @param array $courseInfo real_id and code are used
1836
     * @param    int            Session id (optional, default=0)
1837
     * @param bool $convert_date
1838
     *
1839
     * @return string|bool Date with format long without day or false if there is no date
1840
     */
1841
    public static function get_last_connection_date_on_the_course(
1842
        $student_id,
1843
        $courseInfo,
1844
        $session_id = 0,
1845
        $convert_date = true
1846
    ) {
1847
        // protect data
1848
        $student_id = intval($student_id);
1849
        $courseId = $courseInfo['real_id'];
1850
        $session_id = intval($session_id);
1851
1852
        $tbl_track_e_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
1853
        $sql = 'SELECT access_date
1854
                FROM '.$tbl_track_e_access.'
1855
                WHERE   access_user_id = '.$student_id.' AND
1856
                        c_id = "'.$courseId.'" AND
1857
                        access_session_id = '.$session_id.'
1858
                ORDER BY access_date DESC
1859
                LIMIT 0,1';
1860
1861
        $rs = Database::query($sql);
1862
        if (Database::num_rows($rs) > 0) {
1863
            if ($last_login_date = Database::result($rs, 0, 0)) {
1864
                if (empty($last_login_date)) {
1865
                    return false;
1866
                }
1867
                //see #5736
1868
                $last_login_date_timestamp = api_strtotime($last_login_date);
1869
                $now = time();
1870
                //If the last connection is > than 7 days, the text is red
1871
                //345600 = 7 days in seconds
1872
                if ($now - $last_login_date_timestamp > 604800) {
1873
                    if ($convert_date) {
1874
                        $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
1875
                        $icon = api_is_allowed_to_edit() ?
1876
                            '<a href="'.api_get_path(WEB_CODE_PATH).'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'].'" title="'.get_lang('RemindInactiveUser').'">
1877
                              '.Display::return_icon('messagebox_warning.gif').'
1878
                             </a>'
1879
                            : null;
1880
1881
                        return $icon.Display::label($last_login_date, 'warning');
1882
                    } else {
1883
                        return $last_login_date;
1884
                    }
1885
                } else {
1886
                    if ($convert_date) {
1887
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
1888
                    } else {
1889
                        return $last_login_date;
1890
                    }
1891
                }
1892
            }
1893
        }
1894
1895
        return false;
1896
    }
1897
1898
    /**
1899
     * Get count of the connections to the course during a specified period.
1900
     *
1901
     * @param int $courseId
1902
     * @param   int     Session id (optional)
1903
     * @param   int     Datetime from which to collect data (defaults to 0)
1904
     * @param   int     Datetime to which to collect data (defaults to now)
1905
     *
1906
     * @return int count connections
1907
     */
1908
    public static function get_course_connections_count(
1909
        $courseId,
1910
        $session_id = 0,
1911
        $start = 0,
1912
        $stop = null
1913
    ) {
1914
        if ($start < 0) {
1915
            $start = 0;
1916
        }
1917
        if (!isset($stop) or ($stop < 0)) {
1918
            $stop = api_get_utc_datetime();
1919
        }
1920
1921
        // Given we're storing in cache, round the start and end times
1922
        // to the lower minute
1923
        $roundedStart = substr($start, 0, -2).'00';
1924
        $roundedStop = substr($stop, 0, -2).'00';
1925
        $roundedStart = Database::escape_string($roundedStart);
1926
        $roundedStop = Database::escape_string($roundedStop);
1927
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
1928
        $courseId = intval($courseId);
1929
        $session_id = intval($session_id);
1930
        $count = 0;
1931
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1932
        $sql = "SELECT count(*) as count_connections
1933
                FROM $table
1934
                WHERE
1935
                    c_id = $courseId AND
1936
                    session_id = $session_id
1937
                    $month_filter";
1938
1939
        //This query can be very slow (several seconds on an indexed table
1940
        // with 14M rows). As such, we'll try to use APCu if it is
1941
        // available to store the resulting value for a few seconds
1942
        $cacheAvailable = api_get_configuration_value('apc');
1943
        if ($cacheAvailable === true) {
1944
            $apc = apcu_cache_info(true);
1945
            $apc_end = $apc['start_time'] + $apc['ttl'];
1946
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
1947
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
1948
                apcu_fetch($apc_var) > 0
1949
            ) {
1950
                $count = apcu_fetch($apc_var);
1951
            } else {
1952
                $rs = Database::query($sql);
1953
                if (Database::num_rows($rs) > 0) {
1954
                    $row = Database::fetch_object($rs);
1955
                    $count = $row->count_connections;
1956
                }
1957
                apcu_clear_cache();
1958
                apcu_store($apc_var, $count, 60);
1959
            }
1960
        } else {
1961
            $rs = Database::query($sql);
1962
            if (Database::num_rows($rs) > 0) {
1963
                $row = Database::fetch_object($rs);
1964
                $count = $row->count_connections;
1965
            }
1966
        }
1967
1968
        return $count;
1969
    }
1970
1971
    /**
1972
     * Get count courses per student.
1973
     *
1974
     * @param int  $user_id          Student id
1975
     * @param bool $include_sessions Include sessions (optional)
1976
     *
1977
     * @return int count courses
1978
     */
1979
    public static function count_course_per_student($user_id, $include_sessions = true)
1980
    {
1981
        $user_id = intval($user_id);
1982
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1983
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1984
1985
        $sql = 'SELECT DISTINCT c_id
1986
                FROM '.$tbl_course_rel_user.'
1987
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
1988
        $rs = Database::query($sql);
1989
        $nb_courses = Database::num_rows($rs);
1990
1991
        if ($include_sessions) {
1992
            $sql = 'SELECT DISTINCT c_id
1993
                    FROM '.$tbl_session_course_rel_user.'
1994
                    WHERE user_id = '.$user_id;
1995
            $rs = Database::query($sql);
1996
            $nb_courses += Database::num_rows($rs);
1997
        }
1998
1999
        return $nb_courses;
2000
    }
2001
2002
    /**
2003
     * Gets the score average from all tests in a course by student.
2004
     *
2005
     * @param $student_id
2006
     * @param $course_code
2007
     * @param int  $exercise_id
2008
     * @param null $session_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $session_id is correct as it would always require null to be passed?
Loading history...
2009
     * @param int  $active_filter 2 for consider all tests
2010
     *                            1 for active <> -1
2011
     *                            0 for active <> 0
2012
     * @param int  $into_lp       1 for all exercises
2013
     *                            0 for without LP
2014
     * @param mixed id
2015
     * @param string code
2016
     * @param int id (optional), filtered by exercise
2017
     * @param int id (optional), if param $session_id is null
2018
     *                                                it'll return results including sessions, 0 = session is not filtered
2019
     *
2020
     * @return string value (number %) Which represents a round integer about the score average
2021
     */
2022
    public static function get_avg_student_exercise_score(
2023
        $student_id,
2024
        $course_code,
2025
        $exercise_id = 0,
2026
        $session_id = null,
2027
        $active_filter = 1,
2028
        $into_lp = 0
2029
    ) {
2030
        $course_code = Database::escape_string($course_code);
2031
        $course_info = api_get_course_info($course_code);
2032
        if (!empty($course_info)) {
2033
            // table definition
2034
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2035
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2036
2037
            // Compose a filter based on optional exercise given
2038
            $condition_quiz = "";
2039
            if (!empty($exercise_id)) {
2040
                $exercise_id = intval($exercise_id);
2041
                $condition_quiz = " AND id = $exercise_id ";
2042
            }
2043
2044
            // Compose a filter based on optional session id given
2045
            $condition_session = '';
2046
            if (isset($session_id)) {
2047
                $session_id = intval($session_id);
2048
                $condition_session = " AND session_id = $session_id ";
2049
            }
2050
            if ($active_filter == 1) {
2051
                $condition_active = 'AND active <> -1';
2052
            } elseif ($active_filter == 0) {
2053
                $condition_active = 'AND active <> 0';
2054
            } else {
2055
                $condition_active = '';
2056
            }
2057
            $condition_into_lp = '';
2058
            $select_lp_id = '';
2059
            if ($into_lp == 0) {
2060
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2061
            } else {
2062
                $select_lp_id = ', orig_lp_id as lp_id ';
2063
            }
2064
2065
            $sql = "SELECT count(id) 
2066
    		        FROM $tbl_course_quiz
2067
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2068
            $count_quiz = 0;
2069
            $countQuizResult = Database::query($sql);
2070
            if (!empty($countQuizResult)) {
2071
                $count_quiz = Database::fetch_row($countQuizResult);
2072
            }
2073
2074
            if (!empty($count_quiz[0]) && !empty($student_id)) {
2075
                if (is_array($student_id)) {
2076
                    $student_id = array_map('intval', $student_id);
2077
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2078
                } else {
2079
                    $student_id = intval($student_id);
2080
                    $condition_user = " AND exe_user_id = '$student_id' ";
2081
                }
2082
2083
                if (empty($exercise_id)) {
2084
                    $sql = "SELECT id FROM $tbl_course_quiz
2085
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2086
                    $result = Database::query($sql);
2087
                    $exercise_list = [];
2088
                    $exercise_id = null;
2089
                    if (!empty($result) && Database::num_rows($result)) {
2090
                        while ($row = Database::fetch_array($result)) {
2091
                            $exercise_list[] = $row['id'];
2092
                        }
2093
                    }
2094
                    if (!empty($exercise_list)) {
2095
                        $exercise_id = implode("','", $exercise_list);
2096
                    }
2097
                }
2098
2099
                $count_quiz = Database::fetch_row(Database::query($sql));
2100
                $sql = "SELECT
2101
                        SUM(exe_result/exe_weighting*100) as avg_score,
2102
                        COUNT(*) as num_attempts
2103
                        $select_lp_id
2104
                        FROM $tbl_stats_exercise
2105
                        WHERE
2106
                            exe_exo_id IN ('".$exercise_id."')
2107
                            $condition_user AND
2108
                            status = '' AND
2109
                            c_id = {$course_info['real_id']}
2110
                            $condition_session
2111
                            $condition_into_lp
2112
                        ORDER BY exe_date DESC";
2113
2114
                $res = Database::query($sql);
2115
                $row = Database::fetch_array($res);
2116
                $quiz_avg_score = null;
2117
2118
                if (!empty($row['avg_score'])) {
2119
                    $quiz_avg_score = round($row['avg_score'], 2);
2120
                }
2121
2122
                if (!empty($row['num_attempts'])) {
2123
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2124
                }
2125
                if (is_array($student_id)) {
2126
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2127
                }
2128
                if ($into_lp == 0) {
2129
                    return $quiz_avg_score;
2130
                } else {
2131
                    if (!empty($row['lp_id'])) {
2132
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2133
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2134
                        $sql = "SELECT lp.name
2135
                                FROM $tbl_lp as lp, $tbl_course as c
2136
                                WHERE
2137
                                    c.code = '$course_code' AND
2138
                                    lp.id = ".$row['lp_id']." AND
2139
                                    lp.c_id = c.id
2140
                                LIMIT 1;
2141
                        ";
2142
                        $result = Database::query($sql);
2143
                        $row_lp = Database::fetch_row($result);
2144
                        $lp_name = $row_lp[0];
2145
2146
                        return [$quiz_avg_score, $lp_name];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($quiz_avg_score, $lp_name) returns the type array<integer,null|mixed|double> which is incompatible with the documented return type string.
Loading history...
2147
                    } else {
2148
                        return [$quiz_avg_score, null];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($quiz_avg_score, null) returns the type array<integer,null|double> which is incompatible with the documented return type string.
Loading history...
2149
                    }
2150
                }
2151
            }
2152
        }
2153
2154
        return null;
2155
    }
2156
2157
    /**
2158
     * Get count student's exercise COMPLETED attempts.
2159
     *
2160
     * @param int $student_id
2161
     * @param int $courseId
2162
     * @param int $exercise_id
2163
     * @param int $lp_id
2164
     * @param int $lp_item_id
2165
     * @param int $session_id
2166
     * @param int $find_all_lp 0 = just LP specified
2167
     *                         1 = LP specified or whitout LP,
2168
     *                         2 = all rows
2169
     *
2170
     * @internal param \Student $int id
2171
     * @internal param \Course $string code
2172
     * @internal param \Exercise $int id
2173
     * @internal param \Learning $int path id (optional),
2174
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2175
     * @internal param \Learning $int path item id (optional),
2176
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2177
     *
2178
     * @return int count of attempts
2179
     */
2180
    public static function count_student_exercise_attempts(
2181
        $student_id,
2182
        $courseId,
2183
        $exercise_id,
2184
        $lp_id = 0,
2185
        $lp_item_id = 0,
2186
        $session_id = 0,
2187
        $find_all_lp = 0
2188
    ) {
2189
        $courseId = intval($courseId);
2190
        $student_id = intval($student_id);
2191
        $exercise_id = intval($exercise_id);
2192
        $session_id = intval($session_id);
2193
2194
        $lp_id = intval($lp_id);
2195
        $lp_item_id = intval($lp_item_id);
2196
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2197
2198
        $sql = "SELECT COUNT(ex.exe_id) as essais 
2199
                FROM $tbl_stats_exercises AS ex
2200
                WHERE  
2201
                    ex.c_id = $courseId AND 
2202
                    ex.exe_exo_id = $exercise_id AND 
2203
                    status = '' AND 
2204
                    exe_user_id= $student_id AND 
2205
                    session_id = $session_id ";
2206
2207
        if ($find_all_lp == 1) {
2208
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2209
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2210
        } elseif ($find_all_lp == 0) {
2211
            $sql .= "AND orig_lp_id = $lp_id
2212
                AND orig_lp_item_id = $lp_item_id";
2213
        }
2214
2215
        $rs = Database::query($sql);
2216
        $row = Database::fetch_row($rs);
2217
        $count_attempts = $row[0];
2218
2219
        return $count_attempts;
2220
    }
2221
2222
    /**
2223
     * Get count student's exercise progress.
2224
     *
2225
     * @param array $exercise_list
2226
     * @param int   $user_id
2227
     * @param int   $courseId
2228
     * @param int   $session_id
2229
     *
2230
     * @return string
2231
     */
2232
    public static function get_exercise_student_progress(
2233
        $exercise_list,
2234
        $user_id,
2235
        $courseId,
2236
        $session_id
2237
    ) {
2238
        $courseId = intval($courseId);
2239
        $user_id = intval($user_id);
2240
        $session_id = intval($session_id);
2241
2242
        if (empty($exercise_list)) {
2243
            return '0%';
2244
        }
2245
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2246
        $exercise_list = array_keys($exercise_list);
2247
        $exercise_list = array_map('intval', $exercise_list);
2248
2249
        $exercise_list_imploded = implode("' ,'", $exercise_list);
2250
2251
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
2252
                FROM $tbl_stats_exercises AS ex
2253
                WHERE
2254
                    ex.c_id = $courseId AND
2255
                    ex.session_id  = $session_id AND
2256
                    ex.exe_user_id = $user_id AND
2257
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
2258
2259
        $rs = Database::query($sql);
2260
        $count = 0;
2261
        if ($rs) {
2262
            $row = Database::fetch_row($rs);
2263
            $count = $row[0];
2264
        }
2265
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
2266
2267
        return $count;
2268
    }
2269
2270
    /**
2271
     * @param array $exercise_list
2272
     * @param int   $user_id
2273
     * @param int   $courseId
2274
     * @param int   $session_id
2275
     *
2276
     * @return string
2277
     */
2278
    public static function get_exercise_student_average_best_attempt(
2279
        $exercise_list,
2280
        $user_id,
2281
        $courseId,
2282
        $session_id
2283
    ) {
2284
        $result = 0;
2285
        if (!empty($exercise_list)) {
2286
            foreach ($exercise_list as $exercise_data) {
2287
                $exercise_id = $exercise_data['id'];
2288
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2289
                    $user_id,
2290
                    $exercise_id,
2291
                    $courseId,
2292
                    $session_id
2293
                );
2294
2295
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
2296
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
2297
                }
2298
            }
2299
            $result = $result / count($exercise_list);
2300
            $result = round($result, 2) * 100;
2301
        }
2302
2303
        return $result.'%';
2304
    }
2305
2306
    /**
2307
     * get teacher progress by course and session.
2308
     *
2309
     * @param int course id
2310
     * @param int session id
2311
     *
2312
     * @return array
2313
     */
2314
    public static function get_teachers_progress_by_course($courseId, $sessionId)
2315
    {
2316
        $course = api_get_course_info_by_id($courseId);
2317
        $sessionId = intval($sessionId);
2318
        $courseId = intval($courseId);
2319
2320
        $sessionCourseUserTable = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2321
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
2322
2323
        //get teachers
2324
        $sql = "SELECT scu.session_id, scu.user_id, s.name
2325
                FROM $sessionCourseUserTable scu, $sessionTable s
2326
                WHERE
2327
                    scu.session_id = s.id
2328
                    AND scu.status = 2
2329
                    AND scu.visibility = 1
2330
                    AND scu.c_id = '%s'
2331
                    AND scu.session_id = %s";
2332
        $query = sprintf($sql, intval($courseId), $sessionId);
2333
        $rs = Database::query($query);
2334
        $teachers = [];
2335
        while ($teacher = Database::fetch_array($rs, 'ASSOC')) {
2336
            $teachers[] = $teacher;
2337
        }
2338
        $data = [];
2339
        foreach ($teachers as $teacher) {
2340
            //total documents added
2341
            $sql = "SELECT count(*) as total
2342
                    FROM c_item_property
2343
                    WHERE lastedit_type = 'DocumentAdded'
2344
                    AND c_id = %s
2345
                    AND insert_user_id = %s
2346
                    AND session_id = %s";
2347
            $query = sprintf(
2348
                $sql,
2349
                $courseId,
2350
                $teacher['user_id'],
2351
                $teacher['session_id']
2352
            );
2353
2354
            $rs = Database::query($query);
2355
            $totalDocuments = 0;
2356
            if ($rs) {
2357
                $row = Database::fetch_row($rs);
2358
                $totalDocuments = $row[0];
2359
            }
2360
            // Total links added
2361
            $sql = "SELECT count(*) as total
2362
                    FROM c_item_property
2363
                    WHERE lastedit_type = 'LinkAdded'
2364
                    AND c_id = %s
2365
                    AND insert_user_id = %s
2366
                    AND session_id = %s";
2367
            $query = sprintf(
2368
                $sql,
2369
                $courseId,
2370
                $teacher['user_id'],
2371
                $teacher['session_id']
2372
            );
2373
            $rs = Database::query($query);
2374
2375
            $totalLinks = 0;
2376
            if ($rs) {
2377
                $row = Database::fetch_row($rs);
2378
                $totalLinks = $row[0];
2379
            }
2380
            //total forums added
2381
            $sql = "SELECT count(*) as total
2382
                    FROM c_item_property
2383
                    WHERE lastedit_type = 'ForumthreadVisible'
2384
                    AND c_id = %s
2385
                    AND insert_user_id = %s
2386
                    AND session_id = %s";
2387
            $query = sprintf(
2388
                $sql,
2389
                $courseId,
2390
                $teacher['user_id'],
2391
                $teacher['session_id']
2392
            );
2393
            $rs = Database::query($query);
2394
2395
            $totalForums = 0;
2396
            if ($rs) {
2397
                $row = Database::fetch_row($rs);
2398
                $totalForums = $row[0];
2399
            }
2400
2401
            //total wikis added
2402
            $sql = "SELECT COUNT(DISTINCT(ref)) as total
2403
                    FROM c_item_property
2404
                    WHERE lastedit_type = 'WikiAdded'
2405
                    AND c_id = %s
2406
                    AND insert_user_id = %s
2407
                    AND session_id = %s";
2408
2409
            $query = sprintf(
2410
                $sql,
2411
                $courseId,
2412
                $teacher['user_id'],
2413
                $teacher['session_id']
2414
            );
2415
2416
            $rs = Database::query($query);
2417
2418
            $totalWikis = 0;
2419
            if ($rs) {
2420
                $row = Database::fetch_row($rs);
2421
                $totalWikis = $row[0];
2422
            }
2423
2424
            // Total works added
2425
            $sql = "SELECT COUNT(*) as total
2426
                    FROM c_item_property
2427
                    WHERE lastedit_type = 'DirectoryCreated'
2428
                    AND tool = 'work'
2429
                    AND c_id = %s
2430
                    AND insert_user_id = %s
2431
                    AND session_id = %s";
2432
            $query = sprintf(
2433
                $sql,
2434
                $courseId,
2435
                $teacher['user_id'],
2436
                $teacher['session_id']
2437
            );
2438
            $rs = Database::query($query);
2439
2440
            $totalWorks = 0;
2441
            if ($rs) {
2442
                $row = Database::fetch_row($rs);
2443
                $totalWorks = $row[0];
2444
            }
2445
            //total announcements added
2446
            $sql = "SELECT COUNT(*) as total
2447
                    FROM c_item_property
2448
                    WHERE lastedit_type = 'AnnouncementAdded'
2449
                    AND c_id = %s
2450
                    AND insert_user_id = %s
2451
                    AND session_id = %s";
2452
            $query = sprintf(
2453
                $sql,
2454
                $courseId,
2455
                $teacher['user_id'],
2456
                $teacher['session_id']
2457
            );
2458
            $rs = Database::query($query);
2459
2460
            $totalAnnouncements = 0;
2461
            if ($rs) {
2462
                $row = Database::fetch_row($rs);
2463
                $totalAnnouncements = $row[0];
2464
            }
2465
            $tutor = api_get_user_info($teacher['user_id']);
2466
            $data[] = [
2467
                'course' => $course['title'],
2468
                'session' => $teacher['name'],
2469
                'tutor' => $tutor['username'].' - '.$tutor['lastname'].' '.$tutor['firstname'],
2470
                'documents' => $totalDocuments,
2471
                'links' => $totalLinks,
2472
                'forums' => $totalForums,
2473
                'works' => $totalWorks,
2474
                'wikis' => $totalWikis,
2475
                'announcements' => $totalAnnouncements,
2476
            ];
2477
        }
2478
2479
        return $data;
2480
    }
2481
2482
    /**
2483
     * Returns the average student progress in the learning paths of the given
2484
     * course, it will take into account the progress that were not started.
2485
     *
2486
     * @param int|array $studentId
2487
     * @param string    $courseCode
2488
     * @param array     $lpIdList        Limit average to listed lp ids
2489
     * @param int       $sessionId       Session id (optional),
2490
     *                                   if parameter $session_id is null(default) it'll return results including
2491
     *                                   sessions, 0 = session is not filtered
2492
     * @param bool      $returnArray     Will return an array of the type:
2493
     *                                   [sum_of_progresses, number] if it is set to true
2494
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2495
     *
2496
     * @return float Average progress of the user in this course
2497
     */
2498
    public static function get_avg_student_progress(
2499
        $studentId,
2500
        $courseCode = null,
2501
        $lpIdList = [],
2502
        $sessionId = null,
2503
        $returnArray = false,
2504
        $onlySeriousGame = false
2505
    ) {
2506
        // If there is at least one learning path and one student.
2507
        if (empty($studentId)) {
2508
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type double.
Loading history...
2509
        }
2510
2511
        $sessionId = intval($sessionId);
2512
        $courseInfo = api_get_course_info($courseCode);
2513
2514
        if (empty($courseInfo)) {
2515
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type double.
Loading history...
2516
        }
2517
2518
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2519
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2520
        $lpConditions = [];
2521
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2522
2523
        if ($sessionId > 0) {
2524
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2525
        } else {
2526
            $lpConditions['AND session_id = ?'] = $sessionId;
2527
        }
2528
2529
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2530
            $placeHolders = [];
2531
            for ($i = 0; $i < count($lpIdList); $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...
2532
                $placeHolders[] = '?';
2533
            }
2534
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
2535
        }
2536
2537
        if ($onlySeriousGame) {
2538
            $lpConditions['AND seriousgame_mode = ? '] = true;
2539
        }
2540
2541
        $resultLP = Database::select(
2542
            'id',
2543
            $lPTable,
2544
            ['where' => $lpConditions]
2545
        );
2546
        $filteredLP = array_keys($resultLP);
2547
2548
        if (empty($filteredLP)) {
2549
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type double.
Loading history...
2550
        }
2551
2552
        $conditions = [
2553
            " c_id = {$courseInfo['real_id']} ",
2554
            " lp_view.lp_id IN(".implode(', ', $filteredLP).") ",
2555
        ];
2556
2557
        $groupBy = 'GROUP BY lp_id';
2558
2559
        if (is_array($studentId)) {
2560
            $studentId = array_map('intval', $studentId);
2561
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
2562
        } else {
2563
            $studentId = intval($studentId);
2564
            $conditions[] = " lp_view.user_id = '$studentId' ";
2565
2566
            if (empty($lpIdList)) {
2567
                $lpList = new LearnpathList(
2568
                    $studentId,
2569
                    $courseCode,
2570
                    $sessionId,
2571
                    null,
2572
                    false,
2573
                    null,
2574
                    true
2575
                );
2576
                $lpList = $lpList->get_flat_list();
2577
                if (!empty($lpList)) {
2578
                    /** @var $lp */
2579
                    foreach ($lpList as $lpId => $lp) {
2580
                        $lpIdList[] = $lpId;
2581
                    }
2582
                }
2583
            }
2584
        }
2585
2586
        if (!empty($sessionId)) {
2587
            $conditions[] = " session_id = $sessionId ";
2588
        } else {
2589
            $conditions[] = " (session_id = 0 OR session_id IS NULL) ";
2590
        }
2591
2592
        $conditionToString = implode('AND', $conditions);
2593
        $sql = "SELECT lp_id, view_count, progress 
2594
                FROM $lpViewTable lp_view
2595
                WHERE
2596
                    $conditionToString
2597
                    $groupBy
2598
                ORDER BY view_count DESC";
2599
2600
        $result = Database::query($sql);
2601
2602
        $progress = [];
2603
        $viewCount = [];
2604
        while ($row = Database::fetch_array($result, 'ASSOC')) {
2605
            if (!isset($viewCount[$row['lp_id']])) {
2606
                $progress[$row['lp_id']] = $row['progress'];
2607
            }
2608
            $viewCount[$row['lp_id']] = $row['view_count'];
2609
        }
2610
2611
        // Fill with lp ids
2612
        if (!empty($lpIdList)) {
2613
            foreach ($lpIdList as $lpId) {
2614
                if (!isset($progress[$lpId])) {
2615
                    $progress[$lpId] = 0;
2616
                }
2617
            }
2618
        }
2619
2620
        if (!empty($progress)) {
2621
            $sum = array_sum($progress);
2622
            $average = 0;
2623
            if (!empty($lpIdList)) {
2624
                $average = $sum / count($lpIdList);
2625
            }
2626
        } else {
2627
            $average = 0;
2628
            $sum = 0;
2629
        }
2630
2631
        if ($returnArray) {
2632
            return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($sum, count($lpIdList)) returns the type array<integer,integer|double> which is incompatible with the documented return type double.
Loading history...
2633
                $sum,
2634
                count($lpIdList),
2635
            ];
2636
        }
2637
2638
        return round($average, 1);
2639
    }
2640
2641
    /**
2642
     * This function gets:
2643
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2644
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2645
     * 3. And finally it will return the average between 1. and 2.
2646
     *
2647
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2648
     * This function does not take the results of a Test out of a LP
2649
     *
2650
     * @param mixed  $student_id                      Array of user ids or an user id
2651
     * @param string $course_code
2652
     * @param array  $lp_ids                          List of LP ids
2653
     * @param int    $session_id                      Session id (optional),
2654
     *                                                if param $session_id is null(default) it'll return results
2655
     *                                                including sessions, 0 = session is not filtered
2656
     * @param bool   $return_array                    Returns an array of the
2657
     *                                                type [sum_score, num_score] if set to true
2658
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2659
     * @param bool   $getOnlyBestAttempt
2660
     *
2661
     * @return string value (number %) Which represents a round integer explain in got in 3
2662
     */
2663
    public static function get_avg_student_score(
2664
        $student_id,
2665
        $course_code,
2666
        $lp_ids = [],
2667
        $session_id = null,
2668
        $return_array = false,
2669
        $get_only_latest_attempt_results = false,
2670
        $getOnlyBestAttempt = false
2671
    ) {
2672
        $debug = false;
2673
        if ($debug) {
2674
            echo '<h1>Tracking::get_avg_student_score</h1>';
2675
        }
2676
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2677
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2678
        $course = api_get_course_info($course_code);
2679
        if (!empty($course)) {
2680
            // Get course tables names
2681
            $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
2682
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
2683
            $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
2684
            $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
2685
            $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2686
            $course_id = $course['real_id'];
2687
2688
            // Compose a filter based on optional learning paths list given
2689
            $condition_lp = '';
2690
            if (count($lp_ids) > 0) {
2691
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
2692
            }
2693
2694
            // Compose a filter based on optional session id
2695
            $session_id = intval($session_id);
2696
            if (count($lp_ids) > 0) {
2697
                $condition_session = " AND session_id = $session_id ";
2698
            } else {
2699
                $condition_session = " WHERE session_id = $session_id ";
2700
            }
2701
2702
            // Check the real number of LPs corresponding to the filter in the
2703
            // database (and if no list was given, get them all)
2704
            if (empty($session_id)) {
2705
                $sql = "SELECT DISTINCT(id), use_max_score
2706
                        FROM $lp_table
2707
                        WHERE 
2708
                            c_id = $course_id AND 
2709
                            (session_id = 0 OR session_id IS NULL) $condition_lp ";
2710
            } else {
2711
                $sql = "SELECT DISTINCT(id), use_max_score
2712
                        FROM $lp_table
2713
                        WHERE c_id = $course_id $condition_lp ";
2714
            }
2715
2716
            $res_row_lp = Database::query($sql);
2717
            $count_row_lp = Database::num_rows($res_row_lp);
2718
2719
            $lp_list = $use_max_score = [];
2720
            while ($row_lp = Database::fetch_array($res_row_lp)) {
2721
                $lp_list[] = $row_lp['id'];
2722
                $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
2723
            }
2724
2725
            if ($debug) {
2726
                echo '$lp_list: ';
2727
                var_dump($lp_list);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($lp_list) looks like debug code. Are you sure you do not want to remove it?
Loading history...
2728
                echo 'Use max score or not list: ';
2729
                var_dump($use_max_score);
2730
            }
2731
2732
            // prepare filter on users
2733
            if (is_array($student_id)) {
2734
                array_walk($student_id, 'intval');
2735
                $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
2736
            } else {
2737
                $condition_user1 = " AND user_id = $student_id ";
2738
            }
2739
2740
            if ($count_row_lp > 0 && !empty($student_id)) {
2741
                // Getting latest LP result for a student
2742
                //@todo problem when a  course have more than 1500 users
2743
                $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
2744
                        FROM $lp_view_table
2745
                        WHERE
2746
                            c_id = $course_id AND
2747
                            lp_id IN (".implode(',', $lp_list).")
2748
                            $condition_user1 AND
2749
                            session_id = $session_id
2750
                        GROUP BY lp_id, user_id";
2751
                if ($debug) {
2752
                    echo 'get LP results';
2753
                    var_dump($sql);
2754
                }
2755
2756
                $rs_last_lp_view_id = Database::query($sql);
2757
                $global_result = 0;
2758
2759
                if (Database::num_rows($rs_last_lp_view_id) > 0) {
2760
                    // Cycle through each line of the results (grouped by lp_id, user_id)
2761
                    while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2762
                        $count_items = 0;
2763
                        $lpPartialTotal = 0;
2764
                        $list = [];
2765
                        $lp_view_id = $row_lp_view['id'];
2766
                        $lp_id = $row_lp_view['lp_id'];
2767
                        $user_id = $row_lp_view['user_id'];
2768
2769
                        if ($debug) {
2770
                            echo '<h2>LP id '.$lp_id.'</h2>';
2771
                            echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
2772
                            echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
2773
                        }
2774
2775
                        if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
2776
                            // Getting lp_items done by the user
2777
                            $sql = "SELECT DISTINCT lp_item_id
2778
                                    FROM $lp_item_view_table
2779
                                    WHERE
2780
                                        c_id = $course_id AND
2781
                                        lp_view_id = $lp_view_id
2782
                                    ORDER BY lp_item_id";
2783
                            $res_lp_item = Database::query($sql);
2784
                            if ($debug) {
2785
                                echo 'Getting lp_items done by the user<br />';
2786
                                var_dump($sql);
2787
                            }
2788
2789
                            while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
2790
                                $my_lp_item_id = $row_lp_item['lp_item_id'];
2791
                                $order = ' view_count DESC';
2792
                                if ($getOnlyBestAttempt) {
2793
                                    $order = ' lp_iv.score DESC';
2794
                                }
2795
2796
                                // Getting the most recent attempt
2797
                                $sql = "SELECT  
2798
                                            lp_iv.id as lp_item_view_id,
2799
                                            lp_iv.score as score,
2800
                                            lp_i.max_score,
2801
                                            lp_iv.max_score as max_score_item_view,
2802
                                            lp_i.path,
2803
                                            lp_i.item_type,
2804
                                            lp_i.id as iid
2805
                                        FROM $lp_item_view_table as lp_iv
2806
                                        INNER JOIN $lp_item_table as lp_i
2807
                                        ON (
2808
                                            lp_i.id = lp_iv.lp_item_id AND 
2809
                                            lp_iv.c_id = lp_i.c_id
2810
                                        )                                            
2811
                                        WHERE
2812
                                            lp_iv.c_id = $course_id AND
2813
                                            lp_i.c_id  = $course_id AND
2814
                                            lp_item_id = $my_lp_item_id AND
2815
                                            lp_view_id = $lp_view_id AND
2816
                                            (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2817
                                        ORDER BY $order
2818
                                        LIMIT 1";
2819
2820
                                $res_lp_item_result = Database::query($sql);
2821
                                while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
2822
                                    $list[] = $row_max_score;
2823
                                }
2824
                            }
2825
                        } else {
2826
                            // For the currently analysed view, get the score and
2827
                            // max_score of each item if it is a sco or a TOOL_QUIZ
2828
                            $sql = "SELECT
2829
                                        lp_iv.id as lp_item_view_id,
2830
                                        lp_iv.score as score,
2831
                                        lp_i.max_score,
2832
                                        lp_iv.max_score as max_score_item_view,
2833
                                        lp_i.path,
2834
                                        lp_i.item_type,
2835
                                        lp_i.id as iid
2836
                                      FROM $lp_item_view_table as lp_iv
2837
                                      INNER JOIN $lp_item_table as lp_i
2838
                                      ON lp_i.id = lp_iv.lp_item_id AND
2839
                                         lp_iv.c_id = lp_i.c_id
2840
                                      WHERE 
2841
                                        lp_iv.c_id = $course_id AND 
2842
                                        lp_i.c_id  = $course_id AND
2843
                                        lp_view_id = $lp_view_id AND
2844
                                        (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2845
                                    ";
2846
                            $res_max_score = Database::query($sql);
2847
                            while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
2848
                                $list[] = $row_max_score;
2849
                            }
2850
                        }
2851
2852
                        // Go through each scorable element of this view
2853
                        $score_of_scorm_calculate = 0;
2854
                        foreach ($list as $row_max_score) {
2855
                            // Came from the original lp_item
2856
                            $max_score = $row_max_score['max_score'];
2857
                            // Came from the lp_item_view
2858
                            $max_score_item_view = $row_max_score['max_score_item_view'];
2859
                            $score = $row_max_score['score'];
2860
                            if ($debug) {
2861
                                echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
2862
                            }
2863
2864
                            if ($row_max_score['item_type'] == 'sco') {
2865
                                /* Check if it is sco (easier to get max_score)
2866
                                   when there's no max score, we assume 100 as the max score,
2867
                                   as the SCORM 1.2 says that the value should always be between 0 and 100.
2868
                                */
2869
                                if ($max_score == 0 || is_null($max_score) || $max_score == '') {
2870
                                    // Chamilo style
2871
                                    if ($use_max_score[$lp_id]) {
2872
                                        $max_score = 100;
2873
                                    } else {
2874
                                        // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
2875
                                        $max_score = $max_score_item_view;
2876
                                    }
2877
                                }
2878
                                // Avoid division by zero errors
2879
                                if (!empty($max_score)) {
2880
                                    $lpPartialTotal += $score / $max_score;
2881
                                }
2882
                                if ($debug) {
2883
                                    var_dump("lpPartialTotal: $lpPartialTotal");
2884
                                    var_dump("score: $score");
2885
                                    var_dump("max_score: $max_score");
2886
                                }
2887
                            } else {
2888
                                // Case of a TOOL_QUIZ element
2889
                                $item_id = $row_max_score['iid'];
2890
                                $item_path = $row_max_score['path'];
2891
                                $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
2892
2893
                                $lpItemCondition = '';
2894
                                if (empty($lp_item_view_id)) {
2895
                                    $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
2896
                                } else {
2897
                                    $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
2898
                                }
2899
2900
                                // Get last attempt to this exercise through
2901
                                // the current lp for the current user
2902
                                $order = 'exe_date DESC';
2903
                                if ($getOnlyBestAttempt) {
2904
                                    $order = 'exe_result DESC';
2905
                                }
2906
                                $sql = "SELECT exe_id, exe_result
2907
                                        FROM $tbl_stats_exercices
2908
                                        WHERE
2909
                                            exe_exo_id = '$item_path' AND
2910
                                            exe_user_id = $user_id AND
2911
                                            orig_lp_item_id = $item_id AND
2912
                                            $lpItemCondition AND
2913
                                            c_id = $course_id AND
2914
                                            session_id = $session_id AND
2915
                                            status = ''
2916
                                        ORDER BY $order
2917
                                        LIMIT 1";
2918
2919
                                $result_last_attempt = Database::query($sql);
2920
                                if ($debug) {
2921
                                    var_dump($sql);
2922
                                }
2923
                                $num = Database::num_rows($result_last_attempt);
2924
                                if ($num > 0) {
2925
                                    $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
2926
                                    $id_last_attempt = $attemptResult['exe_id'];
2927
                                    // We overwrite the score with the best one not the one saved in the LP (latest)
2928
                                    if ($getOnlyBestAttempt && $get_only_latest_attempt_results == false) {
2929
                                        if ($debug) {
2930
                                            echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
2931
                                        }
2932
                                        $score = $attemptResult['exe_result'];
2933
                                    }
2934
2935
                                    if ($debug) {
2936
                                        echo "Attempt id: $id_last_attempt with score $score<br />";
2937
                                    }
2938
                                    // Within the last attempt number tracking, get the sum of
2939
                                    // the max_scores of all questions that it was
2940
                                    // made of (we need to make this call dynamic because of random questions selection)
2941
                                    $sql = "SELECT SUM(t.ponderation) as maxscore FROM
2942
                                            (
2943
                                                SELECT DISTINCT
2944
                                                    question_id,
2945
                                                    marks,
2946
                                                    ponderation
2947
                                                FROM $tbl_stats_attempts AS at
2948
                                                INNER JOIN $tbl_quiz_questions AS q
2949
                                                ON (q.id = at.question_id AND q.c_id = q.c_id)
2950
                                                WHERE
2951
                                                    exe_id ='$id_last_attempt' AND
2952
                                                    q.c_id = $course_id
2953
                                            )
2954
                                            AS t";
2955
2956
                                    $res_max_score_bis = Database::query($sql);
2957
                                    $row_max_score_bis = Database::fetch_array($res_max_score_bis);
2958
2959
                                    if (!empty($row_max_score_bis['maxscore'])) {
2960
                                        $max_score = $row_max_score_bis['maxscore'];
2961
                                    }
2962
                                    if (!empty($max_score) && floatval($max_score) > 0) {
2963
                                        $lpPartialTotal += $score / $max_score;
2964
                                    }
2965
                                    if ($debug) {
2966
                                        var_dump("score: $score");
2967
                                        var_dump("max_score: $max_score");
2968
                                        var_dump("lpPartialTotal: $lpPartialTotal");
2969
                                    }
2970
                                }
2971
                            }
2972
2973
                            if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
2974
                                // Normal way
2975
                                if ($use_max_score[$lp_id]) {
2976
                                    $count_items++;
2977
                                } else {
2978
                                    if ($max_score != '') {
2979
                                        $count_items++;
2980
                                    }
2981
                                }
2982
                                if ($debug) {
2983
                                    echo '$count_items: '.$count_items;
2984
                                }
2985
                            }
2986
                        } //end for
2987
2988
                        $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
2989
                        $global_result += $score_of_scorm_calculate;
2990
2991
                        if ($debug) {
2992
                            var_dump("count_items: $count_items");
2993
                            var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
2994
                            var_dump("global_result: $global_result");
2995
                        }
2996
                    } // end while
2997
                }
2998
2999
                $lp_with_quiz = 0;
3000
                foreach ($lp_list as $lp_id) {
3001
                    // Check if LP have a score we assume that all SCO have an score
3002
                    $sql = "SELECT count(id) as count
3003
                            FROM $lp_item_table
3004
                            WHERE
3005
                                c_id = $course_id AND
3006
                                (item_type = 'quiz' OR item_type = 'sco') AND
3007
                                lp_id = ".$lp_id;
3008
                    if ($debug) {
3009
                        var_dump($sql);
3010
                    }
3011
                    $result_have_quiz = Database::query($sql);
3012
                    if (Database::num_rows($result_have_quiz) > 0) {
3013
                        $row = Database::fetch_array($result_have_quiz, 'ASSOC');
3014
                        if (is_numeric($row['count']) && $row['count'] != 0) {
3015
                            $lp_with_quiz++;
3016
                        }
3017
                    }
3018
                }
3019
3020
                if ($debug) {
3021
                    echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
3022
                }
3023
                if ($debug) {
3024
                    echo '<h3>Final return</h3>';
3025
                }
3026
3027
                if ($lp_with_quiz != 0) {
3028
                    if (!$return_array) {
3029
                        $score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
3030
                        if ($debug) {
3031
                            var_dump($score_of_scorm_calculate);
3032
                        }
3033
                        if (empty($lp_ids)) {
3034
                            if ($debug) {
3035
                                echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
3036
                            }
3037
                        }
3038
3039
                        return $score_of_scorm_calculate;
3040
                    } else {
3041
                        if ($debug) {
3042
                            var_dump($global_result, $lp_with_quiz);
3043
                        }
3044
3045
                        return [$global_result, $lp_with_quiz];
3046
                    }
3047
                } else {
3048
                    return '-';
3049
                }
3050
            }
3051
        }
3052
3053
        return null;
3054
    }
3055
3056
    /**
3057
     * This function gets:
3058
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3059
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3060
     * 3. And finally it will return the average between 1. and 2.
3061
     * This function does not take the results of a Test out of a LP.
3062
     *
3063
     * @param   int|array   Array of user ids or an user id
3064
     * @param string $course_code Course code
3065
     * @param array  $lp_ids      List of LP ids
3066
     * @param int    $session_id  Session id (optional), if param $session_id is 0(default)
3067
     *                            it'll return results including sessions, 0 = session is not filtered
3068
     * @param   bool        Returns an array of the type [sum_score, num_score] if set to true
3069
     * @param   bool        get only the latest attempts or ALL attempts
3070
     *
3071
     * @return string value (number %) Which represents a round integer explain in got in 3
3072
     */
3073
    public static function getAverageStudentScore(
3074
        $student_id,
3075
        $course_code = '',
3076
        $lp_ids = [],
3077
        $session_id = 0
3078
    ) {
3079
        if (empty($student_id)) {
3080
            return 0;
3081
        }
3082
3083
        $conditions = [];
3084
        if (!empty($course_code)) {
3085
            $course = api_get_course_info($course_code);
3086
            $courseId = $course['real_id'];
3087
            $conditions[] = " c_id = $courseId";
3088
        }
3089
3090
        // Get course tables names
3091
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3092
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3093
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3094
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3095
3096
        // Compose a filter based on optional learning paths list given
3097
        if (!empty($lp_ids) && count($lp_ids) > 0) {
3098
            $conditions[] = " id IN(".implode(',', $lp_ids).") ";
3099
        }
3100
3101
        // Compose a filter based on optional session id
3102
        $session_id = intval($session_id);
3103
        if (!empty($session_id)) {
3104
            $conditions[] = " session_id = $session_id ";
3105
        }
3106
3107
        if (is_array($student_id)) {
3108
            array_walk($student_id, 'intval');
3109
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
3110
        } else {
3111
            $conditions[] = " lp_view.user_id = $student_id ";
3112
        }
3113
3114
        $conditionsToString = implode('AND ', $conditions);
3115
        $sql = "SELECT
3116
                    SUM(lp_iv.score) sum_score,
3117
                    SUM(lp_i.max_score) sum_max_score
3118
                FROM $lp_table as lp
3119
                INNER JOIN $lp_item_table as lp_i
3120
                ON lp.id = lp_id AND lp.c_id = lp_i.c_id
3121
                INNER JOIN $lp_view_table as lp_view
3122
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
3123
                INNER JOIN $lp_item_view_table as lp_iv
3124
                ON lp_i.id = lp_iv.lp_item_id AND lp_view.c_id = lp_iv.c_id AND lp_iv.lp_view_id = lp_view.id
3125
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
3126
                $conditionsToString
3127
        ";
3128
        $result = Database::query($sql);
3129
        $row = Database::fetch_array($result, 'ASSOC');
3130
3131
        if (empty($row['sum_max_score'])) {
3132
            return 0;
3133
        }
3134
3135
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
3136
    }
3137
3138
    /**
3139
     * This function gets time spent in learning path for a student inside a course.
3140
     *
3141
     * @param int|array $student_id  Student id(s)
3142
     * @param string    $course_code Course code
3143
     * @param array     $lp_ids      Limit average to listed lp ids
3144
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
3145
     *                               it'll return results including sessions, 0 = session is not filtered
3146
     *
3147
     * @return int Total time
3148
     */
3149
    public static function get_time_spent_in_lp(
3150
        $student_id,
3151
        $course_code,
3152
        $lp_ids = [],
3153
        $session_id = 0
3154
    ) {
3155
        $course = api_get_course_info($course_code);
3156
        $student_id = (int) $student_id;
3157
        $session_id = (int) $session_id;
3158
        $total_time = 0;
3159
3160
        if (!empty($course)) {
3161
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
3162
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
3163
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3164
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3165
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3166
            $course_id = $course['real_id'];
3167
3168
            // Compose a filter based on optional learning paths list given
3169
            $condition_lp = '';
3170
            if (count($lp_ids) > 0) {
3171
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3172
            }
3173
3174
            // Check the real number of LPs corresponding to the filter in the
3175
            // database (and if no list was given, get them all)
3176
            $sql = "SELECT DISTINCT(id) FROM $lpTable 
3177
                    WHERE c_id = $course_id $condition_lp";
3178
            $result = Database::query($sql);
3179
            $session_condition = api_get_session_condition($session_id);
3180
3181
            // calculates time
3182
            if (Database::num_rows($result) > 0) {
3183
                while ($row = Database::fetch_array($result)) {
3184
                    $lp_id = (int) $row['id'];
3185
3186
                    // Start Exercise in LP total_time
3187
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
3188
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
3189
                    foreach ($list as $itemId) {
3190
                        $sql = "SELECT max(view_count)
3191
                                FROM $lpViewTable
3192
                                WHERE
3193
                                    c_id = $course_id AND
3194
                                    lp_id = $lp_id AND
3195
                                    user_id = $student_id
3196
                                    $session_condition";
3197
                        $res = Database::query($sql);
3198
                        $view = '';
3199
                        if (Database::num_rows($res) > 0) {
3200
                            $myrow = Database::fetch_array($res);
3201
                            $view = $myrow[0];
3202
                        }
3203
                        $viewCondition = null;
3204
                        if (!empty($view)) {
3205
                            $viewCondition = " AND v.view_count = $view  ";
3206
                        }
3207
                        $sql = "SELECT
3208
                            iv.iid,                                             
3209
                            iv.total_time as mytime,
3210
                            i.id as myid,
3211
                            iv.view_count as iv_view_count,                            
3212
                            path
3213
                        FROM $lpItemTable as i
3214
                        INNER JOIN $lpItemViewTable as iv
3215
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
3216
                        INNER JOIN $lpViewTable as v
3217
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
3218
                        WHERE
3219
                            v.c_id = $course_id AND
3220
                            i.id = $itemId AND
3221
                            i.lp_id = $lp_id  AND
3222
                            v.user_id = $student_id AND
3223
                            item_type = 'quiz' AND 
3224
                            path <> '' AND
3225
                            v.session_id = $session_id
3226
                            $viewCondition
3227
                        ORDER BY iv.view_count DESC ";
3228
3229
                        $result = Database::query($sql);
3230
                        if (Database::num_rows($result)) {
3231
                            $row = Database::fetch_array($result);
3232
                            $totalTimeInLpItemView = $row['mytime'];
3233
                            $lpItemViewId = $row['iid'];
3234
3235
                            $sql = 'SELECT SUM(exe_duration) exe_duration 
3236
                                    FROM '.$trackExercises.'
3237
                                    WHERE
3238
                                        exe_exo_id="'.$row['path'].'" AND
3239
                                        exe_user_id="'.$student_id.'" AND
3240
                                        orig_lp_id = "'.$lp_id.'" AND
3241
                                        orig_lp_item_id = "'.$row['myid'].'" AND
3242
                                        c_id = '.$course_id.' AND
3243
                                        status <> "incomplete" AND
3244
                                        session_id = '.$session_id.'
3245
                                     ORDER BY exe_date DESC ';
3246
3247
                            $sumScoreResult = Database::query($sql);
3248
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
3249
                            if (!empty($durationRow['exe_duration'])) {
3250
                                $exeDuration = $durationRow['exe_duration'];
3251
                                if ($exeDuration != $totalTimeInLpItemView &&
3252
                                    !empty($lpItemViewId) &&
3253
                                    !empty($exeDuration)
3254
                                ) {
3255
                                    // Update c_lp_item_view.total_time
3256
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration' 
3257
                                                  WHERE iid = ".$lpItemViewId;
3258
                                    Database::query($sqlUpdate);
3259
                                }
3260
                            }
3261
                        }
3262
                    }
3263
3264
                    // End total_time fix
3265
3266
                    // Calculate total time
3267
                    $sql = "SELECT SUM(total_time)
3268
                            FROM $lpItemViewTable AS item_view
3269
                            INNER JOIN $lpViewTable AS view
3270
                            ON (
3271
                                item_view.lp_view_id = view.id AND
3272
                                item_view.c_id = view.c_id
3273
                            )
3274
                            WHERE
3275
                                item_view.c_id = $course_id AND
3276
                                view.c_id = $course_id AND
3277
                                view.lp_id = $lp_id AND
3278
                                view.user_id = $student_id AND
3279
                                session_id = $session_id";
3280
3281
                    $rs = Database::query($sql);
3282
                    if (Database::num_rows($rs) > 0) {
3283
                        $total_time += Database::result($rs, 0, 0);
3284
                    }
3285
                }
3286
            }
3287
        }
3288
3289
        return $total_time;
3290
    }
3291
3292
    /**
3293
     * This function gets last connection time to one learning path.
3294
     *
3295
     * @param int|array $student_id  Student id(s)
3296
     * @param string    $course_code Course code
3297
     * @param int       $lp_id       Learning path id
3298
     * @param int       $session_id
3299
     *
3300
     * @return int Total time
3301
     */
3302
    public static function get_last_connection_time_in_lp(
3303
        $student_id,
3304
        $course_code,
3305
        $lp_id,
3306
        $session_id = 0
3307
    ) {
3308
        $course = api_get_course_info($course_code);
3309
        $student_id = intval($student_id);
3310
        $lp_id = intval($lp_id);
3311
        $last_time = 0;
3312
        $session_id = intval($session_id);
3313
3314
        if (!empty($course)) {
3315
            $course_id = $course['real_id'];
3316
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3317
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3318
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3319
3320
            // Check the real number of LPs corresponding to the filter in the
3321
            // database (and if no list was given, get them all)
3322
            $sql = "SELECT id FROM $lp_table 
3323
                    WHERE c_id = $course_id AND id = $lp_id ";
3324
            $res_row_lp = Database::query($sql);
3325
            $count_row_lp = Database::num_rows($res_row_lp);
3326
3327
            // calculates last connection time
3328
            if ($count_row_lp > 0) {
3329
                $sql = 'SELECT MAX(start_time)
3330
                        FROM '.$t_lpiv.' AS item_view
3331
                        INNER JOIN '.$t_lpv.' AS view
3332
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
3333
                        WHERE
3334
                            total_time > 0 AND
3335
                            item_view.c_id = '.$course_id.' AND
3336
                            view.c_id = '.$course_id.' AND
3337
                            view.lp_id = '.$lp_id.' AND 
3338
                            view.user_id = '.$student_id.' AND 
3339
                            view.session_id = '.$session_id;
3340
                $rs = Database::query($sql);
3341
                if (Database::num_rows($rs) > 0) {
3342
                    $last_time = Database::result($rs, 0, 0);
3343
                }
3344
            }
3345
        }
3346
3347
        return $last_time;
3348
    }
3349
3350
    /**
3351
     * gets the list of students followed by coach.
3352
     *
3353
     * @param int $coach_id Coach id
3354
     *
3355
     * @return array List of students
3356
     */
3357
    public static function get_student_followed_by_coach($coach_id)
3358
    {
3359
        $coach_id = intval($coach_id);
3360
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3361
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3362
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3363
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3364
3365
        $students = [];
3366
        // At first, courses where $coach_id is coach of the course //
3367
        $sql = 'SELECT session_id, c_id
3368
                FROM '.$tbl_session_course_user.'
3369
                WHERE user_id='.$coach_id.' AND status=2';
3370
3371
        if (api_is_multiple_url_enabled()) {
3372
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3373
            $access_url_id = api_get_current_access_url_id();
3374
            if ($access_url_id != -1) {
3375
                $sql = 'SELECT scu.session_id, scu.c_id
3376
                        FROM '.$tbl_session_course_user.' scu
3377
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
3378
                        ON (scu.session_id=sru.session_id)
3379
                        WHERE
3380
                            scu.user_id='.$coach_id.' AND
3381
                            scu.status=2 AND
3382
                            sru.access_url_id = '.$access_url_id;
3383
            }
3384
        }
3385
3386
        $result = Database::query($sql);
3387
3388
        while ($a_courses = Database::fetch_array($result)) {
3389
            $courseId = $a_courses['c_id'];
3390
            $id_session = $a_courses['session_id'];
3391
3392
            $sql = "SELECT DISTINCT srcru.user_id
3393
                    FROM $tbl_session_course_user AS srcru
3394
                    INNER JOIN $tbl_session_user sru
3395
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
3396
                    WHERE
3397
                        sru.relation_type<>".SESSION_RELATION_TYPE_RRHH." AND
3398
                        srcru.c_id = '$courseId' AND
3399
                        srcru.session_id = '$id_session'";
3400
3401
            $rs = Database::query($sql);
3402
            while ($row = Database::fetch_array($rs)) {
3403
                $students[$row['user_id']] = $row['user_id'];
3404
            }
3405
        }
3406
3407
        // Then, courses where $coach_id is coach of the session    //
3408
        $sql = 'SELECT session_course_user.user_id
3409
                FROM '.$tbl_session_course_user.' as session_course_user
3410
                INNER JOIN '.$tbl_session_user.' sru
3411
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
3412
                INNER JOIN '.$tbl_session_course.' as session_course
3413
                ON session_course.c_id = session_course_user.c_id
3414
                AND session_course_user.session_id = session_course.session_id
3415
                INNER JOIN '.$tbl_session.' as session
3416
                ON session.id = session_course.session_id
3417
                AND session.id_coach = '.$coach_id;
3418
        if (api_is_multiple_url_enabled()) {
3419
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3420
            $access_url_id = api_get_current_access_url_id();
3421
            if ($access_url_id != -1) {
3422
                $sql = 'SELECT session_course_user.user_id
3423
                        FROM '.$tbl_session_course_user.' as session_course_user
3424
                        INNER JOIN '.$tbl_session_user.' sru
3425
                        ON session_course_user.user_id = sru.user_id AND
3426
                           session_course_user.session_id = sru.session_id
3427
                        INNER JOIN '.$tbl_session_course.' as session_course
3428
                        ON session_course.c_id = session_course_user.c_id AND
3429
                        session_course_user.session_id = session_course.session_id
3430
                        INNER JOIN '.$tbl_session.' as session
3431
                        ON session.id = session_course.session_id AND
3432
                        session.id_coach = '.$coach_id.'
3433
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
3434
                        ON session.id = session_rel_url.session_id 
3435
                        WHERE access_url_id = '.$access_url_id;
3436
            }
3437
        }
3438
3439
        $result = Database::query($sql);
3440
        while ($row = Database::fetch_array($result)) {
3441
            $students[$row['user_id']] = $row['user_id'];
3442
        }
3443
3444
        return $students;
3445
    }
3446
3447
    /**
3448
     * Get student followed by a coach inside a session.
3449
     *
3450
     * @param    int        Session id
3451
     * @param    int        Coach id
3452
     *
3453
     * @return array students list
3454
     */
3455
    public static function get_student_followed_by_coach_in_a_session(
3456
        $id_session,
3457
        $coach_id
3458
    ) {
3459
        $coach_id = intval($coach_id);
3460
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3461
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3462
3463
        $students = [];
3464
        // At first, courses where $coach_id is coach of the course //
3465
        $sql = 'SELECT c_id FROM '.$tbl_session_course_user.'
3466
                WHERE session_id="'.$id_session.'" AND user_id='.$coach_id.' AND status=2';
3467
        $result = Database::query($sql);
3468
3469
        while ($a_courses = Database::fetch_array($result)) {
3470
            $courseId = $a_courses['c_id'];
3471
            $sql = "SELECT DISTINCT srcru.user_id
3472
                    FROM $tbl_session_course_user AS srcru
3473
                    WHERE
3474
                        c_id = '$courseId' AND
3475
                        session_id = '".$id_session."'";
3476
            $rs = Database::query($sql);
3477
            while ($row = Database::fetch_array($rs)) {
3478
                $students[$row['user_id']] = $row['user_id'];
3479
            }
3480
        }
3481
3482
        // Then, courses where $coach_id is coach of the session
3483
        $sql = 'SELECT id_coach FROM '.$tbl_session.'
3484
                WHERE id="'.$id_session.'" AND id_coach="'.$coach_id.'"';
3485
        $result = Database::query($sql);
3486
3487
        //He is the session_coach so we select all the users in the session
3488
        if (Database::num_rows($result) > 0) {
3489
            $sql = 'SELECT DISTINCT srcru.user_id
3490
                    FROM '.$tbl_session_course_user.' AS srcru
3491
                    WHERE session_id="'.$id_session.'"';
3492
            $result = Database::query($sql);
3493
            while ($row = Database::fetch_array($result)) {
3494
                $students[$row['user_id']] = $row['user_id'];
3495
            }
3496
        }
3497
3498
        return $students;
3499
    }
3500
3501
    /**
3502
     * Check if a coach is allowed to follow a student.
3503
     *
3504
     * @param    int        Coach id
3505
     * @param    int        Student id
3506
     *
3507
     * @return bool
3508
     */
3509
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3510
    {
3511
        $coach_id = intval($coach_id);
3512
        $student_id = intval($student_id);
3513
3514
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3515
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3516
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3517
3518
        // At first, courses where $coach_id is coach of the course //
3519
3520
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
3521
                WHERE user_id='.$coach_id.' AND status=2';
3522
        $result = Database::query($sql);
3523
        if (Database::num_rows($result) > 0) {
3524
            return true;
3525
        }
3526
3527
        // Then, courses where $coach_id is coach of the session
3528
        $sql = 'SELECT session_course_user.user_id
3529
                FROM '.$tbl_session_course_user.' as session_course_user
3530
                INNER JOIN '.$tbl_session_course.' as session_course
3531
                ON session_course.c_id = session_course_user.c_id
3532
                INNER JOIN '.$tbl_session.' as session
3533
                ON session.id = session_course.session_id
3534
                AND session.id_coach = '.$coach_id.'
3535
                WHERE user_id = '.$student_id;
3536
        $result = Database::query($sql);
3537
        if (Database::num_rows($result) > 0) {
3538
            return true;
3539
        }
3540
3541
        return false;
3542
    }
3543
3544
    /**
3545
     * Get courses followed by coach.
3546
     *
3547
     * @param     int        Coach id
3548
     * @param    int        Session id (optional)
3549
     *
3550
     * @return array Courses list
3551
     */
3552
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
3553
    {
3554
        $coach_id = intval($coach_id);
3555
        if (!empty($id_session)) {
3556
            $id_session = intval($id_session);
3557
        }
3558
3559
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3560
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3561
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3562
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3563
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3564
3565
        // At first, courses where $coach_id is coach of the course.
3566
3567
        $sql = 'SELECT DISTINCT c.code
3568
                FROM '.$tbl_session_course_user.' sc
3569
                INNER JOIN '.$tbl_course.' c
3570
                ON (c.id = sc.c_id)
3571
                WHERE user_id = '.$coach_id.' AND status = 2';
3572
3573
        if (api_is_multiple_url_enabled()) {
3574
            $access_url_id = api_get_current_access_url_id();
3575
            if ($access_url_id != -1) {
3576
                $sql = 'SELECT DISTINCT c.code
3577
                        FROM '.$tbl_session_course_user.' scu
3578
                        INNER JOIN '.$tbl_course.' c
3579
                        ON (c.code = scu.c_id)
3580
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3581
                        ON (c.id = cru.c_id)
3582
                        WHERE
3583
                            scu.user_id='.$coach_id.' AND
3584
                            scu.status=2 AND
3585
                            cru.access_url_id = '.$access_url_id;
3586
            }
3587
        }
3588
3589
        if (!empty($id_session)) {
3590
            $sql .= ' AND session_id='.$id_session;
3591
        }
3592
3593
        $courseList = [];
3594
        $result = Database::query($sql);
3595
        while ($row = Database::fetch_array($result)) {
3596
            $courseList[$row['code']] = $row['code'];
3597
        }
3598
3599
        // Then, courses where $coach_id is coach of the session
3600
        $sql = 'SELECT DISTINCT course.code
3601
                FROM '.$tbl_session_course.' as session_course
3602
                INNER JOIN '.$tbl_session.' as session
3603
                    ON session.id = session_course.session_id
3604
                    AND session.id_coach = '.$coach_id.'
3605
                INNER JOIN '.$tbl_course.' as course
3606
                    ON course.id = session_course.c_id';
3607
3608
        if (api_is_multiple_url_enabled()) {
3609
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3610
            $access_url_id = api_get_current_access_url_id();
3611
            if ($access_url_id != -1) {
3612
                $sql = 'SELECT DISTINCT c.code
3613
                    FROM '.$tbl_session_course.' as session_course
3614
                    INNER JOIN '.$tbl_course.' c
3615
                    ON (c.id = session_course.c_id)
3616
                    INNER JOIN '.$tbl_session.' as session
3617
                    ON session.id = session_course.session_id
3618
                        AND session.id_coach = '.$coach_id.'
3619
                    INNER JOIN '.$tbl_course.' as course
3620
                        ON course.id = session_course.c_id
3621
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
3622
                    ON (course_rel_url.c_id = c.id)';
3623
            }
3624
        }
3625
3626
        if (!empty($id_session)) {
3627
            $sql .= ' WHERE session_course.session_id='.$id_session;
3628
            if (api_is_multiple_url_enabled()) {
3629
                $sql .= ' AND access_url_id = '.$access_url_id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $access_url_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
3630
            }
3631
        } else {
3632
            if (api_is_multiple_url_enabled()) {
3633
                $sql .= ' WHERE access_url_id = '.$access_url_id;
3634
            }
3635
        }
3636
3637
        $result = Database::query($sql);
3638
        while ($row = Database::fetch_array($result)) {
3639
            $courseList[$row['code']] = $row['code'];
3640
        }
3641
3642
        return $courseList;
3643
    }
3644
3645
    /**
3646
     * Get sessions coached by user.
3647
     *
3648
     * @param $coach_id
3649
     * @param int    $start
3650
     * @param int    $limit
3651
     * @param bool   $getCount
3652
     * @param string $keyword
3653
     * @param string $description
3654
     *
3655
     * @return mixed
3656
     */
3657
    public static function get_sessions_coached_by_user(
3658
        $coach_id,
3659
        $start = 0,
3660
        $limit = 0,
3661
        $getCount = false,
3662
        $keyword = '',
3663
        $description = ''
3664
    ) {
3665
        // table definition
3666
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3667
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3668
        $coach_id = intval($coach_id);
3669
3670
        $select = " SELECT * FROM ";
3671
        if ($getCount) {
3672
            $select = " SELECT count(DISTINCT id) as count FROM ";
3673
        }
3674
3675
        $limitCondition = null;
3676
        if (!empty($start) && !empty($limit)) {
3677
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3678
        }
3679
3680
        $keywordCondition = null;
3681
3682
        if (!empty($keyword)) {
3683
            $keyword = Database::escape_string($keyword);
3684
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
3685
3686
            if (!empty($description)) {
3687
                $description = Database::escape_string($description);
3688
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3689
            }
3690
        }
3691
3692
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3693
        $access_url_id = api_get_current_access_url_id();
3694
3695
        $sql = "
3696
            $select
3697
            (
3698
                SELECT DISTINCT
3699
                    session.id,
3700
                    name,
3701
                    access_start_date,
3702
                    access_end_date
3703
                FROM $tbl_session session
3704
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3705
                ON (session.id = session_rel_url.session_id)
3706
                WHERE
3707
                    id_coach = $coach_id AND
3708
                    access_url_id = $access_url_id
3709
                    $keywordCondition
3710
            UNION
3711
                SELECT DISTINCT
3712
                    session.id,
3713
                    session.name,
3714
                    session.access_start_date,
3715
                    session.access_end_date
3716
                FROM $tbl_session as session
3717
                INNER JOIN $tbl_session_course_user as session_course_user
3718
                ON
3719
                    session.id = session_course_user.session_id AND
3720
                    session_course_user.user_id = $coach_id AND
3721
                    session_course_user.status = 2
3722
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3723
                ON (session.id = session_rel_url.session_id)
3724
                WHERE
3725
                    access_url_id = $access_url_id
3726
                    $keywordCondition
3727
            ) as sessions $limitCondition
3728
            ";
3729
3730
        $rs = Database::query($sql);
3731
        if ($getCount) {
3732
            $row = Database::fetch_array($rs);
3733
3734
            return $row['count'];
3735
        }
3736
3737
        $sessions = [];
3738
        while ($row = Database::fetch_array($rs)) {
3739
            if ($row['access_start_date'] == '0000-00-00 00:00:00') {
3740
                $row['access_start_date'] = null;
3741
            }
3742
3743
            $sessions[$row['id']] = $row;
3744
        }
3745
3746
        if (!empty($sessions)) {
3747
            foreach ($sessions as &$session) {
3748
                if (empty($session['access_start_date'])
3749
                ) {
3750
                    $session['status'] = get_lang('SessionActive');
3751
                } else {
3752
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3753
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3754
                    if ($time_start < time() && time() < $time_end) {
3755
                        $session['status'] = get_lang('SessionActive');
3756
                    } else {
3757
                        if (time() < $time_start) {
3758
                            $session['status'] = get_lang('SessionFuture');
3759
                        } else {
3760
                            if (time() > $time_end) {
3761
                                $session['status'] = get_lang('SessionPast');
3762
                            }
3763
                        }
3764
                    }
3765
                }
3766
            }
3767
        }
3768
3769
        return $sessions;
3770
    }
3771
3772
    /**
3773
     * Get courses list from a session.
3774
     *
3775
     * @param    int        Session id
3776
     *
3777
     * @return array Courses list
3778
     */
3779
    public static function get_courses_list_from_session($session_id)
3780
    {
3781
        $session_id = intval($session_id);
3782
3783
        // table definition
3784
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3785
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3786
3787
        $sql = "SELECT DISTINCT code, c_id
3788
                FROM $tbl_session_course sc
3789
                INNER JOIN $courseTable c
3790
                ON sc.c_id = c.id
3791
                WHERE session_id= $session_id";
3792
3793
        $result = Database::query($sql);
3794
3795
        $courses = [];
3796
        while ($row = Database::fetch_array($result)) {
3797
            $courses[$row['code']] = $row;
3798
        }
3799
3800
        return $courses;
3801
    }
3802
3803
    /**
3804
     * Count the number of documents that an user has uploaded to a course.
3805
     *
3806
     * @param    int|array   Student id(s)
3807
     * @param    string      Course code
3808
     * @param    int         Session id (optional),
3809
     * if param $session_id is null(default)
3810
     * return count of assignments including sessions, 0 = session is not filtered
3811
     *
3812
     * @return int Number of documents
3813
     */
3814
    public static function count_student_uploaded_documents(
3815
        $student_id,
3816
        $course_code,
3817
        $session_id = null
3818
    ) {
3819
        // get the information of the course
3820
        $a_course = api_get_course_info($course_code);
3821
        if (!empty($a_course)) {
3822
            // table definition
3823
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3824
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
3825
            $course_id = $a_course['real_id'];
3826
            if (is_array($student_id)) {
3827
                $studentList = array_map('intval', $student_id);
3828
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
3829
            } else {
3830
                $student_id = intval($student_id);
3831
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
3832
            }
3833
3834
            $condition_session = null;
3835
            if (isset($session_id)) {
3836
                $session_id = intval($session_id);
3837
                $condition_session = " AND pub.session_id = $session_id ";
3838
            }
3839
3840
            $sql = "SELECT count(ip.tool) AS count
3841
                    FROM $tbl_item_property ip
3842
                    INNER JOIN $tbl_document pub
3843
                    ON (ip.ref = pub.id AND ip.c_id = pub.c_id)
3844
                    WHERE
3845
                        ip.c_id  = $course_id AND
3846
                        pub.c_id  = $course_id AND
3847
                        pub.filetype ='file' AND
3848
                        ip.tool = 'document'
3849
                        $condition_user $condition_session ";
3850
            $rs = Database::query($sql);
3851
            $row = Database::fetch_array($rs, 'ASSOC');
3852
3853
            return $row['count'];
3854
        }
3855
3856
        return null;
3857
    }
3858
3859
    /**
3860
     * Count assignments per student.
3861
     *
3862
     * @param $student_id
3863
     * @param null $course_code
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $course_code is correct as it would always require null to be passed?
Loading history...
Documentation Bug introduced by
Are you sure the doc-type for parameter $session_id is correct as it would always require null to be passed?
Loading history...
3864
     * @param null $session_id
3865
     *
3866
     * @return int Count of assignments
3867
     *
3868
     * @internal param array|int $Student id(s)
3869
     * @internal param Course $string code
3870
     * @internal param Session $int id (optional),
3871
     * if param $session_id is null(default) return count of assignments
3872
     * including sessions, 0 = session is not filtered
3873
     */
3874
    public static function count_student_assignments(
3875
        $student_id,
3876
        $course_code = null,
3877
        $session_id = null
3878
    ) {
3879
        if (empty($student_id)) {
3880
            return 0;
3881
        }
3882
3883
        $conditions = [];
3884
3885
        // Get the information of the course
3886
        $a_course = api_get_course_info($course_code);
3887
        if (!empty($a_course)) {
3888
            $course_id = $a_course['real_id'];
3889
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
3890
        }
3891
3892
        // table definition
3893
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3894
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
3895
3896
        if (is_array($student_id)) {
3897
            $studentList = array_map('intval', $student_id);
3898
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
3899
        } else {
3900
            $student_id = intval($student_id);
3901
            $conditions[] = " ip.insert_user_id = '$student_id' ";
3902
        }
3903
3904
        $conditions[] = ' pub.active <> 2 ';
3905
        $conditionToString = implode(' AND ', $conditions);
3906
3907
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
3908
        $conditionToString .= $sessionCondition;
3909
3910
        $sql = "SELECT count(ip.tool) as count
3911
                FROM $tbl_item_property ip
3912
                INNER JOIN $tbl_student_publication pub
3913
                ON (ip.ref = pub.id AND ip.c_id = pub.c_id)
3914
                WHERE
3915
                    ip.tool='work' AND
3916
                    $conditionToString";
3917
        $rs = Database::query($sql);
3918
        $row = Database::fetch_array($rs, 'ASSOC');
3919
3920
        return $row['count'];
3921
    }
3922
3923
    /**
3924
     * Count messages per student inside forum tool.
3925
     *
3926
     * @param    int|array        Student id
3927
     * @param    string    Course code
3928
     * @param int        Session id (optional), if param $session_id is
3929
     *                                                               null(default) return count of messages including sessions, 0 = session is not filtered
3930
     *
3931
     * @return int Count of messages
3932
     */
3933
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
3934
    {
3935
        if (empty($student_id)) {
3936
            return 0;
3937
        }
3938
3939
        // Table definition.
3940
        $tbl_forum_post = Database::get_course_table(TABLE_FORUM_POST);
3941
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
3942
3943
        $conditions = [];
3944
        if (is_array($student_id)) {
3945
            $studentList = array_map('intval', $student_id);
3946
            $conditions[] = " post.poster_id IN ('".implode("','", $studentList)."') ";
3947
        } else {
3948
            $student_id = intval($student_id);
3949
            $conditions[] = " post.poster_id = '$student_id' ";
3950
        }
3951
3952
        $conditionsToString = implode('AND ', $conditions);
3953
3954
        if (empty($courseCode)) {
3955
            $sql = "SELECT count(poster_id) as count
3956
                    FROM $tbl_forum_post post
3957
                    INNER JOIN $tbl_forum forum
3958
                    ON (forum.forum_id = post.forum_id AND forum.c_id = post.c_id)
3959
                    WHERE $conditionsToString";
3960
3961
            $rs = Database::query($sql);
3962
            $row = Database::fetch_array($rs, 'ASSOC');
3963
3964
            return $row['count'];
3965
        }
3966
3967
        require_once api_get_path(SYS_CODE_PATH).'forum/forumconfig.inc.php';
3968
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
3969
3970
        $courseInfo = api_get_course_info($courseCode);
3971
3972
        $forums = [];
3973
        if (!empty($courseInfo)) {
3974
            $forums = get_forums('', $courseCode, true, $session_id);
3975
            $course_id = $courseInfo['real_id'];
3976
            $conditions[] = " post.c_id  = $course_id ";
3977
        }
3978
3979
        if (!empty($forums)) {
3980
            $idList = array_column($forums, 'forum_id');
3981
            $idListToString = implode("', '", $idList);
3982
            $conditions[] = " post.forum_id  IN ('$idListToString')";
3983
        }
3984
3985
        $conditionsToString = implode('AND ', $conditions);
3986
        $sql = "SELECT count(poster_id) as count
3987
                FROM $tbl_forum_post post
3988
                WHERE $conditionsToString";
3989
3990
        $rs = Database::query($sql);
3991
        $row = Database::fetch_array($rs, 'ASSOC');
3992
        $count = $row['count'];
3993
3994
        return $count;
3995
    }
3996
3997
    /**
3998
     * This function counts the number of post by course.
3999
     *
4000
     * @param      string     Course code
4001
     * @param int        Session id (optional), if param $session_id is
4002
     *                                                               null(default) it'll return results including sessions,
4003
     *                                                               0 = session is not filtered
4004
     * @param int                                        $groupId
4005
     *
4006
     * @return int The number of post by course
4007
     */
4008
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
4009
    {
4010
        $courseInfo = api_get_course_info($course_code);
4011
        if (!empty($courseInfo)) {
4012
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
4013
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
4014
4015
            $condition_session = '';
4016
            if (isset($session_id)) {
4017
                $session_id = intval($session_id);
4018
                $condition_session = api_get_session_condition(
4019
                    $session_id,
4020
                    true,
4021
                    false,
4022
                    'f.session_id'
4023
                );
4024
            }
4025
4026
            $course_id = $courseInfo['real_id'];
4027
            $groupId = intval($groupId);
4028
            if (!empty($groupId)) {
4029
                $groupCondition = " i.to_group_id = $groupId  ";
4030
            } else {
4031
                $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4032
            }
4033
4034
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4035
            $sql = "SELECT count(*) FROM $tbl_posts p
4036
                    INNER JOIN $tbl_forums f
4037
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
4038
                    INNER JOIN $item i
4039
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
4040
                    WHERE
4041
                        p.c_id = $course_id AND
4042
                        f.c_id = $course_id AND
4043
                        $groupCondition
4044
                        $condition_session
4045
                    ";
4046
            $result = Database::query($sql);
4047
            $row = Database::fetch_row($result);
4048
            $count = $row[0];
4049
4050
            return $count;
4051
        } else {
4052
            return null;
4053
        }
4054
    }
4055
4056
    /**
4057
     * This function counts the number of threads by course.
4058
     *
4059
     * @param      string     Course code
4060
     * @param    int        Session id (optional),
4061
     * if param $session_id is null(default) it'll return results including
4062
     * sessions, 0 = session is not filtered
4063
     * @param int $groupId
4064
     *
4065
     * @return int The number of threads by course
4066
     */
4067
    public static function count_number_of_threads_by_course(
4068
        $course_code,
4069
        $session_id = null,
4070
        $groupId = 0
4071
    ) {
4072
        $course_info = api_get_course_info($course_code);
4073
        if (empty($course_info)) {
4074
            return null;
4075
        }
4076
4077
        $course_id = $course_info['real_id'];
4078
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
4079
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4080
4081
        $condition_session = '';
4082
        if (isset($session_id)) {
4083
            $session_id = intval($session_id);
4084
            $condition_session = ' AND f.session_id = '.$session_id;
4085
        }
4086
4087
        $groupId = intval($groupId);
4088
4089
        if (!empty($groupId)) {
4090
            $groupCondition = " i.to_group_id = $groupId ";
4091
        } else {
4092
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4093
        }
4094
4095
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4096
        $sql = "SELECT count(*)
4097
                FROM $tbl_threads t
4098
                INNER JOIN $tbl_forums f
4099
                ON f.iid = t.forum_id AND f.c_id = t.c_id
4100
                INNER JOIN $item i
4101
                ON (
4102
                    tool = '".TOOL_FORUM_THREAD."' AND
4103
                    f.c_id = i.c_id AND
4104
                    t.iid = i.ref
4105
                )
4106
                WHERE
4107
                    t.c_id = $course_id AND
4108
                    f.c_id = $course_id AND
4109
                    $groupCondition
4110
                    $condition_session
4111
                ";
4112
4113
        $result = Database::query($sql);
4114
        if (Database::num_rows($result)) {
4115
            $row = Database::fetch_row($result);
4116
            $count = $row[0];
4117
4118
            return $count;
4119
        } else {
4120
            return null;
4121
        }
4122
    }
4123
4124
    /**
4125
     * This function counts the number of forums by course.
4126
     *
4127
     * @param      string     Course code
4128
     * @param    int        Session id (optional),
4129
     * if param $session_id is null(default) it'll return results
4130
     * including sessions, 0 = session is not filtered
4131
     * @param int $groupId
4132
     *
4133
     * @return int The number of forums by course
4134
     */
4135
    public static function count_number_of_forums_by_course(
4136
        $course_code,
4137
        $session_id = null,
4138
        $groupId = 0
4139
    ) {
4140
        $course_info = api_get_course_info($course_code);
4141
        if (empty($course_info)) {
4142
            return null;
4143
        }
4144
        $course_id = $course_info['real_id'];
4145
4146
        $condition_session = '';
4147
        if (isset($session_id)) {
4148
            $session_id = intval($session_id);
4149
            $condition_session = ' AND f.session_id = '.$session_id;
4150
        }
4151
4152
        $groupId = intval($groupId);
4153
        if (!empty($groupId)) {
4154
            $groupCondition = " i.to_group_id = $groupId ";
4155
        } else {
4156
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
4157
        }
4158
4159
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
4160
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
4161
4162
        $sql = "SELECT count(*)
4163
                FROM $tbl_forums f
4164
                INNER JOIN $item i
4165
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
4166
                WHERE
4167
                    f.c_id = $course_id AND
4168
                    $groupCondition
4169
                    $condition_session
4170
                ";
4171
        $result = Database::query($sql);
4172
        if (Database::num_rows($result)) {
4173
            $row = Database::fetch_row($result);
4174
            $count = $row[0];
4175
4176
            return $count;
4177
        } else {
4178
            return null;
4179
        }
4180
    }
4181
4182
    /**
4183
     * This function counts the chat last connections by course in x days.
4184
     *
4185
     * @param      string     Course code
4186
     * @param      int     Last x days
4187
     * @param    int        Session id (optional)
4188
     *
4189
     * @return int Chat last connections by course in x days
4190
     */
4191
    public static function chat_connections_during_last_x_days_by_course(
4192
        $course_code,
4193
        $last_days,
4194
        $session_id = 0
4195
    ) {
4196
        $course_info = api_get_course_info($course_code);
4197
        if (empty($course_info)) {
4198
            return null;
4199
        }
4200
        $course_id = $course_info['real_id'];
4201
4202
        // Protect data
4203
        $last_days = intval($last_days);
4204
        $session_id = intval($session_id);
4205
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4206
        $now = api_get_utc_datetime();
4207
4208
        $sql = "SELECT count(*) FROM $tbl_stats_access
4209
                WHERE
4210
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
4211
                    c_id = '$course_id' AND
4212
                    access_tool='".TOOL_CHAT."' AND
4213
                    access_session_id='$session_id' ";
4214
        $result = Database::query($sql);
4215
        if (Database::num_rows($result)) {
4216
            $row = Database::fetch_row($result);
4217
            $count = $row[0];
4218
4219
            return $count;
4220
        } else {
4221
            return null;
4222
        }
4223
    }
4224
4225
    /**
4226
     * This function gets the last student's connection in chat.
4227
     *
4228
     * @param      int     Student id
4229
     * @param      string     Course code
4230
     * @param    int        Session id (optional)
4231
     *
4232
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
4233
     */
4234
    public static function chat_last_connection(
4235
        $student_id,
4236
        $courseId,
4237
        $session_id = 0
4238
    ) {
4239
        $student_id = intval($student_id);
4240
        $courseId = intval($courseId);
4241
        $session_id = intval($session_id);
4242
        $date_time = '';
4243
4244
        // table definition
4245
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4246
        $sql = "SELECT access_date
4247
                FROM $tbl_stats_access
4248
                WHERE
4249
                     access_tool='".TOOL_CHAT."' AND
4250
                     access_user_id='$student_id' AND
4251
                     c_id = $courseId AND
4252
                     access_session_id = '$session_id'
4253
                ORDER BY access_date DESC limit 1";
4254
        $rs = Database::query($sql);
4255
        if (Database::num_rows($rs) > 0) {
4256
            $row = Database::fetch_array($rs);
4257
            $date_time = api_convert_and_format_date(
4258
                $row['access_date'],
4259
                null,
4260
                date_default_timezone_get()
4261
            );
4262
        }
4263
4264
        return $date_time;
4265
    }
4266
4267
    /**
4268
     * Get count student's visited links.
4269
     *
4270
     * @param int $student_id Student id
4271
     * @param int $courseId
4272
     * @param int $session_id Session id (optional)
4273
     *
4274
     * @return int count of visited links
4275
     */
4276
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
4277
    {
4278
        $student_id = intval($student_id);
4279
        $courseId = intval($courseId);
4280
        $session_id = intval($session_id);
4281
4282
        // table definition
4283
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4284
4285
        $sql = 'SELECT 1
4286
                FROM '.$table.'
4287
                WHERE
4288
                    links_user_id= '.$student_id.' AND
4289
                    c_id = "'.$courseId.'" AND
4290
                    links_session_id = '.$session_id.' ';
4291
4292
        $rs = Database::query($sql);
4293
4294
        return Database::num_rows($rs);
4295
    }
4296
4297
    /**
4298
     * Get count student downloaded documents.
4299
     *
4300
     * @param    int        Student id
4301
     * @param int $courseId
4302
     * @param    int        Session id (optional)
4303
     *
4304
     * @return int Count downloaded documents
4305
     */
4306
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
4307
    {
4308
        $student_id = intval($student_id);
4309
        $courseId = intval($courseId);
4310
        $session_id = intval($session_id);
4311
4312
        // table definition
4313
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4314
4315
        $sql = 'SELECT 1
4316
                FROM '.$table.'
4317
                WHERE down_user_id = '.$student_id.'
4318
                AND c_id  = "'.$courseId.'"
4319
                AND down_session_id = '.$session_id.' ';
4320
        $rs = Database::query($sql);
4321
4322
        return Database::num_rows($rs);
4323
    }
4324
4325
    /**
4326
     * Get course list inside a session from a student.
4327
     *
4328
     * @param int $user_id    Student id
4329
     * @param int $id_session Session id (optional)
4330
     *
4331
     * @return array Courses list
4332
     */
4333
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
4334
    {
4335
        $user_id = intval($user_id);
4336
        $id_session = intval($id_session);
4337
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4338
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4339
4340
        $sql = "SELECT c.code
4341
                FROM $tbl_session_course_user sc
4342
                INNER JOIN $courseTable c
4343
                WHERE
4344
                    user_id= $user_id  AND
4345
                    session_id = $id_session";
4346
        $result = Database::query($sql);
4347
        $courses = [];
4348
        while ($row = Database::fetch_array($result)) {
4349
            $courses[$row['code']] = $row['code'];
4350
        }
4351
4352
        return $courses;
4353
    }
4354
4355
    /**
4356
     * Get inactive students in course.
4357
     *
4358
     * @param int        $courseId
4359
     * @param string|int $since      Since login course date (optional, default = 'never')
4360
     * @param int        $session_id (optional)
4361
     *
4362
     * @return array Inactive users
4363
     */
4364
    public static function getInactiveStudentsInCourse(
4365
        $courseId,
4366
        $since = 'never',
4367
        $session_id = 0
4368
    ) {
4369
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4370
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4371
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4372
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4373
        $now = api_get_utc_datetime();
4374
        $courseId = (int) $courseId;
4375
        $session_id = (int) $session_id;
4376
4377
        if (empty($courseId)) {
4378
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
4379
        }
4380
4381
        if ($since === 'never') {
4382
            if (empty($session_id)) {
4383
                $sql = 'SELECT course_user.user_id
4384
                        FROM '.$table_course_rel_user.' course_user
4385
                        LEFT JOIN '.$tbl_track_login.' stats_login
4386
                        ON course_user.user_id = stats_login.user_id AND
4387
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
4388
                        INNER JOIN '.$tableCourse.' c
4389
                        ON (c.id = course_user.c_id)
4390
                        WHERE
4391
                            course_user.c_id = '.$courseId.' AND
4392
                            stats_login.login_course_date IS NULL
4393
                        GROUP BY course_user.user_id';
4394
            } else {
4395
                $sql = 'SELECT session_course_user.user_id
4396
                        FROM '.$tbl_session_course_user.' session_course_user
4397
                        LEFT JOIN '.$tbl_track_login.' stats_login
4398
                        ON session_course_user.user_id = stats_login.user_id
4399
                        INNER JOIN '.$tableCourse.' c
4400
                        ON (c.id = session_course_user.c_id)
4401
                        WHERE
4402
                            session_course_user.c_id = '.$courseId.' AND
4403
                            stats_login.login_course_date IS NULL
4404
                        GROUP BY session_course_user.user_id';
4405
            }
4406
        } else {
4407
            $since = (int) $since;
4408
            if (empty($session_id)) {
4409
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
4410
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
4411
            } else {
4412
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
4413
                          ON
4414
                            c.id = session_course_user.c_id AND
4415
                            session_course_user.session_id = '.$session_id.' AND
4416
                            session_course_user.user_id = stats_login.user_id ';
4417
            }
4418
4419
            $sql = 'SELECT 
4420
                    stats_login.user_id, 
4421
                    MAX(login_course_date) max_date
4422
                FROM '.$tbl_track_login.' stats_login
4423
                INNER JOIN '.$tableCourse.' c
4424
                ON (c.id = stats_login.c_id)
4425
                '.$inner.'
4426
                WHERE c.id = '.$courseId.'
4427
                GROUP BY stats_login.user_id
4428
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
4429
        }
4430
4431
        $rs = Database::query($sql);
4432
        $users = [];
4433
        while ($user = Database::fetch_array($rs)) {
4434
            $users[] = $user['user_id'];
4435
        }
4436
4437
        return $users;
4438
    }
4439
4440
    /**
4441
     * Get count login per student.
4442
     *
4443
     * @param int $student_id Student id
4444
     * @param int $courseId
4445
     * @param int $session_id Session id (optional)
4446
     *
4447
     * @return int count login
4448
     */
4449
    public static function count_login_per_student($student_id, $courseId, $session_id = 0)
4450
    {
4451
        $student_id = intval($student_id);
4452
        $courseId = intval($courseId);
4453
        $session_id = intval($session_id);
4454
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4455
4456
        $sql = 'SELECT '.$student_id.'
4457
                FROM '.$table.'
4458
                WHERE
4459
                    access_user_id='.$student_id.' AND
4460
                    c_id="'.$courseId.'" AND
4461
                    access_session_id = "'.$session_id.'" ';
4462
4463
        $rs = Database::query($sql);
4464
        $nb_login = Database::num_rows($rs);
4465
4466
        return $nb_login;
4467
    }
4468
4469
    /**
4470
     * Get students followed by a human resources manager.
4471
     *
4472
     * @param    int        Drh id
4473
     *
4474
     * @return array Student list
4475
     */
4476
    public static function get_student_followed_by_drh($hr_dept_id)
4477
    {
4478
        $hr_dept_id = intval($hr_dept_id);
4479
        $a_students = [];
4480
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4481
4482
        $sql = 'SELECT DISTINCT user_id FROM '.$tbl_user.' as user
4483
                WHERE hr_dept_id='.$hr_dept_id;
4484
        $rs = Database::query($sql);
4485
4486
        while ($user = Database::fetch_array($rs)) {
4487
            $a_students[$user['user_id']] = $user['user_id'];
4488
        }
4489
4490
        return $a_students;
4491
    }
4492
4493
    /**
4494
     * get count clicks about tools most used by course.
4495
     *
4496
     * @param int $courseId
4497
     * @param    int        Session id (optional),
4498
     * if param $session_id is null(default) it'll return results
4499
     * including sessions, 0 = session is not filtered
4500
     *
4501
     * @return array tools data
4502
     */
4503
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
4504
    {
4505
        $courseId = intval($courseId);
4506
        $data = [];
4507
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4508
        $condition_session = '';
4509
        if (isset($session_id)) {
4510
            $session_id = intval($session_id);
4511
            $condition_session = ' AND access_session_id = '.$session_id;
4512
        }
4513
        $sql = "SELECT
4514
                    access_tool,
4515
                    COUNT(DISTINCT access_user_id),
4516
                    count(access_tool) as count_access_tool
4517
                FROM $TABLETRACK_ACCESS
4518
                WHERE
4519
                    access_tool IS NOT NULL AND
4520
                    access_tool != '' AND
4521
                    c_id = '$courseId'
4522
                    $condition_session
4523
                GROUP BY access_tool
4524
                ORDER BY count_access_tool DESC
4525
                LIMIT 0, 3";
4526
        $rs = Database::query($sql);
4527
        if (Database::num_rows($rs) > 0) {
4528
            while ($row = Database::fetch_array($rs)) {
4529
                $data[] = $row;
4530
            }
4531
        }
4532
4533
        return $data;
4534
    }
4535
4536
    /**
4537
     * get documents most downloaded by course.
4538
     *
4539
     * @param      string     Course code
4540
     * @param    int        Session id (optional),
4541
     * if param $session_id is null(default) it'll return results including
4542
     * sessions, 0 = session is not filtered
4543
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4544
     *
4545
     * @return array documents downloaded
4546
     */
4547
    public static function get_documents_most_downloaded_by_course(
4548
        $course_code,
4549
        $session_id = 0,
4550
        $limit = 0
4551
    ) {
4552
        $courseId = api_get_course_int_id($course_code);
4553
        $data = [];
4554
4555
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4556
        $condition_session = '';
4557
        $session_id = intval($session_id);
4558
        if (!empty($session_id)) {
4559
            $condition_session = ' AND down_session_id = '.$session_id;
4560
        }
4561
        $sql = "SELECT
4562
                    down_doc_path,
4563
                    COUNT(DISTINCT down_user_id),
4564
                    COUNT(down_doc_path) as count_down
4565
                FROM $TABLETRACK_DOWNLOADS
4566
                WHERE c_id = $courseId
4567
                    $condition_session
4568
                GROUP BY down_doc_path
4569
                ORDER BY count_down DESC
4570
                LIMIT 0,  $limit";
4571
        $rs = Database::query($sql);
4572
4573
        if (Database::num_rows($rs) > 0) {
4574
            while ($row = Database::fetch_array($rs)) {
4575
                $data[] = $row;
4576
            }
4577
        }
4578
4579
        return $data;
4580
    }
4581
4582
    /**
4583
     * get links most visited by course.
4584
     *
4585
     * @param      string     Course code
4586
     * @param    int        Session id (optional),
4587
     * if param $session_id is null(default) it'll
4588
     * return results including sessions, 0 = session is not filtered
4589
     *
4590
     * @return array links most visited
4591
     */
4592
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4593
    {
4594
        $course_code = Database::escape_string($course_code);
4595
        $course_info = api_get_course_info($course_code);
4596
        $course_id = $course_info['real_id'];
4597
        $data = [];
4598
4599
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4600
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4601
4602
        $condition_session = '';
4603
        if (isset($session_id)) {
4604
            $session_id = intval($session_id);
4605
            $condition_session = ' AND cl.session_id = '.$session_id;
4606
        }
4607
4608
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4609
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4610
                WHERE
4611
                    cl.c_id = $course_id AND
4612
                    sl.links_link_id = cl.id AND
4613
                    sl.c_id = $course_id
4614
                    $condition_session
4615
                GROUP BY cl.title, cl.url
4616
                ORDER BY count_visits DESC
4617
                LIMIT 0, 3";
4618
        $rs = Database::query($sql);
4619
        if (Database::num_rows($rs) > 0) {
4620
            while ($row = Database::fetch_array($rs)) {
4621
                $data[] = $row;
4622
            }
4623
        }
4624
4625
        return $data;
4626
    }
4627
4628
    /**
4629
     * Shows the user progress (when clicking in the Progress tab).
4630
     *
4631
     * @param int    $user_id
4632
     * @param int    $session_id
4633
     * @param string $extra_params
4634
     * @param bool   $show_courses
4635
     * @param bool   $showAllSessions
4636
     *
4637
     * @return string
4638
     */
4639
    public static function show_user_progress(
4640
        $user_id,
4641
        $session_id = 0,
4642
        $extra_params = '',
4643
        $show_courses = true,
4644
        $showAllSessions = true
4645
    ) {
4646
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4647
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4648
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4649
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4650
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4651
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4652
4653
        $trackingColumns = [
4654
            'course_session' => [
4655
                'course_title' => true,
4656
                'published_exercises' => true,
4657
                'new_exercises' => true,
4658
                'my_average' => true,
4659
                'average_exercise_result' => true,
4660
                'time_spent' => true,
4661
                'lp_progress' => true,
4662
                'score' => true,
4663
                'best_score' => true,
4664
                'last_connection' => true,
4665
                'details' => true,
4666
            ],
4667
        ];
4668
4669
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
4670
        if (!empty($trackingColumnsConfig)) {
4671
            $trackingColumns = $trackingColumnsConfig;
4672
        }
4673
4674
        $user_id = intval($user_id);
4675
        $session_id = intval($session_id);
4676
        $urlId = api_get_current_access_url_id();
4677
4678
        if (api_is_multiple_url_enabled()) {
4679
            $sql = "SELECT c.code, title
4680
                    FROM $tbl_course_user cu
4681
                    INNER JOIN $tbl_course c
4682
                    ON (cu.c_id = c.id)
4683
                    INNER JOIN $tbl_access_rel_course a
4684
                    ON (a.c_id = c.id)
4685
                    WHERE
4686
                        cu.user_id = $user_id AND
4687
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4688
                        access_url_id = ".$urlId."
4689
                    ORDER BY title";
4690
        } else {
4691
            $sql = "SELECT c.code, title
4692
                    FROM $tbl_course_user u
4693
                    INNER JOIN $tbl_course c ON (c_id = c.id)
4694
                    WHERE
4695
                        u.user_id= $user_id AND
4696
                        relation_type<>".COURSE_RELATION_TYPE_RRHH."
4697
                    ORDER BY title";
4698
        }
4699
4700
        $rs = Database::query($sql);
4701
        $courses = $course_in_session = $temp_course_in_session = [];
4702
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4703
            $courses[$row['code']] = $row['title'];
4704
        }
4705
4706
        $orderBy = " ORDER BY name ";
4707
        $extraInnerJoin = null;
4708
4709
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4710
            $orderBy = " ORDER BY s.id, position ";
4711
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4712
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4713
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4714
        }
4715
4716
        $sessionCondition = '';
4717
        if (!empty($session_id)) {
4718
            $sessionCondition = " AND s.id = $session_id";
4719
        }
4720
4721
        // Get the list of sessions where the user is subscribed as student
4722
        if (api_is_multiple_url_enabled()) {
4723
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4724
                    FROM $tbl_session_course_user cu
4725
                    INNER JOIN $tbl_access_rel_session a
4726
                    ON (a.session_id = cu.session_id)
4727
                    INNER JOIN $tbl_session s
4728
                    ON (s.id = a.session_id)
4729
                    INNER JOIN $tbl_course c
4730
                    ON (c.id = cu.c_id)
4731
                    $extraInnerJoin
4732
                    WHERE
4733
                        cu.user_id = $user_id AND
4734
                        access_url_id = ".$urlId."
4735
                        $sessionCondition
4736
                    $orderBy ";
4737
        } else {
4738
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4739
                    FROM $tbl_session_course_user cu
4740
                    INNER JOIN $tbl_session s
4741
                    ON (s.id = cu.session_id)
4742
                    INNER JOIN $tbl_course c
4743
                    ON (c.id = cu.c_id)
4744
                    $extraInnerJoin
4745
                    WHERE
4746
                        cu.user_id = $user_id
4747
                        $sessionCondition
4748
                    $orderBy ";
4749
        }
4750
4751
        $rs = Database::query($sql);
4752
        $simple_session_array = [];
4753
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
4754
            $course_info = api_get_course_info($row['code']);
4755
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4756
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
4757
            $simple_session_array[$row['session_id']] = $row['name'];
4758
        }
4759
4760
        foreach ($simple_session_array as $my_session_id => $session_name) {
4761
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4762
            $my_course_data = [];
4763
            foreach ($course_list as $courseId => $course_data) {
4764
                $my_course_data[$courseId] = $course_data['title'];
4765
            }
4766
4767
            if (empty($session_id)) {
4768
                $my_course_data = utf8_sort($my_course_data);
4769
            }
4770
4771
            $final_course_data = [];
4772
            foreach ($my_course_data as $course_id => $value) {
4773
                if (isset($course_list[$course_id])) {
4774
                    $final_course_data[$course_id] = $course_list[$course_id];
4775
                }
4776
            }
4777
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4778
            $course_in_session[$my_session_id]['name'] = $session_name;
4779
        }
4780
4781
        $html = '';
4782
        // Course list
4783
        if ($show_courses) {
4784
            if (!empty($courses)) {
4785
                $html .= Display::page_subheader(
4786
                    Display::return_icon(
4787
                        'course.png',
4788
                        get_lang('MyCourses'),
4789
                        [],
4790
                        ICON_SIZE_SMALL
4791
                    ).' '.get_lang('MyCourses')
4792
                );
4793
4794
                $columns = [
4795
                    'course_title' => get_lang('Course'),
4796
                    'time_spent' => get_lang('TimeSpentInTheCourse'),
4797
                    'progress' => get_lang('Progress'),
4798
                    'best_score_in_lp' => get_lang('BestScoreInLearningPath'),
4799
                    'best_score_not_in_lp' => get_lang('BestScoreNotInLearningPath'),
4800
                    'latest_login' => get_lang('LastConnexion'),
4801
                    'details' => get_lang('Details'),
4802
                ];
4803
                $availableColumns = [];
4804
                if (isset($trackingColumns['my_progress_courses'])) {
4805
                    $availableColumns = $trackingColumns['my_progress_courses'];
4806
                }
4807
                $html .= '<div class="table-responsive">';
4808
                $html .= '<table class="table table-striped table-hover">';
4809
                $html .= '<thead><tr>';
4810
                foreach ($columns as $columnKey => $name) {
4811
                    if (!empty($availableColumns)) {
4812
                        if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
4813
                            continue;
4814
                        }
4815
                    }
4816
                    $html .= Display::tag('th', $name);
4817
                }
4818
                $html .= '</tr></thead><tbody>';
4819
4820
                foreach ($courses as $course_code => $course_title) {
4821
                    $courseInfo = api_get_course_info($course_code);
4822
                    $courseId = $courseInfo['real_id'];
4823
4824
                    $total_time_login = self::get_time_spent_on_the_course(
4825
                        $user_id,
4826
                        $courseId
4827
                    );
4828
                    $time = api_time_to_hms($total_time_login);
4829
                    $progress = self::get_avg_student_progress(
4830
                        $user_id,
4831
                        $course_code
4832
                    );
4833
                    $bestScore = self::get_avg_student_score(
4834
                        $user_id,
4835
                        $course_code,
4836
                        [],
4837
                        null,
4838
                        false,
4839
                        false,
4840
                        true
4841
                    );
4842
4843
                    $exerciseList = ExerciseLib::get_all_exercises(
4844
                        $courseInfo,
4845
                        0,
4846
                        false,
4847
                        null,
4848
                        false,
4849
                        1
4850
                    );
4851
4852
                    $bestScoreAverageNotInLP = 0;
4853
                    if (!empty($exerciseList)) {
4854
                        foreach ($exerciseList as $exerciseData) {
4855
                            $results = Event::get_best_exercise_results_by_user(
4856
                                $exerciseData['id'],
4857
                                $courseInfo['real_id'],
4858
                                0,
4859
                                $user_id
4860
                            );
4861
                            $best = 0;
4862
                            if (!empty($results)) {
4863
                                foreach ($results as $result) {
4864
                                    if (!empty($result['exe_weighting'])) {
4865
                                        $score = $result['exe_result'] / $result['exe_weighting'];
4866
                                        if ($score > $best) {
4867
                                            $best = $score;
4868
                                        }
4869
                                    }
4870
                                }
4871
                            }
4872
                            $bestScoreAverageNotInLP += $best;
4873
                        }
4874
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
4875
                    }
4876
4877
                    $last_connection = self::get_last_connection_date_on_the_course(
4878
                        $user_id,
4879
                        $courseInfo
4880
                    );
4881
4882
                    if (is_null($progress) || empty($progress)) {
4883
                        $progress = '0%';
4884
                    } else {
4885
                        $progress = $progress.'%';
4886
                    }
4887
4888
                    if (isset($_GET['course']) &&
4889
                        $course_code == $_GET['course'] &&
4890
                        empty($_GET['session_id'])
4891
                    ) {
4892
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D">';
4893
                    } else {
4894
                        $html .= '<tr class="row_even">';
4895
                    }
4896
                    $url = api_get_course_url($course_code, $session_id);
4897
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
4898
                    $bestScoreResult = '';
4899
                    if (empty($bestScore)) {
4900
                        $bestScoreResult = '-';
4901
                    } else {
4902
                        $bestScoreResult = $bestScore.'%';
4903
                    }
4904
                    $bestScoreNotInLP = '';
4905
                    if (empty($bestScoreAverageNotInLP)) {
4906
                        $bestScoreNotInLP = '-';
4907
                    } else {
4908
                        $bestScoreNotInLP = $bestScoreAverageNotInLP.'%';
4909
                    }
4910
4911
                    $detailsLink = '';
4912
                    if (isset($_GET['course']) &&
4913
                        $course_code == $_GET['course'] &&
4914
                        empty($_GET['session_id'])
4915
                    ) {
4916
                        $detailsLink .= '<a href="#course_session_header">';
4917
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4918
                        $detailsLink .= '</a>';
4919
                    } else {
4920
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_header">';
4921
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4922
                        $detailsLink .= '</a>';
4923
                    }
4924
4925
                    $result = [
4926
                        'course_title' => $course_url,
4927
                        'time_spent' => $time,
4928
                        'progress' => $progress,
4929
                        'best_score_in_lp' => $bestScoreResult,
4930
                        'best_score_not_in_lp' => $bestScoreNotInLP,
4931
                        'latest_login' => $last_connection,
4932
                        'details' => $detailsLink,
4933
                    ];
4934
4935
                    foreach ($result as $columnKey => $data) {
4936
                        if (!empty($availableColumns)) {
4937
                            if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
4938
                                continue;
4939
                            }
4940
                        }
4941
                        $html .= '<td>'.$data.'</td>';
0 ignored issues
show
Bug introduced by
Are you sure $data of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

4941
                        $html .= '<td>'./** @scrutinizer ignore-type */ $data.'</td>';
Loading history...
4942
                    }
4943
4944
                    $html .= '</tr>';
4945
                }
4946
                $html .= '</tbody></table>';
4947
                $html .= '</div>';
4948
            }
4949
        }
4950
4951
        // Session list
4952
        if (!empty($course_in_session)) {
4953
            $main_session_graph = '';
4954
            // Load graphics only when calling to an specific session
4955
            $all_exercise_graph_name_list = [];
4956
            $my_results = [];
4957
            $all_exercise_graph_list = [];
4958
            $all_exercise_start_time = [];
4959
            foreach ($course_in_session as $my_session_id => $session_data) {
4960
                $course_list = $session_data['course_list'];
4961
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
0 ignored issues
show
Bug introduced by
It seems like SessionManager::get_user...session($my_session_id) can also be of type integer; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

4961
                $user_count = count(/** @scrutinizer ignore-type */ SessionManager::get_users_by_session($my_session_id));
Loading history...
4962
                $exercise_graph_name_list = [];
4963
                $exercise_graph_list = [];
4964
4965
                foreach ($course_list as $course_data) {
4966
                    $exercise_list = ExerciseLib::get_all_exercises(
4967
                        $course_data,
4968
                        $my_session_id,
4969
                        false,
4970
                        null,
4971
                        false,
4972
                        1
4973
                    );
4974
4975
                    foreach ($exercise_list as $exercise_data) {
4976
                        $exercise_obj = new Exercise($course_data['real_id']);
4977
                        $exercise_obj->read($exercise_data['id']);
4978
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
4979
                        //$visible_return = $exercise_obj->is_visible();
4980
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
4981
                            $best_average = intval(
4982
                                ExerciseLib::get_best_average_score_by_exercise(
4983
                                    $exercise_data['id'],
4984
                                    $course_data['real_id'],
4985
                                    $my_session_id,
4986
                                    $user_count
4987
                                )
4988
                            );
4989
4990
                            $exercise_graph_list[] = $best_average;
4991
                            $all_exercise_graph_list[] = $best_average;
4992
4993
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
4994
                                api_get_user_id(),
4995
                                $exercise_data['id'],
4996
                                $course_data['real_id'],
4997
                                $my_session_id
4998
                            );
4999
5000
                            $score = 0;
5001
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
5002
                                $score = intval($user_result_data['exe_result'] / $user_result_data['exe_weighting'] * 100);
5003
                            }
5004
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
5005
                            $all_exercise_start_time[] = $time;
5006
                            $my_results[] = $score;
5007
                            if (count($exercise_list) <= 10) {
5008
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
5009
                                $exercise_graph_name_list[] = $title;
5010
                                $all_exercise_graph_name_list[] = $title;
5011
                            } else {
5012
                                // if there are more than 10 results, space becomes difficult to find,
5013
                                // so only show the title of the exercise, not the tool
5014
                                $title = cut($exercise_data['title'], 30);
5015
                                $exercise_graph_name_list[] = $title;
5016
                                $all_exercise_graph_name_list[] = $title;
5017
                            }
5018
                        }
5019
                    }
5020
                }
5021
            }
5022
5023
            // Complete graph
5024
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
5025
                asort($all_exercise_start_time);
5026
5027
                //Fix exams order
5028
                $final_all_exercise_graph_name_list = [];
5029
                $my_results_final = [];
5030
                $final_all_exercise_graph_list = [];
5031
5032
                foreach ($all_exercise_start_time as $key => $time) {
5033
                    $label_time = '';
5034
                    if (!empty($time)) {
5035
                        $label_time = date('d-m-y', $time);
5036
                    }
5037
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
5038
                    $my_results_final[] = $my_results[$key];
5039
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
5040
                }
5041
                $main_session_graph = self::generate_session_exercise_graph(
5042
                    $final_all_exercise_graph_name_list,
5043
                    $my_results_final,
5044
                    $final_all_exercise_graph_list
5045
                );
5046
            }
5047
5048
            $sessionIcon = Display::return_icon(
5049
                'session.png',
5050
                get_lang('Sessions'),
5051
                [],
5052
                ICON_SIZE_SMALL
5053
            );
5054
5055
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5056
            $html .= $anchor.Display::page_subheader(
5057
                $sessionIcon.' '.get_lang('Sessions')
5058
            );
5059
5060
            $html .= '<div class="table-responsive">';
5061
            $html .= '<table class="table table-striped table-hover">';
5062
            $html .= '<thead>';
5063
            $html .= '<tr>
5064
                  '.Display::tag('th', get_lang('Session'), ['width' => '300px']).'
5065
                  '.Display::tag('th', get_lang('PublishedExercises'), ['width' => '300px']).'
5066
                  '.Display::tag('th', get_lang('NewExercises')).'
5067
                  '.Display::tag('th', get_lang('AverageExerciseResult')).'
5068
                  '.Display::tag('th', get_lang('Details')).'
5069
                  </tr>';
5070
            $html .= '</thead>';
5071
            $html .= '<tbody>';
5072
5073
            foreach ($course_in_session as $my_session_id => $session_data) {
5074
                $course_list = $session_data['course_list'];
5075
                $session_name = $session_data['name'];
5076
                if ($showAllSessions == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
5077
                    if (isset($session_id) && !empty($session_id)) {
5078
                        if ($session_id != $my_session_id) {
5079
                            continue;
5080
                        }
5081
                    }
5082
                }
5083
5084
                $all_exercises = 0;
5085
                $all_unanswered_exercises_by_user = 0;
5086
                $all_average = 0;
5087
                $stats_array = [];
5088
5089
                foreach ($course_list as $course_data) {
5090
                    // All exercises in the course @todo change for a real count
5091
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5092
                    $count_exercises = 0;
5093
                    if (is_array($exercises) && !empty($exercises)) {
5094
                        $count_exercises = count($exercises);
5095
                    }
5096
5097
                    // Count of user results
5098
                    $done_exercises = null;
5099
                    $courseInfo = api_get_course_info($course_data['code']);
5100
5101
                    $answered_exercises = 0;
5102
                    if (!empty($exercises)) {
5103
                        foreach ($exercises as $exercise_item) {
5104
                            $attempts = Event::count_exercise_attempts_by_user(
5105
                                api_get_user_id(),
5106
                                $exercise_item['id'],
5107
                                $courseInfo['real_id'],
5108
                                $my_session_id
5109
                            );
5110
                            if ($attempts > 1) {
5111
                                $answered_exercises++;
5112
                            }
5113
                        }
5114
                    }
5115
5116
                    // Average
5117
                    $average = ExerciseLib::get_average_score_by_course(
5118
                        $courseInfo['real_id'],
5119
                        $my_session_id
5120
                    );
5121
                    $all_exercises += $count_exercises;
5122
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5123
                    $all_average += $average;
5124
                }
5125
5126
                if (!empty($course_list)) {
5127
                    $all_average = $all_average / count($course_list);
5128
                }
5129
5130
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5131
                    $html .= '<tr style="background-color:#FBF09D">';
5132
                } else {
5133
                    $html .= '<tr>';
5134
                }
5135
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5136
5137
                $html .= Display::tag('td', Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]));
5138
                $html .= Display::tag('td', $all_exercises);
5139
                $html .= Display::tag('td', $all_unanswered_exercises_by_user);
5140
                $html .= Display::tag('td', ExerciseLib::convert_to_percentage($all_average));
5141
5142
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5143
                    $icon = Display::url(
5144
                        Display::return_icon(
5145
                            '2rightarrow_na.png',
5146
                            get_lang('Details')
5147
                        ),
5148
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5149
                    );
5150
                } else {
5151
                    $icon = Display::url(
5152
                        Display::return_icon(
5153
                            '2rightarrow.png',
5154
                            get_lang('Details')
5155
                        ),
5156
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5157
                    );
5158
                }
5159
                $html .= Display::tag('td', $icon);
5160
                $html .= '</tr>';
5161
            }
5162
            $html .= '</tbody>';
5163
            $html .= '</table></div><br />';
5164
            $html .= Display::div(
5165
                $main_session_graph,
5166
                [
5167
                    'id' => 'session_graph',
5168
                    'class' => 'chart-session',
5169
                    'style' => 'position:relative; text-align: center;',
5170
                ]
5171
            );
5172
5173
            // Checking selected session.
5174
            if (isset($_GET['session_id'])) {
5175
                $session_id_from_get = intval($_GET['session_id']);
5176
                $session_data = $course_in_session[$session_id_from_get];
5177
                $course_list = $session_data['course_list'];
5178
5179
                $html .= '<a name= "course_session_list"></a>';
5180
                $html .= Display::tag('h3', $session_data['name'].' - '.get_lang('CourseList'));
5181
5182
                $html .= '<div class="table-responsive">';
5183
                $html .= '<table class="table table-hover table-striped">';
5184
5185
                $columnHeaders = [
5186
                    'course_title' => [
5187
                        get_lang('Course'),
5188
                        ['width' => '300px'],
5189
                    ],
5190
                    'published_exercises' => [
5191
                        get_lang('PublishedExercises'),
5192
                    ],
5193
                    'new_exercises' => [
5194
                        get_lang('NewExercises'),
5195
                    ],
5196
                    'my_average' => [
5197
                        get_lang('MyAverage'),
5198
                    ],
5199
                    'average_exercise_result' => [
5200
                        get_lang('AverageExerciseResult'),
5201
                    ],
5202
                    'time_spent' => [
5203
                        get_lang('TimeSpentInTheCourse'),
5204
                    ],
5205
                    'lp_progress' => [
5206
                        get_lang('LPProgress'),
5207
                    ],
5208
                    'score' => [
5209
                        get_lang('Score').
5210
                        Display::return_icon(
5211
                            'info3.gif',
5212
                            get_lang('ScormAndLPTestTotalAverage'),
5213
                            ['align' => 'absmiddle', 'hspace' => '3px']
5214
                        ),
5215
                    ],
5216
                    'best_score' => [
5217
                        get_lang('BestScore'),
5218
                    ],
5219
                    'last_connection' => [
5220
                        get_lang('LastConnexion'),
5221
                    ],
5222
                    'details' => [
5223
                        get_lang('Details'),
5224
                    ],
5225
                ];
5226
5227
                $html .= '<thead><tr>';
5228
                foreach ($columnHeaders as $key => $columnSetting) {
5229
                    if (isset($trackingColumns['course_session']) &&
5230
                        in_array($key, $trackingColumns['course_session']) &&
5231
                        $trackingColumns['course_session'][$key]
5232
                    ) {
5233
                        $settings = isset($columnSetting[1]) ? $columnSetting[1] : [];
5234
                        $html .= Display::tag(
5235
                             'th',
5236
                             $columnSetting[0],
5237
                             $settings
5238
                         );
5239
                    }
5240
                }
5241
5242
                $html .= '</tr>
5243
                    </thead>
5244
                    <tbody>';
5245
5246
                foreach ($course_list as $course_data) {
5247
                    $course_code = $course_data['code'];
5248
                    $course_title = $course_data['title'];
5249
                    $courseId = $course_data['real_id'];
5250
5251
                    // All exercises in the course @todo change for a real count
5252
                    $exercises = ExerciseLib::get_all_exercises(
5253
                        $course_data,
5254
                        $session_id_from_get
5255
                    );
5256
                    $count_exercises = 0;
5257
                    if (!empty($exercises)) {
5258
                        $count_exercises = count($exercises);
5259
                    }
5260
                    $answered_exercises = 0;
5261
                    foreach ($exercises as $exercise_item) {
5262
                        $attempts = Event::count_exercise_attempts_by_user(
5263
                            api_get_user_id(),
5264
                            $exercise_item['id'],
5265
                            $courseId,
5266
                            $session_id_from_get
5267
                        );
5268
                        if ($attempts > 1) {
5269
                            $answered_exercises++;
5270
                        }
5271
                    }
5272
5273
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5274
5275
                    // Average
5276
                    $average = ExerciseLib::get_average_score_by_course(
5277
                        $courseId,
5278
                        $session_id_from_get
5279
                    );
5280
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5281
                        api_get_user_id(),
5282
                        $courseId,
5283
                        $session_id_from_get
5284
                    );
5285
5286
                    $bestScore = self::get_avg_student_score(
5287
                        $user_id,
5288
                        $course_code,
5289
                        [],
5290
                        $session_id_from_get,
5291
                        false,
5292
                        false,
5293
                        true
5294
                    );
5295
5296
                    $stats_array[$course_code] = [
5297
                        'exercises' => $count_exercises,
5298
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5299
                        'done_exercises' => $done_exercises,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $done_exercises does not seem to be defined for all execution paths leading up to this point.
Loading history...
5300
                        'average' => $average,
5301
                        'my_average' => $my_average,
5302
                        'best_score' => $bestScore,
5303
                    ];
5304
5305
                    $last_connection = self::get_last_connection_date_on_the_course(
5306
                        $user_id,
5307
                        $course_data,
5308
                        $session_id_from_get
5309
                    );
5310
5311
                    $progress = self::get_avg_student_progress(
5312
                        $user_id,
5313
                        $course_code,
5314
                        [],
5315
                        $session_id_from_get
5316
                    );
5317
5318
                    $total_time_login = self::get_time_spent_on_the_course(
5319
                        $user_id,
5320
                        $courseId,
5321
                        $session_id_from_get
5322
                    );
5323
                    $time = api_time_to_hms($total_time_login);
5324
5325
                    $percentage_score = self::get_avg_student_score(
5326
                        $user_id,
5327
                        $course_code,
5328
                        [],
5329
                        $session_id_from_get
5330
                    );
5331
                    $courseCodeFromGet = isset($_GET['course']) ? $_GET['course'] : null;
5332
5333
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
5334
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D" >';
5335
                    } else {
5336
                        $html .= '<tr class="row_even">';
5337
                    }
5338
5339
                    $url = api_get_course_url($course_code, $session_id_from_get);
5340
                    $course_url = Display::url(
5341
                        $course_title,
5342
                        $url,
5343
                        ['target' => SESSION_LINK_TARGET]
5344
                    );
5345
5346
                    if (is_numeric($progress)) {
5347
                        $progress = $progress.'%';
5348
                    } else {
5349
                        $progress = '0%';
5350
                    }
5351
                    if (is_numeric($percentage_score)) {
5352
                        $percentage_score = $percentage_score.'%';
5353
                    } else {
5354
                        $percentage_score = '0%';
5355
                    }
5356
5357
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5358
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5359
                    } else {
5360
                        $bestScore = '-';
5361
                    }
5362
5363
                    if (empty($last_connection) || is_bool($last_connection)) {
5364
                        $last_connection = '';
5365
                    }
5366
5367
                    if ($course_code == $courseCodeFromGet &&
5368
                        $_GET['session_id'] == $session_id_from_get
5369
                    ) {
5370
                        $details = Display::url(
5371
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
5372
                        '#course_session_data'
5373
                        );
5374
                    } else {
5375
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5376
                        $details = Display::url(
5377
                            Display::return_icon(
5378
                                '2rightarrow.png',
5379
                                get_lang('Details')
5380
                            ),
5381
                            $url
5382
                        );
5383
                    }
5384
                    $details .= '</a>';
5385
5386
                    $data = [
5387
                        'course_title' => $course_url,
5388
                        'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5389
                        'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5390
                        'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5391
                        'average_exercise_result' => $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5392
                        'time_spent' => $time,
5393
                        'lp_progress' => $progress,
5394
                        'score' => $percentage_score,
5395
                        'best_score' => $bestScore,
5396
                        'last_connection' => $last_connection,
5397
                        'details' => $details,
5398
                    ];
5399
5400
                    foreach ($data as $key => $value) {
5401
                        if (in_array($key, $trackingColumns['course_session'])
5402
                            && $trackingColumns['course_session'][$key]
5403
                        ) {
5404
                            $html .= Display::tag('td', $value);
5405
                        }
5406
                    }
5407
                    $html .= '</tr>';
5408
                }
5409
                $html .= '</tbody></table></div>';
5410
            }
5411
        }
5412
5413
        return $html;
5414
    }
5415
5416
    /**
5417
     * Shows the user detail progress (when clicking in the details link).
5418
     *
5419
     * @param int    $user_id
5420
     * @param string $course_code
5421
     * @param int    $session_id
5422
     *
5423
     * @return string html code
5424
     */
5425
    public static function show_course_detail($user_id, $course_code, $session_id)
5426
    {
5427
        $html = '';
5428
        if (isset($course_code)) {
5429
            $user_id = intval($user_id);
5430
            $session_id = intval($session_id);
5431
            $course = Database::escape_string($course_code);
5432
            $course_info = api_get_course_info($course);
5433
            if (empty($course_info)) {
5434
                return '';
5435
            }
5436
5437
            $html .= '<a name="course_session_data"></a>';
5438
            $html .= Display::page_subheader($course_info['title']);
5439
            $html .= '<div class="table-responsive">';
5440
            $html .= '<table class="table table-striped table-hover">';
5441
5442
            //Course details
5443
            $html .= '
5444
                <thead>
5445
                <tr>
5446
                <th>'.get_lang('Exercises').'</th>
5447
                <th>'.get_lang('Attempts').'</th>
5448
                <th>'.get_lang('BestAttempt').'</th>
5449
                <th>'.get_lang('Ranking').'</th>
5450
                <th>'.get_lang('BestResultInCourse').'</th>
5451
                <th>'.get_lang('Statistics').' '.Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent'), ['align' => 'absmiddle', 'hspace' => '3px']).'</th>
5452
                </tr>
5453
                </thead>
5454
                <tbody>';
5455
5456
            if (empty($session_id)) {
5457
                $user_list = CourseManager::get_user_list_from_course_code(
5458
                    $course,
5459
                    $session_id,
5460
                    null,
5461
                    null,
5462
                    STUDENT
5463
                );
5464
            } else {
5465
                $user_list = CourseManager::get_user_list_from_course_code(
5466
                    $course,
5467
                    $session_id,
5468
                    null,
5469
                    null,
5470
                    0
5471
                );
5472
            }
5473
5474
            // Show exercise results of invisible exercises? see BT#4091
5475
            $exercise_list = ExerciseLib::get_all_exercises(
5476
                $course_info,
5477
                $session_id,
5478
                false,
5479
                null,
5480
                false,
5481
                2
5482
            );
5483
5484
            $to_graph_exercise_result = [];
5485
            if (!empty($exercise_list)) {
5486
                $score = $weighting = $exe_id = 0;
5487
                foreach ($exercise_list as $exercices) {
5488
                    $exercise_obj = new Exercise($course_info['real_id']);
5489
                    $exercise_obj->read($exercices['id']);
5490
                    $visible_return = $exercise_obj->is_visible();
5491
                    $score = $weighting = $attempts = 0;
5492
5493
                    // Getting count of attempts by user
5494
                    $attempts = Event::count_exercise_attempts_by_user(
5495
                        api_get_user_id(),
5496
                        $exercices['id'],
5497
                        $course_info['real_id'],
5498
                        $session_id
5499
                    );
5500
5501
                    $html .= '<tr class="row_even">';
5502
                    $url = api_get_path(WEB_CODE_PATH)."exercise/overview.php?cidReq={$course_info['code']}&id_session=$session_id&exerciseId={$exercices['id']}";
5503
5504
                    if ($visible_return['value'] == true) {
5505
                        $exercices['title'] = Display::url(
5506
                            $exercices['title'],
5507
                            $url,
5508
                            ['target' => SESSION_LINK_TARGET]
5509
                        );
5510
                    } elseif ($exercices['active'] == -1) {
5511
                        $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
5512
                    }
5513
5514
                    $html .= Display::tag('td', $exercices['title']);
5515
5516
                    // Exercise configuration show results or show only score
5517
                    if ($exercices['results_disabled'] == 0 || $exercices['results_disabled'] == 2) {
5518
                        //For graphics
5519
                        $best_exercise_stats = Event::get_best_exercise_results_by_user(
5520
                            $exercices['id'],
5521
                            $course_info['real_id'],
5522
                            $session_id
5523
                        );
5524
5525
                        $to_graph_exercise_result[$exercices['id']] = [
5526
                            'title' => $exercices['title'],
5527
                            'data' => $best_exercise_stats,
5528
                        ];
5529
5530
                        $latest_attempt_url = '';
5531
                        $best_score = $position = $percentage_score_result = '-';
5532
                        $graph = $normal_graph = null;
5533
5534
                        // Getting best results
5535
                        $best_score_data = ExerciseLib::get_best_attempt_in_course(
5536
                            $exercices['id'],
5537
                            $course_info['real_id'],
5538
                            $session_id
5539
                        );
5540
5541
                        $best_score = '';
5542
                        if (!empty($best_score_data)) {
5543
                            $best_score = ExerciseLib::show_score(
5544
                                $best_score_data['exe_result'],
5545
                                $best_score_data['exe_weighting']
5546
                            );
5547
                        }
5548
5549
                        if ($attempts > 0) {
5550
                            $exercise_stat = ExerciseLib::get_best_attempt_by_user(
5551
                                api_get_user_id(),
5552
                                $exercices['id'],
5553
                                $course_info['real_id'],
5554
                                $session_id
5555
                            );
5556
                            if (!empty($exercise_stat)) {
5557
                                // Always getting the BEST attempt
5558
                                $score = $exercise_stat['exe_result'];
5559
                                $weighting = $exercise_stat['exe_weighting'];
5560
                                $exe_id = $exercise_stat['exe_id'];
5561
5562
                                $latest_attempt_url .= api_get_path(WEB_CODE_PATH).'exercise/result.php?id='.$exe_id.'&cidReq='.$course_info['code'].'&show_headers=1&id_session='.$session_id;
5563
                                $percentage_score_result = Display::url(
5564
                                    ExerciseLib::show_score($score, $weighting),
5565
                                    $latest_attempt_url
5566
                                );
5567
                                $my_score = 0;
5568
                                if (!empty($weighting) && intval($weighting) != 0) {
5569
                                    $my_score = $score / $weighting;
5570
                                }
5571
                                //@todo this function slows the page
5572
                                if (is_int($user_list)) {
5573
                                    $user_list = [$user_list];
5574
                                }
5575
                                $position = ExerciseLib::get_exercise_result_ranking(
5576
                                    $my_score,
5577
                                    $exe_id,
5578
                                    $exercices['id'],
5579
                                    $course_info['code'],
5580
                                    $session_id,
5581
                                    $user_list
5582
                                );
5583
5584
                                $graph = self::generate_exercise_result_thumbnail_graph(
5585
                                    $to_graph_exercise_result[$exercices['id']]
5586
                                );
5587
                                $normal_graph = self::generate_exercise_result_graph(
5588
                                    $to_graph_exercise_result[$exercices['id']]
5589
                                );
5590
                            }
5591
                        }
5592
                        $html .= Display::div(
5593
                            $normal_graph,
5594
                            [
5595
                                'id' => 'main_graph_'.$exercices['id'],
5596
                                'class' => 'dialog',
5597
                                'style' => 'display:none',
5598
                            ]
5599
                        );
5600
5601
                        if (empty($graph)) {
5602
                            $graph = '-';
5603
                        } else {
5604
                            $graph = Display::url(
5605
                                '<img src="'.$graph.'" >',
5606
                                $normal_graph,
5607
                                [
5608
                                    'id' => $exercices['id'],
5609
                                    'class' => 'expand-image',
5610
                                ]
5611
                            );
5612
                        }
5613
5614
                        $html .= Display::tag('td', $attempts);
5615
                        $html .= Display::tag('td', $percentage_score_result);
5616
                        $html .= Display::tag('td', $position);
5617
                        $html .= Display::tag('td', $best_score);
5618
                        $html .= Display::tag('td', $graph);
5619
                    } else {
5620
                        // Exercise configuration NO results
5621
                        $html .= Display::tag('td', $attempts);
5622
                        $html .= Display::tag('td', '-');
5623
                        $html .= Display::tag('td', '-');
5624
                        $html .= Display::tag('td', '-');
5625
                        $html .= Display::tag('td', '-');
5626
                    }
5627
                    $html .= '</tr>';
5628
                }
5629
            } else {
5630
                $html .= '<tr><td colspan="5">'.get_lang('NoEx').'</td></tr>';
5631
            }
5632
            $html .= '</tbody></table></div>';
5633
5634
            $columnHeaders = [
5635
                'lp' => get_lang('LearningPath'),
5636
                'time' => get_lang('LatencyTimeSpent'),
5637
                'progress' => get_lang('Progress'),
5638
                'score' => get_lang('Score'),
5639
                'best_score' => get_lang('BestScore'),
5640
                'last_connection' => get_lang('LastConnexion'),
5641
            ];
5642
5643
            $headers = '';
5644
            $trackingColumns = api_get_configuration_value('tracking_columns');
5645
            if (isset($trackingColumns['my_progress_lp'])) {
5646
                foreach ($columnHeaders as $key => $value) {
5647
                    if (!isset($trackingColumns['my_progress_lp'][$key]) ||
5648
                        $trackingColumns['my_progress_lp'][$key] == false
5649
                    ) {
5650
                        unset($columnHeaders[$key]);
5651
                    }
5652
                }
5653
            }
5654
5655
            $columnHeadersKeys = array_keys($columnHeaders);
5656
            foreach ($columnHeaders as $key => $columnName) {
5657
                $headers .= Display::tag(
5658
                    'th',
5659
                    $columnName
5660
                );
5661
            }
5662
5663
            // LP table results
5664
            $html .= '<div class="table-responsive">';
5665
            $html .= '<table class="table table-striped table-hover">';
5666
            $html .= '<thead><tr>';
5667
            $html .= $headers;
5668
            $html .= '</tr></thead><tbody>';
5669
5670
            $list = new LearnpathList(
5671
                api_get_user_id(),
5672
                $course_info['code'],
5673
                $session_id,
5674
                'lp.publicatedOn ASC',
5675
                true,
5676
                null,
5677
                true
5678
            );
5679
5680
            $lp_list = $list->get_flat_list();
5681
5682
            if (!empty($lp_list) > 0) {
5683
                foreach ($lp_list as $lp_id => $learnpath) {
5684
                    $progress = self::get_avg_student_progress(
5685
                        $user_id,
5686
                        $course,
5687
                        [$lp_id],
5688
                        $session_id
5689
                    );
5690
                    $last_connection_in_lp = self::get_last_connection_time_in_lp(
5691
                        $user_id,
5692
                        $course,
5693
                        $lp_id,
5694
                        $session_id
5695
                    );
5696
5697
                    $time_spent_in_lp = self::get_time_spent_in_lp(
5698
                        $user_id,
5699
                        $course,
5700
                        [$lp_id],
5701
                        $session_id
5702
                    );
5703
                    $percentage_score = self::get_avg_student_score(
5704
                        $user_id,
5705
                        $course,
5706
                        [$lp_id],
5707
                        $session_id
5708
                    );
5709
5710
                    $bestScore = self::get_avg_student_score(
5711
                        $user_id,
5712
                        $course,
5713
                        [$lp_id],
5714
                        $session_id,
5715
                        false,
5716
                        false,
5717
                        true
5718
                    );
5719
5720
                    if (is_numeric($progress)) {
5721
                        $progress = $progress.'%';
5722
                    }
5723
                    if (is_numeric($percentage_score)) {
5724
                        $percentage_score = $percentage_score.'%';
5725
                    } else {
5726
                        $percentage_score = '0%';
5727
                    }
5728
5729
                    if (is_numeric($bestScore)) {
5730
                        $bestScore = $bestScore.'%';
5731
                    } else {
5732
                        $bestScore = '-';
5733
                    }
5734
5735
                    $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
5736
                    $last_connection = '-';
5737
                    if (!empty($last_connection_in_lp)) {
5738
                        $last_connection = api_convert_and_format_date(
5739
                            $last_connection_in_lp,
5740
                            DATE_TIME_FORMAT_LONG
5741
                        );
5742
                    }
5743
5744
                    $url = api_get_path(WEB_CODE_PATH)."lp/lp_controller.php?cidReq={$course_code}&id_session=$session_id&lp_id=$lp_id&action=view";
5745
                    $html .= '<tr class="row_even">';
5746
5747
                    if (in_array('lp', $columnHeadersKeys)) {
5748
                        if ($learnpath['lp_visibility'] == 0) {
5749
                            $html .= Display::tag('td', $learnpath['lp_name']);
5750
                        } else {
5751
                            $html .= Display::tag(
5752
                                'td',
5753
                                Display::url(
5754
                                    $learnpath['lp_name'],
5755
                                    $url,
5756
                                    ['target' => SESSION_LINK_TARGET]
5757
                                )
5758
                            );
5759
                        }
5760
                    }
5761
5762
                    if (in_array('time', $columnHeadersKeys)) {
5763
                        $html .= Display::tag(
5764
                            'td',
5765
                            $time_spent_in_lp
5766
                        );
5767
                    }
5768
5769
                    if (in_array('progress', $columnHeadersKeys)) {
5770
                        $html .= Display::tag(
5771
                            'td',
5772
                            $progress
5773
                        );
5774
                    }
5775
5776
                    if (in_array('score', $columnHeadersKeys)) {
5777
                        $html .= Display::tag('td', $percentage_score);
5778
                    }
5779
                    if (in_array('best_score', $columnHeadersKeys)) {
5780
                        $html .= Display::tag('td', $bestScore);
5781
                    }
5782
5783
                    if (in_array('last_connection', $columnHeadersKeys)) {
5784
                        $html .= Display::tag('td', $last_connection, ['width' => '180px']);
5785
                    }
5786
                    $html .= '</tr>';
5787
                }
5788
            } else {
5789
                $html .= '<tr>
5790
                        <td colspan="4" align="center">
5791
                            '.get_lang('NoLearnpath').'
5792
                        </td>
5793
                      </tr>';
5794
            }
5795
            $html .= '</tbody></table></div>';
5796
5797
            $html .= self::displayUserSkills($user_id, $course_info['id'], $session_id);
5798
        }
5799
5800
        return $html;
5801
    }
5802
5803
    /**
5804
     * Generates an histogram.
5805
     *
5806
     * @param array $names      list of exercise names
5807
     * @param array $my_results my results 0 to 100
5808
     * @param array $average    average scores 0-100
5809
     *
5810
     * @return string
5811
     */
5812
    public static function generate_session_exercise_graph($names, $my_results, $average)
5813
    {
5814
        /* Create and populate the pData object */
5815
        $myData = new pData();
5816
        $myData->addPoints($names, 'Labels');
5817
        $myData->addPoints($my_results, 'Serie1');
5818
        $myData->addPoints($average, 'Serie2');
5819
        $myData->setSerieWeight('Serie1', 1);
5820
        $myData->setSerieTicks('Serie2', 4);
5821
        $myData->setSerieDescription('Labels', 'Months');
5822
        $myData->setAbscissa('Labels');
5823
        $myData->setSerieDescription('Serie1', get_lang('MyResults'));
5824
        $myData->setSerieDescription('Serie2', get_lang('AverageScore'));
5825
        $myData->setAxisUnit(0, '%');
5826
        $myData->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5827
        // Cache definition
5828
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5829
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5830
        $chartHash = $myCache->getHash($myData);
5831
5832
        if ($myCache->isInCache($chartHash)) {
5833
            //if we already created the img
5834
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5835
            $myCache->saveFromCache($chartHash, $imgPath);
5836
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5837
        } else {
5838
            /* Define width, height and angle */
5839
            $mainWidth = 860;
5840
            $mainHeight = 500;
5841
            $angle = 50;
5842
5843
            /* Create the pChart object */
5844
            $myPicture = new pImage($mainWidth, $mainHeight, $myData);
5845
5846
            /* Turn of Antialiasing */
5847
            $myPicture->Antialias = false;
5848
5849
            /* Draw the background */
5850
            $settings = ['R' => 255, 'G' => 255, 'B' => 255];
5851
            $myPicture->drawFilledRectangle(0, 0, $mainWidth, $mainHeight, $settings);
5852
5853
            /* Add a border to the picture */
5854
            $myPicture->drawRectangle(
5855
                0,
5856
                0,
5857
                $mainWidth - 1,
5858
                $mainHeight - 1,
5859
                ['R' => 0, 'G' => 0, 'B' => 0]
5860
            );
5861
5862
            /* Set the default font */
5863
            $myPicture->setFontProperties(
5864
                [
5865
                    'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
5866
                    'FontSize' => 10, ]
5867
            );
5868
            /* Write the chart title */
5869
            $myPicture->drawText(
5870
                $mainWidth / 2,
5871
                30,
5872
                get_lang('ExercisesInTimeProgressChart'),
5873
                [
5874
                    'FontSize' => 12,
5875
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
5876
                ]
5877
            );
5878
5879
            /* Set the default font */
5880
            $myPicture->setFontProperties(
5881
                [
5882
                    'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
5883
                    'FontSize' => 6,
5884
                ]
5885
            );
5886
5887
            /* Define the chart area */
5888
            $myPicture->setGraphArea(60, 60, $mainWidth - 60, $mainHeight - 150);
5889
5890
            /* Draw the scale */
5891
            $scaleSettings = [
5892
                'XMargin' => 10,
5893
                'YMargin' => 10,
5894
                'Floating' => true,
5895
                'GridR' => 200,
5896
                'GridG' => 200,
5897
                'GridB' => 200,
5898
                'DrawSubTicks' => true,
5899
                'CycleBackground' => true,
5900
                'LabelRotation' => $angle,
5901
                'Mode' => SCALE_MODE_ADDALL_START0,
5902
            ];
5903
            $myPicture->drawScale($scaleSettings);
5904
5905
            /* Turn on Antialiasing */
5906
            $myPicture->Antialias = true;
5907
5908
            /* Enable shadow computing */
5909
            $myPicture->setShadow(
5910
                true,
5911
                [
5912
                    'X' => 1,
5913
                    'Y' => 1,
5914
                    'R' => 0,
5915
                    'G' => 0,
5916
                    'B' => 0,
5917
                    'Alpha' => 10,
5918
                ]
5919
            );
5920
5921
            /* Draw the line chart */
5922
            $myPicture->setFontProperties(
5923
                [
5924
                    'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
5925
                    'FontSize' => 10,
5926
                ]
5927
            );
5928
            $myPicture->drawSplineChart();
5929
            $myPicture->drawPlotChart(
5930
                [
5931
                    'DisplayValues' => true,
5932
                    'PlotBorder' => true,
5933
                    'BorderSize' => 1,
5934
                    'Surrounding' => -60,
5935
                    'BorderAlpha' => 80,
5936
                ]
5937
            );
5938
5939
            /* Write the chart legend */
5940
            $myPicture->drawLegend(
5941
                $mainWidth / 2 + 50,
5942
                50,
5943
                [
5944
                    'Style' => LEGEND_BOX,
5945
                    'Mode' => LEGEND_HORIZONTAL,
5946
                    'FontR' => 0,
5947
                    'FontG' => 0,
5948
                    'FontB' => 0,
5949
                    'R' => 220,
5950
                    'G' => 220,
5951
                    'B' => 220,
5952
                    'Alpha' => 100,
5953
                ]
5954
            );
5955
5956
            $myCache->writeToCache($chartHash, $myPicture);
5957
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5958
            $myCache->saveFromCache($chartHash, $imgPath);
5959
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5960
        }
5961
5962
        $html = '<img src="'.$imgPath.'">';
5963
5964
        return $html;
5965
    }
5966
5967
    /**
5968
     * Returns a thumbnail of the function generate_exercise_result_graph.
5969
     *
5970
     * @param array $attempts
5971
     */
5972
    public static function generate_exercise_result_thumbnail_graph($attempts)
5973
    {
5974
        //$exercise_title = $attempts['title'];
5975
        $attempts = $attempts['data'];
5976
        $my_exercise_result_array = $exercise_result = [];
5977
        if (empty($attempts)) {
5978
            return null;
5979
        }
5980
5981
        foreach ($attempts as $attempt) {
5982
            if (api_get_user_id() == $attempt['exe_user_id']) {
5983
                if ($attempt['exe_weighting'] != 0) {
5984
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5985
                }
5986
            } else {
5987
                if ($attempt['exe_weighting'] != 0) {
5988
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
5989
                }
5990
            }
5991
        }
5992
5993
        //Getting best result
5994
        rsort($my_exercise_result_array);
5995
        $my_exercise_result = 0;
5996
        if (isset($my_exercise_result_array[0])) {
5997
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5998
        }
5999
6000
        $max = 100;
6001
        $pieces = 5;
6002
        $part = round($max / $pieces);
6003
        $x_axis = [];
6004
        $final_array = [];
6005
        $my_final_array = [];
6006
6007
        for ($i = 1; $i <= $pieces; $i++) {
6008
            $sum = 1;
6009
            if ($i == 1) {
6010
                $sum = 0;
6011
            }
6012
            $min = ($i - 1) * $part + $sum;
6013
            $max = ($i) * $part;
6014
            $x_axis[] = $min." - ".$max;
6015
            $count = 0;
6016
            foreach ($exercise_result as $result) {
6017
                $percentage = $result * 100;
6018
                //echo $percentage.' - '.$min.' - '.$max."<br />";
6019
                if ($percentage >= $min && $percentage <= $max) {
6020
                    //echo ' is > ';
6021
                    $count++;
6022
                }
6023
            }
6024
            //echo '<br />';
6025
            $final_array[] = $count;
6026
6027
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6028
                $my_final_array[] = 1;
6029
            } else {
6030
                $my_final_array[] = 0;
6031
            }
6032
        }
6033
6034
        //Fix to remove the data of the user with my data
6035
        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...
6036
            if (!empty($my_final_array[$i])) {
6037
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6038
                $final_array[$i] = 0;
6039
            }
6040
        }
6041
6042
        // Dataset definition
6043
        $dataSet = new pData();
6044
        $dataSet->addPoints($final_array, 'Serie1');
6045
        $dataSet->addPoints($my_final_array, 'Serie2');
6046
        $dataSet->normalize(100, "%");
6047
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6048
6049
        // Cache definition
6050
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6051
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6052
        $chartHash = $myCache->getHash($dataSet);
6053
        if ($myCache->isInCache($chartHash)) {
6054
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6055
            $myCache->saveFromCache($chartHash, $imgPath);
6056
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6057
        } else {
6058
            /* Create the pChart object */
6059
            $widthSize = 80;
6060
            $heightSize = 35;
6061
            $fontSize = 2;
6062
6063
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6064
6065
            /* Turn of Antialiasing */
6066
            $myPicture->Antialias = false;
6067
6068
            /* Add a border to the picture */
6069
            $myPicture->drawRectangle(
6070
                0,
6071
                0,
6072
                $widthSize - 1,
6073
                $heightSize - 1,
6074
                ['R' => 0, 'G' => 0, 'B' => 0]
6075
            );
6076
6077
            /* Set the default font */
6078
            $myPicture->setFontProperties(
6079
                [
6080
                    'FontName' => api_get_path(
6081
                            SYS_FONTS_PATH
6082
                        ).'opensans/OpenSans-Regular.ttf',
6083
                    'FontSize' => $fontSize,
6084
                ]
6085
            );
6086
6087
            /* Do not write the chart title */
6088
6089
            /* Define the chart area */
6090
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
6091
6092
            /* Draw the scale */
6093
            $scaleSettings = [
6094
                'GridR' => 200,
6095
                'GridG' => 200,
6096
                'GridB' => 200,
6097
                'DrawSubTicks' => true,
6098
                'CycleBackground' => true,
6099
                'Mode' => SCALE_MODE_MANUAL,
6100
                'ManualScale' => [
6101
                    '0' => [
6102
                        'Min' => 0,
6103
                        'Max' => 100,
6104
                    ],
6105
                ],
6106
            ];
6107
            $myPicture->drawScale($scaleSettings);
6108
6109
            /* Turn on shadow computing */
6110
            $myPicture->setShadow(
6111
                true,
6112
                [
6113
                    'X' => 1,
6114
                    'Y' => 1,
6115
                    'R' => 0,
6116
                    'G' => 0,
6117
                    'B' => 0,
6118
                    'Alpha' => 10,
6119
                ]
6120
            );
6121
6122
            /* Draw the chart */
6123
            $myPicture->setShadow(
6124
                true,
6125
                [
6126
                    'X' => 1,
6127
                    'Y' => 1,
6128
                    'R' => 0,
6129
                    'G' => 0,
6130
                    'B' => 0,
6131
                    'Alpha' => 10,
6132
                ]
6133
            );
6134
            $settings = [
6135
                'DisplayValues' => true,
6136
                'DisplaySize' => $fontSize,
6137
                'DisplayR' => 0,
6138
                'DisplayG' => 0,
6139
                'DisplayB' => 0,
6140
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6141
                'Gradient' => false,
6142
                'Surrounding' => 5,
6143
                'InnerSurrounding' => 5,
6144
            ];
6145
            $myPicture->drawStackedBarChart($settings);
6146
6147
            /* Save and write in cache */
6148
            $myCache->writeToCache($chartHash, $myPicture);
6149
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6150
            $myCache->saveFromCache($chartHash, $imgPath);
6151
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6152
        }
6153
6154
        return $imgPath;
6155
    }
6156
6157
    /**
6158
     * Generates a big graph with the number of best results.
6159
     *
6160
     * @param	array
6161
     */
6162
    public static function generate_exercise_result_graph($attempts)
6163
    {
6164
        $exercise_title = strip_tags($attempts['title']);
6165
        $attempts = $attempts['data'];
6166
        $my_exercise_result_array = $exercise_result = [];
6167
        if (empty($attempts)) {
6168
            return null;
6169
        }
6170
        foreach ($attempts as $attempt) {
6171
            if (api_get_user_id() == $attempt['exe_user_id']) {
6172
                if ($attempt['exe_weighting'] != 0) {
6173
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6174
                }
6175
            } else {
6176
                if ($attempt['exe_weighting'] != 0) {
6177
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6178
                }
6179
            }
6180
        }
6181
6182
        //Getting best result
6183
        rsort($my_exercise_result_array);
6184
        $my_exercise_result = 0;
6185
        if (isset($my_exercise_result_array[0])) {
6186
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6187
        }
6188
6189
        $max = 100;
6190
        $pieces = 5;
6191
        $part = round($max / $pieces);
6192
        $x_axis = [];
6193
        $final_array = [];
6194
        $my_final_array = [];
6195
6196
        for ($i = 1; $i <= $pieces; $i++) {
6197
            $sum = 1;
6198
            if ($i == 1) {
6199
                $sum = 0;
6200
            }
6201
            $min = ($i - 1) * $part + $sum;
6202
            $max = ($i) * $part;
6203
            $x_axis[] = $min." - ".$max;
6204
            $count = 0;
6205
            foreach ($exercise_result as $result) {
6206
                $percentage = $result * 100;
6207
                if ($percentage >= $min && $percentage <= $max) {
6208
                    $count++;
6209
                }
6210
            }
6211
            $final_array[] = $count;
6212
6213
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6214
                $my_final_array[] = 1;
6215
            } else {
6216
                $my_final_array[] = 0;
6217
            }
6218
        }
6219
6220
        //Fix to remove the data of the user with my data
6221
6222
        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...
6223
            if (!empty($my_final_array[$i])) {
6224
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6225
                $final_array[$i] = 0;
6226
            }
6227
        }
6228
6229
        // Dataset definition
6230
        $dataSet = new pData();
6231
        $dataSet->addPoints($final_array, 'Serie1');
6232
        $dataSet->addPoints($my_final_array, 'Serie2');
6233
        $dataSet->addPoints($x_axis, 'Serie3');
6234
6235
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
6236
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
6237
        $dataSet->setAbscissa('Serie3');
6238
6239
        $dataSet->setXAxisName(get_lang('Score'));
6240
        $dataSet->normalize(100, "%");
6241
6242
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6243
6244
        // Cache definition
6245
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6246
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6247
        $chartHash = $myCache->getHash($dataSet);
6248
6249
        if ($myCache->isInCache($chartHash)) {
6250
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6251
            $myCache->saveFromCache($chartHash, $imgPath);
6252
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6253
        } else {
6254
            /* Create the pChart object */
6255
            $widthSize = 480;
6256
            $heightSize = 250;
6257
            $fontSize = 8;
6258
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6259
6260
            /* Turn of Antialiasing */
6261
            $myPicture->Antialias = false;
6262
6263
            /* Add a border to the picture */
6264
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
6265
6266
            /* Set the default font */
6267
            $myPicture->setFontProperties(
6268
                [
6269
                    'FontName' => api_get_path(
6270
                            SYS_FONTS_PATH
6271
                        ).'opensans/OpenSans-Regular.ttf',
6272
                    'FontSize' => 10,
6273
                ]
6274
            );
6275
6276
            /* Write the chart title */
6277
            $myPicture->drawText(
6278
                250,
6279
                20,
6280
                $exercise_title,
6281
                [
6282
                    'FontSize' => 12,
6283
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
6284
                ]
6285
            );
6286
6287
            /* Define the chart area */
6288
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
6289
6290
            /* Draw the scale */
6291
            $scaleSettings = [
6292
                'GridR' => 200,
6293
                'GridG' => 200,
6294
                'GridB' => 200,
6295
                'DrawSubTicks' => true,
6296
                'CycleBackground' => true,
6297
                'Mode' => SCALE_MODE_MANUAL,
6298
                'ManualScale' => [
6299
                    '0' => [
6300
                        'Min' => 0,
6301
                        'Max' => 100,
6302
                    ],
6303
                ],
6304
            ];
6305
            $myPicture->drawScale($scaleSettings);
6306
6307
            /* Turn on shadow computing */
6308
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6309
6310
            /* Draw the chart */
6311
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6312
            $settings = [
6313
                'DisplayValues' => true,
6314
                'DisplaySize' => $fontSize,
6315
                'DisplayR' => 0,
6316
                'DisplayG' => 0,
6317
                'DisplayB' => 0,
6318
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6319
                'Gradient' => false,
6320
                'Surrounding' => 30,
6321
                'InnerSurrounding' => 25,
6322
            ];
6323
            $myPicture->drawStackedBarChart($settings);
6324
6325
            $legendSettings = [
6326
                'Mode' => LEGEND_HORIZONTAL,
6327
                'Style' => LEGEND_NOBORDER,
6328
            ];
6329
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
6330
6331
            /* Write and save into cache */
6332
            $myCache->writeToCache($chartHash, $myPicture);
6333
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6334
            $myCache->saveFromCache($chartHash, $imgPath);
6335
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6336
        }
6337
6338
        return $imgPath;
6339
    }
6340
6341
    /**
6342
     * @param FormValidator $form
6343
     *
6344
     * @return mixed
6345
     */
6346
    public static function setUserSearchForm($form)
6347
    {
6348
        global $_configuration;
6349
        $form->addElement('text', 'keyword', get_lang('Keyword'));
6350
        $form->addElement(
6351
            'select',
6352
            'active',
6353
            get_lang('Status'),
6354
            [1 => get_lang('Active'), 0 => get_lang('Inactive')]
6355
        );
6356
6357
        $form->addElement(
6358
            'select',
6359
            'sleeping_days',
6360
            get_lang('InactiveDays'),
6361
            [
6362
                '',
6363
                1 => 1,
6364
                5 => 5,
6365
                15 => 15,
6366
                30 => 30,
6367
                60 => 60,
6368
                90 => 90,
6369
                120 => 120,
6370
            ]
6371
        );
6372
6373
        $form->addButtonSearch(get_lang('Search'));
6374
6375
        return $form;
6376
    }
6377
6378
    /**
6379
     * Get the progress of a exercise.
6380
     *
6381
     * @param int    $sessionId  The session ID (session.id)
6382
     * @param int    $courseId   The course ID (course.id)
6383
     * @param int    $exerciseId The quiz ID (c_quiz.id)
6384
     * @param string $date_from
6385
     * @param string $date_to
6386
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
6387
     *
6388
     * @return array An array with the data of exercise(s) progress
6389
     */
6390
    public static function get_exercise_progress(
6391
        $sessionId = 0,
6392
        $courseId = 0,
6393
        $exerciseId = 0,
6394
        $date_from = null,
6395
        $date_to = null,
6396
        $options = []
6397
    ) {
6398
        $sessionId = intval($sessionId);
6399
        $courseId = intval($courseId);
6400
        $exerciseId = intval($exerciseId);
6401
        $date_from = Database::escape_string($date_from);
6402
        $date_to = Database::escape_string($date_to);
6403
        /*
6404
         * This method gets the data by blocks, as previous attempts at one single
6405
         * query made it take ages. The logic of query division is described below
6406
         */
6407
        // Get tables names
6408
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6409
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6410
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6411
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6412
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6413
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6414
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6415
6416
        $sessions = [];
6417
        $courses = [];
6418
        // if session ID is defined but course ID is empty, get all the courses
6419
        // from that session
6420
        if (!empty($sessionId) && empty($courseId)) {
6421
            // $courses is an array of course int id as index and course details hash as value
6422
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6423
            $sessions[$sessionId] = api_get_session_info($sessionId);
6424
        } elseif (empty($sessionId) && !empty($courseId)) {
6425
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6426
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
6427
            $course = api_get_course_info_by_id($courseId);
6428
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6429
            $courses[$courseId] = $course;
6430
            foreach ($sessionsTemp as $sessionItem) {
6431
                $sessions[$sessionItem['id']] = $sessionItem;
6432
            }
6433
        } elseif (!empty($courseId) && !empty($sessionId)) {
6434
            //none is empty
6435
            $course = api_get_course_info_by_id($courseId);
6436
            $courses[$courseId] = [$course['code']];
6437
            $courses[$courseId]['code'] = $course['code'];
6438
            $sessions[$sessionId] = api_get_session_info($sessionId);
6439
        } else {
6440
            //both are empty, not enough data, return an empty array
6441
            return [];
6442
        }
6443
        // Now we have two arrays of courses and sessions with enough data to proceed
6444
        // If no course could be found, we shouldn't return anything.
6445
        // Sessions can be empty (then we only return the pure-course-context results)
6446
        if (count($courses) < 1) {
0 ignored issues
show
Bug introduced by
It seems like $courses can also be of type integer; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

6446
        if (count(/** @scrutinizer ignore-type */ $courses) < 1) {
Loading history...
6447
            return [];
6448
        }
6449
6450
        $data = [];
6451
        // The following loop is less expensive than what it seems:
6452
        // - if a course was defined, then we only loop through sessions
6453
        // - if a session was defined, then we only loop through courses
6454
        // - if a session and a course were defined, then we only loop once
6455
        foreach ($courses as $courseIdx => $courseData) {
6456
            $where = '';
6457
            $whereParams = [];
6458
            $whereSessionParams = '';
6459
            if (count($sessions > 0)) {
6460
                foreach ($sessions as $sessionIdx => $sessionData) {
6461
                    if (!empty($sessionIdx)) {
6462
                        $whereSessionParams .= $sessionIdx.',';
6463
                    }
6464
                }
6465
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6466
            }
6467
6468
            if (!empty($exerciseId)) {
6469
                $exerciseId = intval($exerciseId);
6470
                $where .= ' AND q.id = %d ';
6471
                $whereParams[] = $exerciseId;
6472
            }
6473
6474
            /*
6475
             * This feature has been disabled for now, to avoid having to
6476
             * join two very large tables
6477
            //2 = show all questions (wrong and correct answered)
6478
            if ($answer != 2) {
6479
                $answer = intval($answer);
6480
                //$where .= ' AND qa.correct = %d';
6481
                //$whereParams[] = $answer;
6482
            }
6483
            */
6484
6485
            $limit = '';
6486
            if (!empty($options['limit'])) {
6487
                $limit = " LIMIT ".$options['limit'];
6488
            }
6489
6490
            if (!empty($options['where'])) {
6491
                $where .= ' AND '.Database::escape_string($options['where']);
6492
            }
6493
6494
            $order = '';
6495
            if (!empty($options['order'])) {
6496
                $order = " ORDER BY ".$options['order'];
6497
            }
6498
6499
            if (!empty($date_to) && !empty($date_from)) {
6500
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6501
            }
6502
6503
            $sql = "SELECT
6504
                te.session_id,
6505
                ta.id as attempt_id,
6506
                te.exe_user_id as user_id,
6507
                te.exe_id as exercise_attempt_id,
6508
                ta.question_id,
6509
                ta.answer as answer_id,
6510
                ta.tms as time,
6511
                te.exe_exo_id as quiz_id,
6512
                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
6513
                q.title as quiz_title,
6514
                qq.description as description
6515
                FROM $ttrack_exercises te
6516
                INNER JOIN $ttrack_attempt ta 
6517
                ON ta.exe_id = te.exe_id
6518
                INNER JOIN $tquiz q 
6519
                ON q.id = te.exe_exo_id
6520
                INNER JOIN $tquiz_rel_question rq 
6521
                ON rq.exercice_id = q.id AND rq.c_id = q.c_id
6522
                INNER JOIN $tquiz_question qq
6523
                ON
6524
                    qq.id = rq.question_id AND
6525
                    qq.c_id = rq.c_id AND
6526
                    qq.position = rq.question_order AND
6527
                    ta.question_id = rq.question_id
6528
                WHERE
6529
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6530
                    AND q.c_id = $courseIdx
6531
                    $where $order $limit";
6532
            $sql_query = vsprintf($sql, $whereParams);
6533
6534
            // Now browse through the results and get the data
6535
            $rs = Database::query($sql_query);
6536
            $userIds = [];
6537
            $questionIds = [];
6538
            $answerIds = [];
6539
            while ($row = Database::fetch_array($rs)) {
6540
                //only show if exercise is visible
6541
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6542
                    $userIds[$row['user_id']] = $row['user_id'];
6543
                    $questionIds[$row['question_id']] = $row['question_id'];
6544
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6545
                    $row['session'] = $sessions[$row['session_id']];
6546
                    $data[] = $row;
6547
                }
6548
            }
6549
            // Now fill questions data. Query all questions and answers for this test to avoid
6550
            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
6551
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
6552
                            FROM $tquiz_question tq, $tquiz_answer tqa
6553
                            WHERE
6554
                                tqa.question_id = tq.id AND
6555
                                tqa.c_id = tq.c_id AND
6556
                                tq.c_id = $courseIdx AND
6557
                                tq.id IN (".implode(',', $questionIds).")";
6558
6559
            $resQuestions = Database::query($sqlQuestions);
6560
            $answer = [];
6561
            $question = [];
6562
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6563
                $questionId = $rowQuestion['question_id'];
6564
                $answerId = $rowQuestion['answer_id'];
6565
                $answer[$questionId][$answerId] = [
6566
                    'position' => $rowQuestion['position'],
6567
                    'question' => $rowQuestion['question'],
6568
                    'answer' => $rowQuestion['answer'],
6569
                    'correct' => $rowQuestion['correct'],
6570
                ];
6571
                $question[$questionId]['question'] = $rowQuestion['question'];
6572
            }
6573
6574
            // Now fill users data
6575
            $sqlUsers = "SELECT user_id, username, lastname, firstname
6576
                         FROM $tuser
6577
                         WHERE user_id IN (".implode(',', $userIds).")";
6578
            $resUsers = Database::query($sqlUsers);
6579
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6580
                $users[$rowUser['user_id']] = $rowUser;
6581
            }
6582
6583
            foreach ($data as $id => $row) {
6584
                $rowQuestId = $row['question_id'];
6585
                $rowAnsId = $row['answer_id'];
6586
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
6587
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $users does not seem to be defined for all execution paths leading up to this point.
Loading history...
6588
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6589
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6590
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6591
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
6592
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6593
                $data[$id]['question_id'] = $rowQuestId;
6594
                $data[$id]['description'] = $row['description'];
6595
            }
6596
6597
            /*
6598
            The minimum expected array structure at the end is:
6599
            attempt_id,
6600
            session name,
6601
            exercise_id,
6602
            quiz_title,
6603
            username,
6604
            lastname,
6605
            firstname,
6606
            time,
6607
            question_id,
6608
            question,
6609
            answer,
6610
            */
6611
        }
6612
6613
        return $data;
6614
    }
6615
6616
    /**
6617
     * @param User                $user
6618
     * @param string              $tool
6619
     * @param Course              $course
6620
     * @param sessionEntity |null $session Optional
6621
     *
6622
     * @throws \Doctrine\ORM\NonUniqueResultException
6623
     *
6624
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
6625
     */
6626
    public static function getLastStudentPublication(
6627
        User $user,
6628
        $tool,
6629
        Course $course,
6630
        SessionEntity $session = null
6631
    ) {
6632
        return Database::getManager()
6633
            ->createQuery("
6634
                SELECT csp
6635
                FROM ChamiloCourseBundle:CStudentPublication csp
6636
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6637
                    WITH (
6638
                        csp.iid = cip.ref AND
6639
                        csp.session = cip.session AND
6640
                        csp.cId = cip.course AND
6641
                        csp.userId = cip.lasteditUserId
6642
                    )
6643
                WHERE
6644
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6645
                ORDER BY csp.iid DESC
6646
            ")
6647
            ->setMaxResults(1)
6648
            ->setParameters([
6649
                'tool' => $tool,
6650
                'session' => $session,
6651
                'course' => $course,
6652
                'user' => $user,
6653
            ])
6654
            ->getOneOrNullResult();
6655
    }
6656
6657
    /**
6658
     * Get the HTML code for show a block with the achieved user skill on course/session.
6659
     *
6660
     * @param int  $userId
6661
     * @param int  $courseId
6662
     * @param int  $sessionId
6663
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6664
     *
6665
     * @return string
6666
     */
6667
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6668
    {
6669
        if (Skill::isAllowed($userId, false) === false && $forceView == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
6670
            return '';
6671
        }
6672
        $skillManager = new Skill();
6673
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6674
6675
        return $html;
6676
    }
6677
6678
    /**
6679
     * Gets the IP of a given user, using the last login before the given date.
6680
     *
6681
     * @param int User ID
6682
     * @param string Datetime
6683
     * @param bool Whether to return the IP as a link or just as an IP
6684
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6685
     *
6686
     * @return string IP address (or false on error)
6687
     * @assert (0,0) === false
6688
     */
6689
    public static function get_ip_from_user_event(
6690
        $user_id,
6691
        $event_date,
6692
        $return_as_link = false,
6693
        $body_replace = null
6694
    ) {
6695
        if (empty($user_id) || empty($event_date)) {
6696
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
6697
        }
6698
        $user_id = intval($user_id);
6699
        $event_date = Database::escape_string($event_date);
6700
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6701
        $sql_ip = "SELECT login_date, user_ip 
6702
                   FROM $table_login
6703
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6704
                   ORDER BY login_date DESC LIMIT 1";
6705
        $ip = '';
6706
        $res_ip = Database::query($sql_ip);
6707
        if ($res_ip !== false && Database::num_rows($res_ip) > 0) {
6708
            $row_ip = Database::fetch_row($res_ip);
6709
            if ($return_as_link) {
6710
                $ip = Display::url(
6711
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6712
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6713
                    ['title' => get_lang('TraceIP'), 'target' => '_blank']
6714
                );
6715
            } else {
6716
                $ip = $row_ip[1];
6717
            }
6718
        }
6719
6720
        return $ip;
6721
    }
6722
6723
    /**
6724
     * @param int   $userId
6725
     * @param array $courseInfo
6726
     * @param int   $sessionId
6727
     *
6728
     * @return array
6729
     */
6730
    public static function getToolInformation(
6731
        $userId,
6732
        $courseInfo,
6733
        $sessionId = 0
6734
    ) {
6735
        $csvContent = [];
6736
        $courseToolInformation = '';
6737
        $headerTool = [
6738
            [get_lang('Title')],
6739
            [get_lang('CreatedAt')],
6740
            [get_lang('UpdatedAt')],
6741
        ];
6742
6743
        $headerListForCSV = [];
6744
        foreach ($headerTool as $item) {
6745
            $headerListForCSV[] = $item[0];
6746
        }
6747
6748
        $courseForumInformationArray = getForumCreatedByUser(
6749
            $userId,
6750
            $courseInfo['real_id'],
6751
            $sessionId
6752
        );
6753
6754
        if (!empty($courseForumInformationArray)) {
6755
            $csvContent[] = [];
6756
            $csvContent[] = [get_lang('Forums')];
6757
            $csvContent[] = $headerListForCSV;
6758
            foreach ($courseForumInformationArray as $row) {
6759
                $csvContent[] = $row;
6760
            }
6761
6762
            $courseToolInformation .= Display::page_subheader2(
6763
                get_lang('Forums')
6764
            );
6765
            $courseToolInformation .= Display::return_sortable_table(
6766
                $headerTool,
6767
                $courseForumInformationArray
6768
            );
6769
        }
6770
6771
        $courseWorkInformationArray = getWorkCreatedByUser(
6772
            $userId,
6773
            $courseInfo['real_id'],
6774
            $sessionId
6775
        );
6776
6777
        if (!empty($courseWorkInformationArray)) {
6778
            $csvContent[] = null;
6779
            $csvContent[] = [get_lang('Works')];
6780
            $csvContent[] = $headerListForCSV;
6781
6782
            foreach ($courseWorkInformationArray as $row) {
6783
                $csvContent[] = $row;
6784
            }
6785
            $csvContent[] = null;
6786
6787
            $courseToolInformation .= Display::page_subheader2(
6788
                get_lang('Works')
6789
            );
6790
            $courseToolInformation .= Display::return_sortable_table(
6791
                $headerTool,
6792
                $courseWorkInformationArray
6793
            );
6794
        }
6795
6796
        $courseToolInformationTotal = null;
6797
        if (!empty($courseToolInformation)) {
6798
            $sessionTitle = null;
6799
            if (!empty($sessionId)) {
6800
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6801
            }
6802
6803
            $courseToolInformationTotal .= Display::page_subheader(
6804
                $courseInfo['title'].$sessionTitle
6805
            );
6806
            $courseToolInformationTotal .= $courseToolInformation;
6807
        }
6808
6809
        return [
6810
            'array' => $csvContent,
6811
            'html' => $courseToolInformationTotal,
6812
        ];
6813
    }
6814
}
6815
6816
/**
6817
 * @todo move into a proper file
6818
 *
6819
 * @package chamilo.tracking
6820
 */
6821
class TrackingCourseLog
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
6822
{
6823
    /**
6824
     * @return mixed
6825
     */
6826
    public static function count_item_resources()
6827
    {
6828
        $session_id = api_get_session_id();
6829
        $course_id = api_get_course_int_id();
6830
6831
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
6832
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6833
6834
        $sql = "SELECT count(tool) AS total_number_of_items
6835
                FROM $table_item_property track_resource, $table_user user
6836
                WHERE
6837
                    track_resource.c_id = $course_id AND
6838
                    track_resource.insert_user_id = user.user_id AND
6839
                    session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
6840
6841
        if (isset($_GET['keyword'])) {
6842
            $keyword = Database::escape_string(trim($_GET['keyword']));
6843
            $sql .= " AND (
6844
                        user.username LIKE '%".$keyword."%' OR
6845
                        lastedit_type LIKE '%".$keyword."%' OR
6846
                        tool LIKE '%".$keyword."%'
6847
                    )";
6848
        }
6849
6850
        $sql .= " AND tool IN (
6851
                    'document',
6852
                    'learnpath',
6853
                    'quiz',
6854
                    'glossary',
6855
                    'link',
6856
                    'course_description',
6857
                    'announcement',
6858
                    'thematic',
6859
                    'thematic_advance',
6860
                    'thematic_plan'
6861
                )";
6862
        $res = Database::query($sql);
6863
        $obj = Database::fetch_object($res);
6864
6865
        return $obj->total_number_of_items;
6866
    }
6867
6868
    /**
6869
     * @param $from
6870
     * @param $number_of_items
6871
     * @param $column
6872
     * @param $direction
6873
     *
6874
     * @return array
6875
     */
6876
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
6877
    {
6878
        $session_id = api_get_session_id();
6879
        $course_id = api_get_course_int_id();
6880
6881
        $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
6882
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6883
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
6884
        $session_id = intval($session_id);
6885
6886
        $sql = "SELECT
6887
                    tool as col0,
6888
                    lastedit_type as col1,
6889
                    ref as ref,
6890
                    user.username as col3,
6891
                    insert_date as col6,
6892
                    visibility as col7,
6893
                    user.user_id as user_id
6894
                FROM $table_item_property track_resource, $table_user user
6895
                WHERE
6896
                  track_resource.c_id = $course_id AND
6897
                  track_resource.insert_user_id = user.user_id AND
6898
                  session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id ");
6899
6900
        if (isset($_GET['keyword'])) {
6901
            $keyword = Database::escape_string(trim($_GET['keyword']));
6902
            $sql .= " AND (
6903
                        user.username LIKE '%".$keyword."%' OR
6904
                        lastedit_type LIKE '%".$keyword."%' OR
6905
                        tool LIKE '%".$keyword."%'
6906
                     ) ";
6907
        }
6908
6909
        $sql .= " AND tool IN (
6910
                    'document',
6911
                    'learnpath',
6912
                    'quiz',
6913
                    'glossary',
6914
                    'link',
6915
                    'course_description',
6916
                    'announcement',
6917
                    'thematic',
6918
                    'thematic_advance',
6919
                    'thematic_plan'
6920
                )";
6921
6922
        if ($column == 0) {
6923
            $column = '0';
6924
        }
6925
        if ($column != '' && $direction != '') {
6926
            if ($column != 2 && $column != 4) {
6927
                $sql .= " ORDER BY col$column $direction";
6928
            }
6929
        } else {
6930
            $sql .= " ORDER BY col6 DESC ";
6931
        }
6932
6933
        $from = intval($from);
6934
        if ($from) {
6935
            $number_of_items = intval($number_of_items);
6936
            $sql .= " LIMIT $from, $number_of_items ";
6937
        }
6938
6939
        $res = Database::query($sql);
6940
        $resources = [];
6941
        $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan'];
6942
        while ($row = Database::fetch_array($res)) {
6943
            $ref = $row['ref'];
6944
            $table_name = self::get_tool_name_table($row['col0']);
6945
            $table_tool = Database::get_course_table($table_name['table_name']);
6946
6947
            $id = $table_name['id_tool'];
6948
            $recorset = false;
6949
6950
            if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) {
6951
                $tbl_thematic = Database::get_course_table(TABLE_THEMATIC);
6952
                $sql = "SELECT thematic_id FROM $table_tool
6953
                        WHERE c_id = $course_id AND id = $ref";
6954
                $rs_thematic = Database::query($sql);
6955
                if (Database::num_rows($rs_thematic)) {
6956
                    $row_thematic = Database::fetch_array($rs_thematic);
6957
                    $thematic_id = $row_thematic['thematic_id'];
6958
6959
                    $sql = "SELECT session.id, session.name, user.username
6960
                            FROM $tbl_thematic t, $table_session session, $table_user user
6961
                            WHERE
6962
                              t.c_id = $course_id AND
6963
                              t.session_id = session.id AND
6964
                              session.id_coach = user.user_id AND
6965
                              t.id = $thematic_id";
6966
                    $recorset = Database::query($sql);
6967
                }
6968
            } else {
6969
                $sql = "SELECT session.id, session.name, user.username
6970
                          FROM $table_tool tool, $table_session session, $table_user user
6971
                          WHERE
6972
                              tool.c_id = $course_id AND
6973
                              tool.session_id = session.id AND
6974
                              session.id_coach = user.user_id AND
6975
                              tool.$id = $ref";
6976
                $recorset = Database::query($sql);
6977
            }
6978
6979
            if (!empty($recorset)) {
6980
                $obj = Database::fetch_object($recorset);
6981
6982
                $name_session = '';
6983
                $coach_name = '';
6984
                if (!empty($obj)) {
6985
                    $name_session = $obj->name;
6986
                    $coach_name = $obj->username;
6987
                }
6988
6989
                $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
6990
                $row[0] = '';
6991
                if ($row['col6'] != 2) {
6992
                    if (in_array($row['col0'], $thematic_tools)) {
6993
                        $exp_thematic_tool = explode('_', $row['col0']);
6994
                        $thematic_tool_title = '';
6995
                        if (is_array($exp_thematic_tool)) {
6996
                            foreach ($exp_thematic_tool as $exp) {
6997
                                $thematic_tool_title .= api_ucfirst($exp);
6998
                            }
6999
                        } else {
7000
                            $thematic_tool_title = api_ucfirst($row['col0']);
7001
                        }
7002
7003
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
7004
                    } else {
7005
                        $row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
7006
                    }
7007
                } else {
7008
                    $row[0] = api_ucfirst($row['col0']);
7009
                }
7010
                $row[1] = get_lang($row[1]);
7011
                $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get());
7012
                $row[5] = '';
7013
                //@todo Improve this code please
7014
                switch ($table_name['table_name']) {
7015
                    case 'document':
7016
                        $sql = "SELECT tool.title as title FROM $table_tool tool
7017
                                WHERE c_id = $course_id AND id = $ref";
7018
                        $rs_document = Database::query($sql);
7019
                        $obj_document = Database::fetch_object($rs_document);
7020
                        if ($obj_document) {
7021
                            $row[5] = $obj_document->title;
7022
                        }
7023
                        break;
7024
                    case 'announcement':
7025
                        $sql = "SELECT title FROM $table_tool
7026
                                WHERE c_id = $course_id AND id = $ref";
7027
                        $rs_document = Database::query($sql);
7028
                        $obj_document = Database::fetch_object($rs_document);
7029
                        if ($obj_document) {
7030
                            $row[5] = $obj_document->title;
7031
                        }
7032
                        break;
7033
                    case 'glossary':
7034
                        $sql = "SELECT name FROM $table_tool
7035
                                WHERE c_id = $course_id AND glossary_id = $ref";
7036
                        $rs_document = Database::query($sql);
7037
                        $obj_document = Database::fetch_object($rs_document);
7038
                        if ($obj_document) {
7039
                            $row[5] = $obj_document->name;
7040
                        }
7041
                        break;
7042
                    case 'lp':
7043
                        $sql = "SELECT name
7044
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
7045
                        $rs_document = Database::query($sql);
7046
                        $obj_document = Database::fetch_object($rs_document);
7047
                        $row[5] = $obj_document->name;
7048
                        break;
7049
                    case 'quiz':
7050
                        $sql = "SELECT title FROM $table_tool
7051
                                WHERE c_id = $course_id AND id = $ref";
7052
                        $rs_document = Database::query($sql);
7053
                        $obj_document = Database::fetch_object($rs_document);
7054
                        if ($obj_document) {
7055
                            $row[5] = $obj_document->title;
7056
                        }
7057
                        break;
7058
                    case 'course_description':
7059
                        $sql = "SELECT title FROM $table_tool
7060
                                WHERE c_id = $course_id AND id = $ref";
7061
                        $rs_document = Database::query($sql);
7062
                        $obj_document = Database::fetch_object($rs_document);
7063
                        if ($obj_document) {
7064
                            $row[5] = $obj_document->title;
7065
                        }
7066
                        break;
7067
                    case 'thematic':
7068
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7069
                        if (Database::num_rows($rs) > 0) {
7070
                            $obj = Database::fetch_object($rs);
7071
                            if ($obj) {
7072
                                $row[5] = $obj->title;
7073
                            }
7074
                        }
7075
                        break;
7076
                    case 'thematic_advance':
7077
                        $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7078
                        if (Database::num_rows($rs) > 0) {
7079
                            $obj = Database::fetch_object($rs);
7080
                            if ($obj) {
7081
                                $row[5] = $obj->content;
7082
                            }
7083
                        }
7084
                        break;
7085
                    case 'thematic_plan':
7086
                        $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
7087
                        if (Database::num_rows($rs) > 0) {
7088
                            $obj = Database::fetch_object($rs);
7089
                            if ($obj) {
7090
                                $row[5] = $obj->title;
7091
                            }
7092
                        }
7093
                        break;
7094
                    default:
7095
                        break;
7096
                }
7097
7098
                $row2 = $name_session;
7099
                if (!empty($coach_name)) {
7100
                    $row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
7101
                }
7102
                $row[2] = $row2;
7103
                if (!empty($row['col3'])) {
7104
                    $userInfo = api_get_user_info($row['user_id']);
7105
                    $row['col3'] = Display::url(
7106
                        $row['col3'],
7107
                        $userInfo['profile_url']
7108
                    );
7109
                    $row[3] = $row['col3'];
7110
7111
                    $ip = Tracking::get_ip_from_user_event(
7112
                        $row['user_id'],
7113
                        $row['col6'],
7114
                        true
7115
                    );
7116
                    if (empty($ip)) {
7117
                        $ip = get_lang('Unknown');
7118
                    }
7119
                    $row[4] = $ip;
7120
                }
7121
7122
                $resources[] = $row;
7123
            }
7124
        }
7125
7126
        return $resources;
7127
    }
7128
7129
    /**
7130
     * @param string $tool
7131
     *
7132
     * @return array
7133
     */
7134
    public static function get_tool_name_table($tool)
7135
    {
7136
        switch ($tool) {
7137
            case 'document':
7138
                $table_name = TABLE_DOCUMENT;
7139
                $link_tool = 'document/document.php';
7140
                $id_tool = 'id';
7141
                break;
7142
            case 'learnpath':
7143
                $table_name = TABLE_LP_MAIN;
7144
                $link_tool = 'lp/lp_controller.php';
7145
                $id_tool = 'id';
7146
                break;
7147
            case 'quiz':
7148
                $table_name = TABLE_QUIZ_TEST;
7149
                $link_tool = 'exercise/exercise.php';
7150
                $id_tool = 'id';
7151
                break;
7152
            case 'glossary':
7153
                $table_name = TABLE_GLOSSARY;
7154
                $link_tool = 'glossary/index.php';
7155
                $id_tool = 'glossary_id';
7156
                break;
7157
            case 'link':
7158
                $table_name = TABLE_LINK;
7159
                $link_tool = 'link/link.php';
7160
                $id_tool = 'id';
7161
                break;
7162
            case 'course_description':
7163
                $table_name = TABLE_COURSE_DESCRIPTION;
7164
                $link_tool = 'course_description/';
7165
                $id_tool = 'id';
7166
                break;
7167
            case 'announcement':
7168
                $table_name = TABLE_ANNOUNCEMENT;
7169
                $link_tool = 'announcements/announcements.php';
7170
                $id_tool = 'id';
7171
                break;
7172
            case 'thematic':
7173
                $table_name = TABLE_THEMATIC;
7174
                $link_tool = 'course_progress/index.php';
7175
                $id_tool = 'id';
7176
                break;
7177
            case 'thematic_advance':
7178
                $table_name = TABLE_THEMATIC_ADVANCE;
7179
                $link_tool = 'course_progress/index.php';
7180
                $id_tool = 'id';
7181
                break;
7182
            case 'thematic_plan':
7183
                $table_name = TABLE_THEMATIC_PLAN;
7184
                $link_tool = 'course_progress/index.php';
7185
                $id_tool = 'id';
7186
                break;
7187
            default:
7188
                $table_name = $tool;
7189
            break;
7190
        }
7191
7192
        return [
7193
            'table_name' => $table_name,
7194
            'link_tool' => $link_tool,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $link_tool does not seem to be defined for all execution paths leading up to this point.
Loading history...
7195
            'id_tool' => $id_tool,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_tool does not seem to be defined for all execution paths leading up to this point.
Loading history...
7196
        ];
7197
    }
7198
7199
    /**
7200
     * @return string
7201
     */
7202
    public static function display_additional_profile_fields()
7203
    {
7204
        // getting all the extra profile fields that are defined by the platform administrator
7205
        $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
7206
7207
        // creating the form
7208
        $return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
7209
7210
        // the select field with the additional user profile fields (= this is where we select the field of which we want to see
7211
        // the information the users have entered or selected.
7212
        $return .= '<select class="chzn-select" name="additional_profile_field[]" multiple>';
7213
        $return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
7214
        $extra_fields_to_show = 0;
7215
        foreach ($extra_fields as $key => $field) {
7216
            // show only extra fields that are visible + and can be filtered, added by J.Montoya
7217
            if ($field[6] == 1 && $field[8] == 1) {
7218
                if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field']) {
7219
                    $selected = 'selected="selected"';
7220
                } else {
7221
                    $selected = '';
7222
                }
7223
                $extra_fields_to_show++;
7224
                $return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
7225
            }
7226
        }
7227
        $return .= '</select>';
7228
7229
        // the form elements for the $_GET parameters (because the form is passed through GET
7230
        foreach ($_GET as $key => $value) {
7231
            if ($key != 'additional_profile_field') {
7232
                $return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
7233
            }
7234
        }
7235
        // the submit button
7236
        $return .= '<button class="save" type="submit">'.get_lang('AddAdditionalProfileField').'</button>';
7237
        $return .= '</form>';
7238
        if ($extra_fields_to_show > 0) {
7239
            return $return;
7240
        } else {
7241
            return '';
7242
        }
7243
    }
7244
7245
    /**
7246
     * This function gets all the information of a certrain ($field_id)
7247
     * additional profile field for a specific list of users is more efficent
7248
     * than get_addtional_profile_information_of_field() function
7249
     * It gets the information of all the users so that it can be displayed
7250
     * in the sortable table or in the csv or xls export.
7251
     *
7252
     * @author    Julio Montoya <[email protected]>
7253
     *
7254
     * @param    int field id
7255
     * @param    array list of user ids
7256
     *
7257
     * @return array
7258
     *
7259
     * @since    Nov 2009
7260
     *
7261
     * @version    1.8.6.2
7262
     */
7263
    public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users)
7264
    {
7265
        // Database table definition
7266
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
7267
        $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
7268
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
7269
        $result_extra_field = UserManager::get_extra_field_information($field_id);
0 ignored issues
show
Deprecated Code introduced by
The function UserManager::get_extra_field_information() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

7269
        $result_extra_field = /** @scrutinizer ignore-deprecated */ UserManager::get_extra_field_information($field_id);
Loading history...
7270
        $return = [];
7271
        if (!empty($users)) {
7272
            if ($result_extra_field['field_type'] == UserManager::USER_FIELD_TYPE_TAG) {
7273
                foreach ($users as $user_id) {
7274
                    $user_result = UserManager::get_user_tags($user_id, $field_id);
7275
                    $tag_list = [];
7276
                    foreach ($user_result as $item) {
7277
                        $tag_list[] = $item['tag'];
7278
                    }
7279
                    $return[$user_id][] = implode(', ', $tag_list);
7280
                }
7281
            } else {
7282
                $new_user_array = [];
7283
                foreach ($users as $user_id) {
7284
                    $new_user_array[] = "'".$user_id."'";
7285
                }
7286
                $users = implode(',', $new_user_array);
7287
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
7288
                // Selecting only the necessary information NOT ALL the user list
7289
                $sql = "SELECT user.user_id, v.value
7290
                        FROM $table_user user
7291
                        INNER JOIN $table_user_field_values v
7292
                        ON (user.user_id = v.item_id)
7293
                        INNER JOIN $extraField f
7294
                        ON (f.id = v.field_id)
7295
                        WHERE
7296
                            f.extra_field_type = $extraFieldType AND
7297
                            v.field_id=".intval($field_id)." AND
7298
                            user.user_id IN ($users)";
7299
7300
                $result = Database::query($sql);
7301
                while ($row = Database::fetch_array($result)) {
7302
                    // get option value for field type double select by id
7303
                    if (!empty($row['value'])) {
7304
                        if ($result_extra_field['field_type'] ==
7305
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
7306
                        ) {
7307
                            $id_double_select = explode(';', $row['value']);
7308
                            if (is_array($id_double_select)) {
7309
                                $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
7310
                                $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
7311
                                $row['value'] = ($value1.';'.$value2);
7312
                            }
7313
                        }
7314
7315
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) {
7316
                            $parsedValue = explode('::', $row['value']);
7317
7318
                            if ($parsedValue) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsedValue of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
7319
                                $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text'];
7320
                                $value2 = $parsedValue[1];
7321
7322
                                $row['value'] = "$value1: $value2";
7323
                            }
7324
                        }
7325
7326
                        if ($result_extra_field['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) {
7327
                            list($level1, $level2, $level3) = explode(';', $row['value']);
7328
7329
                            $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / ';
7330
                            $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / ';
7331
                            $row['value'] .= $result_extra_field['options'][$level3]['display_text'];
7332
                        }
7333
                    }
7334
                    // get other value from extra field
7335
                    $return[$row['user_id']][] = $row['value'];
7336
                }
7337
            }
7338
        }
7339
7340
        return $return;
7341
    }
7342
7343
    /**
7344
     * count the number of students in this course (used for SortableTable)
7345
     * Deprecated.
7346
     */
7347
    public function count_student_in_course()
7348
    {
7349
        global $nbStudents;
7350
7351
        return $nbStudents;
7352
    }
7353
7354
    public function sort_users($a, $b)
7355
    {
7356
        $tracking = Session::read('tracking_column');
7357
7358
        return strcmp(
7359
            trim(api_strtolower($a[$tracking])),
7360
            trim(api_strtolower($b[$tracking]))
7361
        );
7362
    }
7363
7364
    public function sort_users_desc($a, $b)
7365
    {
7366
        $tracking = Session::read('tracking_column');
7367
7368
        return strcmp(
7369
            trim(api_strtolower($b[$tracking])),
7370
            trim(api_strtolower($a[$tracking]))
7371
        );
7372
    }
7373
7374
    /**
7375
     * Get number of users for sortable with pagination.
7376
     *
7377
     * @return int
7378
     */
7379
    public static function get_number_of_users()
7380
    {
7381
        global $user_ids;
7382
7383
        return count($user_ids);
7384
    }
7385
7386
    /**
7387
     * Get data for users list in sortable with pagination.
7388
     *
7389
     * @param $from
7390
     * @param $number_of_items
7391
     * @param $column
7392
     * @param $direction
7393
     * @param $includeInvitedUsers boolean Whether include the invited users
7394
     *
7395
     * @return array
7396
     */
7397
    public static function get_user_data(
7398
        $from,
7399
        $number_of_items,
7400
        $column,
7401
        $direction,
7402
        $includeInvitedUsers = false
7403
    ) {
7404
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
7405
7406
        $course_code = Database::escape_string($course_code);
7407
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
7408
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
7409
7410
        $access_url_id = api_get_current_access_url_id();
7411
7412
        // get all users data from a course for sortable with limit
7413
        if (is_array($user_ids)) {
7414
            $user_ids = array_map('intval', $user_ids);
7415
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
7416
        } else {
7417
            $user_ids = intval($user_ids);
7418
            $condition_user = " WHERE user.user_id = $user_ids ";
7419
        }
7420
7421
        if (!empty($_GET['user_keyword'])) {
7422
            $keyword = trim(Database::escape_string($_GET['user_keyword']));
7423
            $condition_user .= " AND (
7424
                user.firstname LIKE '%".$keyword."%' OR
7425
                user.lastname LIKE '%".$keyword."%'  OR
7426
                user.username LIKE '%".$keyword."%'  OR
7427
                user.email LIKE '%".$keyword."%'
7428
             ) ";
7429
        }
7430
7431
        $url_table = null;
7432
        $url_condition = null;
7433
        if (api_is_multiple_url_enabled()) {
7434
            $url_table = ", ".$tbl_url_rel_user." as url_users";
7435
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
7436
        }
7437
7438
        $invitedUsersCondition = '';
7439
        if (!$includeInvitedUsers) {
7440
            $invitedUsersCondition = " AND user.status != ".INVITEE;
7441
        }
7442
7443
        $sql = "SELECT  user.user_id as user_id,
7444
                    user.official_code  as col0,
7445
                    user.lastname       as col1,
7446
                    user.firstname      as col2,
7447
                    user.username       as col3
7448
                FROM $tbl_user as user $url_table
7449
                $condition_user $url_condition $invitedUsersCondition";
7450
7451
        if (!in_array($direction, ['ASC', 'DESC'])) {
7452
            $direction = 'ASC';
7453
        }
7454
7455
        $column = intval($column);
7456
        $from = intval($from);
7457
        $number_of_items = intval($number_of_items);
7458
7459
        $sql .= " ORDER BY col$column $direction ";
7460
        $sql .= " LIMIT $from,$number_of_items";
7461
7462
        $res = Database::query($sql);
7463
        $users = [];
7464
7465
        $course_info = api_get_course_info($course_code);
7466
        $total_surveys = 0;
7467
        $total_exercises = ExerciseLib::get_all_exercises(
7468
            $course_info,
7469
            $session_id,
7470
            false,
7471
            null,
7472
            false,
7473
            3
7474
        );
7475
7476
        if (empty($session_id)) {
7477
            $survey_user_list = [];
7478
            $survey_list = SurveyManager::get_surveys($course_code, $session_id);
7479
7480
            $total_surveys = count($survey_list);
7481
            foreach ($survey_list as $survey) {
7482
                $user_list = SurveyManager::get_people_who_filled_survey(
7483
                    $survey['survey_id'],
7484
                    false,
7485
                    $course_info['real_id']
7486
                );
7487
7488
                foreach ($user_list as $user_id) {
7489
                    isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
7490
                }
7491
            }
7492
        }
7493
7494
        $courseInfo = api_get_course_info($course_code);
7495
        $courseId = $courseInfo['real_id'];
7496
7497
        $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$course_code.
7498
            '&course='.$course_code.'&origin=tracking_course&id_session='.$session_id;
7499
7500
        while ($user = Database::fetch_array($res, 'ASSOC')) {
7501
            $user['official_code'] = $user['col0'];
7502
            $user['lastname'] = $user['col1'];
7503
            $user['firstname'] = $user['col2'];
7504
            $user['username'] = $user['col3'];
7505
7506
            $user['time'] = api_time_to_hms(
7507
                Tracking::get_time_spent_on_the_course(
7508
                    $user['user_id'],
7509
                    $courseId,
7510
                    $session_id
7511
                )
7512
            );
7513
7514
            $avg_student_score = Tracking::get_avg_student_score(
7515
                $user['user_id'],
7516
                $course_code,
7517
                [],
7518
                $session_id
7519
            );
7520
7521
            $avg_student_progress = Tracking::get_avg_student_progress(
7522
                $user['user_id'],
7523
                $course_code,
7524
                [],
7525
                $session_id
7526
            );
7527
7528
            if (empty($avg_student_progress)) {
7529
                $avg_student_progress = 0;
7530
            }
7531
            $user['average_progress'] = $avg_student_progress.'%';
7532
7533
            $total_user_exercise = Tracking::get_exercise_student_progress(
7534
                $total_exercises,
7535
                $user['user_id'],
7536
                $courseId,
7537
                $session_id
7538
            );
7539
7540
            $user['exercise_progress'] = $total_user_exercise;
7541
7542
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
7543
                $total_exercises,
7544
                $user['user_id'],
7545
                $courseId,
7546
                $session_id
7547
            );
7548
7549
            $user['exercise_average_best_attempt'] = $total_user_exercise;
7550
7551
            if (is_numeric($avg_student_score)) {
7552
                $user['student_score'] = $avg_student_score.'%';
7553
            } else {
7554
                $user['student_score'] = $avg_student_score;
7555
            }
7556
7557
            $user['count_assignments'] = Tracking::count_student_assignments(
7558
                $user['user_id'],
7559
                $course_code,
7560
                $session_id
7561
            );
7562
            $user['count_messages'] = Tracking::count_student_messages(
7563
                $user['user_id'],
7564
                $course_code,
7565
                $session_id
7566
            );
7567
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
7568
                $user['user_id'],
7569
                $courseId,
7570
                $session_id
7571
            );
7572
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
7573
                $user['user_id'],
7574
                $courseInfo,
7575
                $session_id,
7576
                $export_csv === false
7577
            );
7578
7579
            if (empty($session_id)) {
7580
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys;
7581
            }
7582
7583
            $url = $urlBase.'&student='.$user['user_id'];
7584
7585
            $user['link'] = '<center><a href="'.$url.'">
7586
                            '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
7587
                             </a></center>';
7588
7589
            // store columns in array $users
7590
            $is_western_name_order = api_is_western_name_order();
7591
            $user_row = [];
7592
            $user_row['official_code'] = $user['official_code']; //0
7593
            if ($is_western_name_order) {
7594
                $user_row['firstname'] = $user['firstname'];
7595
                $user_row['lastname'] = $user['lastname'];
7596
            } else {
7597
                $user_row['lastname'] = $user['lastname'];
7598
                $user_row['firstname'] = $user['firstname'];
7599
            }
7600
            $user_row['username'] = $user['username'];
7601
            $user_row['time'] = $user['time'];
7602
            $user_row['average_progress'] = $user['average_progress'];
7603
            $user_row['exercise_progress'] = $user['exercise_progress'];
7604
            $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt'];
7605
            $user_row['student_score'] = $user['student_score'];
7606
            $user_row['count_assignments'] = $user['count_assignments'];
7607
            $user_row['count_messages'] = $user['count_messages'];
7608
7609
            $userGroupManager = new UserGroup();
7610
            $user_row['classes'] = $userGroupManager->getLabelsFromNameList($user['user_id'], UserGroup::NORMAL_CLASS);
7611
7612
            if (empty($session_id)) {
7613
                $user_row['survey'] = $user['survey'];
7614
            }
7615
7616
            $user_row['first_connection'] = $user['first_connection'];
7617
            $user_row['last_connection'] = $user['last_connection'];
7618
7619
            // we need to display an additional profile field
7620
            if (isset($_GET['additional_profile_field'])) {
7621
                $data = Session::read('additional_user_profile_info');
7622
                $extraFieldInfo = Session::read('extra_field_info');
7623
                foreach ($_GET['additional_profile_field'] as $fieldId) {
7624
                    if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) {
7625
                        if (is_array($data[$fieldId][$user['user_id']])) {
7626
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = implode(
7627
                                ', ',
7628
                                $data[$fieldId][$user['user_id']]
7629
                            );
7630
                        } else {
7631
                            $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']];
7632
                        }
7633
                    } else {
7634
                        $user_row[$extraFieldInfo[$fieldId]['variable']] = '';
7635
                    }
7636
                }
7637
            }
7638
7639
            $user_row['link'] = $user['link'];
7640
7641
            if ($export_csv) {
7642
                if (empty($session_id)) {
7643
                    unset($user_row['classes']);
7644
                    unset($user_row['link']);
7645
                } else {
7646
                    unset($user_row['classes']);
7647
                    unset($user_row['link']);
7648
                }
7649
7650
                $csv_content[] = $user_row;
7651
            }
7652
7653
            $users[] = array_values($user_row);
7654
        }
7655
7656
        Session::erase('additional_user_profile_info');
7657
        Session::erase('extra_field_info');
7658
7659
        return $users;
7660
    }
7661
7662
    /**
7663
     * Get data for users list in sortable with pagination.
7664
     *
7665
     * @param $from
7666
     * @param $number_of_items
7667
     * @param $column
7668
     * @param $direction
7669
     * @param $includeInvitedUsers boolean Whether include the invited users
7670
     *
7671
     * @return array
7672
     */
7673
    public static function getTotalTimeReport(
7674
        $from,
7675
        $number_of_items,
7676
        $column,
7677
        $direction,
7678
        $includeInvitedUsers = false
7679
    ) {
7680
        global $user_ids, $course_code, $export_csv, $csv_content, $session_id;
7681
7682
        $course_code = Database::escape_string($course_code);
7683
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
7684
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
7685
        $access_url_id = api_get_current_access_url_id();
7686
7687
        // get all users data from a course for sortable with limit
7688
        if (is_array($user_ids)) {
7689
            $user_ids = array_map('intval', $user_ids);
7690
            $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") ";
7691
        } else {
7692
            $user_ids = intval($user_ids);
7693
            $condition_user = " WHERE user.user_id = $user_ids ";
7694
        }
7695
7696
        $url_table = null;
7697
        $url_condition = null;
7698
        if (api_is_multiple_url_enabled()) {
7699
            $url_table = ", ".$tbl_url_rel_user." as url_users";
7700
            $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
7701
        }
7702
7703
        $invitedUsersCondition = '';
7704
        if (!$includeInvitedUsers) {
7705
            $invitedUsersCondition = " AND user.status != ".INVITEE;
7706
        }
7707
7708
        $sql = "SELECT  user.user_id as user_id,
7709
                    user.official_code  as col0,
7710
                    user.lastname       as col1,
7711
                    user.firstname      as col2,
7712
                    user.username       as col3
7713
                FROM $tbl_user as user $url_table
7714
                $condition_user $url_condition $invitedUsersCondition";
7715
7716
        if (!in_array($direction, ['ASC', 'DESC'])) {
7717
            $direction = 'ASC';
7718
        }
7719
7720
        $column = intval($column);
7721
        $from = intval($from);
7722
        $number_of_items = intval($number_of_items);
7723
7724
        $sql .= " ORDER BY col$column $direction ";
7725
        $sql .= " LIMIT $from,$number_of_items";
7726
7727
        $res = Database::query($sql);
7728
        $users = [];
7729
7730
        $course_info = api_get_course_info($course_code);
7731
7732
        while ($user = Database::fetch_array($res, 'ASSOC')) {
7733
            $courseInfo = api_get_course_info($course_code);
7734
            $courseId = $courseInfo['real_id'];
7735
7736
            $user['official_code'] = $user['col0'];
7737
            $user['lastname'] = $user['col1'];
7738
            $user['firstname'] = $user['col2'];
7739
            $user['username'] = $user['col3'];
7740
7741
            $totalCourseTime = Tracking::get_time_spent_on_the_course(
7742
                $user['user_id'],
7743
                $courseId,
7744
                $session_id
7745
            );
7746
7747
            $user['time'] = api_time_to_hms($totalCourseTime);
7748
            $totalLpTime = Tracking::get_time_spent_in_lp(
7749
                $user['user_id'],
7750
                $course_code,
7751
                [],
7752
                $session_id
7753
            );
7754
7755
            $user['total_lp_time'] = $totalLpTime;
7756
            $warning = '';
7757
            if ($totalLpTime > $totalCourseTime) {
7758
                $warning = '&nbsp;'.Display::label(get_lang('TimeDifference'), 'danger');
7759
            }
7760
7761
            $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning;
7762
7763
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
7764
                $user['user_id'],
7765
                $courseId,
7766
                $session_id
7767
            );
7768
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
7769
                $user['user_id'],
7770
                $courseInfo,
7771
                $session_id,
7772
                $export_csv === false
7773
            );
7774
7775
            $user['link'] = '<center>
7776
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
7777
                             '.Display::return_icon('2rightarrow.png', get_lang('Details')).'
7778
                             </a>
7779
                         </center>';
7780
7781
            // store columns in array $users
7782
            $is_western_name_order = api_is_western_name_order();
7783
            $user_row = [];
7784
            $user_row['official_code'] = $user['official_code']; //0
7785
            if ($is_western_name_order) {
7786
                $user_row['firstname'] = $user['firstname'];
7787
                $user_row['lastname'] = $user['lastname'];
7788
            } else {
7789
                $user_row['lastname'] = $user['lastname'];
7790
                $user_row['firstname'] = $user['firstname'];
7791
            }
7792
            $user_row['username'] = $user['username'];
7793
            $user_row['time'] = $user['time'];
7794
            $user_row['total_lp_time'] = $user['total_lp_time'];
7795
            $user_row['first_connection'] = $user['first_connection'];
7796
            $user_row['last_connection'] = $user['last_connection'];
7797
7798
            $user_row['link'] = $user['link'];
7799
            $users[] = array_values($user_row);
7800
        }
7801
7802
        return $users;
7803
    }
7804
7805
    /**
7806
     * @param string $current
7807
     */
7808
    public static function actionsLeft($current, $sessionId = 0)
7809
    {
7810
        $usersLink = Display::url(
7811
            Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM),
7812
            'courseLog.php?'.api_get_cidreq(true, false)
7813
        );
7814
7815
        $groupsLink = Display::url(
7816
            Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
7817
            'course_log_groups.php?'.api_get_cidreq()
7818
        );
7819
7820
        $resourcesLink = Display::url(
7821
            Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM),
7822
            'course_log_resources.php?'.api_get_cidreq(true, false)
7823
        );
7824
7825
        $courseLink = Display::url(
7826
            Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
7827
            'course_log_tools.php?'.api_get_cidreq(true, false)
7828
        );
7829
7830
        $examLink = Display::url(
7831
            Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
7832
            api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq()
7833
        );
7834
7835
        $eventsLink = Display::url(
7836
            Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
7837
            api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq()
7838
        );
7839
7840
        $attendanceLink = '';
7841
        if (!empty($sessionId)) {
7842
            $attendanceLink = Display::url(
7843
                Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
7844
                api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins'
7845
            );
7846
        }
7847
7848
        switch ($current) {
7849
            case 'users':
7850
                $usersLink = Display::url(
7851
                        Display::return_icon(
7852
                        'user_na.png',
7853
                        get_lang('StudentsTracking'),
7854
                        [],
7855
                        ICON_SIZE_MEDIUM
7856
                    ),
7857
                    '#'
7858
                );
7859
                break;
7860
            case 'groups':
7861
                $groupsLink = Display::url(
7862
                    Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM),
7863
                    '#'
7864
                );
7865
                break;
7866
            case 'courses':
7867
                $courseLink = Display::url(
7868
                    Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM),
7869
                    '#'
7870
                );
7871
                break;
7872
            case 'resources':
7873
                $resourcesLink = Display::url(
7874
                    Display::return_icon(
7875
                    'tools_na.png',
7876
                    get_lang('ResourcesTracking'),
7877
                    [],
7878
                    ICON_SIZE_MEDIUM
7879
                    ),
7880
                    '#'
7881
                );
7882
                break;
7883
            case 'exams':
7884
                $examLink = Display::url(
7885
                    Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM),
7886
                    '#'
7887
                );
7888
                break;
7889
            case 'logs':
7890
                $eventsLink = Display::url(
7891
                    Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM),
7892
                    '#'
7893
                );
7894
                break;
7895
            case 'attendance':
7896
                if (!empty($sessionId)) {
7897
                    $attendanceLink = Display::url(
7898
                        Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM),
7899
                        '#'
7900
                    );
7901
                }
7902
                break;
7903
        }
7904
7905
        $items = [
7906
            $usersLink,
7907
            $groupsLink,
7908
            $courseLink,
7909
            $resourcesLink,
7910
            $examLink,
7911
            $eventsLink,
7912
            $attendanceLink,
7913
        ];
7914
7915
        return implode('', $items).'&nbsp;';
7916
    }
7917
}
7918