Completed
Push — 1.10.x ( f53958...0225a3 )
by Angel Fernando Quiroz
42:01
created

Tracking::getLastStudentPublication()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 10
c 1
b 0
f 1
nc 1
nop 4
dl 0
loc 26
rs 8.8571
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) {
1201
                $courses[$course['code']] = $course['code'];
1202
            }
1203
            $sessions = SessionManager::get_sessions_followed_by_drh($userId);
1204
        } else {
1205
1206
            $studentList = UserManager::getUsersFollowedByUser(
1207
                $userId,
1208
                STUDENT,
1209
                false,
1210
                false,
1211
                false,
1212
                null,
1213
                null,
1214
                null,
1215
                null,
1216
                null,
1217
                null,
1218
                COURSEMANAGER
1219
            );
1220
1221
            $students = array();
1222
            foreach ($studentList as $studentData) {
1223
                $students[] = $studentData['user_id'];
1224
            }
1225
1226
            $studentBossesList = UserManager::getUsersFollowedByUser(
1227
                $userId,
1228
                STUDENT_BOSS,
1229
                false,
1230
                false,
1231
                false,
1232
                null,
1233
                null,
1234
                null,
1235
                null,
1236
                null,
1237
                null,
1238
                COURSEMANAGER
1239
            );
1240
            $studentBosses = array();
1241
            foreach ($studentBossesList as $studentBossData) {
1242
                $studentBosses[] = $studentBossData['user_id'];
1243
            }
1244
1245
            $teacherList = UserManager::getUsersFollowedByUser(
1246
                $userId,
1247
                COURSEMANAGER,
1248
                false,
1249
                false,
1250
                false,
1251
                null,
1252
                null,
1253
                null,
1254
                null,
1255
                null,
1256
                null,
1257
                COURSEMANAGER
1258
            );
1259
1260
            $teachers = array();
1261
            foreach ($teacherList as $teacherData) {
1262
                $teachers[] = $teacherData['user_id'];
1263
            }
1264
1265
            $humanResources = UserManager::getUsersFollowedByUser(
1266
                $userId,
1267
                DRH,
1268
                false,
1269
                false,
1270
                false,
1271
                null,
1272
                null,
1273
                null,
1274
                null,
1275
                null,
1276
                null,
1277
                COURSEMANAGER
1278
            );
1279
1280
            $humanResourcesList = array();
1281
            foreach ($humanResources as $item) {
1282
                $humanResourcesList[] = $item['user_id'];
1283
            }
1284
1285
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1286
                $userId,
1287
                COURSEMANAGER,
1288
                null,
1289
                null,
1290
                null,
1291
                null,
1292
                false,
1293
                null,
1294
                null,
1295
                true
1296
            );
1297
1298
            foreach ($platformCourses as $course) {
1299
                $assignedCourses[$course['code']] = $course['code'];
1300
            }
1301
1302
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1303
                $userId,
1304
                COURSEMANAGER
1305
            );
1306
            foreach ($platformCourses as $course) {
1307
                $courses[$course['code']] = $course['code'];
1308
            }
1309
1310
            $sessions = SessionManager::getSessionsFollowedByUser(
1311
                $userId,
1312
                COURSEMANAGER
1313
            );
1314
        }
1315
1316
        return array(
1317
            'drh' => $humanResourcesList,
1318
            'teachers' => $teachers,
1319
            'students' => $students,
1320
            'studentBosses' => $studentBosses,
1321
            'courses' => $courses,
1322
            'sessions' => $sessions,
1323
            'assignedCourses' => $assignedCourses
1324
        );
1325
    }
1326
1327
    /**
1328
     * Calculates the time spent on the platform by a user
1329
     * @param   int|array User id
1330
     * @param   string type of time filter: 'last_week' or 'custom'
1331
     * @param   string  start date date('Y-m-d H:i:s')
1332
     * @param   string  end date date('Y-m-d H:i:s')
1333
     * @return timestamp $nb_seconds
1334
     */
1335
    public static function get_time_spent_on_the_platform(
1336
        $userId,
1337
        $timeFilter = 'last_7_days',
1338
        $start_date = null,
1339
        $end_date = null
1340
    ) {
1341
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1342
        $condition_time = '';
1343
1344
        if (is_array($userId)) {
1345
            $userList = array_map('intval', $userId);
1346
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1347
        } else {
1348
            $userCondition = " login_user_id = ".intval($userId);
1349
        }
1350
1351
        if (empty($timeFilter)) {
1352
            $timeFilter = 'last_week';
1353
        }
1354
1355
        $today = date('Y-m-d H:i:s');
1356
1357
        switch ($timeFilter) {
1358 View Code Duplication
            case 'last_7_days':
1359
                $new_date = date('Y-m-d H:i:s', strtotime('-7 day'));
1360
                $condition_time = ' AND (login_date >= "'.$new_date.'" AND logout_date <= "'.$today.'") ';
1361
                break;
1362 View Code Duplication
            case 'last_30_days':
1363
                $new_date = date('Y-m-d H:i:s', strtotime('-30 day'));
1364
                $condition_time = ' AND (login_date >= "'.$new_date.'" AND logout_date <= "'.$today.'") ';
1365
               break;
1366
            case 'custom':
1367
                if (!empty($start_date) && !empty($end_date)) {
1368
                    $start_date = Database::escape_string($start_date);
1369
                    $end_date = Database::escape_string($end_date);
1370
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1371
                }
1372
                break;
1373
        }
1374
1375
    	$sql = 'SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1376
    	        FROM '.$tbl_track_login.'
1377
                WHERE '.$userCondition.$condition_time;
1378
    	$rs = Database::query($sql);
1379
        $row = Database::fetch_array($rs, 'ASSOC');
1380
        $diff = $row['diff'];
1381
1382
        if ($diff >= 0) {
1383
            return $diff;
1384
        } else {
1385
            return -1;
1386
        }
1387
    }
1388
1389
    /**
1390
     * Calculates the time spent on the course
1391
     * @param integer $user_id
1392
     * @param integer  $courseId
1393
     * @param int Session id (optional)
1394
     *
1395
     * @return int Time in seconds
1396
     */
1397
    public static function get_time_spent_on_the_course($user_id, $courseId, $session_id = 0)
1398
    {
1399
        $courseId = intval($courseId);
1400
    	$session_id  = intval($session_id);
1401
1402
    	$tbl_track_course = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1403
    	if (is_array($user_id)) {
1404
    	    $user_id = array_map('intval', $user_id);
1405
    		$condition_user = " AND user_id IN (".implode(',',$user_id).") ";
1406
    	} else {
1407
    		$user_id = intval($user_id);
1408
    		$condition_user = " AND user_id = $user_id ";
1409
    	}
1410
1411
    	$sql = "SELECT
1412
    	        SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1413
                FROM $tbl_track_course
1414
                WHERE UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) ";
1415
1416
        if ($courseId != 0) {
1417
            $sql .= "AND c_id = '$courseId' ";
1418
        }
1419
1420
        if ($session_id != -1) {
1421
            $sql .= "AND session_id = '$session_id' ";
1422
        }
1423
1424
        $sql .= $condition_user;
1425
        $rs = Database::query($sql);
1426
    	$row = Database::fetch_array($rs);
1427
1428
    	return $row['nb_seconds'];
1429
    }
1430
1431
    /**
1432
     * Get first connection date for a student
1433
     * @param    int $student_id
1434
     *
1435
     * @return    string|bool Date format long without day or false if there are no connections
1436
     */
1437 View Code Duplication
    public static function get_first_connection_date($student_id)
1438
    {
1439
    	$tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1440
    	$sql = 'SELECT login_date
1441
    	        FROM ' . $tbl_track_login . '
1442
                WHERE login_user_id = ' . intval($student_id) . '
1443
                ORDER BY login_date ASC
1444
                LIMIT 0,1';
1445
1446
    	$rs = Database::query($sql);
1447
    	if (Database::num_rows($rs)>0) {
1448
    		if ($first_login_date = Database::result($rs, 0, 0)) {
1449
                return api_convert_and_format_date(
1450
                    $first_login_date,
1451
                    DATE_FORMAT_SHORT,
1452
                    date_default_timezone_get()
1453
                );
1454
    		}
1455
    	}
1456
1457
    	return false;
1458
    }
1459
1460
    /**
1461
     * Get las connection date for a student
1462
     * @param int $student_id
1463
     * @param bool $warning_message Show a warning message (optional)
1464
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1465
     * @return string|int|bool Date format long without day, false if there are no connections or
1466
     * timestamp if parameter $return_timestamp is true
1467
     */
1468
    public static function get_last_connection_date($student_id, $warning_message = false, $return_timestamp = false)
1469
    {
1470
    	$table = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1471
    	$sql = 'SELECT login_date
1472
    	        FROM ' . $table . '
1473
                WHERE login_user_id = ' . intval($student_id) . '
1474
                ORDER BY login_date
1475
                DESC LIMIT 0,1';
1476
1477
    	$rs = Database::query($sql);
1478
    	if (Database::num_rows($rs) > 0) {
1479
    		if ($last_login_date = Database::result($rs, 0, 0)) {
1480
    			$last_login_date = api_get_local_time($last_login_date);
1481
    			if ($return_timestamp) {
1482
    				return api_strtotime($last_login_date,'UTC');
1483
    			} else {
1484
    				if (!$warning_message) {
1485
    					return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1486
    				} else {
1487
    					$timestamp = api_strtotime($last_login_date,'UTC');
1488
    					$currentTimestamp = time();
1489
1490
    					//If the last connection is > than 7 days, the text is red
1491
    					//345600 = 7 days in seconds
1492
    					if ($currentTimestamp - $timestamp > 604800) {
1493
    						return '<span style="color: #F00;">' . api_format_date($last_login_date, DATE_FORMAT_SHORT) . '</span>';
1494
    					} else {
1495
    						return api_format_date($last_login_date, DATE_FORMAT_SHORT);
1496
    					}
1497
    				}
1498
    			}
1499
    		}
1500
    	}
1501
    	return false;
1502
    }
1503
1504
    /**
1505
     * Get las connection date for a student
1506
     * @param array $studentList Student id array
1507
     * @param int $days
1508
     * @param bool $getCount
1509
     * @return int
1510
     */
1511
    public static function getInactiveUsers($studentList, $days, $getCount = true)
1512
    {
1513
        if (empty($studentList)) {
1514
            return 0;
1515
        }
1516
        $days = intval($days);
1517
        $date = api_get_utc_datetime(strtotime($days.' days ago'));
1518
        $studentList = array_map('intval', $studentList);
1519
1520
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1521
        $select = " SELECT login_user_id ";
1522
        if ($getCount) {
1523
            $select = " SELECT count(DISTINCT login_user_id) as count";
1524
        }
1525
        $sql = "$select
1526
                FROM $tbl_track_login
1527
                WHERE
1528
                    login_user_id IN (' ". implode("','", $studentList) . "' ) AND
1529
                    login_date < '$date'
1530
                ";
1531
        $rs = Database::query($sql);
1532 View Code Duplication
        if (Database::num_rows($rs) > 0) {
1533
            if ($getCount) {
1534
                $count = Database::fetch_array($rs);
1535
                return $count['count'];
1536
            }
1537
            return Database::store_result($rs, 'ASSOC');
1538
        }
1539
        return false;
1540
    }
1541
1542
    /**
1543
     * Get first user's connection date on the course
1544
     * @param     int         User id
1545
     * @param    int        $courseId
1546
     * @param    int            Session id (optional, default=0)
1547
     * @return    string|bool    Date with format long without day or false if there is no date
1548
     */
1549
    public static function get_first_connection_date_on_the_course(
1550
        $student_id,
1551
        $courseId,
1552
        $session_id = 0,
1553
        $convert_date = true
1554
    ) {
1555
    	$student_id  = intval($student_id);
1556
        $courseId = intval($courseId);
1557
    	$session_id  = intval($session_id);
1558
1559
    	$tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1560
    	$sql = 'SELECT login_course_date
1561
    	        FROM '.$tbl_track_login.'
1562
                WHERE
1563
                    user_id = '.$student_id.' AND
1564
                    c_id = '.$courseId.' AND
1565
                    session_id = '.$session_id.'
1566
                ORDER BY login_course_date ASC LIMIT 0,1';
1567
    	$rs = Database::query($sql);
1568
    	if (Database::num_rows($rs) > 0) {
1569
    		if ($first_login_date = Database::result($rs, 0, 0)) {
1570
    			if ($convert_date) {
1571
    				return api_convert_and_format_date($first_login_date, DATE_FORMAT_SHORT);
1572
    			} else {
1573
    				return $first_login_date;
1574
    			}
1575
    		}
1576
    	}
1577
1578
    	return false;
1579
    }
1580
1581
    /**
1582
     * Get last user's connection date on the course
1583
     * @param     int         User id
1584
     * @param    array        $courseInfo real_id and code are used
1585
     * @param    int            Session id (optional, default=0)
1586
     * @return    string|bool    Date with format long without day or false if there is no date
1587
     */
1588
    public static function get_last_connection_date_on_the_course(
1589
        $student_id,
1590
        $courseInfo,
1591
        $session_id = 0,
1592
        $convert_date = true
1593
    ) {
1594
    	// protect data
1595
    	$student_id  = intval($student_id);
1596
        $courseId = $courseInfo['real_id'];
1597
    	$session_id  = intval($session_id);
1598
1599
    	$tbl_track_e_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
1600
    	$sql = 'SELECT access_date
1601
                FROM '.$tbl_track_e_access.'
1602
                WHERE   access_user_id = '.$student_id.' AND
1603
                        c_id = "'.$courseId.'" AND
1604
                        access_session_id = '.$session_id.'
1605
                ORDER BY access_date DESC
1606
                LIMIT 0,1';
1607
1608
    	$rs = Database::query($sql);
1609
    	if (Database::num_rows($rs) > 0) {
1610
    		if ($last_login_date = Database::result($rs, 0, 0)) {
1611
                if (empty($last_login_date) || $last_login_date == '0000-00-00 00:00:00') {
1612
                    return false;
1613
                }
1614
                //see #5736
1615
                $last_login_date_timestamp = api_strtotime($last_login_date);
1616
    			$now = time();
1617
    			//If the last connection is > than 7 days, the text is red
1618
    			//345600 = 7 days in seconds
1619
    			if ($now - $last_login_date_timestamp > 604800) {
1620
    				if ($convert_date) {
1621
                        $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
1622
                        $icon = api_is_allowed_to_edit() ?
1623
                            '<a href="'.api_get_path(WEB_CODE_PATH).'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'].'" title="'.get_lang('RemindInactiveUser').'">
1624
                              '.Display::return_icon('messagebox_warning.gif').'
1625
                             </a>'
1626
                            : null;
1627
    					return $icon. Display::label($last_login_date, 'warning');
1628
    				} else {
1629
    					return $last_login_date;
1630
    				}
1631
    			} else {
1632
    				if ($convert_date) {
1633
    					return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
1634
    				} else {
1635
    					return $last_login_date;
1636
    				}
1637
    			}
1638
    		}
1639
    	}
1640
    	return false;
1641
    }
1642
1643
    /**
1644
     * Get count of the connections to the course during a specified period
1645
     * @param   int  $courseId
1646
     * @param   int     Session id (optional)
1647
     * @param   int     Datetime from which to collect data (defaults to 0)
1648
     * @param   int     Datetime to which to collect data (defaults to now)
1649
     * @return  int     count connections
1650
     */
1651
    public static function get_course_connections_count($courseId, $session_id = 0, $start = 0, $stop = null)
1652
    {
1653
    	if ($start < 0) {
1654
    		$start = 0;
1655
    	}
1656
    	if (!isset($stop) or ($stop < 0)) {
1657
    		$stop = api_get_utc_datetime();
1658
    	}
1659
1660
        $start = Database::escape_string($start);
1661
        $stop = Database::escape_string($stop);
1662
1663
    	$month_filter = " AND login_course_date > '$start' AND login_course_date < '$stop' ";
1664
1665
        $courseId = intval($courseId);
1666
    	$session_id  = intval($session_id);
1667
    	$count = 0;
1668
1669
    	$tbl_track_e_course_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1670
    	$sql = "SELECT count(*) as count_connections
1671
                FROM $tbl_track_e_course_access
1672
                WHERE
1673
                    c_id = $courseId AND
1674
                    session_id = $session_id
1675
                    $month_filter";
1676
    	$rs = Database::query($sql);
1677
    	if (Database::num_rows($rs)>0) {
1678
    		$row = Database::fetch_object($rs);
1679
    		$count = $row->count_connections;
1680
    	}
1681
1682
    	return $count;
1683
    }
1684
1685
    /**
1686
     * Get count courses per student
1687
     * @param     int        Student id
1688
     * @param    bool    Include sessions (optional)
1689
     * @return  int        count courses
1690
     */
1691
    public static function count_course_per_student($user_id, $include_sessions = true)
1692
    {
1693
    	$user_id = intval($user_id);
1694
    	$tbl_course_rel_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
1695
    	$tbl_session_course_rel_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1696
1697
    	$sql = 'SELECT DISTINCT c_id
1698
                FROM ' . $tbl_course_rel_user . '
1699
                WHERE user_id = ' . $user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
1700
    	$rs = Database::query($sql);
1701
    	$nb_courses = Database::num_rows($rs);
1702
1703
    	if ($include_sessions) {
1704
    		$sql = 'SELECT DISTINCT c_id
1705
                    FROM ' . $tbl_session_course_rel_user . '
1706
                    WHERE user_id = ' . $user_id;
1707
    		$rs = Database::query($sql);
1708
    		$nb_courses += Database::num_rows($rs);
1709
    	}
1710
1711
    	return $nb_courses;
1712
    }
1713
1714
    /**
1715
     * Gets the score average from all tests in a course by student
1716
     *
1717
     * @param $student_id
1718
     * @param $course_code
1719
     * @param int $exercise_id
1720
     * @param null $session_id
1721
     * @param int $active_filter    2 for consider all tests
1722
     *                              1 for active <> -1
1723
     *                              0 for active <> 0
1724
     * @param int $into_lp  1 for all exercises
1725
     *                      0 for without LP
1726
     * @internal param \Student $mixed id
1727
     * @internal param \Course $string code
1728
     * @internal param \Exercise $int id (optional), filtered by exercise
1729
     * @internal param \Session $int id (optional), if param $session_id is null
1730
     * it'll return results including sessions, 0 = session is not filtered
1731
     * @return   string    value (number %) Which represents a round integer about the score average.
1732
     */
1733
    public static function get_avg_student_exercise_score(
1734
        $student_id,
1735
        $course_code,
1736
        $exercise_id = 0,
1737
        $session_id = null,
1738
        $active_filter = 1,
1739
        $into_lp = 0
1740
    ) {
1741
        $course_code = Database::escape_string($course_code);
1742
    	$course_info = api_get_course_info($course_code);
1743
    	if (!empty($course_info)) {
1744
    		// table definition
1745
    		$tbl_course_quiz     = Database::get_course_table(TABLE_QUIZ_TEST);
1746
    		$tbl_stats_exercise  = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1747
1748
    		// Compose a filter based on optional exercise given
1749
    		$condition_quiz = "";
1750
    		if (!empty($exercise_id)) {
1751
    			$exercise_id = intval($exercise_id);
1752
    			$condition_quiz =" AND id = $exercise_id ";
1753
    		}
1754
1755
    		// Compose a filter based on optional session id given
1756
    		$condition_session = "";
1757
    		if (isset($session_id)) {
1758
    			$session_id = intval($session_id);
1759
    			$condition_session = " AND session_id = $session_id ";
1760
    		}
1761
            if ($active_filter == 1) {
1762
                $condition_active = 'AND active <> -1';
1763
            } elseif ($active_filter == 0) {
1764
                $condition_active = 'AND active <> 0';
1765
            } else {
1766
                $condition_active = '';
1767
            }
1768
            $condition_into_lp = '';
1769
            $select_lp_id = '';
1770
            if ($into_lp == 0) {
1771
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
1772
            } else {
1773
                $select_lp_id = ', orig_lp_id as lp_id ';
1774
            }
1775
1776
    		$sql = "SELECT count(id) FROM $tbl_course_quiz
1777
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
1778
    		$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...
1779
1780
    		if (!empty($count_quiz[0]) && !empty($student_id)) {
1781
    			if (is_array($student_id)) {
1782
                    $student_id = array_map('intval', $student_id);
1783
    				$condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
1784
    			} else {
1785
                    $student_id = intval($student_id);
1786
    				$condition_user = " AND exe_user_id = '$student_id' ";
1787
    			}
1788
1789 View Code Duplication
    			if (empty($exercise_id)) {
1790
    				$sql = "SELECT id FROM $tbl_course_quiz
1791
    						WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
1792
                    $result = Database::query($sql);
1793
                    $exercise_list = array();
1794
    				$exercise_id = null;
1795
                    if (Database::num_rows($result)) {
1796
                        while ($row = Database::fetch_array($result)) {
1797
                            $exercise_list[] = $row['id'];
1798
                        }
1799
                    }
1800
                    if (!empty($exercise_list)) {
1801
                        $exercise_id = implode("','",$exercise_list);
1802
                    }
1803
    			}
1804
1805
    			$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...
1806
1807
    			$sql = "SELECT
1808
    			        SUM(exe_result/exe_weighting*100) as avg_score,
1809
    			        COUNT(*) as num_attempts
1810
    			        $select_lp_id
1811
                        FROM $tbl_stats_exercise
1812
                        WHERE
1813
                            exe_exo_id IN ('".$exercise_id."')
1814
                            $condition_user AND
1815
                            status = '' AND
1816
                            c_id = {$course_info['real_id']}
1817
                            $condition_session
1818
                            $condition_into_lp
1819
                        ORDER BY exe_date DESC";
1820
1821
    			$res = Database::query($sql);
1822
    			$row = Database::fetch_array($res);
1823
    			$quiz_avg_score = null;
1824
1825
    			if (!empty($row['avg_score'])) {
1826
    				$quiz_avg_score = round($row['avg_score'],2);
1827
    			}
1828
1829
    			if(!empty($row['num_attempts'])) {
1830
    				$quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
1831
    			}
1832
    			if (is_array($student_id)) {
1833
    				$quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
1834
    			}
1835
                if ($into_lp == 0) {
1836
                    return $quiz_avg_score;
1837
                } else {
1838
                    if (!empty($row['lp_id'])) {
1839
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
1840
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
1841
                        $sql = "SELECT lp.name
1842
                                FROM $tbl_lp as lp, $tbl_course as c
1843
                                WHERE
1844
                                    c.code = '$course_code' AND
1845
                                    lp.id = ".$row['lp_id']." AND
1846
                                    lp.c_id = c.id
1847
                                LIMIT 1;
1848
                        ";
1849
                        $result = Database::query($sql);
1850
                        $row_lp = Database::fetch_row($result);
1851
                        $lp_name = $row_lp[0];
1852
                        return array($quiz_avg_score, $lp_name);
1853
                    } else {
1854
                        return array($quiz_avg_score, null);
1855
                    }
1856
                }
1857
    		}
1858
    	}
1859
    	return null;
1860
    }
1861
1862
    /**
1863
     * Get count student's exercise COMPLETED attempts
1864
     * @param int $student_id
1865
     * @param int $courseId
1866
     * @param int $exercise_id
1867
     * @param int $lp_id
1868
     * @param int $lp_item_id
1869
     * @param int $session_id
1870
     * @param int $find_all_lp  0 = just LP specified
1871
     *                          1 = LP specified or whitout LP,
1872
     *                          2 = all rows
1873
     * @internal param \Student $int id
1874
     * @internal param \Course $string code
1875
     * @internal param \Exercise $int id
1876
     * @internal param \Learning $int path id (optional),
1877
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
1878
     * @internal param \Learning $int path item id (optional),
1879
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
1880
     * @return  int     count of attempts
1881
     */
1882
    public static function count_student_exercise_attempts(
1883
        $student_id,
1884
        $courseId,
1885
        $exercise_id,
1886
        $lp_id = 0,
1887
        $lp_item_id = 0,
1888
        $session_id = 0,
1889
        $find_all_lp = 0
1890
    ) {
1891
        $courseId = intval($courseId);
1892
    	$student_id  = intval($student_id);
1893
    	$exercise_id = intval($exercise_id);
1894
    	$session_id  = intval($session_id);
1895
1896
    	$lp_id = intval($lp_id);
1897
        $lp_item_id = intval($lp_item_id);
1898
    	$tbl_stats_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1899
1900
    	$sql = "SELECT COUNT(ex.exe_id) as essais FROM $tbl_stats_exercises AS ex
1901
                WHERE  ex.c_id = $courseId
1902
                AND ex.exe_exo_id = $exercise_id
1903
                AND status = ''
1904
                AND exe_user_id= $student_id
1905
                AND session_id = $session_id ";
1906
1907
        if ($find_all_lp == 1) {
1908
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
1909
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
1910
        } elseif ($find_all_lp == 0) {
1911
            $sql .= "AND orig_lp_id = $lp_id
1912
                AND orig_lp_item_id = $lp_item_id";
1913
        }
1914
1915
    	$rs = Database::query($sql);
1916
    	$row = Database::fetch_row($rs);
1917
    	$count_attempts = $row[0];
1918
1919
    	return $count_attempts;
1920
    }
1921
1922
    /**
1923
     * Get count student's exercise progress
1924
     *
1925
     * @param array  $exercise_list
1926
     * @param int    $user_id
1927
     * @param int    $courseId
1928
     * @param int    $session_id
1929
    */
1930
    public static function get_exercise_student_progress($exercise_list, $user_id, $courseId, $session_id)
1931
    {
1932
        $courseId = intval($courseId);
1933
        $user_id = intval($user_id);
1934
        $session_id = intval($session_id);
1935
1936
        if (empty($exercise_list)) {
1937
            return '0%';
1938
        }
1939
        $tbl_stats_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1940
        $exercise_list = array_keys($exercise_list);
1941
        $exercise_list = array_map('intval', $exercise_list);
1942
1943
        $exercise_list_imploded = implode("' ,'", $exercise_list);
1944
1945
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
1946
                FROM $tbl_stats_exercises AS ex
1947
                WHERE
1948
                    ex.c_id = $courseId AND
1949
                    ex.session_id  = $session_id AND
1950
                    ex.exe_user_id = $user_id AND
1951
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
1952
1953
        $rs = Database::query($sql);
1954
        $count = 0;
1955
        if ($rs) {
1956
            $row = Database::fetch_row($rs);
1957
            $count = $row[0];
1958
        }
1959
        $count = ($count != 0 ) ? 100*round(intval($count)/count($exercise_list), 2) .'%' : '0%';
1960
        return $count;
1961
    }
1962
1963
    /**
1964
     * @param array $exercise_list
1965
     * @param int $user_id
1966
     * @param int $courseId
1967
     * @param int $session_id
1968
     * @return string
1969
     */
1970 View Code Duplication
    public static function get_exercise_student_average_best_attempt($exercise_list, $user_id, $courseId, $session_id)
1971
    {
1972
        $result = 0;
1973
        if (!empty($exercise_list)) {
1974
            foreach ($exercise_list as $exercise_data) {
1975
                $exercise_id = $exercise_data['id'];
1976
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
1977
                    $user_id,
1978
                    $exercise_id,
1979
                    $courseId,
1980
                    $session_id
1981
                );
1982
1983
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
1984
                    $result += $best_attempt['exe_result']/$best_attempt['exe_weighting'];
1985
                }
1986
            }
1987
            $result = $result / count($exercise_list);
1988
            $result = round($result, 2) * 100;
1989
        }
1990
1991
        return $result.'%';
1992
    }
1993
1994
    /**
1995
     * get teacher progress by course and session
1996
     * @param int course id
1997
     * @param int session id
1998
     * @return array
1999
     */
2000
    static function get_teachers_progress_by_course($courseId, $sessionId)
