Passed
Push — master ( 49c89f...65060b )
by Julito
10:01
created

Statistics::printToolStats()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 6
rs 10
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * This class provides some functions for statistics.
6
 *
7
 * @package chamilo.statistics
8
 */
9
class Statistics
10
{
11
    /**
12
     * Converts a number of bytes in a formatted string.
13
     *
14
     * @param int $size
15
     *
16
     * @return string Formatted file size
17
     */
18
    public static function makeSizeString($size)
19
    {
20
        if ($size < pow(2, 10)) {
21
            return $size." bytes";
22
        }
23
        if ($size >= pow(2, 10) && $size < pow(2, 20)) {
24
            return round($size / pow(2, 10), 0)." KB";
25
        }
26
        if ($size >= pow(2, 20) && $size < pow(2, 30)) {
27
            return round($size / pow(2, 20), 1)." MB";
28
        }
29
        if ($size > pow(2, 30)) {
30
            return round($size / pow(2, 30), 2)." GB";
31
        }
32
    }
33
34
    /**
35
     * Count courses.
36
     *
37
     * @param string $categoryCode Code of a course category.
38
     *                             Default: count all courses.
39
     *
40
     * @return int Number of courses counted
41
     */
42
    public static function countCourses($categoryCode = null)
43
    {
44
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
45
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
46
        $urlId = api_get_current_access_url_id();
47
        if (api_is_multiple_url_enabled()) {
48
            $sql = "SELECT COUNT(*) AS number
49
                    FROM ".$course_table." as c, $access_url_rel_course_table as u
50
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'";
51
            if (isset($categoryCode)) {
52
                $sql .= " AND category_code = '".Database::escape_string($categoryCode)."'";
53
            }
54
        } else {
55
            $sql = "SELECT COUNT(*) AS number
56
                    FROM $course_table";
57
            if (isset($categoryCode)) {
58
                $sql .= " WHERE category_code = '".Database::escape_string($categoryCode)."'";
59
            }
60
        }
61
62
        $res = Database::query($sql);
63
        $obj = Database::fetch_object($res);
64
65
        return $obj->number;
66
    }
67
68
    /**
69
     * Count courses by visibility.
70
     *
71
     * @param int $visibility visibility (0 = closed, 1 = private, 2 = open, 3 = public) all courses
72
     *
73
     * @return int Number of courses counted
74
     */
75
    public static function countCoursesByVisibility($visibility = null)
76
    {
77
        if (!isset($visibility)) {
78
            return 0;
79
        }
80
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
81
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
82
        $urlId = api_get_current_access_url_id();
83
        if (api_is_multiple_url_enabled()) {
84
            $sql = "SELECT COUNT(*) AS number
85
                    FROM $course_table as c, $access_url_rel_course_table as u
86
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'";
87
            if (isset($visibility)) {
88
                $sql .= " AND visibility = ".intval($visibility);
89
            }
90
        } else {
91
            $sql = "SELECT COUNT(*) AS number FROM $course_table ";
92
            if (isset($visibility)) {
93
                $sql .= " WHERE visibility = ".intval($visibility);
94
            }
95
        }
96
        $res = Database::query($sql);
97
        $obj = Database::fetch_object($res);
98
99
        return $obj->number;
100
    }
101
102
    /**
103
     * Count users.
104
     *
105
     * @param int    $status                user status (COURSEMANAGER or STUDENT) if not setted it'll count all users
106
     * @param string $categoryCode          course category code. Default: count only users without filtering category
107
     * @param bool   $countInvisibleCourses Count invisible courses (todo)
108
     * @param bool   $onlyActive            Count only active users (false to only return currently active users)
109
     *
110
     * @return int Number of users counted
111
     */
112
    public static function countUsers(
113
        $status = null,
114
        $categoryCode = null,
115
        $countInvisibleCourses = true,
116
        $onlyActive = false
117
    ) {
118
        // Database table definitions
119
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
120
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
121
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
122
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
123
        $urlId = api_get_current_access_url_id();
124
        $active_filter = $onlyActive ? ' AND active=1' : '';
125
        $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
126
127
        if (api_is_multiple_url_enabled()) {
128
            $sql = "SELECT COUNT(DISTINCT(u.user_id)) AS number
129
                    FROM $user_table as u, $access_url_rel_user_table as url
130
                    WHERE
131
                        u.user_id = url.user_id AND
132
                        access_url_id = '".$urlId."'
133
                        $status_filter $active_filter";
134
            if (isset($categoryCode)) {
135
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
136
                        FROM $course_user_table cu, $course_table c, $access_url_rel_user_table as url
137
                        WHERE
138
                            c.id = cu.c_id AND
139
                            c.category_code = '".Database::escape_string($categoryCode)."' AND
140
                            cu.user_id = url.user_id AND
141
                            access_url_id='".$urlId."'
142
                            $status_filter $active_filter";
143
            }
144
        } else {
145
            $sql = "SELECT COUNT(DISTINCT(user_id)) AS number
146
                    FROM $user_table
147
                    WHERE 1=1 $status_filter $active_filter";
148
            if (isset($categoryCode)) {
149
                $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
150
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
151
                        FROM $course_user_table cu, $course_table c
152
                        WHERE
153
                            c.id = cu.c_id AND
154
                            c.category_code = '".Database::escape_string($categoryCode)."'
155
                            $status_filter
156
                            $active_filter
157
                        ";
158
            }
159
        }
160
161
        $res = Database::query($sql);
162
        $obj = Database::fetch_object($res);
163
164
        return $obj->number;
165
    }
166
167
    /**
168
     * Count activities from track_e_default_table.
169
     *
170
     * @return int Number of activities counted
171
     */
172
    public static function getNumberOfActivities($courseId = 0, $sessionId = 0)
173
    {
174
        // Database table definitions
175
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
176
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
177
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
178
        $urlId = api_get_current_access_url_id();
179
        if (api_is_multiple_url_enabled()) {
180
            $sql = "SELECT count(default_id) AS total_number_of_items
181
                    FROM $track_e_default, $table_user user, $access_url_rel_user_table url
182
                    WHERE
183
                        default_user_id = user.user_id AND
184
                        user.user_id=url.user_id AND
185
                        access_url_id = '".$urlId."'";
186
        } else {
187
            $sql = "SELECT count(default_id) AS total_number_of_items
188
                    FROM $track_e_default, $table_user user
189
                    WHERE default_user_id = user.user_id ";
190
        }
191
192
        if (!empty($courseId)) {
193
            $courseId = (int) $courseId;
194
            $sql .= " AND c_id = $courseId";
195
            $sql .= api_get_session_condition($sessionId);
196
        }
197
198
        if (isset($_GET['keyword'])) {
199
            $keyword = Database::escape_string(trim($_GET['keyword']));
200
            $sql .= " AND (
201
                        user.username LIKE '%".$keyword."%' OR 
202
                        default_event_type LIKE '%".$keyword."%' OR 
203
                        default_value_type LIKE '%".$keyword."%' OR 
204
                        default_value LIKE '%".$keyword."%') ";
205
        }
206
207
        $res = Database::query($sql);
208
        $obj = Database::fetch_object($res);
209
210
        return $obj->total_number_of_items;
211
    }
