Completed
Push — 1.10.x ( 31f8f3...d91a6d )
by Yannick
66:02 queued 19:40
created

Tracking::get_course_connections_count()   C

Complexity

Conditions 10
Paths 20

Size

Total Lines 61
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 40
nc 20
nop 4
dl 0
loc 61
rs 6.4757
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use CpChart\Classes\pCache as pCache;
6
use CpChart\Classes\pData as pData;
7
use CpChart\Classes\pImage as pImage;
8
use Chamilo\UserBundle\Entity\User;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, User.

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...
9
use Chamilo\CoreBundle\Entity\Course;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Course.

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...
10
use Chamilo\CoreBundle\Entity\Session;
11
12
/**
13
 *  Class Tracking
14
 *
15
 *  @author  Julio Montoya <[email protected]>
16
 *  @package chamilo.library
17
 */
18
class Tracking
19
{
20
    public static function get_group_reporting(
21
        $course_id,
22
        $group_id = null,
23
        $type = 'all',
24
        $start = 0,
25
        $limit = 1000,
26
        $sidx = 1,
27
        $sord = 'desc',
28
        $where_condition = array()
29
    ) {
30
        if (empty($course_id)) {
31
            return null;
32
        }
33
        $course_info = api_get_course_info_by_id($course_id);
34
        $table_group = Database :: get_course_table(TABLE_GROUP);
35
        $course_id = intval($course_id);
36
37
        $select = ' * ';
38
        if ($type == 'count') {
39
            $select = ' count(id) as count ';
40
        }
41
42
        $default_where = array('c_id = ? ' => array($course_id));
43
44
        $result = Database::select($select, $table_group, array(
45
                'limit' => " $start, $limit",
46
                'where' => $default_where,
47
                'order' => "$sidx $sord")
48
        );
49
50
        if ($type == 'count') {
51
            return $result[0]['count'];
52
        }
53
54
        $parsed_result = array();
55
        if (!empty($result)) {
56
            foreach ($result as $group) {
57
                $users = GroupManager::get_users($group['id'], true);
58
59
                $time = 0;
60
                $avg_student_score = 0;
61
                $avg_student_progress = 0;
62
                $work = 0;
63
                $messages = 0;
64
65
                foreach ($users as $user_data) {
66
                    $time += Tracking::get_time_spent_on_the_course($user_data['user_id'], $course_info['code'], 0);
67
                    $avg_student_score += Tracking::get_avg_student_score($user_data['user_id'], $course_info['code'], array(), 0);
68
                    $avg_student_progress += Tracking::get_avg_student_progress($user_data['user_id'], $course_info['code'], array(), 0);
69
                    $work += Tracking::count_student_assignments($user_data['user_id'], $course_info['code'], 0);
70
                    $messages += Tracking::count_student_messages($user_data['user_id'], $course_info['code'], 0);
71
                }
72
73
                $group_item = array(
74
                    'id' => $group['id'],
75
                    'name' => $group['name'],
76
                    'time' => api_time_to_hms($time),
77
                    'progress' => $avg_student_progress,
78
                    'score' => $avg_student_score,
79
                    'works' => $work,
80
                    'messages' => $messages,
81
                );
82
                $parsed_result[] = $group_item;
83
            }
84
        }
85
        return $parsed_result;
86
    }
87
88
    /**
89
     * @param int $user_id
90
     * @param array $courseInfo
91
     * @param int $session_id
92
     * @param string $origin
93
     * @param bool $export_csv
94
     * @param int $lp_id
95
     * @param int $lp_item_id
96
     * @param int $extendId
97
     * @param int $extendAttemptId
98
     * @param string $extendedAttempt
99
     * @param string $extendedAll
100
     * @param string $type classic or simple
101
     * @param boolean $allowExtend Optional. Allow or not extend te results
102
     * @return null|string
103
     */
104
    public static function getLpStats(
105
        $user_id,
106
        $courseInfo,
107
        $session_id,
108
        $origin,
109
        $export_csv,
110
        $lp_id,
111
        $lp_item_id = null,
112
        $extendId = null,
113
        $extendAttemptId = null,
114
        $extendedAttempt = null,
115
        $extendedAll = null,
116
        $type = 'classic',
117
        $allowExtend =  true
118
    ) {
119
        if (empty($courseInfo) || empty($lp_id)) {
120
            return null;
121
        }
122
123
        $lp_id = intval($lp_id);
124
        $lp_item_id = intval($lp_item_id);
125
        $user_id = intval($user_id);
126
        $session_id = intval($session_id);
127
        $origin = Security::remove_XSS($origin);
128
129
        $list = learnpath :: get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
130
131
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
132
        $course_id = $courseInfo['real_id'];
133
        $courseCode = $courseInfo['code'];
134
        $session_condition = api_get_session_condition($session_id);
135
136
        // Extend all button
137
        $output = null;
138
        $extend_all = 0;
139
140
        if ($origin == 'tracking') {
141
            $url_suffix = '&session_id=' . $session_id . '&course=' . $courseCode . '&student_id=' . $user_id . '&lp_id=' . $lp_id . '&origin=' . $origin;
142
        } else {
143
            $url_suffix = '&lp_id=' . $lp_id;
144
        }
145
146
        if (!empty($extendedAll)) {
147
            $extend_all_link = Display::url(
148
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
149
                api_get_self() . '?action=stats' . $url_suffix
150
            );
151
152
            $extend_all = 1;
153
        } else {
154
            $extend_all_link = Display::url(
155
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
156
                api_get_self() . '?action=stats&extend_all=1' . $url_suffix
157
            );
158
        }
159
160
        if ($origin != 'tracking') {
161
            $output .= '<div class="section-status">';
162
            $output .= Display::page_header(get_lang('ScormMystatus'));
163
            $output .= '</div>';
164
        }
165
166
        $actionColumn = null;
167
        if ($type == 'classic') {
168
            $actionColumn = ' <th>' . get_lang('Actions') . '</th>';
169
        }
170
        $output .= '<div class="table-responsive">';
171
        $output .= '<table class="table tracking">
172
            <thead>
173
            <tr class="table-header">
174
                <th width="16">' . ($allowExtend == true ? $extend_all_link : '&nbsp;') . '</th>
175
                <th colspan="4">
176
                    ' . get_lang('ScormLessonTitle') .'
177
                </th>
178
                <th colspan="2">
179
                    ' . get_lang('ScormStatus') . '
180
                </th>
181
                <th colspan="2">
182
                    ' . get_lang('ScormScore') . '
183
                </th>
184
                <th colspan="2">
185
                    ' . get_lang('ScormTime') . '
186
                </th>
187
                '.$actionColumn.'
188
                </tr>
189
            </thead>
190
            <tbody>
191
        ';
192
193
        // Going through the items using the $items[] array instead of the database order ensures
194
        // we get them in the same order as in the imsmanifest file, which is rather random when using
195
        // the database table.
196
197
        $TBL_LP_ITEM = Database :: get_course_table(TABLE_LP_ITEM);
198
        $TBL_LP_ITEM_VIEW = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
199
        $TBL_LP_VIEW = Database :: get_course_table(TABLE_LP_VIEW);
200
        $tbl_quiz_questions = Database :: get_course_table(TABLE_QUIZ_QUESTION);
201
        $TBL_QUIZ = Database :: get_course_table(TABLE_QUIZ_TEST);
202
        $tbl_stats_exercices = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
203
        $tbl_stats_attempts = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
204
205
        $sql = "SELECT max(view_count)
206
                FROM $TBL_LP_VIEW
207
                WHERE
208
                    c_id = $course_id AND
209
                    lp_id = $lp_id AND
210
                    user_id = $user_id
211
                    $session_condition";
212
        $res = Database::query($sql);
213
        $view = '';
214
        if (Database :: num_rows($res) > 0) {
215
            $myrow = Database :: fetch_array($res);
216
            $view = $myrow[0];
217
        }
218
219
        $counter = 0;
220
        $total_time = 0;
221
        $h = get_lang('h');
222
223
        if (!empty($export_csv)) {
224
            $csv_content[] = array(
225
                get_lang('ScormLessonTitle'),
226
                get_lang('ScormStatus'),
227
                get_lang('ScormScore'),
228
                get_lang('ScormTime')
229
            );
230
        }
231
232
        $result_disabled_ext_all = true;
233
        $chapterTypes = learnpath::getChapterTypes();
234
235
        // Show lp items
236
        if (is_array($list) && count($list) > 0) {
237
            foreach ($list as $my_item_id) {
238
                $extend_this = 0;
239
                $order = 'DESC';
240
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
241
                    $extend_this = 1;
242
                    $order = 'ASC';
243
                }
244
245
                // Prepare statement to go through each attempt.
246
                $viewCondition = null;
247
                if (!empty($view)) {
248
                    $viewCondition =  " AND v.view_count = $view  ";
249
                }
250
251
                $sql = "SELECT
252
                    iv.status as mystatus,
253
                    v.view_count as mycount,
254
                    iv.score as myscore,
255
                    iv.total_time as mytime,
256
                    i.id as myid,
257
                    i.lp_id as mylpid,
258
                    iv.lp_view_id as mylpviewid,
259
                    i.title as mytitle,
260
                    i.max_score as mymaxscore,
261
                    iv.max_score as myviewmaxscore,
262
                    i.item_type as item_type,
263
                    iv.view_count as iv_view_count,
264
                    iv.id as iv_id,
265
                    path
266
                FROM $TBL_LP_ITEM as i
267
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
268
                ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
269
                INNER JOIN $TBL_LP_VIEW as v
270
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
271
                WHERE
272
                    v.c_id = $course_id AND
273
                    i.id = $my_item_id AND
274
                    i.lp_id = $lp_id  AND
275
                    v.user_id = $user_id AND
276
                    v.session_id = $session_id
277
                    $viewCondition
278
                ORDER BY iv.view_count $order ";
279
280
                $result = Database::query($sql);
281
                $num = Database :: num_rows($result);
282
                $time_for_total = 'NaN';
283
284
                // Extend all
285
                if (($extend_this || $extend_all) && $num > 0) {
286
                    $row = Database :: fetch_array($result);
287
                    $result_disabled_ext_all = false;
288 View Code Duplication
                    if ($row['item_type'] == 'quiz') {
289
                        // Check results_disabled in quiz table.
290
                        $my_path = Database::escape_string($row['path']);
291
292
                        $sql = "SELECT results_disabled
293
                                FROM $TBL_QUIZ
294
                                WHERE
295
                                    c_id = $course_id AND
296
                                    id ='" . $my_path . "'";
297
                        $res_result_disabled = Database::query($sql);
298
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
299
300
                        if (Database::num_rows($res_result_disabled) > 0 &&
301
                            (int) $row_result_disabled[0] === 1
302
                        ) {
303
                            $result_disabled_ext_all = true;
304
                        }
305
                    }
306
307
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
308
                    if (($counter % 2) == 0) {
309
                        $oddclass = 'row_odd';
310
                    } else {
311
                        $oddclass = 'row_even';
312
                    }
313
314
                    $extend_link = '';
315 View Code Duplication
                    if (!empty($inter_num)) {
316
                        $extend_link = Display::url(
317
                              Display::return_icon('visible.gif', get_lang('HideAttemptView')),
318
                              api_get_self() . '?action=stats&fold_id=' . $my_item_id . $url_suffix
319
                        );
320
                    }
321
                    $title = $row['mytitle'];
322
323
                    if (empty($title)) {
324
                        $title = rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
325
                    }
326
327
                    if (in_array($row['item_type'], $chapterTypes)) {
328
                        $title = "<h4> $title </h4>";
329
                    }
330
                    $lesson_status = $row['mystatus'];
331
                    $title = Security::remove_XSS($title);
332
                    $counter++;
333
334
                    $action = null;
335
                    if ($type == 'classic') {
336
                        $action =  '<td></td>';
337
                    }
338
339
                    if (in_array($row['item_type'], $chapterTypes)) {
340
                        $output .= '<tr class="'.$oddclass.'">
341
                                <td>'.$extend_link.'</td>
342
                                <td colspan="4">
343
                                   '.$title.'
344
                                </td>
345
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
346
                                <td colspan="2"></td>
347
                                <td colspan="2"></td>
348
                                '.$action.'
349
                            </tr>';
350
                        continue;
351
                    } else {
352
                        $output .= '<tr class="'.$oddclass.'">
353
                                <td>'.$extend_link.'</td>
354
                                <td colspan="4">
355
                                   '.$title.'
356
                                </td>
357
                                <td colspan="2"></td>
358
                                <td colspan="2"></td>
359
                                <td colspan="2"></td>
360
                                '.$action.'
361
                            </tr>';
362
                    }
363
364
                    $attemptCount = 1;
365
366
                    do {
367
                        // Check if there are interactions below.
368
                        $extend_attempt_link = '';
369
                        $extend_this_attempt = 0;
370
371
                        if ((learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0 ||
372
                            learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0) &&
373
                            !$extend_all
374
                        ) {
375 View Code Duplication
                            if ($extendAttemptId == $row['iv_id']) {
376
                                // The extend button for this attempt has been clicked.
377
                                $extend_this_attempt = 1;
378
                                $extend_attempt_link = Display::url(
379
                                    Display::return_icon('visible.gif', get_lang('HideAttemptView')),
380
                                    api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&fold_attempt_id=' . $row['iv_id'] . $url_suffix
381
                                );
382
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
383
                                // The extend button for this attempt has not been clicked.
384
                                $extend_attempt_link = Display::url(
385
                                    Display::return_icon('invisible.gif', get_lang('ExtendAttemptView')),
386
                                    api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&extend_attempt_id=' . $row['iv_id'] . $url_suffix
387
                                );
388
                            }
389
                        }
390
391
                        if (($counter % 2) == 0) {
392
                            $oddclass = 'row_odd';
393
                        } else {
394
                            $oddclass = 'row_even';
395
                        }
396
397
                        $lesson_status = $row['mystatus'];
398
                        $score = $row['myscore'];
399
                        $time_for_total = $row['mytime'];
400
                        $time = learnpathItem :: getScormTimeFromParameter('js', $row['mytime']);
401
402
                        if ($score == 0) {
403
                            $maxscore = $row['mymaxscore'];
404
                        } else {
405
                            if ($row['item_type'] == 'sco') {
406 View Code Duplication
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
407
                                    $maxscore = $row['myviewmaxscore'];
408
                                } elseif ($row['myviewmaxscore'] === '') {
409
                                    $maxscore = 0;
410
                                } else {
411
                                    $maxscore = $row['mymaxscore'];
412
                                }
413
                            } else {
414
                                $maxscore = $row['mymaxscore'];
415
                            }
416
                        }
417
418
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
419
                        $time = str_replace('NaN', '00' . $h . '00\'00"', $time);
420
421
                        if ($row['item_type'] != 'dokeos_chapter') {
422
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
423
                                $view_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
424
                            } else {
425
                                switch ($row['item_type']) {
426 View Code Duplication
                                    case 'sco':
427
                                        if ($maxscore == 0) {
428
                                            $view_score = $score;
429
                                        } else {
430
                                            $view_score = ExerciseLib::show_score($score, $maxscore, false);
431
                                        }
432
                                        break;
433 View Code Duplication
                                    case 'document':
434
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
435
                                        break;
436
                                    default:
437
                                        $view_score = ExerciseLib::show_score($score, $maxscore, false);
438
                                        break;
439
                                }
440
                            }
441
442
                            $action = null;
443
                            if ($type == 'classic') {
444
                                $action = '<td></td>';
445
                            }
446
447
                            $output .= '<tr class="' . $oddclass . '">
448
                                    <td></td>
449
                                    <td>' . $extend_attempt_link . '</td>
450
                                    <td colspan="3">' . get_lang('Attempt') . ' ' . $attemptCount . '</td>
451
                                    <td colspan="2">' . learnpathItem::humanize_status($lesson_status, true, $type) . '</td>
452
                                    <td colspan="2">' . $view_score . '</td>
453
                                    <td colspan="2">' . $time . '</td>
454
                                    '.$action.'
455
                                </tr>';
456
                            $attemptCount++;
457
                            if (!empty($export_csv)) {
458
                                $temp = array();
459
                                $temp[] = $title = Security::remove_XSS($title);
460
                                $temp[] = Security::remove_XSS(learnpathItem::humanize_status($lesson_status, false, $type));
461
462 View Code Duplication
                                if ($row['item_type'] == 'quiz') {
463
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
464
                                        $temp[] = '/';
465
                                    } else {
466
                                        $temp[] = ($score == 0 ? '0/' . $maxscore : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
467
                                    }
468
                                } else {
469
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
470
                                }
471
                                $temp[] = $time;
472
                                $csv_content[] = $temp;
473
                            }
474
                        }
475
476
                        $counter++;
477
478
                        $action = null;
479
                        if ($type == 'classic') {
480
                            $action = '<td></td>';
481
                        }
482
483
                        if ($extend_this_attempt || $extend_all) {
484
                            $list1 = learnpath :: get_iv_interactions_array($row['iv_id']);
485
                            foreach ($list1 as $id => $interaction) {
486
                                if (($counter % 2) == 0) {
487
                                    $oddclass = 'row_odd';
488
                                } else {
489
                                    $oddclass = 'row_even';
490
                                }
491
                                $student_response = urldecode($interaction['student_response']);
492
                                $content_student_response = explode('__|', $student_response);
493
494
                                if (count($content_student_response) > 0) {
495
                                    if (count($content_student_response) >= 3) {
496
                                        // Pop the element off the end of array.
497
                                        array_pop($content_student_response);
498
                                    }
499
                                    $student_response = implode(',', $content_student_response);
500
                                }
501
502
                                $output .= '<tr class="'.$oddclass.'">
503
                                        <td></td>
504
                                        <td></td>
505
                                        <td></td>
506
                                        <td>'.$interaction['order_id'] . '</td>
507
                                        <td>'.$interaction['id'] . '</td>
508
                                        <td colspan="2">' . $interaction['type'].'</td>
509
                                        <td>'.$student_response . '</td>
510
                                        <td>'.$interaction['result'] . '</td>
511
                                        <td>'.$interaction['latency'] . '</td>
512
                                        <td>'.$interaction['time'] . '</td>
513
                                        '.$action.'
514
                                    </tr>';
515
                                $counter++;
516
                            }
517
                            $list2 = learnpath :: get_iv_objectives_array($row['iv_id']);
518
519 View Code Duplication
                            foreach ($list2 as $id => $interaction) {
520
                                if (($counter % 2) == 0) {
521
                                    $oddclass = 'row_odd';
522
                                } else {
523
                                    $oddclass = 'row_even';
524
                                }
525
                                $output .= '<tr class="'.$oddclass.'">
526
                                        <td></td>
527
                                        <td></td>
528
                                        <td></td>
529
                                        <td>' . $interaction['order_id'] . '</td>
530
                                        <td colspan="2">' . $interaction['objective_id'] . '</td>
531
                                        <td colspan="2">' . $interaction['status'] .'</td>
532
                                        <td>' . $interaction['score_raw'] . '</td>
533
                                        <td>' . $interaction['score_max'] . '</td>
534
                                        <td>' . $interaction['score_min'] . '</td>
535
                                        '.$action.'
536
                                     </tr>';
537
                                $counter++;
538
                            }
539
                        }
540
                    } while ($row = Database :: fetch_array($result));
541
                } elseif ($num > 0) {
542
                    // Not extended.
543
544
                    $row = Database :: fetch_array($result, 'ASSOC');
545
                    $my_id = $row['myid'];
546
                    $my_lp_id = $row['mylpid'];
547
                    $my_lp_view_id = $row['mylpviewid'];
548
                    $my_path = $row['path'];
549
550
                    $result_disabled_ext_all = false;
551
552 View Code Duplication
                    if ($row['item_type'] == 'quiz') {
553
                        // Check results_disabled in quiz table.
554
                        $my_path = Database::escape_string($my_path);
555
                        $sql = "SELECT results_disabled
556
                                FROM $TBL_QUIZ
557
                                WHERE c_id = $course_id AND id ='" . $my_path . "'";
558
                        $res_result_disabled = Database::query($sql);
559
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
560
561
                        if (Database::num_rows($res_result_disabled) > 0 &&
562
                            (int) $row_result_disabled[0] === 1
563
                        ) {
564
                            $result_disabled_ext_all = true;
565
                        }
566
                    }
567
568
                    // Check if there are interactions below
569
                    $extend_this_attempt = 0;
570
571
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
572
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
573
574
                    $extend_attempt_link = '';
575
                    if ($inter_num > 0 || $objec_num > 0) {
576 View Code Duplication
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
577
                            // The extend button for this attempt has been clicked.
578
                            $extend_this_attempt = 1;
579
                            $extend_attempt_link = Display::url(
580
                                Display::return_icon('visible.gif', get_lang('HideAttemptView')),
581
                                api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&fold_attempt_id=' . $row['iv_id'] . $url_suffix
582
                            );
583
                        } else {
584
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
585
                            // The extend button for this attempt has not been clicked.
586
                            $extend_attempt_link = Display::url(
587
                                Display::return_icon('invisible.gif', get_lang('ExtendAttemptView')),
588
                                api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&extend_attempt_id=' . $row['iv_id'] . $url_suffix
589
                            );
590
                        }
591
                    }
592
593
                    if (($counter % 2) == 0) {
594
                        $oddclass = 'row_odd';
595
                    } else {
596
                        $oddclass = 'row_even';
597
                    }
598
599
                    $extend_link = '';
600
                    if ($inter_num > 1) {
601
                        $extend_link = Display::url(
602
                            Display::return_icon('invisible.gif', get_lang('ExtendAttemptView')),
603
                            api_get_self() . '?action=stats&extend_id=' . $my_item_id . '&extend_attempt_id=' . $row['iv_id'] . $url_suffix
604
                        );
605
                    }
606
607
                    $lesson_status = $row['mystatus'];
608
                    $score = $row['myscore'];
609
                    $subtotal_time = $row['mytime'];
610
611
                    while ($tmp_row = Database :: fetch_array($result)) {
612
                        $subtotal_time += $tmp_row['mytime'];
613
                    }
614
615
                    $title = $row['mytitle'];
616
617
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
618
619
                    $sql = 'SELECT * FROM ' . $tbl_stats_exercices . '
620
                             WHERE
621
                                exe_exo_id="' . $row['path'] . '" AND
622
                                exe_user_id="' . $user_id . '" AND
623
                                orig_lp_id = "' . $lp_id . '" AND
624
                                orig_lp_item_id = "' . $row['myid'] . '" AND
625
                                c_id = ' . $course_id . ' AND
626
                                status <> "incomplete" AND
627
                                session_id = ' . $session_id . '
628
                             ORDER BY exe_date DESC
629
                             LIMIT 1';
630
631
                    $resultLastAttempt = Database::query($sql);
632
                    $num = Database :: num_rows($resultLastAttempt);
633
                    $id_last_attempt = null;
634
                    if ($num > 0) {
635
                        while ($rowLA = Database :: fetch_array($resultLastAttempt)) {
636
                            $id_last_attempt = $rowLA['exe_id'];
637
                        }
638
                    }
639
640
                    if ($score == 0) {
641
                        $maxscore = $row['mymaxscore'];
642
                    } else {
643
                        if ($row['item_type'] == 'sco') {
644 View Code Duplication
                            if (!empty($row['myviewmaxscore']) and $row['myviewmaxscore'] > 0) {
645
                                $maxscore = $row['myviewmaxscore'];
646
                            } elseif ($row['myviewmaxscore'] === '') {
647
                                $maxscore = 0;
648
                            } else {
649
                                $maxscore = $row['mymaxscore'];
650
                            }
651
                        } else {
652
                            if ($row['item_type'] == 'quiz') {
653
                                // Get score and total time from last attempt of a exercise en lp.
654
                                $sql = "SELECT score
655
                                        FROM $TBL_LP_ITEM_VIEW
656
                                        WHERE
657
                                            c_id = $course_id AND
658
                                            lp_item_id = '" . (int) $my_id . "' AND
659
                                            lp_view_id = '" . (int) $my_lp_view_id . "'
660
                                        ORDER BY view_count DESC limit 1";
661
                                $res_score = Database::query($sql);
662
                                $row_score = Database::fetch_array($res_score);
663
664
                                $sql = "SELECT SUM(total_time) as total_time
665
                                        FROM $TBL_LP_ITEM_VIEW
666
                                        WHERE
667
                                            c_id = $course_id AND
668
                                            lp_item_id = '" . (int) $my_id . "' AND
669
                                            lp_view_id = '" . (int) $my_lp_view_id . "'";
670
                                $res_time = Database::query($sql);
671
                                $row_time = Database::fetch_array($res_time);
672
673
                                if (Database::num_rows($res_score) > 0 &&
674
                                    Database::num_rows($res_time) > 0
675
                                ) {
676
                                    $score = (float) $row_score['score'];
677
                                    $subtotal_time = (int) $row_time['total_time'];
678
                                } else {
679
                                    $score = 0;
680
                                    $subtotal_time = 0;
681
                                }
682
                                // Selecting the max score from an attempt.
683
                                $sql = "SELECT SUM(t.ponderation) as maxscore
684
                                        FROM (
685
                                            SELECT DISTINCT
686
                                                question_id, marks, ponderation
687
                                            FROM $tbl_stats_attempts as at
688
                                            INNER JOIN  $tbl_quiz_questions as q
689
                                            ON (q.id = at.question_id AND q.c_id = $course_id)
690
                                            WHERE exe_id ='$id_last_attempt'
691
                                        ) as t";
692
693
                                $result = Database::query($sql);
694
                                $row_max_score = Database :: fetch_array($result);
695
                                $maxscore = $row_max_score['maxscore'];
696
                            } else {
697
                                $maxscore = $row['mymaxscore'];
698
                            }
699
                        }
700
                    }
701
702
                    $time_for_total = $subtotal_time;
703
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
704
                    if (empty($title)) {
705
                        $title = rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
706
                    }
707
708
                    $action = null;
709
                    if ($type == 'classic') {
710
                        $action = '<td></td>';
711
                    }
712
713
                    if (in_array($row['item_type'], $chapterTypes)) {
714
                        $title = Security::remove_XSS($title);
715
                        $output .= '<tr class="'.$oddclass.'">
716
                                <td>'.$extend_link.'</td>
717
                                <td colspan="4">
718
                                <h4>'.$title.'</h4>
719
                                </td>
720
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
721
                                <td colspan="2"></td>
722
                                <td colspan="2"></td>
723
                                '.$action.'
724
                            </tr>';
725
                    } else {
726
                        $correct_test_link = '-';
727
                        if ($row['item_type'] == 'quiz') {
728
                            $my_url_suffix = '&course=' . $courseCode . '&student_id=' . $user_id . '&lp_id=' . intval($row['mylpid']) . '&origin=' . $origin;
729
                            $sql = 'SELECT * FROM ' . $tbl_stats_exercices . '
730
                                     WHERE
731
                                        exe_exo_id="' . $row['path'] . '" AND
732
                                        exe_user_id="' . $user_id . '" AND
733
                                        orig_lp_id = "' . $lp_id . '" AND
734
                                        orig_lp_item_id = "' . $row['myid'] . '" AND
735
                                        c_id = ' . $course_id . ' AND
736
                                        status <> "incomplete" AND
737
                                        session_id = ' . $session_id . '
738
                                     ORDER BY exe_date DESC ';
739
740
                            $resultLastAttempt = Database::query($sql);
741
                            $num = Database :: num_rows($resultLastAttempt);
742
                            if ($num > 0) {
743
                                if ($extendedAttempt == 1 &&
744
                                    $lp_id == $my_lp_id &&
745
                                    $lp_item_id == $my_id
746
                                ) {
747
                                    $correct_test_link = Display::url(
748
                                        Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
749
                                        api_get_self() . '?action=stats' . $my_url_suffix . '&session_id=' . $session_id . '&lp_item_id=' . $my_id
750
                                    );
751 View Code Duplication
                                } else {
752
                                    $correct_test_link = Display::url(
753
                                        Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttemptsByExercise')),
754
                                        api_get_self() . '?action=stats&extend_attempt=1' . $my_url_suffix . '&session_id=' . $session_id . '&lp_item_id=' . $my_id
755
                                    );
756
                                }
757
                            }
758
                        }
759
760
                        $title = Security::remove_XSS($title);
761
762
                        $action = null;
763
                        if ($type == 'classic') {
764
                            $action =  '<td>' . $correct_test_link . '</td>';
765
                        }
766
767
                        if ($lp_id == $my_lp_id && false) {
768
769
                            $output .= '<tr class =' . $oddclass . '>
770
                                    <td>' . $extend_link . '</td>
771
                                    <td colspan="4">' . $title . '</td>
772
                                    <td colspan="2">&nbsp;</td>
773
                                    <td colspan="2">&nbsp;</td>
774
                                    <td colspan="2">&nbsp;</td>
775
                                    '.$action.'
776
                                </tr>';
777
                            $output .= '</tr>';
778
                        } else {
779
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
780
                                $output .= "<tr class='$oddclass'>";
781
                            } else {
782
                                $output .= "<tr class='$oddclass'>";
783
                            }
784
785
                            $scoreItem = null;
786
                            if ($row['item_type'] == 'quiz') {
787
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
788
                                    $scoreItem .= Display::return_icon(
789
                                        'invisible.gif',
790
                                        get_lang('ResultsHiddenByExerciseSetting')
791
                                    );
792
                                } else {
793
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
794
                                }
795
                            } else {
796
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score . '/' . $maxscore);
797
                            }
798
799
                            $output .= '
800
                                <td>'.$extend_link.'</td>
801
                                <td colspan="4">' . $title . '</td>
802
                                <td colspan="2">' . learnpathitem::humanize_status($lesson_status) .'</td>
803
                                <td colspan="2">'.$scoreItem.'</td>
804
                                <td colspan="2">'.$time.'</td>
805
                                '.$action.'
806
                             ';
807
                            $output .= '</tr>';
808
                        }
809
810
                        if (!empty($export_csv)) {
811
                            $temp = array();
812
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
813
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
814
815 View Code Duplication
                            if ($row['item_type'] == 'quiz') {
816
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
817
                                    $temp[] = '/';
818
                                } else {
819
                                    $temp[] = ($score == 0 ? '0/' . $maxscore : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
820
                                }
821
                            } else {
822
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score . '/' . float_format($maxscore, 1)));
823
                            }
824
                            $temp[] = $time;
825
                            $csv_content[] = $temp;
826
                        }
827
                    }
828
829
                    $counter++;
830
831
                    $action = null;
832
                    if ($type == 'classic') {
833
                        $action =  '<td></td>';
834
                    }
835
836
                    if ($extend_this_attempt || $extend_all) {
837
                        $list1 = learnpath :: get_iv_interactions_array($row['iv_id']);
838
                        foreach ($list1 as $id => $interaction) {
839
                            if (($counter % 2) == 0) {
840
                                $oddclass = 'row_odd';
841
                            } else {
842
                                $oddclass = 'row_even';
843
                            }
844
                            $output .= '<tr class="'.$oddclass.'">
845
                                    <td></td>
846
                                    <td></td>
847
                                    <td></td>
848
                                    <td>'.$interaction['order_id'].'</td>
849
                                    <td>'.$interaction['id'].'</td>
850
                                    <td colspan="2">' . $interaction['type'].'</td>
851
                                    <td>'.urldecode($interaction['student_response']).'</td>
852
                                    <td>'.$interaction['result'].'</td>
853
                                    <td>'.$interaction['latency'].'</td>
854
                                    <td>'.$interaction['time'].'</td>
855
                                    '.$action.'
856
                               </tr>';
857
                            $counter++;
858
                        }
859
                        $list2 = learnpath :: get_iv_objectives_array($row['iv_id']);
860 View Code Duplication
                        foreach ($list2 as $id => $interaction) {
861
                            if (($counter % 2) == 0) {
862
                                $oddclass = 'row_odd';
863
                            } else {
864
                                $oddclass = 'row_even';
865
                            }
866
                            $output .= '<tr class="'.$oddclass.'">
867
                                    <td></td>
868
                                    <td></td>
869
                                    <td></td>
870
                                    <td>' . $interaction['order_id'] . '</td>
871
                                    <td colspan="2">'.$interaction['objective_id'] . '</td>
872
                                    <td colspan="2">' . $interaction['status'] . '</td>
873
                                    <td>' . $interaction['score_raw'].'</td>
874
                                    <td>' . $interaction['score_max'] .'</td>
875
                                    <td>' . $interaction['score_min'].'</td>
876
                                    '.$action.'
877
                               </tr>';
878
                            $counter++;
879
                        }
880
                    }
881
882
                    // Attempts listing by exercise.