2001
    {
2002
        $course = api_get_course_info_by_id($courseId);
2003
        $sessionId = intval($sessionId);
2004
        $courseId = intval($courseId);
2005
2006
        $sessionCourseUserTable = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2007
        $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
2008
2009
        //get teachers
2010
        $sql = "SELECT scu.session_id, scu.user_id, s.name
2011
                FROM $sessionCourseUserTable scu, $sessionTable s
2012
                WHERE
2013
                    scu.session_id = s.id
2014
                    AND scu.status = 2
2015
                    AND scu.visibility = 1
2016
                    AND scu.c_id = '%s'
2017
                    AND scu.session_id = %s";
2018
        $query = sprintf($sql, intval($courseId), $sessionId);
2019
        $rs = Database::query($query);
2020
        $teachers = array();
2021
        while ($teacher = Database::fetch_array($rs,'ASSOC')) {
2022
            $teachers[] = $teacher;
2023
        }
2024
        $data = array();
2025
        foreach ($teachers as $teacher) {
2026
            //total documents added
2027
            $sql = "SELECT count(*) as total
2028
                    FROM c_item_property
2029
                    WHERE lastedit_type = 'DocumentAdded'
2030
                    AND c_id = %s
2031
                    AND insert_user_id = %s
2032
                    AND session_id = %s";
2033
            $query = sprintf($sql,
2034
                $courseId,
2035
                $teacher['user_id'],
2036
                $teacher['session_id']
2037
            );
2038
2039
            $rs = Database::query($query);
2040
            $totalDocuments = 0;
2041
            if ($rs) {
2042
                $row = Database::fetch_row($rs);
2043
                $totalDocuments = $row[0];
2044
            }
2045
            //total links added
2046
            $sql = "SELECT count(*) as total
2047
                    FROM c_item_property
2048
                    WHERE lastedit_type = 'LinkAdded'
2049
                    AND c_id = %s
2050
                    AND insert_user_id = %s
2051
                    AND session_id = %s";
2052
            $query = sprintf($sql,
2053
                $courseId,
2054
                $teacher['user_id'],
2055
                $teacher['session_id']
2056
            );
2057
            $rs = Database::query($query);
2058
2059
            $totalLinks = 0;
2060
            if ($rs) {
2061
                $row = Database::fetch_row($rs);
2062
                $totalLinks = $row[0];
2063
            }
2064
            //total forums added
2065
            $sql = "SELECT count(*) as total
2066
                    FROM c_item_property
2067
                    WHERE lastedit_type = 'ForumthreadVisible'
2068
                    AND c_id = %s
2069
                    AND insert_user_id = %s
2070
                    AND session_id = %s";
2071
            $query = sprintf($sql,
2072
                $courseId,
2073
                $teacher['user_id'],
2074
                $teacher['session_id']
2075
            );
2076
            $rs = Database::query($query);
2077
2078
            $totalForums = 0;
2079
            if ($rs) {
2080
                $row = Database::fetch_row($rs);
2081
                $totalForums = $row[0];
2082
            }
2083
            //total wikis added
2084
            $sql = "SELECT COUNT(DISTINCT(ref)) as total
2085
                    FROM c_item_property
2086
                    WHERE lastedit_type = 'WikiAdded'
2087
                    AND c_id = %s
2088
                    AND insert_user_id = %s
2089
                    AND session_id = %s";
2090
            $query = sprintf($sql,
2091
                $courseId,
2092
                $teacher['user_id'],
2093
                $teacher['session_id']
2094
            );
2095
            $rs = Database::query($query);
2096
2097
            $totalWikis = 0;
2098
            if ($rs) {
2099
                $row = Database::fetch_row($rs);
2100
                $totalWikis = $row[0];
2101
            }
2102
            //total works added
2103
            $sql = "SELECT COUNT(*) as total
2104
                    FROM c_item_property
2105
                    WHERE lastedit_type = 'DirectoryCreated'
2106
                    AND tool = 'work'
2107
                    AND c_id = %s
2108
                    AND insert_user_id = %s
2109
                    AND session_id = %s";
2110
            $query = sprintf($sql,
2111
                $courseId,
2112
                $teacher['user_id'],
2113
                $teacher['session_id']
2114
            );
2115
            $rs = Database::query($query);
2116
2117
            $totalWorks = 0;
2118
            if ($rs) {
2119
                $row = Database::fetch_row($rs);
2120
                $totalWorks = $row[0];
2121
            }
2122
            //total announcements added
2123
            $sql = "SELECT COUNT(*) as total
2124
                    FROM c_item_property
2125
                    WHERE lastedit_type = 'AnnouncementAdded'
2126
                    AND c_id = %s
2127
                    AND insert_user_id = %s
2128
                    AND session_id = %s";
2129
            $query = sprintf($sql,
2130
                $courseId,
2131
                $teacher['user_id'],
2132
                $teacher['session_id']
2133
            );
2134
            $rs = Database::query($query);
2135
2136
            $totalAnnouncements = 0;
2137
            if ($rs) {
2138
                $row = Database::fetch_row($rs);
2139
                $totalAnnouncements = $row[0];
2140
            }
2141
            $tutor = api_get_user_info($teacher['user_id']);
2142
            $data[] = array(
2143
                'course' => $course['title'],
2144
                'session' => $teacher['name'],
2145
                'tutor' => $tutor['username'] . ' - ' . $tutor['lastname'] . ' ' . $tutor['firstname'],
2146
                'documents' => $totalDocuments,
2147
                'links' => $totalLinks,
2148
                'forums' => $totalForums,
2149
                'works' => $totalWorks,
2150
                'wikis' => $totalWikis,
2151
                'announcements' => $totalAnnouncements,
2152
            );
2153
        }
2154
2155
        return $data;
2156
    }
2157
2158
    /**
2159
     * Returns the average student progress in the learning paths of the given
2160
     * course.
2161
     * @param int|array $studentId
2162
     * @param string    $courseCode
2163
     * @param array     $lpIdList Limit average to listed lp ids
2164
     * @param int       $sessionId     Session id (optional),
2165
     * if parameter $session_id is null(default) it'll return results including
2166
     * sessions, 0 = session is not filtered
2167
     * @param bool      $returnArray Will return an array of the type:
2168
     * [sum_of_progresses, number] if it is set to true
2169
     * @param boolean $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2170
     * @return double   Average progress of the user in this course
2171
     */
2172
    public static function get_avg_student_progress(
2173
        $studentId,
2174
        $courseCode = null,
2175
        $lpIdList = array(),
2176
        $sessionId = null,
2177
        $returnArray = false,
2178
        $onlySeriousGame = false
2179
    ) {
2180
        // If there is at least one learning path and one student.
2181
        if (empty($studentId)) {
2182
            return false;
2183
        }
2184
2185
        $sessionId = intval($sessionId);
2186
        $courseInfo = api_get_course_info($courseCode);
2187
2188
        if (empty($courseInfo)) {
2189
            return false;
2190
        }
2191
2192
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
2193
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2194
        $lpConditions = [];
2195
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2196
2197
        if ($sessionId > 0) {
2198
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2199
        } else {
2200
            $lpConditions['AND session_id = ?'] = $sessionId;
2201
        }
2202
2203
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2204
            $placeHolders = [];
2205
            for ($i = 0; $i < count($lpIdList); $i++) {
2206
                $placeHolders[] = '?';
2207
            }
2208
            $lpConditions['AND id IN(' . implode(', ', $placeHolders) . ') '] = $lpIdList;
2209
        }
2210
2211
        if ($onlySeriousGame) {
2212
            $lpConditions['AND seriousgame_mode = ? '] = true;
2213
        }
2214
2215
        $resultLP = Database::select(
2216
            'id',
2217
            $lPTable,
2218
            ['where' => $lpConditions]
2219
        );
2220
        $filteredLP = array_keys($resultLP);
2221
2222
        if (empty($filteredLP)) {
2223
            return false;
2224
        }
2225
2226
        $conditions = [
2227
            " c_id = {$courseInfo['real_id']} ",
2228
            " lp_view.lp_id IN(" . implode(', ', $filteredLP) . ") "
2229
        ];
2230
2231
        $groupBy = 'GROUP BY lp_id';
2232
2233
        if (is_array($studentId)) {
2234
            $studentId = array_map('intval', $studentId);
2235
            $conditions[] = " lp_view.user_id IN (" . implode(',', $studentId) . ")  ";
2236
2237
2238
        } else {
2239
            $studentId = intval($studentId);
2240
            $conditions[] = " lp_view.user_id = '$studentId' ";
2241
2242
            if (empty($lpIdList)) {
2243
                $lpList = new LearnpathList($studentId, $courseCode, $sessionId);
2244
                $lpList = $lpList->get_flat_list();
2245
                if (!empty($lpList)) {
2246
                    /** @var  $lp */
2247
                    foreach ($lpList as $lpId => $lp) {
2248
                        $lpIdList[] = $lpId;
2249
                    }
2250
                }
2251
            }
2252
        }
2253
2254
        if (!empty($sessionId)) {
2255
            $conditions[] = " session_id = $sessionId ";
2256
        }
2257
2258
        $conditionToString = implode('AND', $conditions);
2259
        // Get last view for each student (in case of multi-attempt)
2260
        // Also filter on LPs of this session
2261
        /*$sql = " SELECT
2262
                    MAX(view_count),
2263
                    AVG(progress) average,
2264
                    SUM(progress) sum_progress,
2265
                    count(progress) count_progress
2266
                FROM $lpViewTable lp_view
2267
                WHERE
2268
                  $conditionToString
2269
                $groupBy";*/
2270
2271
        $sql = "
2272
            SELECT lp_id, view_count, progress FROM $lpViewTable lp_view
2273
            WHERE
2274
                $conditionToString
2275
                $groupBy
2276
            ORDER BY view_count DESC
2277
        ";
2278
2279
        $result = Database::query($sql);
2280
2281
        $progress = array();
2282
        $viewCount = array();
2283 View Code Duplication
        while ($row = Database::fetch_array($result, 'ASSOC')) {
2284
            if (!isset($viewCount[$row['lp_id']])) {
2285
                $progress[$row['lp_id']] = $row['progress'];
2286
            }
2287
            $viewCount[$row['lp_id']] = $row['view_count'];
2288
        }
2289
2290
        // Fill with lp ids
2291
        if (!empty($lpIdList)) {
2292
            foreach ($lpIdList as $lpId) {
2293
                if (!isset($progress[$lpId])) {
2294
                    $progress[$lpId] = 0;
2295
                }
2296
            }
2297
        }
2298
2299
        if (!empty($progress)) {
2300
            $sum = array_sum($progress);
2301
            $average = $sum / count($progress);
2302
        } else {
2303
            $average = 0;
2304
            $sum = 0;
2305
        }
2306
2307
        if ($returnArray) {
2308
            return [
2309
                $sum,
2310
                count($progress)
2311
            ];
2312
        }
2313
2314
        return round($average, 1);
2315
    }
2316
2317
    /**
2318
     * This function gets:
2319
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2320
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2321
     * 3. And finally it will return the average between 1. and 2.
2322
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2323
     * This function does not take the results of a Test out of a LP
2324
     *
2325
     * @param   mixed       $student_id Array of user ids or an user id
2326
     * @param   string      $course_code
2327
     * @param   array       $lp_ids List of LP ids
2328
     * @param   int         $session_id Session id (optional),
2329
     * if param $session_id is null(default) it'll return results
2330
     * including sessions, 0 = session is not filtered
2331
     * @param   bool        $return_array Returns an array of the
2332
     * type [sum_score, num_score] if set to true
2333
     * @param   bool        $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2334
     *
2335
     * @return  string      Value (number %) Which represents a round integer explain in got in 3.
2336
     */
2337
    public static function get_avg_student_score(
2338
        $student_id,
2339
        $course_code,
2340
        $lp_ids = array(),
2341
        $session_id = null,
2342
        $return_array = false,
2343
        $get_only_latest_attempt_results = false
2344
    ) {
2345
        $debug = false;
2346
        if (empty($lp_ids)) {
2347
            $debug = false;
2348
        }
2349
2350
        if ($debug) echo '<h1>Tracking::get_avg_student_score</h1>';
2351
        $tbl_stats_exercices = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2352
        $tbl_stats_attempts = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2353
2354
        $course = api_get_course_info($course_code);
2355
2356
        if (!empty($course)) {
2357
2358
            // Get course tables names
2359
            $tbl_quiz_questions = Database :: get_course_table(TABLE_QUIZ_QUESTION);
2360
            $lp_table = Database:: get_course_table(TABLE_LP_MAIN);
2361
            $lp_item_table = Database:: get_course_table(TABLE_LP_ITEM);
2362
            $lp_view_table = Database:: get_course_table(TABLE_LP_VIEW);
2363
            $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2364
2365
            $course_id = $course['real_id'];
2366
2367
            // Compose a filter based on optional learning paths list given
2368
            $condition_lp = "";
2369 View Code Duplication
            if (count($lp_ids) > 0) {
2370
                $condition_lp =" AND id IN(".implode(',',$lp_ids).") ";
2371
            }
2372
2373
            // Compose a filter based on optional session id
2374
            $session_id = intval($session_id);
2375
            if (count($lp_ids) > 0) {
2376
                $condition_session = " AND session_id = $session_id ";
2377
            } else {
2378
                $condition_session = " WHERE session_id = $session_id ";
2379
            }
2380
2381
            // Check the real number of LPs corresponding to the filter in the
2382
            // database (and if no list was given, get them all)
2383
2384
            if (empty($session_id)) {
2385
                $sql = "SELECT DISTINCT(id), use_max_score
2386
                        FROM $lp_table
2387
                        WHERE c_id = $course_id AND (session_id = 0 OR session_id IS NULL ) $condition_lp ";
2388
            } else {
2389
                $sql = "SELECT DISTINCT(id), use_max_score
2390
                        FROM $lp_table
2391
                        WHERE c_id = $course_id $condition_lp ";
2392
            }
2393
2394
            $res_row_lp   = Database::query($sql);
2395
            $count_row_lp = Database::num_rows($res_row_lp);
2396
2397
            $lp_list = $use_max_score = array();
2398
            while ($row_lp = Database::fetch_array($res_row_lp)) {
2399
                $lp_list[] = $row_lp['id'];
2400
                $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
2401
            }
2402
2403
            if ($debug) {
2404
                echo 'Use max score or not list '; var_dump($use_max_score);
2405
            }
2406
2407
            // prepare filter on users
2408 View Code Duplication
            if (is_array($student_id)) {
2409
                array_walk($student_id, 'intval');
2410
                $condition_user1 =" AND user_id IN (".implode(',', $student_id).") ";
2411
            } else {
2412
                $condition_user1 =" AND user_id = $student_id ";
2413
            }
2414
2415
            if ($count_row_lp > 0 && !empty($student_id)) {
2416
2417
                // Getting latest LP result for a student
2418
                //@todo problem when a  course have more than 1500 users
2419
                $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
2420
                        FROM $lp_view_table
2421
                        WHERE
2422
                            c_id = $course_id AND
2423
                            lp_id IN (".implode(',', $lp_list).")
2424
                            $condition_user1 AND
2425
                            session_id = $session_id
2426
                        GROUP BY lp_id, user_id";
2427
                if ($debug) echo $sql;
2428
2429
                $rs_last_lp_view_id = Database::query($sql);
2430
2431
                $global_result = 0;
2432
2433
                if (Database::num_rows($rs_last_lp_view_id) > 0) {
2434
                    // Cycle through each line of the results (grouped by lp_id, user_id)
2435
                    while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2436
                        $count_items = 0;
2437
                        $lp_partial_total = 0;
2438
2439
                        $list = array();
2440
                        $lp_view_id = $row_lp_view['id'];
2441
                        $lp_id      = $row_lp_view['lp_id'];
2442
                        $user_id    = $row_lp_view['user_id'];
2443
                        if ($debug) echo '<h2>LP id '.$lp_id.'</h2>';
2444
2445
                        if ($get_only_latest_attempt_results) {
2446
                            //Getting lp_items done by the user
2447
                            $sql = "SELECT DISTINCT lp_item_id
2448
                                    FROM $lp_item_view_table
2449
                                    WHERE
2450
                                        c_id = $course_id AND
2451
                                        lp_view_id = $lp_view_id
2452
                                    ORDER BY lp_item_id";
2453
                            $res_lp_item = Database::query($sql);
2454
2455
                            while ($row_lp_item = Database::fetch_array($res_lp_item,'ASSOC')) {
2456
                                $my_lp_item_id = $row_lp_item['lp_item_id'];
2457
2458
                                // Getting the most recent attempt
2459
                                $sql = "SELECT  lp_iv.id as lp_item_view_id,
2460
                                                lp_iv.score as score,
2461
                                                lp_i.max_score,
2462
                                                lp_iv.max_score as max_score_item_view,
2463
                                                lp_i.path,
2464
                                                lp_i.item_type,
2465
                                                lp_i.id as iid
2466
                                        FROM $lp_item_view_table as lp_iv
2467
                                            INNER JOIN $lp_item_table as lp_i
2468
                                            ON  lp_i.id = lp_iv.lp_item_id AND
2469
                                                lp_iv.c_id = $course_id AND
2470
                                                lp_i.c_id  = $course_id AND
2471
                                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2472
                                        WHERE
2473
                                            lp_item_id = $my_lp_item_id AND
2474
                                            lp_view_id = $lp_view_id
2475
                                        ORDER BY view_count DESC
2476
                                        LIMIT 1";
2477
                                $res_lp_item_result = Database::query($sql);
2478
                                while ($row_max_score = Database::fetch_array($res_lp_item_result,'ASSOC')) {
2479
                                    $list[]= $row_max_score;
2480
                                }
2481
                            }
2482
                        } else {
2483
                            // For the currently analysed view, get the score and
2484
                            // max_score of each item if it is a sco or a TOOL_QUIZ
2485
                            $sql = "SELECT
2486
                                        lp_iv.id as lp_item_view_id,
2487
                                        lp_iv.score as score,
2488
                                        lp_i.max_score,
2489
                                        lp_iv.max_score as max_score_item_view,
2490
                                        lp_i.path,
2491
                                        lp_i.item_type,
2492
                                        lp_i.id as iid
2493
                                      FROM $lp_item_view_table as lp_iv
2494
                                      INNER JOIN $lp_item_table as lp_i
2495
                                      ON lp_i.id = lp_iv.lp_item_id AND
2496
                                         lp_iv.c_id = $course_id AND
2497
                                         lp_i.c_id  = $course_id AND
2498
                                         (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2499
                                      WHERE lp_view_id = $lp_view_id ";
2500
                            if ($debug) echo $sql.'<br />';
2501
                            $res_max_score = Database::query($sql);
2502
2503
                            while ($row_max_score = Database::fetch_array($res_max_score,'ASSOC')) {
2504
                                $list[]= $row_max_score;
2505
                            }
2506
                        }
2507
2508
                        // Go through each scorable element of this view
2509
2510
                        $score_of_scorm_calculate = 0;
2511
2512
                        foreach ($list as $row_max_score) {
2513
                            // Came from the original lp_item
2514
                            $max_score = $row_max_score['max_score'];
2515
                            // Came from the lp_item_view
2516
                            $max_score_item_view = $row_max_score['max_score_item_view'];
2517
                            $score = $row_max_score['score'];
2518
2519
                            if ($debug) echo '<h3>Item Type: ' .$row_max_score['item_type'].'</h3>';
2520
2521
                            if ($row_max_score['item_type'] == 'sco') {
2522
                                /* Check if it is sco (easier to get max_score)
2523
                                   when there's no max score, we assume 100 as the max score,
2524
                                   as the SCORM 1.2 says that the value should always be between 0 and 100.
2525
                                */
2526
                                if ($max_score == 0 || is_null($max_score) || $max_score == '') {
2527
                                    // Chamilo style
2528
                                    if ($use_max_score[$lp_id]) {
2529
                                        $max_score = 100;
2530
                                    } else {
2531
                                        // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
2532
                                        $max_score = $max_score_item_view;
2533
                                    }
2534
                                }
2535
                                // Avoid division by zero errors
2536
                                if (!empty($max_score)) {
2537
                                    $lp_partial_total += $score/$max_score;
2538
                                }
2539 View Code Duplication
                                if ($debug) echo '<b>$lp_partial_total, $score, $max_score '.$lp_partial_total.' '.$score.' '.$max_score.'</b><br />';
2540
                            } else {
2541
                                // Case of a TOOL_QUIZ element
2542
                                $item_id = $row_max_score['iid'];
2543
                                $item_path = $row_max_score['path'];
2544
                                $lp_item_view_id = $row_max_score['lp_item_view_id'];
2545
2546
                                // Get last attempt to this exercise through
2547
                                // the current lp for the current user
2548
                                $sql = "SELECT exe_id
2549
                                        FROM $tbl_stats_exercices
2550
                                        WHERE
2551
                                            exe_exo_id           = '$item_path' AND
2552
                                            exe_user_id          = $user_id AND
2553
                                            orig_lp_item_id      = $item_id AND
2554
                                            orig_lp_item_view_id = $lp_item_view_id AND
2555
                                            c_id                 = $course_id AND
2556
                                            session_id           = $session_id AND
2557
                                            status = ''
2558
                                        ORDER BY exe_date DESC
2559
                                        LIMIT 1";
2560
2561
                                if ($debug) echo $sql .'<br />';
2562
                                $result_last_attempt = Database::query($sql);
2563
                                $num = Database :: num_rows($result_last_attempt);
2564
                                if ($num > 0 ) {
2565
                                    $id_last_attempt = Database :: result($result_last_attempt, 0, 0);
2566
                                    if ($debug) echo $id_last_attempt.'<br />';
2567
2568
                                    // Within the last attempt number tracking, get the sum of
2569
                                    // the max_scores of all questions that it was
2570
                                    // made of (we need to make this call dynamic because of random questions selection)
2571
                                    $sql = "SELECT SUM(t.ponderation) as maxscore FROM
2572
                                            (
2573
                                                SELECT DISTINCT
2574
                                                    question_id,
2575
                                                    marks,
2576
                                                    ponderation
2577
                                                FROM $tbl_stats_attempts AS at
2578
                                                INNER JOIN $tbl_quiz_questions AS q
2579
                                                ON (q.id = at.question_id)
2580
                                                WHERE
2581
                                                    exe_id ='$id_last_attempt' AND
2582
                                                    q.c_id = $course_id
2583
                                            )
2584
                                            AS t";
2585
                                    if ($debug) echo '$sql: '.$sql.' <br />';
2586
                                    $res_max_score_bis = Database::query($sql);
2587
                                    $row_max_score_bis = Database::fetch_array($res_max_score_bis);
2588
2589
                                    if (!empty($row_max_score_bis['maxscore'])) {
2590
                                        $max_score = $row_max_score_bis['maxscore'];
2591
                                    }
2592
                                    if (!empty($max_score) && floatval($max_score) > 0) {
2593
                                        $lp_partial_total += $score/$max_score;
2594
                                    }
2595 View Code Duplication
                                    if ($debug) echo '$lp_partial_total, $score, $max_score <b>'.$lp_partial_total.' '.$score.' '.$max_score.'</b><br />';
2596
                                }
2597
                            }
2598
2599
                            if (in_array($row_max_score['item_type'], array('quiz','sco'))) {
2600
                                // Normal way
2601
                                if ($use_max_score[$lp_id]) {
2602
                                    $count_items++;
2603
                                } else {
2604
                                    if ($max_score != '') {
2605
                                        $count_items++;
2606
                                    }
2607
                                }
2608
                                if ($debug) echo '$count_items: '.$count_items;
2609
                            }
2610
                        } //end for
2611
2612
                        $score_of_scorm_calculate += $count_items ? (($lp_partial_total / $count_items) * 100) : 0;
2613
2614
                        if ($debug) echo '<h3>$count_items '.$count_items.'</h3>';
2615
                        if ($debug) echo '<h3>$score_of_scorm_calculate '.$score_of_scorm_calculate.'</h3>';
2616
2617
                        $global_result += $score_of_scorm_calculate;
2618
                        if ($debug) echo '<h3>$global_result '.$global_result.'</h3>';
2619
                    } // end while
2620
                }
2621
2622
                $lp_with_quiz = 0;
2623
                foreach ($lp_list as $lp_id) {
2624
                    // Check if LP have a score we assume that all SCO have an score
2625
                    $sql = "SELECT count(id) as count
2626
                            FROM $lp_item_table
2627
                            WHERE
2628
                                c_id = $course_id AND
2629
                                (item_type = 'quiz' OR item_type = 'sco') AND
2630
                                lp_id = ".$lp_id;
2631
                    if ($debug) echo $sql;
2632
                    $result_have_quiz = Database::query($sql);
2633
2634
                    if (Database::num_rows($result_have_quiz) > 0 ) {
2635
                        $row = Database::fetch_array($result_have_quiz,'ASSOC');
2636
                        if (is_numeric($row['count']) && $row['count'] != 0) {
2637
                            $lp_with_quiz++;
2638
                        }
2639
                    }
2640
                }
2641
2642
                if ($debug) echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
2643
                if ($debug) echo '<h3>Final return</h3>';
2644
2645
                if ($lp_with_quiz != 0) {
2646
                    if (!$return_array) {
2647
                        $score_of_scorm_calculate = round(($global_result/$lp_with_quiz),2);
2648
                        if ($debug) var_dump($score_of_scorm_calculate);
2649
                        if (empty($lp_ids)) {
2650
                            if ($debug) echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
2651
                        }
2652
                        return $score_of_scorm_calculate;
2653
                    } else {
2654
                        if ($debug) var_dump($global_result, $lp_with_quiz);
2655
                        return array($global_result, $lp_with_quiz);
2656
                    }
2657
                } else {
2658
2659
                    return '-';
2660
                }
2661
            }
2662
        }
2663
2664
        return null;
2665
    }
2666
2667
    /**
2668
     * This function gets:
2669
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2670
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2671
     * 3. And finally it will return the average between 1. and 2.
2672
     * This function does not take the results of a Test out of a LP
2673
     *
2674
     * @param   int|array   Array of user ids or an user id
2675
     * @param   string      Course code
2676
     * @param   array       List of LP ids
2677
     * @param   int         Session id (optional), if param $session_id is null(default)
2678
     * it'll return results including sessions, 0 = session is not filtered
2679
     * @param   bool        Returns an array of the type [sum_score, num_score] if set to true
2680
     * @param   bool        get only the latest attempts or ALL attempts
2681
     * @return  string      Value (number %) Which represents a round integer explain in got in 3.
2682
     */
2683
    public static function getAverageStudentScore(
2684
        $student_id,
2685
        $course_code = null,
2686
        $lp_ids = array(),
2687
        $session_id = null
2688
    ) {
2689
        if (empty($student_id)) {
2690
            return 0;
2691
        }
2692
2693
        $conditions = array();
2694
2695
        if (!empty($course_code)) {
2696
            $course = api_get_course_info($course_code);
2697
            $courseId = $course['real_id'];
2698
            $conditions[] = " c_id = $courseId";
2699
        }
2700
2701
        // Get course tables names
2702
        $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
2703
        $lp_item_table = Database :: get_course_table(TABLE_LP_ITEM);
2704
        $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
2705
        $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2706
2707
        // Compose a filter based on optional learning paths list given
2708
2709
        if (!empty($lp_ids) && count($lp_ids) > 0) {
2710
            $conditions[] = " id IN(".implode(',', $lp_ids).") ";
2711
        }
2712
2713
        // Compose a filter based on optional session id
2714
        $session_id = intval($session_id);
2715
        if (!empty($session_id)) {
2716
            $conditions[] = " session_id = $session_id ";
2717
        }
2718
2719 View Code Duplication
        if (is_array($student_id)) {
2720
            array_walk($student_id, 'intval');
2721
            $conditions[] =" lp_view.user_id IN (".implode(',', $student_id).") ";
2722
        } else {
2723
            $conditions[] =" lp_view.user_id = $student_id ";
2724
        }
2725
2726
        $conditionsToString = implode('AND ', $conditions);
2727
        $sql = "SELECT  SUM(lp_iv.score) sum_score,
2728
                        SUM(lp_i.max_score) sum_max_score,
2729
                        count(*) as count
2730
                FROM $lp_table as lp
2731
                INNER JOIN $lp_item_table as lp_i
2732
                ON lp.id = lp_id AND lp.c_id = lp_i.c_id
2733
                INNER JOIN $lp_view_table as lp_view
2734
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
2735
                INNER JOIN $lp_item_view_table as lp_iv
2736
                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
2737
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
2738
                $conditionsToString
2739
                ";
2740
        $result = Database::query($sql);
2741
        $row = Database::fetch_array($result, 'ASSOC');
2742
2743
        if (empty($row['sum_max_score'])) {
2744
            return 0;
2745
        }
2746
2747
        return ($row['sum_score'] / $row['sum_max_score'])*100;
2748
2749
    }
2750
2751
    /**
2752
     * This function gets time spent in learning path for a student inside a course
2753
     * @param     int|array    Student id(s)
2754
     * @param     string         Course code
2755
     * @param     array         Limit average to listed lp ids
2756
     * @param     int            Session id (optional), if param $session_id is
2757
     * null(default) it'll return results including sessions, 0 = session is not filtered
2758
     * @return     int         Total time
2759
     */
2760
    public static function get_time_spent_in_lp($student_id, $course_code, $lp_ids = array(), $session_id = null)