212
213
    /**
214
     * Get activities data to display.
215
     *
216
     * @param int    $from
217
     * @param int    $numberOfItems
218
     * @param int    $column
219
     * @param string $direction
220
     * @param int    $courseId
221
     * @param int    $sessionId
222
     *
223
     * @return array
224
     */
225
    public static function getActivitiesData(
226
        $from,
227
        $numberOfItems,
228
        $column,
229
        $direction,
230
        $courseId = 0,
231
        $sessionId = 0
232
    ) {
233
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
234
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
235
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
236
        $urlId = api_get_current_access_url_id();
237
        $column = intval($column);
238
        $from = intval($from);
239
        $numberOfItems = intval($numberOfItems);
240
        $direction = strtoupper($direction);
241
242
        if (!in_array($direction, ['ASC', 'DESC'])) {
243
            $direction = 'DESC';
244
        }
245
246
        if (api_is_multiple_url_enabled()) {
247
            $sql = "SELECT
248
                        default_event_type  as col0,
249
                        default_value_type    as col1,
250
                        default_value        as col2,
251
                        c_id         as col3,
252
                        session_id as col4,
253
                        user.username         as col5,
254
                        user.user_id         as col6,
255
                        default_date         as col7
256
                    FROM $track_e_default as track_default,
257
                    $table_user as user,
258
                    $access_url_rel_user_table as url
259
                    WHERE
260
                        track_default.default_user_id = user.user_id AND
261
                        url.user_id = user.user_id AND
262
                        access_url_id= $urlId ";
263
        } else {
264
            $sql = "SELECT
265
                       default_event_type  as col0,
266
                       default_value_type    as col1,
267
                       default_value        as col2,
268
                       c_id         as col3,
269
                       session_id as col4,
270
                       user.username         as col5,
271
                       user.user_id         as col6,
272
                       default_date         as col7
273
                   FROM $track_e_default track_default, $table_user user
274
                   WHERE track_default.default_user_id = user.user_id ";
275
        }
276
277
        if (!empty($_GET['keyword'])) {
278
            $keyword = Database::escape_string(trim($_GET['keyword']));
279
            $sql .= " AND (user.username LIKE '%".$keyword."%' OR
280
                        default_event_type LIKE '%".$keyword."%' OR
281
                        default_value_type LIKE '%".$keyword."%' OR
282
                        default_value LIKE '%".$keyword."%') ";
283
        }