883
                    if ($lp_id == $my_lp_id && $lp_item_id== $my_id && $extendedAttempt) {
884
                        // Get attempts of a exercise.
885
                        if (!empty($lp_id) &&
886
                            !empty($lp_item_id) &&
887
                            $row['item_type'] === 'quiz'
888
                        ) {
889
                            $sql = "SELECT path FROM $TBL_LP_ITEM
890
                                    WHERE
891
                                        c_id = $course_id AND
892
                                        id = '$lp_item_id' AND
893
                                        lp_id = '$lp_id'";
894
                            $res_path = Database::query($sql);
895
                            $row_path = Database::fetch_array($res_path);
896
897
                            if (Database::num_rows($res_path) > 0) {
898
                                $sql = 'SELECT * FROM ' . $tbl_stats_exercices . '
899
                                        WHERE
900
                                            exe_exo_id="' . (int) $row_path['path'] . '" AND
901
                                            status <> "incomplete" AND
902
                                            exe_user_id="' . $user_id . '" AND
903
                                            orig_lp_id = "' . (int) $lp_id . '" AND
904
                                            orig_lp_item_id = "' . (int) $lp_item_id . '" AND
905
                                            c_id = ' . $course_id . '  AND
906
                                            session_id = ' . $session_id . '
907
                                        ORDER BY exe_date';
908
                                $res_attempts = Database::query($sql);
909
                                $num_attempts = Database::num_rows($res_attempts);
910
                                if ($num_attempts > 0) {
911
                                    $n = 1;
912
                                    while ($row_attempts = Database :: fetch_array($res_attempts)) {
913
                                        $my_score = $row_attempts['exe_result'];
914
                                        $my_maxscore = $row_attempts['exe_weighting'];
915
                                        $my_exe_id = $row_attempts['exe_id'];
916
                                        $my_orig_lp = $row_attempts['orig_lp_id'];
917
                                        $my_orig_lp_item = $row_attempts['orig_lp_item_id'];
918
                                        $my_exo_exe_id = $row_attempts['exe_exo_id'];
919
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
920
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
921
                                        if ($mktime_start_date && $mktime_exe_date) {
922
                                            $mytime = ((int) $mktime_exe_date - (int) $mktime_start_date);
923
                                            $time_attemp = learnpathItem :: getScormTimeFromParameter('js', $mytime);
924
                                            $time_attemp = str_replace('NaN', '00' . $h . '00\'00"', $time_attemp);
925
                                        } else {
926
                                            $time_attemp = ' - ';
927
                                        }
928
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
929
                                            $view_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
930
                                        } else {
931
                                            // Show only float when need it
932
                                            if ($my_score == 0) {
933
                                                $view_score = ExerciseLib::show_score(0, $my_maxscore, false);
934
                                            } else {
935
                                                if ($my_maxscore == 0) {
936
                                                    $view_score = $my_score;
937
                                                } else {
938
                                                    $view_score = ExerciseLib::show_score($my_score, $my_maxscore, false);
939
                                                }
940
                                            }
941
                                        }
942
                                        $my_lesson_status = $row_attempts['status'];
943
944
                                        if ($my_lesson_status == '') {
945
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
946
                                        } elseif ($my_lesson_status == 'incomplete') {
947
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
948
                                        }
949
950
                                        $output .= '<tr class="' . $oddclass . '" >
951
                                        <td></td>
952
                                        <td>' . $extend_attempt_link . '</td>
953
                                        <td colspan="3">' . get_lang('Attempt').' '. $n.'</td>
954
                                        <td colspan="2">' . $my_lesson_status . '</td>
955
                                        <td colspan="2">'.$view_score . '</td>
956
                                        <td colspan="2">'.$time_attemp . '</td>';
957
                                        if ($action == 'classic') {
958
                                            if ($origin != 'tracking') {
959 View Code Duplication
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
960
                                                    $output .= '<td>
961
                                                            <img src="' . Display::returnIconPath('quiz_na.gif').'" alt="' . get_lang('ShowAttempt') . '" title="' . get_lang('ShowAttempt') . '">
962
                                                            </td>';
963
                                                } else {
964
                                                    $output .= '<td>
965
                                                            <a href="../exercice/exercise_show.php?origin=' . $origin . '&id=' . $my_exe_id . '&cidReq=' . $courseCode . '" target="_parent">
966
                                                            <img src="' . Display::returnIconPath('quiz.gif').'" alt="' . get_lang('ShowAttempt') . '" title="' . get_lang('ShowAttempt') . '">
967
                                                            </a></td>';
968
                                                }
969 View Code Duplication
                                            } else {
970
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
971
                                                    $output .= '<td>
972
                                                                <img src="' . Display::returnIconPath('quiz_na.gif').'" alt="' . get_lang('ShowAndQualifyAttempt') . '" title="' . get_lang('ShowAndQualifyAttempt') . '"></td>';
973
                                                } else {
974
                                                    $output .= '<td>
975
                                                                    <a href="../exercice/exercise_show.php?cidReq=' . $courseCode . '&origin=correct_exercise_in_lp&id=' . $my_exe_id . '" target="_parent">
976
                                                                    <img src="' . Display::returnIconPath('quiz.gif').'" alt="' . get_lang('ShowAndQualifyAttempt') . '" title="' . get_lang('ShowAndQualifyAttempt') . '"></a></td>';
977
                                                }
978
                                            }
979
                                        }
980
                                        $output .= '</tr>';
981
                                        $n++;
982
                                    }
983
                                }
984
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
985
                            }
986
                        }
987
                    }
988
                }
989
990
                $total_time += $time_for_total;
991
                // QUIZZ IN LP
992
                $a_my_id = array();
993
                if (!empty($my_lp_id)) {
994
                    $a_my_id[] = $my_lp_id;
995
                }
996
            }
997
        }
998
999
        // NOT Extend all "left green cross"
1000
        if (!empty($a_my_id)) {
1001
            if ($extendedAttempt) {
1002
                // "Right green cross" extended
1003
                $total_score = self::get_avg_student_score(
1004
                    $user_id,
1005
                    $course_id,
1006
                    $a_my_id,
1007
                    $session_id,
1008
                    false,
1009
                    false
1010
                );
1011
            } else {
1012
                // "Left green cross" extended
1013
                $total_score = self::get_avg_student_score(
1014
                    $user_id,
1015
                    $course_id,
1016
                    $a_my_id,
1017
                    $session_id,
1018
                    false,
1019
                    true
1020
                );
1021
            }
1022
        } else {
1023
            // Extend all "left green cross"
1024
            $total_score = self::get_avg_student_score(
1025
                $user_id,
1026
                $course_id,
1027
                array($lp_id),
1028
                $session_id,
1029
                false,
1030
                false
1031
            );
1032
        }
1033
1034
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1035
        $total_time = str_replace('NaN', '00' . $h . '00\'00"', $total_time);
1036
1037
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1038
            $final_score = Display::return_icon('invisible.gif', get_lang('ResultsHiddenByExerciseSetting'));
1039
        } else {
1040
            if (is_numeric($total_score)) {
1041
                $final_score = $total_score . '%';
1042
            } else {
1043
                $final_score = $total_score;
1044
            }
1045
        }
1046
1047
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
1048
1049
        if (($counter % 2) == 0) {
1050
            $oddclass = 'row_odd';
1051
        } else {
1052
            $oddclass = 'row_even';
1053
        }
1054
1055
        $action = null;
1056
        if ($type == 'classic') {
1057
            $action =  '<td></td>';
1058
        }
1059
1060
        $output .= '<tr class="'.$oddclass.'">
1061
                <td></td>
1062
                <td colspan="4">
1063
                    <i>' . get_lang('AccomplishedStepsTotal') .'</i>
1064
                </td>
1065
                <td colspan="2">'.$progress.'%</td>
1066
                <td colspan="2">
1067
                    ' . $final_score.'
1068
                </td>
1069
                <td colspan="2">' . $total_time . '</div>
1070
                '.$action.'
1071
           </tr>';
1072
1073
        $output .= '
1074
                    </tbody>
1075
                </table>
1076
            </div>
1077
        ';
1078
1079
        if (!empty($export_csv)) {
1080
            $temp = array(
1081
                '',
1082
                '',
1083
                '',
1084
                ''
1085
            );
1086
            $csv_content[] = $temp;
1087
            $temp = array(
1088
                get_lang('AccomplishedStepsTotal'),
1089
                '',
1090
                $final_score,
1091
                $total_time
1092
            );
1093
            $csv_content[] = $temp;
1094
            ob_end_clean();
1095
            Export :: arrayToCsv($csv_content, 'reporting_learning_path_details');
1096
            exit;
1097
        }
1098
        return $output;
1099
    }
1100
1101
    /**
1102
     * @param int $userId
1103
     *
1104
     * @return array
1105
     */
1106
    public static function getStats($userId)
1107
    {
1108
        $courses = array();
1109
        $assignedCourses = array();
1110
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1111
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1112
                'drh_all',
1113
                $userId,
1114
                false,
1115
                null,
1116
                null,
1117
                null,
1118
                null,
1119
                null,
1120
                null,
1121
                null,
1122
                array(),
1123
                array(),
1124
                STUDENT
1125
            );
1126
            $students = array();
1127
            foreach ($studentList as $studentData) {
0 ignored issues
show
Bug introduced by
The expression $studentList of type array|integer is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1128
                $students[] = $studentData['user_id'];
1129
            }
1130
1131
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1132
                'drh_all',
1133
                $userId,
1134
                false,
1135
                null,
1136
                null,
1137
                null,
1138
                null,
1139
                null,
1140
                null,
1141
                null,
1142
                array(),
1143
                array(),
1144
                STUDENT_BOSS
1145
            );
1146
            $studentBosses = array();
1147
            foreach ($studentBossesList as $studentBossData) {
0 ignored issues
show
Bug introduced by
The expression $studentBossesList of type array|integer is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1148
                $studentBosses[] = $studentBossData['user_id'];
1149
            }
1150
1151
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1152
                'drh_all',
1153
                $userId,
1154
                false,
1155
                null,
1156
                null,
1157
                null,
1158
                null,
1159
                null,
1160
                null,
1161
                null,
1162
                array(),
1163
                array(),
1164
                COURSEMANAGER
1165
            );
1166
            $teachers = array();
1167
            foreach ($teacherList as $teacherData) {
0 ignored issues
show
Bug introduced by
The expression $teacherList of type array|integer is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1168
                $teachers[] = $teacherData['user_id'];
1169
            }
1170
1171
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1172
                'drh_all',
1173
                $userId,
1174
                false,
1175
                null,
1176
                null,
1177
                null,
1178
                null,
1179
                null,
1180
                null,
1181
                null,
1182
                array(),
1183
                array(),
1184
                DRH
1185
            );
1186
1187
            $humanResourcesList = array();
1188
            foreach ($humanResources as $item) {
0 ignored issues
show
Bug introduced by
The expression $humanResources of type array|integer is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1189
                $humanResourcesList[] = $item['user_id'];
1190
            }
1191
1192
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1193
                $userId,
1194
                null,
1195
                null,
1196
                null,
1197
                null,
1198
                null
1199
            );
1200
            foreach ($platformCourses as $course) {
0 ignored issues
show
Bug introduced by
The expression $platformCourses of type integer|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1201
                $courses[$course['code']] = $course['code'];
1202
            }
1203
1204
1205
1206
            $sessions = SessionManager::get_sessions_followed_by_drh($userId);
1207
        } else {
1208
            $studentList = UserManager::getUsersFollowedByUser(
1209
                $userId,
1210
                STUDENT,
1211
                false,
1212
                false,
1213
                false,
1214
                null,
1215
                null,
1216
                null,
1217
                null,
1218
                null,
1219
                null,
1220
                COURSEMANAGER
1221
            );
1222
1223
            $students = array();
1224
            foreach ($studentList as $studentData) {
1225
                $students[] = $studentData['user_id'];
1226
            }
1227
1228
            $studentBossesList = UserManager::getUsersFollowedByUser(
1229
                $userId,
1230
                STUDENT_BOSS,
1231
                false,
1232
                false,
1233
                false,
1234
                null,
1235
                null,
1236
                null,
1237
                null,
1238
                null,
1239
                null,
1240
                COURSEMANAGER
1241
            );
1242
            $studentBosses = array();
1243
            foreach ($studentBossesList as $studentBossData) {
1244
                $studentBosses[] = $studentBossData['user_id'];
1245
            }
1246
1247
            $teacherList = UserManager::getUsersFollowedByUser(
1248
                $userId,
1249
                COURSEMANAGER,
1250
                false,
1251
                false,
1252
                false,
1253
                null,
1254
                null,
1255
                null,
1256
                null,
1257
                null,
1258
                null,
1259
                COURSEMANAGER
1260
            );
1261
1262
            $teachers = array();
1263
            foreach ($teacherList as $teacherData) {
1264
                $teachers[] = $teacherData['user_id'];
1265
            }
1266
1267
            $humanResources = UserManager::getUsersFollowedByUser(
1268
                $userId,
1269
                DRH,
1270
                false,
1271
                false,
1272
                false,
1273
                null,
1274
                null,
1275
                null,
1276
                null,
1277
                null,
1278
                null,
1279
                COURSEMANAGER
1280
            );
1281
1282
            $humanResourcesList = array();
1283
            foreach ($humanResources as $item) {
1284
                $humanResourcesList[] = $item['user_id'];
1285
            }
1286
1287
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1288
                $userId,
1289
                COURSEMANAGER,
1290
                null,
1291
                null,
1292
                null,
1293
                null,
1294
                false,
1295
                null,
1296
                null,
1297
                true
1298
            );
1299
1300
            foreach ($platformCourses as $course) {
1301
                $assignedCourses[$course['code']] = $course['code'];
1302
            }
1303
1304
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1305
                $userId,
1306
                COURSEMANAGER
1307
            );
1308
            foreach ($platformCourses as $course) {
1309
                $courses[$course['code']] = $course['code'];
1310
            }
1311
1312
            $sessions = SessionManager::getSessionsFollowedByUser(
1313
                $userId,
1314
                COURSEMANAGER
1315
            );
1316
        }
1317
1318
        return array(
1319
            'drh' => $humanResourcesList,
1320
            'teachers' => $teachers,
1321
            'students' => $students,
1322
            'studentBosses' => $studentBosses,
1323
            'courses' => $courses,
1324
            'sessions' => $sessions,
1325
            'assignedCourses' => $assignedCourses
1326
        );
1327
    }
1328
1329
    /**
1330
     * Calculates the time spent on the platform by a user
1331
     * @param   int|array User id
1332
     * @param   string type of time filter: 'last_week' or 'custom'
1333
     * @param   string  start date date('Y-m-d H:i:s')
1334
     * @param   string  end date date('Y-m-d H:i:s')
1335
     * @return timestamp $nb_seconds
1336
     */
1337
    public static function get_time_spent_on_the_platform(
1338
        $userId,
1339
        $timeFilter = 'last_7_days',
1340
        $start_date = null,
1341
        $end_date = null
1342
    ) {
1343
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1344
        $condition_time = '';
1345
1346
        if (is_array($userId)) {
1347
            $userList = array_map('intval', $userId);
1348
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1349
        } else {
1350
            $userCondition = " login_user_id = ".intval($userId);
1351
        }
1352
1353
        if (empty($timeFilter)) {
1354
            $timeFilter = 'last_week';
1355
        }
1356
1357
        $today = new DateTime('now', new DateTimeZone('UTC'));
1358
1359
        switch ($timeFilter) {
1360 View Code Duplication
            case 'last_7_days':
1361
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1362
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1363
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1364
                break;
1365 View Code Duplication
            case 'last_30_days':
1366
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1367
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1368
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1369
               break;
1370
            case 'custom':
1371
                if (!empty($start_date) && !empty($end_date)) {
1372
                    $start_date = Database::escape_string($start_date);
1373
                    $end_date = Database::escape_string($end_date);
1374
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1375
                }
1376
                break;
1377
        }
1378
1379
    	$sql = 'SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1380
    	        FROM '.$tbl_track_login.'
1381
                WHERE '.$userCondition.$condition_time;
1382
    	$rs = Database::query($sql);
1383
        $row = Database::fetch_array($rs, 'ASSOC');
1384
        $diff = $row['diff'];
1385
1386
        if ($diff >= 0) {
1387
            return $diff;
1388
        } else {
1389
            return -1;
1390
        }
1391
    }
1392
1393
    /**
1394
     * Calculates the time spent on the course
1395
     * @param integer $user_id
1396
     * @param integer  $courseId
1397
     * @param int Session id (optional)
1398
     *
1399
     * @return int Time in seconds
1400
     */
1401
    public static function get_time_spent_on_the_course($user_id, $courseId, $session_id = 0)
1402
    {
1403
        $courseId = intval($courseId);
1404
    	$session_id  = intval($session_id);
1405
1406
    	$tbl_track_course = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1407
    	if (is_array($user_id)) {
1408
    	    $user_id = array_map('intval', $user_id);
1409
    		$condition_user = " AND user_id IN (".implode(',',$user_id).") ";
1410
    	} else {
1411
    		$user_id = intval($user_id);
1412
    		$condition_user = " AND user_id = $user_id ";
1413
    	}
1414
1415
    	$sql = "SELECT
1416
    	        SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1417
                FROM $tbl_track_course
1418
                WHERE UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) ";
1419
1420
        if ($courseId != 0) {
1421
            $sql .= "AND c_id = '$courseId' ";
1422
        }
1423
1424
        if ($session_id != -1) {
1425
            $sql .= "AND session_id = '$session_id' ";
1426
        }
1427
1428
        $sql .= $condition_user;
1429
        $rs = Database::query($sql);
1430
    	$row = Database::fetch_array($rs);
1431
1432
    	return $row['nb_seconds'];
1433
    }
1434
1435
    /**
1436
     * Get first connection date for a student
1437
     * @param    int $student_id
1438
     *
1439
     * @return    string|bool Date format long without day or false if there are no connections
1440
     */
1441 View Code Duplication
    public static function get_first_connection_date($student_id)
1442
    {
1443
    	$tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1444
    	$sql = 'SELECT login_date
1445
    	        FROM ' . $tbl_track_login . '
1446
                WHERE login_user_id = ' . intval($student_id) . '
1447
                ORDER BY login_date ASC
1448
                LIMIT 0,1';
1449
1450
    	$rs = Database::query($sql);
1451
    	if (Database::num_rows($rs)>0) {
1452
    		if ($first_login_date = Database::result($rs, 0, 0)) {
1453
                return api_convert_and_format_date(
1454
                    $first_login_date,
1455
                    DATE_FORMAT_SHORT,
1456
                    date_default_timezone_get()
1457
                );
1458
    		}
1459
    	}
1460
1461
    	return false;
1462
    }
1463
1464
    /**
1465
     * Get las connection date for a student
1466
     * @param int $student_id
1467
     * @param bool $warning_message Show a warning message (optional)
1468
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1469
     * @return string|int|bool Date format long without day, false if there are no connections or
1470
     * timestamp if parameter $return_timestamp is true
1471
     */
1472
    public static function get_last_connection_date($student_id, $warning_message = false, $return_timestamp = false)
1473
    {
1474
    	$table = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1475
    	$sql = 'SELECT login_date
1476
    	        FROM ' . $table . '
1477
                WHERE login_user_id = ' . intval($student_id) . '
1478
                ORDER BY login_date
1479
                DESC LIMIT 0,1';
1480
1481
    	$rs = Database::query($sql);
1482
    	if (Database::num_rows($rs) > 0) {
1483
    		if ($last_login_date = Database::result($rs, 0, 0)) {
1484
    			$last_login_date = api_get_local_time($last_login_date);
1485
    			if ($return_timestamp) {
1486
    				return api_strtotime($last_login_date,'UTC');
1487
    			} else {
1488
    				if (!$warning_message) {
1489
    					return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1490
    				} else {
1491
    					$timestamp = api_strtotime($last_login_date,'UTC');
1492
    					$currentTimestamp = time();
1493
1494
    					//If the last connection is > than 7 days, the text is red
1495
    					//345600 = 7 days in seconds
1496
    					if ($currentTimestamp - $timestamp > 604800) {
1497
    						return '<span style="color: #F00;">' . api_format_date($last_login_date, DATE_FORMAT_SHORT) . '</span>';
1498
    					} else {
1499
    						return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1500
    					}
1501
    				}
1502
    			}
1503
    		}
1504
    	}
1505
    	return false;
1506
    }
1507
1508
    /**
1509
     * Get las connection date for a student
1510
     * @param array $studentList Student id array
1511
     * @param int $days
1512
     * @param bool $getCount
1513
     * @return int
1514
     */
1515
    public static function getInactiveUsers($studentList, $days, $getCount = true)
1516
    {
1517
        if (empty($studentList)) {
1518
            return 0;
1519
        }
1520
        $days = intval($days);
1521
        $date = api_get_utc_datetime(strtotime($days.' days ago'));
1522
        $studentList = array_map('intval', $studentList);
1523
1524
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1525
        $select = " SELECT login_user_id ";
1526
        if ($getCount) {
1527
            $select = " SELECT count(DISTINCT login_user_id) as count";
1528
        }
1529
        $sql = "$select
1530
                FROM $tbl_track_login
1531
                WHERE
1532
                    login_user_id IN (' ". implode("','", $studentList) . "' ) AND
1533
                    login_date < '$date'
1534
                ";
1535
        $rs = Database::query($sql);
1536 View Code Duplication
        if (Database::num_rows($rs) > 0) {
1537
            if ($getCount) {
1538
                $count = Database::fetch_array($rs);
1539
                return $count['count'];
1540
            }
1541
            return Database::store_result($rs, 'ASSOC');
1542
        }
1543
        return false;
1544
    }
1545
1546
    /**
1547
     * Get first user's connection date on the course
1548
     * @param     int         User id
1549
     * @param    int        $courseId
1550
     * @param    int            Session id (optional, default=0)
1551
     * @return    string|bool    Date with format long without day or false if there is no date
1552
     */
1553
    public static function get_first_connection_date_on_the_course(
1554
        $student_id,
1555
        $courseId,
1556
        $session_id = 0,
1557
        $convert_date = true
1558
    ) {
1559
    	$student_id  = intval($student_id);
1560
        $courseId = intval($courseId);
1561
    	$session_id  = intval($session_id);
1562
1563
    	$tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1564
    	$sql = 'SELECT login_course_date
1565
    	        FROM '.$tbl_track_login.'
1566
                WHERE
1567
                    user_id = '.$student_id.' AND
1568
                    c_id = '.$courseId.' AND
1569
                    session_id = '.$session_id.'
1570
                ORDER BY login_course_date ASC LIMIT 0,1';
1571
    	$rs = Database::query($sql);
1572
    	if (Database::num_rows($rs) > 0) {
1573
    		if ($first_login_date = Database::result($rs, 0, 0)) {
1574
    			if ($convert_date) {
1575
    				return api_convert_and_format_date($first_login_date, DATE_FORMAT_SHORT);
1576
    			} else {
1577
    				return $first_login_date;
1578
    			}
1579
    		}
1580
    	}
1581
1582
    	return false;
1583
    }
1584
1585
    /**
1586
     * Get last user's connection date on the course
1587
     * @param     int         User id
1588
     * @param    array        $courseInfo real_id and code are used
1589
     * @param    int            Session id (optional, default=0)
1590
     * @return    string|bool    Date with format long without day or false if there is no date
1591
     */
1592
    public static function get_last_connection_date_on_the_course(
1593
        $student_id,
1594
        $courseInfo,
1595
        $session_id = 0,
1596
        $convert_date = true
1597
    ) {
1598
    	// protect data
1599
    	$student_id  = intval($student_id);
1600
        $courseId = $courseInfo['real_id'];
1601
    	$session_id  = intval($session_id);
1602
1603
    	$tbl_track_e_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
1604
    	$sql = 'SELECT access_date
1605
                FROM '.$tbl_track_e_access.'
1606
                WHERE   access_user_id = '.$student_id.' AND
1607
                        c_id = "'.$courseId.'" AND
1608
                        access_session_id = '.$session_id.'
1609
                ORDER BY access_date DESC
1610
                LIMIT 0,1';
1611
1612
    	$rs = Database::query($sql);
1613
    	if (Database::num_rows($rs) > 0) {
1614
    		if ($last_login_date = Database::result($rs, 0, 0)) {
1615
                if (empty($last_login_date) || $last_login_date == '0000-00-00 00:00:00') {
1616
                    return false;
1617
                }
1618
                //see #5736
1619
                $last_login_date_timestamp = api_strtotime($last_login_date);
1620
    			$now = time();
1621
    			//If the last connection is > than 7 days, the text is red
1622
    			//345600 = 7 days in seconds
1623
    			if ($now - $last_login_date_timestamp > 604800) {
1624
    				if ($convert_date) {
1625
                        $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
1626
                        $icon = api_is_allowed_to_edit() ?
1627
                            '<a href="'.api_get_path(WEB_CODE_PATH).'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'].'" title="'.get_lang('RemindInactiveUser').'">
1628
                              '.Display::return_icon('messagebox_warning.gif').'
1629
                             </a>'
1630
                            : null;
1631
    					return $icon. Display::label($last_login_date, 'warning');
1632
    				} else {
1633
    					return $last_login_date;
1634
    				}
1635
    			} else {
1636
    				if ($convert_date) {
1637
    					return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
1638
    				} else {
1639
    					return $last_login_date;
1640
    				}
1641
    			}
1642
    		}
1643
    	}
1644
    	return false;
1645
    }
1646
1647
    /**
1648
     * Get count of the connections to the course during a specified period
1649
     * @param   int  $courseId
1650
     * @param   int     Session id (optional)
1651
     * @param   int     Datetime from which to collect data (defaults to 0)
1652
     * @param   int     Datetime to which to collect data (defaults to now)
1653
     * @return  int     count connections
1654
     */
1655
    public static function get_course_connections_count($courseId, $session_id = 0, $start = 0, $stop = null)
1656
    {
1657
        if ($start < 0) {
1658
    		$start = 0;
1659
    	}
1660
    	if (!isset($stop) or ($stop < 0)) {
1661
    		$stop = api_get_utc_datetime();
1662
    	}
1663
1664
        // Given we're storing in cache, round the start and end times
1665
        // to the lower minute
1666
        $roundedStart = substr($start, 0, -2) . '00';
1667
        $roundedStop = substr($stop, 0, -2) . '00';
1668
        $roundedStart = Database::escape_string($roundedStart);
1669
        $roundedStop = Database::escape_string($roundedStop);
1670
1671
    	$month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
1672
1673
        $courseId = intval($courseId);
1674
    	$session_id  = intval($session_id);
1675
    	$count = 0;
1676
        $tbl_track_e_course_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1677
        $sql =  "SELECT count(*) as count_connections
1678
                FROM $tbl_track_e_course_access
1679
                WHERE
1680
                    c_id = $courseId AND
1681
                    session_id = $session_id
1682
                    $month_filter";
1683
1684
        //This query can be very slow (several seconds on an indexed table
1685
        // with 14M rows). As such, we'll try to use APCU if it is
1686
        // available to store the resulting value for a few seconds
1687
        $cacheEnabled = function_exists('apcu_exists');
1688
        if ($cacheEnabled) {
1689
            $apc = apcu_cache_info(true);
1690
            $apc_end = $apc['start_time'] + $apc['ttl'];
1691
1692
            $apc_var = 'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
1693
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
1694
                apcu_fetch($apc_var) > 0
1695
            ) {
1696
                $count = apcu_fetch($apc_var);
1697
            } else {
1698
                $rs = Database::query($sql);
1699
                if (Database::num_rows($rs)>0) {
1700
                    $row = Database::fetch_object($rs);
1701
                    $count = $row->count_connections;
1702
                }
1703
                apcu_clear_cache();
1704
                apcu_store($apc_var, $count, 60);
1705
            }
1706
        } else {
1707
            $rs = Database::query($sql);
1708
            if (Database::num_rows($rs)>0) {
1709
                $row = Database::fetch_object($rs);
1710
                $count = $row->count_connections;
1711
            }
1712
        }
1713
1714
    	return $count;
1715
    }
1716
1717
    /**
1718
     * Get count courses per student
1719
     * @param     int        Student id
1720
     * @param    bool    Include sessions (optional)
1721
     * @return  int        count courses
1722
     */
1723
    public static function count_course_per_student($user_id, $include_sessions = true)
1724
    {
1725
    	$user_id = intval($user_id);
1726
    	$tbl_course_rel_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
1727
    	$tbl_session_course_rel_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1728
1729
    	$sql = 'SELECT DISTINCT c_id
1730
                FROM ' . $tbl_course_rel_user . '
1731
                WHERE user_id = ' . $user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
1732
    	$rs = Database::query($sql);
1733
    	$nb_courses = Database::num_rows($rs);
1734
1735
    	if ($include_sessions) {
1736
    		$sql = 'SELECT DISTINCT c_id
1737
                    FROM ' . $tbl_session_course_rel_user . '
1738
                    WHERE user_id = ' . $user_id;
1739
    		$rs = Database::query($sql);
1740
    		$nb_courses += Database::num_rows($rs);
1741
    	}
1742
1743
    	return $nb_courses;
1744
    }
1745
1746
    /**
1747
     * Gets the score average from all tests in a course by student
1748
     *
1749
     * @param $student_id
1750
     * @param $course_code
1751
     * @param int $exercise_id
1752
     * @param null $session_id
1753
     * @param int $active_filter    2 for consider all tests
1754
     *                              1 for active <> -1
1755
     *                              0 for active <> 0
1756
     * @param int $into_lp  1 for all exercises
1757
     *                      0 for without LP
1758
     * @internal param \Student $mixed id
1759
     * @internal param \Course $string code
1760
     * @internal param \Exercise $int id (optional), filtered by exercise
1761
     * @internal param \Session $int id (optional), if param $session_id is null
1762
     * it'll return results including sessions, 0 = session is not filtered
1763
     * @return   string    value (number %) Which represents a round integer about the score average.
1764
     */
1765
    public static function get_avg_student_exercise_score(
1766
        $student_id,
1767
        $course_code,
1768
        $exercise_id = 0,
1769
        $session_id = null,
1770
        $active_filter = 1,
1771
        $into_lp = 0
1772
    ) {
1773
        $course_code = Database::escape_string($course_code);
1774
    	$course_info = api_get_course_info($course_code);
1775
    	if (!empty($course_info)) {
1776
    		// table definition
1777
    		$tbl_course_quiz     = Database::get_course_table(TABLE_QUIZ_TEST);
1778
    		$tbl_stats_exercise  = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1779
1780
    		// Compose a filter based on optional exercise given
1781
    		$condition_quiz = "";
1782
    		if (!empty($exercise_id)) {
1783
    			$exercise_id = intval($exercise_id);
1784
    			$condition_quiz =" AND id = $exercise_id ";
1785
    		}
1786
1787
    		// Compose a filter based on optional session id given
1788
    		$condition_session = "";
1789
    		if (isset($session_id)) {
1790
    			$session_id = intval($session_id);
1791
    			$condition_session = " AND session_id = $session_id ";
1792
    		}
1793
            if ($active_filter == 1) {
1794
                $condition_active = 'AND active <> -1';
1795
            } elseif ($active_filter == 0) {
1796
                $condition_active = 'AND active <> 0';
1797
            } else {
1798
                $condition_active = '';
1799
            }
1800
            $condition_into_lp = '';
1801
            $select_lp_id = '';
1802
            if ($into_lp == 0) {
1803
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
1804
            } else {
1805
                $select_lp_id = ', orig_lp_id as lp_id ';
1806
            }
1807
1808
    		$sql = "SELECT count(id) FROM $tbl_course_quiz
1809
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
1810
    		$count_quiz = Database::fetch_row(Database::query($sql));
0 ignored issues
show
Bug introduced by
It seems like \Database::query($sql) can be null; however, fetch_row() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1811
1812
    		if (!empty($count_quiz[0]) && !empty($student_id)) {
1813
    			if (is_array($student_id)) {
1814
                    $student_id = array_map('intval', $student_id);
1815
    				$condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
1816
    			} else {
1817
                    $student_id = intval($student_id);
1818
    				$condition_user = " AND exe_user_id = '$student_id' ";
1819
    			}
1820
1821 View Code Duplication
    			if (empty($exercise_id)) {
1822
    				$sql = "SELECT id FROM $tbl_course_quiz
1823
    						WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
1824
                    $result = Database::query($sql);
1825
                    $exercise_list = array();
1826
    				$exercise_id = null;
1827
                    if (Database::num_rows($result)) {
1828
                        while ($row = Database::fetch_array($result)) {
1829
                            $exercise_list[] = $row['id'];
1830
                        }
1831
                    }
1832
                    if (!empty($exercise_list)) {
1833
                        $exercise_id = implode("','",$exercise_list);
1834
                    }
1835
    			}
1836
1837
    			$count_quiz = Database::fetch_row(Database::query($sql));
0 ignored issues
show
Bug introduced by
It seems like \Database::query($sql) can be null; however, fetch_row() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1838
1839
    			$sql = "SELECT
1840
    			        SUM(exe_result/exe_weighting*100) as avg_score,
1841
    			        COUNT(*) as num_attempts
1842
    			        $select_lp_id
1843
                        FROM $tbl_stats_exercise
1844
                        WHERE
1845
                            exe_exo_id IN ('".$exercise_id."')
1846
                            $condition_user AND
1847
                            status = '' AND
1848
                            c_id = {$course_info['real_id']}
1849
                            $condition_session
1850
                            $condition_into_lp
1851
                        ORDER BY exe_date DESC";
1852
1853
    			$res = Database::query($sql);
1854
    			$row = Database::fetch_array($res);
1855
    			$quiz_avg_score = null;
1856
1857
    			if (!empty($row['avg_score'])) {
1858
    				$quiz_avg_score = round($row['avg_score'],2);
1859
    			}
1860
1861
    			if(!empty($row['num_attempts'])) {
1862
    				$quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
1863
    			}
1864
    			if (is_array($student_id)) {
1865
    				$quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
1866
    			}
1867
                if ($into_lp == 0) {
1868
                    return $quiz_avg_score;
1869
                } else {
1870
                    if (!empty($row['lp_id'])) {
1871
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
1872
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
1873
                        $sql = "SELECT lp.name
1874
                                FROM $tbl_lp as lp, $tbl_course as c
1875
                                WHERE
1876
                                    c.code = '$course_code' AND
1877
                                    lp.id = ".$row['lp_id']." AND
1878
                                    lp.c_id = c.id
1879
                                LIMIT 1;
1880
                        ";
1881
                        $result = Database::query($sql);
1882
                        $row_lp = Database::fetch_row($result);
1883
                        $lp_name = $row_lp[0];
1884
                        return array($quiz_avg_score, $lp_name);
1885
                    } else {
1886
                        return array($quiz_avg_score, null);
1887
                    }
1888
                }
1889
    		}
1890
    	}
1891
    	return null;
1892
    }
1893
1894
    /**
1895
     * Get count student's exercise COMPLETED attempts
1896
     * @param int $student_id
1897
     * @param int $courseId
1898
     * @param int $exercise_id
1899
     * @param int $lp_id
1900
     * @param int $lp_item_id
1901
     * @param int $session_id
1902
     * @param int $find_all_lp  0 = just LP specified
1903
     *                          1 = LP specified or whitout LP,
1904
     *                          2 = all rows
1905
     * @internal param \Student $int id
1906
     * @internal param \Course $string code
1907
     * @internal param \Exercise $int id
1908
     * @internal param \Learning $int path id (optional),
1909
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
1910
     * @internal param \Learning $int path item id (optional),
1911
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
1912
     * @return  int     count of attempts
1913
     */
1914
    public static function count_student_exercise_attempts(
1915
        $student_id,
1916
        $courseId,
1917
        $exercise_id,
1918
        $lp_id = 0,
1919
        $lp_item_id = 0,
1920
        $session_id = 0,
1921
        $find_all_lp = 0
1922
    ) {
1923
        $courseId = intval($courseId);
1924
    	$student_id  = intval($student_id);
1925
    	$exercise_id = intval($exercise_id);
1926
    	$session_id  = intval($session_id);
1927
1928
    	$lp_id = intval($lp_id);
1929
        $lp_item_id = intval($lp_item_id);
1930
    	$tbl_stats_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1931
1932
    	$sql = "SELECT COUNT(ex.exe_id) as essais FROM $tbl_stats_exercises AS ex
1933
                WHERE  ex.c_id = $courseId
1934
                AND ex.exe_exo_id = $exercise_id
1935
                AND status = ''
1936
                AND exe_user_id= $student_id
1937
                AND session_id = $session_id ";
1938
1939
        if ($find_all_lp == 1) {
1940
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
1941
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
1942
        } elseif ($find_all_lp == 0) {
1943
            $sql .= "AND orig_lp_id = $lp_id
1944
                AND orig_lp_item_id = $lp_item_id";
1945
        }
1946
1947
    	$rs = Database::query($sql);
1948
    	$row = Database::fetch_row($rs);
1949
    	$count_attempts = $row[0];
1950
1951
    	return $count_attempts;
1952
    }