2761
    {
2762
        $course = api_get_course_info($course_code);
2763
        $student_id = intval($student_id);
2764
        $total_time = 0;
2765
2766
        if (!empty($course)) {
2767
2768
            $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
2769
            $t_lpv = Database :: get_course_table(TABLE_LP_VIEW);
2770
            $t_lpiv = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2771
2772
            $course_id = $course['real_id'];
2773
2774
            // Compose a filter based on optional learning paths list given
2775
            $condition_lp = "";
2776 View Code Duplication
            if (count($lp_ids) > 0) {
2777
                $condition_lp =" AND id IN(".implode(',',$lp_ids).") ";
2778
            }
2779
2780
            // Compose a filter based on optional session id
2781
            $session_id = intval($session_id);
2782
            $condition_session = "";
2783
            if (isset($session_id)) {
2784
                $condition_session = " AND session_id = $session_id ";
2785
            }
2786
2787
            // Check the real number of LPs corresponding to the filter in the
2788
            // database (and if no list was given, get them all)
2789
            $sql = "SELECT DISTINCT(id) FROM $lp_table WHERE c_id = $course_id $condition_lp";
2790
            $res_row_lp = Database::query($sql);
2791
            $count_row_lp = Database::num_rows($res_row_lp);
2792
2793
            // calculates time
2794
            if ($count_row_lp > 0) {
2795 View Code Duplication
                while ($row_lp = Database::fetch_array($res_row_lp)) {
2796
                    $lp_id = intval($row_lp['id']);
2797
                    $sql = 'SELECT SUM(total_time)
2798
                            FROM '.$t_lpiv.' AS item_view
2799
                            INNER JOIN '.$t_lpv.' AS view
2800
                            ON (
2801
                                item_view.lp_view_id = view.id AND 
2802
                                item_view.c_id = view.c_id
2803
                            )
2804
                            WHERE
2805
                                item_view.c_id = '.$course_id.' AND
2806
                                view.c_id = '.$course_id.' AND
2807
                                view.lp_id = '.$lp_id.' AND 
2808
                                view.user_id = '.$student_id.' AND
2809
                                session_id = '.$session_id;
2810
2811
                    $rs = Database::query($sql);
2812
                    if (Database :: num_rows($rs) > 0) {
2813
                        $total_time += Database :: result($rs, 0, 0);
2814
                    }
2815
                }
2816
            }
2817
        }
2818
2819
        return $total_time;
2820
    }
2821
2822
    /**
2823
     * This function gets last connection time to one learning path
2824
     * @param     int|array    Student id(s)
2825
     * @param     string         Course code
2826
     * @param     int         Learning path id
2827
     * @return     int         Total time
2828
     */
2829
    public static function get_last_connection_time_in_lp($student_id, $course_code, $lp_id, $session_id = 0)
2830
    {
2831
        $course = CourseManager :: get_course_information($course_code);
2832
        $student_id = intval($student_id);
2833
        $lp_id = intval($lp_id);
2834
        $last_time = 0;
2835
        $session_id = intval($session_id);
2836
2837
        if (!empty($course)) {
2838
2839
            $course_id	 = $course['real_id'];
2840
2841
            $lp_table    = Database :: get_course_table(TABLE_LP_MAIN);
2842
            $t_lpv       = Database :: get_course_table(TABLE_LP_VIEW);
2843
            $t_lpiv      = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
2844
2845
            // Check the real number of LPs corresponding to the filter in the
2846
            // database (and if no list was given, get them all)
2847
            $res_row_lp = Database::query("SELECT id FROM $lp_table WHERE c_id = $course_id AND id = $lp_id ");
2848
            $count_row_lp = Database::num_rows($res_row_lp);
2849
2850
            // calculates last connection time
2851
            if ($count_row_lp > 0) {
2852
                $sql = 'SELECT MAX(start_time)
2853
                        FROM ' . $t_lpiv . ' AS item_view
2854
                        INNER JOIN ' . $t_lpv . ' AS view
2855
                            ON item_view.lp_view_id = view.id
2856
                        WHERE
2857
                            item_view.c_id 		= '.$course_id.' AND
2858
                            view.c_id 			= '.$course_id.' AND
2859
                            view.lp_id 			= '.$lp_id.'
2860
                            AND view.user_id 	= '.$student_id.'
2861
                            AND view.session_id = '.$session_id;
2862
                $rs = Database::query($sql);
2863
                if (Database :: num_rows($rs) > 0) {
2864
                    $last_time = Database :: result($rs, 0, 0);
2865
                }
2866
            }
2867
        }
2868
2869
        return $last_time;
2870
    }
2871
2872
    /**
2873
     * gets the list of students followed by coach
2874
     * @param     int     Coach id
2875
     * @return     array     List of students
2876
     */
2877
    public static function get_student_followed_by_coach($coach_id)
2878
    {
2879
        $coach_id = intval($coach_id);
2880
2881
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2882
        $tbl_session_course = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE);
2883
        $tbl_session_user = Database :: get_main_table(TABLE_MAIN_SESSION_USER);
2884
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
2885
2886
        $students = [];
2887
2888
        // At first, courses where $coach_id is coach of the course //
2889
        $sql = 'SELECT session_id, c_id
2890
                FROM ' . $tbl_session_course_user . '
2891
                WHERE user_id=' . $coach_id.' AND status=2';
2892
2893
        if (api_is_multiple_url_enabled()) {
2894
            $tbl_session_rel_access_url= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2895
            $access_url_id = api_get_current_access_url_id();
2896
            if ($access_url_id != -1) {
2897
                $sql = 'SELECT scu.session_id, scu.c_id
2898
                    FROM ' . $tbl_session_course_user . ' scu
2899
                    INNER JOIN '.$tbl_session_rel_access_url.'  sru
2900
                    ON (scu.session_id=sru.session_id)
2901
                    WHERE
2902
                        scu.user_id=' . $coach_id.' AND
2903
                        scu.status=2 AND
2904
                        sru.access_url_id = '.$access_url_id;
2905
            }
2906
        }
2907
2908
        $result = Database::query($sql);
2909
2910 View Code Duplication
        while ($a_courses = Database::fetch_array($result)) {
2911
            $courseId = $a_courses["c_id"];
2912
            $id_session = $a_courses["session_id"];
2913
2914
            $sql = "SELECT DISTINCT srcru.user_id
2915
                    FROM $tbl_session_course_user AS srcru, $tbl_session_user sru
2916
                    WHERE
2917
                        srcru.user_id = sru.user_id AND
2918
                        sru.relation_type<>".SESSION_RELATION_TYPE_RRHH." AND
2919
                        srcru.session_id = sru.session_id AND
2920
                        srcru.c_id = '$courseId' AND
2921
                        srcru.session_id='$id_session'";
2922
2923
            $rs = Database::query($sql);
2924
2925
            while ($row = Database::fetch_array($rs)) {
2926
                $students[$row['user_id']] = $row['user_id'];
2927
            }
2928
        }
2929
2930
        // Then, courses where $coach_id is coach of the session    //
2931
        $sql = 'SELECT session_course_user.user_id
2932
                FROM ' . $tbl_session_course_user . ' as session_course_user
2933
                INNER JOIN     '.$tbl_session_user.' sru
2934
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
2935
                INNER JOIN ' . $tbl_session_course . ' as session_course
2936
                ON session_course.c_id = session_course_user.c_id
2937
                AND session_course_user.session_id = session_course.session_id
2938
                INNER JOIN ' . $tbl_session . ' as session
2939
                ON session.id = session_course.session_id
2940
                AND session.id_coach = ' . $coach_id;
2941 View Code Duplication
        if (api_is_multiple_url_enabled()) {
2942
            $tbl_session_rel_access_url= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
2943
            $access_url_id = api_get_current_access_url_id();
2944
            if ($access_url_id != -1){
2945
                $sql = 'SELECT session_course_user.user_id
2946
                        FROM ' . $tbl_session_course_user . ' as session_course_user
2947
                        INNER JOIN     '.$tbl_session_user.' sru
2948
                            ON session_course_user.user_id = sru.user_id AND
2949
                               session_course_user.session_id = sru.session_id
2950
                        INNER JOIN ' . $tbl_session_course . ' as session_course
2951
                            ON session_course.c_id = session_course_user.c_id AND
2952
                            session_course_user.session_id = session_course.session_id
2953
                        INNER JOIN ' . $tbl_session . ' as session
2954
                            ON session.id = session_course.session_id AND
2955
                            session.id_coach = ' . $coach_id.'
2956
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
2957
                            ON session.id = session_rel_url.session_id WHERE access_url_id = '.$access_url_id;
2958
            }
2959
        }
2960
2961
        $result = Database::query($sql);
2962
        while ($row = Database::fetch_array($result)) {
2963
            $students[$row['user_id']] = $row['user_id'];
2964
        }
2965
2966
        return $students;
2967
    }
2968
2969
    /**
2970
     * Get student followed by a coach inside a session
2971
     * @param    int        Session id
2972
     * @param    int        Coach id
2973
     * @return   array    students list
2974
     */
2975
    public static function get_student_followed_by_coach_in_a_session($id_session, $coach_id)
2976
    {
2977
        $coach_id = intval($coach_id);
2978
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2979
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
2980
2981
        $students = [];
2982
        // At first, courses where $coach_id is coach of the course //
2983
        $sql = 'SELECT c_id FROM ' . $tbl_session_course_user . '
2984
                WHERE session_id="' . $id_session . '" AND user_id=' . $coach_id.' AND status=2';
2985
        $result = Database::query($sql);
2986
2987
        while ($a_courses = Database::fetch_array($result)) {
2988
            $courseId = $a_courses["c_id"];
2989
2990
            $sql = "SELECT DISTINCT srcru.user_id
2991
                    FROM $tbl_session_course_user AS srcru
2992
                    WHERE
2993
                        c_id = '$courseId' AND
2994
                        session_id = '" . $id_session . "'";
2995
            $rs = Database::query($sql);
2996
            while ($row = Database::fetch_array($rs)) {
2997
                $students[$row['user_id']] = $row['user_id'];
2998
            }
2999
        }
3000
3001
        // Then, courses where $coach_id is coach of the session
3002
        $sql = 'SELECT id_coach FROM ' . $tbl_session . '
3003
                WHERE id="' . $id_session.'" AND id_coach="' . $coach_id . '"';
3004
        $result = Database::query($sql);
3005
3006
        //He is the session_coach so we select all the users in the session
3007
        if (Database::num_rows($result) > 0) {
3008
            $sql = 'SELECT DISTINCT srcru.user_id
3009
                    FROM ' . $tbl_session_course_user . ' AS srcru
3010
                    WHERE session_id="' . $id_session . '"';
3011
            $result = Database::query($sql);
3012
            while ($row = Database::fetch_array($result)) {
3013
                $students[$row['user_id']] = $row['user_id'];
3014
            }
3015
        }
3016
3017
        return $students;
3018
    }
3019
3020
    /**
3021
     * Check if a coach is allowed to follow a student
3022
     * @param    int        Coach id
3023
     * @param    int        Student id
3024
     * @return    bool
3025
     */
3026
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3027
    {
3028
        $coach_id = intval($coach_id);
3029
        $student_id = intval($student_id);
3030
3031
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3032
        $tbl_session_course = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE);
3033
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
3034
3035
        // At first, courses where $coach_id is coach of the course //
3036
3037
        $sql = 'SELECT 1 FROM ' . $tbl_session_course_user . '
3038
                WHERE user_id=' . $coach_id .' AND status=2';
3039
        $result = Database::query($sql);
3040
        if (Database::num_rows($result) > 0) {
3041
            return true;
3042
        }
3043
3044
        // Then, courses where $coach_id is coach of the session
3045
        $sql = 'SELECT session_course_user.user_id
3046
                FROM ' . $tbl_session_course_user . ' as session_course_user
3047
                INNER JOIN ' . $tbl_session_course . ' as session_course
3048
                    ON session_course.c_id = session_course_user.c_id
3049
                INNER JOIN ' . $tbl_session . ' as session
3050
                    ON session.id = session_course.session_id
3051
                    AND session.id_coach = ' . $coach_id . '
3052
                WHERE user_id = ' . $student_id;
3053
        $result = Database::query($sql);
3054
        if (Database::num_rows($result) > 0) {
3055
            return true;
3056
        }
3057
3058
        return false;
3059
    }
3060
3061
    /**
3062
     * Get courses followed by coach
3063
     * @param     int        Coach id
3064
     * @param    int        Session id (optional)
3065
     * @return    array    Courses list
3066
     */
3067
    public static function get_courses_followed_by_coach($coach_id, $id_session = null)
3068
    {
3069
        $coach_id = intval($coach_id);
3070
        if (!empty($id_session)) {
3071
            $id_session = intval($id_session);
3072
        }
3073
3074
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3075
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3076
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3077
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3078
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3079
3080
        // At first, courses where $coach_id is coach of the course.
3081
3082
        $sql = 'SELECT DISTINCT c.code
3083
                FROM ' . $tbl_session_course_user . ' sc
3084
                INNER JOIN '.$tbl_course.' c
3085
                ON (c.id = sc.c_id)
3086
                WHERE user_id = ' . $coach_id.' AND status = 2';
3087
3088
        if (api_is_multiple_url_enabled()) {
3089
            $access_url_id = api_get_current_access_url_id();
3090
            if ($access_url_id != -1){
3091
                $sql = 'SELECT DISTINCT c.code
3092
                        FROM ' . $tbl_session_course_user . ' scu
3093
                        INNER JOIN '.$tbl_course.' c
3094
                        ON (c.code = scu.c_id)
3095
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3096
                        ON (c.id = cru.c_id)
3097
                        WHERE
3098
                            scu.user_id=' . $coach_id.' AND
3099
                            scu.status=2 AND
3100
                            cru.access_url_id = '.$access_url_id;
3101
            }
3102
        }
3103
3104
        if (!empty($id_session)) {
3105
            $sql .= ' AND session_id=' . $id_session;
3106
        }
3107
3108
        $courseList = array();
3109
        $result = Database::query($sql);
3110
        while ($row = Database::fetch_array($result)) {
3111
            $courseList[$row['code']] = $row['code'];
3112
        }
3113
3114
        // Then, courses where $coach_id is coach of the session
3115
3116
        $sql = 'SELECT DISTINCT course.code
3117
                FROM ' . $tbl_session_course . ' as session_course
3118
                INNER JOIN ' . $tbl_session . ' as session
3119
                    ON session.id = session_course.session_id
3120
                    AND session.id_coach = ' . $coach_id . '
3121
                INNER JOIN ' . $tbl_course . ' as course
3122
                    ON course.id = session_course.c_id';
3123
3124 View Code Duplication
        if (api_is_multiple_url_enabled()) {
3125
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3126
            $access_url_id = api_get_current_access_url_id();
3127
            if ($access_url_id != -1){
3128
                $sql = 'SELECT DISTINCT c.code
3129
                    FROM ' . $tbl_session_course . ' as session_course
3130
                    INNER JOIN '.$tbl_course.' c
3131
                    ON (c.id = session_course.c_id)
3132
                    INNER JOIN ' . $tbl_session . ' as session
3133
                    ON session.id = session_course.session_id
3134
                        AND session.id_coach = ' . $coach_id . '
3135
                    INNER JOIN ' . $tbl_course . ' as course
3136
                        ON course.id = session_course.c_id
3137
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
3138
                    ON (course_rel_url.c_id = c.id)';
3139
            }
3140
        }
3141
3142
        if (!empty ($id_session)) {
3143
            $sql .= ' WHERE session_course.session_id=' . $id_session;
3144
            if (api_is_multiple_url_enabled())
3145
            $sql .=  ' AND access_url_id = '.$access_url_id;
3146
        }  else {
3147
            if (api_is_multiple_url_enabled())
3148
            $sql .=  ' WHERE access_url_id = '.$access_url_id;
3149
        }
3150
3151
        $result = Database::query($sql);
3152
        while ($row = Database::fetch_array($result)) {
3153
            $courseList[$row['code']] = $row['code'];
3154
        }
3155
3156
        return $courseList;
3157
    }
3158
3159
    /**
3160
     * Get sessions coached by user
3161
     * @param $coach_id
3162
     * @param int $start
3163
     * @param int $limit
3164
     * @param bool $getCount
3165
     * @param string $keyword
3166
     * @param string $description
3167
     * @return mixed
3168
     */
3169
    public static function get_sessions_coached_by_user(
3170
        $coach_id,
3171
        $start = 0,
3172
        $limit = 0,
3173
        $getCount = false,
3174
        $keyword = '',
3175
        $description = ''
3176
    ) {
3177
        // table definition
3178
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
3179
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3180
        $coach_id = intval($coach_id);
3181
3182
        $select = " SELECT * FROM ";
3183
        if ($getCount) {
3184
            $select = " SELECT count(DISTINCT id) as count FROM ";
3185
        }
3186
3187
        $limitCondition = null;
3188 View Code Duplication
        if (!empty($start) && !empty($limit)) {
3189
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3190
        }
3191
3192
        $keywordCondition = null;
3193
3194 View Code Duplication
        if (!empty($keyword)) {
3195
            $keyword = Database::escape_string($keyword);
3196
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
3197
3198
            if (!empty($description)) {
3199
                $description = Database::escape_string($description);
3200
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3201
            }
3202
        }
3203
3204
        $tbl_session_rel_access_url= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3205
        $access_url_id = api_get_current_access_url_id();
3206
3207
        $sql = "
3208
            $select
3209
            (
3210
                SELECT DISTINCT
3211
                    id,
3212
                    name,
3213
                    access_start_date,
3214
                    access_end_date
3215
                FROM $tbl_session session INNER JOIN $tbl_session_rel_access_url session_rel_url
3216
                ON (session.id = session_rel_url.session_id)
3217
                WHERE
3218
                    id_coach = $coach_id AND
3219
                    access_url_id = $access_url_id
3220
                    $keywordCondition
3221
            UNION
3222
                SELECT DISTINCT
3223
                    session.id,
3224
                    session.name,
3225
                    session.access_start_date,
3226
                    session.access_end_date
3227
                FROM $tbl_session as session
3228
                INNER JOIN $tbl_session_course_user as session_course_user
3229
                    ON session.id = session_course_user.session_id AND
3230
                    session_course_user.user_id = $coach_id AND
3231
                    session_course_user.status = 2
3232
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3233
                ON (session.id = session_rel_url.session_id)
3234
                WHERE
3235
                    access_url_id = $access_url_id
3236
                    $keywordCondition
3237
            ) as sessions $limitCondition
3238
            ";
3239
3240
        $rs = Database::query($sql);
3241
        if ($getCount) {
3242
            $row = Database::fetch_array($rs);
3243
            return $row['count'];
3244
        }
3245
3246
        $sessions = [];
3247
        while ($row = Database::fetch_array($rs)) {
3248
            $sessions[$row['id']] = $row;
3249
        }
3250
3251
        if (!empty($sessions)) {
3252
            foreach ($sessions as & $session) {
3253
                if ($session['access_start_date'] == '0000-00-00 00:00:00' || empty($session['access_start_date'])
3254
                ) {
3255
                    $session['status'] = get_lang('SessionActive');
3256
                }
3257
                else {
3258
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3259
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3260
                    if ($time_start < time() && time() < $time_end) {
3261
                        $session['status'] = get_lang('SessionActive');
3262
                    } else {
3263
                        if (time() < $time_start) {
3264
                            $session['status'] = get_lang('SessionFuture');
3265
                        } else {
3266
                            if (time() > $time_end) {
3267
                                $session['status'] = get_lang('SessionPast');
3268
                            }
3269
                        }
3270
                    }
3271
                }
3272
            }
3273
        }
3274
3275
        return $sessions;
3276
    }
3277
3278
    /**
3279
     * Get courses list from a session
3280
     * @param    int        Session id
3281
     * @return    array    Courses list
3282
     */
3283 View Code Duplication
    public static function get_courses_list_from_session($session_id)
3284
    {
3285
        $session_id = intval($session_id);
3286
3287
        // table definition
3288
        $tbl_session_course = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE);
3289
        $courseTable = Database :: get_main_table(TABLE_MAIN_COURSE);
3290
3291
        $sql = "SELECT DISTINCT code, c_id
3292
                FROM $tbl_session_course sc
3293
                INNER JOIN $courseTable c
3294
                ON sc.c_id = c.id
3295
                WHERE session_id= $session_id";
3296
3297
        $result = Database::query($sql);
3298
3299
        $courses = array();
3300
        while ($row = Database::fetch_array($result)) {
3301
            $courses[$row['code']] = $row;
3302
        }
3303
3304
        return $courses;
3305
    }
3306
3307
    /**
3308
     * Count the number of documents that an user has uploaded to a course
3309
     * @param    int|array   Student id(s)
3310
     * @param    string      Course code
3311
     * @param    int         Session id (optional),
3312
     * if param $session_id is null(default)
3313
     * return count of assignments including sessions, 0 = session is not filtered
3314
     * @return    int        Number of documents
3315
     */
3316
    public static function count_student_uploaded_documents($student_id, $course_code, $session_id = null)
3317
    {
3318
        // get the information of the course
3319
        $a_course = CourseManager::get_course_information($course_code);
3320
        if (!empty($a_course)) {
3321
            // table definition
3322
            $tbl_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3323
            $tbl_document = Database :: get_course_table(TABLE_DOCUMENT);
3324
            $course_id	 = $a_course['real_id'];
3325
            if (is_array($student_id)) {
3326
                $studentList = array_map('intval', $student_id);
3327
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
3328
            } else {
3329
                $student_id = intval($student_id);
3330
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
3331
            }
3332
3333
            $condition_session = null;
3334
            if (isset($session_id)) {
3335
                $session_id = intval($session_id);
3336
                $condition_session = " AND pub.session_id = $session_id ";
3337
            }
3338
3339
            $sql = "SELECT count(ip.tool) AS count
3340
                    FROM $tbl_item_property ip INNER JOIN $tbl_document pub
3341
                            ON ip.ref = pub.id
3342
                    WHERE 	ip.c_id  = $course_id AND
3343
                            pub.c_id  = $course_id AND
3344
                            pub.filetype ='file' AND
3345
                            ip.tool = 'document'
3346
                            $condition_user $condition_session ";
3347
            $rs = Database::query($sql);
3348
            $row = Database::fetch_array($rs, 'ASSOC');
3349
            return $row['count'];
3350
        }
3351
        return null;
3352
    }
3353
3354
    /**
3355
     * Count assignments per student
3356
     * @param    int|array   Student id(s)
3357
     * @param    string        Course code
3358
     * @param    int            Session id (optional),
3359
     * if param $session_id is null(default) return count of assignments
3360
     * including sessions, 0 = session is not filtered
3361
     * @return    int            Count of assignments
3362
     */
3363
    public static function count_student_assignments($student_id, $course_code = null, $session_id = null)
3364
    {
3365
        if (empty($student_id)) {
3366
            return 0;
3367
        }
3368
3369
        $conditions = array();
3370
3371
        // Get the information of the course
3372
        $a_course = CourseManager::get_course_information($course_code);
3373
        if (!empty($a_course)) {
3374
            $course_id = $a_course['real_id'];
3375
            $conditions[]= " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
3376
        }
3377
3378
        // table definition
3379
        $tbl_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3380
        $tbl_student_publication = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
3381
3382 View Code Duplication
        if (is_array($student_id)) {
3383
            $studentList = array_map('intval', $student_id);
3384
            $conditions[]= " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
3385
        } else {
3386
            $student_id = intval($student_id);
3387
            $conditions[]= " ip.insert_user_id = '$student_id' ";
3388
        }
3389
        if (isset($session_id)) {
3390
            $session_id = intval($session_id);
3391
            $conditions[]= " pub.session_id = $session_id ";
3392
        }
3393
        $conditionToString = implode('AND', $conditions);
3394
3395
        $sql = "SELECT count(ip.tool) as count
3396
                FROM $tbl_item_property ip
3397
                INNER JOIN $tbl_student_publication pub ON ip.ref = pub.id
3398
                WHERE
3399
                    ip.tool='work' AND
3400
                    $conditionToString";
3401
        $rs = Database::query($sql);
3402
        $row = Database::fetch_array($rs, 'ASSOC');
3403
        return $row['count'];
3404
    }
3405
3406
    /**
3407
     * Count messages per student inside forum tool
3408
     * @param    int|array        Student id
3409
     * @param    string    Course code
3410
     * @param    int        Session id (optional), if param $session_id is
3411
     * null(default) return count of messages including sessions, 0 = session is not filtered
3412
     * @return    int        Count of messages
3413
     */
3414
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
3415
    {
3416
        if (empty($student_id)) {
3417
            return 0;
3418
        }
3419
3420
        $courseInfo = api_get_course_info($courseCode);
3421
        $courseCondition = null;
3422
        $conditions = array();
3423
        if (!empty($courseInfo)) {
3424
            $course_id	    = $courseInfo['real_id'];
3425
            $conditions[]= " post.c_id  = $course_id AND forum.c_id = $course_id ";
3426
        }
3427
3428
        // Table definition.
3429
        $tbl_forum_post = Database :: get_course_table(TABLE_FORUM_POST);
3430
        $tbl_forum = Database :: get_course_table(TABLE_FORUM);
3431
3432 View Code Duplication
        if (is_array($student_id)) {
3433
            $studentList = array_map('intval', $student_id);
3434
            $conditions[]= " post.poster_id IN ('".implode("','", $studentList)."') ";
3435
        } else {
3436
            $student_id = intval($student_id);
3437
            $conditions[]= " post.poster_id = '$student_id' ";
3438
        }
3439
3440
        if (isset($session_id)) {
3441
            $session_id = intval($session_id);
3442
            $conditions[]= " forum.session_id = $session_id";
3443
        }
3444
3445
        $conditionsToString = implode('AND ', $conditions);
3446
        $sql = "SELECT count(poster_id) as count
3447
                FROM $tbl_forum_post post INNER JOIN $tbl_forum forum
3448
                ON forum.forum_id = post.forum_id
3449
                WHERE $conditionsToString";
3450
3451
        $rs = Database::query($sql);
3452
        $row = Database::fetch_array($rs, 'ASSOC');
3453
        $count = $row['count'];
3454
3455
        return $count;
3456
    }
3457
3458
    /**
3459
     * This function counts the number of post by course
3460
     * @param      string     Course code
3461
     * @param    int        Session id (optional), if param $session_id is
3462
     * null(default) it'll return results including sessions,
3463
     * 0 = session is not filtered
3464
     * @param int $groupId
3465
     * @return    int     The number of post by course
3466
     */
3467 View Code Duplication
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
3468
    {
3469
        $courseInfo = api_get_course_info($course_code);
3470
        if (!empty($courseInfo)) {
3471
            $tbl_posts = Database :: get_course_table(TABLE_FORUM_POST);
3472
            $tbl_forums = Database :: get_course_table(TABLE_FORUM);
3473
3474
            $condition_session = '';
3475
            if (isset($session_id)) {
3476
                $session_id = intval($session_id);
3477
                $condition_session = api_get_session_condition($session_id, true,  false, 'f.session_id');
3478
            }
3479
3480
            $course_id = $courseInfo['real_id'];
3481
            $groupId = intval($groupId);
3482
            if (!empty($groupId)) {
3483
                $groupCondition = " i.to_group_id = $groupId  ";
3484
            } else {
3485
                $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
3486
            }
3487
3488
            $item = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3489
            $sql = "SELECT count(*) FROM $tbl_posts p
3490
                    INNER JOIN $tbl_forums f
3491
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
3492
                    INNER JOIN $item i
3493
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
3494
                    WHERE
3495
                        p.c_id = $course_id AND
3496
                        f.c_id = $course_id AND
3497
                        $groupCondition
3498
                        $condition_session
3499
                    ";
3500
            $result = Database::query($sql);
3501
            $row = Database::fetch_row($result);
3502
            $count = $row[0];
3503
3504
            return $count;
3505
        } else {
3506
            return null;
3507
        }
3508
    }
3509
3510
    /**
3511
     * This function counts the number of threads by course
3512
     * @param      string     Course code
3513
     * @param    int        Session id (optional),
3514
     * if param $session_id is null(default) it'll return results including
3515
     * sessions, 0 = session is not filtered
3516
     * @param int $groupId
3517
     * @return    int     The number of threads by course
3518
     */
3519 View Code Duplication
    public static function count_number_of_threads_by_course($course_code, $session_id = null, $groupId = 0)
3520
    {
3521
        $course_info = api_get_course_info($course_code);
3522
        if (empty($course_info)) {
3523
            return null;
3524
        }
3525
3526
        $course_id = $course_info['real_id'];
3527
        $tbl_threads = Database :: get_course_table(TABLE_FORUM_THREAD);
3528
        $tbl_forums = Database :: get_course_table(TABLE_FORUM);
3529
3530
        $condition_session = '';
3531
        if (isset($session_id)) {
3532
            $session_id = intval($session_id);
3533
            $condition_session = ' AND f.session_id = '. $session_id;
3534
        }
3535
3536
        $groupId = intval($groupId);
3537
3538
        if (!empty($groupId)) {
3539
            $groupCondition = " i.to_group_id = $groupId ";
3540
        } else {
3541
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
3542
        }
3543
3544
        $item = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3545
        $sql = "SELECT count(*)
3546
                FROM $tbl_threads t
3547
                INNER JOIN $tbl_forums f
3548
                ON f.iid = t.forum_id AND f.c_id = t.c_id
3549
                INNER JOIN $item i
3550
                ON (
3551
                    tool = '".TOOL_FORUM_THREAD."' AND
3552
                    f.c_id = i.c_id AND
3553
                    t.iid = i.ref
3554
                )
3555
                WHERE
3556
                    t.c_id = $course_id AND
3557
                    f.c_id = $course_id AND
3558
                    $groupCondition
3559
                    $condition_session
3560
                ";
3561
3562
        $result = Database::query($sql);
3563
        if (Database::num_rows($result)) {
3564
            $row = Database::fetch_row($result);
3565
            $count = $row[0];
3566
3567
            return $count;
3568
        } else {
3569
3570
            return null;
3571
        }
3572
    }