284
285
        if (!empty($courseId)) {
286
            $courseId = (int) $courseId;
287
            $sql .= " AND c_id = $courseId";
288
            $sql .= api_get_session_condition($sessionId);
289
        }
290
291
        if (!empty($column) && !empty($direction)) {
292
            $sql .= " ORDER BY col$column $direction";
293
        } else {
294
            $sql .= " ORDER BY col7 DESC ";
295
        }
296
        $sql .= " LIMIT $from, $numberOfItems ";
297
298
        $res = Database::query($sql);
299
        $activities = [];
300
        while ($row = Database::fetch_row($res)) {
301
            if (strpos($row[1], '_object') === false &&
302
                strpos($row[1], '_array') === false
303
            ) {
304
                $row[2] = $row[2];
305
            } else {
306
                if (!empty($row[2])) {
307
                    $originalData = str_replace('\\', '', $row[2]);
308
                    $row[2] = unserialize($originalData);
309
                    if (is_array($row[2]) && !empty($row[2])) {
310
                        $row[2] = implode_with_key(', ', $row[2]);
311
                    } else {
312
                        $row[2] = $originalData;
313
                    }
314
                }
315
            }
316
317
            if (!empty($row['default_date'])) {
318
                $row['default_date'] = api_get_local_time($row['default_date']);
319
            } else {
320
                $row['default_date'] = '-';
321
            }
322
323
            if (!empty($row[5])) {
324
                // Course
325
                if (!empty($row[3])) {
326
                    $row[3] = Display::url(
327
                        $row[3],
328
                        api_get_path(WEB_CODE_PATH).'admin/course_edit.php?id='.$row[3]
329
                    );
330
                } else {
331
                    $row[3] = '-';
332
                }
333
334
                // session
335
                if (!empty($row[4])) {
336
                    $row[4] = Display::url(
337
                        $row[4],
338
                        api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$row[4]
339
                    );
340
                } else {
341
                    $row[4] = '-';
342
                }
343
344
                // User id.
345
                $row[5] = Display::url(
346
                    $row[5],
347
                    api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$row[6],
348
                    ['class' => 'ajax']
349
                );
350
351
                $row[6] = Tracking::get_ip_from_user_event(
352
                    $row[6],
353
                    $row[7],
354
                    true
355
                );
356
                if (empty($row[6])) {
357
                    $row[6] = get_lang('Unknown');
358
                }
359
            }
360
            $activities[] = $row;
361
        }
362
363
        return $activities;
364
    }
365
366
    /**
367
     * Get all course categories.
368
     *
369
     * @return array All course categories (code => name)
370
     */
371
    public static function getCourseCategories()
372
    {
373
        $categoryTable = Database::get_main_table(TABLE_MAIN_CATEGORY);
374
        $sql = "SELECT code, name 
375
                FROM $categoryTable
376
                ORDER BY tree_pos";
377
        $res = Database::query($sql);
378
        $categories = [];
379
        while ($category = Database::fetch_object($res)) {
380
            $categories[$category->code] = $category->name;
381
        }
382
383
        return $categories;
384
    }
385
386
    /**
387
     * Rescale data.
388
     *
389
     * @param array $data The data that should be rescaled
390
     * @param int   $max  The maximum value in the rescaled data (default = 500);
391
     *
392
     * @return array The rescaled data, same key as $data
393
     */
394
    public static function rescale($data, $max = 500)
395
    {
396
        $data_max = 1;
397
        foreach ($data as $index => $value) {
398
            $data_max = ($data_max < $value ? $value : $data_max);
399
        }
400
        reset($data);
401
        $result = [];
402
        $delta = $max / $data_max;
403
        foreach ($data as $index => $value) {
404
            $result[$index] = (int) round($value * $delta);
405
        }
406
407
        return $result;
408
    }
409
410
    /**
411
     * Show statistics.
412
     *
413
     * @param string $title      The title
414
     * @param array  $stats
415
     * @param bool   $showTotal
416
     * @param bool   $isFileSize
417
     */
418
    public static function printStats(
419
        $title,
420
        $stats,
421
        $showTotal = true,
422
        $isFileSize = false
423
    ) {
424
        $total = 0;
425
        $data = self::rescale($stats);
426
        echo '<table class="data_table" cellspacing="0" cellpadding="3">
427
                <tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr>';
428
        $i = 0;
429
        foreach ($stats as $subtitle => $number) {
430
            $total += $number;
431
        }
432
433
        foreach ($stats as $subtitle => $number) {
434
            if (!$isFileSize) {
435
                $number_label = number_format($number, 0, ',', '.');
436
            } else {
437
                $number_label = self::makeSizeString($number);
438
            }
439
            $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
440
441
            echo '<tr class="row_'.($i % 2 == 0 ? 'odd' : 'even').'">
442
                    <td width="150">'.$subtitle.'</td>
443
                    <td width="550">'.Display::bar_progress($percentage, false).'</td>
444
                    <td align="right">'.$number_label.'</td>';
445
            if ($showTotal) {
446
                echo '<td align="right"> '.$percentage.'%</td>';
447
            }
448
            echo '</tr>';
449
            $i++;
450
        }
451
        if ($showTotal) {
452
            if (!$isFileSize) {
453
                $total_label = number_format($total, 0, ',', '.');
454
            } else {
455
                $total_label = self::makeSizeString($total);
456
            }
457
            echo '<tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr>';
458
        }
459
        echo '</table>';
460
    }