1953
1954
    /**
1955
     * Get count student's exercise progress
1956
     *
1957
     * @param array  $exercise_list
1958
     * @param int    $user_id
1959
     * @param int    $courseId
1960
     * @param int    $session_id
1961
    */
1962
    public static function get_exercise_student_progress($exercise_list, $user_id, $courseId, $session_id)
1963
    {
1964
        $courseId = intval($courseId);
1965
        $user_id = intval($user_id);
1966
        $session_id = intval($session_id);
1967
1968
        if (empty($exercise_list)) {
1969
            return '0%';
1970
        }
1971
        $tbl_stats_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1972
        $exercise_list = array_keys($exercise_list);
1973
        $exercise_list = array_map('intval', $exercise_list);
1974
1975
        $exercise_list_imploded = implode("' ,'", $exercise_list);
1976
1977
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
1978
                FROM $tbl_stats_exercises AS ex
1979
                WHERE
1980
                    ex.c_id = $courseId AND
1981
                    ex.session_id  = $session_id AND
1982
                    ex.exe_user_id = $user_id AND
1983
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
1984
1985
        $rs = Database::query($sql);
1986
        $count = 0;
1987
        if ($rs) {
1988
            $row = Database::fetch_row($rs);
1989
            $count = $row[0];
1990
        }
1991
        $count = ($count != 0 ) ? 100*round(intval($count)/count($exercise_list), 2) .'%' : '0%';
1992
        return $count;
1993
    }
1994
1995
    /**
1996
     * @param array $exercise_list
1997
     * @param int $user_id
1998
     * @param int $courseId
1999
     * @param int $session_id
2000
     * @return string
2001
     */
2002 View Code Duplication
    public static function get_exercise_student_average_best_attempt($exercise_list, $user_id, $courseId, $session_id)
2003
    {
2004
        $result = 0;
2005
        if (!empty($exercise_list)) {
2006
            foreach ($exercise_list as $exercise_data) {
2007
                $exercise_id = $exercise_data['id'];
2008
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2009
                    $user_id,
2010
                    $exercise_id,
2011
                    $courseId,
2012
                    $session_id
2013
                );
2014
2015
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
2016
                    $result += $best_attempt['exe_result']/$best_attempt['exe_weighting'];
2017
                }
2018
            }
2019
            $result = $result / count($exercise_list);
2020
            $result = round($result, 2) * 100;
2021
        }
2022
2023
        return $result.'%';
2024
    }
2025
2026
    /**
2027
     * get teacher progress by course and session
2028
     * @param int course id
2029
     * @param int session id
2030
     * @return array
2031
     */
2032
    static function get_teachers_progress_by_course($courseId, $sessionId)
2033
    {
2034
        $course = api_get_course_info_by_id($courseId);
2035
        $sessionId = intval($sessionId);
2036
        $courseId = intval($courseId);
2037
2038
        $sessionCourseUserTable = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2039
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
2040
2041
        //get teachers
2042
        $sql = "SELECT scu.session_id, scu.user_id, s.name
2043
                FROM $sessionCourseUserTable scu, $sessionTable s
2044
                WHERE
2045
                    scu.session_id = s.id
2046
                    AND scu.status = 2
2047
                    AND scu.visibility = 1
2048
                    AND scu.c_id = '%s'
2049
                    AND scu.session_id = %s";
2050
        $query = sprintf($sql, intval($courseId), $sessionId);
2051
        $rs = Database::query($query);
2052
        $teachers = array();
2053
        while ($teacher = Database::fetch_array($rs,'ASSOC')) {
2054
            $teachers[] = $teacher;
2055
        }
2056
        $data = array();
2057
        foreach ($teachers as $teacher) {
2058
            //total documents added
2059
            $sql = "SELECT count(*) as total
2060
                    FROM c_item_property
2061
                    WHERE lastedit_type = 'DocumentAdded'
2062
                    AND c_id = %s
2063
                    AND insert_user_id = %s
2064
                    AND session_id = %s";
2065
            $query = sprintf($sql,
2066
                $courseId,
2067
                $teacher['user_id'],
2068
                $teacher['session_id']
2069
            );
2070
2071
            $rs = Database::query($query);
2072
            $totalDocuments = 0;
2073
            if ($rs) {
2074
                $row = Database::fetch_row($rs);
2075
                $totalDocuments = $row[0];
2076
            }
2077
            //total links added
2078
            $sql = "SELECT count(*) as total
2079
                    FROM c_item_property
2080
                    WHERE lastedit_type = 'LinkAdded'
2081
                    AND c_id = %s
2082
                    AND insert_user_id = %s
2083
                    AND session_id = %s";
2084
            $query = sprintf($sql,
2085
                $courseId,
2086
                $teacher['user_id'],
2087
                $teacher['session_id']
2088
            );
2089
            $rs = Database::query($query);
2090
2091
            $totalLinks = 0;
2092
            if ($rs) {
2093
                $row = Database::fetch_row($rs);
2094
                $totalLinks = $row[0];
2095
            }
2096
            //total forums added
2097
            $sql = "SELECT count(*) as total
2098
                    FROM c_item_property
2099
                    WHERE lastedit_type = 'ForumthreadVisible'
2100
                    AND c_id = %s
2101
                    AND insert_user_id = %s
2102
                    AND session_id = %s";
2103
            $query = sprintf($sql,
2104
                $courseId,
2105
                $teacher['user_id'],
2106
                $teacher['session_id']
2107
            );
2108
            $rs = Database::query($query);
2109
2110
            $totalForums = 0;
2111
            if ($rs) {
2112
                $row = Database::fetch_row($rs);
2113
                $totalForums = $row[0];
2114
            }
2115
            //total wikis added
2116
            $sql = "SELECT COUNT(DISTINCT(ref)) as total
2117
                    FROM c_item_property
2118
                    WHERE lastedit_type = 'WikiAdded'
2119
                    AND c_id = %s
2120
                    AND insert_user_id = %s
2121
                    AND session_id = %s";
2122
            $query = sprintf($sql,
2123
                $courseId,
2124
                $teacher['user_id'],
2125
                $teacher['session_id']
2126
            );
2127
            $rs = Database::query($query);
2128
2129
            $totalWikis = 0;
2130
            if ($rs) {
2131
                $row = Database::fetch_row($rs);
2132
                $totalWikis = $row[0];
2133
            }
2134
            //total works added
2135
            $sql = "SELECT COUNT(*) as total
2136
                    FROM c_item_property
2137
                    WHERE lastedit_type = 'DirectoryCreated'
2138
                    AND tool = 'work'
2139
                    AND c_id = %s
2140
                    AND insert_user_id = %s
2141
                    AND session_id = %s";
2142
            $query = sprintf($sql,
2143
                $courseId,
2144
                $teacher['user_id'],
2145
                $teacher['session_id']
2146
            );
2147
            $rs = Database::query($query);
2148
2149
            $totalWorks = 0;
2150
            if ($rs) {
2151
                $row = Database::fetch_row($rs);
2152
                $totalWorks = $row[0];
2153
            }
2154
            //total announcements added
2155
            $sql = "SELECT COUNT(*) as total
2156
                    FROM c_item_property
2157
                    WHERE lastedit_type = 'AnnouncementAdded'
2158
                    AND c_id = %s
2159
                    AND insert_user_id = %s
2160
                    AND session_id = %s";
2161
            $query = sprintf($sql,
2162
                $courseId,
2163
                $teacher['user_id'],
2164
                $teacher['session_id']
2165
            );
2166
            $rs = Database::query($query);
2167
2168
            $totalAnnouncements = 0;
2169
            if ($rs) {
2170
                $row = Database::fetch_row($rs);
2171
                $totalAnnouncements = $row[0];
2172
            }
2173
            $tutor = api_get_user_info($teacher['user_id']);
2174
            $data[] = array(
2175
                'course' => $course['title'],
2176
                'session' => $teacher['name'],
2177
                'tutor' => $tutor['username'] . ' - ' . $tutor['lastname'] . ' ' . $tutor['firstname'],
2178
                'documents' => $totalDocuments,
2179
                'links' => $totalLinks,
2180
                'forums' => $totalForums,
2181
                'works' => $totalWorks,
2182
                'wikis' => $totalWikis,
2183
                'announcements' => $totalAnnouncements,
2184
            );
2185
        }
2186
2187
        return $data;
2188
    }
2189
2190
    /**
2191
     * Returns the average student progress in the learning paths of the given
2192
     * course.
2193
     * @param int|array $studentId
2194
     * @param string    $courseCode
2195
     * @param array     $lpIdList Limit average to listed lp ids
2196
     * @param int       $sessionId     Session id (optional),
2197
     * if parameter $session_id is null(default) it'll return results including
2198
     * sessions, 0 = session is not filtered
2199
     * @param bool      $returnArray Will return an array of the type:
2200
     * [sum_of_progresses, number] if it is set to true
2201
     * @param boolean $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2202
     * @return double   Average progress of the user in this course
2203
     */
2204
    public static function get_avg_student_progress(
2205
        $studentId,
2206
        $courseCode = null,
2207
        $lpIdList = array(),
2208
        $sessionId = null,
2209
        $returnArray = false,
2210
        $onlySeriousGame = false
2211
    ) {
2212
        // If there is at least one learning path and one student.
2213
        if (empty($studentId)) {
2214
            return false;
2215
        }
2216
2217
        $sessionId = intval($sessionId);
2218
        $courseInfo = api_get_course_info($courseCode);
2219
2220
        if (empty($courseInfo)) {
2221
            return false;
2222
        }
2223
2224
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2225
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2226
        $lpConditions = [];
2227
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2228
2229
        if ($sessionId > 0) {
2230
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2231
        } else {
2232
            $lpConditions['AND session_id = ?'] = $sessionId;
2233
        }
2234
2235
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2236
            $placeHolders = [];
2237
            for ($i = 0; $i < count($lpIdList); $i++) {
2238
                $placeHolders[] = '?';
2239
            }
2240
            $lpConditions['AND id IN(' . implode(', ', $placeHolders) . ') '] = $lpIdList;
2241
        }
2242
2243
        if ($onlySeriousGame) {
2244
            $lpConditions['AND seriousgame_mode = ? '] = true;
2245
        }
2246
2247
        $resultLP = Database::select(
2248
            'id',
2249
            $lPTable,
2250
            ['where' => $lpConditions]
2251
        );
2252
        $filteredLP = array_keys($resultLP);
2253
2254
        if (empty($filteredLP)) {
2255
            return false;
2256
        }
2257
2258
        $conditions = [
2259
            " c_id = {$courseInfo['real_id']} ",
2260
            " lp_view.lp_id IN(" . implode(', ', $filteredLP) . ") "
2261
        ];
2262
2263
        $groupBy = 'GROUP BY lp_id';
2264
2265
        if (is_array($studentId)) {
2266
            $studentId = array_map('intval', $studentId);
2267
            $conditions[] = " lp_view.user_id IN (" . implode(',', $studentId) . ")  ";
2268
2269
2270
        } else {
2271
            $studentId = intval($studentId);
2272
            $conditions[] = " lp_view.user_id = '$studentId' ";
2273
2274
            if (empty($lpIdList)) {
2275
                $lpList = new LearnpathList($studentId, $courseCode, $sessionId);
2276
                $lpList = $lpList->get_flat_list();
2277
                if (!empty($lpList)) {
2278
                    /** @var  $lp */
2279
                    foreach ($lpList as $lpId => $lp) {
2280
                        $lpIdList[] = $lpId;
2281
                    }
2282
                }
2283
            }
2284
        }
2285
2286
        if (!empty($sessionId)) {
2287
            $conditions[] = " session_id = $sessionId ";
2288
        }
2289
2290
        $conditionToString = implode('AND', $conditions);
2291
        // Get last view for each student (in case of multi-attempt)
2292
        // Also filter on LPs of this session
2293
        /*$sql = " SELECT
2294
                    MAX(view_count),
2295
                    AVG(progress) average,
2296
                    SUM(progress) sum_progress,
2297
                    count(progress) count_progress
2298
                FROM $lpViewTable lp_view
2299
                WHERE
2300
                  $conditionToString
2301
                $groupBy";*/
2302
2303
        $sql = "
2304
            SELECT lp_id, view_count, progress FROM $lpViewTable lp_view
2305
            WHERE
2306
                $conditionToString
2307
                $groupBy
2308
            ORDER BY view_count DESC
2309
        ";
2310
2311
        $result = Database::query($sql);
2312
2313
        $progress = array();
2314
        $viewCount = array();
2315 View Code Duplication
        while ($row = Database::fetch_array($result, 'ASSOC')) {
2316
            if (!isset($viewCount[$row['lp_id']])) {
2317
                $progress[$row['lp_id']] = $row['progress'];
2318
            }
2319
            $viewCount[$row['lp_id']] = $row['view_count'];
2320
        }
2321
2322
        // Fill with lp ids
2323
        if (!empty($lpIdList)) {
2324
            foreach ($lpIdList as $lpId) {
2325
                if (!isset($progress[$lpId])) {
2326
                    $progress[$lpId] = 0;
2327
                }
2328
            }
2329
        }
2330
2331
        if (!empty($progress)) {
2332
            $sum = array_sum($progress);
2333
            $average = $sum / count($progress);
2334
        } else {
2335
            $average = 0;
2336
            $sum = 0;
2337
        }
2338
2339
        if ($returnArray) {
2340
            return [
2341
                $sum,
2342
                count($progress)
2343
            ];
2344
        }
2345
2346
        return round($average, 1);
2347
    }
2348
2349
    /**
2350
     * This function gets:
2351
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2352
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2353
     * 3. And finally it will return the average between 1. and 2.
2354
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2355
     * This function does not take the results of a Test out of a LP
2356
     *
2357
     * @param   mixed       $student_id Array of user ids or an user id
2358
     * @param   string      $course_code
2359
     * @param   array       $lp_ids List of LP ids
2360
     * @param   int         $session_id Session id (optional),
2361
     * if param $session_id is null(default) it'll return results
2362
     * including sessions, 0 = session is not filtered
2363
     * @param   bool        $return_array Returns an array of the
2364
     * type [sum_score, num_score] if set to true
2365
     * @param   bool        $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2366
     *
2367
     * @return  string      Value (number %) Which represents a round integer explain in got in 3.
2368
     */
2369
    public static function get_avg_student_score(
2370
        $student_id,
2371
        $course_code,
2372
        $lp_ids = array(),
2373
        $session_id = null,
2374
        $return_array = false,
2375
        $get_only_latest_attempt_results = false
2376
    ) {
2377
        $debug = false;
2378
        if (empty($lp_ids)) {
2379
            $debug = false;
2380
        }
2381
2382
        if ($debug) echo '<h1>Tracking::get_avg_student_score</h1>';
2383
        $tbl_stats_exercices = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2384
        $tbl_stats_attempts = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2385
2386
        $course = api_get_course_info($course_code);
2387
2388
        if (!empty($course)) {
2389
2390
            // Get course tables names
2391
            $tbl_quiz_questions = Database :: get_course_table(TABLE_QUIZ_QUESTION);
2392
            $lp_table = Database:: get_course_table(TABLE_LP_MAIN);
2393
            $lp_item_table = Database:: get_course_table(TABLE_LP_ITEM);
2394
            $lp_view_table = Database:: get_course_table(TABLE_LP_VIEW);
2395
            $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2396
2397
            $course_id = $course['real_id'];
2398
2399
            // Compose a filter based on optional learning paths list given
2400
            $condition_lp = "";
2401 View Code Duplication
            if (count($lp_ids) > 0) {
2402
                $condition_lp =" AND id IN(".implode(',',$lp_ids).") ";
2403
            }
2404
2405
            // Compose a filter based on optional session id
2406
            $session_id = intval($session_id);
2407
            if (count($lp_ids) > 0) {
2408
                $condition_session = " AND session_id = $session_id ";
2409
            } else {
2410
                $condition_session = " WHERE session_id = $session_id ";
2411
            }
2412
2413
            // Check the real number of LPs corresponding to the filter in the
2414
            // database (and if no list was given, get them all)
2415
2416
            if (empty($session_id)) {
2417
                $sql = "SELECT DISTINCT(id), use_max_score
2418
                        FROM $lp_table
2419
                        WHERE c_id = $course_id AND (session_id = 0 OR session_id IS NULL ) $condition_lp ";
2420
            } else {
2421
                $sql = "SELECT DISTINCT(id), use_max_score
2422
                        FROM $lp_table
2423
                        WHERE c_id = $course_id $condition_lp ";
2424
            }
2425
2426
            $res_row_lp   = Database::query($sql);
2427
            $count_row_lp = Database::num_rows($res_row_lp);
2428
2429
            $lp_list = $use_max_score = array();
2430
            while ($row_lp = Database::fetch_array($res_row_lp)) {
2431
                $lp_list[] = $row_lp['id'];
2432
                $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
2433
            }
2434
2435
            if ($debug) {
2436
                echo 'Use max score or not list '; var_dump($use_max_score);
2437
            }
2438
2439
            // prepare filter on users
2440 View Code Duplication
            if (is_array($student_id)) {
2441
                array_walk($student_id, 'intval');
2442
                $condition_user1 =" AND user_id IN (".implode(',', $student_id).") ";
2443
            } else {
2444
                $condition_user1 =" AND user_id = $student_id ";
2445
            }
2446
2447
            if ($count_row_lp > 0 && !empty($student_id)) {
2448
2449
                // Getting latest LP result for a student
2450
                //@todo problem when a  course have more than 1500 users
2451
                $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
2452
                        FROM $lp_view_table
2453
                        WHERE
2454
                            c_id = $course_id AND
2455
                            lp_id IN (".implode(',', $lp_list).")
2456
                            $condition_user1 AND
2457
                            session_id = $session_id
2458
                        GROUP BY lp_id, user_id";
2459
                if ($debug) echo $sql;
2460
2461
                $rs_last_lp_view_id = Database::query($sql);
2462
2463
                $global_result = 0;
2464
2465
                if (Database::num_rows($rs_last_lp_view_id) > 0) {
2466
                    // Cycle through each line of the results (grouped by lp_id, user_id)
2467
                    while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2468
                        $count_items = 0;
2469
                        $lp_partial_total = 0;
2470
2471
                        $list = array();
2472
                        $lp_view_id = $row_lp_view['id'];
2473
                        $lp_id      = $row_lp_view['lp_id'];
2474
                        $user_id    = $row_lp_view['user_id'];
2475
                        if ($debug) echo '<h2>LP id '.$lp_id.'</h2>';
2476
2477
                        if ($get_only_latest_attempt_results) {
2478
                            //Getting lp_items done by the user
2479
                            $sql = "SELECT DISTINCT lp_item_id
2480
                                    FROM $lp_item_view_table
2481
                                    WHERE
2482
                                        c_id = $course_id AND
2483
                                        lp_view_id = $lp_view_id
2484
                                    ORDER BY lp_item_id";
2485
                            $res_lp_item = Database::query($sql);
2486
2487
                            while ($row_lp_item = Database::fetch_array($res_lp_item,'ASSOC')) {
2488
                                $my_lp_item_id = $row_lp_item['lp_item_id'];
2489
2490
                                // Getting the most recent attempt
2491
                                $sql = "SELECT  lp_iv.id as lp_item_view_id,
2492
                                                lp_iv.score as score,
2493
                                                lp_i.max_score,
2494
                                                lp_iv.max_score as max_score_item_view,
2495
                                                lp_i.path,
2496
                                                lp_i.item_type,
2497
                                                lp_i.id as iid
2498
                                        FROM $lp_item_view_table as lp_iv
2499
                                            INNER JOIN $lp_item_table as lp_i
2500
                                            ON  lp_i.id = lp_iv.lp_item_id AND
2501
                                                lp_iv.c_id = $course_id AND
2502
                                                lp_i.c_id  = $course_id AND
2503
                                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2504
                                        WHERE
2505
                                            lp_item_id = $my_lp_item_id AND
2506
                                            lp_view_id = $lp_view_id
2507
                                        ORDER BY view_count DESC
2508
                                        LIMIT 1";
2509
                                $res_lp_item_result = Database::query($sql);
2510
                                while ($row_max_score = Database::fetch_array($res_lp_item_result,'ASSOC')) {
2511
                                    $list[]= $row_max_score;
2512
                                }
2513
                            }
2514
                        } else {
2515
                            // For the currently analysed view, get the score and
2516
                            // max_score of each item if it is a sco or a TOOL_QUIZ
2517
                            $sql = "SELECT
2518
                                        lp_iv.id as lp_item_view_id,
2519
                                        lp_iv.score as score,
2520
                                        lp_i.max_score,
2521
                                        lp_iv.max_score as max_score_item_view,
2522
                                        lp_i.path,
2523
                                        lp_i.item_type,
2524
                                        lp_i.id as iid
2525
                                      FROM $lp_item_view_table as lp_iv
2526
                                      INNER JOIN $lp_item_table as lp_i
2527
                                      ON lp_i.id = lp_iv.lp_item_id AND
2528
                                         lp_iv.c_id = $course_id AND
2529
                                         lp_i.c_id  = $course_id AND
2530
                                         (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2531
                                      WHERE lp_view_id = $lp_view_id ";
2532
                            if ($debug) echo $sql.'<br />';
2533
                            $res_max_score = Database::query($sql);
2534
2535
                            while ($row_max_score = Database::fetch_array($res_max_score,'ASSOC')) {
2536
                                $list[]= $row_max_score;
2537
                            }
2538
                        }
2539
2540
                        // Go through each scorable element of this view
2541
2542
                        $score_of_scorm_calculate = 0;
2543
2544
                        foreach ($list as $row_max_score) {
2545
                            // Came from the original lp_item
2546
                            $max_score = $row_max_score['max_score'];
2547
                            // Came from the lp_item_view
2548
                            $max_score_item_view = $row_max_score['max_score_item_view'];
2549
                            $score = $row_max_score['score'];
2550
2551
                            if ($debug) echo '<h3>Item Type: ' .$row_max_score['item_type'].'</h3>';
2552
2553
                            if ($row_max_score['item_type'] == 'sco') {
2554
                                /* Check if it is sco (easier to get max_score)
2555
                                   when there's no max score, we assume 100 as the max score,
2556
                                   as the SCORM 1.2 says that the value should always be between 0 and 100.
2557
                                */
2558
                                if ($max_score == 0 || is_null($max_score) || $max_score == '') {
2559
                                    // Chamilo style
2560
                                    if ($use_max_score[$lp_id]) {
2561
                                        $max_score = 100;
2562
                                    } else {
2563
                                        // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
2564
                                        $max_score = $max_score_item_view;
2565
                                    }
2566
                                }
2567
                                // Avoid division by zero errors
2568
                                if (!empty($max_score)) {
2569
                                    $lp_partial_total += $score/$max_score;
2570
                                }
2571 View Code Duplication
                                if ($debug) echo '<b>$lp_partial_total, $score, $max_score '.$lp_partial_total.' '.$score.' '.$max_score.'</b><br />';
2572
                            } else {
2573
                                // Case of a TOOL_QUIZ element
2574
                                $item_id = $row_max_score['iid'];
2575
                                $item_path = $row_max_score['path'];
2576
                                $lp_item_view_id = $row_max_score['lp_item_view_id'];
2577
2578
                                // Get last attempt to this exercise through
2579
                                // the current lp for the current user
2580
                                $sql = "SELECT exe_id
2581
                                        FROM $tbl_stats_exercices
2582
                                        WHERE
2583
                                            exe_exo_id           = '$item_path' AND
2584
                                            exe_user_id          = $user_id AND
2585
                                            orig_lp_item_id      = $item_id AND
2586
                                            orig_lp_item_view_id = $lp_item_view_id AND
2587
                                            c_id                 = $course_id AND
2588
                                            session_id           = $session_id AND
2589
                                            status = ''
2590
                                        ORDER BY exe_date DESC
2591
                                        LIMIT 1";
2592
2593
                                if ($debug) echo $sql .'<br />';
2594
                                $result_last_attempt = Database::query($sql);
2595
                                $num = Database :: num_rows($result_last_attempt);
2596
                                if ($num > 0 ) {
2597
                                    $id_last_attempt = Database :: result($result_last_attempt, 0, 0);
2598
                                    if ($debug) echo $id_last_attempt.'<br />';
2599
2600
                                    // Within the last attempt number tracking, get the sum of
2601
                                    // the max_scores of all questions that it was
2602
                                    // made of (we need to make this call dynamic because of random questions selection)
2603
                                    $sql = "SELECT SUM(t.ponderation) as maxscore FROM
2604
                                            (
2605
                                                SELECT DISTINCT
2606
                                                    question_id,
2607
                                                    marks,
2608
                                                    ponderation
2609
                                                FROM $tbl_stats_attempts AS at
2610
                                                INNER JOIN $tbl_quiz_questions AS q
2611
                                                ON (q.id = at.question_id)
2612
                                                WHERE
2613
                                                    exe_id ='$id_last_attempt' AND
2614
                                                    q.c_id = $course_id
2615
                                            )
2616
                                            AS t";
2617
                                    if ($debug) echo '$sql: '.$sql.' <br />';
2618
                                    $res_max_score_bis = Database::query($sql);
2619
                                    $row_max_score_bis = Database::fetch_array($res_max_score_bis);
2620
2621
                                    if (!empty($row_max_score_bis['maxscore'])) {
2622
                                        $max_score = $row_max_score_bis['maxscore'];
2623
                                    }
2624
                                    if (!empty($max_score) && floatval($max_score) > 0) {
2625
                                        $lp_partial_total += $score/$max_score;
2626
                                    }
2627 View Code Duplication
                                    if ($debug) echo '$lp_partial_total, $score, $max_score <b>'.$lp_partial_total.' '.$score.' '.$max_score.'</b><br />';
2628
                                }
2629
                            }
2630
2631
                            if (in_array($row_max_score['item_type'], array('quiz','sco'))) {
2632
                                // Normal way
2633
                                if ($use_max_score[$lp_id]) {
2634
                                    $count_items++;
2635
                                } else {
2636
                                    if ($max_score != '') {
2637
                                        $count_items++;
2638
                                    }
2639
                                }
2640
                                if ($debug) echo '$count_items: '.$count_items;
2641
                            }
2642
                        } //end for
2643
2644
                        $score_of_scorm_calculate += $count_items ? (($lp_partial_total / $count_items) * 100) : 0;
2645
2646
                        if ($debug) echo '<h3>$count_items '.$count_items.'</h3>';
2647
                        if ($debug) echo '<h3>$score_of_scorm_calculate '.$score_of_scorm_calculate.'</h3>';
2648
2649
                        $global_result += $score_of_scorm_calculate;
2650
                        if ($debug) echo '<h3>$global_result '.$global_result.'</h3>';
2651
                    } // end while
2652
                }
2653
2654
                $lp_with_quiz = 0;
2655
                foreach ($lp_list as $lp_id) {
2656
                    // Check if LP have a score we assume that all SCO have an score
2657
                    $sql = "SELECT count(id) as count
2658
                            FROM $lp_item_table
2659
                            WHERE
2660
                                c_id = $course_id AND
2661
                                (item_type = 'quiz' OR item_type = 'sco') AND
2662
                                lp_id = ".$lp_id;
2663
                    if ($debug) echo $sql;
2664
                    $result_have_quiz = Database::query($sql);
2665
2666
                    if (Database::num_rows($result_have_quiz) > 0 ) {
2667
                        $row = Database::fetch_array($result_have_quiz,'ASSOC');
2668
                        if (is_numeric($row['count']) && $row['count'] != 0) {
2669
                            $lp_with_quiz++;
2670
                        }
2671
                    }
2672
                }
2673
2674
                if ($debug) echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
2675
                if ($debug) echo '<h3>Final return</h3>';
2676
2677
                if ($lp_with_quiz != 0) {
2678
                    if (!$return_array) {
2679
                        $score_of_scorm_calculate = round(($global_result/$lp_with_quiz),2);
2680
                        if ($debug) var_dump($score_of_scorm_calculate);
2681
                        if (empty($lp_ids)) {
2682
                            if ($debug) echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
2683
                        }
2684
                        return $score_of_scorm_calculate;
2685
                    } else {
2686
                        if ($debug) var_dump($global_result, $lp_with_quiz);
2687
                        return array($global_result, $lp_with_quiz);
2688
                    }
2689
                } else {
2690
2691
                    return '-';
2692
                }
2693
            }
2694
        }
2695
2696
        return null;
2697
    }
2698
2699
    /**
2700
     * This function gets:
2701
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2702
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2703
     * 3. And finally it will return the average between 1. and 2.
2704
     * This function does not take the results of a Test out of a LP
2705
     *
2706
     * @param   int|array   Array of user ids or an user id
2707
     * @param   string      Course code
2708
     * @param   array       List of LP ids
2709
     * @param   int         Session id (optional), if param $session_id is null(default)
2710
     * it'll return results including sessions, 0 = session is not filtered
2711
     * @param   bool        Returns an array of the type [sum_score, num_score] if set to true
2712
     * @param   bool        get only the latest attempts or ALL attempts
2713
     * @return  string      Value (number %) Which represents a round integer explain in got in 3.
2714
     */
2715
    public static function getAverageStudentScore(
2716
        $student_id,
2717
        $course_code = null,
2718
        $lp_ids = array(),
2719
        $session_id = null
2720
    ) {
2721
        if (empty($student_id)) {
2722
            return 0;
2723
        }
2724
2725
        $conditions = array();
2726
2727
        if (!empty($course_code)) {
2728
            $course = api_get_course_info($course_code);
2729
            $courseId = $course['real_id'];
2730
            $conditions[] = " c_id = $courseId";
2731
        }
2732
2733
        // Get course tables names
2734
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
2735
        $lp_item_table = Database :: get_course_table(TABLE_LP_ITEM);
2736
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
2737
        $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2738
2739
        // Compose a filter based on optional learning paths list given
2740
2741
        if (!empty($lp_ids) && count($lp_ids) > 0) {
2742
            $conditions[] = " id IN(".implode(',', $lp_ids).") ";
2743
        }
2744
2745
        // Compose a filter based on optional session id
2746
        $session_id = intval($session_id);
2747
        if (!empty($session_id)) {
2748
            $conditions[] = " session_id = $session_id ";
2749
        }
2750
2751 View Code Duplication
        if (is_array($student_id)) {
2752
            array_walk($student_id, 'intval');
2753
            $conditions[] =" lp_view.user_id IN (".implode(',', $student_id).") ";
2754
        } else {
2755
            $conditions[] =" lp_view.user_id = $student_id ";
2756
        }
2757
2758
        $conditionsToString = implode('AND ', $conditions);
2759
        $sql = "SELECT  SUM(lp_iv.score) sum_score,
2760
                        SUM(lp_i.max_score) sum_max_score,
2761
                        count(*) as count
2762
                FROM $lp_table as lp
2763
                INNER JOIN $lp_item_table as lp_i
2764
                ON lp.id = lp_id AND lp.c_id = lp_i.c_id
2765
                INNER JOIN $lp_view_table as lp_view
2766
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
2767
                INNER JOIN $lp_item_view_table as lp_iv
2768
                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
2769
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
2770
                $conditionsToString
2771
                ";
2772
        $result = Database::query($sql);
2773
        $row = Database::fetch_array($result, 'ASSOC');
2774
2775
        if (empty($row['sum_max_score'])) {
2776
            return 0;
2777
        }
2778
2779
        return ($row['sum_score'] / $row['sum_max_score'])*100;
2780
2781
    }
2782
2783
    /**
2784
     * This function gets time spent in learning path for a student inside a course
2785
     * @param     int|array    Student id(s)
2786
     * @param     string         Course code
2787
     * @param     array         Limit average to listed lp ids
2788
     * @param     int            Session id (optional), if param $session_id is
2789
     * null(default) it'll return results including sessions, 0 = session is not filtered
2790
     * @return     int         Total time
2791
     */
2792
    public static function get_time_spent_in_lp($student_id, $course_code, $lp_ids = array(), $session_id = null)
2793
    {
2794
        $course = api_get_course_info($course_code);
2795
        $student_id = intval($student_id);
2796
        $total_time = 0;
2797
2798
        if (!empty($course)) {
2799
2800
            $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
2801
            $t_lpv = Database :: get_course_table(TABLE_LP_VIEW);
2802
            $t_lpiv = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2803
2804
            $course_id = $course['real_id'];
2805
2806
            // Compose a filter based on optional learning paths list given
2807
            $condition_lp = "";
2808 View Code Duplication
            if (count($lp_ids) > 0) {
2809
                $condition_lp =" AND id IN(".implode(',',$lp_ids).") ";
2810
            }
2811
2812
            // Compose a filter based on optional session id
2813
            $session_id = intval($session_id);
2814
            $condition_session = "";
2815
            if (isset($session_id)) {
2816
                $condition_session = " AND session_id = $session_id ";
2817
            }
2818
2819
            // Check the real number of LPs corresponding to the filter in the
2820
            // database (and if no list was given, get them all)
2821
            $sql = "SELECT DISTINCT(id) FROM $lp_table WHERE c_id = $course_id $condition_lp";
2822
            $res_row_lp = Database::query($sql);
2823
            $count_row_lp = Database::num_rows($res_row_lp);
2824
2825
            // calculates time
2826
            if ($count_row_lp > 0) {
2827 View Code Duplication
                while ($row_lp = Database::fetch_array($res_row_lp)) {
2828
                    $lp_id = intval($row_lp['id']);
2829
                    $sql = 'SELECT SUM(total_time)
2830
                            FROM '.$t_lpiv.' AS item_view
2831
                            INNER JOIN '.$t_lpv.' AS view
2832
                            ON (
2833
                                item_view.lp_view_id = view.id AND 
2834
                                item_view.c_id = view.c_id
2835
                            )
2836
                            WHERE
2837
                                item_view.c_id = '.$course_id.' AND
2838
                                view.c_id = '.$course_id.' AND
2839
                                view.lp_id = '.$lp_id.' AND 
2840
                                view.user_id = '.$student_id.' AND
2841
                                session_id = '.$session_id;
2842
2843
                    $rs = Database::query($sql);
2844
                    if (Database :: num_rows($rs) > 0) {
2845
                        $total_time += Database :: result($rs, 0, 0);
2846
                    }
2847
                }
2848
            }
2849
        }
2850
2851
        return $total_time;
2852
    }
2853
2854
    /**
2855
     * This function gets last connection time to one learning path
2856
     * @param     int|array    Student id(s)
2857
     * @param     string         Course code
2858
     * @param     int         Learning path id
2859
     * @return     int         Total time
2860
     */
2861
    public static function get_last_connection_time_in_lp($student_id, $course_code, $lp_id, $session_id = 0)