3573
3574
    /**
3575
     * This function counts the number of forums by course
3576
     * @param      string     Course code
3577
     * @param    int        Session id (optional),
3578
     * if param $session_id is null(default) it'll return results
3579
     * including sessions, 0 = session is not filtered
3580
     * @param int $groupId
3581
     * @return    int     The number of forums by course
3582
     */
3583
    public static function count_number_of_forums_by_course($course_code, $session_id = null, $groupId = 0)
3584
    {
3585
        $course_info = api_get_course_info($course_code);
3586
        if (empty($course_info)) {
3587
            return null;
3588
        }
3589
        $course_id = $course_info['real_id'];
3590
3591
        $condition_session = '';
3592
        if (isset($session_id)) {
3593
             $session_id = intval($session_id);
3594
             $condition_session = ' AND f.session_id = '. $session_id;
3595
        }
3596
3597
        $groupId = intval($groupId);
3598
        if (!empty($groupId)) {
3599
            $groupCondition = " i.to_group_id = $groupId ";
3600
        } else {
3601
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
3602
        }
3603
3604
        $tbl_forums = Database :: get_course_table(TABLE_FORUM);
3605
        $item = Database :: get_course_table(TABLE_ITEM_PROPERTY);
3606
3607
        $sql = "SELECT count(*)
3608
                FROM $tbl_forums f
3609
                INNER JOIN $item i
3610
                    ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
3611
                WHERE
3612
                    f.c_id = $course_id AND
3613
                    $groupCondition
3614
                    $condition_session
3615
                ";
3616
        $result = Database::query($sql);
3617
        if (Database::num_rows($result)) {
3618
            $row = Database::fetch_row($result);
3619
            $count = $row[0];
3620
            return $count;
3621
        } else {
3622
            return null;
3623
        }
3624
    }
3625
3626
    /**
3627
     * This function counts the chat last connections by course in x days
3628
     * @param      string     Course code
3629
     * @param      int     Last x days
3630
     * @param    int        Session id (optional)
3631
     * @return     int     Chat last connections by course in x days
3632
     */
3633
    public static function chat_connections_during_last_x_days_by_course($course_code, $last_days, $session_id = 0)
3634
    {
3635
        $course_info = api_get_course_info($course_code);
3636
        if (empty($course_info)) {
3637
            return null;
3638
        }
3639
        $course_id = $course_info['real_id'];
3640
3641
        //protect data
3642
        $last_days   = intval($last_days);
3643
        $session_id  = intval($session_id);
3644
        $tbl_stats_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
3645
        $now = api_get_utc_datetime();
3646
        $sql = "SELECT count(*) FROM $tbl_stats_access
3647
                WHERE
3648
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
3649
                    c_id = '$course_id' AND
3650
                    access_tool='".TOOL_CHAT."' AND
3651
                    access_session_id='$session_id' ";
3652
        $result = Database::query($sql);
3653
        if (Database::num_rows($result)) {
3654
            $row = Database::fetch_row($result);
3655
            $count = $row[0];
3656
            return $count;
3657
        } else {
3658
            return null;
3659
        }
3660
    }
3661
3662
    /**
3663
     * This function gets the last student's connection in chat
3664
     * @param      int     Student id
3665
     * @param      string     Course code
3666
     * @param    int        Session id (optional)
3667
     * @return     string    datetime formatted without day (e.g: February 23, 2010 10:20:50 )
3668
     */
3669
    public static function chat_last_connection($student_id, $courseId, $session_id = 0)
3670
    {
3671
        $student_id = intval($student_id);
3672
        $courseId = intval($courseId);
3673
        $session_id    = intval($session_id);
3674
        $date_time  = '';
3675
3676
        // table definition
3677
        $tbl_stats_access = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
3678
        $sql = "SELECT access_date
3679
                FROM $tbl_stats_access
3680
                WHERE
3681
                     access_tool='".TOOL_CHAT."' AND
3682
                     access_user_id='$student_id' AND
3683
                     c_id = $courseId AND
3684
                     access_session_id = '$session_id'
3685
                ORDER BY access_date DESC limit 1";
3686
        $rs = Database::query($sql);
3687 View Code Duplication
        if (Database::num_rows($rs) > 0) {
3688
            $row = Database::fetch_array($rs);
3689
            $date_time = api_convert_and_format_date(
3690
                $row['access_date'],
3691
                null,
3692
                date_default_timezone_get()
3693
            );
3694
        }
3695
        return $date_time;
3696
    }
3697
3698
    /**
3699
     * Get count student's visited links
3700
     * @param    int        Student id
3701
     * @param    int    $courseId
3702
     * @param    int        Session id (optional)
3703
     * @return    int        count of visited links
3704
     */
3705 View Code Duplication
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
3706
    {
3707
        $student_id  = intval($student_id);
3708
        $courseId = intval($courseId);
3709
        $session_id  = intval($session_id);
3710
3711
        // table definition
3712
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
3713
3714
        $sql = 'SELECT 1
3715
                FROM '.$table.'
3716
                WHERE
3717
                    links_user_id= '.$student_id.' AND
3718
                    c_id = "'.$courseId.'" AND
3719
                    links_session_id = '.$session_id.' ';
3720
3721
        $rs = Database::query($sql);
3722
        return Database::num_rows($rs);
3723
    }
3724
3725
    /**
3726
     * Get count student downloaded documents
3727
     * @param    int        Student id
3728
     * @param    int    $courseId
3729
     * @param    int        Session id (optional)
3730
     * @return    int        Count downloaded documents
3731
     */
3732 View Code Duplication
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
3733
    {
3734
        $student_id  = intval($student_id);
3735
        $courseId = intval($courseId);
3736
        $session_id  = intval($session_id);
3737
3738
        // table definition
3739
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
3740
3741
        $sql = 'SELECT 1
3742
                FROM ' . $table . '
3743
                WHERE down_user_id = '.$student_id.'
3744
                AND c_id  = "'.$courseId.'"
3745
                AND down_session_id = '.$session_id.' ';
3746
        $rs = Database::query($sql);
3747
3748
        return Database::num_rows($rs);
3749
    }
3750
3751
    /**
3752
     * Get course list inside a session from a student
3753
     * @param    int        $user_id Student id
3754
     * @param    int        $id_session Session id (optional)
3755
     * @return    array    Courses list
3756
     */
3757
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
3758
    {
3759
        $user_id = intval($user_id);
3760
        $id_session = intval($id_session);
3761
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3762
        $courseTable = Database :: get_main_table(TABLE_MAIN_COURSE);
3763
3764
        $sql = "SELECT c.code
3765
                FROM $tbl_session_course_user sc
3766
                INNER JOIN $courseTable c
3767
                WHERE
3768
                    user_id= $user_id  AND
3769
                    session_id = $id_session";
3770
        $result = Database::query($sql);
3771
        $courses = array();
3772
        while ($row = Database::fetch_array($result)) {
3773
            $courses[$row['code']] = $row['code'];
3774
        }
3775
3776
        return $courses;
3777
    }
3778
3779
    /**
3780
     * Get inactive students in course
3781
     * @param    int   $courseId
3782
     * @param    string  $since  Since login course date (optional, default = 'never')
3783
     * @param    int        $session_id    (optional)
3784
     * @return    array    Inactive users
3785
     */
3786
    public static function getInactiveStudentsInCourse($courseId, $since = 'never', $session_id = 0)
3787
    {
3788
        $tbl_track_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3789
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3790
        $table_course_rel_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
3791
        $tableCourse = Database :: get_main_table(TABLE_MAIN_COURSE);
3792
        $now = api_get_utc_datetime();
3793
        $courseId = intval($courseId);
3794
3795
        if (empty($courseId)) {
3796
            return false;
3797
        }
3798
3799
        if (empty($session_id)) {
3800
            $inner = '
3801
                INNER JOIN '.$table_course_rel_user.' course_user
3802
                ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id
3803
            ';
3804
        } else {
3805
            $inner = '
3806
                    INNER JOIN '.$tbl_session_course_user.' session_course_user
3807
                    ON
3808
                        c.id = session_course_user.c_id AND
3809
                        session_course_user.session_id = '.intval($session_id).' AND
3810
                        session_course_user.user_id = stats_login.user_id ';
3811
        }
3812
3813
        $sql = 'SELECT stats_login.user_id, MAX(login_course_date) max_date
3814
                FROM '.$tbl_track_login.' stats_login
3815
                INNER JOIN '.$tableCourse.' c
3816
                ON (c.id = stats_login.c_id)
3817
                '.$inner.'
3818
                WHERE c.id = '.$courseId.'
3819
                GROUP BY stats_login.user_id
3820
                HAVING DATE_SUB( "' . $now . '", INTERVAL '.$since.' DAY) > max_date ';
3821
3822
        if ($since == 'never') {
3823
            if (empty($session_id)) {
3824
                $sql = 'SELECT course_user.user_id
3825
                        FROM ' . $table_course_rel_user . ' course_user
3826
                        LEFT JOIN ' . $tbl_track_login . ' stats_login
3827
                        ON course_user.user_id = stats_login.user_id AND
3828
                        relation_type<>' . COURSE_RELATION_TYPE_RRHH . '
3829
                        INNER JOIN ' . $tableCourse . ' c
3830
                        ON (c.id = course_user.c_id)
3831
                        WHERE
3832
                            course_user.c_id = ' . $courseId . ' AND
3833
                            stats_login.login_course_date IS NULL
3834
                        GROUP BY course_user.user_id';
3835
            } else {
3836
                $sql = 'SELECT session_course_user.user_id
3837
                        FROM '.$tbl_session_course_user.' session_course_user
3838
                        LEFT JOIN ' . $tbl_track_login . ' stats_login
3839
                        ON session_course_user.user_id = stats_login.user_id
3840
                        INNER JOIN ' . $tableCourse . ' c
3841
                        ON (c.id = session_course_user.c_id)
3842
                        WHERE
3843
                            session_course_user.c_id = ' . $courseId . ' AND
3844
                            stats_login.login_course_date IS NULL
3845
                        GROUP BY session_course_user.user_id';
3846
3847
            }
3848
        }
3849
3850
        $rs = Database::query($sql);
3851
        $inactive_users = array();
3852
        while($user = Database::fetch_array($rs)) {
3853
            $inactive_users[] = $user['user_id'];
3854
        }
3855
3856
        return $inactive_users;
3857
    }
3858
3859
    /**
3860
     * Get count login per student
3861
     * @param    int    $student_id    Student id
3862
     * @param    int    $courseId
3863
     * @param    int    $session_id    Session id (optional)
3864
     * @return    int        count login
3865
     */
3866
    public static function count_login_per_student($student_id, $courseId, $session_id = 0)
3867
    {
3868
        $student_id  = intval($student_id);
3869
        $courseId = intval($courseId);
3870
        $session_id  = intval($session_id);
3871
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
3872
3873
        $sql = 'SELECT '.$student_id.'
3874
                FROM ' . $table . '
3875
                WHERE
3876
                    access_user_id=' . $student_id . ' AND
3877
                    c_id="' . $courseId . '" AND
3878
                    access_session_id = "'.$session_id.'" ';
3879
3880
        $rs = Database::query($sql);
3881
        $nb_login = Database::num_rows($rs);
3882
3883
        return $nb_login;
3884
    }
3885
3886
    /**
3887
     * Get students followed by a human resources manager
3888
     * @param    int        Drh id
3889
     * @return    array    Student list
3890
     */
3891
    public static function get_student_followed_by_drh($hr_dept_id)
3892
    {
3893
        $hr_dept_id = intval($hr_dept_id);
3894
        $a_students = array();
3895
        $tbl_user     = Database :: get_main_table(TABLE_MAIN_USER);
3896
3897
        $sql = 'SELECT DISTINCT user_id FROM '.$tbl_user.' as user
3898
                WHERE hr_dept_id='.$hr_dept_id;
3899
        $rs = Database::query($sql);
3900
3901
        while($user = Database :: fetch_array($rs)) {
3902
            $a_students[$user['user_id']] = $user['user_id'];
3903
        }
3904
3905
        return $a_students;
3906
    }
3907
3908
3909
3910
    /**
3911
     * get count clicks about tools most used by course
3912
     * @param    int      $courseId
3913
     * @param    int        Session id (optional),
3914
     * if param $session_id is null(default) it'll return results
3915
     * including sessions, 0 = session is not filtered
3916
     * @return    array     tools data
3917
     */
3918 View Code Duplication
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
3919
    {
3920
        $courseId = intval($courseId);
3921
        $data = array();
3922
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
3923
        $condition_session     = '';
3924
        if (isset($session_id)) {
3925
            $session_id = intval($session_id);
3926
            $condition_session = ' AND access_session_id = '. $session_id;
3927
        }
3928
        $sql = "SELECT
3929
                    access_tool,
3930
                    COUNT(DISTINCT access_user_id),
3931
                    count(access_tool) as count_access_tool
3932
                FROM $TABLETRACK_ACCESS
3933
                WHERE
3934
                    access_tool IS NOT NULL AND
3935
                    access_tool != '' AND
3936
                    c_id = '$courseId'
3937
                    $condition_session
3938
                GROUP BY access_tool
3939
                ORDER BY count_access_tool DESC
3940
                LIMIT 0, 3";
3941
        $rs = Database::query($sql);
3942
        if (Database::num_rows($rs) > 0) {
3943
            while ($row = Database::fetch_array($rs)) {
3944
                $data[] = $row;
3945
            }
3946
        }
3947
        return $data;
3948
    }
3949
    /**
3950
     * Get total clicks
3951
     * 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,
3952
     * BUT NO ROW MATCH THE CONDITION, IT SHOULD BE FINE TO USE IT WHEN YOU USE USER DEFINED DATES AND NO CHAMILO DATES
3953
     * @param   int     User Id
3954
     * @param   int     Course Id
3955
     * @param   int     Session Id (optional), if param $session_id is null(default) it'll return results including sessions, 0 = session is not filtered
3956
     * @param   string  Date from
3957
     * @param   string  Date to
3958
     * @return  array   Data
3959
     * @author  César Perales [email protected] 2014-01-16
3960
     */
3961
    public static function get_total_clicks($userId, $courseId, $sessionId = 0, $date_from = '', $date_to = '')
3962
    {
3963
        $course = api_get_course_info_by_id($courseId);
3964
        $tables = array(
3965
            TABLE_STATISTIC_TRACK_E_LASTACCESS => array(
3966
                'course'    => 'c_id',
3967
                'session'   => 'access_session_id',
3968
                'user'      => 'access_user_id',
3969
                'start_date'=> 'access_date',
3970
            ),
3971
            TABLE_STATISTIC_TRACK_E_ACCESS => array(
3972
                'course'    => 'c_id',
3973
                'session'   => 'access_session_id',
3974
                'user'      => 'access_user_id',
3975
                'start_date'=> 'access_date',
3976
            ),
3977
            #TABLE_STATISTIC_TRACK_E_LOGIN, array(,, 'login_date', 'logout_date');
3978
            TABLE_STATISTIC_TRACK_E_DOWNLOADS => array(
3979
                'course'    => 'c_id',
3980
                'session'   => 'down_session_id',
3981
                'user'      => 'down_user_id',
3982
                'start_date'=> 'down_date',
3983
                ),
3984
            TABLE_STATISTIC_TRACK_E_LINKS => array(
3985
                'course'    => 'c_id',
3986
                'session'   => 'links_session_id',
3987
                'user'      => 'links_user_id',
3988
                'start_date'=> 'links_date',
3989
            ),
3990
            TABLE_STATISTIC_TRACK_E_ONLINE => array(
3991
                'course'    => 'c_id',
3992
                'session'   => 'session_id',
3993
                'user'      => 'login_user_id',
3994
                'start_date'=> 'login_date',
3995
            ),
3996
            #TABLE_STATISTIC_TRACK_E_HOTPOTATOES,
3997
            /*TABLE_STATISTIC_TRACK_E_COURSE_ACCESS => array(
3998
                'course'    => 'c_id',
3999
                'session'   => 'session_id',
4000
                'user'      => 'user_id',
4001
                'start_date'=> 'login_course_date',
4002
                'end_date'  => 'logout_course_date',
4003
                ),*/
4004
            TABLE_STATISTIC_TRACK_E_EXERCISES => array(
4005
                'course'    => 'c_id',
4006
                'session'   => 'session_id',
4007
                'user'      => 'exe_user_id',
4008
                'start_date'=> 'exe_date',
4009
            ),
4010
            TABLE_STATISTIC_TRACK_E_ATTEMPT => array(
4011
                'course'    => 'c_id',
4012
                'session'   => 'session_id',
4013
                'user'      => 'user_id',
4014
                'start_date'=> 'tms',
4015
            ),
4016
            #TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING,
4017
            #TABLE_STATISTIC_TRACK_E_DEFAULT,
4018
            TABLE_STATISTIC_TRACK_E_UPLOADS => array(
4019
                'course'    => 'c_id',
4020
                'session'   => 'upload_session_id',
4021
                'user'      => 'upload_user_id',
4022
                'start_date'=> 'upload_date',
4023
            ),
4024
        );
4025
4026
        foreach ($tables as $tableName => $fields) {
4027
            //If session is defined, add it to query
4028
            $where = '';
4029
            if (isset($sessionId) && !empty($sessionId)) {
4030
                $sessionField = $fields['session'];
4031
                $where .= " AND $sessionField = $sessionId";
4032
            }
4033
4034
            //filter by date
4035
            if (!empty($date_from) && !empty($date_to)) {
4036
                $fieldStartDate = $fields['start_date'];
4037
                if (!isset($fields['end_date'])) {
4038
                    $where .= sprintf(" AND ($fieldStartDate BETWEEN '%s' AND '%s' )", $date_from, $date_to) ;
4039
                } else {
4040
                    $fieldEndDate = $fields['end_date'];
4041
                    $where .= sprintf(" AND fieldStartDate >= '%s'
4042
                        AND $fieldEndDate <= '%s'", $date_from, $date_to);
4043
                }
4044
            }
4045
4046
            //query
4047
            $sql = "SELECT %s as user, count(*) as total
4048
                FROM %s
4049
                WHERE %s = '%s'
4050
                AND %s = %s
4051
                $where
4052
                GROUP BY %s";
4053
            $sql = sprintf($sql,
4054
                $fields['user'],    //user field
4055
                $tableName,         //FROM
4056
                $fields['course'],  //course condition
4057
                $course['real_id'],    //course condition
4058
                $fields['user'],    //user condition
4059
                $userId,            //user condition
4060
                $fields['user']     //GROUP BY
4061
                );
4062
            $rs = Database::query($sql);
4063
4064
            //iterate query
4065
            if (Database::num_rows($rs) > 0) {
4066
                while ($row = Database::fetch_array($rs)) {
4067
                    $data[$row['user']] = (isset($data[$row['user']])) ?  $data[$row['user']] + $row[total]: $row['total'];
4068
                }
4069
            }
4070
        }
4071
4072
        return $data;
4073
    }
4074
4075
    /**
4076
     * get documents most downloaded by course
4077
     * @param      string     Course code
4078
     * @param    int        Session id (optional),
4079
     * if param $session_id is null(default) it'll return results including
4080
     * sessions, 0 = session is not filtered
4081
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4082
     * @return    array     documents downloaded
4083
     */
4084 View Code Duplication
    public static function get_documents_most_downloaded_by_course($course_code, $session_id = null, $limit = 0)
4085
    {
4086
        //protect data
4087
        $courseId = api_get_course_int_id($course_code);
4088
        $data = array();
4089
4090
        $TABLETRACK_DOWNLOADS   = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4091
        $condition_session = '';
4092
        if (isset($session_id)) {
4093
            $session_id = intval($session_id);
4094
            $condition_session = ' AND down_session_id = '. $session_id;
4095
        }
4096
        $sql = "SELECT down_doc_path, COUNT(DISTINCT down_user_id), COUNT(down_doc_path) as count_down
4097
                FROM $TABLETRACK_DOWNLOADS
4098
                WHERE c_id = $courseId
4099
                    $condition_session
4100
                GROUP BY down_doc_path
4101
                ORDER BY count_down DESC
4102
                LIMIT 0,  $limit";
4103
        $rs = Database::query($sql);
4104
4105
        if (Database::num_rows($rs) > 0) {
4106
            while ($row = Database::fetch_array($rs)) {
4107
                $data[] = $row;
4108
            }
4109
        }
4110
        return $data;
4111
    }
4112
4113
    /**
4114
     * get links most visited by course
4115
     * @param      string     Course code
4116
     * @param    int        Session id (optional),
4117
     * if param $session_id is null(default) it'll
4118
     * return results including sessions, 0 = session is not filtered
4119
     * @return    array     links most visited
4120
     */
4121
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4122
    {
4123
        $course_code = Database::escape_string($course_code);
4124
        $course_info = api_get_course_info($course_code);
4125
        $course_id = $course_info['real_id'];
4126
        $data = array();
4127
4128
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4129
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4130
4131
        $condition_session = '';
4132
        if (isset($session_id)) {
4133
            $session_id = intval($session_id);
4134
            $condition_session = ' AND cl.session_id = '.$session_id;
4135
        }
4136
4137
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4138
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4139
                WHERE
4140
                    cl.c_id = $course_id AND
4141
                    sl.links_link_id = cl.id AND
4142
                    sl.c_id = $course_id
4143
                    $condition_session
4144
                GROUP BY cl.title, cl.url
4145
                ORDER BY count_visits DESC
4146
                LIMIT 0, 3";
4147
        $rs = Database::query($sql);
4148
        if (Database::num_rows($rs) > 0) {
4149
            while ($row = Database::fetch_array($rs)) {
4150
                $data[] = $row;
4151
            }
4152
        }
4153
        return $data;
4154
    }
4155
4156
    /**
4157
     * Shows the user progress (when clicking in the Progress tab)
4158
     *
4159
     * @param int $user_id
4160
     * @param int $session_id
4161
     * @param string $extra_params
4162
     * @param bool $show_courses
4163
     * @param bool $showAllSessions
4164
     *
4165
     * @return string
4166
     */
4167
    public static function show_user_progress(
4168
        $user_id,
4169
        $session_id = 0,
4170
        $extra_params = '',
4171
        $show_courses = true,
4172
        $showAllSessions = true
4173
    ) {
4174
        $tbl_course = Database :: get_main_table(TABLE_MAIN_COURSE);
4175
        $tbl_session = Database :: get_main_table(TABLE_MAIN_SESSION);
4176
        $tbl_course_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
4177
        $tbl_access_rel_course = Database :: get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4178
        $tbl_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4179
        $tbl_access_rel_session = Database :: get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4180
4181
        $user_id = intval($user_id);
4182
        $session_id = intval($session_id);
4183
4184
        if (api_is_multiple_url_enabled()) {
4185
            $sql = "SELECT c.code, title
4186
                    FROM $tbl_course_user cu
4187
                    INNER JOIN $tbl_course c
4188
                    ON (cu.c_id = c.id)
4189
                    INNER JOIN $tbl_access_rel_course a
4190
                    ON (a.c_id = c.id)
4191
                    WHERE
4192
                        cu.user_id = $user_id AND
4193
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4194
                        access_url_id = ".api_get_current_access_url_id()."
4195
                    ORDER BY title";
4196
        } else {
4197
            $sql = "SELECT c.code, title
4198
                    FROM $tbl_course_user u
4199
                    INNER JOIN $tbl_course c ON (c_id = c.id)
4200
                    WHERE
4201
                        u.user_id= $user_id AND
4202
                        relation_type<>".COURSE_RELATION_TYPE_RRHH."
4203
                    ORDER BY title";
4204
        }
4205
4206
        $rs = Database::query($sql);
4207
        $courses = $course_in_session = $temp_course_in_session = array();
4208
        while ($row = Database :: fetch_array($rs, 'ASSOC')) {
4209
            $courses[$row['code']] = $row['title'];
4210
        }
4211
4212
        $orderBy = " ORDER BY name ";
4213
        $extraInnerJoin = null;
4214
4215
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4216
            $orderBy = " ORDER BY s.id, position ";
4217
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4218
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4219
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4220
        }
4221
4222
        $sessionCondition = '';
4223
        if (!empty($session_id)) {
4224
            $sessionCondition = " AND s.id = $session_id";
4225
        }
4226
4227
        // Get the list of sessions where the user is subscribed as student
4228
        if (api_is_multiple_url_enabled()) {
4229
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4230
                    FROM $tbl_session_course_user cu
4231
                    INNER JOIN $tbl_access_rel_session a
4232
                    ON (a.session_id = cu.session_id)
4233
                    INNER JOIN $tbl_session s
4234
                    ON (s.id = a.session_id)
4235
                    INNER JOIN $tbl_course c
4236
                    ON (c.id = cu.c_id)
4237
                    $extraInnerJoin
4238
                    WHERE
4239
                        cu.user_id = $user_id AND
4240
                        access_url_id = ".api_get_current_access_url_id()."
4241
                        $sessionCondition
4242
                    $orderBy ";
4243
        } else {
4244
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
4245
                    FROM $tbl_session_course_user cu
4246
                    INNER JOIN $tbl_session s
4247
                    ON (s.id = cu.session_id)
4248
                    INNER JOIN $tbl_course c
4249
                    ON (c.id = cu.c_id)
4250
                    $extraInnerJoin
4251
                    WHERE
4252
                        cu.user_id = $user_id
4253
                        $sessionCondition
4254
                    $orderBy ";
4255
        }
4256
4257
        $rs = Database::query($sql);
4258
        $simple_session_array = array();
4259
        while ($row = Database :: fetch_array($rs)) {
4260
            $course_info = CourseManager::get_course_information($row['code']);
4261
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4262
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
4263
            $simple_session_array[$row['session_id']] = $row['name'];
4264
        }
4265
4266
        foreach ($simple_session_array as $my_session_id => $session_name) {
4267
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4268
            $my_course_data = array();
4269
            foreach ($course_list as $course_data) {
4270
                $my_course_data[$course_data['id']] = $course_data['title'];
4271
            }
4272
4273
            if (empty($session_id)) {
4274
                $my_course_data = utf8_sort($my_course_data);
4275
            }
4276
4277
            $final_course_data = array();
4278
4279
            foreach($my_course_data as $course_id => $value) {
4280
                $final_course_data[$course_id] = $course_list[$course_id];
4281
            }
4282
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4283
            $course_in_session[$my_session_id]['name'] = $session_name;
4284
        }
4285
4286
        $html = '';
4287
4288
        // Course list
4289
4290
        if ($show_courses) {
4291
            if (!empty($courses)) {
4292
                $html .= Display::page_subheader(
4293
                    Display::return_icon('course.png', get_lang('MyCourses'), array(), ICON_SIZE_SMALL).' '.get_lang('MyCourses')
4294
                );
4295
                $html .= '<table class="data_table" width="100%">';
4296
                $html .= '<tr>
4297
                          '.Display::tag('th', get_lang('Course'), array('width'=>'300px')).'
4298
                          '.Display::tag('th', get_lang('TimeSpentInTheCourse'), array('class'=>'head')).'
4299
                          '.Display::tag('th', get_lang('Progress'), array('class'=>'head')).'
4300
                          '.Display::tag('th', get_lang('Score').Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage'), array('align' => 'absmiddle', 'hspace' => '3px')),array('class'=>'head')).'
4301
                          '.Display::tag('th', get_lang('LastConnexion'), array('class'=>'head')).'
4302
                          '.Display::tag('th', get_lang('Details'), array('class'=>'head')).'
4303
                        </tr>';