461
462
    /**
463
     * Show some stats about the number of logins.
464
     *
465
     * @param string $type month, hour or day
466
     */
467
    public static function printLoginStats($type)
468
    {
469
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
470
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
471
        $urlId = api_get_current_access_url_id();
472
473
        $table_url = null;
474
        $where_url = null;
475
        $now = api_get_utc_datetime();
476
        $where_url_last = ' WHERE login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
477
        if (api_is_multiple_url_enabled()) {
478
            $table_url = ", $access_url_rel_user_table";
479
            $where_url = " WHERE login_user_id=user_id AND access_url_id='".$urlId."'";
480
            $where_url_last = ' AND login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
481
        }
482
483
        $period = get_lang('PeriodMonth');
484
        $periodCollection = api_get_months_long();
485
        $sql = "SELECT 
486
                DATE_FORMAT( login_date, '%Y-%m' ) AS stat_date , 
487
                count( login_id ) AS number_of_logins
488
                FROM $table $table_url $where_url
489
                GROUP BY stat_date
490
                ORDER BY login_date DESC";
491
        $sql_last_x = null;
492
493
        switch ($type) {
494
            case 'hour':
495
                $period = get_lang('PeriodHour');
496
                $sql = "SELECT 
497
                          DATE_FORMAT( login_date, '%H') AS stat_date, 
498
                          count( login_id ) AS number_of_logins 
499
                        FROM $table $table_url $where_url 
500
                        GROUP BY stat_date 
501
                        ORDER BY stat_date ";
502
                $sql_last_x = "SELECT 
503
                                DATE_FORMAT( login_date, '%H' ) AS stat_date, 
504
                                count( login_id ) AS number_of_logins 
505
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'DAY')." 
506
                               GROUP BY stat_date 
507
                               ORDER BY stat_date ";
508
                break;
509
            case 'day':
510
                $periodCollection = api_get_week_days_long();
511
                $period = get_lang('PeriodDay');
512
                $sql = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date , 
513
                        count( login_id ) AS number_of_logins 
514
                        FROM  $table $table_url $where_url 
515
                        GROUP BY stat_date 
516
                        ORDER BY DATE_FORMAT( login_date, '%w' ) ";
517
                $sql_last_x = "SELECT 
518
                                DATE_FORMAT( login_date, '%w' ) AS stat_date, 
519
                                count( login_id ) AS number_of_logins 
520
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'WEEK')." 
521
                               GROUP BY stat_date 
522
                               ORDER BY DATE_FORMAT( login_date, '%w' ) ";
523
                break;
524
        }
525
526
        if ($sql_last_x) {
527
            $res_last_x = Database::query($sql_last_x);
528
            $result_last_x = [];
529
            while ($obj = Database::fetch_object($res_last_x)) {
530
                $stat_date = ($type === 'day') ? $periodCollection[$obj->stat_date] : $obj->stat_date;
531
                $result_last_x[$stat_date] = $obj->number_of_logins;
532
            }
533
            self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true);
534
            flush(); //flush web request at this point to see something already while the full data set is loading
535
            echo '<br />';
536
        }
537
        $res = Database::query($sql);
538
        $result = [];
539
        while ($obj = Database::fetch_object($res)) {
540
            $stat_date = $obj->stat_date;
541
            switch ($type) {
542
                case 'month':
543
                    $stat_date = explode('-', $stat_date);
544
                    $stat_date[1] = $periodCollection[$stat_date[1] - 1];
545
                    $stat_date = implode(' ', $stat_date);
546
                    break;
547
                case 'day':
548
                    $stat_date = $periodCollection[$stat_date];
549
                    break;
550
            }
551
            $result[$stat_date] = $obj->number_of_logins;
552
        }
553
        self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true);
554
    }
555
556
    /**
557
     * Print the number of recent logins.
558
     *
559
     * @param bool $distinct        whether to only give distinct users stats, or *all* logins
560
     * @param int  $sessionDuration
561
     */
562
    public static function printRecentLoginStats($distinct = false, $sessionDuration = 0)