2862
    {
2863
        $course = CourseManager :: get_course_information($course_code);
2864
        $student_id = intval($student_id);
2865
        $lp_id = intval($lp_id);
2866
        $last_time = 0;
2867
        $session_id = intval($session_id);
2868
2869
        if (!empty($course)) {
2870
2871
            $course_id	 = $course['real_id'];
2872
2873
            $lp_table    = Database :: get_course_table(TABLE_LP_MAIN);
2874
            $t_lpv       = Database :: get_course_table(TABLE_LP_VIEW);
2875
            $t_lpiv      = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2876
2877
            // Check the real number of LPs corresponding to the filter in the
2878
            // database (and if no list was given, get them all)
2879
            $res_row_lp = Database::query("SELECT id FROM $lp_table WHERE c_id = $course_id AND id = $lp_id ");
2880
            $count_row_lp = Database::num_rows($res_row_lp);
2881
2882
            // calculates last connection time
2883
            if ($count_row_lp > 0) {
2884
                $sql = 'SELECT MAX(start_time)
2885
                        FROM ' . $t_lpiv . ' AS item_view
2886
                        INNER JOIN ' . $t_lpv . ' AS view
2887
                            ON item_view.lp_view_id = view.id
2888
                        WHERE
2889
                            item_view.c_id 		= '.$course_id.' AND
2890
                            view.c_id 			= '.$course_id.' AND
2891
                            view.lp_id 			= '.$lp_id.'
2892
                            AND view.user_id 	= '.$student_id.'
2893
                            AND view.session_id = '.$session_id;
2894
                $rs = Database::query($sql);
2895
                if (Database :: num_rows($rs) > 0) {
2896
                    $last_time = Database :: result($rs, 0, 0);
2897
                }
2898
            }
2899
        }
2900
2901
        return $last_time;
2902
    }
2903
2904
    /**
2905
     * gets the list of students followed by coach
2906
     * @param     int     Coach id
2907
     * @return     array     List of students
2908
     */
2909
    public static function get_student_followed_by_coach($coach_id)
2910
    {
2911
        $coach_id = intval($coach_id);
2912
2913
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2914
        $tbl_session_course = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE);
2915
        $tbl_session_user = Database :: get_main_table(TABLE_MAIN_SESSION_USER);
2916
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
2917
2918
        $students = [];
2919
2920
        // At first, courses where $coach_id is coach of the course //
2921
        $sql = 'SELECT session_id, c_id
2922
                FROM ' . $tbl_session_course_user . '
2923
                WHERE user_id=' . $coach_id.' AND status=2';
2924
2925
        if (api_is_multiple_url_enabled()) {
2926
            $tbl_session_rel_access_url= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2927
            $access_url_id = api_get_current_access_url_id();
2928
            if ($access_url_id != -1) {
2929
                $sql = 'SELECT scu.session_id, scu.c_id
2930
                    FROM ' . $tbl_session_course_user . ' scu
2931
                    INNER JOIN '.$tbl_session_rel_access_url.'  sru
2932
                    ON (scu.session_id=sru.session_id)
2933
                    WHERE
2934
                        scu.user_id=' . $coach_id.' AND
2935
                        scu.status=2 AND
2936
                        sru.access_url_id = '.$access_url_id;
2937
            }
2938
        }
2939
2940
        $result = Database::query($sql);
2941
2942 View Code Duplication
        while ($a_courses = Database::fetch_array($result)) {
2943
            $courseId = $a_courses["c_id"];
2944
            $id_session = $a_courses["session_id"];
2945
2946
            $sql = "SELECT DISTINCT srcru.user_id
2947
                    FROM $tbl_session_course_user AS srcru, $tbl_session_user sru
2948
                    WHERE
2949
                        srcru.user_id = sru.user_id AND
2950
                        sru.relation_type<>".SESSION_RELATION_TYPE_RRHH." AND
2951
                        srcru.session_id = sru.session_id AND
2952
                        srcru.c_id = '$courseId' AND
2953
                        srcru.session_id='$id_session'";
2954
2955
            $rs = Database::query($sql);
2956
2957
            while ($row = Database::fetch_array($rs)) {
2958
                $students[$row['user_id']] = $row['user_id'];
2959
            }
2960
        }
2961
2962
        // Then, courses where $coach_id is coach of the session    //
2963
        $sql = 'SELECT session_course_user.user_id
2964
                FROM ' . $tbl_session_course_user . ' as session_course_user
2965
                INNER JOIN     '.$tbl_session_user.' sru
2966
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
2967
                INNER JOIN ' . $tbl_session_course . ' as session_course
2968
                ON session_course.c_id = session_course_user.c_id
2969
                AND session_course_user.session_id = session_course.session_id
2970
                INNER JOIN ' . $tbl_session . ' as session
2971
                ON session.id = session_course.session_id
2972
                AND session.id_coach = ' . $coach_id;
2973 View Code Duplication
        if (api_is_multiple_url_enabled()) {
2974
            $tbl_session_rel_access_url= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2975
            $access_url_id = api_get_current_access_url_id();
2976
            if ($access_url_id != -1){
2977
                $sql = 'SELECT session_course_user.user_id
2978
                        FROM ' . $tbl_session_course_user . ' as session_course_user
2979
                        INNER JOIN     '.$tbl_session_user.' sru
2980
                            ON session_course_user.user_id = sru.user_id AND
2981
                               session_course_user.session_id = sru.session_id
2982
                        INNER JOIN ' . $tbl_session_course . ' as session_course
2983
                            ON session_course.c_id = session_course_user.c_id AND
2984
                            session_course_user.session_id = session_course.session_id
2985
                        INNER JOIN ' . $tbl_session . ' as session
2986
                            ON session.id = session_course.session_id AND
2987
                            session.id_coach = ' . $coach_id.'
2988
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
2989
                            ON session.id = session_rel_url.session_id WHERE access_url_id = '.$access_url_id;
2990
            }
2991
        }
2992
2993
        $result = Database::query($sql);
2994
        while ($row = Database::fetch_array($result)) {
2995
            $students[$row['user_id']] = $row['user_id'];
2996
        }
2997
2998
        return $students;
2999
    }
3000
3001
    /**
3002
     * Get student followed by a coach inside a session
3003
     * @param    int        Session id
3004
     * @param    int        Coach id
3005
     * @return   array    students list
3006
     */
3007
    public static function get_student_followed_by_coach_in_a_session($id_session, $coach_id)
3008
    {
3009
        $coach_id = intval($coach_id);
3010
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3011
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
3012
3013
        $students = [];
3014
        // At first, courses where $coach_id is coach of the course //
3015
        $sql = 'SELECT c_id FROM ' . $tbl_session_course_user . '
3016
                WHERE session_id="' . $id_session . '" AND user_id=' . $coach_id.' AND status=2';
3017
        $result = Database::query($sql);
3018
3019
        while ($a_courses = Database::fetch_array($result)) {
3020
            $courseId = $a_courses["c_id"];
3021
3022
            $sql = "SELECT DISTINCT srcru.user_id
3023
                    FROM $tbl_session_course_user AS srcru
3024
                    WHERE
3025
                        c_id = '$courseId' AND
3026
                        session_id = '" . $id_session . "'";
3027
            $rs = Database::query($sql);
3028
            while ($row = Database::fetch_array($rs)) {
3029
                $students[$row['user_id']] = $row['user_id'];
3030
            }
3031
        }
3032
3033
        // Then, courses where $coach_id is coach of the session
3034
        $sql = 'SELECT id_coach FROM ' . $tbl_session . '
3035
                WHERE id="' . $id_session.'" AND id_coach="' . $coach_id . '"';
3036
        $result = Database::query($sql);
3037
3038
        //He is the session_coach so we select all the users in the session
3039
        if (Database::num_rows($result) > 0) {
3040
            $sql = 'SELECT DISTINCT srcru.user_id
3041
                    FROM ' . $tbl_session_course_user . ' AS srcru
3042
                    WHERE session_id="' . $id_session . '"';
3043
            $result = Database::query($sql);
3044
            while ($row = Database::fetch_array($result)) {
3045
                $students[$row['user_id']] = $row['user_id'];
3046
            }
3047
        }
3048
3049
        return $students;
3050
    }
3051
3052
    /**
3053
     * Check if a coach is allowed to follow a student
3054
     * @param    int        Coach id
3055
     * @param    int        Student id
3056
     * @return    bool
3057
     */
3058
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3059
    {
3060
        $coach_id = intval($coach_id);
3061
        $student_id = intval($student_id);
3062
3063
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3064
        $tbl_session_course = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE);
3065
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
3066
3067
        // At first, courses where $coach_id is coach of the course //
3068
3069
        $sql = 'SELECT 1 FROM ' . $tbl_session_course_user . '
3070
                WHERE user_id=' . $coach_id .' AND status=2';
3071
        $result = Database::query($sql);
3072
        if (Database::num_rows($result) > 0) {
3073
            return true;
3074
        }
3075
3076
        // Then, courses where $coach_id is coach of the session
3077
        $sql = 'SELECT session_course_user.user_id
3078
                FROM ' . $tbl_session_course_user . ' as session_course_user
3079
                INNER JOIN ' . $tbl_session_course . ' as session_course
3080
                    ON session_course.c_id = session_course_user.c_id
3081
                INNER JOIN ' . $tbl_session . ' as session
3082
                    ON session.id = session_course.session_id
3083
                    AND session.id_coach = ' . $coach_id . '
3084
                WHERE user_id = ' . $student_id;
3085
        $result = Database::query($sql);
3086
        if (Database::num_rows($result) > 0) {
3087
            return true;
3088
        }
3089
3090
        return false;
3091
    }
3092
3093
    /**
3094
     * Get courses followed by coach
3095
     * @param     int        Coach id
3096
     * @param    int        Session id (optional)
3097
     * @return    array    Courses list
3098
     */
3099
    public static function get_courses_followed_by_coach($coach_id, $id_session = null)
3100
    {
3101
        $coach_id = intval($coach_id);
3102
        if (!empty($id_session)) {
3103
            $id_session = intval($id_session);
3104
        }
3105
3106
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3107
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3108
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3109
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3110
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3111
3112
        // At first, courses where $coach_id is coach of the course.
3113
3114
        $sql = 'SELECT DISTINCT c.code
3115
                FROM ' . $tbl_session_course_user . ' sc
3116
                INNER JOIN '.$tbl_course.' c
3117
                ON (c.id = sc.c_id)
3118
                WHERE user_id = ' . $coach_id.' AND status = 2';
3119
3120
        if (api_is_multiple_url_enabled()) {
3121
            $access_url_id = api_get_current_access_url_id();
3122
            if ($access_url_id != -1){
3123
                $sql = 'SELECT DISTINCT c.code
3124
                        FROM ' . $tbl_session_course_user . ' scu
3125
                        INNER JOIN '.$tbl_course.' c
3126
                        ON (c.code = scu.c_id)
3127
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3128
                        ON (c.id = cru.c_id)
3129
                        WHERE
3130
                            scu.user_id=' . $coach_id.' AND
3131
                            scu.status=2 AND
3132
                            cru.access_url_id = '.$access_url_id;
3133
            }
3134
        }
3135
3136
        if (!empty($id_session)) {
3137
            $sql .= ' AND session_id=' . $id_session;
3138
        }
3139
3140
        $courseList = array();
3141
        $result = Database::query($sql);
3142
        while ($row = Database::fetch_array($result)) {
3143
            $courseList[$row['code']] = $row['code'];
3144
        }
3145
3146
        // Then, courses where $coach_id is coach of the session
3147
3148
        $sql = 'SELECT DISTINCT course.code
3149
                FROM ' . $tbl_session_course . ' as session_course
3150
                INNER JOIN ' . $tbl_session . ' as session
3151
                    ON session.id = session_course.session_id
3152
                    AND session.id_coach = ' . $coach_id . '
3153
                INNER JOIN ' . $tbl_course . ' as course
3154
                    ON course.id = session_course.c_id';
3155
3156 View Code Duplication
        if (api_is_multiple_url_enabled()) {
3157
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3158
            $access_url_id = api_get_current_access_url_id();
3159
            if ($access_url_id != -1){
3160
                $sql = 'SELECT DISTINCT c.code
3161
                    FROM ' . $tbl_session_course . ' as session_course
3162
                    INNER JOIN '.$tbl_course.' c
3163
                    ON (c.id = session_course.c_id)
3164
                    INNER JOIN ' . $tbl_session . ' as session
3165
                    ON session.id = session_course.session_id
3166
                        AND session.id_coach = ' . $coach_id . '
3167
                    INNER JOIN ' . $tbl_course . ' as course
3168
                        ON course.id = session_course.c_id
3169
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
3170
                    ON (course_rel_url.c_id = c.id)';
3171
            }
3172
        }
3173
3174
        if (!empty ($id_session)) {
3175
            $sql .= ' WHERE session_course.session_id=' . $id_session;
3176
            if (api_is_multiple_url_enabled())
3177
            $sql .=  ' AND access_url_id = '.$access_url_id;
3178
        }  else {
3179
            if (api_is_multiple_url_enabled())
3180
            $sql .=  ' WHERE access_url_id = '.$access_url_id;
3181
        }
3182
3183
        $result = Database::query($sql);
3184
        while ($row = Database::fetch_array($result)) {
3185
            $courseList[$row['code']] = $row['code'];
3186
        }
3187
3188
        return $courseList;
3189
    }
3190
3191
    /**
3192
     * Get sessions coached by user
3193
     * @param $coach_id
3194
     * @param int $start
3195
     * @param int $limit
3196
     * @param bool $getCount
3197
     * @param string $keyword
3198
     * @param string $description
3199
     * @return mixed
3200
     */
3201
    public static function get_sessions_coached_by_user(
3202
        $coach_id,
3203
        $start = 0,
3204
        $limit = 0,
3205
        $getCount = false,
3206
        $keyword = '',
3207
        $description = ''
3208
    ) {
3209
        // table definition
3210
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
3211
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3212
        $coach_id = intval($coach_id);
3213
3214
        $select = " SELECT * FROM ";
3215
        if ($getCount) {
3216
            $select = " SELECT count(DISTINCT id) as count FROM ";
3217
        }
3218
3219
        $limitCondition = null;
3220 View Code Duplication
        if (!empty($start) && !empty($limit)) {
3221
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3222
        }
3223
3224
        $keywordCondition = null;
3225
3226 View Code Duplication
        if (!empty($keyword)) {
3227
            $keyword = Database::escape_string($keyword);
3228
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
3229
3230
            if (!empty($description)) {
3231
                $description = Database::escape_string($description);
3232
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3233
            }
3234
        }
3235
3236
        $tbl_session_rel_access_url= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3237
        $access_url_id = api_get_current_access_url_id();
3238
3239
        $sql = "
3240
            $select
3241
            (
3242
                SELECT DISTINCT
3243
                    id,
3244
                    name,
3245
                    access_start_date,
3246
                    access_end_date
3247
                FROM $tbl_session session INNER JOIN $tbl_session_rel_access_url session_rel_url
3248
                ON (session.id = session_rel_url.session_id)
3249
                WHERE
3250
                    id_coach = $coach_id AND
3251
                    access_url_id = $access_url_id
3252
                    $keywordCondition
3253
            UNION
3254
                SELECT DISTINCT
3255
                    session.id,
3256
                    session.name,
3257
                    session.access_start_date,
3258
                    session.access_end_date
3259
                FROM $tbl_session as session
3260
                INNER JOIN $tbl_session_course_user as session_course_user
3261
                    ON session.id = session_course_user.session_id AND
3262
                    session_course_user.user_id = $coach_id AND
3263
                    session_course_user.status = 2
3264
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3265
                ON (session.id = session_rel_url.session_id)
3266
                WHERE
3267
                    access_url_id = $access_url_id
3268
                    $keywordCondition
3269
            ) as sessions $limitCondition
3270
            ";
3271
3272
        $rs = Database::query($sql);
3273
        if ($getCount) {
3274
            $row = Database::fetch_array($rs);
3275
            return $row['count'];
3276
        }
3277
3278
        $sessions = [];
3279
        while ($row = Database::fetch_array($rs)) {
3280
            $sessions[$row['id']] = $row;
3281
        }
3282
3283
        if (!empty($sessions)) {
3284
            foreach ($sessions as & $session) {
3285
                if ($session['access_start_date'] == '0000-00-00 00:00:00' || empty($session['access_start_date'])
3286
                ) {
3287
                    $session['status'] = get_lang('SessionActive');
3288
                }
3289
                else {
3290
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3291
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3292
                    if ($time_start < time() && time() < $time_end) {
3293
                        $session['status'] = get_lang('SessionActive');
3294
                    } else {
3295
                        if (time() < $time_start) {
3296
                            $session['status'] = get_lang('SessionFuture');
3297
                        } else {
3298
                            if (time() > $time_end) {
3299
                                $session['status'] = get_lang('SessionPast');
3300
                            }
3301
                        }
3302
                    }
3303
                }
3304
            }
3305
        }
3306
3307
        return $sessions;
3308
    }
3309
3310
    /**
3311
     * Get courses list from a session
3312
     * @param    int        Session id
3313
     * @return    array    Courses list
3314
     */
3315 View Code Duplication
    public static function get_courses_list_from_session($session_id)
3316
    {
3317
        $session_id = intval($session_id);
3318
3319
        // table definition
3320
        $tbl_session_course = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE);
3321
        $courseTable = Database :: get_main_table(TABLE_MAIN_COURSE);
3322
3323
        $sql = "SELECT DISTINCT code, c_id
3324
                FROM $tbl_session_course sc
3325
                INNER JOIN $courseTable c
3326
                ON sc.c_id = c.id
3327
                WHERE session_id= $session_id";
3328
3329
        $result = Database::query($sql);
3330
3331
        $courses = array();
3332
        while ($row = Database::fetch_array($result)) {
3333
            $courses[$row['code']] = $row;
3334
        }
3335
3336
        return $courses;
3337
    }
3338
3339
    /**
3340
     * Count the number of documents that an user has uploaded to a course
3341
     * @param    int|array   Student id(s)
3342
     * @param    string      Course code
3343
     * @param    int         Session id (optional),
3344
     * if param $session_id is null(default)
3345
     * return count of assignments including sessions, 0 = session is not filtered
3346
     * @return    int        Number of documents
3347
     */
3348
    public static function count_student_uploaded_documents($student_id, $course_code, $session_id = null)
3349
    {
3350
        // get the information of the course
3351
        $a_course = CourseManager::get_course_information($course_code);
3352
        if (!empty($a_course)) {
3353
            // table definition
3354
            $tbl_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3355
            $tbl_document = Database :: get_course_table(TABLE_DOCUMENT);
3356
            $course_id	 = $a_course['real_id'];
3357
            if (is_array($student_id)) {
3358
                $studentList = array_map('intval', $student_id);
3359
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
3360
            } else {
3361
                $student_id = intval($student_id);
3362
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
3363
            }
3364
3365
            $condition_session = null;
3366
            if (isset($session_id)) {
3367
                $session_id = intval($session_id);
3368
                $condition_session = " AND pub.session_id = $session_id ";
3369
            }
3370
3371
            $sql = "SELECT count(ip.tool) AS count
3372
                    FROM $tbl_item_property ip INNER JOIN $tbl_document pub
3373
                            ON ip.ref = pub.id
3374
                    WHERE 	ip.c_id  = $course_id AND
3375
                            pub.c_id  = $course_id AND
3376
                            pub.filetype ='file' AND
3377
                            ip.tool = 'document'
3378
                            $condition_user $condition_session ";
3379
            $rs = Database::query($sql);
3380
            $row = Database::fetch_array($rs, 'ASSOC');
3381
            return $row['count'];
3382
        }
3383
        return null;
3384
    }
3385
3386
    /**
3387
     * Count assignments per student
3388
     * @param    int|array   Student id(s)
3389
     * @param    string        Course code
3390
     * @param    int            Session id (optional),
3391
     * if param $session_id is null(default) return count of assignments
3392
     * including sessions, 0 = session is not filtered
3393
     * @return    int            Count of assignments
3394
     */
3395
    public static function count_student_assignments($student_id, $course_code = null, $session_id = null)
3396
    {
3397
        if (empty($student_id)) {
3398
            return 0;
3399
        }
3400
3401
        $conditions = array();
3402
3403
        // Get the information of the course
3404
        $a_course = CourseManager::get_course_information($course_code);
3405
        if (!empty($a_course)) {
3406
            $course_id = $a_course['real_id'];
3407
            $conditions[]= " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
3408
        }
3409
3410
        // table definition
3411
        $tbl_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3412
        $tbl_student_publication = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
3413
3414 View Code Duplication
        if (is_array($student_id)) {
3415
            $studentList = array_map('intval', $student_id);
3416
            $conditions[]= " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
3417
        } else {
3418
            $student_id = intval($student_id);
3419
            $conditions[]= " ip.insert_user_id = '$student_id' ";
3420
        }
3421
        if (isset($session_id)) {
3422
            $session_id = intval($session_id);
3423
            $conditions[]= " pub.session_id = $session_id ";
3424
        }
3425
        $conditionToString = implode('AND', $conditions);
3426
3427
        $sql = "SELECT count(ip.tool) as count
3428
                FROM $tbl_item_property ip
3429
                INNER JOIN $tbl_student_publication pub ON ip.ref = pub.id
3430
                WHERE
3431
                    ip.tool='work' AND
3432
                    $conditionToString";
3433
        $rs = Database::query($sql);
3434
        $row = Database::fetch_array($rs, 'ASSOC');
3435
        return $row['count'];
3436
    }
3437
3438
    /**
3439
     * Count messages per student inside forum tool
3440
     * @param    int|array        Student id
3441
     * @param    string    Course code
3442
     * @param    int        Session id (optional), if param $session_id is
3443
     * null(default) return count of messages including sessions, 0 = session is not filtered
3444
     * @return    int        Count of messages
3445
     */
3446
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
3447
    {
3448
        if (empty($student_id)) {
3449
            return 0;
3450
        }
3451
3452
        $courseInfo = api_get_course_info($courseCode);
3453
        $courseCondition = null;
3454
        $conditions = array();
3455
        if (!empty($courseInfo)) {
3456
            $course_id	    = $courseInfo['real_id'];
3457
            $conditions[]= " post.c_id  = $course_id AND forum.c_id = $course_id ";
3458
        }
3459
3460
        // Table definition.
3461
        $tbl_forum_post = Database :: get_course_table(TABLE_FORUM_POST);
3462
        $tbl_forum = Database :: get_course_table(TABLE_FORUM);
3463
3464 View Code Duplication
        if (is_array($student_id)) {
3465
            $studentList = array_map('intval', $student_id);
3466
            $conditions[]= " post.poster_id IN ('".implode("','", $studentList)."') ";
3467
        } else {
3468
            $student_id = intval($student_id);
3469
            $conditions[]= " post.poster_id = '$student_id' ";
3470
        }
3471
3472
        if (isset($session_id)) {
3473
            $session_id = intval($session_id);
3474
            $conditions[]= " forum.session_id = $session_id";
3475
        }
3476
3477
        $conditionsToString = implode('AND ', $conditions);
3478
        $sql = "SELECT count(poster_id) as count
3479
                FROM $tbl_forum_post post INNER JOIN $tbl_forum forum
3480
                ON forum.forum_id = post.forum_id
3481
                WHERE $conditionsToString";
3482
3483
        $rs = Database::query($sql);
3484
        $row = Database::fetch_array($rs, 'ASSOC');
3485
        $count = $row['count'];
3486
3487
        return $count;
3488
    }
3489
3490
    /**
3491
     * This function counts the number of post by course
3492
     * @param      string     Course code
3493
     * @param    int        Session id (optional), if param $session_id is
3494
     * null(default) it'll return results including sessions,
3495
     * 0 = session is not filtered
3496
     * @param int $groupId
3497
     * @return    int     The number of post by course
3498
     */
3499 View Code Duplication
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
3500
    {
3501
        $courseInfo = api_get_course_info($course_code);
3502
        if (!empty($courseInfo)) {
3503
            $tbl_posts = Database :: get_course_table(TABLE_FORUM_POST);
3504
            $tbl_forums = Database :: get_course_table(TABLE_FORUM);
3505
3506
            $condition_session = '';
3507
            if (isset($session_id)) {
3508
                $session_id = intval($session_id);
3509
                $condition_session = api_get_session_condition($session_id, true,  false, 'f.session_id');
3510
            }
3511
3512
            $course_id = $courseInfo['real_id'];
3513
            $groupId = intval($groupId);
3514
            if (!empty($groupId)) {
3515
                $groupCondition = " i.to_group_id = $groupId  ";
3516
            } else {
3517
                $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
3518
            }
3519
3520
            $item = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3521
            $sql = "SELECT count(*) FROM $tbl_posts p
3522
                    INNER JOIN $tbl_forums f
3523
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
3524
                    INNER JOIN $item i
3525
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
3526
                    WHERE
3527
                        p.c_id = $course_id AND
3528
                        f.c_id = $course_id AND
3529
                        $groupCondition
3530
                        $condition_session
3531
                    ";
3532
            $result = Database::query($sql);
3533
            $row = Database::fetch_row($result);
3534
            $count = $row[0];
3535
3536
            return $count;
3537
        } else {
3538
            return null;
3539
        }
3540
    }
3541
3542
    /**
3543
     * This function counts the number of threads by course
3544
     * @param      string     Course code
3545
     * @param    int        Session id (optional),
3546
     * if param $session_id is null(default) it'll return results including
3547
     * sessions, 0 = session is not filtered
3548
     * @param int $groupId
3549
     * @return    int     The number of threads by course
3550
     */
3551 View Code Duplication
    public static function count_number_of_threads_by_course($course_code, $session_id = null, $groupId = 0)
3552
    {
3553
        $course_info = api_get_course_info($course_code);
3554
        if (empty($course_info)) {
3555
            return null;
3556
        }
3557
3558
        $course_id = $course_info['real_id'];
3559
        $tbl_threads = Database :: get_course_table(TABLE_FORUM_THREAD);
3560
        $tbl_forums = Database :: get_course_table(TABLE_FORUM);
3561
3562
        $condition_session = '';
3563
        if (isset($session_id)) {
3564
            $session_id = intval($session_id);
3565
            $condition_session = ' AND f.session_id = '. $session_id;
3566
        }
3567
3568
        $groupId = intval($groupId);
3569
3570
        if (!empty($groupId)) {
3571
            $groupCondition = " i.to_group_id = $groupId ";
3572
        } else {
3573
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
3574
        }
3575
3576
        $item = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3577
        $sql = "SELECT count(*)
3578
                FROM $tbl_threads t
3579
                INNER JOIN $tbl_forums f
3580
                ON f.iid = t.forum_id AND f.c_id = t.c_id
3581
                INNER JOIN $item i
3582
                ON (
3583
                    tool = '".TOOL_FORUM_THREAD."' AND
3584
                    f.c_id = i.c_id AND
3585
                    t.iid = i.ref
3586
                )
3587
                WHERE
3588
                    t.c_id = $course_id AND
3589
                    f.c_id = $course_id AND
3590
                    $groupCondition
3591
                    $condition_session
3592
                ";
3593
3594
        $result = Database::query($sql);
3595
        if (Database::num_rows($result)) {
3596
            $row = Database::fetch_row($result);
3597
            $count = $row[0];
3598
3599
            return $count;
3600
        } else {
3601
3602
            return null;
3603
        }
3604
    }
3605
3606
    /**
3607
     * This function counts the number of forums by course
3608
     * @param      string     Course code
3609
     * @param    int        Session id (optional),
3610
     * if param $session_id is null(default) it'll return results
3611
     * including sessions, 0 = session is not filtered
3612
     * @param int $groupId
3613
     * @return    int     The number of forums by course
3614
     */
3615
    public static function count_number_of_forums_by_course($course_code, $session_id = null, $groupId = 0)
3616
    {
3617
        $course_info = api_get_course_info($course_code);
3618
        if (empty($course_info)) {
3619
            return null;
3620
        }
3621
        $course_id = $course_info['real_id'];
3622
3623
        $condition_session = '';
3624
        if (isset($session_id)) {
3625
             $session_id = intval($session_id);
3626
             $condition_session = ' AND f.session_id = '. $session_id;
3627
        }
3628
3629
        $groupId = intval($groupId);
3630
        if (!empty($groupId)) {
3631
            $groupCondition = " i.to_group_id = $groupId ";
3632
        } else {
3633
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
3634
        }
3635
3636
        $tbl_forums = Database :: get_course_table(TABLE_FORUM);
3637
        $item = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3638
3639
        $sql = "SELECT count(*)
3640
                FROM $tbl_forums f
3641
                INNER JOIN $item i
3642
                    ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
3643
                WHERE
3644
                    f.c_id = $course_id AND
3645
                    $groupCondition
3646
                    $condition_session
3647
                ";
3648
        $result = Database::query($sql);
3649
        if (Database::num_rows($result)) {
3650
            $row = Database::fetch_row($result);
3651
            $count = $row[0];
3652
            return $count;
3653
        } else {
3654
            return null;
3655
        }
3656
    }
3657
3658
    /**
3659
     * This function counts the chat last connections by course in x days
3660
     * @param      string     Course code
3661
     * @param      int     Last x days
3662
     * @param    int        Session id (optional)
3663
     * @return     int     Chat last connections by course in x days
3664
     */
3665
    public static function chat_connections_during_last_x_days_by_course($course_code, $last_days, $session_id = 0)
3666
    {
3667
        $course_info = api_get_course_info($course_code);
3668
        if (empty($course_info)) {
3669
            return null;
3670
        }
3671
        $course_id = $course_info['real_id'];
3672
3673
        //protect data
3674
        $last_days   = intval($last_days);
3675
        $session_id  = intval($session_id);
3676
        $tbl_stats_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
3677
        $now = api_get_utc_datetime();
3678
        $sql = "SELECT count(*) FROM $tbl_stats_access
3679
                WHERE
3680
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
3681
                    c_id = '$course_id' AND
3682
                    access_tool='".TOOL_CHAT."' AND
3683
                    access_session_id='$session_id' ";
3684
        $result = Database::query($sql);
3685
        if (Database::num_rows($result)) {
3686
            $row = Database::fetch_row($result);
3687
            $count = $row[0];
3688
            return $count;
3689
        } else {
3690
            return null;
3691
        }
3692
    }
3693
3694
    /**
3695
     * This function gets the last student's connection in chat
3696
     * @param      int     Student id
3697
     * @param      string     Course code
3698
     * @param    int        Session id (optional)
3699
     * @return     string    datetime formatted without day (e.g: February 23, 2010 10:20:50 )
3700
     */
3701
    public static function chat_last_connection($student_id, $courseId, $session_id = 0)
3702
    {
3703
        $student_id = intval($student_id);
3704
        $courseId = intval($courseId);
3705
        $session_id    = intval($session_id);
3706
        $date_time  = '';
3707
3708
        // table definition
3709
        $tbl_stats_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
3710
        $sql = "SELECT access_date
3711
                FROM $tbl_stats_access
3712
                WHERE
3713
                     access_tool='".TOOL_CHAT."' AND
3714
                     access_user_id='$student_id' AND
3715
                     c_id = $courseId AND
3716
                     access_session_id = '$session_id'
3717
                ORDER BY access_date DESC limit 1";
3718
        $rs = Database::query($sql);
3719 View Code Duplication
        if (Database::num_rows($rs) > 0) {
3720
            $row = Database::fetch_array($rs);
3721
            $date_time = api_convert_and_format_date(
3722
                $row['access_date'],
3723
                null,
3724
                date_default_timezone_get()
3725
            );
3726
        }
3727
        return $date_time;
3728
    }
3729
3730
    /**
3731
     * Get count student's visited links
3732
     * @param    int        Student id
3733
     * @param    int    $courseId
3734
     * @param    int        Session id (optional)
3735
     * @return    int        count of visited links
3736
     */
3737 View Code Duplication
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
3738
    {
3739
        $student_id  = intval($student_id);
3740
        $courseId = intval($courseId);
3741
        $session_id  = intval($session_id);
3742
3743
        // table definition
3744
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
3745
3746
        $sql = 'SELECT 1
3747
                FROM '.$table.'
3748
                WHERE
3749
                    links_user_id= '.$student_id.' AND
3750
                    c_id = "'.$courseId.'" AND
3751
                    links_session_id = '.$session_id.' ';
3752
3753
        $rs = Database::query($sql);
3754
        return Database::num_rows($rs);
3755
    }
3756
3757
    /**
3758
     * Get count student downloaded documents
3759
     * @param    int        Student id
3760
     * @param    int    $courseId
3761
     * @param    int        Session id (optional)
3762
     * @return    int        Count downloaded documents
3763
     */
3764 View Code Duplication
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
3765
    {
3766
        $student_id  = intval($student_id);
3767
        $courseId = intval($courseId);
3768
        $session_id  = intval($session_id);
3769
3770
        // table definition
3771
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
3772
3773
        $sql = 'SELECT 1
3774
                FROM ' . $table . '
3775
                WHERE down_user_id = '.$student_id.'
3776
                AND c_id  = "'.$courseId.'"
3777
                AND down_session_id = '.$session_id.' ';
3778
        $rs = Database::query($sql);
3779
3780
        return Database::num_rows($rs);
3781
    }
3782
3783
    /**
3784
     * Get course list inside a session from a student
3785
     * @param    int        $user_id Student id
3786
     * @param    int        $id_session Session id (optional)
3787
     * @return    array    Courses list
3788
     */
3789
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
3790
    {
3791
        $user_id = intval($user_id);
3792
        $id_session = intval($id_session);
3793
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3794
        $courseTable = Database :: get_main_table(TABLE_MAIN_COURSE);
3795
3796
        $sql = "SELECT c.code
3797
                FROM $tbl_session_course_user sc
3798
                INNER JOIN $courseTable c
3799
                WHERE
3800
                    user_id= $user_id  AND
3801
                    session_id = $id_session";
3802
        $result = Database::query($sql);
3803
        $courses = array();
3804
        while ($row = Database::fetch_array($result)) {
3805
            $courses[$row['code']] = $row['code'];
3806
        }
3807
3808
        return $courses;
3809
    }
3810
3811
    /**
3812
     * Get inactive students in course
3813
     * @param    int   $courseId
3814
     * @param    string  $since  Since login course date (optional, default = 'never')
3815
     * @param    int        $session_id    (optional)
3816
     * @return    array    Inactive users
3817
     */
3818
    public static function getInactiveStudentsInCourse($courseId, $since = 'never', $session_id = 0)