4304
4305
                foreach ($courses as $course_code => $course_title) {
4306
                    $courseInfo = api_get_course_info($course_code);
4307
                    $courseId = $courseInfo['real_id'];
4308
4309
                    $total_time_login = Tracking :: get_time_spent_on_the_course(
4310
                        $user_id,
4311
                        $courseId
4312
                    );
4313
                    $time = api_time_to_hms($total_time_login);
4314
                    $progress = Tracking :: get_avg_student_progress(
4315
                        $user_id,
4316
                        $course_code
4317
                    );
4318
                    $percentage_score = Tracking :: get_avg_student_score(
4319
                        $user_id,
4320
                        $course_code,
4321
                        array()
4322
                    );
4323
                    $last_connection = Tracking :: get_last_connection_date_on_the_course(
4324
                        $user_id,
4325
                        $courseInfo
4326
                    );
4327
4328
                    if (is_null($progress)) {
4329
                        $progress = '0%';
4330
                    } else {
4331
                        $progress = $progress.'%';
4332
                    }
4333
4334
                    if (isset($_GET['course']) &&
4335
                        $course_code == $_GET['course'] &&
4336
                        empty($_GET['session_id'])
4337
                    ) {
4338
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D">';
4339
                    } else {
4340
                        $html .= '<tr class="row_even">';
4341
                    }
4342
                    $url = api_get_course_url($course_code, $session_id);
4343
                    $course_url = Display::url($course_title, $url, array('target'=>SESSION_LINK_TARGET));
4344
                    $html .= '<td>'.$course_url.'</td>';
4345
4346
                    $html .= '<td align="center">'.$time.'</td>';
4347
                    $html .= '<td align="center">'.$progress.'</td>';
4348
                    $html .= '<td align="center">';
4349
                    if (is_numeric($percentage_score)) {
4350
                        $html .= $percentage_score.'%';
4351
                    } else {
4352
                        $html .= '0%';
4353
                    }
4354
                    $html .= '</td>';
4355
                    $html .= '<td align="center">'.$last_connection.'</td>';
4356
                    $html .= '<td align="center">';
4357
                    if (isset($_GET['course']) &&
4358
                        $course_code == $_GET['course'] &&
4359
                        empty($_GET['session_id'])
4360
                    ) {
4361
                        $html .= '<a href="#">';
4362
                        $html .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4363 View Code Duplication
                    } else {
4364
                        $html .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'">';
4365
                        $html .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4366
                    }
4367
                    $html .= '</a>';
4368
                    $html .= '</td></tr>';
4369
                }
4370
                $html .= '</table>';
4371
            }
4372
        }
4373
4374
        // Session list
4375
        if (!empty($course_in_session)) {
4376
            $main_session_graph = '';
4377
            //Load graphics only when calling to an specific session
4378
            $session_graph = array();
4379
4380
            $all_exercise_graph_name_list = array();
4381
            $my_results = array();
4382
            $all_exercise_graph_list = array();
4383
4384
            $all_exercise_start_time = array();
4385
4386
            foreach ($course_in_session as $my_session_id => $session_data) {
4387
                $course_list  = $session_data['course_list'];
4388
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
4389
                $exercise_graph_name_list = array();
4390
                //$user_results = array();
4391
                $exercise_graph_list = array();
4392
4393
                foreach ($course_list as $course_data) {
4394
                    $exercise_list = ExerciseLib::get_all_exercises(
4395
                        $course_data,
4396
                        $my_session_id,
4397
                        false,
4398
                        null,
4399
                        false,
4400
                        1
4401
                    );
4402
4403
                    foreach ($exercise_list as $exercise_data) {
4404
                        $exercise_obj = new Exercise($course_data['id']);
4405
                        $exercise_obj->read($exercise_data['id']);
4406
                        //Exercise is not necessary to be visible to show results check the result_disable configuration instead
4407
                        //$visible_return = $exercise_obj->is_visible();
4408
4409
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
4410
4411
                            $best_average = intval(
4412
                                ExerciseLib::get_best_average_score_by_exercise(
4413
                                    $exercise_data['id'],
4414
                                    $course_data['id'],
4415
                                    $my_session_id,
4416
                                    $user_count
4417
                                )
4418
                            );
4419
4420
                            $exercise_graph_list[] = $best_average;
4421
                            $all_exercise_graph_list[] = $best_average;
4422
4423
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
4424
                                api_get_user_id(),
4425
                                $exercise_data['id'],
4426
                                $course_data['real_id'],
4427
                                $my_session_id
4428
                            );
4429
4430
                            $score = 0;
4431
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
4432
                                $score = intval($user_result_data['exe_result']/$user_result_data['exe_weighting'] * 100);
4433
                            }
4434
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
4435
                            $all_exercise_start_time[] = $time;
4436
                            $my_results[] = $score;
4437
                            if (count($exercise_list)<=10) {
4438
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
4439
                                $exercise_graph_name_list[]= $title;
4440
                                $all_exercise_graph_name_list[] = $title;
4441
                            } else {
4442
                                // if there are more than 10 results, space becomes difficult to find, so only show the title of the exercise, not the tool
4443
                                $title = cut($exercise_data['title'], 30);
4444
                                $exercise_graph_name_list[]= $title;
4445
                                $all_exercise_graph_name_list[]= $title;
4446
                            }
4447
                        }
4448
                    }
4449
                }
4450
            }
4451
4452
            // Complete graph
4453
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
4454
                asort($all_exercise_start_time);
4455
4456
                //Fix exams order
4457
                $final_all_exercise_graph_name_list = array();
4458
                $my_results_final = array();
4459
                $final_all_exercise_graph_list = array();
4460
4461
                foreach ($all_exercise_start_time as $key => $time) {
4462
                    $label_time = '';
4463
                    if (!empty($time)) {
4464
                        $label_time = date('d-m-y', $time);
4465
                    }
4466
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
4467
                    $my_results_final[] = $my_results[$key];
4468
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
4469
                }
4470
                $main_session_graph = self::generate_session_exercise_graph(
4471
                    $final_all_exercise_graph_name_list,
4472
                    $my_results_final,
4473
                    $final_all_exercise_graph_list
4474
                );
4475
            }
4476
4477
            $html .= Display::page_subheader(
4478
                Display::return_icon('session.png', get_lang('Sessions'), array(), ICON_SIZE_SMALL) . ' ' . get_lang('Sessions')
4479
            );
4480
4481
            $html .= '<table class="data_table" width="100%">';
4482
            $html .= '<tr>
4483
                  '.Display::tag('th', get_lang('Session'), array('width'=>'300px')).'
4484
                  '.Display::tag('th', get_lang('PublishedExercises'), array('width'=>'300px')).'
4485
                  '.Display::tag('th', get_lang('NewExercises'), array('class'=>'head')).'
4486
                  '.Display::tag('th', get_lang('AverageExerciseResult'), array('class'=>'head')).'
4487
                  '.Display::tag('th', get_lang('Details'), array('class'=>'head')).'
4488
                  </tr>';
4489
4490
            foreach ($course_in_session as $my_session_id => $session_data) {
4491
                $course_list  = $session_data['course_list'];
4492
                $session_name = $session_data['name'];
4493
4494
                if ($showAllSessions == false) {
4495
                    if (isset($session_id) && !empty($session_id)) {
4496
                        if ($session_id != $my_session_id) {
4497
                            continue;
4498
                        }
4499
                    }
4500
                }
4501
4502
                $all_exercises = 0;
4503
                $all_unanswered_exercises_by_user = 0;
4504
                $all_average = 0;
4505
                $stats_array = array();
4506
4507
                foreach ($course_list as $course_data) {
4508
                    //All exercises in the course @todo change for a real count
4509
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
4510
                    $count_exercises = 0;
4511
                    if (is_array($exercises) && !empty($exercises)) {
4512
                        $count_exercises = count($exercises);
4513
                    }
4514
4515
                    // Count of user results
4516
                    $done_exercises = null;
4517
                    $courseInfo = api_get_course_info($course_data['code']);
4518
4519
                    $answered_exercises = 0;
4520
                    if (!empty($exercises)) {
4521
                        foreach ($exercises as $exercise_item) {
4522
                            $attempts = Event::count_exercise_attempts_by_user(
4523
                                api_get_user_id(),
4524
                                $exercise_item['id'],
4525
                                $courseInfo['real_id'],
4526
                                $my_session_id
4527
                            );
4528
                            if ($attempts > 1)  {
4529
                                $answered_exercises++;
4530
                            }
4531
                        }
4532
                    }
4533
4534
                    // Average
4535
                    $average = ExerciseLib::get_average_score_by_course($courseInfo['real_id'], $my_session_id);
4536
                    $all_exercises += $count_exercises;
4537
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
4538
                    $all_average += $average;
4539
                }
4540
4541
                $all_average = $all_average /  count($course_list);
4542
4543
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
4544
                    $html .= '<tr style="background-color:#FBF09D">';
4545
                } else {
4546
                    $html .= '<tr>';
4547
                }
4548
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
4549
4550
                $html .= Display::tag('td', Display::url($session_name, $url, array('target'=>SESSION_LINK_TARGET)));
4551
                $html .= Display::tag('td', $all_exercises);
4552
                $html .= Display::tag('td', $all_unanswered_exercises_by_user);
4553
4554
                //$html .= Display::tag('td', $all_done_exercise);
4555
                $html .= Display::tag('td', ExerciseLib::convert_to_percentage($all_average));
4556
4557
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
4558
                    $icon = Display::url(Display::return_icon('2rightarrow_na.png', get_lang('Details')), '?session_id='.$my_session_id);
4559
                } else {
4560
                    $icon = Display::url(Display::return_icon('2rightarrow.png', get_lang('Details')), '?session_id='.$my_session_id);
4561
                }
4562
                $html .= Display::tag('td', $icon);
4563
                $html .= '</tr>';
4564
            }
4565
            $html .= '</table><br />';
4566
            $html .= Display::div($main_session_graph, array('id'=>'session_graph','class'=>'chart-session', 'style'=>'position:relative; text-align: center;') );
4567
4568
            // Checking selected session.
4569
4570
            if (isset($_GET['session_id'])) {
4571
                $session_id_from_get = intval($_GET['session_id']);
4572
                $session_data 	= $course_in_session[$session_id_from_get];
4573
                $course_list 	= $session_data['course_list'];
4574
4575
                $html .= Display::tag('h3',$session_data['name'].' - '.get_lang('CourseList'));
4576
4577
                $html .= '<table class="data_table" width="100%">';
4578
                //'.Display::tag('th', get_lang('DoneExercises'),         array('class'=>'head')).'
4579
                $html .= '
4580
                    <tr>
4581
                      <th width="300px">'.get_lang('Course').'</th>
4582
                      '.Display::tag('th', get_lang('PublishedExercises'),    	array('class'=>'head')).'
4583
                      '.Display::tag('th', get_lang('NewExercises'),    		array('class'=>'head')).'
4584
                      '.Display::tag('th', get_lang('MyAverage'), 				array('class'=>'head')).'
4585
                      '.Display::tag('th', get_lang('AverageExerciseResult'), 	array('class'=>'head')).'
4586
                      '.Display::tag('th', get_lang('TimeSpentInTheCourse'),    array('class'=>'head')).'
4587
                      '.Display::tag('th', get_lang('LPProgress')     ,      	array('class'=>'head')).'
4588
                      '.Display::tag('th', get_lang('Score').Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage'), array ('align' => 'absmiddle', 'hspace' => '3px')), array('class'=>'head')).'
4589
                      '.Display::tag('th', get_lang('LastConnexion'),         	array('class'=>'head')).'
4590
                      '.Display::tag('th', get_lang('Details'),               	array('class'=>'head')).'
4591
                    </tr>';
4592
4593
                foreach ($course_list as $course_data) {
4594
                    $course_code  = $course_data['code'];
4595
                    $course_title = $course_data['title'];
4596
                    $courseInfo = api_get_course_info($course_code);
4597
                    $courseId = $courseInfo['real_id'];
4598
4599
                    // All exercises in the course @todo change for a real count
4600
                    $exercises = ExerciseLib::get_all_exercises($course_data, $session_id_from_get);
4601
                    $count_exercises = 0;
4602
                    if (!empty($exercises)) {
4603
                        $count_exercises = count($exercises);
4604
                    }
4605
                    $answered_exercises = 0;
4606
                    foreach($exercises as $exercise_item) {
4607
                        $attempts = Event::count_exercise_attempts_by_user(
4608
                            api_get_user_id(),
4609
                            $exercise_item['id'],
4610
                            $courseId,
4611
                            $session_id_from_get
4612
                        );
4613
                        if ($attempts > 1)  {
4614
                            $answered_exercises++;
4615
                        }
4616
                    }
4617
4618
                    $unanswered_exercises = $count_exercises - $answered_exercises;
4619
4620
                    // Average
4621
                    $average = ExerciseLib::get_average_score_by_course($courseId, $session_id_from_get);
4622
                    $my_average	= ExerciseLib::get_average_score_by_course_by_user(api_get_user_id(), $courseId, $session_id_from_get);
4623
4624
                    $stats_array[$course_code] = array(
4625
                        'exercises' => $count_exercises,
4626
                        'unanswered_exercises_by_user' => $unanswered_exercises,
4627
                        'done_exercises' => $done_exercises,
4628
                        'average' => $average,
4629
                        'my_average' => $my_average
4630
                    );
4631
4632
                    $last_connection = Tracking:: get_last_connection_date_on_the_course(
4633
                        $user_id,
4634
                        $courseInfo,
4635
                        $session_id_from_get
4636
                    );
4637
4638
                    $progress = Tracking::get_avg_student_progress(
4639
                        $user_id,
4640
                        $course_code,
4641
                        array(),
4642
                        $session_id_from_get
4643
                    );
4644
4645
                    $total_time_login = Tracking:: get_time_spent_on_the_course(
4646
                        $user_id,
4647
                        $courseId,
4648
                        $session_id_from_get
4649
                    );
4650
                    $time = api_time_to_hms($total_time_login);
4651
4652
                    $percentage_score = Tracking::get_avg_student_score(
4653
                        $user_id,
4654
                        $course_code,
4655
                        array(),
4656
                        $session_id_from_get
4657
                    );
4658
                    $courseCodeFromGet = isset($_GET['course']) ? $_GET['course'] : null;
4659
4660
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
4661
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D" >';
4662
                    } else {
4663
                        $html .= '<tr class="row_even">';
4664
                    }
4665
4666
                    $url = api_get_course_url($course_code, $session_id_from_get);
4667
                    $course_url = Display::url($course_title, $url, array('target' => SESSION_LINK_TARGET));
4668
4669
                    $html .= Display::tag('td', $course_url);
4670
                    $html .= Display::tag('td', $stats_array[$course_code]['exercises']);
4671
                    $html .= Display::tag('td', $stats_array[$course_code]['unanswered_exercises_by_user']);
4672
                    //$html .= Display::tag('td', $stats_array[$course_code]['done_exercises']);
4673
                    $html .= Display::tag('td', ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']));
4674
4675
                    $html .= Display::tag('td', $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')');
4676
                    $html .= Display::tag('td', $time, array('align'=>'center'));
4677
4678
                    if (is_numeric($progress)) {
4679
                        $progress = $progress.'%';
4680
                    } else {
4681
                        $progress = '0%';
4682
                    }
4683
                    // Progress
4684
                    $html .= Display::tag('td', $progress, array('align'=>'center'));
4685
                    if (is_numeric($percentage_score)) {
4686
                        $percentage_score = $percentage_score.'%';
4687
                    } else {
4688
                        $percentage_score = '0%';
4689
                    }
4690
                    //Score
4691
                    $html .= Display::tag('td', $percentage_score, array('align'=>'center'));
4692
                    $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 4632 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...
4693
4694
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
4695
                        $details = '<a href="#">';
4696
                        $details .=Display::return_icon('2rightarrow_na.png', get_lang('Details'));
4697 View Code Duplication
                    } else {
4698
                        $details = '<a href="'.api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'">';
4699
                        $details .= Display::return_icon('2rightarrow.png', get_lang('Details'));
4700
                    }
4701
                    $details .= '</a>';
4702
                    $html .= Display::tag('td', $details, array('align'=>'center'));
4703
                    $html .= '</tr>';
4704
                }
4705
                $html .= '</table>';
4706
            }
4707
        }
4708
4709
        return $html;
4710
    }
4711
4712
    /**
4713
     * Shows the user detail progress (when clicking in the details link)
4714
     * @param   int     $user_id
4715
     * @param   string  $course_code
4716
     * @param   int     $session_id
4717
     * @return  string  html code
4718
     */
4719
    public static function show_course_detail($user_id, $course_code, $session_id)
4720
    {
4721
        $html = '';
4722
        if (isset($course_code)) {
4723
4724
            $user_id = intval($user_id);
4725
            $session_id = intval($session_id);
4726
            $course = Database::escape_string($course_code);
4727
            $course_info = CourseManager::get_course_information($course);
4728
4729
            $html .= Display::page_subheader($course_info['title']);
4730
            $html .= '<table class="data_table" width="100%">';
4731
4732
            //Course details
4733
            $html .= '
4734
                <tr>
4735
                <th class="head" style="color:#000">'.get_lang('Exercises').'</th>
4736
                <th class="head" style="color:#000">'.get_lang('Attempts').'</th>
4737
                <th class="head" style="color:#000">'.get_lang('BestAttempt').'</th>
4738
                <th class="head" style="color:#000">'.get_lang('Ranking').'</th>
4739
                <th class="head" style="color:#000">'.get_lang('BestResultInCourse').'</th>
4740
                <th class="head" style="color:#000">'.get_lang('Statistics').' '.Display :: return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent'), array('align' => 'absmiddle', 'hspace' => '3px')).'</th>
4741
                </tr>';
4742
4743
            if (empty($session_id)) {
4744
                $user_list = CourseManager::get_user_list_from_course_code(
4745
                    $course,
4746
                    $session_id,
4747
                    null,
4748
                    null,
4749
                    STUDENT
4750
                );
4751
            } else {
4752
                $user_list = CourseManager::get_user_list_from_course_code(
4753
                    $course,
4754
                    $session_id,
4755
                    null,
4756
                    null,
4757
                    0
4758
                );
4759
            }
4760
4761
            // Show exercise results of invisible exercises? see BT#4091
4762
            $exercise_list = ExerciseLib::get_all_exercises(
4763
                $course_info,
4764
                $session_id,
4765
                false,
4766
                null,
4767
                false,
4768
                2
4769
            );
4770
4771
            $to_graph_exercise_result = array();
4772
4773
            if (!empty($exercise_list)) {
4774
                $score = $weighting = $exe_id = 0;
4775
                foreach ($exercise_list as $exercices) {
4776
4777
                    $exercise_obj = new Exercise($course_info['real_id']);
4778
                    $exercise_obj->read($exercices['id']);
4779
                    $visible_return = $exercise_obj->is_visible();
4780
4781
                    $score = $weighting = $attempts = 0;
4782
4783
                    // Getting count of attempts by user
4784
                    $attempts = Event::count_exercise_attempts_by_user(
4785
                        api_get_user_id(),
4786
                        $exercices['id'],
4787
                        $course_info['real_id'],
4788
                        $session_id
4789
                    );
4790
4791
                    $html .= '<tr class="row_even">';
4792
                    $url = api_get_path(WEB_CODE_PATH)."exercice/overview.php?cidReq={$course_info['code']}&id_session=$session_id&exerciseId={$exercices['id']}";
4793
4794
                    if ($visible_return['value'] == true) {
4795
                        $exercices['title'] = Display::url(
4796
                            $exercices['title'],
4797
                            $url,
4798
                            array('target' => SESSION_LINK_TARGET)
4799
                        );
4800
                    }
4801
4802
                    $html .= Display::tag('td', $exercices['title']);
4803
4804
                    // Exercise configuration show results or show only score
4805
                    if ($exercices['results_disabled'] == 0 || $exercices['results_disabled'] == 2) {
4806
                        //For graphics
4807
                        $best_exercise_stats = Event::get_best_exercise_results_by_user(
4808
                            $exercices['id'],
4809
                            $course_info['real_id'],
4810
                            $session_id
4811
                        );
4812
4813
                        $to_graph_exercise_result[$exercices['id']] = array(
4814
                            'title' => $exercices['title'],
4815
                            'data' => $best_exercise_stats
4816
                        );
4817
4818
                        $latest_attempt_url = '';
4819
                        $best_score = $position = $percentage_score_result  = '-';
4820
                        $graph = $normal_graph = null;
4821
4822
                        // Getting best results
4823
                        $best_score_data = ExerciseLib::get_best_attempt_in_course(
4824
                            $exercices['id'],
4825
                            $course_info['real_id'],
4826
                            $session_id
4827
                        );
4828
4829
                        $best_score = '';
4830
                        if (!empty($best_score_data)) {
4831
                            $best_score = ExerciseLib::show_score(
4832
                                $best_score_data['exe_result'],
4833
                                $best_score_data['exe_weighting']
4834
                            );
4835
                        }
4836
4837
                        if ($attempts > 0) {
4838
                            $exercise_stat = ExerciseLib::get_best_attempt_by_user(
4839
                                api_get_user_id(),
4840
                                $exercices['id'],
4841
                                $course_info['real_id'],
4842
                                $session_id
4843
                            );
4844
                            if (!empty($exercise_stat)) {
4845
4846
                                // Always getting the BEST attempt
4847
                                $score          = $exercise_stat['exe_result'];
4848
                                $weighting      = $exercise_stat['exe_weighting'];
4849
                                $exe_id         = $exercise_stat['exe_id'];
4850
4851
                                $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;
4852
                                $percentage_score_result = Display::url(ExerciseLib::show_score($score, $weighting), $latest_attempt_url);
4853
                                $my_score = 0;
4854
                                if (!empty($weighting) && intval($weighting) != 0) {
4855
                                    $my_score = $score/$weighting;
4856
                                }
4857
                                //@todo this function slows the page
4858
                                $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...
4859
4860
                                $graph = self::generate_exercise_result_thumbnail_graph($to_graph_exercise_result[$exercices['id']]);
4861
                                $normal_graph = self::generate_exercise_result_graph($to_graph_exercise_result[$exercices['id']]);
4862
                            }
4863
                        }
4864
                        $html .= Display::div(
4865
                            $normal_graph,
4866
                            array('id'=>'main_graph_'.$exercices['id'],'class'=>'dialog', 'style'=>'display:none')
4867
                        );
4868
4869
                        if (empty($graph)) {
4870
                            $graph = '-';
4871
                        } else {
4872
                            $graph = Display::url(
4873
                                '<img src="' . $graph . '" >',
4874
                                $normal_graph,
4875
                                array(
4876
                                    'id' => $exercices['id'],
4877
                                    'class' => 'expand-image',
4878
                                )
4879
                            );
4880
                        }
4881
4882
                        $html .= Display::tag('td', $attempts, array('align'=>'center'));
4883
                        $html .= Display::tag('td', $percentage_score_result, array('align'=>'center'));
4884
                        $html .= Display::tag('td', $position, array('align'=>'center'));
4885
                        $html .= Display::tag('td', $best_score, array('align'=>'center'));
4886
                        $html .= Display::tag('td', $graph, array('align'=>'center'));
4887
                        //$html .= Display::tag('td', $latest_attempt_url,       array('align'=>'center', 'width'=>'25'));
4888
4889
                    } else {
4890
                        // Exercise configuration NO results
4891
                        $html .= Display::tag('td', $attempts, array('align'=>'center'));
4892
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4893
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4894
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4895
                        $html .= Display::tag('td', '-', array('align'=>'center'));
4896
                    }
4897
                    $html .= '</tr>';
4898
                }
4899
            } else {
4900
                $html .= '<tr><td colspan="5" align="center">'.get_lang('NoEx').'</td></tr>';
4901
            }
4902
            $html .= '</table>';
4903
4904
4905
            // LP table results
4906
            $html .='<table class="data_table">';
4907
            $html .= Display::tag('th', get_lang('Learnpaths'), array('class'=>'head', 'style'=>'color:#000'));
4908
            $html .= Display::tag('th', get_lang('LatencyTimeSpent'), array('class'=>'head', 'style'=>'color:#000'));
4909
            $html .= Display::tag('th', get_lang('Progress'), array('class'=>'head', 'style'=>'color:#000'));
4910
            $html .= Display::tag('th', get_lang('Score'), array('class'=>'head', 'style'=>'color:#000'));
4911
            $html .= Display::tag('th', get_lang('LastConnexion'), array('class'=>'head', 'style'=>'color:#000'));
4912
            $html .= '</tr>';
4913
4914
            $list = new LearnpathList(
4915
                api_get_user_id(),
4916
                $course_info['code'],
4917
                $session_id,
4918
                'publicated_on ASC',
4919
                true,
4920
                null,
4921
                true
4922
            );
4923
4924
            $lp_list = $list->get_flat_list();
4925
4926
            if (!empty($lp_list) > 0) {
4927
                foreach ($lp_list as $lp_id => $learnpath) {
4928
                    $progress = Tracking::get_avg_student_progress($user_id, $course, array($lp_id), $session_id);
4929
                    $last_connection_in_lp = Tracking::get_last_connection_time_in_lp($user_id, $course, $lp_id, $session_id);
4930
                    $time_spent_in_lp = Tracking::get_time_spent_in_lp($user_id, $course, array($lp_id), $session_id);
4931
                    $percentage_score = Tracking::get_avg_student_score($user_id, $course, array($lp_id), $session_id);
4932
                    if (is_numeric($percentage_score)) {
4933
                        $percentage_score = $percentage_score.'%';
4934
                    } else {
4935
                        $percentage_score = '0%';
4936
                    }
4937
4938
                    $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
4939
4940
                    $html .= '<tr class="row_even">';
4941
                    $url = api_get_path(WEB_CODE_PATH)."newscorm/lp_controller.php?cidReq={$course_code}&id_session=$session_id&lp_id=$lp_id&action=view";
4942
4943
                    if ($learnpath['lp_visibility'] == 0) {
4944
                        $html .= Display::tag('td', $learnpath['lp_name']);
4945
                    } else {
4946
                        $html .= Display::tag('td', Display::url($learnpath['lp_name'], $url, array('target'=>SESSION_LINK_TARGET)));
4947
                    }
4948
4949
                    $html .= Display::tag('td', $time_spent_in_lp, array('align'=>'center'));
4950
                    if (is_numeric($progress)) {
4951
                        $progress = $progress.'%';
4952
                    }
4953
                    $html .= Display::tag('td', $progress, array('align'=>'center'));
4954
                    $html .= Display::tag('td', $percentage_score);
4955
4956
                    $last_connection = '-';
4957
                    if (!empty($last_connection_in_lp)) {
4958
                        $last_connection = api_convert_and_format_date($last_connection_in_lp, DATE_TIME_FORMAT_LONG);
4959
                    }
4960
                    $html .= Display::tag('td', $last_connection, array('align'=>'center','width'=>'180px'));
4961
                    $html .= "</tr>";
4962
                }
4963
            } else {
4964
                $html .= '<tr>
4965
                        <td colspan="4" align="center">
4966
                            '.get_lang('NoLearnpath').'
4967
                        </td>
4968
                      </tr>';
4969
            }
4970
            $html .='</table>';
4971
        }
4972
4973
        return $html;
4974
    }
4975
4976
    /**
4977
     * Generates an histogram
4978
     * @param    array    list of exercise names
4979
     * @param    array    my results 0 to 100
4980
     * @param    array    average scores 0-100
4981
     * @return string
4982
     */
4983
    static function generate_session_exercise_graph($names, $my_results, $average)