563
    {
564
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
565
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
566
        $urlId = api_get_current_access_url_id();
567
        $table_url = '';
568
        $where_url = '';
569
        if (api_is_multiple_url_enabled()) {
570
            $table_url = ", $access_url_rel_user_table";
571
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
572
        }
573
574
        $now = api_get_utc_datetime();
575
        $field = 'login_id';
576
        if ($distinct) {
577
            $field = 'DISTINCT(login_user_id)';
578
        }
579
580
        $days = [1, 7, 15, 31];
581
        $sqlList = [];
582
583
        $sessionDuration = (int) $sessionDuration;
584
        foreach ($days as $day) {
585
            $date = new DateTime($now);
586
            $startDate = $date->format('Y-m-d').' 00:00:00';
587
            $endDate = $date->format('Y-m-d').' 23:59:59';
588
589
            if ($day > 1) {
590
                $startDate = $date->sub(new DateInterval('P'.$day.'D'));
591
                $startDate = $startDate->format('Y-m-d').' 00:00:00';
592
            }
593
594
            $localDate = api_get_local_time($startDate, null, null, false, false);
595
            $localEndDate = api_get_local_time($endDate, null, null, false, false);
596
597
            $label = sprintf(get_lang('LastXDays'), $day);
598
            if ($day == 1) {
599
                $label = get_lang('Today');
600
            }
601
            $label .= " <br /> $localDate - $localEndDate";
602
            $sql = "SELECT count($field) AS number 
603
                    FROM $table $table_url 
604
                    WHERE 
605
                        UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND
606
                        login_date BETWEEN '$startDate' AND '$endDate'  
607
                        $where_url";
608
            $sqlList[$label] = $sql;
609
        }
610
611
        $sql = "SELECT count($field) AS number 
612
                FROM $table $table_url                
613
                WHERE UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration $where_url
614
               ";
615
        $sqlList[get_lang('Total')] = $sql;
616
        $totalLogin = [];
617
        foreach ($sqlList as $label => $query) {
618
            $res = Database::query($query);
619
            $obj = Database::fetch_object($res);
620
            $totalLogin[$label] = $obj->number;
621
        }
622
        if ($distinct) {
623
            self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false);
624
        } else {
625
            self::printStats(get_lang('Logins'), $totalLogin, false);
626
        }
627
    }
628
629
    /**
630
     * get the number of recent logins.
631
     *
632
     * @param bool $distinct        Whether to only give distinct users stats, or *all* logins
633
     * @param int  $sessionDuration
634
     *
635
     * @return array
636
     */
637
    public static function getRecentLoginStats($distinct = false, $sessionDuration = 0)
638
    {
639
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
640
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
641
        $urlId = api_get_current_access_url_id();
642
        $table_url = '';
643
        $where_url = '';
644
        if (api_is_multiple_url_enabled()) {
645
            $table_url = ", $access_url_rel_user_table";
646
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
647
        }
648
649
        $now = api_get_utc_datetime();
650
        $date = new DateTime($now);
651
        $date->sub(new DateInterval('P15D'));
652
        $newDate = $date->format('Y-m-d h:i:s');
653
654
        $field = 'login_id';
655
        if ($distinct) {
656
            $field = 'DISTINCT(login_user_id)';
657
        }
658
        $sessionDuration = (int) $sessionDuration;
659
660
        $sql = "SELECT count($field) AS number, date(login_date) as login_date
661
                FROM $table $table_url 
662
                WHERE 
663
                UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND
664
                login_date >= '$newDate' $where_url 
665
                GROUP BY date(login_date)";
666
667
        $res = Database::query($sql);
668
        $totalLogin = [];
669
        while ($row = Database::fetch_array($res, 'ASSOC')) {
670
            $totalLogin[$row['login_date']] = $row['number'];
671
        }
672
673
        return $totalLogin;
674
    }
675
676
    /**
677
     * Get course tools usage statistics for the whole platform (by URL if multi-url).
678
     */
679
    public static function getToolsStats()
680
    {
681
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
682
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
683
        $urlId = api_get_current_access_url_id();
684
685
        $tools = [
686
            'announcement',
687
            'assignment',
688
            'calendar_event',
689
            'chat',
690
            'course_description',
691
            'document',
692
            'dropbox',
693
            'group',
694
            'learnpath',
695
            'link',
696
            'quiz',
697
            'student_publication',
698
            'user',
699
            'forum',
700
        ];
701
        $tool_names = [];
702
        foreach ($tools as $tool) {
703
            $tool_names[$tool] = get_lang(ucfirst($tool), '');
704
        }
705
        if (api_is_multiple_url_enabled()) {
706
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
707
                    FROM $table t , $access_url_rel_course_table a
708
                    WHERE
709
                        access_tool IN ('".implode("','", $tools)."') AND
710
                        t.c_id = a.c_id AND
711
                        access_url_id='".$urlId."'
712
                        GROUP BY access_tool
713
                    ";
714
        } else {
715
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
716
                    FROM $table
717
                    WHERE access_tool IN ('".implode("','", $tools)."')
718
                    GROUP BY access_tool ";
719
        }
720
721
        $res = Database::query($sql);
722
        $result = [];
723
        while ($obj = Database::fetch_object($res)) {
724
            $result[$tool_names[$obj->access_tool]] = $obj->number_of_logins;
725
        }
726
727
        return $result;
728
    }