3819
    {
3820
        $tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3821
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3822
        $table_course_rel_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
3823
        $tableCourse = Database :: get_main_table(TABLE_MAIN_COURSE);
3824
        $now = api_get_utc_datetime();
3825
        $courseId = intval($courseId);
3826
3827
        if (empty($courseId)) {
3828
            return false;
3829
        }
3830
3831
        if (empty($session_id)) {
3832
            $inner = '
3833
                INNER JOIN '.$table_course_rel_user.' course_user
3834
                ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id
3835
            ';
3836
        } else {
3837
            $inner = '
3838
                    INNER JOIN '.$tbl_session_course_user.' session_course_user
3839
                    ON
3840
                        c.id = session_course_user.c_id AND
3841
                        session_course_user.session_id = '.intval($session_id).' AND
3842
                        session_course_user.user_id = stats_login.user_id ';
3843
        }
3844
3845
        $sql = 'SELECT stats_login.user_id, MAX(login_course_date) max_date
3846
                FROM '.$tbl_track_login.' stats_login
3847
                INNER JOIN '.$tableCourse.' c
3848
                ON (c.id = stats_login.c_id)
3849
                '.$inner.'
3850
                WHERE c.id = '.$courseId.'
3851
                GROUP BY stats_login.user_id
3852
                HAVING DATE_SUB( "' . $now . '", INTERVAL '.$since.' DAY) > max_date ';
3853
3854
        if ($since == 'never') {
3855
            if (empty($session_id)) {
3856
                $sql = 'SELECT course_user.user_id
3857
                        FROM ' . $table_course_rel_user . ' course_user
3858
                        LEFT JOIN ' . $tbl_track_login . ' stats_login
3859
                        ON course_user.user_id = stats_login.user_id AND
3860
                        relation_type<>' . COURSE_RELATION_TYPE_RRHH . '
3861
                        INNER JOIN ' . $tableCourse . ' c
3862
                        ON (c.id = course_user.c_id)
3863
                        WHERE
3864
                            course_user.c_id = ' . $courseId . ' AND
3865
                            stats_login.login_course_date IS NULL
3866
                        GROUP BY course_user.user_id';
3867
            } else {
3868
                $sql = 'SELECT session_course_user.user_id
3869
                        FROM '.$tbl_session_course_user.' session_course_user
3870
                        LEFT JOIN ' . $tbl_track_login . ' stats_login
3871
                        ON session_course_user.user_id = stats_login.user_id
3872
                        INNER JOIN ' . $tableCourse . ' c
3873
                        ON (c.id = session_course_user.c_id)
3874
                        WHERE
3875
                            session_course_user.c_id = ' . $courseId . ' AND
3876
                            stats_login.login_course_date IS NULL
3877
                        GROUP BY session_course_user.user_id';
3878
3879
            }
3880
        }
3881
3882
        $rs = Database::query($sql);
3883
        $inactive_users = array();
3884
        while($user = Database::fetch_array($rs)) {
3885
            $inactive_users[] = $user['user_id'];
3886
        }
3887
3888
        return $inactive_users;
3889
    }
3890
3891
    /**
3892
     * Get count login per student
3893
     * @param    int    $student_id    Student id
3894
     * @param    int    $courseId
3895
     * @param    int    $session_id    Session id (optional)
3896
     * @return    int        count login
3897
     */
3898
    public static function count_login_per_student($student_id, $courseId, $session_id = 0)
3899
    {
3900
        $student_id  = intval($student_id);
3901
        $courseId = intval($courseId);
3902
        $session_id  = intval($session_id);
3903
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
3904
3905
        $sql = 'SELECT '.$student_id.'
3906
                FROM ' . $table . '
3907
                WHERE
3908
                    access_user_id=' . $student_id . ' AND
3909
                    c_id="' . $courseId . '" AND
3910
                    access_session_id = "'.$session_id.'" ';
3911
3912
        $rs = Database::query($sql);
3913
        $nb_login = Database::num_rows($rs);
3914
3915
        return $nb_login;
3916
    }
3917
3918
    /**
3919
     * Get students followed by a human resources manager
3920
     * @param    int        Drh id
3921
     * @return    array    Student list
3922
     */
3923
    public static function get_student_followed_by_drh($hr_dept_id)
3924
    {
3925
        $hr_dept_id = intval($hr_dept_id);
3926
        $a_students = array();
3927
        $tbl_user     = Database :: get_main_table(TABLE_MAIN_USER);
3928
3929
        $sql = 'SELECT DISTINCT user_id FROM '.$tbl_user.' as user
3930
                WHERE hr_dept_id='.$hr_dept_id;
3931
        $rs = Database::query($sql);
3932
3933
        while($user = Database :: fetch_array($rs)) {
3934
            $a_students[$user['user_id']] = $user['user_id'];
3935
        }
3936
3937
        return $a_students;
3938
    }
3939
3940
3941
3942
    /**
3943
     * get count clicks about tools most used by course
3944
     * @param    int      $courseId
3945
     * @param    int        Session id (optional),
3946
     * if param $session_id is null(default) it'll return results
3947
     * including sessions, 0 = session is not filtered
3948
     * @return    array     tools data
3949
     */
3950 View Code Duplication
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
3951
    {
3952
        $courseId = intval($courseId);
3953
        $data = array();
3954
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
3955
        $condition_session     = '';
3956
        if (isset($session_id)) {
3957
            $session_id = intval($session_id);
3958
            $condition_session = ' AND access_session_id = '. $session_id;
3959
        }
3960
        $sql = "SELECT
3961
                    access_tool,
3962
                    COUNT(DISTINCT access_user_id),
3963
                    count(access_tool) as count_access_tool
3964
                FROM $TABLETRACK_ACCESS
3965
                WHERE
3966
                    access_tool IS NOT NULL AND
3967
                    access_tool != '' AND
3968
                    c_id = '$courseId'
3969
                    $condition_session
3970
                GROUP BY access_tool
3971
                ORDER BY count_access_tool DESC
3972
                LIMIT 0, 3";
3973
        $rs = Database::query($sql);
3974
        if (Database::num_rows($rs) > 0) {
3975
            while ($row = Database::fetch_array($rs)) {
3976
                $data[] = $row;
3977
            }
3978
        }
3979
        return $data;
3980
    }
3981
    /**
3982
     * Get total clicks
3983
     * THIS FUNCTION IS NOT BEEN USED, IT WAS MEANT TO BE USE WITH track_e_course_access.date_from and track_e_course_access.date_to,
3984
     * BUT NO ROW MATCH THE CONDITION, IT SHOULD BE FINE TO USE IT WHEN YOU USE USER DEFINED DATES AND NO CHAMILO DATES
3985
     * @param   int     User Id
3986
     * @param   int     Course Id
3987
     * @param   int     Session Id (optional), if param $session_id is null(default) it'll return results including sessions, 0 = session is not filtered
3988
     * @param   string  Date from
3989
     * @param   string  Date to
3990
     * @return  array   Data
3991
     * @author  César Perales [email protected] 2014-01-16
3992
     */
3993
    public static function get_total_clicks($userId, $courseId, $sessionId = 0, $date_from = '', $date_to = '')
3994
    {
3995
        $course = api_get_course_info_by_id($courseId);
3996
        $tables = array(
3997
            TABLE_STATISTIC_TRACK_E_LASTACCESS => array(
3998
                'course'    => 'c_id',
3999
                'session'   => 'access_session_id',
4000
                'user'      => 'access_user_id',
4001
                'start_date'=> 'access_date',
4002
            ),
4003
            TABLE_STATISTIC_TRACK_E_ACCESS => array(
4004
                'course'    => 'c_id',
4005
                'session'   => 'access_session_id',
4006
                'user'      => 'access_user_id',
4007
                'start_date'=> 'access_date',
4008
            ),
4009
            #TABLE_STATISTIC_TRACK_E_LOGIN, array(,, 'login_date', 'logout_date');
4010
            TABLE_STATISTIC_TRACK_E_DOWNLOADS => array(
4011
                'course'    => 'c_id',
4012
                'session'   => 'down_session_id',
4013
                'user'      => 'down_user_id',
4014
                'start_date'=> 'down_date',
4015
                ),
4016
            TABLE_STATISTIC_TRACK_E_LINKS => array(
4017
                'course'    => 'c_id',
4018
                'session'   => 'links_session_id',
4019
                'user'      => 'links_user_id',
4020
                'start_date'=> 'links_date',
4021
            ),
4022
            TABLE_STATISTIC_TRACK_E_ONLINE => array(
4023
                'course'    => 'c_id',
4024
                'session'   => 'session_id',
4025
                'user'      => 'login_user_id',
4026
                'start_date'=> 'login_date',
4027
            ),
4028
            #TABLE_STATISTIC_TRACK_E_HOTPOTATOES,
4029
            /*TABLE_STATISTIC_TRACK_E_COURSE_ACCESS => array(
4030
                'course'    => 'c_id',
4031
                'session'   => 'session_id',
4032
                'user'      => 'user_id',
4033
                'start_date'=> 'login_course_date',
4034
                'end_date'  => 'logout_course_date',
4035
                ),*/
4036
            TABLE_STATISTIC_TRACK_E_EXERCISES => array(
4037
                'course'    => 'c_id',
4038
                'session'   => 'session_id',
4039
                'user'      => 'exe_user_id',
4040
                'start_date'=> 'exe_date',
4041
            ),
4042
            TABLE_STATISTIC_TRACK_E_ATTEMPT => array(
4043
                'course'    => 'c_id',
4044
                'session'   => 'session_id',
4045
                'user'      => 'user_id',
4046
                'start_date'=> 'tms',
4047
            ),
4048
            #TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING,
4049
            #TABLE_STATISTIC_TRACK_E_DEFAULT,
4050
            TABLE_STATISTIC_TRACK_E_UPLOADS => array(
4051
                'course'    => 'c_id',
4052
                'session'   => 'upload_session_id',
4053
                'user'      => 'upload_user_id',
4054
                'start_date'=> 'upload_date',
4055
            ),
4056
        );
4057
4058
        foreach ($tables as $tableName => $fields) {
4059
            //If session is defined, add it to query
4060
            $where = '';
4061
            if (isset($sessionId) && !empty($sessionId)) {
4062
                $sessionField = $fields['session'];
4063
                $where .= " AND $sessionField = $sessionId";
4064
            }
4065
4066
            //filter by date
4067
            if (!empty($date_from) && !empty($date_to)) {
4068
                $fieldStartDate = $fields['start_date'];
4069
                if (!isset($fields['end_date'])) {
4070
                    $where .= sprintf(" AND ($fieldStartDate BETWEEN '%s' AND '%s' )", $date_from, $date_to) ;
4071
                } else {
4072
                    $fieldEndDate = $fields['end_date'];
4073
                    $where .= sprintf(" AND fieldStartDate >= '%s'
4074
                        AND $fieldEndDate <= '%s'", $date_from, $date_to);
4075
                }
4076
            }
4077
4078
            //query
4079
            $sql = "SELECT %s as user, count(*) as total
4080
                FROM %s
4081
                WHERE %s = '%s'
4082
                AND %s = %s
4083
                $where
4084
                GROUP BY %s";
4085
            $sql = sprintf($sql,
4086
                $fields['user'],    //user field
4087
                $tableName,         //FROM
4088
                $fields['course'],  //course condition
4089
                $course['real_id'],    //course condition
4090
                $fields['user'],    //user condition
4091
                $userId,            //user condition
4092
                $fields['user']     //GROUP BY
4093
                );
4094
            $rs = Database::query($sql);
4095
4096
            //iterate query
4097
            if (Database::num_rows($rs) > 0) {
4098
                while ($row = Database::fetch_array($rs)) {
4099
                    $data[$row['user']] = (isset($data[$row['user']])) ?  $data[$row['user']] + $row[total]: $row['total'];
4100
                }
4101
            }
4102
        }
4103
4104
        return $data;
4105
    }
4106
4107
    /**
4108
     * get documents most downloaded by course
4109
     * @param      string     Course code
4110
     * @param    int        Session id (optional),
4111
     * if param $session_id is null(default) it'll return results including
4112
     * sessions, 0 = session is not filtered
4113
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4114
     * @return    array     documents downloaded
4115
     */
4116 View Code Duplication
    public static function get_documents_most_downloaded_by_course($course_code, $session_id = null, $limit = 0)
4117
    {
4118
        //protect data
4119
        $courseId = api_get_course_int_id($course_code);
4120
        $data = array();
4121
4122
        $TABLETRACK_DOWNLOADS   = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4123
        $condition_session = '';
4124
        if (isset($session_id)) {
4125
            $session_id = intval($session_id);
4126
            $condition_session = ' AND down_session_id = '. $session_id;
4127
        }
4128
        $sql = "SELECT down_doc_path, COUNT(DISTINCT down_user_id), COUNT(down_doc_path) as count_down
4129
                FROM $TABLETRACK_DOWNLOADS
4130
                WHERE c_id = $courseId
4131
                    $condition_session
4132
                GROUP BY down_doc_path
4133
                ORDER BY count_down DESC
4134
                LIMIT 0,  $limit";
4135
        $rs = Database::query($sql);
4136
4137
        if (Database::num_rows($rs) > 0) {
4138
            while ($row = Database::fetch_array($rs)) {
4139
                $data[] = $row;
4140
            }
4141
        }
4142
        return $data;
4143
    }
4144
4145
    /**
4146
     * get links most visited by course
4147
     * @param      string     Course code
4148
     * @param    int        Session id (optional),
4149
     * if param $session_id is null(default) it'll
4150
     * return results including sessions, 0 = session is not filtered
4151
     * @return    array     links most visited
4152
     */
4153
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4154
    {
4155
        $course_code = Database::escape_string($course_code);
4156
        $course_info = api_get_course_info($course_code);
4157
        $course_id = $course_info['real_id'];
4158
        $data = array();
4159
4160
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4161
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4162
4163
        $condition_session = '';
4164
        if (isset($session_id)) {
4165
            $session_id = intval($session_id);
4166
            $condition_session = ' AND cl.session_id = '.$session_id;
4167
        }
4168
4169
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4170
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4171
                WHERE
4172
                    cl.c_id = $course_id AND
4173
                    sl.links_link_id = cl.id AND
4174
                    sl.c_id = $course_id
4175
                    $condition_session
4176
                GROUP BY cl.title, cl.url
4177
                ORDER BY count_visits DESC
4178
                LIMIT 0, 3";
4179
        $rs = Database::query($sql);
4180
        if (Database::num_rows($rs) > 0) {
4181
            while ($row = Database::fetch_array($rs)) {
4182
                $data[] = $row;
4183
            }
4184
        }
4185
        return $data;
4186
    }
4187
4188
    /**
4189
     * Shows the user progress (when clicking in the Progress tab)
4190
     *
4191
     * @param int $user_id
4192
     * @param int $session_id
4193
     * @param string $extra_params
4194
     * @param bool $show_courses
4195
     * @param bool $showAllSessions
4196
     *
4197
     * @return string
4198
     */
4199
    public static function show_user_progress(
4200
        $user_id,
4201
        $session_id = 0,
4202
        $extra_params = '',
4203
        $show_courses = true,
4204
        $showAllSessions = true
4205
    ) {
4206
        $tbl_course = Database :: get_main_table(TABLE_MAIN_COURSE);
4207
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
4208
        $tbl_course_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
4209
        $tbl_access_rel_course = Database :: get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4210
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4211
        $tbl_access_rel_session = Database :: get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4212
4213
        $user_id = intval($user_id);
4214
        $session_id = intval($session_id);
4215
4216
        if (api_is_multiple_url_enabled()) {
4217
            $sql = "SELECT c.code, title
4218
                    FROM $tbl_course_user cu
4219
                    INNER JOIN $tbl_course c
4220
                    ON (cu.c_id = c.id)
4221
                    INNER JOIN $tbl_access_rel_course a
4222
                    ON (a.c_id = c.id)
4223
                    WHERE
4224
                        cu.user_id = $user_id AND
4225
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4226
                        access_url_id = ".api_get_current_access_url_id()."
4227
                    ORDER BY title";
4228
        } else {
4229
            $sql = "SELECT c.code, title
4230
                    FROM $tbl_course_user u
4231
                    INNER JOIN $tbl_course c ON (c_id = c.id)
4232
                    WHERE
4233
                        u.user_id= $user_id AND
4234
                        relation_type<>".COURSE_RELATION_TYPE_RRHH."
4235
                    ORDER BY title";
4236
        }
4237
4238
        $rs = Database::query($sql);
4239
        $courses = $course_in_session = $temp_course_in_session = array();
4240
        while ($row = Database :: fetch_array($rs, 'ASSOC')) {
4241
            $courses[$row['code']] = $row['title'];
4242
        }
4243
4244
        $orderBy = " ORDER BY name ";
4245
        $extraInnerJoin = null;
4246
4247
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4248
            $orderBy = " ORDER BY s.id, position ";
4249
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4250
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4251
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4252
        }
4253
4254
        $sessionCondition = '';
4255
        if (!empty($session_id)) {
4256
            $sessionCondition = " AND s.id = $session_id";
4257
        }
4258
4259
        // Get the list of sessions where the user is subscribed as student
4260
        if (api_is_multiple_url_enabled()) {
4261
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4262
                    FROM $tbl_session_course_user cu
4263
                    INNER JOIN $tbl_access_rel_session a
4264
                    ON (a.session_id = cu.session_id)
4265
                    INNER JOIN $tbl_session s
4266
                    ON (s.id = a.session_id)
4267
                    INNER JOIN $tbl_course c
4268
                    ON (c.id = cu.c_id)
4269
                    $extraInnerJoin
4270
                    WHERE
4271
                        cu.user_id = $user_id AND
4272
                        access_url_id = ".api_get_current_access_url_id()."
4273
                        $sessionCondition
4274
                    $orderBy ";
4275
        } else {
4276
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4277
                    FROM $tbl_session_course_user cu
4278
                    INNER JOIN $tbl_session s
4279
                    ON (s.id = cu.session_id)
4280
                    INNER JOIN $tbl_course c
4281
                    ON (c.id = cu.c_id)
4282
                    $extraInnerJoin
4283
                    WHERE
4284
                        cu.user_id = $user_id
4285
                        $sessionCondition
4286
                    $orderBy ";
4287
        }
4288
4289
        $rs = Database::query($sql);
4290
        $simple_session_array = array();
4291
        while ($row = Database :: fetch_array($rs)) {
4292
            $course_info = CourseManager::get_course_information($row['code']);
4293
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4294
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
4295
            $simple_session_array[$row['session_id']] = $row['name'];
4296
        }
4297
4298
        foreach ($simple_session_array as $my_session_id => $session_name) {
4299
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4300
            $my_course_data = array();
4301
            foreach ($course_list as $course_data) {
4302
                $my_course_data[$course_data['id']] = $course_data['title'];
4303
            }
4304
4305
            if (empty($session_id)) {
4306
                $my_course_data = utf8_sort($my_course_data);
4307
            }
4308
4309
            $final_course_data = array();
4310
4311
            foreach($my_course_data as $course_id => $value) {
4312
                $final_course_data[$course_id] = $course_list[$course_id];
4313
            }
4314
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4315
            $course_in_session[$my_session_id]['name'] = $session_name;
4316
        }
4317
4318
        $html = '';
4319
4320
        // Course list
4321
4322
        if ($show_courses) {
4323
            if (!empty($courses)) {
4324
                $html .= Display::page_subheader(
4325
                    Display::return_icon('course.png', get_lang('MyCourses'), array(), ICON_SIZE_SMALL).' '.get_lang('MyCourses')
4326
                );
4327
                $html .= '<table class="data_table" width="100%">';
4328
                $html .= '<tr>
4329
                          '.Display::tag('th', get_lang('Course'), array('width'=>'300px')).'
4330
                          '.Display::tag('th', get_lang('TimeSpentInTheCourse'), array('class'=>'head')).'
4331
                          '.Display::tag('th', get_lang('Progress'), array('class'=>'head')).'
4332
                          '.Display::tag('th', get_lang('Score').Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage'), array('align' => 'absmiddle', 'hspace' => '3px')),array('class'=>'head')).'
4333
                          '.Display::tag('th', get_lang('LastConnexion'), array('class'=>'head')).'
4334
                          '.Display::tag('th', get_lang('Details'), array('class'=>'head')).'
4335
                        </tr>';
4336
4337
                foreach ($courses as $course_code => $course_title) {
4338
                    $courseInfo = api_get_course_info($course_code);
4339
                    $courseId = $courseInfo['real_id'];
4340
4341
                    $total_time_login = Tracking :: get_time_spent_on_the_course(
4342
                        $user_id,
4343
                        $courseId
4344
                    );
4345
                    $time = api_time_to_hms($total_time_login);
4346
                    $progress = Tracking :: get_avg_student_progress(
4347
                        $user_id,
4348
                        $course_code
4349
                    );
4350
                    $percentage_score = Tracking :: get_avg_student_score(
4351
                        $user_id,
4352
                        $course_code,
4353
                        array()
4354
                    );
4355
                    $last_connection = Tracking :: get_last_connection_date_on_the_course(
4356
                        $user_id,
4357
                        $courseInfo
4358
                    );
4359
4360
                    if (is_null($progress)) {
4361
                        $progress = '0%';
4362
                    } else {
4363
                        $progress = $progress.'%';
4364
                    }
4365
4366
                    if (isset($_GET['course']) &&
4367
                        $course_code == $_GET['course'] &&
4368
                        empty($_GET['session_id'])
4369
                    ) {
4370
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D">';
4371
                    } else {
4372
                        $html .= '<tr class="row_even">';
4373
                    }
4374
                    $url = api_get_course_url($course_code, $session_id);
4375
                    $course_url = Display::url($course_title, $url, array('target'=>SESSION_LINK_TARGET));
4376
                    $html .= '<td>'.$course_url.'</td>';
4377
4378
                    $html .= '<td align="center">'.$time.'</td>';
4379
                    $html .= '<td align="center">'.$progress.'</td>';
4380
                    $html .= '<td align="center">';
4381
                    if (is_numeric($percentage_score)) {
4382
                        $html .= $percentage_score.'%';
4383
                    } else {
4384
                        $html .= '0%';
4385
                    }
4386
                    $html .= '</td>';
4387
                    $html .= '<td align="center">'.$last_connection.'</td>';
4388
                    $html .= '<td align="center">';
4389
                    if (isset($_GET['course']) &&
4390
                        $course_code == $_GET['course'] &&
4391
                        empty($_GET['session_id'])
4392
                    ) {
4393
                        $html .= '<a href="#">';
4394
                        $html .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4395 View Code Duplication
                    } else {
4396
                        $html .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'">';
4397
                        $html .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4398
                    }
4399
                    $html .= '</a>';
4400
                    $html .= '</td></tr>';
4401
                }
4402
                $html .= '</table>';
4403
            }
4404
        }
4405
4406
        // Session list
4407
        if (!empty($course_in_session)) {
4408
            $main_session_graph = '';
4409
            //Load graphics only when calling to an specific session
4410
            $session_graph = array();
4411
4412
            $all_exercise_graph_name_list = array();
4413
            $my_results = array();
4414
            $all_exercise_graph_list = array();
4415
4416
            $all_exercise_start_time = array();
4417
4418
            foreach ($course_in_session as $my_session_id => $session_data) {
4419
                $course_list  = $session_data['course_list'];
4420
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
4421
                $exercise_graph_name_list = array();
4422
                //$user_results = array();
4423
                $exercise_graph_list = array();
4424
4425
                foreach ($course_list as $course_data) {
4426
                    $exercise_list = ExerciseLib::get_all_exercises(
4427
                        $course_data,
4428
                        $my_session_id,
4429
                        false,
4430
                        null,
4431
                        false,
4432
                        1
4433
                    );
4434
4435
                    foreach ($exercise_list as $exercise_data) {
4436
                        $exercise_obj = new Exercise($course_data['id']);
4437
                        $exercise_obj->read($exercise_data['id']);
4438
                        //Exercise is not necessary to be visible to show results check the result_disable configuration instead
4439
                        //$visible_return = $exercise_obj->is_visible();
4440
4441
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
4442
4443
                            $best_average = intval(
4444
                                ExerciseLib::get_best_average_score_by_exercise(
4445
                                    $exercise_data['id'],
4446
                                    $course_data['id'],
4447
                                    $my_session_id,
4448
                                    $user_count
4449
                                )
4450
                            );
4451
4452
                            $exercise_graph_list[] = $best_average;
4453
                            $all_exercise_graph_list[] = $best_average;
4454
4455
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
4456
                                api_get_user_id(),
4457
                                $exercise_data['id'],
4458
                                $course_data['real_id'],
4459
                                $my_session_id
4460
                            );
4461
4462
                            $score = 0;
4463
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
4464
                                $score = intval($user_result_data['exe_result']/$user_result_data['exe_weighting'] * 100);
4465
                            }
4466
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
4467
                            $all_exercise_start_time[] = $time;
4468
                            $my_results[] = $score;
4469
                            if (count($exercise_list)<=10) {
4470
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
4471
                                $exercise_graph_name_list[]= $title;
4472
                                $all_exercise_graph_name_list[] = $title;
4473
                            } else {
4474
                                // if there are more than 10 results, space becomes difficult to find, so only show the title of the exercise, not the tool
4475
                                $title = cut($exercise_data['title'], 30);
4476
                                $exercise_graph_name_list[]= $title;
4477
                                $all_exercise_graph_name_list[]= $title;
4478
                            }
4479
                        }
4480
                    }
4481
                }
4482
            }
4483
4484
            // Complete graph
4485
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
4486
                asort($all_exercise_start_time);
4487
4488
                //Fix exams order
4489
                $final_all_exercise_graph_name_list = array();
4490
                $my_results_final = array();
4491
                $final_all_exercise_graph_list = array();
4492
4493
                foreach ($all_exercise_start_time as $key => $time) {
4494
                    $label_time = '';
4495
                    if (!empty($time)) {
4496
                        $label_time = date('d-m-y', $time);
4497
                    }
4498
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
4499
                    $my_results_final[] = $my_results[$key];
4500
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
4501
                }
4502
                $main_session_graph = self::generate_session_exercise_graph(
4503
                    $final_all_exercise_graph_name_list,
4504
                    $my_results_final,
4505
                    $final_all_exercise_graph_list
4506
                );
4507
            }
4508
4509
            $html .= Display::page_subheader(
4510
                Display::return_icon('session.png', get_lang('Sessions'), array(), ICON_SIZE_SMALL) . ' ' . get_lang('Sessions')
4511
            );
4512
4513
            $html .= '<table class="data_table" width="100%">';
4514
            $html .= '<tr>
4515
                  '.Display::tag('th', get_lang('Session'), array('width'=>'300px')).'
4516
                  '.Display::tag('th', get_lang('PublishedExercises'), array('width'=>'300px')).'
4517
                  '.Display::tag('th', get_lang('NewExercises'), array('class'=>'head')).'
4518
                  '.Display::tag('th', get_lang('AverageExerciseResult'), array('class'=>'head')).'
4519
                  '.Display::tag('th', get_lang('Details'), array('class'=>'head')).'
4520
                  </tr>';
4521
4522
            foreach ($course_in_session as $my_session_id => $session_data) {
4523
                $course_list  = $session_data['course_list'];
4524
                $session_name = $session_data['name'];
4525
4526
                if ($showAllSessions == false) {
4527
                    if (isset($session_id) && !empty($session_id)) {
4528
                        if ($session_id != $my_session_id) {
4529
                            continue;
4530
                        }
4531
                    }
4532
                }
4533
4534
                $all_exercises = 0;
4535
                $all_unanswered_exercises_by_user = 0;
4536
                $all_average = 0;
4537
                $stats_array = array();
4538
4539
                foreach ($course_list as $course_data) {
4540
                    //All exercises in the course @todo change for a real count
4541
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
4542
                    $count_exercises = 0;
4543
                    if (is_array($exercises) && !empty($exercises)) {
4544
                        $count_exercises = count($exercises);
4545
                    }
4546
4547
                    // Count of user results
4548
                    $done_exercises = null;
4549
                    $courseInfo = api_get_course_info($course_data['code']);
4550
4551
                    $answered_exercises = 0;
4552
                    if (!empty($exercises)) {
4553
                        foreach ($exercises as $exercise_item) {
4554
                            $attempts = Event::count_exercise_attempts_by_user(
4555
                                api_get_user_id(),
4556
                                $exercise_item['id'],
4557
                                $courseInfo['real_id'],
4558
                                $my_session_id
4559
                            );
4560
                            if ($attempts > 1)  {
4561
                                $answered_exercises++;
4562
                            }
4563
                        }
4564
                    }
4565
4566
                    // Average
4567
                    $average = ExerciseLib::get_average_score_by_course($courseInfo['real_id'], $my_session_id);
4568
                    $all_exercises += $count_exercises;
4569
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
4570
                    $all_average += $average;
4571
                }
4572
4573
                $all_average = $all_average /  count($course_list);
4574
4575
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
4576
                    $html .= '<tr style="background-color:#FBF09D">';
4577
                } else {
4578
                    $html .= '<tr>';
4579
                }
4580
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
4581
4582
                $html .= Display::tag('td', Display::url($session_name, $url, array('target'=>SESSION_LINK_TARGET)));
4583
                $html .= Display::tag('td', $all_exercises);
4584
                $html .= Display::tag('td', $all_unanswered_exercises_by_user);
4585
4586
                //$html .= Display::tag('td', $all_done_exercise);
4587
                $html .= Display::tag('td', ExerciseLib::convert_to_percentage($all_average));
4588
4589
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
4590
                    $icon = Display::url(Display::return_icon('2rightarrow_na.png', get_lang('Details')), '?session_id='.$my_session_id);
4591
                } else {
4592
                    $icon = Display::url(Display::return_icon('2rightarrow.png', get_lang('Details')), '?session_id='.$my_session_id);
4593
                }
4594
                $html .= Display::tag('td', $icon);
4595
                $html .= '</tr>';
4596
            }
4597
            $html .= '</table><br />';
4598
            $html .= Display::div($main_session_graph, array('id'=>'session_graph','class'=>'chart-session', 'style'=>'position:relative; text-align: center;') );
4599
4600
            // Checking selected session.
4601
4602
            if (isset($_GET['session_id'])) {
4603
                $session_id_from_get = intval($_GET['session_id']);
4604
                $session_data 	= $course_in_session[$session_id_from_get];
4605
                $course_list 	= $session_data['course_list'];
4606
4607
                $html .= Display::tag('h3',$session_data['name'].' - '.get_lang('CourseList'));
4608
4609
                $html .= '<table class="data_table" width="100%">';
4610
                //'.Display::tag('th', get_lang('DoneExercises'),         array('class'=>'head')).'
4611
                $html .= '
4612
                    <tr>
4613
                      <th width="300px">'.get_lang('Course').'</th>
4614
                      '.Display::tag('th', get_lang('PublishedExercises'),    	array('class'=>'head')).'
4615
                      '.Display::tag('th', get_lang('NewExercises'),    		array('class'=>'head')).'
4616
                      '.Display::tag('th', get_lang('MyAverage'), 				array('class'=>'head')).'
4617
                      '.Display::tag('th', get_lang('AverageExerciseResult'), 	array('class'=>'head')).'
4618
                      '.Display::tag('th', get_lang('TimeSpentInTheCourse'),    array('class'=>'head')).'
4619
                      '.Display::tag('th', get_lang('LPProgress')     ,      	array('class'=>'head')).'
4620
                      '.Display::tag('th', get_lang('Score').Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage'), array ('align' => 'absmiddle', 'hspace' => '3px')), array('class'=>'head')).'
4621
                      '.Display::tag('th', get_lang('LastConnexion'),         	array('class'=>'head')).'
4622
                      '.Display::tag('th', get_lang('Details'),               	array('class'=>'head')).'
4623
                    </tr>';
4624
4625
                foreach ($course_list as $course_data) {
4626
                    $course_code  = $course_data['code'];
4627
                    $course_title = $course_data['title'];
4628
                    $courseInfo = api_get_course_info($course_code);
4629
                    $courseId = $courseInfo['real_id'];
4630
4631
                    // All exercises in the course @todo change for a real count
4632
                    $exercises = ExerciseLib::get_all_exercises($course_data, $session_id_from_get);
4633
                    $count_exercises = 0;
4634
                    if (!empty($exercises)) {
4635
                        $count_exercises = count($exercises);
4636
                    }
4637
                    $answered_exercises = 0;
4638
                    foreach($exercises as $exercise_item) {
4639
                        $attempts = Event::count_exercise_attempts_by_user(
4640
                            api_get_user_id(),
4641
                            $exercise_item['id'],
4642
                            $courseId,
4643
                            $session_id_from_get
4644
                        );
4645
                        if ($attempts > 1)  {
4646
                            $answered_exercises++;
4647
                        }
4648
                    }
4649
4650
                    $unanswered_exercises = $count_exercises - $answered_exercises;
4651
4652
                    // Average
4653
                    $average = ExerciseLib::get_average_score_by_course($courseId, $session_id_from_get);
4654
                    $my_average	= ExerciseLib::get_average_score_by_course_by_user(api_get_user_id(), $courseId, $session_id_from_get);
4655
4656
                    $stats_array[$course_code] = array(
4657
                        'exercises' => $count_exercises,
4658
                        'unanswered_exercises_by_user' => $unanswered_exercises,
4659
                        'done_exercises' => $done_exercises,
4660
                        'average' => $average,
4661
                        'my_average' => $my_average
4662
                    );
4663
4664
                    $last_connection = Tracking:: get_last_connection_date_on_the_course(
4665
                        $user_id,
4666
                        $courseInfo,
4667
                        $session_id_from_get
4668
                    );
4669
4670
                    $progress = Tracking::get_avg_student_progress(
4671
                        $user_id,
4672
                        $course_code,
4673
                        array(),
4674
                        $session_id_from_get
4675
                    );
4676
4677
                    $total_time_login = Tracking:: get_time_spent_on_the_course(
4678
                        $user_id,
4679
                        $courseId,
4680
                        $session_id_from_get
4681
                    );
4682
                    $time = api_time_to_hms($total_time_login);
4683
4684
                    $percentage_score = Tracking::get_avg_student_score(
4685
                        $user_id,
4686
                        $course_code,
4687
                        array(),
4688
                        $session_id_from_get
4689
                    );
4690
                    $courseCodeFromGet = isset($_GET['course']) ? $_GET['course'] : null;
4691
4692
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
4693
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D" >';
4694
                    } else {
4695
                        $html .= '<tr class="row_even">';
4696
                    }
4697
4698
                    $url = api_get_course_url($course_code, $session_id_from_get);
4699
                    $course_url = Display::url($course_title, $url, array('target' => SESSION_LINK_TARGET));
4700
4701
                    $html .= Display::tag('td', $course_url);
4702
                    $html .= Display::tag('td', $stats_array[$course_code]['exercises']);
4703
                    $html .= Display::tag('td', $stats_array[$course_code]['unanswered_exercises_by_user']);
4704
                    //$html .= Display::tag('td', $stats_array[$course_code]['done_exercises']);