4984
    {
4985
        /* Create and populate the pData object */
4986
        $myData = new pData();
4987
        $myData->addPoints($names, 'Labels');
4988
        $myData->addPoints($my_results, 'Serie1');
4989
        $myData->addPoints($average, 'Serie2');
4990
        $myData->setSerieWeight('Serie1', 1);
4991
        $myData->setSerieTicks('Serie2', 4);
4992
        $myData->setSerieDescription('Labels', 'Months');
4993
        $myData->setAbscissa('Labels');
4994
        $myData->setSerieDescription('Serie1', get_lang('MyResults'));
4995
        $myData->setSerieDescription('Serie2', get_lang('AverageScore'));
4996
        $myData->setAxisUnit(0, '%');
4997
        $myData->loadPalette(api_get_path(SYS_CODE_PATH) . 'palettes/pchart/default.color', true);
4998
        // Cache definition
4999
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5000
        $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
5001
        $chartHash = $myCache->getHash($myData);
5002
5003
        if ($myCache->isInCache($chartHash)) {
5004
            //if we already created the img
5005
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5006
            $myCache->saveFromCache($chartHash, $imgPath);
5007
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5008
        } else {
5009
            /* Define width, height and angle */
5010
            $mainWidth = 860;
5011
            $mainHeight = 500;
5012
            $angle = 50;
5013
5014
            /* Create the pChart object */
5015
            $myPicture = new pImage($mainWidth, $mainHeight, $myData);
5016
5017
            /* Turn of Antialiasing */
5018
            $myPicture->Antialias = false;
5019
5020
            /* Draw the background */
5021
            $settings = array('R' => 255, 'G' => 255, 'B' => 255);
5022
            $myPicture->drawFilledRectangle(0, 0, $mainWidth, $mainHeight, $settings);
5023
5024
            /* Add a border to the picture */
5025
            $myPicture->drawRectangle(
5026
                0,
5027
                0,
5028
                $mainWidth - 1,
5029
                $mainHeight - 1,
5030
                array('R' => 0, 'G' => 0, 'B' => 0)
5031
            );
5032
5033
            /* Set the default font */
5034
            $myPicture->setFontProperties(
5035
                array(
5036
                    'FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf',
5037
                    'FontSize' => 10)
5038
            );
5039
            /* Write the chart title */
5040
            $myPicture->drawText(
5041
                $mainWidth / 2,
5042
                30,
5043
                get_lang('ExercisesInTimeProgressChart'),
5044
                array(
5045
                    'FontSize' => 12,
5046
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE
5047
                )
5048
            );
5049
5050
            /* Set the default font */
5051
            $myPicture->setFontProperties(
5052
                array(
5053
                    'FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf',
5054
                    'FontSize' => 6
5055
                )
5056
            );
5057
5058
            /* Define the chart area */
5059
            $myPicture->setGraphArea(60, 60, $mainWidth - 60, $mainHeight - 150);
5060
5061
            /* Draw the scale */
5062
            $scaleSettings = array(
5063
                'XMargin' => 10,
5064
                'YMargin' => 10,
5065
                'Floating' => true,
5066
                'GridR' => 200,
5067
                'GridG' => 200,
5068
                'GridB' => 200,
5069
                'DrawSubTicks' => true,
5070
                'CycleBackground' => true,
5071
                'LabelRotation' => $angle,
5072
                'Mode' => SCALE_MODE_ADDALL_START0,
5073
            );
5074
            $myPicture->drawScale($scaleSettings);
5075
5076
            /* Turn on Antialiasing */
5077
            $myPicture->Antialias = true;
5078
5079
            /* Enable shadow computing */
5080
            $myPicture->setShadow(
5081
                true,
5082
                array(
5083
                    'X' => 1,
5084
                    'Y' => 1,
5085
                    'R' => 0,
5086
                    'G' => 0,
5087
                    'B' => 0,
5088
                    'Alpha' => 10
5089
                )
5090
            );
5091
5092
            /* Draw the line chart */
5093
            $myPicture->setFontProperties(
5094
                array(
5095
                    'FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf',
5096
                    'FontSize' => 10
5097
                )
5098
            );
5099
            $myPicture->drawSplineChart();
5100
            $myPicture->drawPlotChart(
5101
                array(
5102
                    'DisplayValues' => true,
5103
                    'PlotBorder' => true,
5104
                    'BorderSize' => 1,
5105
                    'Surrounding' => -60,
5106
                    'BorderAlpha' => 80
5107
                )
5108
            );
5109
5110
            /* Write the chart legend */
5111
            $myPicture->drawLegend(
5112
                $mainWidth / 2 + 50,
5113
                50,
5114
                array(
5115
                    'Style' => LEGEND_BOX,
5116
                    'Mode' => LEGEND_HORIZONTAL,
5117
                    'FontR' => 0,
5118
                    'FontG' => 0,
5119
                    'FontB' => 0,
5120
                    'R' => 220,
5121
                    'G' => 220,
5122
                    'B' => 220,
5123
                    'Alpha' => 100
5124
                )
5125
            );
5126
5127
            $myCache->writeToCache($chartHash, $myPicture);
5128
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5129
            $myCache->saveFromCache($chartHash, $imgPath);
5130
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5131
        }
5132
5133
        $html = '<img src="' . $imgPath . '">';
5134
5135
        return $html;
5136
    }
5137
5138
    /**
5139
     *
5140
     * Returns a thumbnail of the function generate_exercise_result_graph
5141
     * @param  array $attempts
5142
     */
5143
    static function generate_exercise_result_thumbnail_graph($attempts)
5144
    {
5145
        //$exercise_title = $attempts['title'];
5146
        $attempts = $attempts['data'];
5147
        $my_exercise_result_array = $exercise_result = array();
5148
        if (empty($attempts)) {
5149
            return null;
5150
        }
5151
5152 View Code Duplication
        foreach ($attempts as $attempt) {
5153
            if (api_get_user_id() == $attempt['exe_user_id']) {
5154
                if ($attempt['exe_weighting'] != 0 ) {
5155
                    $my_exercise_result_array[]= $attempt['exe_result']/$attempt['exe_weighting'];
5156
                }
5157
            } else {
5158
                if ($attempt['exe_weighting'] != 0 ) {
5159
                    $exercise_result[]=  $attempt['exe_result']/$attempt['exe_weighting'];
5160
                }
5161
            }
5162
        }
5163
5164
        //Getting best result
5165
        rsort($my_exercise_result_array);
5166
        $my_exercise_result = 0;
5167
        if (isset($my_exercise_result_array[0])) {
5168
            $my_exercise_result = $my_exercise_result_array[0] *100;
5169
        }
5170
5171
        $max     = 100;
5172
        $pieces  = 5 ;
5173
        $part    = round($max / $pieces);
5174
        $x_axis = array();
5175
        $final_array = array();
5176
        $my_final_array = array();
5177
5178 View Code Duplication
        for ($i=1; $i <=$pieces; $i++) {
5179
            $sum = 1;
5180
            if ($i == 1) {
5181
                $sum = 0;
5182
            }
5183
            $min = ($i-1)*$part + $sum;
5184
            $max = ($i)*$part;
5185
            $x_axis[]= $min." - ".$max;
5186
            $count = 0;
5187
            foreach($exercise_result as $result) {
5188
                $percentage = $result*100;
5189
                //echo $percentage.' - '.$min.' - '.$max."<br />";
5190
                if ($percentage >= $min && $percentage <= $max) {
5191
                    //echo ' is > ';
5192
                    $count++;
5193
                }
5194
            }
5195
            //echo '<br />';
5196
            $final_array[]= $count;
5197
5198
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5199
                $my_final_array[] = 1;
5200
            } else {
5201
                $my_final_array[] = 0;
5202
            }
5203
        }
5204
5205
        //Fix to remove the data of the user with my data
5206 View Code Duplication
        for($i = 0; $i<=count($my_final_array); $i++) {
5207
            if (!empty($my_final_array[$i])) {
5208
                $my_final_array[$i] =  $final_array[$i] + 1; //Add my result
5209
                $final_array[$i] = 0;
5210
            }
5211
        }
5212
5213
        // Dataset definition
5214
        $dataSet = new pData();
5215
        $dataSet->addPoints($final_array, 'Serie1');
5216
        $dataSet->addPoints($my_final_array, 'Serie2');
5217
        $dataSet->normalize(100, "%");
5218
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH) . 'palettes/pchart/default.color', true);
5219
5220
        // Cache definition
5221
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5222
        $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
5223
        $chartHash = $myCache->getHash($dataSet);
5224
        if ($myCache->isInCache($chartHash)) {
5225
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5226
            $myCache->saveFromCache($chartHash, $imgPath);
5227
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5228
        } else {
5229
            /* Create the pChart object */
5230
            $widthSize = 80;
5231
            $heightSize = 35;
5232
            $fontSize = 2;
5233
5234
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5235
5236
            /* Turn of Antialiasing */
5237
            $myPicture->Antialias = false;
5238
5239
            /* Add a border to the picture */
5240
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, array('R' => 0, 'G' => 0, 'B' => 0));
5241
5242
            /* Set the default font */
5243
            $myPicture->setFontProperties(array('FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf', 'FontSize' => $fontSize));
5244
5245
            /* Do not write the chart title */
5246
5247
            /* Define the chart area */
5248
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
5249
5250
            /* Draw the scale */
5251
            $scaleSettings = array(
5252
                'GridR' => 200,
5253
                'GridG' => 200,
5254
                'GridB' => 200,
5255
                'DrawSubTicks' => true,
5256
                'CycleBackground' => true,
5257
                'Mode' => SCALE_MODE_MANUAL,
5258
                'ManualScale' => array(
5259
                    '0' => array(
5260
                        'Min' => 0,
5261
                        'Max' => 100
5262
                    )
5263
                )
5264
            );
5265
            $myPicture->drawScale($scaleSettings);
5266
5267
            /* Turn on shadow computing */
5268
            $myPicture->setShadow(
5269
                true,
5270
                array(
5271
                    'X' => 1,
5272
                    'Y' => 1,
5273
                    'R' => 0,
5274
                    'G' => 0,
5275
                    'B' => 0,
5276
                    'Alpha' => 10
5277
                )
5278
            );
5279
5280
            /* Draw the chart */
5281
            $myPicture->setShadow(
5282
                true,
5283
                array(
5284
                    'X' => 1,
5285
                    'Y' => 1,
5286
                    'R' => 0,
5287
                    'G' => 0,
5288
                    'B' => 0,
5289
                    'Alpha' => 10
5290
                )
5291
            );
5292
            $settings = array(
5293
                'DisplayValues' => true,
5294
                'DisplaySize' => $fontSize,
5295
                'DisplayR' => 0,
5296
                'DisplayG' => 0,
5297
                'DisplayB' => 0,
5298
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5299
                'Gradient' => false,
5300
                'Surrounding' => 5,
5301
                'InnerSurrounding' => 5
5302
            );
5303
            $myPicture->drawStackedBarChart($settings);
5304
5305
            /* Save and write in cache */
5306
            $myCache->writeToCache($chartHash, $myPicture);
5307
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5308
            $myCache->saveFromCache($chartHash, $imgPath);
5309
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5310
        }
5311
5312
        return $imgPath;
5313
    }
5314
5315
    /**
5316
     * Generates a big graph with the number of best results
5317
     * @param	array
5318
     */
5319
    static function generate_exercise_result_graph($attempts)
5320
    {
5321
        $exercise_title = strip_tags($attempts['title']);
5322
        $attempts       = $attempts['data'];
5323
        $my_exercise_result_array = $exercise_result = array();
5324
        if (empty($attempts)) {
5325
            return null;
5326
        }
5327 View Code Duplication
        foreach ($attempts as $attempt) {
5328
            if (api_get_user_id() == $attempt['exe_user_id']) {
5329
                if ($attempt['exe_weighting'] != 0 ) {
5330
                    $my_exercise_result_array[]= $attempt['exe_result']/$attempt['exe_weighting'];
5331
                }
5332
            } else {
5333
                if ($attempt['exe_weighting'] != 0 ) {
5334
                    $exercise_result[]=  $attempt['exe_result']/$attempt['exe_weighting'];
5335
                }
5336
            }
5337
        }
5338
5339
        //Getting best result
5340
        rsort($my_exercise_result_array);
5341
        $my_exercise_result = 0;
5342
        if (isset($my_exercise_result_array[0])) {
5343
            $my_exercise_result = $my_exercise_result_array[0] *100;
5344
        }
5345
5346
        $max = 100;
5347
        $pieces = 5 ;
5348
        $part = round($max / $pieces);
5349
        $x_axis = array();
5350
        $final_array = array();
5351
        $my_final_array = array();
5352
5353 View Code Duplication
        for ($i=1; $i <=$pieces; $i++) {
5354
            $sum = 1;
5355
            if ($i == 1) {
5356
                $sum = 0;
5357
            }
5358
            $min = ($i-1)*$part + $sum;
5359
            $max = ($i)*$part;
5360
            $x_axis[]= $min." - ".$max;
5361
            $count = 0;
5362
            foreach($exercise_result as $result) {
5363
                $percentage = $result*100;
5364
                if ($percentage >= $min && $percentage <= $max) {
5365
                    $count++;
5366
                }
5367
            }
5368
            $final_array[]= $count;
5369
5370
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5371
                $my_final_array[] = 1;
5372
            } else {
5373
                $my_final_array[] = 0;
5374
            }
5375
        }
5376
5377
        //Fix to remove the data of the user with my data
5378
5379 View Code Duplication
        for($i = 0; $i<=count($my_final_array); $i++) {
5380
            if (!empty($my_final_array[$i])) {
5381
                $my_final_array[$i] =  $final_array[$i] + 1; //Add my result
5382
                $final_array[$i] = 0;
5383
            }
5384
        }
5385
5386
        // Dataset definition
5387
        $dataSet = new pData();
5388
        $dataSet->addPoints($final_array, 'Serie1');
5389
        $dataSet->addPoints($my_final_array, 'Serie2');
5390
        $dataSet->addPoints($x_axis, 'Serie3');
5391
5392
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
5393
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
5394
        $dataSet->setAbscissa('Serie3');
5395
5396
        $dataSet->setXAxisName(get_lang('Score'));
5397
        $dataSet->normalize(100, "%");
5398
5399
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH) . 'palettes/pchart/default.color', true);
5400
5401
        // Cache definition
5402
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5403
        $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
5404
        $chartHash = $myCache->getHash($dataSet);
5405
5406
        if ($myCache->isInCache($chartHash)) {
5407
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5408
            $myCache->saveFromCache($chartHash, $imgPath);
5409
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5410
        } else {
5411
            /* Create the pChart object */
5412
            $widthSize = 480;
5413
            $heightSize = 250;
5414
            $fontSize = 8;
5415
5416
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5417
5418
            /* Turn of Antialiasing */
5419
            $myPicture->Antialias = false;
5420
5421
            /* Add a border to the picture */
5422
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, array('R' => 0, 'G' => 0, 'B' => 0));
5423
5424
            /* Set the default font */
5425
            $myPicture->setFontProperties(array('FontName' => api_get_path(SYS_FONTS_PATH) . 'opensans/OpenSans-Regular.ttf', 'FontSize' => 10));
5426
5427
            /* Write the chart title */
5428
            $myPicture->drawText(
5429
                250,
5430
                20,
5431
                $exercise_title,
5432
                array(
5433
                    'FontSize' => 12,
5434
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE
5435
                )
5436
            );
5437
5438
            /* Define the chart area */
5439
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
5440
5441
            /* Draw the scale */
5442
            $scaleSettings = array(
5443
                'GridR' => 200,
5444
                'GridG' => 200,
5445
                'GridB' => 200,
5446
                'DrawSubTicks' => true,
5447
                'CycleBackground' => true,
5448
                'Mode' => SCALE_MODE_MANUAL,
5449
                'ManualScale' => array(
5450
                    '0' => array(
5451
                        'Min' => 0,
5452
                        'Max' => 100
5453
                    )
5454
                )
5455
            );
5456
            $myPicture->drawScale($scaleSettings);
5457
5458
            /* Turn on shadow computing */
5459
            $myPicture->setShadow(true, array('X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10));
5460
5461
            /* Draw the chart */
5462
            $myPicture->setShadow(true, array('X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10));
5463
            $settings = array(
5464
                'DisplayValues' => true,
5465
                'DisplaySize' => $fontSize,
5466
                'DisplayR' => 0,
5467
                'DisplayG' => 0,
5468
                'DisplayB' => 0,
5469
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
5470
                'Gradient' => false,
5471
                'Surrounding' => 30,
5472
                'InnerSurrounding' => 25
5473
            );
5474
            $myPicture->drawStackedBarChart($settings);
5475
5476
            $legendSettings = array(
5477
                'Mode' => LEGEND_HORIZONTAL,
5478
                'Style' => LEGEND_NOBORDER,
5479
            );
5480
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
5481
5482
            /* Write and save into cache */
5483
            $myCache->writeToCache($chartHash, $myPicture);
5484
            $imgPath = api_get_path(SYS_ARCHIVE_PATH) . $chartHash;
5485
            $myCache->saveFromCache($chartHash, $imgPath);
5486
            $imgPath = api_get_path(WEB_ARCHIVE_PATH) . $chartHash;
5487
        }
5488
5489
        return $imgPath;
5490
    }
5491
5492
    /**
5493
    * @param FormValidator $form
5494
    * @return mixed
5495
    */
5496
    public static function setUserSearchForm($form)
5497
    {
5498
        global $_configuration;
5499
        $form->addElement('text', 'keyword', get_lang('Keyword'));
5500
        $form->addElement(
5501
            'select',
5502
            'active',
5503
            get_lang('Status'),
5504
            array(1 => get_lang('Active'), 0 => get_lang('Inactive'))
5505
        );
5506
5507
        $form->addElement(
5508
            'select',
5509
            'sleeping_days',
5510
            get_lang('InactiveDays'),
5511
            array(
5512
                '',
5513
                1 => 1,
5514
                5 => 5,
5515
                15 => 15,
5516
                30 => 30,
5517
                60 => 60,
5518
                90 => 90,
5519
                120 => 120,
5520
            )
5521
        );
5522
5523
        $form->addButtonSearch(get_lang('Search'));
5524
5525
        return $form;
5526
    }
5527
5528
    /**
5529
     * Get the progress of a exercise
5530
     * @param   int $sessionId  The session ID (session.id)
5531
     * @param   int $courseId   The course ID (course.id)
5532
     * @param   int $exerciseId The quiz ID (c_quiz.id)
5533
     * @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...
5534
     * @param   array   $options    An array of options you can pass to the query (limit, where and order)
5535
     * @return array An array with the data of exercise(s) progress
5536
     */
5537
    public static function get_exercise_progress(
5538
        $sessionId = 0,
5539
        $courseId = 0,
5540
        $exerciseId = 0,
5541
        $date_from = null,
5542
        $date_to = null,
5543
        $options = array()
5544
    ) {
5545
        $sessionId  = intval($sessionId);
5546
        $courseId   = intval($courseId);
5547
        $exerciseId = intval($exerciseId);
5548
        $date_from  = Database::escape_string($date_from);
5549
        $date_to    = Database::escape_string($date_to);
5550
        /*
5551
         * This method gets the data by blocks, as previous attempts at one single
5552
         * query made it take ages. The logic of query division is described below
5553
         */
5554
        // Get tables names
5555
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
5556
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
5557
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
5558
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
5559
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
5560
        $ttrack_exercises  = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
5561
        $ttrack_attempt    = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
5562
5563
        $sessions = array();
5564
        $courses = array();
5565
        // if session ID is defined but course ID is empty, get all the courses
5566
        // from that session
5567
        if (!empty($sessionId) && empty($courseId)) {
5568
            // $courses is an array of course int id as index and course details hash as value
5569
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
5570
            $sessions[$sessionId] = api_get_session_info($sessionId);
5571
        } elseif (empty($sessionId) && !empty($courseId)) {
5572
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
5573
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
5574
            $course = api_get_course_info_by_id($courseId);
5575
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
5576
            $courses[$courseId] = $course;
5577
            foreach ($sessionsTemp as $sessionItem) {
5578
                $sessions[$sessionItem['id']] = $sessionItem;
5579
            }
5580
        } elseif (!empty($courseId) && !empty($sessionId)) {
5581
            //none is empty
5582
            $course = api_get_course_info_by_id($courseId);
5583
            $courses[$courseId] = array($course['code']);
5584
            $courses[$courseId]['code'] = $course['code'];
5585
            $sessions[$sessionId] = api_get_session_info($sessionId);
5586
        } else {
5587
            //both are empty, not enough data, return an empty array
5588
            return array();
5589
        }
5590
        // Now we have two arrays of courses and sessions with enough data to proceed
5591
        // If no course could be found, we shouldn't return anything.
5592
        // Sessions can be empty (then we only return the pure-course-context results)
5593
        if (count($courses) < 1) {
5594
            return array();
5595
        }
5596
5597
        $data = array();
5598
        // The following loop is less expensive than what it seems:
5599
        // - if a course was defined, then we only loop through sessions
5600
        // - if a session was defined, then we only loop through courses
5601
        // - if a session and a course were defined, then we only loop once
5602
        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...
5603
            $where = '';
5604
            $whereParams = array();
5605
            $whereSessionParams = '';
5606
            if (count($sessions > 0)) {
5607
                foreach ($sessions as $sessionIdx => $sessionData) {
5608
                    if (!empty($sessionIdx)) {
5609
                        $whereSessionParams .= $sessionIdx.',';
5610
                    }
5611
                }
5612
                $whereSessionParams = substr($whereSessionParams,0,-1);
5613
            }
5614
5615
            if (!empty($exerciseId)) {
5616
                $exerciseId = intval($exerciseId);
5617
                $where .= ' AND q.id = %d ';
5618
                $whereParams[] = $exerciseId;
5619
            }
5620
5621
            /*
5622
             * This feature has been disabled for now, to avoid having to
5623
             * join two very large tables
5624
            //2 = show all questions (wrong and correct answered)
5625
            if ($answer != 2) {
5626
                $answer = intval($answer);
5627
                //$where .= ' AND qa.correct = %d';
5628
                //$whereParams[] = $answer;
5629
            }
5630
            */
5631
5632
            $limit = '';
5633
            if (!empty($options['limit'])) {
5634
                $limit = " LIMIT ".$options['limit'];
5635
            }
5636
5637
            if (!empty($options['where'])) {
5638
                $where .= ' AND '.Database::escape_string($options['where']);
5639
            }
5640
5641
            $order = '';
5642
            if (!empty($options['order'])) {
5643
                $order = " ORDER BY ".$options['order'];
5644
            }
5645
5646
            if (!empty($date_to) && !empty($date_from)) {
5647
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
5648
            }
5649
5650
            $sql = "SELECT
5651
                te.session_id,
5652
                ta.id as attempt_id,
5653
                te.exe_user_id as user_id,
5654
                te.exe_id as exercise_attempt_id,
5655
                ta.question_id,
5656
                ta.answer as answer_id,
5657
                ta.tms as time,
5658
                te.exe_exo_id as quiz_id,
5659
                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
5660
                q.title as quiz_title,
5661
                qq.description as description
5662
                FROM $ttrack_exercises te
5663
                INNER JOIN $ttrack_attempt ta ON ta.exe_id = te.exe_id
5664
                INNER JOIN $tquiz q ON q.id = te.exe_exo_id
5665
                INNER JOIN $tquiz_rel_question rq ON rq.exercice_id = q.id AND rq.c_id = q.c_id
5666
                INNER JOIN $tquiz_question qq
5667
                ON
5668
                    qq.id = rq.question_id AND
5669
                    qq.c_id = rq.c_id AND
5670
                    qq.position = rq.question_order AND
5671
                    ta.question_id = rq.question_id
5672
                WHERE
5673
                    te.c_id = $courseIdx ".(empty($whereSessionParams)?'':"AND te.session_id IN ($whereSessionParams)")."
5674
                    AND q.c_id = $courseIdx
5675
                    $where $order $limit";
5676
            $sql_query = vsprintf($sql, $whereParams);
5677
5678
            // Now browse through the results and get the data
5679
            $rs = Database::query($sql_query);
5680
            $userIds = array();
5681
            $questionIds = array();
5682
            $answerIds = array();
5683
            while ($row = Database::fetch_array($rs)) {
5684
                //only show if exercise is visible
5685
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
5686
                    $userIds[$row['user_id']] = $row['user_id'];
5687
                    $questionIds[$row['question_id']] = $row['question_id'];
5688
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
5689
                    $row['session'] = $sessions[$row['session_id']];
5690
                    $data[] = $row;
5691
                }
5692
            }
5693
            // Now fill questions data. Query all questions and answers for this test to avoid
5694
            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
5695
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
5696
                            FROM $tquiz_question tq, $tquiz_answer tqa
5697
                            WHERE
5698
                                tqa.question_id = tq.id AND
5699
                                tqa.c_id = tq.c_id AND
5700
                                tq.c_id = $courseIdx AND
5701
                                tq.id IN (".implode(',', $questionIds).")";
5702
5703
            $resQuestions = Database::query($sqlQuestions);
5704
            $answer = array();
5705
            $question = array();
5706
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
5707
                $questionId = $rowQuestion['question_id'];
5708
                $answerId = $rowQuestion['answer_id'];
5709
                $answer[$questionId][$answerId] = array(
5710
                    'position' => $rowQuestion['position'],
5711
                    'question' => $rowQuestion['question'],
5712
                    'answer' => $rowQuestion['answer'],
5713
                    'correct' => $rowQuestion['correct']
5714
                );
5715
                $question[$questionId]['question'] = $rowQuestion['question'];
5716
            }
5717
5718
            // Now fill users data
5719
            $sqlUsers = "SELECT user_id, username, lastname, firstname
5720
                         FROM $tuser
5721
                         WHERE user_id IN (".implode(',',$userIds).")";
5722
            $resUsers = Database::query($sqlUsers);
5723
            while ($rowUser = Database::fetch_assoc($resUsers)) {
5724
                $users[$rowUser['user_id']] = $rowUser;
5725
            }
5726
5727
            foreach ($data as $id => $row) {
5728
                $rowQuestId = $row['question_id'];
5729
                $rowAnsId = $row['answer_id'];
5730
5731
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
5732
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
5733
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
5734
                $data[$id]['username'] = $users[$row['user_id']]['username'];
5735
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
5736
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
5737
                $data[$id]['question'] = $question[$rowQuestId]['question'];
5738
                $data[$id]['question_id'] = $rowQuestId;
5739
                $data[$id]['description'] = $row['description'];
5740
            }
5741
            /*
5742
            The minimum expected array structure at the end is:
5743
            attempt_id,
5744
            session name,
5745
            exercise_id,
5746
            quiz_title,
5747
            username,
5748
            lastname,
5749
            firstname,
5750
            time,
5751
            question_id,
5752
            question,
5753
            answer,
5754
            */
5755
        }
5756
        return $data;
5757
    }
5758
5759
    /**
5760
     * @param User $user
5761
     * @param string $tool
5762
     * @param Course $course
5763
     * @param Session|null $session Optional.
5764
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
5765
     * @throws \Doctrine\ORM\NonUniqueResultException
5766
     */
5767
    public static function getLastStudentPublication(User $user, $tool, Course $course, Session $session = null)
5768
    {
5769
        return Database::getManager()
5770
            ->createQuery("
5771
                SELECT csp
5772
                FROM ChamiloCourseBundle:CStudentPublication csp
5773
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
5774
                    WITH (
5775
                        csp.iid = cip.ref AND
5776
                        csp.sessionId = cip.session AND
5777
                        csp.cId = cip.course AND
5778
                        csp.userId = cip.lasteditUserId
5779
                    )
5780
                WHERE
5781
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
5782
                ORDER BY csp.iid DESC
5783
            ")
5784
            ->setMaxResults(1)
5785
            ->setParameters([
5786
                'tool' => $tool,
5787
                'session' => $session,
5788
                'course' => $course,
5789
                'user' => $user
5790
            ])
5791
            ->getOneOrNullResult();
5792
    }
5793
}
5794
5795
/**
5796
 * @todo move into a proper file
5797
 * @package chamilo.tracking
5798
 */
5799
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...
5800
{
5801
    /**
5802
     * @return mixed
5803
     */
5804
    public static function count_item_resources()
5805
    {
5806
        $session_id = api_get_session_id();
5807
        $course_id = api_get_course_int_id();
5808
5809
    	$table_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
5810
    	$table_user = Database :: get_main_table(TABLE_MAIN_USER);
5811
5812
    	$sql = "SELECT count(tool) AS total_number_of_items
5813
    	        FROM $table_item_property track_resource, $table_user user
5814
    	        WHERE
5815
                    track_resource.c_id = $course_id AND
5816
                    track_resource.insert_user_id = user.user_id AND
5817
                    session_id " .(empty($session_id) ? ' IS NULL ' : " = $session_id ");
5818
5819
    	if (isset($_GET['keyword'])) {
5820
    		$keyword = Database::escape_string(trim($_GET['keyword']));
5821
    		$sql .= " AND (
5822
    		            user.username LIKE '%".$keyword."%' OR
5823
    		            lastedit_type LIKE '%".$keyword."%' OR
5824
    		            tool LIKE '%".$keyword."%'
5825
                    )";
5826
    	}
5827
5828
    	$sql .= " AND tool IN (
5829
    	            'document',
5830
    	            'learnpath',
5831
    	            'quiz',
5832
    	            'glossary',
5833
    	            'link',
5834
    	            'course_description',
5835
    	            'announcement',
5836
    	            'thematic',
5837
    	            'thematic_advance',
5838
    	            'thematic_plan'
5839
                )";
5840
    	$res = Database::query($sql);
5841
    	$obj = Database::fetch_object($res);
5842
5843
    	return $obj->total_number_of_items;
5844
    }
5845
5846
    /**
5847
     * @param $from
5848
     * @param $number_of_items
5849
     * @param $column
5850
     * @param $direction
5851
     * @return array
5852
     */
5853
    public static function get_item_resources_data($from, $number_of_items, $column, $direction)
5854
    {
5855
        $session_id = api_get_session_id();
5856
        $course_id = api_get_course_int_id();
5857
5858
    	$table_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
5859
    	$table_user = Database :: get_main_table(TABLE_MAIN_USER);
5860
    	$table_session = Database :: get_main_table(TABLE_MAIN_SESSION);
5861
    	$session_id = intval($session_id);
5862
5863
    	$sql = "SELECT
5864
                    tool as col0,
5865
                    lastedit_type as col1,
5866
                    ref as ref,
5867
                    user.username as col3,
5868
                    insert_date as col5,
5869
                    visibility as col6,
5870
                    user.user_id as user_id
5871
                FROM $table_item_property track_resource, $table_user user
5872
                WHERE
5873
                  track_resource.c_id = $course_id AND
5874
                  track_resource.insert_user_id = user.user_id AND
5875
                  session_id " .(empty($session_id) ? ' IS NULL ' : " = $session_id ");
5876
5877
    	if (isset($_GET['keyword'])) {
5878
    		$keyword = Database::escape_string(trim($_GET['keyword']));
5879
    		$sql .= " AND (
5880
    		            user.username LIKE '%".$keyword."%' OR
5881
    		            lastedit_type LIKE '%".$keyword."%' OR
5882
    		            tool LIKE '%".$keyword."%'
5883
                     ) ";
5884
    	}