729
730
    /**
731
     * Show some stats about the accesses to the different course tools.
732
     *
733
     * @param array $result If defined, this serves as data. Otherwise, will get the data from getToolsStats()
734
     */
735
    public static function printToolStats($result = null)
736
    {
737
        if (empty($result)) {
738
            $result = self::getToolsStats();
739
        }
740
        self::printStats(get_lang('PlatformToolAccess'), $result, true);
741
    }
742
743
    /**
744
     * Show some stats about the number of courses per language.
745
     */
746
    public static function printCourseByLanguageStats()
747
    {
748
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
749
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
750
        $urlId = api_get_current_access_url_id();
751
        if (api_is_multiple_url_enabled()) {
752
            $sql = "SELECT course_language, count( c.code ) AS number_of_courses
753
                    FROM $table as c, $access_url_rel_course_table as u
754
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'
755
                    GROUP BY course_language
756
                    ORDER BY number_of_courses DESC";
757
        } else {
758
            $sql = "SELECT course_language, count( code ) AS number_of_courses
759
                   FROM $table GROUP BY course_language
760
                   ORDER BY number_of_courses DESC";
761
        }
762
        $res = Database::query($sql);
763
        $result = [];
764
        while ($obj = Database::fetch_object($res)) {
765
            $result[$obj->course_language] = $obj->number_of_courses;
766
        }
767
        self::printStats(get_lang('CountCourseByLanguage'), $result, true);
768
    }
769
770
    /**
771
     * Shows the number of users having their picture uploaded in Dokeos.
772
     */
773
    public static function printUserPicturesStats()
774
    {
775
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
776
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
777
        $urlId = api_get_current_access_url_id();
778
        $url_condition = null;
779
        $url_condition2 = null;
780
        $table = null;
781
        if (api_is_multiple_url_enabled()) {
782
            $url_condition = ", $access_url_rel_user_table as url WHERE url.user_id=u.user_id AND access_url_id='".$urlId."'";
783
            $url_condition2 = " AND url.user_id=u.user_id AND access_url_id='".$urlId."'";
784
            $table = ", $access_url_rel_user_table as url ";
785
        }
786
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u ".$url_condition;
787
        $res = Database::query($sql);
788
        $count1 = Database::fetch_object($res);
789
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u $table ".
790
               "WHERE LENGTH(picture_uri) > 0 $url_condition2";
791
        $res = Database::query($sql);
792
        $count2 = Database::fetch_object($res);
793
        // #users without picture
794
        $result[get_lang('No')] = $count1->n - $count2->n;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
795
        $result[get_lang('Yes')] = $count2->n; // #users with picture
796
797
        self::printStats(get_lang('CountUsers').' ('.get_lang('UserPicture').')', $result, true);
798
    }
799
800
    /**
801
     * Important activities.
802
     */
803
    public static function printActivitiesStats()
804
    {
805
        echo '<h4>'.get_lang('ImportantActivities').'</h4>';
806
        // Create a search-box
807
        $form = new FormValidator(
808
            'search_simple',
809
            'get',
810
            api_get_path(WEB_CODE_PATH).'admin/statistics/index.php',
811
            '',
812
            'width=200px',
813
            false
814
        );
815
        $renderer = &$form->defaultRenderer();
816
        $renderer->setCustomElementTemplate('<span>{element}</span> ');
817
        $form->addHidden('report', 'activities');
818
        $form->addHidden('activities_direction', 'DESC');
819
        $form->addHidden('activities_column', '4');
820
        $form->addElement('text', 'keyword', get_lang('Keyword'));
821
        $form->addButtonSearch(get_lang('Search'), 'submit');
822
        echo '<div class="actions">';
823
        $form->display();
824
        echo '</div>';
825
826
        $table = new SortableTable(
827
            'activities',
828
            ['Statistics', 'getNumberOfActivities'],
829
            ['Statistics', 'getActivitiesData'],
830
            7,
831
            50,
832
            'DESC'
833
        );
834
        $parameters = [];
835
836
        $parameters['report'] = 'activities';
837
        if (isset($_GET['keyword'])) {
838
            $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
839
        }
840
841
        $table->set_additional_parameters($parameters);
842
        $table->set_header(0, get_lang('EventType'));
843
        $table->set_header(1, get_lang('DataType'));
844
        $table->set_header(2, get_lang('Value'));
845
        $table->set_header(3, get_lang('Course'));
846
        $table->set_header(4, get_lang('Session'));
847
        $table->set_header(5, get_lang('UserName'));
848
        $table->set_header(6, get_lang('IPAddress'));
849
        $table->set_header(7, get_lang('Date'));
850
        $table->display();
851
    }