4705
                    $html .= Display::tag('td', ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']));
4706
4707
                    $html .= Display::tag('td', $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')');
4708
                    $html .= Display::tag('td', $time, array('align'=>'center'));
4709
4710
                    if (is_numeric($progress)) {
4711
                        $progress = $progress.'%';
4712
                    } else {
4713
                        $progress = '0%';
4714
                    }
4715
                    // Progress
4716
                    $html .= Display::tag('td', $progress, array('align'=>'center'));
4717
                    if (is_numeric($percentage_score)) {
4718
                        $percentage_score = $percentage_score.'%';
4719
                    } else {
4720
                        $percentage_score = '0%';
4721
                    }
4722
                    //Score
4723
                    $html .= Display::tag('td', $percentage_score, array('align'=>'center'));
4724
                    $html .= Display::tag('td', $last_connection,  array('align'=>'center'));
0 ignored issues
show
Bug introduced by
It seems like $last_connection defined by \Tracking::get_last_conn..., $session_id_from_get) on line 4664 can also be of type boolean; however, Display::tag() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
4725
4726
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
4727
                        $details = '<a href="#">';
4728
                        $details .=Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4729 View Code Duplication
                    } else {
4730
                        $details = '<a href="'.api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'">';
4731
                        $details .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4732
                    }
4733
                    $details .= '</a>';
4734
                    $html .= Display::tag('td', $details, array('align'=>'center'));
4735
                    $html .= '</tr>';
4736
                }
4737
                $html .= '</table>';
4738
            }
4739
        }
4740
4741
        return $html;
4742
    }
4743
4744
    /**
4745
     * Shows the user detail progress (when clicking in the details link)
4746
     * @param   int     $user_id
4747
     * @param   string  $course_code
4748
     * @param   int     $session_id
4749
     * @return  string  html code
4750
     */
4751
    public static function show_course_detail($user_id, $course_code, $session_id)
4752
    {
4753
        $html = '';
4754
        if (isset($course_code)) {
4755
4756
            $user_id = intval($user_id);
4757
            $session_id = intval($session_id);
4758
            $course = Database::escape_string($course_code);
4759
            $course_info = CourseManager::get_course_information($course);
4760
4761
            $html .= Display::page_subheader($course_info['title']);
4762
            $html .= '<table class="data_table" width="100%">';
4763
4764
            //Course details
4765
            $html .= '
4766
                <tr>
4767
                <th class="head" style="color:#000">'.get_lang('Exercises').'</th>
4768
                <th class="head" style="color:#000">'.get_lang('Attempts').'</th>
4769
                <th class="head" style="color:#000">'.get_lang('BestAttempt').'</th>
4770
                <th class="head" style="color:#000">'.get_lang('Ranking').'</th>
4771
                <th class="head" style="color:#000">'.get_lang('BestResultInCourse').'</th>
4772
                <th class="head" style="color:#000">'.get_lang('Statistics').' '.Display :: return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent'), array('align' => 'absmiddle', 'hspace' => '3px')).'</th>
4773
                </tr>';
4774
4775
            if (empty($session_id)) {
4776
                $user_list = CourseManager::get_user_list_from_course_code(
4777
                    $course,
4778
                    $session_id,
4779
                    null,
4780
                    null,
4781
                    STUDENT
4782
                );
4783
            } else {
4784
                $user_list = CourseManager::get_user_list_from_course_code(
4785
                    $course,
4786
                    $session_id,
4787
                    null,
4788
                    null,
4789
                    0
4790
                );
4791
            }
4792
4793
            // Show exercise results of invisible exercises? see BT#4091
4794
            $exercise_list = ExerciseLib::get_all_exercises(
4795
                $course_info,
4796
                $session_id,
4797
                false,
4798
                null,
4799
                false,
4800
                2
4801
            );
4802
4803
            $to_graph_exercise_result = array();
4804
4805
            if (!empty($exercise_list)) {
4806
                $score = $weighting = $exe_id = 0;
4807
                foreach ($exercise_list as $exercices) {
4808
4809
                    $exercise_obj = new Exercise($course_info['real_id']);
4810
                    $exercise_obj->read($exercices['id']);
4811
                    $visible_return = $exercise_obj->is_visible();
4812
4813
                    $score = $weighting = $attempts = 0;
4814
4815
                    // Getting count of attempts by user
4816
                    $attempts = Event::count_exercise_attempts_by_user(
4817
                        api_get_user_id(),
4818
                        $exercices['id'],
4819
                        $course_info['real_id'],
4820
                        $session_id
4821
                    );
4822
4823
                    $html .= '<tr class="row_even">';
4824
                    $url = api_get_path(WEB_CODE_PATH)."exercice/overview.php?cidReq={$course_info['code']}&id_session=$session_id&exerciseId={$exercices['id']}";
4825
4826
                    if ($visible_return['value'] == true) {
4827
                        $exercices['title'] = Display::url(
4828
                            $exercices['title'],
4829
                            $url,
4830
                            array('target' => SESSION_LINK_TARGET)
4831
                        );
4832
                    }
4833
4834
                    $html .= Display::tag('td', $exercices['title']);
4835
4836
                    // Exercise configuration show results or show only score
4837
                    if ($exercices['results_disabled'] == 0 || $exercices['results_disabled'] == 2) {
4838
                        //For graphics
4839
                        $best_exercise_stats = Event::get_best_exercise_results_by_user(
4840
                            $exercices['id'],
4841
                            $course_info['real_id'],
4842
                            $session_id
4843
                        );
4844
4845
                        $to_graph_exercise_result[$exercices['id']] = array(
4846
                            'title' => $exercices['title'],
4847
                            'data' => $best_exercise_stats
4848
                        );
4849
4850
                        $latest_attempt_url = '';
4851
                        $best_score = $position = $percentage_score_result  = '-';
4852
                        $graph = $normal_graph = null;
4853
4854
                        // Getting best results
4855
                        $best_score_data = ExerciseLib::get_best_attempt_in_course(
4856
                            $exercices['id'],
4857
                            $course_info['real_id'],
4858
                            $session_id
4859
                        );
4860
4861
                        $best_score = '';
4862
                        if (!empty($best_score_data)) {
4863
                            $best_score = ExerciseLib::show_score(
4864
                                $best_score_data['exe_result'],
4865
                                $best_score_data['exe_weighting']
4866
                            );
4867
                        }
4868
4869
                        if ($attempts > 0) {
4870
                            $exercise_stat = ExerciseLib::get_best_attempt_by_user(
4871
                                api_get_user_id(),
4872
                                $exercices['id'],
4873
                                $course_info['real_id'],
4874
                                $session_id
4875
                            );
4876
                            if (!empty($exercise_stat)) {
4877
4878
                                // Always getting the BEST attempt
4879
                                $score          = $exercise_stat['exe_result'];
4880
                                $weighting      = $exercise_stat['exe_weighting'];
4881
                                $exe_id         = $exercise_stat['exe_id'];
4882
4883
                                $latest_attempt_url .= api_get_path(WEB_CODE_PATH).'exercice/result.php?id='.$exe_id.'&cidReq='.$course_info['code'].'&show_headers=1&id_session='.$session_id;
4884
                                $percentage_score_result = Display::url(ExerciseLib::show_score($score, $weighting), $latest_attempt_url);
4885
                                $my_score = 0;
4886
                                if (!empty($weighting) && intval($weighting) != 0) {
4887
                                    $my_score = $score/$weighting;
4888
                                }
4889
                                //@todo this function slows the page
4890
                                $position = ExerciseLib::get_exercise_result_ranking($my_score, $exe_id, $exercices['id'], $course_info['code'], $session_id, $user_list);
0 ignored issues
show
Bug introduced by
It seems like $user_list can also be of type integer; however, ExerciseLib::get_exercise_result_ranking() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
4891
4892
                                $graph = self::generate_exercise_result_thumbnail_graph($to_graph_exercise_result[$exercices['id']]);
4893
                                $normal_graph = self::generate_exercise_result_graph($to_graph_exercise_result[$exercices['id']]);
4894
                            }
4895
                        }
4896
                        $html .= Display::div(
4897
                            $normal_graph,
4898
                            array('id'=>'main_graph_'.$exercices['id'],'class'=>'dialog', 'style'=>'display:none')
4899
                        );
4900
4901
                        if (empty($graph)) {
4902
                            $graph = '-';
4903
                        } else {
4904
                            $graph = Display::url(
4905
                                '<img src="' . $graph . '" >',
4906
                                $normal_graph,
4907
                                array(
4908
                                    'id' => $exercices['id'],
4909
                                    'class' => 'expand-image',
4910
                                )
4911
                            );
4912
                        }
4913
4914
                        $html .= Display::tag('td', $attempts, array('align'=>'center'));
4915
                        $html .= Display::tag('td', $percentage_score_result, array('align'=>'center'));
4916
                        $html .= Display::tag('td', $position, array('align'=>'center'));
4917
                        $html .= Display::tag('td', $best_score, array('align'=>'center'));
4918
                        $html .= Display::tag('td', $graph, array('align'=>'center'));
4919
                        //$html .= Display::tag('td', $latest_attempt_url,       array('align'=>'center', 'width'=>'25'));
4920
4921
                    } else {
4922
                        // Exercise configuration NO results
4923
                        $html .= Display::tag('td', $attempts, array('align'=>'center'));
4924
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4925
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4926
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4927
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4928
                    }
4929
                    $html .= '</tr>';
4930
                }
4931
            } else {
4932
                $html .= '<tr><td colspan="5" align="center">'.get_lang('NoEx').'</td></tr>';
4933
            }
4934
            $html .= '</table>';
4935
4936
4937
            // LP table results
4938
            $html .='<table class="data_table">';
4939
            $html .= Display::tag('th', get_lang('Learnpaths'), array('class'=>'head', 'style'=>'color:#000'));
4940
            $html .= Display::tag('th', get_lang('LatencyTimeSpent'), array('class'=>'head', 'style'=>'color:#000'));
4941
            $html .= Display::tag('th', get_lang('Progress'), array('class'=>'head', 'style'=>'color:#000'));
4942
            $html .= Display::tag('th', get_lang('Score'), array('class'=>'head', 'style'=>'color:#000'));
4943
            $html .= Display::tag('th', get_lang('LastConnexion'), array('class'=>'head', 'style'=>'color:#000'));
4944
            $html .= '</tr>';
4945
4946
            $list = new LearnpathList(
4947
                api_get_user_id(),
4948
                $course_info['code'],
4949
                $session_id,
4950
                'publicated_on ASC',
4951
                true,
4952
                null,
4953
                true
4954
            );
4955
4956
            $lp_list = $list->get_flat_list();
4957
4958
            if (!empty($lp_list) > 0) {
4959
                foreach ($lp_list as $lp_id => $learnpath) {
4960
                    $progress = Tracking::get_avg_student_progress($user_id, $course, array($lp_id), $session_id);
4961
                    $last_connection_in_lp = Tracking::get_last_connection_time_in_lp($user_id, $course, $lp_id, $session_id);
4962
                    $time_spent_in_lp = Tracking::get_time_spent_in_lp($user_id, $course, array($lp_id), $session_id);
4963
                    $percentage_score = Tracking::get_avg_student_score($user_id, $course, array($lp_id), $session_id);
4964
                    if (is_numeric($percentage_score)) {
4965
                        $percentage_score = $percentage_score.'%';
4966
                    } else {
4967
                        $percentage_score = '0%';
4968
                    }
4969
4970
                    $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
4971
4972
                    $html .= '<tr class="row_even">';
4973
                    $url = api_get_path(WEB_CODE_PATH)."newscorm/lp_controller.php?cidReq={$course_code}&id_session=$session_id&lp_id=$lp_id&action=view";
4974
4975
                    if ($learnpath['lp_visibility'] == 0) {
4976
                        $html .= Display::tag('td', $learnpath['lp_name']);
4977
                    } else {
4978
                        $html .= Display::tag('td', Display::url($learnpath['lp_name'], $url, array('target'=>SESSION_LINK_TARGET)));
4979
                    }
4980
4981
                    $html .= Display::tag('td', $time_spent_in_lp, array('align'=>'center'));
4982
                    if (is_numeric($progress)) {
4983
                        $progress = $progress.'%';
4984
                    }
4985
                    $html .= Display::tag('td', $progress, array('align'=>'center'));
4986
                    $html .= Display::tag('td', $percentage_score);
4987
4988
                    $last_connection = '-';
4989
                    if (!empty($last_connection_in_lp)) {
4990
                        $last_connection = api_convert_and_format_date($last_connection_in_lp, DATE_TIME_FORMAT_LONG);
4991
                    }
4992
                    $html .= Display::tag('td', $last_connection, array('align'=>'center','width'=>'180px'));
4993
                    $html .= "</tr>";
4994
                }
4995
            } else {
4996
                $html .= '<tr>
4997
                        <td colspan="4" align="center">
4998
                            '.get_lang('NoLearnpath').'
4999
                        </td>
5000
                      </tr>';
5001
            }
5002
            $html .='</table>';
5003
        }
5004
5005
        return $html;
5006
    }
5007
5008
    /**
5009
     * Generates an histogram
5010
     * @param    array    list of exercise names
5011
     * @param    array    my results 0 to 100
5012
     * @param    array    average scores 0-100
5013
     * @return string
5014
     */
5015
    static function generate_session_exercise_graph($names, $my_results, $average)
5016
    {
5017
        /* Create and populate the pData object */
5018
        $myData = new pData();
5019
        $myData->addPoints($names, 'Labels');
5020
        $myData->addPoints($my_results, 'Serie1');
5021
        $myData->addPoints($average, 'Serie2');
5022
        $myData->setSerieWeight('Serie1', 1);
5023
        $myData->setSerieTicks('Serie2', 4);
5024
        $myData->setSerieDescription('Labels', 'Months');
5025
        $myData->setAbscissa('Labels');
5026
        $myData->setSerieDescription('Serie1', get_lang('MyResults'));
5027
        $myData->setSerieDescription('Serie2', get_lang('AverageScore'));
5028
        $myData->setAxisUnit(0, '%');
5029
        $myData->loadPalette(api_get_path(SYS_CODE_PATH) . 'palettes/pchart/default.color', true);
5030
        // Cache definition
5031
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5032
        $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
5033
        $chartHash = $myCache->getHash($myData);
5034
5035
        if ($myCache->isInCache($chartHash)) {
5036
            //if we already created the img
5037
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5038
            $myCache->saveFromCache($chartHash, $imgPath);
5039
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5040
        } else {
5041
            /* Define width, height and angle */
5042
            $mainWidth = 860;
5043
            $mainHeight = 500;
5044
            $angle = 50;
5045
5046
            /* Create the pChart object */
5047
            $myPicture = new pImage($mainWidth, $mainHeight, $myData);
5048
5049
            /* Turn of Antialiasing */
5050
            $myPicture->Antialias = false;
5051
5052
            /* Draw the background */
5053
            $settings = array('R' => 255, 'G' => 255, 'B' => 255);
5054
            $myPicture->drawFilledRectangle(0, 0, $mainWidth, $mainHeight, $settings);
5055
5056
            /* Add a border to the picture */
5057
            $myPicture->drawRectangle(
5058
                0,
5059
                0,
5060
                $mainWidth - 1,
5061
                $mainHeight - 1,
5062
                array('R' => 0, 'G' => 0, 'B' => 0)
5063
            );
5064
5065
            /* Set the default font */
5066
            $myPicture->setFontProperties(
5067
                array(
5068
                    'FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf',
5069
                    'FontSize' => 10)
5070
            );
5071
            /* Write the chart title */
5072
            $myPicture->drawText(
5073
                $mainWidth / 2,
5074
                30,
5075
                get_lang('ExercisesInTimeProgressChart'),
5076
                array(
5077
                    'FontSize' => 12,
5078
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE
5079
                )
5080
            );
5081
5082
            /* Set the default font */
5083
            $myPicture->setFontProperties(
5084
                array(
5085
                    'FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf',
5086
                    'FontSize' => 6
5087
                )
5088
            );
5089
5090
            /* Define the chart area */
5091
            $myPicture->setGraphArea(60, 60, $mainWidth - 60, $mainHeight - 150);
5092
5093
            /* Draw the scale */
5094
            $scaleSettings = array(
5095
                'XMargin' => 10,
5096
                'YMargin' => 10,
5097
                'Floating' => true,
5098
                'GridR' => 200,
5099
                'GridG' => 200,
5100
                'GridB' => 200,
5101
                'DrawSubTicks' => true,
5102
                'CycleBackground' => true,
5103
                'LabelRotation' => $angle,
5104
                'Mode' => SCALE_MODE_ADDALL_START0,
5105
            );
5106
            $myPicture->drawScale($scaleSettings);
5107
5108
            /* Turn on Antialiasing */
5109
            $myPicture->Antialias = true;
5110
5111
            /* Enable shadow computing */
5112
            $myPicture->setShadow(
5113
                true,
5114
                array(
5115
                    'X' => 1,
5116
                    'Y' => 1,
5117
                    'R' => 0,
5118
                    'G' => 0,
5119
                    'B' => 0,
5120
                    'Alpha' => 10
5121
                )
5122
            );
5123
5124
            /* Draw the line chart */
5125
            $myPicture->setFontProperties(
5126
                array(
5127
                    'FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf',
5128
                    'FontSize' => 10
5129
                )
5130
            );
5131
            $myPicture->drawSplineChart();
5132
            $myPicture->drawPlotChart(
5133
                array(
5134
                    'DisplayValues' => true,
5135
                    'PlotBorder' => true,
5136
                    'BorderSize' => 1,
5137
                    'Surrounding' => -60,
5138
                    'BorderAlpha' => 80
5139
                )
5140
            );
5141
5142
            /* Write the chart legend */
5143
            $myPicture->drawLegend(
5144
                $mainWidth / 2 + 50,
5145
                50,
5146
                array(
5147
                    'Style' => LEGEND_BOX,
5148
                    'Mode' => LEGEND_HORIZONTAL,
5149
                    'FontR' => 0,
5150
                    'FontG' => 0,
5151
                    'FontB' => 0,
5152
                    'R' => 220,
5153
                    'G' => 220,
5154
                    'B' => 220,
5155
                    'Alpha' => 100
5156
                )
5157
            );
5158
5159
            $myCache->writeToCache($chartHash, $myPicture);
5160
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5161
            $myCache->saveFromCache($chartHash, $imgPath);
5162
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5163
        }
5164
5165
        $html = '<img src="' . $imgPath . '">';
5166
5167
        return $html;
5168
    }
5169
5170
    /**
5171
     *
5172
     * Returns a thumbnail of the function generate_exercise_result_graph
5173
     * @param  array $attempts
5174
     */
5175
    static function generate_exercise_result_thumbnail_graph($attempts)
5176
    {
5177
        //$exercise_title = $attempts['title'];
5178
        $attempts = $attempts['data'];
5179
        $my_exercise_result_array = $exercise_result = array();
5180
        if (empty($attempts)) {
5181
            return null;
5182
        }
5183
5184 View Code Duplication
        foreach ($attempts as $attempt) {
5185
            if (api_get_user_id() == $attempt['exe_user_id']) {
5186
                if ($attempt['exe_weighting'] != 0 ) {
5187
                    $my_exercise_result_array[]= $attempt['exe_result']/$attempt['exe_weighting'];
5188
                }
5189
            } else {
5190
                if ($attempt['exe_weighting'] != 0 ) {
5191
                    $exercise_result[]=  $attempt['exe_result']/$attempt['exe_weighting'];
5192
                }
5193
            }
5194
        }
5195
5196
        //Getting best result
5197
        rsort($my_exercise_result_array);
5198
        $my_exercise_result = 0;
5199
        if (isset($my_exercise_result_array[0])) {
5200
            $my_exercise_result = $my_exercise_result_array[0] *100;
5201
        }
5202
5203
        $max     = 100;
5204
        $pieces  = 5 ;
5205
        $part    = round($max / $pieces);
5206
        $x_axis = array();
5207
        $final_array = array();
5208
        $my_final_array = array();
5209
5210 View Code Duplication
        for ($i=1; $i <=$pieces; $i++) {
5211
            $sum = 1;
5212
            if ($i == 1) {
5213
                $sum = 0;
5214
            }
5215
            $min = ($i-1)*$part + $sum;
5216
            $max = ($i)*$part;
5217
            $x_axis[]= $min." - ".$max;
5218
            $count = 0;
5219
            foreach($exercise_result as $result) {
5220
                $percentage = $result*100;
5221
                //echo $percentage.' - '.$min.' - '.$max."<br />";
5222
                if ($percentage >= $min && $percentage <= $max) {
5223
                    //echo ' is > ';
5224
                    $count++;
5225
                }
5226
            }
5227
            //echo '<br />';
5228
            $final_array[]= $count;
5229
5230
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5231
                $my_final_array[] = 1;
5232
            } else {
5233
                $my_final_array[] = 0;
5234
            }
5235
        }
5236
5237
        //Fix to remove the data of the user with my data
5238 View Code Duplication
        for($i = 0; $i<=count($my_final_array); $i++) {
5239
            if (!empty($my_final_array[$i])) {
5240
                $my_final_array[$i] =  $final_array[$i] + 1; //Add my result
5241
                $final_array[$i] = 0;
5242
            }
5243
        }
5244
5245
        // Dataset definition
5246
        $dataSet = new pData();
5247
        $dataSet->addPoints($final_array, 'Serie1');
5248
        $dataSet->addPoints($my_final_array, 'Serie2');
5249
        $dataSet->normalize(100, "%");
5250
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH) . 'palettes/pchart/default.color', true);
5251
5252
        // Cache definition
5253
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5254
        $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
5255
        $chartHash = $myCache->getHash($dataSet);
5256
        if ($myCache->isInCache($chartHash)) {
5257
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5258
            $myCache->saveFromCache($chartHash, $imgPath);
5259
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5260
        } else {
5261
            /* Create the pChart object */
5262
            $widthSize = 80;
5263
            $heightSize = 35;
5264
            $fontSize = 2;
5265
5266
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5267
5268
            /* Turn of Antialiasing */
5269
            $myPicture->Antialias = false;
5270
5271
            /* Add a border to the picture */
5272
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, array('R' => 0, 'G' => 0, 'B' => 0));
5273
5274
            /* Set the default font */
5275
            $myPicture->setFontProperties(array('FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf', 'FontSize' => $fontSize));
5276
5277
            /* Do not write the chart title */
5278
5279
            /* Define the chart area */
5280
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
5281
5282
            /* Draw the scale */
5283
            $scaleSettings = array(
5284
                'GridR' => 200,
5285
                'GridG' => 200,
5286
                'GridB' => 200,
5287
                'DrawSubTicks' => true,
5288
                'CycleBackground' => true,
5289
                'Mode' => SCALE_MODE_MANUAL,
5290
                'ManualScale' => array(
5291
                    '0' => array(
5292
                        'Min' => 0,
5293
                        'Max' => 100
5294
                    )
5295
                )
5296
            );
5297
            $myPicture->drawScale($scaleSettings);
5298
5299
            /* Turn on shadow computing */
5300
            $myPicture->setShadow(
5301
                true,
5302
                array(
5303
                    'X' => 1,
5304
                    'Y' => 1,
5305
                    'R' => 0,
5306
                    'G' => 0,
5307
                    'B' => 0,
5308
                    'Alpha' => 10
5309
                )
5310
            );
5311
5312
            /* Draw the chart */
5313
            $myPicture->setShadow(
5314
                true,
5315
                array(
5316
                    'X' => 1,
5317
                    'Y' => 1,
5318
                    'R' => 0,
5319
                    'G' => 0,
5320
                    'B' => 0,
5321
                    'Alpha' => 10
5322
                )
5323
            );
5324
            $settings = array(
5325
                'DisplayValues' => true,
5326
                'DisplaySize' => $fontSize,
5327
                'DisplayR' => 0,
5328
                'DisplayG' => 0,
5329
                'DisplayB' => 0,
5330
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5331
                'Gradient' => false,
5332
                'Surrounding' => 5,
5333
                'InnerSurrounding' => 5
5334
            );
5335
            $myPicture->drawStackedBarChart($settings);
5336
5337
            /* Save and write in cache */
5338
            $myCache->writeToCache($chartHash, $myPicture);
5339
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5340
            $myCache->saveFromCache($chartHash, $imgPath);
5341
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5342
        }
5343
5344
        return $imgPath;
5345
    }
5346
5347
    /**
5348
     * Generates a big graph with the number of best results
5349
     * @param	array
5350
     */
5351
    static function generate_exercise_result_graph($attempts)
5352
    {
5353
        $exercise_title = strip_tags($attempts['title']);
5354
        $attempts       = $attempts['data'];
5355
        $my_exercise_result_array = $exercise_result = array();
5356
        if (empty($attempts)) {
5357
            return null;
5358
        }
5359 View Code Duplication
        foreach ($attempts as $attempt) {
5360
            if (api_get_user_id() == $attempt['exe_user_id']) {
5361
                if ($attempt['exe_weighting'] != 0 ) {
5362
                    $my_exercise_result_array[]= $attempt['exe_result']/$attempt['exe_weighting'];
5363
                }
5364
            } else {
5365
                if ($attempt['exe_weighting'] != 0 ) {
5366
                    $exercise_result[]=  $attempt['exe_result']/$attempt['exe_weighting'];
5367
                }
5368
            }
5369
        }
5370
5371
        //Getting best result
5372
        rsort($my_exercise_result_array);
5373
        $my_exercise_result = 0;
5374
        if (isset($my_exercise_result_array[0])) {
5375
            $my_exercise_result = $my_exercise_result_array[0] *100;
5376
        }
5377
5378
        $max = 100;
5379
        $pieces = 5 ;
5380
        $part = round($max / $pieces);
5381
        $x_axis = array();
5382
        $final_array = array();
5383
        $my_final_array = array();
5384
5385 View Code Duplication
        for ($i=1; $i <=$pieces; $i++) {
5386
            $sum = 1;
5387
            if ($i == 1) {
5388
                $sum = 0;
5389
            }
5390
            $min = ($i-1)*$part + $sum;
5391
            $max = ($i)*$part;
5392
            $x_axis[]= $min." - ".$max;
5393
            $count = 0;
5394
            foreach($exercise_result as $result) {
5395
                $percentage = $result*100;
5396
                if ($percentage >= $min && $percentage <= $max) {
5397
                    $count++;
5398
                }
5399
            }
5400
            $final_array[]= $count;
5401
5402
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5403
                $my_final_array[] = 1;
5404
            } else {
5405
                $my_final_array[] = 0;
5406
            }
5407
        }
5408
5409
        //Fix to remove the data of the user with my data
5410
5411 View Code Duplication
        for($i = 0; $i<=count($my_final_array); $i++) {
5412
            if (!empty($my_final_array[$i])) {
5413
                $my_final_array[$i] =  $final_array[$i] + 1; //Add my result
5414
                $final_array[$i] = 0;
5415
            }
5416
        }
5417
5418
        // Dataset definition
5419
        $dataSet = new pData();
5420
        $dataSet->addPoints($final_array, 'Serie1');
5421
        $dataSet->addPoints($my_final_array, 'Serie2');
5422
        $dataSet->addPoints($x_axis, 'Serie3');
5423
5424
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
5425
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
5426
        $dataSet->setAbscissa('Serie3');
5427
5428
        $dataSet->setXAxisName(get_lang('Score'));
5429
        $dataSet->normalize(100, "%");
5430
5431
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH) . 'palettes/pchart/default.color', true);
5432
5433
        // Cache definition
5434
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5435
        $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
5436
        $chartHash = $myCache->getHash($dataSet);
5437
5438
        if ($myCache->isInCache($chartHash)) {
5439
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5440
            $myCache->saveFromCache($chartHash, $imgPath);
5441
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5442
        } else {
5443
            /* Create the pChart object */
5444
            $widthSize = 480;
5445
            $heightSize = 250;
5446
            $fontSize = 8;
5447
5448
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5449
5450
            /* Turn of Antialiasing */
5451
            $myPicture->Antialias = false;
5452
5453
            /* Add a border to the picture */
5454
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, array('R' => 0, 'G' => 0, 'B' => 0));
5455
5456
            /* Set the default font */
5457
            $myPicture->setFontProperties(array('FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf', 'FontSize' => 10));
5458
5459
            /* Write the chart title */
5460
            $myPicture->drawText(
5461
                250,
5462
                20,
5463
                $exercise_title,
5464
                array(
5465
                    'FontSize' => 12,
5466
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE
5467
                )
5468
            );
5469
5470
            /* Define the chart area */
5471
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
5472
5473
            /* Draw the scale */
5474
            $scaleSettings = array(
5475
                'GridR' => 200,
5476
                'GridG' => 200,
5477
                'GridB' => 200,
5478
                'DrawSubTicks' => true,
5479
                'CycleBackground' => true,
5480
                'Mode' => SCALE_MODE_MANUAL,
5481
                'ManualScale' => array(
5482
                    '0' => array(
5483
                        'Min' => 0,
5484
                        'Max' => 100
5485
                    )
5486
                )
5487
            );
5488
            $myPicture->drawScale($scaleSettings);
5489
5490
            /* Turn on shadow computing */
5491
            $myPicture->setShadow(true, array('X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10));
5492
5493
            /* Draw the chart */
5494
            $myPicture->setShadow(true, array('X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10));
5495
            $settings = array(
5496
                'DisplayValues' => true,
5497
                'DisplaySize' => $fontSize,
5498
                'DisplayR' => 0,
5499
                'DisplayG' => 0,
5500
                'DisplayB' => 0,
5501
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5502
                'Gradient' => false,
5503
                'Surrounding' => 30,
5504
                'InnerSurrounding' => 25
5505
            );
5506
            $myPicture->drawStackedBarChart($settings);
5507
5508
            $legendSettings = array(
5509
                'Mode' => LEGEND_HORIZONTAL,
5510
                'Style' => LEGEND_NOBORDER,
5511
            );
5512
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
5513
5514
            /* Write and save into cache */
5515
            $myCache->writeToCache($chartHash, $myPicture);
5516
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5517
            $myCache->saveFromCache($chartHash, $imgPath);
5518
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5519
        }
5520
5521
        return $imgPath;
5522
    }
5523
5524
    /**
5525
    * @param FormValidator $form
5526
    * @return mixed
5527
    */
5528
    public static function setUserSearchForm($form)
5529
    {
5530
        global $_configuration;
5531
        $form->addElement('text', 'keyword', get_lang('Keyword'));
5532
        $form->addElement(
5533
            'select',
5534
            'active',
5535
            get_lang('Status'),
5536
            array(1 => get_lang('Active'), 0 => get_lang('Inactive'))
5537
        );
5538
5539
        $form->addElement(
5540
            'select',
5541
            'sleeping_days',
5542
            get_lang('InactiveDays'),
5543
            array(
5544
                '',
5545
                1 => 1,
5546
                5 => 5,
5547
                15 => 15,
5548
                30 => 30,
5549
                60 => 60,
5550
                90 => 90,
5551
                120 => 120,
5552
            )
5553
        );
5554
5555
        $form->addButtonSearch(get_lang('Search'));
5556
5557
        return $form;
5558
    }
5559
5560
    /**
5561
     * Get the progress of a exercise
5562
     * @param   int $sessionId  The session ID (session.id)
5563
     * @param   int $courseId   The course ID (course.id)
5564
     * @param   int $exerciseId The quiz ID (c_quiz.id)
5565
     * @param   int $answer     The answer status (0 = incorrect, 1 = correct, 2 = both)
0 ignored issues
show
Bug introduced by
There is no parameter named $answer. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5566
     * @param   array   $options    An array of options you can pass to the query (limit, where and order)
5567
     * @return array An array with the data of exercise(s) progress
5568
     */
5569
    public static function get_exercise_progress(
5570
        $sessionId = 0,
5571
        $courseId = 0,
5572
        $exerciseId = 0,
5573
        $date_from = null,
5574
        $date_to = null,
5575
        $options = array()
5576
    ) {
5577
        $sessionId  = intval($sessionId);
5578
        $courseId   = intval($courseId);
5579
        $exerciseId = intval($exerciseId);
5580
        $date_from  = Database::escape_string($date_from);
5581
        $date_to    = Database::escape_string($date_to);
5582
        /*
5583
         * This method gets the data by blocks, as previous attempts at one single
5584
         * query made it take ages. The logic of query division is described below
5585
         */
5586
        // Get tables names
5587
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
5588
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
5589
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
5590
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
5591
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
5592
        $ttrack_exercises  = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5593
        $ttrack_attempt    = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
5594
5595
        $sessions = array();
5596
        $courses = array();
5597
        // if session ID is defined but course ID is empty, get all the courses
5598
        // from that session
5599
        if (!empty($sessionId) && empty($courseId)) {
5600
            // $courses is an array of course int id as index and course details hash as value
5601
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
5602
            $sessions[$sessionId] = api_get_session_info($sessionId);
5603
        } elseif (empty($sessionId) && !empty($courseId)) {
5604
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
5605
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
5606
            $course = api_get_course_info_by_id($courseId);
5607
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
5608
            $courses[$courseId] = $course;
5609
            foreach ($sessionsTemp as $sessionItem) {
5610
                $sessions[$sessionItem['id']] = $sessionItem;
5611
            }
5612
        } elseif (!empty($courseId) && !empty($sessionId)) {
5613
            //none is empty
5614
            $course = api_get_course_info_by_id($courseId);
5615
            $courses[$courseId] = array($course['code']);
5616
            $courses[$courseId]['code'] = $course['code'];
5617
            $sessions[$sessionId] = api_get_session_info($sessionId);
5618
        } else {
5619
            //both are empty, not enough data, return an empty array
5620
            return array();
5621
        }
5622
        // Now we have two arrays of courses and sessions with enough data to proceed
5623
        // If no course could be found, we shouldn't return anything.
5624
        // Sessions can be empty (then we only return the pure-course-context results)
5625
        if (count($courses) < 1) {
5626
            return array();
5627
        }
5628
5629
        $data = array();
5630
        // The following loop is less expensive than what it seems:
5631
        // - if a course was defined, then we only loop through sessions
5632
        // - if a session was defined, then we only loop through courses
5633
        // - if a session and a course were defined, then we only loop once
5634
        foreach ($courses as $courseIdx => $courseData) {
0 ignored issues
show
Bug introduced by
The expression $courses of type integer|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
5635
            $where = '';
5636
            $whereParams = array();
5637
            $whereSessionParams = '';
5638
            if (count($sessions > 0)) {
5639
                foreach ($sessions as $sessionIdx => $sessionData) {
5640
                    if (!empty($sessionIdx)) {
5641
                        $whereSessionParams .= $sessionIdx.',';
5642
                    }
5643
                }
5644
                $whereSessionParams = substr($whereSessionParams,0,-1);
5645
            }
5646
5647
            if (!empty($exerciseId)) {
5648
                $exerciseId = intval($exerciseId);
5649
                $where .= ' AND q.id = %d ';
5650
                $whereParams[] = $exerciseId;
5651
            }
5652
5653
            /*
5654
             * This feature has been disabled for now, to avoid having to
5655
             * join two very large tables
5656
            //2 = show all questions (wrong and correct answered)
5657
            if ($answer != 2) {
5658
                $answer = intval($answer);
5659
                //$where .= ' AND qa.correct = %d';
5660
                //$whereParams[] = $answer;
5661
            }
5662
            */
5663
5664
            $limit = '';
5665
            if (!empty($options['limit'])) {
5666
                $limit = " LIMIT ".$options['limit'];
5667
            }
5668
5669
            if (!empty($options['where'])) {
5670
                $where .= ' AND '.Database::escape_string($options['where']);
5671
            }
5672
5673
            $order = '';
5674
            if (!empty($options['order'])) {
5675
                $order = " ORDER BY ".$options['order'];
5676
            }
5677
5678
            if (!empty($date_to) && !empty($date_from)) {
5679
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
5680
            }
5681
5682
            $sql = "SELECT
5683
                te.session_id,
5684
                ta.id as attempt_id,
5685
                te.exe_user_id as user_id,
5686
                te.exe_id as exercise_attempt_id,
5687
                ta.question_id,
5688
                ta.answer as answer_id,
5689
                ta.tms as time,
5690
                te.exe_exo_id as quiz_id,
5691
                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
5692
                q.title as quiz_title,
5693
                qq.description as description
5694
                FROM $ttrack_exercises te
5695
                INNER JOIN $ttrack_attempt ta ON ta.exe_id = te.exe_id
5696
                INNER JOIN $tquiz q ON q.id = te.exe_exo_id
5697
                INNER JOIN $tquiz_rel_question rq ON rq.exercice_id = q.id AND rq.c_id = q.c_id
5698
                INNER JOIN $tquiz_question qq
5699
                ON
5700
                    qq.id = rq.question_id AND
5701
                    qq.c_id = rq.c_id AND
5702
                    qq.position = rq.question_order AND
5703
                    ta.question_id = rq.question_id
5704
                WHERE
5705
                    te.c_id = $courseIdx ".(empty($whereSessionParams)?'':"AND te.session_id IN ($whereSessionParams)")."
5706
                    AND q.c_id = $courseIdx
5707
                    $where $order $limit";
5708
            $sql_query = vsprintf($sql, $whereParams);
5709
5710
            // Now browse through the results and get the data
5711
            $rs = Database::query($sql_query);
5712
            $userIds = array();
5713
            $questionIds = array();
5714
            $answerIds = array();
5715
            while ($row = Database::fetch_array($rs)) {
5716
                //only show if exercise is visible
5717
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
5718
                    $userIds[$row['user_id']] = $row['user_id'];
5719
                    $questionIds[$row['question_id']] = $row['question_id'];
5720
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
5721
                    $row['session'] = $sessions[$row['session_id']];
5722
                    $data[] = $row;
5723
                }
5724
            }
5725
            // Now fill questions data. Query all questions and answers for this test to avoid
5726
            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
5727
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
5728
                            FROM $tquiz_question tq, $tquiz_answer tqa
5729
                            WHERE
5730
                                tqa.question_id = tq.id AND
5731
                                tqa.c_id = tq.c_id AND
5732
                                tq.c_id = $courseIdx AND
5733
                                tq.id IN (".implode(',', $questionIds).")";
5734
5735
            $resQuestions = Database::query($sqlQuestions);
5736
            $answer = array();
5737
            $question = array();
5738
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
5739
                $questionId = $rowQuestion['question_id'];
5740
                $answerId = $rowQuestion['answer_id'];
5741
                $answer[$questionId][$answerId] = array(
5742
                    'position' => $rowQuestion['position'],
5743
                    'question' => $rowQuestion['question'],
5744
                    'answer' => $rowQuestion['answer'],
5745
                    'correct' => $rowQuestion['correct']
5746
                );
5747
                $question[$questionId]['question'] = $rowQuestion['question'];
5748
            }
5749
5750
            // Now fill users data
5751
            $sqlUsers = "SELECT user_id, username, lastname, firstname
5752
                         FROM $tuser
5753
                         WHERE user_id IN (".implode(',',$userIds).")";
5754
            $resUsers = Database::query($sqlUsers);
5755
            while ($rowUser = Database::fetch_assoc($resUsers)) {
5756
                $users[$rowUser['user_id']] = $rowUser;
5757
            }
5758
5759
            foreach ($data as $id => $row) {
5760
                $rowQuestId = $row['question_id'];
5761
                $rowAnsId = $row['answer_id'];
5762
5763
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
5764
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
5765
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
5766
                $data[$id]['username'] = $users[$row['user_id']]['username'];
5767
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
5768
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
5769
                $data[$id]['question'] = $question[$rowQuestId]['question'];
5770
                $data[$id]['question_id'] = $rowQuestId;
5771
                $data[$id]['description'] = $row['description'];
5772
            }
5773
            /*
5774
            The minimum expected array structure at the end is:
5775
            attempt_id,
5776
            session name,
5777
            exercise_id,
5778
            quiz_title,
5779
            username,
5780
            lastname,
5781
            firstname,
5782
            time,
5783
            question_id,
5784
            question,
5785
            answer,
5786
            */
5787
        }
5788
        return $data;
5789
    }