5885
5886
    	$sql .= " AND tool IN (
5887
    	            'document',
5888
    	            'learnpath',
5889
    	            'quiz',
5890
    	            'glossary',
5891
    	            'link',
5892
    	            'course_description',
5893
    	            'announcement',
5894
    	            'thematic',
5895
    	            'thematic_advance',
5896
    	            'thematic_plan'
5897
                )";
5898
5899
    	if ($column == 0) {
5900
    		$column = '0';
5901
    	}
5902
    	if ($column != '' && $direction != '') {
5903
    		if ($column != 2 && $column != 4) {
5904
    			$sql .= " ORDER BY col$column $direction";
5905
    		}
5906
    	} else {
5907
    		$sql .= " ORDER BY col5 DESC ";
5908
    	}
5909
5910
        $from = intval($from);
5911
        $number_of_items = intval($number_of_items);
5912
5913
    	$sql .= " LIMIT $from, $number_of_items ";
5914
5915
    	$res = Database::query($sql);
5916
    	$resources = array();
5917
    	$thematic_tools = array('thematic', 'thematic_advance', 'thematic_plan');
5918
    	while ($row = Database::fetch_array($res)) {
5919
    		$ref = $row['ref'];
5920
    		$table_name = TrackingCourseLog::get_tool_name_table($row['col0']);
5921
    		$table_tool = Database :: get_course_table($table_name['table_name']);
5922
5923
    		$id = $table_name['id_tool'];
5924
    		$recorset = false;
5925
5926
    		if (in_array($row['col0'], array('thematic_plan', 'thematic_advance'))) {
5927
    			$tbl_thematic = Database :: get_course_table(TABLE_THEMATIC);
5928
    			$sql = "SELECT thematic_id FROM $table_tool
5929
    			        WHERE c_id = $course_id AND id = $ref";
5930
    			$rs_thematic  = Database::query($sql);
5931 View Code Duplication
    			if (Database::num_rows($rs_thematic)) {
5932
    				$row_thematic = Database::fetch_array($rs_thematic);
5933
    				$thematic_id = $row_thematic['thematic_id'];
5934
5935
                    $sql = "SELECT session.id, session.name, user.username
5936
                            FROM $tbl_thematic t, $table_session session, $table_user user
5937
                            WHERE
5938
                              t.c_id = $course_id AND
5939
                              t.session_id = session.id AND
5940
                              session.id_coach = user.user_id AND
5941
                              t.id = $thematic_id";
5942
    				$recorset = Database::query($sql);
5943
    			}
5944
    		} else {
5945
                $sql = "SELECT session.id, session.name, user.username
5946
                          FROM $table_tool tool, $table_session session, $table_user user
5947
    			          WHERE
5948
    			              tool.c_id = $course_id AND
5949
    			              tool.session_id = session.id AND
5950
    			              session.id_coach = user.user_id AND
5951
    			              tool.$id = $ref";
5952
    			$recorset = Database::query($sql);
5953
    		}
5954
5955
    		if (!empty($recorset)) {
5956
    			$obj = Database::fetch_object($recorset);
5957
5958
    			$name_session = '';
5959
    			$coach_name = '';
5960
    			if (!empty($obj)) {
5961
    				$name_session = $obj->name;
5962
    				$coach_name   = $obj->username;
5963
    			}
5964
5965
    			$url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool'];
5966
    			$row[0] = '';
5967
    			if ($row['col6'] != 2) {
5968
    				if (in_array($row['col0'], $thematic_tools)) {
5969
5970
    					$exp_thematic_tool = explode('_', $row['col0']);
5971
    					$thematic_tool_title = '';
5972
    					if (is_array($exp_thematic_tool)) {
5973
    						foreach ($exp_thematic_tool as $exp) {
5974
    							$thematic_tool_title .= api_ucfirst($exp);
5975
    						}
5976
    					} else {
5977
    						$thematic_tool_title = api_ucfirst($row['col0']);
5978
    					}
5979
5980
    					$row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'&action=thematic_details">'.get_lang($thematic_tool_title).'</a>';
5981
    				} else {
5982
    					$row[0] = '<a href="'.$url_tool.'?'.api_get_cidreq().'">'.get_lang('Tool'.api_ucfirst($row['col0'])).'</a>';
5983
    				}
5984
    			} else {
5985
    				$row[0] = api_ucfirst($row['col0']);
5986
    			}
5987
    			$row[1] = get_lang($row[1]);
5988
    			$row[6] = api_convert_and_format_date($row['col5'], null, date_default_timezone_get());
5989
    			$row[5] = '';
5990
    			//@todo Improve this code please
5991
    			switch ($table_name['table_name']) {
5992
    				case 'document' :
5993
    					$sql = "SELECT tool.title as title FROM $table_tool tool
5994
                                WHERE c_id = $course_id AND id = $ref";
5995
    					$rs_document = Database::query($sql);
5996
    					$obj_document = Database::fetch_object($rs_document);
5997
    					$row[5] = $obj_document->title;
5998
5999
    					break;
6000 View Code Duplication
    				case 'announcement':
6001
                        $sql = "SELECT title FROM $table_tool
6002
                                WHERE c_id = $course_id AND id = $ref";
6003
    					$rs_document = Database::query($sql);
6004
    					$obj_document = Database::fetch_object($rs_document);
6005
                        if ($obj_document) {
6006
                            $row[5] = $obj_document->title;
6007
                        }
6008
    					break;
6009
    				case 'glossary':
6010
                        $sql = "SELECT name FROM $table_tool
6011
    					        WHERE c_id = $course_id AND glossary_id = $ref";
6012
    					$rs_document = Database::query($sql);
6013
    					$obj_document = Database::fetch_object($rs_document);
6014
                        if ($obj_document) {
6015
                            $row[5] = $obj_document->name;
6016
                        }
6017
    					break;
6018
    				case 'lp':
6019
                        $sql = "SELECT name
6020
                                FROM $table_tool WHERE c_id = $course_id AND id = $ref";
6021
    					$rs_document = Database::query($sql);
6022
    					$obj_document = Database::fetch_object($rs_document);
6023
    					$row[5] = $obj_document->name;
6024
    					break;
6025
    				case 'quiz':
6026
                        $sql = "SELECT title FROM $table_tool
6027
                                WHERE c_id = $course_id AND id = $ref";
6028
    					$rs_document = Database::query($sql);
6029
    					$obj_document = Database::fetch_object($rs_document);
6030
                        if ($obj_document) {
6031
                            $row[5] = $obj_document->title;
6032
                        }
6033
    					break;
6034 View Code Duplication
    				case 'course_description':
6035
                        $sql = "SELECT title FROM $table_tool
6036
                                WHERE c_id = $course_id AND id = $ref";
6037
    					$rs_document = Database::query($sql);
6038
    					$obj_document = Database::fetch_object($rs_document);
6039
                        if ($obj_document) {
6040
                            $row[5] = $obj_document->title;
6041
                        }
6042
    					break;
6043 View Code Duplication
    				case 'thematic':
6044
    					$rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
6045
    					if (Database::num_rows($rs) > 0) {
6046
    						$obj = Database::fetch_object($rs);
6047
    						$row[5] = $obj->title;
6048
    					}
6049
    					break;
6050 View Code Duplication
    				case 'thematic_advance':
6051
    					$rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref");
6052
    					if (Database::num_rows($rs) > 0) {
6053
    						$obj = Database::fetch_object($rs);
6054
    						$row[5] = $obj->content;
6055
    					}
6056
    					break;
6057 View Code Duplication
    				case 'thematic_plan':
6058
    					$rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref");
6059
    					if (Database::num_rows($rs) > 0) {
6060
    						$obj = Database::fetch_object($rs);
6061
    						$row[5] = $obj->title;
6062
    					}
6063
    					break;
6064
    				default:
6065
    					break;
6066
    			}
6067
6068
    			$row2 = $name_session;
6069
    			if (!empty($coach_name)) {
6070
    				$row2 .= '<br />'.get_lang('Coach').': '.$coach_name;
6071
    			}
6072
    			$row[2] = $row2;
6073
                if (!empty($row['col3'])) {
6074
                    $userInfo = api_get_user_info($row['user_id']);
6075
6076
                    $row['col3'] = Display::url(
6077
                        $row['col3'],
6078
                        $userInfo['profile_url']
6079
                    );
6080
                    $row[3] = $row['col3'];
6081
6082
                    $ip = TrackingUserLog::get_ip_from_user_event($row['user_id'], $row['col5'], true);
6083
                    if (empty($ip)) {
6084
                        $ip = get_lang('Unknown');
6085
                    }
6086
                    $row[4] = $ip;
6087
                }
6088
6089
    			$resources[] = $row;
6090
    		}
6091
    	}
6092
6093
    	return $resources;
6094
    }
6095
6096
    /**
6097
     * @param string $tool
6098
     *
6099
     * @return array
6100
     */
6101
    public static function get_tool_name_table($tool)
6102
    {
6103
    	switch ($tool) {
6104
    		case 'document':
6105
    			$table_name = TABLE_DOCUMENT;
6106
    			$link_tool = 'document/document.php';
6107
    			$id_tool = 'id';
6108
    			break;
6109
    		case 'learnpath':
6110
    			$table_name = TABLE_LP_MAIN;
6111
    			$link_tool = 'newscorm/lp_controller.php';
6112
    			$id_tool = 'id';
6113
    			break;
6114
    		case 'quiz':
6115
    			$table_name = TABLE_QUIZ_TEST;
6116
    			$link_tool = 'exercice/exercice.php';
6117
    			$id_tool = 'id';
6118
    			break;
6119
    		case 'glossary':
6120
    			$table_name = TABLE_GLOSSARY;
6121
    			$link_tool = 'glossary/index.php';
6122
    			$id_tool = 'glossary_id';
6123
    			break;
6124
    		case 'link':
6125
    			$table_name = TABLE_LINK;
6126
    			$link_tool = 'link/link.php';
6127
    			$id_tool = 'id';
6128
    			break;
6129
    		case 'course_description':
6130
    			$table_name = TABLE_COURSE_DESCRIPTION;
6131
    			$link_tool = 'course_description/';
6132
    			$id_tool = 'id';
6133
    			break;
6134
    		case 'announcement':
6135
    			$table_name = TABLE_ANNOUNCEMENT;
6136
    			$link_tool = 'announcements/announcements.php';
6137
    			$id_tool = 'id';
6138
    			break;
6139
    		case 'thematic':
6140
    			$table_name = TABLE_THEMATIC;
6141
    			$link_tool = 'course_progress/index.php';
6142
    			$id_tool = 'id';
6143
    			break;
6144
    		case 'thematic_advance':
6145
    			$table_name = TABLE_THEMATIC_ADVANCE;
6146
    			$link_tool = 'course_progress/index.php';
6147
    			$id_tool = 'id';
6148
    			break;
6149
    		case 'thematic_plan':
6150
    			$table_name = TABLE_THEMATIC_PLAN;
6151
    			$link_tool = 'course_progress/index.php';
6152
    			$id_tool = 'id';
6153
    			break;
6154
    		default:
6155
    			$table_name = $tool;
6156
    		break;
6157
    	}
6158
6159
    	return array(
6160
            'table_name' => $table_name,
6161
            'link_tool' => $link_tool,
6162
            'id_tool' => $id_tool
6163
        );
6164
    }
6165
6166
    public static function display_additional_profile_fields()
6167
    {
6168
    	// getting all the extra profile fields that are defined by the platform administrator
6169
    	$extra_fields = UserManager :: get_extra_fields(0,50,5,'ASC');
6170
6171
    	// creating the form
6172
    	$return = '<form action="courseLog.php" method="get" name="additional_profile_field_form" id="additional_profile_field_form">';
6173
6174
    	// the select field with the additional user profile fields (= this is where we select the field of which we want to see
6175
    	// the information the users have entered or selected.
6176
    	$return .= '<select name="additional_profile_field">';
6177
    	$return .= '<option value="-">'.get_lang('SelectFieldToAdd').'</option>';
6178
    	$extra_fields_to_show = 0;
6179
    	foreach ($extra_fields as $key=>$field) {
6180
    		// show only extra fields that are visible + and can be filtered, added by J.Montoya
6181
    		if ($field[6]==1 && $field[8] == 1) {
6182
    			if (isset($_GET['additional_profile_field']) && $field[0] == $_GET['additional_profile_field'] ) {
6183
    				$selected = 'selected="selected"';
6184
    			} else {
6185
    				$selected = '';
6186
    			}
6187
    			$extra_fields_to_show++;
6188
    			$return .= '<option value="'.$field[0].'" '.$selected.'>'.$field[3].'</option>';
6189
    		}
6190
    	}
6191
    	$return .= '</select>';
6192
6193
    	// the form elements for the $_GET parameters (because the form is passed through GET
6194
    	foreach ($_GET as $key=>$value){
6195
    		if ($key <> 'additional_profile_field')    {
6196
    			$return .= '<input type="hidden" name="'.Security::remove_XSS($key).'" value="'.Security::remove_XSS($value).'" />';
6197
    		}
6198
    	}
6199
    	// the submit button
6200
    	$return .= '<button class="save" type="submit">'.get_lang('AddAdditionalProfileField').'</button>';
6201
    	$return .= '</form>';
6202
    	if ($extra_fields_to_show > 0) {
6203
    		return $return;
6204
    	} else {
6205
    		return '';
6206
    	}
6207
    }
6208
6209
    /**
6210
     * This function gets all the information of a certrain ($field_id)
6211
     * additional profile field for a specific list of users is more efficent
6212
     * than get_addtional_profile_information_of_field() function
6213
     * It gets the information of all the users so that it can be displayed
6214
     * in the sortable table or in the csv or xls export
6215
     *
6216
     * @author    Julio Montoya <[email protected]>
6217
     * @param    int field id
6218
     * @param    array list of user ids
6219
     * @return    array
6220
     * @since    Nov 2009
6221
     * @version    1.8.6.2
6222
     */
6223
    public static function get_addtional_profile_information_of_field_by_user($field_id, $users)
6224
    {
6225
    	// Database table definition
6226
    	$table_user = Database::get_main_table(TABLE_MAIN_USER);
6227
    	$table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
6228
        $extraField = Database::get_main_table(TABLE_EXTRA_FIELD);
6229
    	$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...
6230
6231
    	if (!empty($users)) {
6232
    		if ($result_extra_field['field_type'] == UserManager::USER_FIELD_TYPE_TAG ) {
6233
    			foreach($users as $user_id) {
6234
    				$user_result = UserManager::get_user_tags($user_id, $field_id);
6235
    				$tag_list = array();
6236
    				foreach($user_result as $item) {
6237
    					$tag_list[] = $item['tag'];
6238
    				}
6239
    				$return[$user_id][] = implode(', ',$tag_list);
6240
    			}
6241
    		} else {
6242
    			$new_user_array = array();
6243
    			foreach ($users as $user_id) {
6244
    				$new_user_array[]= "'".$user_id."'";
6245
    			}
6246
    			$users = implode(',',$new_user_array);
6247
                $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
6248
    			// Selecting only the necessary information NOT ALL the user list
6249
    			$sql = "SELECT user.user_id, v.value
6250
    			        FROM $table_user user
6251
    			        INNER JOIN $table_user_field_values v
6252
                        ON (user.user_id = v.item_id)
6253
                        INNER JOIN $extraField f
6254
                        ON (f.id = v.field_id)
6255
                        WHERE
6256
                            f.extra_field_type = $extraFieldType AND
6257
                            v.field_id=".intval($field_id)." AND
6258
                            user.user_id IN ($users)";
6259
6260
    			$result = Database::query($sql);
6261
    			while($row = Database::fetch_array($result)) {
6262
    				// get option value for field type double select by id
6263
    				if (!empty($row['value'])) {
6264
    					if ($result_extra_field['field_type'] ==
6265
                            ExtraField::FIELD_TYPE_DOUBLE_SELECT
6266
                        ) {
6267
    						$id_double_select = explode(';', $row['value']);
6268
    						if (is_array($id_double_select)) {
6269
    							$value1 = $result_extra_field['options'][$id_double_select[0]]['option_value'];
6270
    							$value2 = $result_extra_field['options'][$id_double_select[1]]['option_value'];
6271
    							$row['value'] = ($value1.';'.$value2);
6272
    						}
6273
    					}
6274
    				}
6275
    				// get other value from extra field
6276
    				$return[$row['user_id']][] = $row['value'];
6277
    			}
6278
    		}
6279
    	}
6280
    	return $return;
6281
    }
6282
6283
    /**
6284
     * count the number of students in this course (used for SortableTable)
6285
     * Deprecated
6286
     */
6287
    public function count_student_in_course()
6288
    {
6289
    	global $nbStudents;
6290
    	return $nbStudents;
6291
    }
6292
6293
    public function sort_users($a, $b)
6294
    {
6295
    	return strcmp(trim(api_strtolower($a[$_SESSION['tracking_column']])), trim(api_strtolower($b[$_SESSION['tracking_column']])));
6296
    }
6297
6298
    public function sort_users_desc($a, $b)
6299
    {
6300
    	return strcmp( trim(api_strtolower($b[$_SESSION['tracking_column']])), trim(api_strtolower($a[$_SESSION['tracking_column']])));
6301
    }
6302
6303
    /**
6304
     * Get number of users for sortable with pagination
6305
     * @return int
6306
     */
6307
    public static function get_number_of_users()
6308
    {
6309
    	global $user_ids;
6310
    	return count($user_ids);
6311
    }
6312
6313
    /**
6314
     * Get data for users list in sortable with pagination
6315
     * @param $from
6316
     * @param $number_of_items
6317
     * @param $column
6318
     * @param $direction
6319
     * @param $includeInvitedUsers boolean Whether include the invited users
6320
     * @return array
6321
     */
6322
    public static function get_user_data($from, $number_of_items, $column, $direction, $includeInvitedUsers = false)
6323
    {
6324
        global $user_ids, $course_code, $additional_user_profile_info, $export_csv, $is_western_name_order, $csv_content, $session_id;
6325
6326
    	$course_code = Database::escape_string($course_code);
6327
    	$tbl_user = Database::get_main_table(TABLE_MAIN_USER);
6328
    	$tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
6329
6330
    	$access_url_id = api_get_current_access_url_id();
6331
6332
    	// get all users data from a course for sortable with limit
6333
    	if (is_array($user_ids)) {
6334
    		$user_ids = array_map('intval', $user_ids);
6335
    		$condition_user = " WHERE user.user_id IN (".implode(',',$user_ids).") ";
6336
    	} else {
6337
    		$user_ids = intval($user_ids);
6338
    		$condition_user = " WHERE user.user_id = $user_ids ";
6339
    	}
6340
6341 View Code Duplication
    	if (!empty($_GET['user_keyword'])) {
6342
    		$keyword = trim(Database::escape_string($_GET['user_keyword']));
6343
    		$condition_user .=  " AND (
6344
                user.firstname LIKE '%".$keyword."%' OR
6345
                user.lastname LIKE '%".$keyword."%'  OR
6346
                user.username LIKE '%".$keyword."%'  OR
6347
                user.email LIKE '%".$keyword."%'
6348
             ) ";
6349
    	}
6350
6351
        $url_table = null;
6352
        $url_condition = null;
6353
    	if (api_is_multiple_url_enabled()) {
6354
    		$url_table = ", ".$tbl_url_rel_user." as url_users";
6355
    		$url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'";
6356
    	}
6357
6358
        $invitedUsersCondition = '';
6359
6360
        if (!$includeInvitedUsers) {
6361
            $invitedUsersCondition = " AND user.status != " . INVITEE;
6362
        }
6363
6364
    	$sql = "SELECT  user.user_id as user_id,
6365
                    user.official_code  as col0,
6366
                    user.lastname       as col1,
6367
                    user.firstname      as col2,
6368
                    user.username       as col3
6369
                FROM $tbl_user as user $url_table
6370
    	        $condition_user $url_condition $invitedUsersCondition";
6371
6372
    	if (!in_array($direction, array('ASC','DESC'))) {
6373
    		$direction = 'ASC';
6374
    	}
6375
6376
    	$column = intval($column);
6377
6378
    	$from = intval($from);
6379
    	$number_of_items = intval($number_of_items);
6380
6381
    	$sql .= " ORDER BY col$column $direction ";
6382
    	$sql .= " LIMIT $from,$number_of_items";
6383
6384
        $res = Database::query($sql);
6385
        $users = array();
6386
6387
        $course_info = api_get_course_info($course_code);
6388
        $total_surveys = 0;
6389
        $total_exercises = ExerciseLib::get_all_exercises(
6390
            $course_info,
6391
            $session_id,
6392
            false,
6393
            null,
6394
            false,
6395
            3
6396
        );
6397
6398
        if (empty($session_id)) {
6399
            $survey_user_list = array();
6400
            $survey_list = SurveyManager::get_surveys($course_code, $session_id);
6401
6402
            $total_surveys = count($survey_list);
6403 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...
6404
                $user_list = SurveyManager::get_people_who_filled_survey(
6405
                    $survey['survey_id'],
6406
                    false,
6407
                    $course_info['real_id']
6408
                );
6409
6410
                foreach ($user_list as $user_id) {
6411
                    isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1;
6412
                }
6413
            }
6414
        }
6415
6416
    	while ($user = Database::fetch_array($res, 'ASSOC')) {
6417
            $courseInfo = api_get_course_info($course_code);
6418
            $courseId = $courseInfo['real_id'];
6419
6420
            $user['official_code'] = $user['col0'];
6421
            $user['lastname'] = $user['col1'];
6422
            $user['firstname'] = $user['col2'];
6423
            $user['username'] = $user['col3'];
6424
6425
            $user['time'] = api_time_to_hms(
6426
                Tracking::get_time_spent_on_the_course(
6427
                    $user['user_id'],
6428
                    $courseId,
6429
                    $session_id
6430
                )
6431
            );
6432
6433
            $avg_student_score = Tracking::get_avg_student_score(
6434
                $user['user_id'],
6435
                $course_code,
6436
                array(),
6437
                $session_id
6438
            );
6439
6440
            $avg_student_progress = Tracking::get_avg_student_progress(
6441
                $user['user_id'],
6442
                $course_code,
6443
                array(),
6444
                $session_id
6445
            );
6446
6447
    		if (empty($avg_student_progress)) {
6448
                $avg_student_progress = 0;
6449
    		}
6450
    		$user['average_progress'] = $avg_student_progress.'%';
6451
6452
            $total_user_exercise = Tracking::get_exercise_student_progress(
6453
                $total_exercises,
6454
                $user['user_id'],
6455
                $courseId,
6456
                $session_id
6457
            );
6458
6459
            $user['exercise_progress'] = $total_user_exercise;
6460
6461
            $total_user_exercise = Tracking::get_exercise_student_average_best_attempt(
6462
                $total_exercises,
6463
                $user['user_id'],
6464
                $courseId,
6465
                $session_id
6466
            );
6467
6468
            $user['exercise_average_best_attempt'] = $total_user_exercise;
6469
6470
    		if (is_numeric($avg_student_score)) {
6471
    			$user['student_score']  = $avg_student_score.'%';
6472
    		} else {
6473
    			$user['student_score']  = $avg_student_score;
6474
    		}
6475
6476
            $user['count_assignments'] = Tracking::count_student_assignments(
6477
                $user['user_id'],
6478
                $course_code,
6479
                $session_id
6480
            );
6481
            $user['count_messages'] = Tracking::count_student_messages(
6482
                $user['user_id'],
6483
                $course_code,
6484
                $session_id
6485
            );
6486
            $user['first_connection'] = Tracking::get_first_connection_date_on_the_course(
6487
                $user['user_id'],
6488
                $courseId,
6489
                $session_id
6490
            );
6491
            $user['last_connection'] = Tracking::get_last_connection_date_on_the_course(
6492
                $user['user_id'],
6493
                $courseInfo,
6494
                $session_id
6495
            );
6496
6497
    		// we need to display an additional profile field
6498
    		$user['additional'] = '';
6499
6500
    		if (isset($_GET['additional_profile_field']) && is_numeric($_GET['additional_profile_field'])) {
6501 View Code Duplication
    			if (isset($additional_user_profile_info[$user['user_id']]) &&
6502
                    is_array($additional_user_profile_info[$user['user_id']])
6503
                ) {
6504
    				$user['additional'] = implode(', ', $additional_user_profile_info[$user['user_id']]);
6505
    			}
6506
    		}
6507
6508
            if (empty($session_id)) {
6509
                $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0) .' / '.$total_surveys;
6510
            }
6511
6512
    		$user['link'] = '<center>
6513
                             <a href="../mySpace/myStudents.php?student='.$user['user_id'].'&details=true&course='.$course_code.'&origin=tracking_course&id_session='.$session_id.'">
6514
    		                 '.Display::return_icon('2rightarrow.png').'
6515
    		                 </a>
6516
                         </center>';
6517
6518
    		// store columns in array $users
6519
    		$is_western_name_order = api_is_western_name_order();
6520
            $user_row = array();
6521
            $user_row[]= $user['official_code']; //0
6522
            if ($is_western_name_order) {
6523
                $user_row[]= $user['firstname'];
6524
                $user_row[]= $user['lastname'];
6525
            } else {
6526
                $user_row[]= $user['lastname'];
6527
                $user_row[]= $user['firstname'];
6528
            }
6529
            $user_row[]= $user['username'];
6530
            $user_row[]= $user['time'];
6531
            $user_row[]= $user['average_progress'];
6532
            $user_row[]= $user['exercise_progress'];
6533
            $user_row[]= $user['exercise_average_best_attempt'];
6534
            $user_row[]= $user['student_score'];
6535
            $user_row[]= $user['count_assignments'];
6536
            $user_row[]= $user['count_messages'];
6537
6538
            $userGroupManager = new UserGroup();
6539
            $user_row[] = $userGroupManager->getLabelsFromNameList($user['user_id'], UserGroup::NORMAL_CLASS);
6540
6541
            if (empty($session_id)) {
6542
                $user_row[]= $user['survey'];
6543
            }
6544
6545
            $user_row[]= $user['first_connection'];
6546
            $user_row[]= $user['last_connection'];
6547
            if (isset($_GET['additional_profile_field']) && is_numeric($_GET['additional_profile_field'])) {
6548
                $user_row[]= $user['additional'];
6549
            }
6550
6551
            $user_row[]= $user['link'];
6552
6553
            $users[] = $user_row;
6554
6555
    		if ($export_csv) {
6556
    		    if (empty($session_id)) {
6557
                    $user_row = array_map('strip_tags', $user_row);
6558
    			    unset($user_row[14]);
6559
    			    unset($user_row[15]);
6560
                } else {
6561
                    $user_row = array_map('strip_tags', $user_row);
6562
                    unset($user_row[13]);
6563
                    unset($user_row[14]);
6564
                }
6565
6566
    			$csv_content[] = $user_row;
6567
    		}
6568
    	}
6569
    	return $users;
6570
    }
6571
}
6572
6573
/**
6574
 * @package chamilo.tracking
6575
 */