852
853
    /**
854
     * Shows statistics about the time of last visit to each course.
855
     */
856
    public static function printCourseLastVisit()
857
    {
858
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
859
        $urlId = api_get_current_access_url_id();
860
861
        $columns[0] = 't.c_id';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$columns was never initialized. Although not strictly required by PHP, it is generally a good practice to add $columns = array(); before regardless.
Loading history...
862
        $columns[1] = 'access_date';
863
        $sql_order[SORT_ASC] = 'ASC';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql_order was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql_order = array(); before regardless.
Loading history...
864
        $sql_order[SORT_DESC] = 'DESC';
865
        $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
866
        $page_nr = isset($_GET['page_nr']) ? intval($_GET['page_nr']) : 1;
867
        $column = isset($_GET['column']) ? intval($_GET['column']) : 0;
868
        $direction = isset($_GET['direction']) ? $_GET['direction'] : SORT_ASC;
869
870
        if (!in_array($direction, [SORT_ASC, SORT_DESC])) {
871
            $direction = SORT_ASC;
872
        }
873
        $form = new FormValidator('courselastvisit', 'get');
874
        $form->addElement('hidden', 'report', 'courselastvisit');
875
        $form->addText('date_diff', get_lang('Days'), true);
876
        $form->addRule('date_diff', 'InvalidNumber', 'numeric');
877
        $form->addButtonSearch(get_lang('Search'), 'submit');
878
        if (!isset($_GET['date_diff'])) {
879
            $defaults['date_diff'] = 60;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$defaults was never initialized. Although not strictly required by PHP, it is generally a good practice to add $defaults = array(); before regardless.
Loading history...
880
        } else {
881
            $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']);
882
        }
883
        $form->setDefaults($defaults);
884
        $form->display();
885
        $values = $form->exportValues();
886
        $date_diff = $values['date_diff'];
887
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
888
        if (api_is_multiple_url_enabled()) {
889
            $sql = "SELECT * FROM $table t , $access_url_rel_course_table a
890
                   WHERE
891
                        t.c_id = a.c_id AND
892
                        access_url_id='".$urlId."'
893
                   GROUP BY t.c_id
894
                   HAVING t.c_id <> ''
895
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
896
        } else {
897
            $sql = "SELECT * FROM $table t
898
                   GROUP BY t.c_id
899
                   HAVING t.c_id <> ''
900
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
901
        }
902
        $sql .= ' ORDER BY '.$columns[$column].' '.$sql_order[$direction];
903
        $from = ($page_nr - 1) * $per_page;
904
        $sql .= ' LIMIT '.$from.','.$per_page;
905
906
        echo '<p>'.get_lang('LastAccess').' &gt;= '.$date_diff.' '.get_lang('Days').'</p>';
907
        $res = Database::query($sql);
908
        if (Database::num_rows($res) > 0) {
909
            $courses = [];
910
            while ($obj = Database::fetch_object($res)) {
911
                $courseInfo = api_get_course_info_by_id($obj->c_id);
912
                $course = [];
913
                $course[] = '<a href="'.api_get_path(WEB_COURSE_PATH).$courseInfo['code'].'">'.$courseInfo['code'].' <a>';
914
                // Allow sort by date hiding the numerical date
915
                $course[] = '<span style="display:none;">'.$obj->access_date.'</span>'.api_convert_and_format_date($obj->access_date);
916
                $courses[] = $course;
917
            }
918
            $parameters['date_diff'] = $date_diff;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$parameters was never initialized. Although not strictly required by PHP, it is generally a good practice to add $parameters = array(); before regardless.
Loading history...
919
            $parameters['report'] = 'courselastvisit';
920
            $table_header[] = [get_lang("CourseCode"), true];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$table_header was never initialized. Although not strictly required by PHP, it is generally a good practice to add $table_header = array(); before regardless.
Loading history...
921
            $table_header[] = [get_lang("LastAccess"), true];
922
            Display:: display_sortable_table(
923
                $table_header,
924
                $courses,
925
                ['column' => $column, 'direction' => $direction],
926
                [],
927
                $parameters
928
            );
929
        } else {
930
            echo get_lang('NoSearchResults');
931
        }
932
    }
933
934
    /**
935
     * Displays the statistics of the messages sent and received by each user in the social network.
936
     *
937
     * @param string $messageType Type of message: 'sent' or 'received'
938
     *
939
     * @return array Message list
940
     */
941
    public static function getMessages($messageType)