5790
5791
    /**
5792
     * @param User $user
5793
     * @param string $tool
5794
     * @param Course $course
5795
     * @param Session|null $session Optional.
5796
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
5797
     * @throws \Doctrine\ORM\NonUniqueResultException
5798
     */
5799
    public static function getLastStudentPublication(User $user, $tool, Course $course, Session $session = null)
5800
    {
5801
        return Database::getManager()
5802
            ->createQuery("
5803
                SELECT csp
5804
                FROM ChamiloCourseBundle:CStudentPublication csp
5805
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
5806
                    WITH (
5807
                        csp.iid = cip.ref AND
5808
                        csp.sessionId = cip.session AND
5809
                        csp.cId = cip.course AND
5810
                        csp.userId = cip.lasteditUserId
5811
                    )
5812
                WHERE
5813
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
5814
                ORDER BY csp.iid DESC
5815
            ")
5816
            ->setMaxResults(1)
5817
            ->setParameters([
5818
                'tool' => $tool,
5819
                'session' => $session,
5820
                'course' => $course,
5821
                'user' => $user
5822
            ])
5823
            ->getOneOrNullResult();
5824
    }
5825
}
5826
5827
/**
5828
 * @todo move into a proper file
5829
 * @package chamilo.tracking
5830
 */
5831
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...
5832
{
5833
    /**
5834
     * @return mixed
5835
     */
5836
    public static function count_item_resources()
5837
    {
5838
        $session_id = api_get_session_id();
5839
        $course_id = api_get_course_int_id();
5840
5841
    	$table_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
5842
    	$table_user = Database :: get_main_table(TABLE_MAIN_USER);
5843
5844
    	$sql = "SELECT count(tool) AS total_number_of_items
5845
    	        FROM $table_item_property track_resource, $table_user user
5846
    	        WHERE
5847
                    track_resource.c_id = $course_id AND
5848
                    track_resource.insert_user_id = user.user_id AND
5849
                    session_id " .(empty($session_id) ? ' IS NULL ' : " = $session_id ");
5850
5851
    	if (isset($_GET['keyword'])) {
5852
    		$keyword = Database::escape_string(trim($_GET['keyword']));
5853
    		$sql .= " AND (
5854
    		            user.username LIKE '%".$keyword."%' OR
5855
    		            lastedit_type LIKE '%".$keyword."%' OR
5856
    		            tool LIKE '%".$keyword."%'
5857
                    )";
5858
    	}
5859
5860
    	$sql .= " AND tool IN (
5861
    	            'document',
5862
    	            'learnpath',
5863
    	            'quiz',
5864
    	            'glossary',
5865
    	            'link',
5866
    	            'course_description',
5867
    	            'announcement',
5868
    	            'thematic',
5869
    	            'thematic_advance',
5870
    	            'thematic_plan'
5871
                )";
5872
    	$res = Database::query($sql);
5873
    	$obj = Database::fetch_object($res);
5874
5875
    	return $obj->total_number_of_items;
5876
    }
5877
5878
    /**
5879
     * @param $from
5880
     * @param $number_of_items
5881
     * @param $column
5882
     * @param $direction
5883
     * @return array
5884
     */
5885
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
5886
    {
5887
        $session_id = api_get_session_id();
5888
        $course_id = api_get_course_int_id();
5889
5890
    	$table_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
5891
    	$table_user = Database :: get_main_table(TABLE_MAIN_USER);
5892
    	$table_session = Database :: get_main_table(TABLE_MAIN_SESSION);
5893
    	$session_id = intval($session_id);
5894
5895
    	$sql = "SELECT
5896
                    tool as col0,
5897
                    lastedit_type as col1,
5898
                    ref as ref,
5899
                    user.username as col3,
5900
                    insert_date as col5,
5901
                    visibility as col6,
5902
                    user.user_id as user_id
5903
                FROM $table_item_property track_resource, $table_user user
5904
                WHERE
5905
                  track_resource.c_id = $course_id AND
5906
                  track_resource.insert_user_id = user.user_id AND
5907
                  session_id " .(empty($session_id) ? ' IS NULL ' : " = $session_id ");
5908
5909
    	if (isset($_GET['keyword'])) {
5910
    		$keyword = Database::escape_string(trim($_GET['keyword']));
5911
    		$sql .= " AND (
5912
    		            user.username LIKE '%".$keyword."%' OR
5913
    		            lastedit_type LIKE '%".$keyword."%' OR
5914
    		            tool LIKE '%".$keyword."%'
5915
                     ) ";
5916
    	}
5917
5918
    	$sql .= " AND tool IN (
5919
    	            'document',
5920
    	            'learnpath',
5921
    	            'quiz',
5922
    	            'glossary',
5923
    	            'link',
5924
    	            'course_description',
5925
    	            'announcement',
5926
    	            'thematic',
5927
    	            'thematic_advance',
5928
    	            'thematic_plan'
5929
                )";
5930
5931
    	if ($column == 0) {
5932
    		$column = '0';
5933
    	}
5934
    	if ($column != '' && $direction != '') {
5935
    		if ($column != 2 && $column != 4) {
5936
    			$sql .= " ORDER BY col$column $direction";
5937
    		}
5938
    	} else {
5939
    		$sql .= " ORDER BY col5 DESC ";
5940
    	}
5941
5942
        $from = intval($from);
5943
        $number_of_items = intval($number_of_items);
5944
5945
    	$sql .= " LIMIT $from, $number_of_items ";
5946
5947
    	$res = Database::query($sql);
5948
    	$resources = array();
5949
    	$thematic_tools = array('thematic', 'thematic_advance', 'thematic_plan');
5950
    	while ($row = Database::fetch_array($res)) {
5951
    		$ref = $row['ref'];
5952
    		$table_name = TrackingCourseLog::get_tool_name_table($row['col0']);
5953
    		$table_tool = Database :: get_course_table($table_name['table_name']);
5954
5955
    		$id = $table_name['id_tool'];
5956
    		$recorset = false;
5957
5958
    		if (in_array($row['col0'], array('thematic_plan', 'thematic_advance'))) {
5959
    			$tbl_thematic = Database :: get_course_table(TABLE_THEMATIC);
5960
    			$sql = "SELECT thematic_id FROM $table_tool
5961
    			        WHERE c_id = $course_id AND id = $ref";
5962
    			$rs_thematic  = Database::query($sql);
5963 View Code Duplication
    			if (Database::num_rows($rs_thematic)) {
5964
    				$row_thematic = Database::fetch_array($rs_thematic);
5965
    				$thematic_id = $row_thematic['thematic_id'];
5966
5967
                    $sql = "SELECT session.id, session.name, user.username
5968
                            FROM $tbl_thematic t, $table_session session, $table_user user
5969
                            WHERE
5970
                              t.c_id = $course_id AND
5971
                              t.session_id = session.id AND
5972
                              session.id_coach = user.user_id AND
5973
                              t.id = $thematic_id";
5974
    				$recorset = Database::query($sql);
5975
    			}
5976
    		} else {
5977
                $sql = "SELECT session.id, session.name, user.username
5978
                          FROM $table_tool tool, $table_session session, $table_user user
5979
    			          WHERE
5980
    			              tool.c_id = $course_id AND
5981
    			              tool.session_id = session.id AND
5982
    			              session.id_coach = user.user_id AND
5983
    			              tool.$id = $ref";
5984
    			$recorset = Database::query($sql);
5985
    		}
5986
5987
    		if (!empty($recorset)) {
5988
    			$obj = Database::fetch_object($recorset);
5989
5990
    			$name_session = '';
5991
    			$coach_name = '';
5992
    			if (!empty($obj)) {
5993
    				$name_session = $obj->name;
5994
    				$coach_name   = $obj->username;
5995
    			}
5996
5997
    			$url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
5998
    			$row[0] = '';
5999
    			if ($row['col6'] != 2) {
6000
    				if (in_array($row['col0'], $thematic_tools)) {
6001
6002
    					$exp_thematic_tool = explode('_', $row['col0']);
6003
    					$thematic_tool_title = '';
6004
    					if (is_array($exp_thematic_tool)) {
6005
    						foreach ($exp_thematic_tool as $exp) {
6006
    							$thematic_tool_title .= api_ucfirst($exp);
6007
    						}
6008
    					} else {
6009
    						$thematic_tool_title = api_ucfirst($row['col0']);
6010
    					}
6011
6012
    					$row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
6013
    				} else {
6014
    					$row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
6015
    				}
6016
    			} else {
6017
    				$row[0] = api_ucfirst($row['col0']);
6018
    			}
6019
    			$row[1] = get_lang($row[1]);
6020
    			$row[6] = api_convert_and_format_date($row['col5'], null, date_default_timezone_get());
6021
    			$row[5] = '';
6022
    			//@todo Improve this code please
6023
    			switch ($table_name['table_name']) {
6024
    				case 'document' :
6025
    					$sql = "SELECT tool.title as title FROM $table_tool tool
6026
                                WHERE c_id = $course_id AND id = $ref";
6027
    					$rs_document = Database::query($sql);
6028
    					$obj_document = Database::fetch_object($rs_document);
6029
    					$row[5] = $obj_document->title;
6030
6031
    					break;
6032 View Code Duplication
    				case 'announcement':
6033
                        $sql = "SELECT title FROM $table_tool
6034
                                WHERE c_id = $course_id AND id = $ref";
6035
    					$rs_document = Database::query($sql);
6036
    					$obj_document = Database::fetch_object($rs_document);
6037
                        if ($obj_document) {
6038
                            $row[5] = $obj_document->title;
6039
                        }
6040
    					break;
6041
    				case 'glossary':
6042
                        $sql = "SELECT name FROM $table_tool
6043
    					        WHERE c_id = $course_id AND glossary_id = $ref";
6044
    					$rs_document = Database::query($sql);
6045
    					$obj_document = Database::fetch_object($rs_document);
6046
                        if ($obj_document) {
6047
                            $row[5] = $obj_document->name;
6048
                        }
6049
    					break;
6050
    				case 'lp':
6051
                        $sql = "SELECT name
6052
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
6053
    					$rs_document = Database::query($sql);
6054
    					$obj_document = Database::fetch_object($rs_document);
6055
    					$row[5] = $obj_document->name;
6056
    					break;
6057
    				case 'quiz':
6058
                        $sql = "SELECT title FROM $table_tool
6059
                                WHERE c_id = $course_id AND id = $ref";
6060
    					$rs_document = Database::query($sql);
6061
    					$obj_document = Database::fetch_object($rs_document);
6062
                        if ($obj_document) {
6063
                            $row[5] = $obj_document->title;
6064
                        }
6065
    					break;
6066 View Code Duplication
    				case 'course_description':
6067
                        $sql = "SELECT title FROM $table_tool
6068
                                WHERE c_id = $course_id AND id = $ref";
6069
    					$rs_document = Database::query($sql);
6070
    					$obj_document = Database::fetch_object($rs_document);
6071
                        if ($obj_document) {
6072
                            $row[5] = $obj_document->title;
6073
                        }
6074
    					break;
6075 View Code Duplication
    				case 'thematic':
6076
    					$rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
6077
    					if (Database::num_rows($rs) > 0) {
6078
    						$obj = Database::fetch_object($rs);
6079
    						$row[5] = $obj->title;
6080
    					}
6081
    					break;
6082 View Code Duplication
    				case 'thematic_advance':
6083
    					$rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
6084
    					if (Database::num_rows($rs) > 0) {
6085
    						$obj = Database::fetch_object($rs);
6086
    						$row[5] = $obj->content;
6087
    					}
6088
    					break;
6089 View Code Duplication
    				case 'thematic_plan':
6090
    					$rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
6091
    					if (Database::num_rows($rs) > 0) {
6092
    						$obj = Database::fetch_object($rs);
6093
    						$row[5] = $obj->title;
6094
    					}
6095
    					break;
6096
    				default:
6097
    					break;
6098
    			}
6099
6100
    			$row2 = $name_session;
6101
    			if (!empty($coach_name)) {
6102
    				$row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
6103
    			}
6104
    			$row[2] = $row2;
6105
                if (!empty($row['col3'])) {
6106
                    $userInfo = api_get_user_info($row['user_id']);
6107
6108
                    $row['col3'] = Display::url(
6109
                        $row['col3'],
6110
                        $userInfo['profile_url']
6111
                    );
6112
                    $row[3] = $row['col3'];
6113
6114
                    $ip = TrackingUserLog::get_ip_from_user_event($row['user_id'], $row['col5'], true);
6115
                    if (empty($ip)) {
6116
                        $ip = get_lang('Unknown');
6117
                    }
6118
                    $row[4] = $ip;
6119
                }
6120
6121
    			$resources[] = $row;
6122
    		}
6123
    	}
6124
6125
    	return $resources;
6126
    }
6127
6128
    /**
6129
     * @param string $tool
6130
     *
6131
     * @return array
6132
     */
6133
    public static function get_tool_name_table($tool)
6134
    {
6135
    	switch ($tool) {
6136
    		case 'document':
6137
    			$table_name = TABLE_DOCUMENT;
6138
    			$link_tool = 'document/document.php';
6139
    			$id_tool = 'id';
6140
    			break;
6141
    		case 'learnpath':
6142
    			$table_name = TABLE_LP_MAIN;
6143
    			$link_tool = 'newscorm/lp_controller.php';
6144
    			$id_tool = 'id';
6145
    			break;
6146
    		case 'quiz':
6147
    			$table_name = TABLE_QUIZ_TEST;
6148
    			$link_tool = 'exercice/exercice.php';
6149
    			$id_tool = 'id';
6150
    			break;
6151
    		case 'glossary':
6152
    			$table_name = TABLE_GLOSSARY;
6153
    			$link_tool = 'glossary/index.php';
6154
    			$id_tool = 'glossary_id';
6155
    			break;
6156
    		case 'link':
6157
    			$table_name = TABLE_LINK;
6158
    			$link_tool = 'link/link.php';
6159
    			$id_tool = 'id';
6160
    			break;
6161
    		case 'course_description':
6162
    			$table_name = TABLE_COURSE_DESCRIPTION;
6163
    			$link_tool = 'course_description/';
6164
    			$id_tool = 'id';
6165
    			break;
6166
    		case 'announcement':
6167
    			$table_name = TABLE_ANNOUNCEMENT;
6168
    			$link_tool = 'announcements/announcements.php';
6169
    			$id_tool = 'id';
6170
    			break;
6171
    		case 'thematic':
6172
    			$table_name = TABLE_THEMATIC;
6173
    			$link_tool = 'course_progress/index.php';
6174
    			$id_tool = 'id';
6175
    			break;
6176
    		case 'thematic_advance':
6177
    			$table_name = TABLE_THEMATIC_ADVANCE;
6178
    			$link_tool = 'course_progress/index.php';
6179
    			$id_tool = 'id';
6180
    			break;
6181
    		case 'thematic_plan':
6182
    			$table_name = TABLE_THEMATIC_PLAN;
6183
    			$link_tool = 'course_progress/index.php';
6184
    			$id_tool = 'id';
6185
    			break;
6186
    		default:
6187
    			$table_name = $tool;
6188
    		break;
6189
    	}
6190
6191
    	return array(
6192
            'table_name' => $table_name,
6193
            'link_tool' => $link_tool,
6194
            'id_tool' => $id_tool
6195
        );
6196
    }
6197
6198
    public static function display_additional_profile_fields()
6199
    {
6200
    	// getting all the extra profile fields that are defined by the platform administrator
6201
    	$extra_fields = UserManager :: get_extra_fields(0,50,5,'ASC');
6202
6203
    	// creating the form
6204
    	$return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
6205
6206
    	// the select field with the additional user profile fields (= this is where we select the field of which we want to see
6207
    	// the information the users have entered or selected.
6208
    	$return .= '<select name="additional_profile_field">';
6209
    	$return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
6210
    	$extra_fields_to_show = 0;
6211
    	foreach ($extra_fields as $key=>$field) {
6212
    		// show only extra fields that are visible + and can be filtered, added by J.Montoya
6213
    		if ($field[6]==1 && $field[8] == 1) {
6214
    			if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field'] ) {
6215
    				$selected = 'selected="selected"';
6216
    			} else {
6217
    				$selected = '';
6218
    			}
6219
    			$extra_fields_to_show++;
6220
    			$return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
6221
    		}
6222
    	}
6223
    	$return .= '</select>';
6224
6225
    	// the form elements for the $_GET parameters (because the form is passed through GET
6226
    	foreach ($_GET as $key=>$value){
6227
    		if ($key <> 'additional_profile_field')    {
6228
    			$return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
6229
    		}
6230
    	}
6231
    	// the submit button
6232
    	$return .= '<button class="save" type="submit">'.get_lang('AddAdditionalProfileField').'</button>';
6233
    	$return .= '</form>';
6234
    	if ($extra_fields_to_show > 0) {
6235
    		return $return;
6236
    	} else {
6237
    		return '';
6238
    	}
6239
    }
6240
6241
    /**
6242
     * This function gets all the information of a certrain ($field_id)
6243
     * additional profile field for a specific list of users is more efficent
6244
     * than get_addtional_profile_information_of_field() function
6245
     * It gets the information of all the users so that it can be displayed
6246
     * in the sortable table or in the csv or xls export
6247
     *
6248
     * @author    Julio Montoya <[email protected]>
6249
     * @param    int field id
6250
     * @param    array list of user ids
6251
     * @return    array
6252
     * @since    Nov 2009
6253
     * @version    1.8.6.2
6254
     */
6255
    public static function get_addtional_profile_information_of_field_by_user($field_id, $users)
6256
    {
6257
    	// Database table definition
6258
    	$table_user = Database::get_main_table(TABLE_MAIN_USER);
6259
    	$table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
6260
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
6261
    	$result_extra_field = UserManager::get_extra_field_information($field_id);
0 ignored issues
show
Deprecated Code introduced by
The method UserManager::get_extra_field_information() has been deprecated.

This method has been deprecated.

Loading history...
6262
6263
    	if (!empty($users)) {
6264
    		if ($result_extra_field['field_type'] == UserManager::USER_FIELD_TYPE_TAG ) {
6265
    			foreach($users as $user_id) {
6266
    				$user_result = UserManager::get_user_tags($user_id, $field_id);
6267
    				$tag_list = array();
6268
    				foreach($user_result as $item) {
6269
    					$tag_list[] = $item['tag'];
6270
    				}
6271
    				$return[$user_id][] = implode(', ',$tag_list);
6272
    			}
6273
    		} else {
6274
    			$new_user_array = array();
6275
    			foreach ($users as $user_id) {
6276
    				$new_user_array[]= "'".$user_id."'";
6277
    			}
6278
    			$users = implode(',',$new_user_array);
6279
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
6280
    			// Selecting only the necessary information NOT ALL the user list
6281
    			$sql = "SELECT user.user_id, v.value
6282
    			        FROM $table_user user
6283
    			        INNER JOIN $table_user_field_values v
6284
                        ON (user.user_id = v.item_id)
6285
                        INNER JOIN $extraField f
6286
                        ON (f.id = v.field_id)
6287
                        WHERE
6288
                            f.extra_field_type = $extraFieldType AND
6289
                            v.field_id=".intval($field_id)." AND
6290
                            user.user_id IN ($users)";
6291
6292
    			$result = Database::query($sql);
6293
    			while($row = Database::fetch_array($result)) {
6294
    				// get option value for field type double select by id
6295
    				if (!empty($row['value'])) {
6296
    					if ($result_extra_field['field_type'] ==
6297
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
6298
                        ) {
6299
    						$id_double_select = explode(';', $row['value']);
6300
    						if (is_array($id_double_select)) {
6301
    							$value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
6302
    							$value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
6303
    							$row['value'] = ($value1.';'.$value2);
6304
    						}
6305
    					}
6306
    				}
6307
    				// get other value from extra field
6308
    				$return[$row['user_id']][] = $row['value'];
6309
    			}
6310
    		}
6311
    	}
6312
    	return $return;
6313
    }
6314
6315
    /**
6316
     * count the number of students in this course (used for SortableTable)
6317
     * Deprecated
6318
     */
6319
    public function count_student_in_course()
6320
    {
6321
    	global $nbStudents;
6322
    	return $nbStudents;
6323
    }
6324
6325
    public function sort_users($a, $b)
6326
    {
6327
    	return strcmp(trim(api_strtolower($a[$_SESSION['tracking_column']])), trim(api_strtolower($b[$_SESSION['tracking_column']])));
6328
    }
6329
6330
    public function sort_users_desc($a, $b)
6331
    {
6332
    	return strcmp( trim(api_strtolower($b[$_SESSION['tracking_column']])), trim(api_strtolower($a[$_SESSION['tracking_column']])));
6333
    }
6334
6335
    /**
6336
     * Get number of users for sortable with pagination
6337
     * @return int
6338
     */
6339
    public static function get_number_of_users()
6340
    {
6341
    	global $user_ids;
6342
    	return count($user_ids);
6343
    }
6344
6345
    /**
6346
     * Get data for users list in sortable with pagination
6347
     * @param $from
6348
     * @param $number_of_items
6349
     * @param $column
6350
     * @param $direction
6351
     * @param $includeInvitedUsers boolean Whether include the invited users
6352
     * @return array
6353
     */
6354
    public static function get_user_data($from, $number_of_items, $column, $direction, $includeInvitedUsers = false)
6355
    {
6356
        global $user_ids, $course_code, $additional_user_profile_info, $export_csv, $is_western_name_order, $csv_content, $session_id;
6357
6358
    	$course_code = Database::escape_string($course_code);
6359
    	$tbl_user = Database::get_main_table(TABLE_MAIN_USER);
6360
    	$tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
6361
6362
    	$access_url_id = api_get_current_access_url_id();
6363
6364
    	// get all users data from a course for sortable with limit
6365
    	if (is_array($user_ids)) {
6366
    		$user_ids = array_map('intval', $user_ids);
6367
    		$condition_user = " WHERE user.user_id IN (".implode(',',$user_ids).") ";
6368
    	} else {
6369
    		$user_ids = intval($user_ids);
6370
    		$condition_user = " WHERE user.user_id = $user_ids ";
6371
    	}
6372
6373 View Code Duplication
    	if (!empty($_GET['user_keyword'])) {
6374
    		$keyword = trim(Database::escape_string($_GET['user_keyword']));
6375
    		$condition_user .=  " AND (
6376
                user.firstname LIKE '%".$keyword."%' OR
6377
                user.lastname LIKE '%".$keyword."%'  OR
6378
                user.username LIKE '%".$keyword."%'  OR
6379
                user.email LIKE '%".$keyword."%'
6380
             ) ";
6381
    	}
6382
6383
        $url_table = null;
6384
        $url_condition = null;
6385
    	if (api_is_multiple_url_enabled()) {
6386
    		$url_table = ", ".$tbl_url_rel_user." as url_users";
6387
    		$url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
6388
    	}
6389
6390
        $invitedUsersCondition = '';
6391
6392
        if (!$includeInvitedUsers) {
6393
            $invitedUsersCondition = " AND user.status != " . INVITEE;
6394
        }
6395
6396
    	$sql = "SELECT  user.user_id as user_id,
6397
                    user.official_code  as col0,
6398
                    user.lastname       as col1,
6399
                    user.firstname      as col2,
6400
                    user.username       as col3
6401
                FROM $tbl_user as user $url_table
6402
    	        $condition_user $url_condition $invitedUsersCondition";
6403
6404
    	if (!in_array($direction, array('ASC','DESC'))) {
6405
    		$direction = 'ASC';
6406
    	}
6407
6408
    	$column = intval($column);
6409
6410
    	$from = intval($from);
6411
    	$number_of_items = intval($number_of_items);
6412
6413
    	$sql .= " ORDER BY col$column $direction ";
6414
    	$sql .= " LIMIT $from,$number_of_items";
6415
6416
        $res = Database::query($sql);
6417
        $users = array();
6418
6419
        $course_info = api_get_course_info($course_code);
6420
        $total_surveys = 0;
6421
        $total_exercises = ExerciseLib::get_all_exercises(
6422
            $course_info,
6423
            $session_id,
6424
            false,
6425
            null,
6426
            false,
6427
            3
6428
        );
6429
6430
        if (empty($session_id)) {
6431
            $survey_user_list = array();
6432
            $survey_list = SurveyManager::get_surveys($course_code, $session_id);
6433
6434
            $total_surveys = count($survey_list);
6435 View Code Duplication
            foreach ($survey_list as $survey) {
0 ignored issues
show
Bug introduced by
The expression $survey_list of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
6436
                $user_list = SurveyManager::get_people_who_filled_survey(
6437
                    $survey['survey_id'],
6438
                    false,
6439
                    $course_info['real_id']
6440
                );
6441
6442
                foreach ($user_list as $user_id) {
6443
                    isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
6444
                }
6445
            }
6446
        }
6447
6448
    	while ($user = Database::fetch_array($res, 'ASSOC')) {
6449
            $courseInfo = api_get_course_info($course_code);
6450
            $courseId = $courseInfo['real_id'];
6451
6452
            $user['official_code'] = $user['col0'];
6453
            $user['lastname'] = $user['col1'];
6454
            $user['firstname'] = $user['col2'];
6455
            $user['username'] = $user['col3'];
6456
6457
            $user['time'] = api_time_to_hms(
6458
                Tracking::get_time_spent_on_the_course(
6459
                    $user['user_id'],
6460
                    $courseId,
6461
                    $session_id
6462
                )
6463
            );
6464
6465
            $avg_student_score = Tracking::get_avg_student_score(
6466
                $user['user_id'],
6467
                $course_code,
6468
                array(),
6469
                $session_id
6470
            );
6471
6472
            $avg_student_progress = Tracking::get_avg_student_progress(
6473
                $user['user_id'],
6474
                $course_code,
6475
                array(),
6476
                $session_id
6477
            );
6478
6479
    		if (empty($avg_student_progress)) {
6480
                $avg_student_progress = 0;
6481
    		}
6482
    		$user['average_progress'] = $avg_student_progress.'%';
6483
6484
            $total_user_exercise = Tracking::get_exercise_student_progress(
6485
                $total_exercises,
6486
                $user['user_id'],
6487
                $courseId,
6488
                $session_id
6489
            );
6490
6491
            $user['exercise_progress'] = $total_user_exercise;
6492
6493
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
6494
                $total_exercises,
6495
                $user['user_id'],
6496
                $courseId,
6497
                $session_id
6498
            );
6499
6500
            $user['exercise_average_best_attempt'] = $total_user_exercise;
6501
6502
    		if (is_numeric($avg_student_score)) {
6503
    			$user['student_score']  = $avg_student_score.'%';
6504
    		} else {
6505
    			$user['student_score']  = $avg_student_score;
6506
    		}
6507
6508
            $user['count_assignments'] = Tracking::count_student_assignments(
6509
                $user['user_id'],
6510
                $course_code,
6511
                $session_id
6512
            );
6513
            $user['count_messages'] = Tracking::count_student_messages(
6514
                $user['user_id'],
6515
                $course_code,
6516
                $session_id
6517
            );
6518
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
6519
                $user['user_id'],
6520
                $courseId,
6521
                $session_id
6522
            );
6523
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
6524
                $user['user_id'],
6525
                $courseInfo,
6526
                $session_id
6527
            );
6528
6529
    		// we need to display an additional profile field
6530
    		$user['additional'] = '';
6531
6532
    		if (isset($_GET['additional_profile_field']) && is_numeric($_GET['additional_profile_field'])) {
6533 View Code Duplication
    			if (isset($additional_user_profile_info[$user['user_id']]) &&
6534
                    is_array($additional_user_profile_info[$user['user_id']])
6535
                ) {
6536
    				$user['additional'] = implode(', ', $additional_user_profile_info[$user['user_id']]);
6537
    			}
6538
    		}
6539
6540
            if (empty($session_id)) {
6541
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0) .' / '.$total_surveys;
6542
            }
6543
6544
    		$user['link'] = '<center>
6545
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
6546
    		                 '.Display::return_icon('2rightarrow.png').'
6547
    		                 </a>
6548
                         </center>';
6549
6550
    		// store columns in array $users
6551
    		$is_western_name_order = api_is_western_name_order();
6552
            $user_row = array();
6553
            $user_row[]= $user['official_code']; //0
6554
            if ($is_western_name_order) {
6555
                $user_row[]= $user['firstname'];
6556
                $user_row[]= $user['lastname'];
6557
            } else {
6558
                $user_row[]= $user['lastname'];
6559
                $user_row[]= $user['firstname'];
6560
            }
6561
            $user_row[]= $user['username'];
6562
            $user_row[]= $user['time'];
6563
            $user_row[]= $user['average_progress'];
6564
            $user_row[]= $user['exercise_progress'];
6565
            $user_row[]= $user['exercise_average_best_attempt'];
6566
            $user_row[]= $user['student_score'];
6567
            $user_row[]= $user['count_assignments'];
6568
            $user_row[]= $user['count_messages'];
6569
6570
            $userGroupManager = new UserGroup();
6571
            $user_row[] = $userGroupManager->getLabelsFromNameList($user['user_id'], UserGroup::NORMAL_CLASS);
6572
6573
            if (empty($session_id)) {
6574
                $user_row[]= $user['survey'];
6575
            }
6576
6577
            $user_row[]= $user['first_connection'];
6578
            $user_row[]= $user['last_connection'];
6579
            if (isset($_GET['additional_profile_field']) && is_numeric($_GET['additional_profile_field'])) {
6580
                $user_row[]= $user['additional'];
6581
            }
6582
6583
            $user_row[]= $user['link'];
