Completed
Push — 1.10.x ( 2aecf0...57ccd9 )
by Angel Fernando Quiroz
57:59
created

Tracking::get_total_clicks()   D

Complexity

Conditions 10
Paths 25

Size

Total Lines 113
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 71
nc 25
nop 5
dl 0
loc 113
rs 4.8196
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
use Chamilo\CoreBundle\Entity\Course;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Course.

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

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

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

// Bar.php
namespace OtherDir;

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

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

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

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

// Bar.php
namespace OtherDir;

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

There are different options of fixing this problem.

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

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

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

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

There are different options of fixing this problem.

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

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

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

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

There are different options of fixing this problem.

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

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

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

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

There are different options of fixing this problem.

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

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

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

Loading history...
1189
                $humanResourcesList[] = $item['user_id'];
1190
            }
1191
1192
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1193
                $userId,
1194
                null,
1195
                null,
1196
                null,
1197
                null,
1198
                null
1199
            );
1200
            foreach ($platformCourses as $course) {
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 = new DateTime('now', new DateTimeZone('UTC'));
1356
1357
        switch ($timeFilter) {
1358
            case 'last_7_days':
1359
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1360
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1361
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1362
                break;
1363
            case 'last_30_days':
1364
                $new_date = date('Y-m-d H:i:s', strtotime('-30 day'));
1365
                $condition_time = " AND (login_date >= '{$new_date->format('Y-m-d H:i:s')}'";
0 ignored issues
show
Bug introduced by
The method format cannot be called on $new_date (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

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