942
    {
943
        $message_table = Database::get_main_table(TABLE_MESSAGE);
944
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
945
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
946
947
        $urlId = api_get_current_access_url_id();
948
949
        switch ($messageType) {
950
            case 'sent':
951
                $field = 'user_sender_id';
952
                break;
953
            case 'received':
954
                $field = 'user_receiver_id';
955
                break;
956
        }
957
958
        if (api_is_multiple_url_enabled()) {
959
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message 
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.
Loading history...
960
                FROM $access_url_rel_user_table as url, $message_table m 
961
                LEFT JOIN $user_table u ON m.$field = u.user_id 
962
                WHERE  url.user_id = m.$field AND  access_url_id='".$urlId."' 
963
                GROUP BY m.$field 
964
                ORDER BY count_message DESC ";
965
        } else {
966
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message 
967
                FROM $message_table m 
968
                LEFT JOIN $user_table u ON m.$field = u.user_id 
969
                GROUP BY m.$field ORDER BY count_message DESC ";
970
        }
971
        $res = Database::query($sql);
972
        $messages_sent = [];
973
        while ($messages = Database::fetch_array($res)) {
974
            if (empty($messages['username'])) {
975
                $messages['username'] = get_lang('Unknown');
976
            }
977
            $users = api_get_person_name(
978
                $messages['firstname'],
979
                $messages['lastname']
980
            ).'<br />('.$messages['username'].')';
981
            $messages_sent[$users] = $messages['count_message'];
982
        }
983
984
        return $messages_sent;
985
    }
986
987
    /**
988
     * Count the number of friends for social network users.
989
     */
990
    public static function getFriends()
991
    {
992
        $user_friend_table = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
993
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
994
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
995
        $urlId = api_get_current_access_url_id();
996
997
        if (api_is_multiple_url_enabled()) {
998
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend 
999
                    FROM $access_url_rel_user_table as url, $user_friend_table uf 
1000
                    LEFT JOIN $user_table u 
1001
                    ON (uf.user_id = u.user_id) 
1002
                    WHERE 
1003
                        uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' AND 
1004
                        uf.user_id = url.user_id AND  
1005
                        access_url_id = '".$urlId."' 
1006
                    GROUP BY uf.user_id 
1007
                    ORDER BY count_friend DESC ";
1008
        } else {
1009
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend 
1010
                    FROM $user_friend_table uf 
1011
                    LEFT JOIN $user_table u 
1012
                    ON (uf.user_id = u.user_id) 
1013
                    WHERE uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' 
1014
                    GROUP BY uf.user_id 
1015
                    ORDER BY count_friend DESC ";
1016
        }
1017
        $res = Database::query($sql);
1018
        $list_friends = [];
1019
        while ($friends = Database::fetch_array($res)) {
1020
            $users = api_get_person_name($friends['firstname'], $friends['lastname']).'<br />('.$friends['username'].')';
1021
            $list_friends[$users] = $friends['count_friend'];
1022
        }
1023
1024
        return $list_friends;
1025
    }
1026
1027
    /**
1028
     * Print the number of users that didn't login for a certain period of time.
1029
     */
1030
    public static function printUsersNotLoggedInStats()
1031
    {
1032
        $totalLogin = [];
1033
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1034
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1035
        $urlId = api_get_current_access_url_id();
1036
        $total = self::countUsers();
1037
        if (api_is_multiple_url_enabled()) {
1038
            $table_url = ", $access_url_rel_user_table";
1039
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
1040
        } else {
1041
            $table_url = '';
1042
            $where_url = '';
1043
        }
1044
        $now = api_get_utc_datetime();
1045
        $sql[get_lang('ThisDay')] =
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.
Loading history...
1046
            "SELECT count(distinct(login_user_id)) AS number ".
1047
            " FROM $table $table_url ".
1048
            " WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
1049
        $sql[get_lang('Last7days')] =
1050
            "SELECT count(distinct(login_user_id)) AS number ".
1051
            " FROM $table $table_url ".
1052
            " WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
1053
        $sql[get_lang('Last31days')] =
1054
            "SELECT count(distinct(login_user_id)) AS number ".
1055
            " FROM $table $table_url ".
1056
            " WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
1057
        $sql[sprintf(get_lang('LastXMonths'), 6)] =
1058
            "SELECT count(distinct(login_user_id)) AS number ".
1059
            " FROM $table $table_url ".
1060
            " WHERE DATE_ADD(login_date, INTERVAL 6 MONTH) >= '$now' $where_url";
1061
        $sql[get_lang('NeverConnected')] =
1062
            "SELECT count(distinct(login_user_id)) AS number ".
1063
            " FROM $table $table_url WHERE 1=1 $where_url";
1064
        foreach ($sql as $index => $query) {
1065
            $res = Database::query($query);
1066
            $obj = Database::fetch_object($res);
1067
            $r = $total - $obj->number;
1068
            $totalLogin[$index] = $r < 0 ? 0 : $r;
1069
        }
1070
        self::printStats(
1071
            get_lang('StatsUsersDidNotLoginInLastPeriods'),
1072
            $totalLogin,
1073
            false
1074
        );
1075
    }
1076
}
1077