6584
6585
            $users[] = $user_row;
6586
6587
    		if ($export_csv) {
6588
    		    if (empty($session_id)) {
6589
                    $user_row = array_map('strip_tags', $user_row);
6590
    			    unset($user_row[14]);
6591
    			    unset($user_row[15]);
6592
                } else {
6593
                    $user_row = array_map('strip_tags', $user_row);
6594
                    unset($user_row[13]);
6595
                    unset($user_row[14]);
6596
                }
6597
6598
    			$csv_content[] = $user_row;
6599
    		}
6600
    	}
6601
    	return $users;
6602
    }
6603
}
6604
6605
/**
6606
 * @package chamilo.tracking
6607
 */
6608
class TrackingUserLog
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...
6609
{
6610
    /**
6611
     * Displays the number of logins every month for a specific user in a specific course.
6612
     * @param $view
6613
     * @param int $user_id
6614
     * @param int $course_id
6615
     * @param int $session_id
6616
     */
6617
    public function display_login_tracking_info($view, $user_id, $course_id, $session_id = 0)
6618
    {
6619
    	$MonthsLong = $GLOBALS['MonthsLong'];
6620
6621
    	// protected data
6622
    	$user_id = intval($user_id);
6623
    	$session_id = intval($session_id);
6624
    	$course_id = Database::escape_string($course_id);
6625
6626
    	$track_access_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6627
    	$tempView = $view;
6628
    	if(substr($view,0,1) == '1') {
6629
    		$new_view = substr_replace($view,'0',0,1);
6630
    		echo "
6631
                <tr>
6632
                    <td valign='top'>
6633
                    <font color='#0000FF'>-&nbsp;&nbsp;&nbsp;</font>" .
6634
                    "<b>".get_lang('LoginsAndAccessTools')."</b>&nbsp;&nbsp;&nbsp;[<a href='".api_get_self()."?uInfo=".$user_id."&view=".Security::remove_XSS($new_view)."'>".get_lang('Close')."</a>]&nbsp;&nbsp;&nbsp;[<a href='userLogCSV.php?".api_get_cidreq()."&uInfo=".Security::remove_XSS($_GET['uInfo'])."&view=10000'>".get_lang('ExportAsCSV')."</a>]
6635
                    </td>
6636
                </tr>
6637
                ";
6638
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('LoginsDetails')."<br>";
6639
6640
    		$sql = "SELECT UNIX_TIMESTAMP(access_date), count(access_date)
6641
                        FROM $track_access_table
6642
                        WHERE access_user_id = $user_id
6643
                        AND c_id = $course_id
6644
                        AND access_session_id = $session_id
6645
                        GROUP BY YEAR(access_date),MONTH(access_date)
6646
                        ORDER BY YEAR(access_date),MONTH(access_date) ASC";
6647
6648
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6649
    		$results = getManyResults3Col($sql);
6650
6651
    		echo "<table cellpadding='2' cellspacing='1' border='0' align=center>";
6652
    		echo "<tr>
6653
                    <td class='secLine'>
6654
                    ".get_lang('LoginsTitleMonthColumn')."
6655
                    </td>
6656
                    <td class='secLine'>
6657
                    ".get_lang('LoginsTitleCountColumn')."
6658
                    </td>
6659
                </tr>";
6660
    		$total = 0;
6661
    		if (is_array($results)) {
6662
    			for($j = 0 ; $j < count($results) ; $j++) {
6663
    				echo "<tr>";
6664
    				echo "<td class='content'><a href='logins_details.php?uInfo=".$user_id."&reqdate=".$results[$j][0]."&view=".Security::remove_XSS($view)."'>".$MonthsLong[date('n', $results[$j][0])-1].' '.date('Y', $results[$j][0])."</a></td>";
6665
    				echo "<td valign='top' align='right' class='content'>".$results[$j][1]."</td>";
6666
    				echo"</tr>";
6667
    				$total = $total + $results[$j][1];
6668
    			}
6669
    			echo "<tr>";
6670
    			echo "<td>".get_lang('Total')."</td>";
6671
    			echo "<td align='right' class='content'>".$total."</td>";
6672
    			echo"</tr>";
6673
    		} else {
6674
    			echo "<tr>";
6675
    			echo "<td colspan='2'><center>".get_lang('NoResult')."</center></td>";
6676
    			echo"</tr>";
6677
    		}
6678
    		echo "</table>";
6679
    		echo "</td></tr>";
6680 View Code Duplication
    	} else {
6681
    		$new_view = substr_replace($view,'1',0,1);
6682
    		echo "
6683
                <tr>
6684
                    <td valign='top'>
6685
                    +<font color='#0000FF'>&nbsp;&nbsp;</font><a href='".api_get_self()."?uInfo=".$user_id."&view=".Security::remove_XSS($new_view)."' class='specialLink'>".get_lang('LoginsAndAccessTools')."</a>
6686
                    </td>
6687
                </tr>
6688
            ";
6689
    	}
6690
    }
6691
6692
    /**
6693
     * Displays the exercise results for a specific user in a specific course.
6694
     * @param   string $view
6695
     * @param   int $user_id    User ID
6696
     * @param   string  $courseCode Course code
6697
     * @return array
6698
     * @todo remove globals
6699
     */
6700
    public function display_exercise_tracking_info($view, $user_id, $courseCode)
6701
    {
6702
    	global $TBL_TRACK_HOTPOTATOES, $TABLECOURSE_EXERCICES, $TABLETRACK_EXERCICES, $dateTimeFormatLong;
6703
        $courseId = api_get_course_int_id($courseCode);
6704
    	if(substr($view,1,1) == '1') {
6705
    		$new_view = substr_replace($view,'0',1,1);
6706
    		echo "<tr>
6707
                    <td valign='top'>
6708
                        <font color='#0000FF'>-&nbsp;&nbsp;&nbsp;</font><b>".get_lang('ExercicesResults')."</b>&nbsp;&nbsp;&nbsp;[<a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."'>".get_lang('Close')."</a>]&nbsp;&nbsp;&nbsp;[<a href='userLogCSV.php?".api_get_cidreq()."&uInfo=".Security::remove_XSS($_GET['uInfo'])."&view=01000'>".get_lang('ExportAsCSV')."</a>]
6709
                    </td>
6710
                </tr>";
6711
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('ExercicesDetails')."<br />";
6712
6713
    		$sql = "SELECT ce.title, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
6714
                    FROM $TABLECOURSE_EXERCICES AS ce , $TABLETRACK_EXERCICES AS te
6715
                    WHERE te.c_id = $courseId
6716
                        AND te.exe_user_id = ".intval($user_id)."
6717
                        AND te.exe_exo_id = ce.id
6718
                    ORDER BY ce.title ASC, te.exe_date ASC";
6719
6720
    		$hpsql = "SELECT te.exe_name, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
6721
                        FROM $TBL_TRACK_HOTPOTATOES AS te
6722
                        WHERE te.exe_user_id = '".intval($user_id)."' AND te.c_id = $courseId
6723
                        ORDER BY te.c_id ASC, te.exe_date ASC";
6724
6725
    		$hpresults = StatsUtils::getManyResultsXCol($hpsql, 4);
6726
6727
    		$NoTestRes = 0;
6728
    		$NoHPTestRes = 0;
6729
6730
    		echo "<tr>\n<td style='padding-left : 40px;padding-right : 40px;'>\n";
6731
    		$results = StatsUtils::getManyResultsXCol($sql, 4);
6732
    		echo "<table cellpadding='2' cellspacing='1' border='0' align='center'>\n";
6733
    		echo "
6734
                <tr bgcolor='#E6E6E6'>
6735
                    <td>
6736
                    ".get_lang('ExercicesTitleExerciceColumn')."
6737
                    </td>
6738
                    <td>
6739
                    ".get_lang('Date')."
6740
                    </td>
6741
                    <td>
6742
                    ".get_lang('ExercicesTitleScoreColumn')."
6743
                    </td>
6744
                </tr>";
6745
6746
    		if (is_array($results)) {
6747
    			for($i = 0; $i < sizeof($results); $i++) {
6748
    				$display_date = api_convert_and_format_date($results[$i][3], null, date_default_timezone_get());
6749
    				echo "<tr>\n";
6750
    				echo "<td class='content'>".$results[$i][0]."</td>\n";
6751
    				echo "<td class='content'>".$display_date."</td>\n";
6752
    				echo "<td valign='top' align='right' class='content'>".$results[$i][1]." / ".$results[$i][2]."</td>\n";
6753
    				echo "</tr>\n";
6754
    			}
6755
    		} else {
6756
    			// istvan begin
6757
    			$NoTestRes = 1;
6758
    		}
6759
6760
    		// The Result of Tests
6761
    		if (is_array($hpresults)) {
6762
    			for($i = 0; $i < sizeof($hpresults); $i++) {
6763
    				$title = GetQuizName($hpresults[$i][0],'');
6764
    				if ($title == '')
6765
    				$title = basename($hpresults[$i][0]);
6766
    				$display_date = api_convert_and_format_date($hpresults[$i][3], null, date_default_timezone_get());
6767
    				?>
6768
                    <tr>
6769
                        <td class="content"><?php echo $title; ?></td>
6770
                        <td class="content" align="center"><?php echo $display_date; ?></td>
6771
                        <td class="content" align="center"><?php echo $hpresults[$i][1]; ?> / <?php echo $hpresults[$i][2]; ?>
6772
                        </td>
6773
                    </tr>
6774
6775
                    <?php
6776
                }
6777
    		} else {
6778
    			$NoHPTestRes = 1;
6779
    		}
6780
6781
    		if ($NoTestRes == 1 && $NoHPTestRes == 1) {
6782
    			echo "<tr>\n";
6783
    			echo "<td colspan='3'><center>".get_lang('NoResult')."</center></td>\n";
6784
    			echo "</tr>\n";
6785
    		}
6786
    		echo "</table>";
6787
    		echo "</td>\n</tr>\n";
6788
    	} else {
6789
    		$new_view = substr_replace($view,'1',1,1);
6790
    		echo "
6791
                <tr>
6792
                    <td valign='top'>
6793
                        +<font color='#0000FF'>&nbsp;&nbsp;</font><a href='".api_get_self()."?uInfo=$user_id&view=".$new_view."' class='specialLink'>".get_lang('ExercicesResults')."</a>
6794
                    </td>
6795
                </tr>";
6796
    	}
6797
    }
6798
6799
    /**
6800
     * Displays the student publications for a specific user in a specific course.
6801
     * @todo remove globals
6802
     */
6803
    public function display_student_publications_tracking_info($view, $user_id, $course_id)
6804
    {
6805
    	global $TABLETRACK_UPLOADS, $TABLECOURSE_WORK;
6806
        $_course = api_get_course_info_by_id($course_id);
6807
6808
    	if (substr($view,2,1) == '1') {
6809
    		$new_view = substr_replace($view,'0',2,1);
6810
    		echo "<tr>
6811
                    <td valign='top'>
6812
                    <font color='#0000FF'>-&nbsp;&nbsp;&nbsp;</font><b>".get_lang('WorkUploads')."</b>&nbsp;&nbsp;&nbsp;[<a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."'>".get_lang('Close')."</a>]&nbsp;&nbsp;&nbsp;[<a href='userLogCSV.php?".api_get_cidreq()."&uInfo=".Security::remove_XSS($_GET['uInfo'])."&view=00100'>".get_lang('ExportAsCSV')."</a>]
6813
                    </td>
6814
                </tr>";
6815
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('WorksDetails')."<br>";
6816
    		$sql = "SELECT u.upload_date, w.title, w.author,w.url
6817
                    FROM $TABLETRACK_UPLOADS u , $TABLECOURSE_WORK w
6818
                    WHERE u.upload_work_id = w.id
6819
                        AND u.upload_user_id = '".intval($user_id)."'
6820
                        AND u.c_id = '".intval($course_id)."'
6821
                    ORDER BY u.upload_date DESC";
6822
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6823
    		$results = StatsUtils::getManyResultsXCol($sql,4);
6824
    		echo "<table cellpadding='2' cellspacing='1' border='0' align=center>";
6825
    		echo "<tr>
6826
                    <td class='secLine' width='40%'>
6827
                    ".get_lang('WorkTitle')."
6828
                    </td>
6829
                    <td class='secLine' width='30%'>
6830
                    ".get_lang('WorkAuthors')."
6831
                    </td>
6832
                    <td class='secLine' width='30%'>
6833
                    ".get_lang('Date')."
6834
                    </td>
6835
                </tr>";
6836
    		if (is_array($results)) {
6837
    			for($j = 0 ; $j < count($results) ; $j++) {
6838
    				$pathToFile = api_get_path(WEB_COURSE_PATH).$_course['path']."/".$results[$j][3];
6839
    				$beautifulDate = api_convert_and_format_date($results[$j][0], null, date_default_timezone_get());
6840
    				echo "<tr>";
6841
    				echo "<td class='content'>"
6842
    				."<a href ='".$pathToFile."'>".$results[$j][1]."</a>"
6843
    				."</td>";
6844
    				echo "<td class='content'>".$results[$j][2]."</td>";
6845
    				echo "<td class='content'>".$beautifulDate."</td>";
6846
    				echo"</tr>";
6847
    			}
6848
    		} else {
6849
    			echo "<tr>";
6850
    			echo "<td colspan='3'><center>".get_lang('NoResult')."</center></td>";
6851
    			echo"</tr>";
6852
    		}
6853
    		echo "</table>";
6854
    		echo "</td></tr>";
6855 View Code Duplication
    	} else {
6856
    		$new_view = substr_replace($view,'1',2,1);
6857
    		echo "
6858
                <tr>
6859
                    <td valign='top'>
6860
                    +<font color='#0000FF'>&nbsp;&nbsp;</font><a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."' class='specialLink'>".get_lang('WorkUploads')."</a>
6861
                    </td>
6862
                </tr>
6863
            ";
6864
    	}
6865
    }
6866
6867
    /**
6868
     * Displays the links followed for a specific user in a specific course.
6869
     * @todo remove globals
6870
     */
6871
    public function display_links_tracking_info($view, $user_id, $courseCode)
6872
    {
6873
    	global $TABLETRACK_LINKS, $TABLECOURSE_LINKS;
6874
        $courseId = api_get_course_int_id($courseCode);
6875
    	if (substr($view,3,1) == '1') {
6876
    		$new_view = substr_replace($view,'0',3,1);
6877
    		echo "
6878
                <tr>
6879
                        <td valign='top'>
6880
                        <font color='#0000FF'>-&nbsp;&nbsp;&nbsp;</font><b>".get_lang('LinksAccess')."</b>&nbsp;&nbsp;&nbsp;[<a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."'>".get_lang('Close')."</a>]&nbsp;&nbsp;&nbsp;[<a href='userLogCSV.php?".api_get_cidreq()."&uInfo=".Security::remove_XSS($_GET['uInfo'])."&view=00010'>".get_lang('ExportAsCSV')."</a>]
6881
                        </td>
6882
                </tr>
6883
            ";
6884
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('LinksDetails')."<br>";
6885
    		$sql = "SELECT cl.title, cl.url
6886
                    FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
6887
                    WHERE sl.links_link_id = cl.id
6888
                        AND sl.c_id = $courseId
6889
                        AND sl.links_user_id = ".intval($user_id)."
6890
                    GROUP BY cl.title, cl.url";
6891
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6892
    		$results = StatsUtils::getManyResults2Col($sql);
6893
    		echo "<table cellpadding='2' cellspacing='1' border='0' align=center>";
6894
    		echo "<tr>
6895
                    <td class='secLine'>
6896
                    ".get_lang('LinksTitleLinkColumn')."
6897
                    </td>
6898
                </tr>";
6899
    		if (is_array($results)) {
6900
    			for($j = 0 ; $j < count($results) ; $j++) {
6901
    				echo "<tr>";
6902
    				echo "<td class='content'><a href='".$results[$j][1]."'>".$results[$j][0]."</a></td>";
6903
    				echo"</tr>";
6904
    			}
6905
    		} else {
6906
    			echo "<tr>";
6907
    			echo "<td ><center>".get_lang('NoResult')."</center></td>";
6908
    			echo"</tr>";
6909
    		}
6910
    		echo "</table>";
6911
    		echo "</td></tr>";
6912 View Code Duplication
    	} else {
6913
    		$new_view = substr_replace($view,'1',3,1);
6914
    		echo "
6915
                <tr>
6916
                    <td valign='top'>
6917
                    +<font color='#0000FF'>&nbsp;&nbsp;</font><a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."' class='specialLink'>".get_lang('LinksAccess')."</a>
6918
                    </td>
6919
                </tr>
6920
            ";
6921
    	}
6922
    }
6923
6924
    /**
6925
     * Displays the documents downloaded for a specific user in a specific course.
6926
     * @param     string    kind of view inside tracking info
6927
     * @param    int        User id
6928
     * @param    string    Course code
6929
     * @param    int        Session id (optional, default = 0)
6930
     * @return     void
6931
     */
6932
    public static function display_document_tracking_info($view, $user_id, $course_code, $session_id = 0)
6933
    {
6934
    	// protect data
6935
        $user_id = intval($user_id);
6936
        $courseId = api_get_course_int_id($course_code);
6937
    	$session_id = intval($session_id);
6938
6939
    	$downloads_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
6940
    	if(substr($view,4,1) == '1') {
6941
    		$new_view = substr_replace($view,'0',4,1);
6942
    		echo "
6943
                <tr>
6944
                    <td valign='top'>
6945
                    <font color='#0000FF'>-&nbsp;&nbsp;&nbsp;</font><b>".get_lang('DocumentsAccess')."</b>&nbsp;&nbsp;&nbsp;[<a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."'>".get_lang('Close')."</a>]&nbsp;&nbsp;&nbsp;[<a href='userLogCSV.php?".api_get_cidreq()."&uInfo=".Security::remove_XSS($_GET['uInfo'])."&view=00001'>".get_lang('ExportAsCSV')."</a>]
6946
                    </td>
6947
                </tr>
6948
            ";
6949
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('DocumentsDetails')."<br>";
6950
6951
    		$sql = "SELECT down_doc_path
6952
                    FROM $downloads_table
6953
                    WHERE c_id = $courseId
6954
                        AND down_user_id = $user_id
6955
                        AND down_session_id = $session_id
6956
                    GROUP BY down_doc_path";
6957
6958
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6959
    		$results = StatsUtils::getManyResults1Col($sql);
6960
    		echo "<table cellpadding='2' cellspacing='1' border='0' align='center'>";
6961
    		echo "<tr>
6962
                    <td class='secLine'>
6963
                    ".get_lang('DocumentsTitleDocumentColumn')."
6964
                    </td>
6965
                </tr>";
6966
    		if (is_array($results)) {
6967
    			for($j = 0 ; $j < count($results) ; $j++) {
6968
    				echo "<tr>";
6969
    				echo "<td class='content'>".$results[$j]."</td>";
6970
    				echo"</tr>";
6971
    			}
6972
    		} else {
6973
    			echo "<tr>";
6974
    			echo "<td><center>".get_lang('NoResult')."</center></td>";
6975
    			echo"</tr>";
6976
    		}
6977
    		echo "</table>";
6978
    		echo "</td></tr>";
6979 View Code Duplication
    	} else {
6980
    		$new_view = substr_replace($view,'1',4,1);
6981
    		echo "
6982
                <tr>
6983
                    <td valign='top'>
6984
                    +<font color='#0000FF'>&nbsp;&nbsp;</font><a href='".api_get_self()."?uInfo=".Security::remove_XSS($user_id)."&view=".Security::remove_XSS($new_view)."' class='specialLink'>".get_lang('DocumentsAccess')."</a>
6985
                    </td>
6986
                </tr>
6987
            ";
6988
    	}
6989
    }
6990
6991
    /**
6992
     * Gets the IP of a given user, using the last login before the given date
6993
     * @param int User ID
6994
     * @param string Datetime
6995
     * @param bool Whether to return the IP as a link or just as an IP
6996
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6997
     * @return string IP address (or false on error)
6998
     * @assert (0,0) === false
6999
     */
7000
    public static function get_ip_from_user_event($user_id, $event_date, $return_as_link = false, $body_replace = null)
7001
    {
7002
        if (empty($user_id) or empty($event_date)) {
7003
            return false;
7004
        }
7005
        $user_id = intval($user_id);
7006
        $event_date = Database::escape_string($event_date);
7007
7008
        $table_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7009
        $sql_ip = "SELECT login_date, user_ip FROM $table_login
7010
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
7011
                   ORDER BY login_date DESC LIMIT 1";
7012
        $ip = '';
7013
        $res_ip = Database::query($sql_ip);
7014
        if ($res_ip !== false && Database::num_rows($res_ip)>0) {
7015
            $row_ip = Database::fetch_row($res_ip);
7016
            if ($return_as_link) {
7017
                $ip = Display::url(
7018
                    (empty($body_replace)?$row_ip[1]:$body_replace), 'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
7019
                    array('title'=>get_lang('TraceIP'), 'target'=>'_blank')
7020
                );
7021
            } else {
7022
                $ip = $row_ip[1];
7023
            }
7024
        }
7025
7026
        return $ip;
7027
    }
7028
}
7029
7030
/**
7031
 * @package chamilo.tracking
7032
 */
7033
class TrackingUserLogCSV
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...
7034
{
7035
    /**
7036
     * Displays the number of logins every month for a specific user in a specific course.
7037
     * @param $view
7038
     * @param int $user_id
7039
     * @param int $course_id
7040
     * @param int $session_id
7041
     * @return array
7042
     */
7043
    public function display_login_tracking_info($view, $user_id, $course_id, $session_id = 0)
7044
    {
7045
    	$MonthsLong = $GLOBALS['MonthsLong'];
7046
    	$track_access_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
7047
7048
    	// protected data
7049
    	$user_id    = intval($user_id);
7050
    	$session_id = intval($session_id);
7051
    	$course_id  = intval($course_id);
7052
7053
    	$tempView = $view;
7054
    	if (substr($view,0,1) == '1') {
7055
    		$new_view = substr_replace($view,'0',0,1);
7056
    		$title[1]= get_lang('LoginsAndAccessTools').get_lang('LoginsDetails');
7057
    		$sql = "SELECT UNIX_TIMESTAMP(access_date), count(access_date)
7058
                    FROM $track_access_table
7059
                    WHERE access_user_id = $user_id
7060
                    AND c_id = $course_id
7061
                    AND access_session_id = $session_id
7062
                    GROUP BY YEAR(access_date),MONTH(access_date)
7063
                    ORDER BY YEAR(access_date),MONTH(access_date) ASC";
7064
    		//$results = getManyResults2Col($sql);
7065
    		$results = getManyResults3Col($sql);
7066
    		$title_line= get_lang('LoginsTitleMonthColumn').';'.get_lang('LoginsTitleCountColumn')."\n";
7067
    		$line='';
7068
    		$total = 0;
7069 View Code Duplication
    		if (is_array($results)) {
7070
    			for($j = 0 ; $j < count($results) ; $j++) {
7071
    				$line .= $results[$j][0].';'.$results[$j][1]."\n";
7072
    				$total = $total + $results[$j][1];
7073
    			}
7074
    			$line .= get_lang('Total').";".$total."\n";
7075
    		} else {
7076
    			$line= get_lang('NoResult')."</center></td>";
7077
    		}
7078
    	} else {
7079
    		$new_view = substr_replace($view,'1',0,1);
7080
    	}
7081
    	return array($title_line, $line);
7082
    }
7083
7084
    /**
7085
     * Displays the exercise results for a specific user in a specific course.
7086
     * @param   string $view
7087
     * @param   int $user_id    User ID
0 ignored issues
show
Bug introduced by
There is no parameter named $user_id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
7088
     * @param   string  $courseCode Course code
7089
     * @return array
7090
     * @todo remove globals
7091
     */
7092
    public function display_exercise_tracking_info($view, $userId, $courseCode)
7093
    {
7094
    	global $TABLECOURSE_EXERCICES, $TABLETRACK_EXERCICES, $TABLETRACK_HOTPOTATOES, $dateTimeFormatLong;
7095
        $courseId = api_get_course_int_id($courseCode);
7096
        $userId = intval($userId);
7097
    	if (substr($view,1,1) == '1') {
7098
    		$new_view = substr_replace($view,'0',1,1);
7099
    		$title[1] = get_lang('ExercicesDetails');
7100
    		$line = '';
7101
    		$sql = "SELECT ce.title, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
7102
                    FROM $TABLECOURSE_EXERCICES AS ce , $TABLETRACK_EXERCICES AS te
7103
                    WHERE te.c_id = $courseId
7104
                        AND te.exe_user_id = $userId
7105
                        AND te.exe_exo_id = ce.id
7106
                    ORDER BY ce.title ASC, te.exe_date ASC";
7107
7108
    		$hpsql = "SELECT te.exe_name, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
7109
                        FROM $TABLETRACK_HOTPOTATOES AS te
7110
                        WHERE te.exe_user_id = '$userId' AND te.c_id = $courseId
7111
                        ORDER BY te.c_id ASC, te.exe_date ASC";
7112
7113
    		$hpresults = StatsUtils::getManyResultsXCol($hpsql, 4);
7114
7115
    		$NoTestRes = 0;
7116
    		$NoHPTestRes = 0;
7117
7118
    		$results = StatsUtils::getManyResultsXCol($sql, 4);
7119
    		$title_line = get_lang('ExercicesTitleExerciceColumn').";".get_lang('Date').';'.get_lang('ExercicesTitleScoreColumn')."\n";
7120
7121 View Code Duplication
    		if (is_array($results)) {
7122
    			for($i = 0; $i < sizeof($results); $i++)
7123
    			{
7124
    				$display_date = api_convert_and_format_date($results[$i][3], null, date_default_timezone_get());
7125
    				$line .= $results[$i][0].";".$display_date.";".$results[$i][1]." / ".$results[$i][2]."\n";
7126
    			}
7127
    		} else {
7128
                // istvan begin
7129
    			$NoTestRes = 1;
7130
    		}
7131
7132
    		// The Result of Tests
7133
    		if (is_array($hpresults)) {
7134
    			for($i = 0; $i < sizeof($hpresults); $i++) {
7135
    				$title = GetQuizName($hpresults[$i][0],'');
7136
7137
    				if ($title == '')
7138
    				$title = basename($hpresults[$i][0]);
7139
7140
    				$display_date = api_convert_and_format_date($hpresults[$i][3], null, date_default_timezone_get());
7141
7142
    				$line .= $title.';'.$display_date.';'.$hpresults[$i][1].'/'.$hpresults[$i][2]."\n";
7143
    			}
7144
    		} else {
7145
    			$NoHPTestRes = 1;
7146
    		}
7147
7148
    		if ($NoTestRes == 1 && $NoHPTestRes == 1) {
7149
    			$line=get_lang('NoResult');
7150
    		}
7151
    	} else {
7152
    		$new_view = substr_replace($view,'1',1,1);
7153
    	}
7154
    	return array($title_line, $line);
7155
    }
7156
7157
    /**
7158
     * Displays the student publications for a specific user in a specific course.
7159
     * @todo remove globals
7160
     */
7161
    public function display_student_publications_tracking_info($view, $user_id, $course_id)
7162
    {
7163
    	global $TABLETRACK_UPLOADS, $TABLECOURSE_WORK;
7164
        $_course = api_get_course_info();
7165
        $user_id = intval($user_id);
7166
        $course_id = intval($course_id);
7167
7168
    	if (substr($view,2,1) == '1') {
7169
    		$sql = "SELECT u.upload_date, w.title, w.author, w.url
7170
                    FROM $TABLETRACK_UPLOADS u , $TABLECOURSE_WORK w
7171
                    WHERE
7172
                        u.upload_work_id = w.id AND
7173
                        u.upload_user_id = '$user_id' AND
7174
                        u.c_id = '$course_id'
7175
                    ORDER BY u.upload_date DESC";
7176
    		$results = StatsUtils::getManyResultsXCol($sql,4);
7177
7178
    		$title[1]=get_lang('WorksDetails');
7179
    		$line='';
7180
    		$title_line=get_lang('WorkTitle').";".get_lang('WorkAuthors').";".get_lang('Date')."\n";
7181
7182
    		if (is_array($results)) {
7183
    			for($j = 0 ; $j < count($results) ; $j++) {
7184
    				$pathToFile = api_get_path(WEB_COURSE_PATH).$_course['path']."/".$results[$j][3];
7185
    				$beautifulDate = api_convert_and_format_date($results[$j][0], null, date_default_timezone_get());
7186
    				$line .= $results[$j][1].";".$results[$j][2].";".$beautifulDate."\n";
7187
    			}
7188
7189
    		} else {
7190
    			$line= get_lang('NoResult');
7191
    		}
7192
    	}
7193
    	return array($title_line, $line);
7194
    }
7195
7196
    /**
7197
     * Displays the links followed for a specific user in a specific course.
7198
     * @todo remove globals
7199
     */
7200
    public function display_links_tracking_info($view, $userId, $courseCode)
7201
    {
7202
    	global $TABLETRACK_LINKS, $TABLECOURSE_LINKS;
7203
        $courseId = api_get_course_int_id($courseCode);
7204
        $userId = intval($userId);
7205
        $line = null;
7206 View Code Duplication
    	if (substr($view,3,1) == '1') {
7207
    		$new_view = substr_replace($view,'0',3,1);
7208
    		$title[1]=get_lang('LinksDetails');
7209
    		$sql = "SELECT cl.title, cl.url
7210
                        FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
7211
                        WHERE sl.links_link_id = cl.id
7212
                            AND sl.c_id = $courseId
7213
                            AND sl.links_user_id = $userId
7214
                        GROUP BY cl.title, cl.url";
7215
    		$results = StatsUtils::getManyResults2Col($sql);
7216
    		$title_line= get_lang('LinksTitleLinkColumn')."\n";
7217
    		if (is_array($results)) {
7218
    			for ($j = 0 ; $j < count($results) ; $j++) {
7219
    				$line .= $results[$j][0]."\n";
7220
    			}
7221
    		} else {
7222
    			$line=get_lang('NoResult');
7223
    		}
7224
    	} else {
7225
    		$new_view = substr_replace($view,'1',3,1);
7226
    	}
7227
    	return array($title_line, $line);
7228
    }
7229
7230
    /**
7231
     * Displays the documents downloaded for a specific user in a specific course.
7232
     * @param     string    kind of view inside tracking info
7233
     * @param    int        User id
7234
     * @param    string    Course code
7235
     * @param    int        Session id (optional, default = 0)
7236
     * @return     void
7237
     */
7238
    public function display_document_tracking_info($view, $user_id, $courseCode, $session_id = 0)
7239
    {
7240
    	// protect data
7241
    	$user_id     = intval($user_id);
7242
        $courseId = api_get_course_int_id($courseCode);
7243
    	$session_id = intval($session_id);
7244
7245
    	$downloads_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
7246
7247 View Code Duplication
    	if (substr($view,4,1) == '1') {
7248
    		$new_view = substr_replace($view,'0',4,1);
7249
    		$title[1]= get_lang('DocumentsDetails');
7250
7251
    		$sql = "SELECT down_doc_path
7252
                        FROM $downloads_table
7253
                        WHERE c_id = $courseId
7254
                            AND down_user_id = $user_id
7255
                            AND down_session_id = $session_id
7256
                        GROUP BY down_doc_path";
7257
7258
    		$results = StatsUtils::getManyResults1Col($sql);
7259
    		$title_line = get_lang('DocumentsTitleDocumentColumn')."\n";
7260
            $line = null;
7261
    		if (is_array($results)) {
7262
    			for ($j = 0 ; $j < count($results) ; $j++) {
7263
    				$line .= $results[$j]."\n";
7264
    			}
7265
    		} else {
7266
    			$line = get_lang('NoResult');
7267
    		}
7268
    	} else {
7269
    		$new_view = substr_replace($view,'1',4,1);
7270
    	}
7271
    	return array($title_line, $line);
7272
    }
7273
7274
    /**
7275
     * @param $userId
7276
     * @param $courseInfo
7277
     * @param int $sessionId
7278
     * @return array
7279
     */
7280
    public static function getToolInformation(
7281
        $userId,
7282
        $courseInfo,
7283
        $sessionId = 0
7284
    ) {
7285
        $csvContent = array();
7286
        $courseToolInformation = null;
7287
        $headerTool = array(
7288
            array(get_lang('Title')),
7289
            array(get_lang('CreatedAt')),
7290
            array(get_lang('UpdatedAt')),
7291
        );
7292
7293
        $headerListForCSV = array();
7294
        foreach ($headerTool as $item) {
7295
            $headerListForCSV[] = $item[0];
7296
        }
7297
7298
        $courseForumInformationArray = getForumCreatedByUser(
7299
            $userId,
7300
            $courseInfo['real_id'],
7301
            $sessionId
7302
        );
7303
7304
        if (!empty($courseForumInformationArray)) {
7305
            $csvContent[] = array();
7306
            $csvContent[] = get_lang('Forums');
7307
            $csvContent[] = $headerListForCSV;
7308
            foreach ($courseForumInformationArray as $row) {
7309
                $csvContent[] = $row;
7310
            }
7311
7312
            $courseToolInformation .= Display::page_subheader2(
7313
                get_lang('Forums')
7314
            );
7315
            $courseToolInformation .= Display::return_sortable_table(
7316
                $headerTool,
7317
                $courseForumInformationArray
7318
            );
7319
        }
7320
7321
        $courseWorkInformationArray = getWorkCreatedByUser(
7322
            $userId,
7323
            $courseInfo['real_id'],
7324
            $sessionId
7325
        );
7326
7327
        if (!empty($courseWorkInformationArray)) {
7328
            $csvContent[] = null;
7329
            $csvContent[] = get_lang('Works');
7330
            $csvContent[] = $headerListForCSV;
7331
7332
            foreach ($courseWorkInformationArray as $row) {
7333
                $csvContent[] = $row;
7334
            }
7335
            $csvContent[] = null;
7336
7337
            $courseToolInformation .= Display::page_subheader2(
7338
                get_lang('Works')
7339
            );
7340
            $courseToolInformation .= Display::return_sortable_table(
7341
                $headerTool,
7342
                $courseWorkInformationArray
7343
            );
7344
        }
7345
        $courseToolInformationTotal = null;
7346
7347
        if (!empty($courseToolInformation)) {
7348
            $sessionTitle = null;
7349
            if (!empty($sessionId)) {
7350
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
7351
            }
7352
7353
            $courseToolInformationTotal .= Display::page_subheader(
7354
                $courseInfo['title'].$sessionTitle
7355
            );
7356
            $courseToolInformationTotal .= $courseToolInformation;
7357
        }
7358
7359
        return array(
7360
            'array' => $csvContent,
7361
            'html' => $courseToolInformationTotal
7362
        );
7363
    }
7364
}
7365