6576
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...
6577
{
6578
    /**
6579
     * Displays the number of logins every month for a specific user in a specific course.
6580
     * @param $view
6581
     * @param int $user_id
6582
     * @param int $course_id
6583
     * @param int $session_id
6584
     */
6585
    public function display_login_tracking_info($view, $user_id, $course_id, $session_id = 0)
6586
    {
6587
    	$MonthsLong = $GLOBALS['MonthsLong'];
6588
6589
    	// protected data
6590
    	$user_id = intval($user_id);
6591
    	$session_id = intval($session_id);
6592
    	$course_id = Database::escape_string($course_id);
6593
6594
    	$track_access_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6595
    	$tempView = $view;
6596
    	if(substr($view,0,1) == '1') {
6597
    		$new_view = substr_replace($view,'0',0,1);
6598
    		echo "
6599
                <tr>
6600
                    <td valign='top'>
6601
                    <font color='#0000FF'>-&nbsp;&nbsp;&nbsp;</font>" .
6602
                    "<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>]
6603
                    </td>
6604
                </tr>
6605
                ";
6606
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('LoginsDetails')."<br>";
6607
6608
    		$sql = "SELECT UNIX_TIMESTAMP(access_date), count(access_date)
6609
                        FROM $track_access_table
6610
                        WHERE access_user_id = $user_id
6611
                        AND c_id = $course_id
6612
                        AND access_session_id = $session_id
6613
                        GROUP BY YEAR(access_date),MONTH(access_date)
6614
                        ORDER BY YEAR(access_date),MONTH(access_date) ASC";
6615
6616
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6617
    		$results = getManyResults3Col($sql);
6618
6619
    		echo "<table cellpadding='2' cellspacing='1' border='0' align=center>";
6620
    		echo "<tr>
6621
                    <td class='secLine'>
6622
                    ".get_lang('LoginsTitleMonthColumn')."
6623
                    </td>
6624
                    <td class='secLine'>
6625
                    ".get_lang('LoginsTitleCountColumn')."
6626
                    </td>
6627
                </tr>";
6628
    		$total = 0;
6629
    		if (is_array($results)) {
6630
    			for($j = 0 ; $j < count($results) ; $j++) {
6631
    				echo "<tr>";
6632
    				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>";
6633
    				echo "<td valign='top' align='right' class='content'>".$results[$j][1]."</td>";
6634
    				echo"</tr>";
6635
    				$total = $total + $results[$j][1];
6636
    			}
6637
    			echo "<tr>";
6638
    			echo "<td>".get_lang('Total')."</td>";
6639
    			echo "<td align='right' class='content'>".$total."</td>";
6640
    			echo"</tr>";
6641
    		} else {
6642
    			echo "<tr>";
6643
    			echo "<td colspan='2'><center>".get_lang('NoResult')."</center></td>";
6644
    			echo"</tr>";
6645
    		}
6646
    		echo "</table>";
6647
    		echo "</td></tr>";
6648 View Code Duplication
    	} else {
6649
    		$new_view = substr_replace($view,'1',0,1);
6650
    		echo "
6651
                <tr>
6652
                    <td valign='top'>
6653
                    +<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>
6654
                    </td>
6655
                </tr>
6656
            ";
6657
    	}
6658
    }
6659
6660
    /**
6661
     * Displays the exercise results for a specific user in a specific course.
6662
     * @param   string $view
6663
     * @param   int $user_id    User ID
6664
     * @param   string  $courseCode Course code
6665
     * @return array
6666
     * @todo remove globals
6667
     */
6668
    public function display_exercise_tracking_info($view, $user_id, $courseCode)
6669
    {
6670
    	global $TBL_TRACK_HOTPOTATOES, $TABLECOURSE_EXERCICES, $TABLETRACK_EXERCICES, $dateTimeFormatLong;
6671
        $courseId = api_get_course_int_id($courseCode);
6672
    	if(substr($view,1,1) == '1') {
6673
    		$new_view = substr_replace($view,'0',1,1);
6674
    		echo "<tr>
6675
                    <td valign='top'>
6676
                        <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>]
6677
                    </td>
6678
                </tr>";
6679
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('ExercicesDetails')."<br />";
6680
6681
    		$sql = "SELECT ce.title, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
6682
                    FROM $TABLECOURSE_EXERCICES AS ce , $TABLETRACK_EXERCICES AS te
6683
                    WHERE te.c_id = $courseId
6684
                        AND te.exe_user_id = ".intval($user_id)."
6685
                        AND te.exe_exo_id = ce.id
6686
                    ORDER BY ce.title ASC, te.exe_date ASC";
6687
6688
    		$hpsql = "SELECT te.exe_name, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
6689
                        FROM $TBL_TRACK_HOTPOTATOES AS te
6690
                        WHERE te.exe_user_id = '".intval($user_id)."' AND te.c_id = $courseId
6691
                        ORDER BY te.c_id ASC, te.exe_date ASC";
6692
6693
    		$hpresults = StatsUtils::getManyResultsXCol($hpsql, 4);
6694
6695
    		$NoTestRes = 0;
6696
    		$NoHPTestRes = 0;
6697
6698
    		echo "<tr>\n<td style='padding-left : 40px;padding-right : 40px;'>\n";
6699
    		$results = StatsUtils::getManyResultsXCol($sql, 4);
6700
    		echo "<table cellpadding='2' cellspacing='1' border='0' align='center'>\n";
6701
    		echo "
6702
                <tr bgcolor='#E6E6E6'>
6703
                    <td>
6704
                    ".get_lang('ExercicesTitleExerciceColumn')."
6705
                    </td>
6706
                    <td>
6707
                    ".get_lang('Date')."
6708
                    </td>
6709
                    <td>
6710
                    ".get_lang('ExercicesTitleScoreColumn')."
6711
                    </td>
6712
                </tr>";
6713
6714
    		if (is_array($results)) {
6715
    			for($i = 0; $i < sizeof($results); $i++) {
6716
    				$display_date = api_convert_and_format_date($results[$i][3], null, date_default_timezone_get());
6717
    				echo "<tr>\n";
6718
    				echo "<td class='content'>".$results[$i][0]."</td>\n";
6719
    				echo "<td class='content'>".$display_date."</td>\n";
6720
    				echo "<td valign='top' align='right' class='content'>".$results[$i][1]." / ".$results[$i][2]."</td>\n";
6721
    				echo "</tr>\n";
6722
    			}
6723
    		} else {
6724
    			// istvan begin
6725
    			$NoTestRes = 1;
6726
    		}
6727
6728
    		// The Result of Tests
6729
    		if (is_array($hpresults)) {
6730
    			for($i = 0; $i < sizeof($hpresults); $i++) {
6731
    				$title = GetQuizName($hpresults[$i][0],'');
6732
    				if ($title == '')
6733
    				$title = basename($hpresults[$i][0]);
6734
    				$display_date = api_convert_and_format_date($hpresults[$i][3], null, date_default_timezone_get());
6735
    				?>
6736
                    <tr>
6737
                        <td class="content"><?php echo $title; ?></td>
6738
                        <td class="content" align="center"><?php echo $display_date; ?></td>
6739
                        <td class="content" align="center"><?php echo $hpresults[$i][1]; ?> / <?php echo $hpresults[$i][2]; ?>
6740
                        </td>
6741
                    </tr>
6742
6743
                    <?php
6744
                }
6745
    		} else {
6746
    			$NoHPTestRes = 1;
6747
    		}
6748
6749
    		if ($NoTestRes == 1 && $NoHPTestRes == 1) {
6750
    			echo "<tr>\n";
6751
    			echo "<td colspan='3'><center>".get_lang('NoResult')."</center></td>\n";
6752
    			echo "</tr>\n";
6753
    		}
6754
    		echo "</table>";
6755
    		echo "</td>\n</tr>\n";
6756
    	} else {
6757
    		$new_view = substr_replace($view,'1',1,1);
6758
    		echo "
6759
                <tr>
6760
                    <td valign='top'>
6761
                        +<font color='#0000FF'>&nbsp;&nbsp;</font><a href='".api_get_self()."?uInfo=$user_id&view=".$new_view."' class='specialLink'>".get_lang('ExercicesResults')."</a>
6762
                    </td>
6763
                </tr>";
6764
    	}
6765
    }
6766
6767
    /**
6768
     * Displays the student publications for a specific user in a specific course.
6769
     * @todo remove globals
6770
     */
6771
    public function display_student_publications_tracking_info($view, $user_id, $course_id)
6772
    {
6773
    	global $TABLETRACK_UPLOADS, $TABLECOURSE_WORK;
6774
        $_course = api_get_course_info_by_id($course_id);
6775
6776
    	if (substr($view,2,1) == '1') {
6777
    		$new_view = substr_replace($view,'0',2,1);
6778
    		echo "<tr>
6779
                    <td valign='top'>
6780
                    <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>]
6781
                    </td>
6782
                </tr>";
6783
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('WorksDetails')."<br>";
6784
    		$sql = "SELECT u.upload_date, w.title, w.author,w.url
6785
                    FROM $TABLETRACK_UPLOADS u , $TABLECOURSE_WORK w
6786
                    WHERE u.upload_work_id = w.id
6787
                        AND u.upload_user_id = '".intval($user_id)."'
6788
                        AND u.c_id = '".intval($course_id)."'
6789
                    ORDER BY u.upload_date DESC";
6790
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6791
    		$results = StatsUtils::getManyResultsXCol($sql,4);
6792
    		echo "<table cellpadding='2' cellspacing='1' border='0' align=center>";
6793
    		echo "<tr>
6794
                    <td class='secLine' width='40%'>
6795
                    ".get_lang('WorkTitle')."
6796
                    </td>
6797
                    <td class='secLine' width='30%'>
6798
                    ".get_lang('WorkAuthors')."
6799
                    </td>
6800
                    <td class='secLine' width='30%'>
6801
                    ".get_lang('Date')."
6802
                    </td>
6803
                </tr>";
6804
    		if (is_array($results)) {
6805
    			for($j = 0 ; $j < count($results) ; $j++) {
6806
    				$pathToFile = api_get_path(WEB_COURSE_PATH).$_course['path']."/".$results[$j][3];
6807
    				$beautifulDate = api_convert_and_format_date($results[$j][0], null, date_default_timezone_get());
6808
    				echo "<tr>";
6809
    				echo "<td class='content'>"
6810
    				."<a href ='".$pathToFile."'>".$results[$j][1]."</a>"
6811
    				."</td>";
6812
    				echo "<td class='content'>".$results[$j][2]."</td>";
6813
    				echo "<td class='content'>".$beautifulDate."</td>";
6814
    				echo"</tr>";
6815
    			}
6816
    		} else {
6817
    			echo "<tr>";
6818
    			echo "<td colspan='3'><center>".get_lang('NoResult')."</center></td>";
6819
    			echo"</tr>";
6820
    		}
6821
    		echo "</table>";
6822
    		echo "</td></tr>";
6823 View Code Duplication
    	} else {
6824
    		$new_view = substr_replace($view,'1',2,1);
6825
    		echo "
6826
                <tr>
6827
                    <td valign='top'>
6828
                    +<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>
6829
                    </td>
6830
                </tr>
6831
            ";
6832
    	}
6833
    }
6834
6835
    /**
6836
     * Displays the links followed for a specific user in a specific course.
6837
     * @todo remove globals
6838
     */
6839
    public function display_links_tracking_info($view, $user_id, $courseCode)
6840
    {
6841
    	global $TABLETRACK_LINKS, $TABLECOURSE_LINKS;
6842
        $courseId = api_get_course_int_id($courseCode);
6843
    	if (substr($view,3,1) == '1') {
6844
    		$new_view = substr_replace($view,'0',3,1);
6845
    		echo "
6846
                <tr>
6847
                        <td valign='top'>
6848
                        <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>]
6849
                        </td>
6850
                </tr>
6851
            ";
6852
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('LinksDetails')."<br>";
6853
    		$sql = "SELECT cl.title, cl.url
6854
                    FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
6855
                    WHERE sl.links_link_id = cl.id
6856
                        AND sl.c_id = $courseId
6857
                        AND sl.links_user_id = ".intval($user_id)."
6858
                    GROUP BY cl.title, cl.url";
6859
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6860
    		$results = StatsUtils::getManyResults2Col($sql);
6861
    		echo "<table cellpadding='2' cellspacing='1' border='0' align=center>";
6862
    		echo "<tr>
6863
                    <td class='secLine'>
6864
                    ".get_lang('LinksTitleLinkColumn')."
6865
                    </td>
6866
                </tr>";
6867
    		if (is_array($results)) {
6868
    			for($j = 0 ; $j < count($results) ; $j++) {
6869
    				echo "<tr>";
6870
    				echo "<td class='content'><a href='".$results[$j][1]."'>".$results[$j][0]."</a></td>";
6871
    				echo"</tr>";
6872
    			}
6873
    		} else {
6874
    			echo "<tr>";
6875
    			echo "<td ><center>".get_lang('NoResult')."</center></td>";
6876
    			echo"</tr>";
6877
    		}
6878
    		echo "</table>";
6879
    		echo "</td></tr>";
6880 View Code Duplication
    	} else {
6881
    		$new_view = substr_replace($view,'1',3,1);
6882
    		echo "
6883
                <tr>
6884
                    <td valign='top'>
6885
                    +<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>
6886
                    </td>
6887
                </tr>
6888
            ";
6889
    	}
6890
    }
6891
6892
    /**
6893
     * Displays the documents downloaded for a specific user in a specific course.
6894
     * @param     string    kind of view inside tracking info
6895
     * @param    int        User id
6896
     * @param    string    Course code
6897
     * @param    int        Session id (optional, default = 0)
6898
     * @return     void
6899
     */
6900
    public static function display_document_tracking_info($view, $user_id, $course_code, $session_id = 0)
6901
    {
6902
    	// protect data
6903
        $user_id = intval($user_id);
6904
        $courseId = api_get_course_int_id($course_code);
6905
    	$session_id = intval($session_id);
6906
6907
    	$downloads_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
6908
    	if(substr($view,4,1) == '1') {
6909
    		$new_view = substr_replace($view,'0',4,1);
6910
    		echo "
6911
                <tr>
6912
                    <td valign='top'>
6913
                    <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>]
6914
                    </td>
6915
                </tr>
6916
            ";
6917
    		echo "<tr><td style='padding-left : 40px;' valign='top'>".get_lang('DocumentsDetails')."<br>";
6918
6919
    		$sql = "SELECT down_doc_path
6920
                    FROM $downloads_table
6921
                    WHERE c_id = $courseId
6922
                        AND down_user_id = $user_id
6923
                        AND down_session_id = $session_id
6924
                    GROUP BY down_doc_path";
6925
6926
    		echo "<tr><td style='padding-left : 40px;padding-right : 40px;'>";
6927
    		$results = StatsUtils::getManyResults1Col($sql);
6928
    		echo "<table cellpadding='2' cellspacing='1' border='0' align='center'>";
6929
    		echo "<tr>
6930
                    <td class='secLine'>
6931
                    ".get_lang('DocumentsTitleDocumentColumn')."
6932
                    </td>
6933
                </tr>";
6934
    		if (is_array($results)) {
6935
    			for($j = 0 ; $j < count($results) ; $j++) {
6936
    				echo "<tr>";
6937
    				echo "<td class='content'>".$results[$j]."</td>";
6938
    				echo"</tr>";
6939
    			}
6940
    		} else {
6941
    			echo "<tr>";
6942
    			echo "<td><center>".get_lang('NoResult')."</center></td>";
6943
    			echo"</tr>";
6944
    		}
6945
    		echo "</table>";
6946
    		echo "</td></tr>";
6947 View Code Duplication
    	} else {
6948
    		$new_view = substr_replace($view,'1',4,1);
6949
    		echo "
6950
                <tr>
6951
                    <td valign='top'>
6952
                    +<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>
6953
                    </td>
6954
                </tr>
6955
            ";
6956
    	}
6957
    }
6958
6959
    /**
6960
     * Gets the IP of a given user, using the last login before the given date
6961
     * @param int User ID
6962
     * @param string Datetime
6963
     * @param bool Whether to return the IP as a link or just as an IP
6964
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6965
     * @return string IP address (or false on error)
6966
     * @assert (0,0) === false
6967
     */
6968
    public static function get_ip_from_user_event($user_id, $event_date, $return_as_link = false, $body_replace = null)
6969
    {
6970
        if (empty($user_id) or empty($event_date)) {
6971
            return false;
6972
        }
6973
        $user_id = intval($user_id);
6974
        $event_date = Database::escape_string($event_date);
6975
6976
        $table_login = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6977
        $sql_ip = "SELECT login_date, user_ip FROM $table_login
6978
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6979
                   ORDER BY login_date DESC LIMIT 1";
6980
        $ip = '';
6981
        $res_ip = Database::query($sql_ip);
6982
        if ($res_ip !== false && Database::num_rows($res_ip)>0) {
6983
            $row_ip = Database::fetch_row($res_ip);
6984
            if ($return_as_link) {
6985
                $ip = Display::url(
6986
                    (empty($body_replace)?$row_ip[1]:$body_replace), 'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6987
                    array('title'=>get_lang('TraceIP'), 'target'=>'_blank')
6988
                );
6989
            } else {
6990
                $ip = $row_ip[1];
6991
            }
6992
        }
6993
6994
        return $ip;
6995
    }
6996
}
6997
6998
/**
6999
 * @package chamilo.tracking
7000
 */
7001
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...
7002
{
7003
    /**
7004
     * Displays the number of logins every month for a specific user in a specific course.
7005
     * @param $view
7006
     * @param int $user_id
7007
     * @param int $course_id
7008
     * @param int $session_id
7009
     * @return array
7010
     */
7011
    public function display_login_tracking_info($view, $user_id, $course_id, $session_id = 0)
7012
    {
7013
    	$MonthsLong = $GLOBALS['MonthsLong'];
7014
    	$track_access_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
7015
7016
    	// protected data
7017
    	$user_id    = intval($user_id);
7018
    	$session_id = intval($session_id);
7019
    	$course_id  = intval($course_id);
7020
7021
    	$tempView = $view;
7022
    	if (substr($view,0,1) == '1') {
7023
    		$new_view = substr_replace($view,'0',0,1);
7024
    		$title[1]= get_lang('LoginsAndAccessTools').get_lang('LoginsDetails');
7025
    		$sql = "SELECT UNIX_TIMESTAMP(access_date), count(access_date)
7026
                    FROM $track_access_table
7027
                    WHERE access_user_id = $user_id
7028
                    AND c_id = $course_id
7029
                    AND access_session_id = $session_id
7030
                    GROUP BY YEAR(access_date),MONTH(access_date)
7031
                    ORDER BY YEAR(access_date),MONTH(access_date) ASC";
7032
    		//$results = getManyResults2Col($sql);
7033
    		$results = getManyResults3Col($sql);
7034
    		$title_line= get_lang('LoginsTitleMonthColumn').';'.get_lang('LoginsTitleCountColumn')."\n";
7035
    		$line='';
7036
    		$total = 0;
7037 View Code Duplication
    		if (is_array($results)) {
7038
    			for($j = 0 ; $j < count($results) ; $j++) {
7039
    				$line .= $results[$j][0].';'.$results[$j][1]."\n";
7040
    				$total = $total + $results[$j][1];
7041
    			}
7042
    			$line .= get_lang('Total').";".$total."\n";
7043
    		} else {
7044
    			$line= get_lang('NoResult')."</center></td>";
7045
    		}
7046
    	} else {
7047
    		$new_view = substr_replace($view,'1',0,1);
7048
    	}
7049
    	return array($title_line, $line);
7050
    }
7051
7052
    /**
7053
     * Displays the exercise results for a specific user in a specific course.
7054
     * @param   string $view
7055
     * @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...
7056
     * @param   string  $courseCode Course code
7057
     * @return array
7058
     * @todo remove globals
7059
     */
7060
    public function display_exercise_tracking_info($view, $userId, $courseCode)
7061
    {
7062
    	global $TABLECOURSE_EXERCICES, $TABLETRACK_EXERCICES, $TABLETRACK_HOTPOTATOES, $dateTimeFormatLong;
7063
        $courseId = api_get_course_int_id($courseCode);
7064
        $userId = intval($userId);
7065
    	if (substr($view,1,1) == '1') {
7066
    		$new_view = substr_replace($view,'0',1,1);
7067
    		$title[1] = get_lang('ExercicesDetails');
7068
    		$line = '';
7069
    		$sql = "SELECT ce.title, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
7070
                    FROM $TABLECOURSE_EXERCICES AS ce , $TABLETRACK_EXERCICES AS te
7071
                    WHERE te.c_id = $courseId
7072
                        AND te.exe_user_id = $userId
7073
                        AND te.exe_exo_id = ce.id
7074
                    ORDER BY ce.title ASC, te.exe_date ASC";
7075
7076
    		$hpsql = "SELECT te.exe_name, te.exe_result , te.exe_weighting, UNIX_TIMESTAMP(te.exe_date)
7077
                        FROM $TABLETRACK_HOTPOTATOES AS te
7078
                        WHERE te.exe_user_id = '$userId' AND te.c_id = $courseId
7079
                        ORDER BY te.c_id ASC, te.exe_date ASC";
7080
7081
    		$hpresults = StatsUtils::getManyResultsXCol($hpsql, 4);
7082
7083
    		$NoTestRes = 0;
7084
    		$NoHPTestRes = 0;
7085
7086
    		$results = StatsUtils::getManyResultsXCol($sql, 4);
7087
    		$title_line = get_lang('ExercicesTitleExerciceColumn').";".get_lang('Date').';'.get_lang('ExercicesTitleScoreColumn')."\n";
7088
7089 View Code Duplication
    		if (is_array($results)) {
7090
    			for($i = 0; $i < sizeof($results); $i++)
7091
    			{
7092
    				$display_date = api_convert_and_format_date($results[$i][3], null, date_default_timezone_get());
7093
    				$line .= $results[$i][0].";".$display_date.";".$results[$i][1]." / ".$results[$i][2]."\n";
7094
    			}
7095
    		} else {
7096
                // istvan begin
7097
    			$NoTestRes = 1;
7098
    		}
7099
7100
    		// The Result of Tests
7101
    		if (is_array($hpresults)) {
7102
    			for($i = 0; $i < sizeof($hpresults); $i++) {
7103
    				$title = GetQuizName($hpresults[$i][0],'');
7104
7105
    				if ($title == '')
7106
    				$title = basename($hpresults[$i][0]);
7107
7108
    				$display_date = api_convert_and_format_date($hpresults[$i][3], null, date_default_timezone_get());
7109
7110
    				$line .= $title.';'.$display_date.';'.$hpresults[$i][1].'/'.$hpresults[$i][2]."\n";
7111
    			}
7112
    		} else {
7113
    			$NoHPTestRes = 1;
7114
    		}
7115
7116
    		if ($NoTestRes == 1 && $NoHPTestRes == 1) {
7117
    			$line=get_lang('NoResult');
7118
    		}
7119
    	} else {
7120
    		$new_view = substr_replace($view,'1',1,1);
7121
    	}
7122
    	return array($title_line, $line);
7123
    }
7124
7125
    /**
7126
     * Displays the student publications for a specific user in a specific course.
7127
     * @todo remove globals
7128
     */
7129
    public function display_student_publications_tracking_info($view, $user_id, $course_id)
7130
    {
7131
    	global $TABLETRACK_UPLOADS, $TABLECOURSE_WORK;
7132
        $_course = api_get_course_info();
7133
        $user_id = intval($user_id);
7134
        $course_id = intval($course_id);
7135
7136
    	if (substr($view,2,1) == '1') {
7137
    		$sql = "SELECT u.upload_date, w.title, w.author, w.url
7138
                    FROM $TABLETRACK_UPLOADS u , $TABLECOURSE_WORK w
7139
                    WHERE
7140
                        u.upload_work_id = w.id AND
7141
                        u.upload_user_id = '$user_id' AND
7142
                        u.c_id = '$course_id'
7143
                    ORDER BY u.upload_date DESC";
7144
    		$results = StatsUtils::getManyResultsXCol($sql,4);
7145
7146
    		$title[1]=get_lang('WorksDetails');
7147
    		$line='';
7148
    		$title_line=get_lang('WorkTitle').";".get_lang('WorkAuthors').";".get_lang('Date')."\n";
7149
7150
    		if (is_array($results)) {
7151
    			for($j = 0 ; $j < count($results) ; $j++) {
7152
    				$pathToFile = api_get_path(WEB_COURSE_PATH).$_course['path']."/".$results[$j][3];
7153
    				$beautifulDate = api_convert_and_format_date($results[$j][0], null, date_default_timezone_get());
7154
    				$line .= $results[$j][1].";".$results[$j][2].";".$beautifulDate."\n";
7155
    			}
7156
7157
    		} else {
7158
    			$line= get_lang('NoResult');
7159
    		}
7160
    	}
7161
    	return array($title_line, $line);
7162
    }
7163
7164
    /**
7165
     * Displays the links followed for a specific user in a specific course.
7166
     * @todo remove globals
7167
     */
7168
    public function display_links_tracking_info($view, $userId, $courseCode)
7169
    {
7170
    	global $TABLETRACK_LINKS, $TABLECOURSE_LINKS;
7171
        $courseId = api_get_course_int_id($courseCode);
7172
        $userId = intval($userId);
7173
        $line = null;
7174 View Code Duplication
    	if (substr($view,3,1) == '1') {
7175
    		$new_view = substr_replace($view,'0',3,1);
7176
    		$title[1]=get_lang('LinksDetails');
7177
    		$sql = "SELECT cl.title, cl.url
7178
                        FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
7179
                        WHERE sl.links_link_id = cl.id
7180
                            AND sl.c_id = $courseId
7181
                            AND sl.links_user_id = $userId
7182
                        GROUP BY cl.title, cl.url";
7183
    		$results = StatsUtils::getManyResults2Col($sql);
7184
    		$title_line= get_lang('LinksTitleLinkColumn')."\n";
7185
    		if (is_array($results)) {
7186
    			for ($j = 0 ; $j < count($results) ; $j++) {
7187
    				$line .= $results[$j][0]."\n";
7188
    			}
7189
    		} else {
7190
    			$line=get_lang('NoResult');
7191
    		}
7192
    	} else {
7193
    		$new_view = substr_replace($view,'1',3,1);
7194
    	}
7195
    	return array($title_line, $line);
7196
    }
7197
7198
    /**
7199
     * Displays the documents downloaded for a specific user in a specific course.
7200
     * @param     string    kind of view inside tracking info
7201
     * @param    int        User id
7202
     * @param    string    Course code
7203
     * @param    int        Session id (optional, default = 0)
7204
     * @return     void
7205
     */
7206
    public function display_document_tracking_info($view, $user_id, $courseCode, $session_id = 0)
7207
    {
7208
    	// protect data
7209
    	$user_id     = intval($user_id);
7210
        $courseId = api_get_course_int_id($courseCode);
7211
    	$session_id = intval($session_id);
7212
7213
    	$downloads_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
7214
7215 View Code Duplication
    	if (substr($view,4,1) == '1') {
7216
    		$new_view = substr_replace($view,'0',4,1);
7217
    		$title[1]= get_lang('DocumentsDetails');
7218
7219
    		$sql = "SELECT down_doc_path
7220
                        FROM $downloads_table
7221
                        WHERE c_id = $courseId
7222
                            AND down_user_id = $user_id
7223
                            AND down_session_id = $session_id
7224
                        GROUP BY down_doc_path";
7225
7226
    		$results = StatsUtils::getManyResults1Col($sql);
7227
    		$title_line = get_lang('DocumentsTitleDocumentColumn')."\n";
7228
            $line = null;
7229
    		if (is_array($results)) {
7230
    			for ($j = 0 ; $j < count($results) ; $j++) {
7231
    				$line .= $results[$j]."\n";
7232
    			}
7233
    		} else {
7234
    			$line = get_lang('NoResult');
7235
    		}
7236
    	} else {
7237
    		$new_view = substr_replace($view,'1',4,1);
7238
    	}
7239
    	return array($title_line, $line);
7240
    }
7241
7242
    /**
7243
     * @param $userId
7244
     * @param $courseInfo
7245
     * @param int $sessionId
7246
     * @return array
7247
     */
7248
    public static function getToolInformation(
7249
        $userId,
7250
        $courseInfo,
7251
        $sessionId = 0
7252
    ) {
7253
        $csvContent = array();
7254
        $courseToolInformation = null;
7255
        $headerTool = array(
7256
            array(get_lang('Title')),
7257
            array(get_lang('CreatedAt')),
7258
            array(get_lang('UpdatedAt')),
7259
        );
7260
7261
        $headerListForCSV = array();
7262
        foreach ($headerTool as $item) {
7263
            $headerListForCSV[] = $item[0];
7264
        }
7265
7266
        $courseForumInformationArray = getForumCreatedByUser(
7267
            $userId,
7268
            $courseInfo['real_id'],
7269
            $sessionId
7270
        );
7271
7272
        if (!empty($courseForumInformationArray)) {
7273
            $csvContent[] = array();
7274
            $csvContent[] = get_lang('Forums');
7275
            $csvContent[] = $headerListForCSV;
7276
            foreach ($courseForumInformationArray as $row) {
7277
                $csvContent[] = $row;
7278
            }
7279
7280
            $courseToolInformation .= Display::page_subheader2(
7281
                get_lang('Forums')
7282
            );
7283
            $courseToolInformation .= Display::return_sortable_table(
7284
                $headerTool,
7285
                $courseForumInformationArray
7286
            );
7287
        }
7288
7289
        $courseWorkInformationArray = getWorkCreatedByUser(
7290
            $userId,
7291
            $courseInfo['real_id'],
7292
            $sessionId
7293
        );
7294
7295
        if (!empty($courseWorkInformationArray)) {
7296
            $csvContent[] = null;
7297
            $csvContent[] = get_lang('Works');
7298
            $csvContent[] = $headerListForCSV;
7299
7300
            foreach ($courseWorkInformationArray as $row) {
7301
                $csvContent[] = $row;
7302
            }
7303
            $csvContent[] = null;
7304
7305
            $courseToolInformation .= Display::page_subheader2(
7306
                get_lang('Works')
7307
            );
7308
            $courseToolInformation .= Display::return_sortable_table(
7309
                $headerTool,
7310
                $courseWorkInformationArray
7311
            );
7312
        }
7313
        $courseToolInformationTotal = null;
7314
7315
        if (!empty($courseToolInformation)) {
7316
            $sessionTitle = null;
7317
            if (!empty($sessionId)) {
7318
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
7319
            }
7320
7321
            $courseToolInformationTotal .= Display::page_subheader(
7322
                $courseInfo['title'].$sessionTitle
7323
            );
7324
            $courseToolInformationTotal .= $courseToolInformation;
7325
        }
7326
7327
        return array(
7328
            'array' => $csvContent,
7329
            'html' => $courseToolInformationTotal
7330
        );
7331
    }
7332
}
7333