Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

Statistics   F

Complexity

Total Complexity 119

Size/Duplication

Total Lines 1051
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1051
rs 0.941
c 0
b 0
f 0
wmc 119

21 Methods

Rating   Name   Duplication   Size   Complexity  
B printUsersNotLoggedInStats() 0 44 4
B countCourses() 0 24 4
B getNumberOfActivities() 0 39 4
C printLoginStats() 0 87 10
B getMessages() 0 43 6
B countCoursesByVisibility() 0 25 5
B getFriends() 0 35 3
B makeSizeString() 0 13 7
A getCourseCategories() 0 13 2
F getActivitiesData() 0 140 18
B getRecentLoginStats() 0 34 4
C printRecentLoginStats() 0 62 8
A printActivitiesStats() 0 48 2
A printCourseByLanguageStats() 0 22 3
B countUsers() 0 53 7
A rescale() 0 14 4
B printToolStats() 0 49 4
D printStats() 0 42 10
A countSessions() 0 19 2
B printUserPicturesStats() 0 25 2
D printCourseLastVisit() 0 75 10

How to fix   Complexity   

Complex Class

Complex classes like Statistics often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Statistics, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
* This class provides some functions for statistics
6
* @package chamilo.statistics
7
*/
8
class Statistics
9
{
10
    /**
11
     * Converts a number of bytes in a formatted string
12
     * @param int $size
13
     * @return string Formatted file size
14
     */
15
    public static function makeSizeString($size)
16
    {
17
        if ($size < pow(2, 10)) {
18
            return $size." bytes";
19
        }
20
        if ($size >= pow(2, 10) && $size < pow(2, 20)) {
21
            return round($size / pow(2, 10), 0)." KB";
22
        }
23
        if ($size >= pow(2, 20) && $size < pow(2, 30)) {
24
            return round($size / pow(2, 20), 1)." MB";
25
        }
26
        if ($size > pow(2, 30)) {
27
            return round($size / pow(2, 30), 2)." GB";
28
        }
29
    }
30
31
    /**
32
     * Count courses
33
     * @param string $categoryCode  Code of a course category.
34
     * Default: count all courses.
35
     * @return int Number of courses counted
36
     */
37
    public static function countCourses($categoryCode = null)
38
    {
39
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
40
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
41
        $current_url_id = api_get_current_access_url_id();
42
        if (api_is_multiple_url_enabled()) {
43
            $sql = "SELECT COUNT(*) AS number
44
                    FROM ".$course_table." as c, $access_url_rel_course_table as u
45
                    WHERE u.c_id = c.id AND access_url_id='".$current_url_id."'";
46
            if (isset($categoryCode)) {
47
                $sql .= " AND category_code = '".Database::escape_string($categoryCode)."'";
48
            }
49
        } else {
50
            $sql = "SELECT COUNT(*) AS number
51
                    FROM $course_table";
52
            if (isset($categoryCode)) {
53
                $sql .= " WHERE category_code = '".Database::escape_string($categoryCode)."'";
54
            }
55
        }
56
57
        $res = Database::query($sql);
58
        $obj = Database::fetch_object($res);
59
60
        return $obj->number;
61
    }
62
63
    /**
64
     * Count courses by visibility
65
     * @param int $visibility Visibility (0 = closed, 1 = private, 2 = open, 3 = public) all courses.
66
     * @return int Number of courses counted
67
     */
68
    public static function countCoursesByVisibility($visibility = null)
69
    {
70
        if (!isset($visibility)) {
71
            return 0;
72
        }
73
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
74
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
75
        $current_url_id = api_get_current_access_url_id();
76
        if (api_is_multiple_url_enabled()) {
77
            $sql = "SELECT COUNT(*) AS number
78
                    FROM $course_table as c, $access_url_rel_course_table as u
79
                    WHERE u.c_id = c.id AND access_url_id='".$current_url_id."'";
80
            if (isset($visibility)) {
81
                $sql .= " AND visibility = ".intval($visibility);
82
            }
83
        } else {
84
            $sql = "SELECT COUNT(*) AS number FROM ".$course_table." ";
85
            if (isset($visibility)) {
86
                $sql .= " WHERE visibility = ".intval($visibility);
87
            }
88
        }
89
        $res = Database::query($sql);
90
        $obj = Database::fetch_object($res);
91
92
        return $obj->number;
93
    }
94
95
    /**
96
     * Count users
97
     * @param int $status Optional user status (COURSEMANAGER or STUDENT), if it's not setted it'll count all users.
98
     * @param string $categoryCode Optional, code of a course category. Default: count only users without filtering category
99
     * @param bool $countInvisibleCourses Count invisible courses (todo)
100
     * @param bool $onlyActive Count only active users (false to only return currently active users)
101
     * @return int Number of users counted
102
     */
103
    public static function countUsers(
104
        $status = null,
105
        $categoryCode = null,
106
        $countInvisibleCourses = true,
107
        $onlyActive = false
108
    ) {
109
        // Database table definitions
110
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
111
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
112
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
113
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
114
        $current_url_id = api_get_current_access_url_id();
115
        $active_filter = $onlyActive ? ' AND active=1' : '';
116
        $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
117
118
        if (api_is_multiple_url_enabled()) {
119
            $sql = "SELECT COUNT(DISTINCT(u.user_id)) AS number
120
                    FROM $user_table as u, $access_url_rel_user_table as url
121
                    WHERE
122
                        u.user_id = url.user_id AND
123
                        access_url_id = '".$current_url_id."'
124
                        $status_filter $active_filter";
125
            if (isset($categoryCode)) {
126
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
127
                        FROM $course_user_table cu, $course_table c, $access_url_rel_user_table as url
128
                        WHERE
129
                            c.id = cu.c_id AND
130
                            c.category_code = '".Database::escape_string($categoryCode)."' AND
131
                            cu.user_id = url.user_id AND
132
                            access_url_id='".$current_url_id."'
133
                            $status_filter $active_filter";
134
            }
135
        } else {
136
            $sql = "SELECT COUNT(DISTINCT(user_id)) AS number
137
                    FROM $user_table
138
                    WHERE 1=1 $status_filter $active_filter";
139
            if (isset($categoryCode)) {
140
                $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
141
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
142
                        FROM $course_user_table cu, $course_table c
143
                        WHERE
144
                            c.id = cu.c_id AND
145
                            c.category_code = '".Database::escape_string($categoryCode)."'
146
                            $status_filter
147
                            $active_filter
148
                        ";
149
            }
150
        }
151
152
        $res = Database::query($sql);
153
        $obj = Database::fetch_object($res);
154
155
        return $obj->number;
156
    }
157
158
    /**
159
     * Count sessions
160
     * @return int Number of sessions counted
161
     */
162
    public static function countSessions()
163
    {
164
        $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
165
        $access_url_rel_session_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
166
        if (api_is_multiple_url_enabled()) {
167
            $current_url_id = api_get_current_access_url_id();
168
            $sql = "SELECT COUNT(id) AS number
169
                    FROM $session_table as s, $access_url_rel_session_table as u
170
                    WHERE 
171
                        u.session_id = s.id AND 
172
                        access_url_id = '".$current_url_id."'";
173
        } else {
174
            $sql = "SELECT COUNT(id) AS number
175
                    FROM $session_table";
176
        }
177
        $res = Database::query($sql);
178
        $obj = Database::fetch_object($res);
179
180
        return $obj->number;
181
    }
182
183
    /**
184
     * Count activities from track_e_default_table
185
     * @return int Number of activities counted
186
     */
187
    public static function getNumberOfActivities($courseId = 0, $sessionId = 0)
188
    {
189
        // Database table definitions
190
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
191
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
192
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
193
        $current_url_id = api_get_current_access_url_id();
194
        if (api_is_multiple_url_enabled()) {
195
            $sql = "SELECT count(default_id) AS total_number_of_items
196
                    FROM $track_e_default, $table_user user, $access_url_rel_user_table url
197
                    WHERE
198
                        default_user_id = user.user_id AND
199
                        user.user_id=url.user_id AND
200
                        access_url_id = '".$current_url_id."'";
201
        } else {
202
            $sql = "SELECT count(default_id) AS total_number_of_items
203
                    FROM $track_e_default, $table_user user
204
                    WHERE default_user_id = user.user_id ";
205
        }
206
207
        if (!empty($courseId)) {
208
            $courseId = (int) $courseId;
209
            $sql .= " AND c_id = $courseId";
210
            $sql .= api_get_session_condition($sessionId);
211
        }
212
213
        if (isset($_GET['keyword'])) {
214
            $keyword = Database::escape_string(trim($_GET['keyword']));
215
            $sql .= " AND (
216
                        user.username LIKE '%".$keyword."%' OR 
217
                        default_event_type LIKE '%".$keyword."%' OR 
218
                        default_value_type LIKE '%".$keyword."%' OR 
219
                        default_value LIKE '%".$keyword."%') ";
220
        }
221
222
        $res = Database::query($sql);
223
        $obj = Database::fetch_object($res);
224
225
        return $obj->total_number_of_items;
226
    }
227
228
    /**
229
     * Get activities data to display
230
     * @param int $from
231
     * @param int $numberOfItems
232
     * @param int $column
233
     * @param string $direction
234
     * @param int $courseId
235
     * @param int $sessionId
236
     *
237
     * @return array
238
     */
239
    public static function getActivitiesData(
240
        $from,
241
        $numberOfItems,
242
        $column,
243
        $direction,
244
        $courseId = 0,
245
        $sessionId = 0
246
    ) {
247
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
248
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
249
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
250
        $current_url_id = api_get_current_access_url_id();
251
        $column = intval($column);
252
        $from = intval($from);
253
        $numberOfItems = intval($numberOfItems);
254
        $direction = strtoupper($direction);
255
256
        if (!in_array($direction, ['ASC', 'DESC'])) {
257
            $direction = 'DESC';
258
        }
259
260
        if (api_is_multiple_url_enabled()) {
261
            $sql = "SELECT
262
                        default_event_type  as col0,
263
                        default_value_type    as col1,
264
                        default_value        as col2,
265
                        c_id         as col3,
266
                        session_id as col4,
267
                        user.username         as col5,
268
                        user.user_id         as col6,
269
                        default_date         as col7
270
                    FROM $track_e_default as track_default,
271
                    $table_user as user,
272
                    $access_url_rel_user_table as url
273
                    WHERE
274
                        track_default.default_user_id = user.user_id AND
275
                        url.user_id = user.user_id AND
276
                        access_url_id= $current_url_id ";
277
        } else {
278
            $sql = "SELECT
279
                       default_event_type  as col0,
280
                       default_value_type    as col1,
281
                       default_value        as col2,
282
                       c_id         as col3,
283
                       session_id as col4,
284
                       user.username         as col5,
285
                       user.user_id         as col6,
286
                       default_date         as col7
287
                   FROM $track_e_default track_default, $table_user user
288
                   WHERE track_default.default_user_id = user.user_id ";
289
        }
290
291
        if (isset($_GET['keyword'])) {
292
            $keyword = Database::escape_string(trim($_GET['keyword']));
293
            $sql .= " AND (user.username LIKE '%".$keyword."%' OR
294
                        default_event_type LIKE '%".$keyword."%' OR
295
                        default_value_type LIKE '%".$keyword."%' OR
296
                        default_value LIKE '%".$keyword."%') ";
297
        }
298
299
        if (!empty($courseId)) {
300
            $courseId = (int) $courseId;
301
            $sql .= " AND c_id = $courseId";
302
            $sql .= api_get_session_condition($sessionId);
303
        }
304
305
        if (!empty($column) && !empty($direction)) {
306
            $sql .= " ORDER BY col$column $direction";
307
        } else {
308
            $sql .= " ORDER BY col7 DESC ";
309
        }
310
        $sql .= " LIMIT $from, $numberOfItems ";
311
312
        $res = Database::query($sql);
313
        $activities = [];
314
        while ($row = Database::fetch_row($res)) {
315
            if (strpos($row[1], '_object') === false &&
316
                strpos($row[1], '_array') === false
317
            ) {
318
                $row[2] = $row[2];
319
            } else {
320
                if (!empty($row[2])) {
321
                    $originalData = str_replace('\\', '', $row[2]);
322
                    $row[2] = unserialize($originalData);
323
                    if (is_array($row[2]) && !empty($row[2])) {
324
                        $row[2] = implode_with_key(', ', $row[2]);
325
                    } else {
326
                        $row[2] = $originalData;
327
                    }
328
                }
329
            }
330
331
            if (!empty($row['default_date'])) {
332
                $row['default_date'] = api_get_local_time($row['default_date']);
333
            } else {
334
                $row['default_date'] = '-';
335
            }
336
337
            if (!empty($row[5])) {
338
339
                // Course
340
                if (!empty($row[3])) {
341
                    $row[3] = Display::url(
342
                        $row[3],
343
                        api_get_path(WEB_CODE_PATH).'admin/course_edit.php?id='.$row[3]
344
                    );
345
                } else {
346
                    $row[3] = '-';
347
                }
348
349
                // session
350
                if (!empty($row[4])) {
351
                    $row[4] = Display::url(
352
                        $row[4],
353
                        api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$row[4]
354
                    );
355
                } else {
356
                    $row[4] = '-';
357
                }
358
359
                // User id.
360
                $row[5] = Display::url(
361
                    $row[5],
362
                    api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$row[6],
363
                    ['class' => 'ajax']
364
                );
365
366
                $row[6] = Tracking::get_ip_from_user_event(
367
                    $row[6],
368
                    $row[7],
369
                    true
370
                );
371
                if (empty($row[6])) {
372
                    $row[6] = get_lang('Unknown');
373
                }
374
            }
375
            $activities[] = $row;
376
        }
377
378
        return $activities;
379
    }
380
381
    /**
382
     * Get all course categories
383
     * @return array All course categories (code => name)
384
     */
385
    public static function getCourseCategories()
386
    {
387
        $categoryTable = Database::get_main_table(TABLE_MAIN_CATEGORY);
388
        $sql = "SELECT code, name 
389
                FROM $categoryTable
390
                ORDER BY tree_pos";
391
        $res = Database::query($sql);
392
        $categories = [];
393
        while ($category = Database::fetch_object($res)) {
394
            $categories[$category->code] = $category->name;
395
        }
396
397
        return $categories;
398
    }
399
400
    /**
401
     * Rescale data
402
     * @param array $data The data that should be rescaled
403
     * @param int $max The maximum value in the rescaled data (default = 500);
404
     * @return array The rescaled data, same key as $data
405
     */
406
    public static function rescale($data, $max = 500)
407
    {
408
        $data_max = 1;
409
        foreach ($data as $index => $value) {
410
            $data_max = ($data_max < $value ? $value : $data_max);
411
        }
412
        reset($data);
413
        $result = [];
414
        $delta = $max / $data_max;
415
        foreach ($data as $index => $value) {
416
            $result[$index] = (int) round($value * $delta);
417
        }
418
419
        return $result;
420
    }
421
422
    /**
423
     * Show statistics
424
     * @param string $title The title
425
     * @param array $stats
426
     * @param bool $showTotal
427
     * @param bool $isFileSize
428
     */
429
    public static function printStats(
430
        $title,
431
        $stats,
432
        $showTotal = true,
433
        $isFileSize = false
434
    ) {
435
        $total = 0;
436
        $data = self::rescale($stats);
437
        echo '<table class="data_table" cellspacing="0" cellpadding="3">
438
                <tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr>';
439
        $i = 0;
440
        foreach ($stats as $subtitle => $number) {
441
            $total += $number;
442
        }
443
444
        foreach ($stats as $subtitle => $number) {
445
            if (!$isFileSize) {
446
                $number_label = number_format($number, 0, ',', '.');
447
            } else {
448
                $number_label = self::makeSizeString($number);
449
            }
450
            $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
451
452
            echo '<tr class="row_'.($i % 2 == 0 ? 'odd' : 'even').'">
453
                    <td width="150">'.$subtitle.'</td>
454
                    <td width="550">'.Display::bar_progress($percentage, false).'</td>
455
                    <td align="right">'.$number_label.'</td>';
456
            if ($showTotal) {
457
                echo '<td align="right"> '.$percentage.'%</td>';
458
            }
459
            echo '</tr>';
460
            $i++;
461
        }
462
        if ($showTotal) {
463
            if (!$isFileSize) {
464
                $total_label = number_format($total, 0, ',', '.');
465
            } else {
466
                $total_label = self::makeSizeString($total);
467
            }
468
            echo '<tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr>';
469
        }
470
        echo '</table>';
471
    }
472
473
    /**
474
     * Show some stats about the number of logins
475
     * @param string $type month, hour or day
476
     */
477
    public static function printLoginStats($type)
478
    {
479
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
480
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
481
        $current_url_id = api_get_current_access_url_id();
482
483
        $table_url = null;
484
        $where_url = null;
485
        $now = api_get_utc_datetime();
486
        $where_url_last = ' WHERE login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
487
        if (api_is_multiple_url_enabled()) {
488
            $table_url = ", $access_url_rel_user_table";
489
            $where_url = " WHERE login_user_id=user_id AND access_url_id='".$current_url_id."'";
490
            $where_url_last = ' AND login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
491
        }
492
493
        $period = get_lang('PeriodMonth');
494
        $periodCollection = api_get_months_long();
495
        $sql = "SELECT 
496
                DATE_FORMAT( login_date, '%Y-%m' ) AS stat_date , 
497
                count( login_id ) AS number_of_logins
498
                FROM $table $table_url $where_url
499
                GROUP BY stat_date
500
                ORDER BY login_date DESC";
501
        $sql_last_x = null;
502
503
        switch ($type) {
504
            case 'hour':
505
                $period = get_lang('PeriodHour');
506
                $sql = "SELECT 
507
                          DATE_FORMAT( login_date, '%H') AS stat_date, 
508
                          count( login_id ) AS number_of_logins 
509
                        FROM $table $table_url $where_url 
510
                        GROUP BY stat_date 
511
                        ORDER BY stat_date ";
512
                $sql_last_x = "SELECT 
513
                                DATE_FORMAT( login_date, '%H' ) AS stat_date, 
514
                                count( login_id ) AS number_of_logins 
515
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'DAY')." 
516
                               GROUP BY stat_date 
517
                               ORDER BY stat_date ";
518
                break;
519
            case 'day':
520
                $periodCollection = api_get_week_days_long();
521
                $period = get_lang('PeriodDay');
522
                $sql = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date , 
523
                        count( login_id ) AS number_of_logins 
524
                        FROM  $table $table_url $where_url 
525
                        GROUP BY stat_date 
526
                        ORDER BY DATE_FORMAT( login_date, '%w' ) ";
527
                $sql_last_x = "SELECT 
528
                                DATE_FORMAT( login_date, '%w' ) AS stat_date, 
529
                                count( login_id ) AS number_of_logins 
530
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'WEEK')." 
531
                               GROUP BY stat_date 
532
                               ORDER BY DATE_FORMAT( login_date, '%w' ) ";
533
                break;
534
        }
535
536
        if ($sql_last_x) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sql_last_x of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
537
            $res_last_x = Database::query($sql_last_x);
538
            $result_last_x = [];
539
            while ($obj = Database::fetch_object($res_last_x)) {
540
                $stat_date = ($type === 'day') ? $periodCollection[$obj->stat_date] : $obj->stat_date;
541
                $result_last_x[$stat_date] = $obj->number_of_logins;
542
            }
543
            self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true);
544
            flush(); //flush web request at this point to see something already while the full data set is loading
545
            echo '<br />';
546
        }
547
        $res = Database::query($sql);
548
        $result = [];
549
        while ($obj = Database::fetch_object($res)) {
550
            $stat_date = $obj->stat_date;
551
            switch ($type) {
552
                case 'month':
553
                    $stat_date = explode('-', $stat_date);
554
                    $stat_date[1] = $periodCollection[$stat_date[1] - 1];
555
                    $stat_date = implode(' ', $stat_date);
556
                    break;
557
                case 'day':
558
                    $stat_date = $periodCollection[$stat_date];
559
                    break;
560
            }
561
            $result[$stat_date] = $obj->number_of_logins;
562
        }
563
        self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true);
564
    }
565
566
    /**
567
     * Print the number of recent logins
568
     * @param   bool    $distinct   Whether to only give distinct users stats, or *all* logins
569
     * @return void
570
     */
571
    public static function printRecentLoginStats($distinct = false)
572
    {
573
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
574
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
575
        $current_url_id = api_get_current_access_url_id();
576
        if (api_is_multiple_url_enabled()) {
577
            $table_url = ", $access_url_rel_user_table";
578
            $where_url = " AND login_user_id=user_id AND access_url_id='".$current_url_id."'";
579
        } else {
580
            $table_url = '';
581
            $where_url = '';
582
        }
583
        $now = api_get_utc_datetime();
584
        $field = 'login_id';
585
        if ($distinct) {
586
            $field = 'DISTINCT(login_user_id)';
587
        }
588
589
        $days = [
590
            1,
591
            7,
592
            15,
593
            31,
594
        ];
595
596
        $sqlList = [];
597
        foreach ($days as $day) {
598
            $date = new DateTime($now);
599
            if ($day > 1) {
600
                $date->sub(new DateInterval('P'.$day.'D'));
601
            }
602
            $startDate = $date->format('Y-m-d').' 00:00:00';
603
            $endDate = $date->format('Y-m-d').' 23:59:59';
604
605
            $localDate = api_get_local_time($startDate, null, null, false, false);
606
            if ($day == 1) {
607
                $label = get_lang('Today');
608
            } else {
609
                $label = sprintf(get_lang('LastXDays'), $day);
610
            }
611
612
            $label .= ' <br /> ('.$localDate.')';
613
614
            $sql = "SELECT count($field) AS number 
615
                    FROM $table $table_url 
616
                    WHERE 
617
                        login_date BETWEEN '$startDate' AND '$endDate'
618
                        $where_url";
619
            $sqlList[$label] = $sql;
620
        }
621
        $sqlList[get_lang('Total')] = "SELECT count($field) AS number FROM $table $table_url WHERE 1=1 $where_url";
622
623
        $totalLogin = [];
624
        foreach ($sqlList as $label => $query) {
625
            $res = Database::query($query);
626
            $obj = Database::fetch_object($res);
627
            $totalLogin[$label] = $obj->number;
628
        }
629
        if ($distinct) {
630
            self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false);
631
        } else {
632
            self::printStats(get_lang('Logins'), $totalLogin, false);
633
        }
634
    }
635
636
    /**
637
     * get the number of recent logins
638
     * @param bool    $distinct   Whether to only give distinct users stats, or *all* logins
639
     * @return array
640
     */
641
    public static function getRecentLoginStats($distinct = false)
642
    {
643
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
644
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
645
        $current_url_id = api_get_current_access_url_id();
646
        if (api_is_multiple_url_enabled()) {
647
            $table_url = ", $access_url_rel_user_table";
648
            $where_url = " AND login_user_id=user_id AND access_url_id='".$current_url_id."'";
649
        } else {
650
            $table_url = '';
651
            $where_url = '';
652
        }
653
654
        $now = api_get_utc_datetime();
655
        $date = new DateTime($now);
656
        $date->sub(new DateInterval('P15D'));
657
        $newDate = $date->format('Y-m-d').' 00:00:00';
658
659
        $field = 'login_id';
660
        if ($distinct) {
661
            $field = 'DISTINCT(login_user_id)';
662
        }
663
        $sql = "SELECT count($field) AS number, date(login_date) as login_date 
664
                FROM $table $table_url 
665
                WHERE login_date >= '$newDate' $where_url 
666
                GROUP BY date(login_date)";
667
668
        $res = Database::query($sql);
669
        $totalLogin = [];
670
        while ($row = Database::fetch_array($res, 'ASSOC')) {
671
            $totalLogin[$row['login_date']] = $row['number'];
672
        }
673
674
        return $totalLogin;
675
    }
676
677
    /**
678
     * Show some stats about the accesses to the different course tools
679
     */
680
    public static function printToolStats()
681
    {
682
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
683
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
684
        $current_url_id = api_get_current_access_url_id();
685
686
        $tools = [
687
            'announcement',
688
            'assignment',
689
            'calendar_event',
690
            'chat',
691
            'course_description',
692
            'document',
693
            'dropbox',
694
            'group',
695
            'learnpath',
696
            'link',
697
            'quiz',
698
            'student_publication',
699
            'user',
700
            'forum'
701
        ];
702
        $tool_names = [];
703
        foreach ($tools as $tool) {
704
            $tool_names[$tool] = get_lang(ucfirst($tool), '');
705
        }
706
        if (api_is_multiple_url_enabled()) {
707
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
708
                    FROM $table t , $access_url_rel_course_table a
709
                    WHERE
710
                        access_tool IN ('".implode("','", $tools)."') AND
711
                        t.c_id = a.c_id AND
712
                        access_url_id='".$current_url_id."'
713
                        GROUP BY access_tool
714
                    ";
715
        } else {
716
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
717
                    FROM $table
718
                    WHERE access_tool IN ('".implode("','", $tools)."')
719
                    GROUP BY access_tool ";
720
        }
721
722
        $res = Database::query($sql);
723
        $result = [];
724
        while ($obj = Database::fetch_object($res)) {
725
            $result[$tool_names[$obj->access_tool]] = $obj->number_of_logins;
726
        }
727
728
        self::printStats(get_lang('PlatformToolAccess'), $result, true);
729
    }
730
731
    /**
732
     * Show some stats about the number of courses per language
733
     */
734
    public static function printCourseByLanguageStats()
735
    {
736
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
737
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
738
        $current_url_id = api_get_current_access_url_id();
739
        if (api_is_multiple_url_enabled()) {
740
            $sql = "SELECT course_language, count( c.code ) AS number_of_courses
741
                    FROM $table as c, $access_url_rel_course_table as u
742
                    WHERE u.c_id = c.id AND access_url_id='".$current_url_id."'
743
                    GROUP BY course_language
744
                    ORDER BY number_of_courses DESC";
745
        } else {
746
            $sql = "SELECT course_language, count( code ) AS number_of_courses
747
                   FROM $table GROUP BY course_language
748
                   ORDER BY number_of_courses DESC";
749
        }
750
        $res = Database::query($sql);
751
        $result = [];
752
        while ($obj = Database::fetch_object($res)) {
753
            $result[$obj->course_language] = $obj->number_of_courses;
754
        }
755
        self::printStats(get_lang('CountCourseByLanguage'), $result, true);
756
    }
757
758
    /**
759
     * Shows the number of users having their picture uploaded in Dokeos.
760
     */
761
    public static function printUserPicturesStats()
762
    {
763
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
764
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
765
        $current_url_id = api_get_current_access_url_id();
766
        $url_condition = null;
767
        $url_condition2 = null;
768
        $table = null;
769
        if (api_is_multiple_url_enabled()) {
770
            $url_condition = ", $access_url_rel_user_table as url WHERE url.user_id=u.user_id AND access_url_id='".$current_url_id."'";
771
            $url_condition2 = " AND url.user_id=u.user_id AND access_url_id='".$current_url_id."'";
772
            $table = ", $access_url_rel_user_table as url ";
773
        }
774
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u ".$url_condition;
775
        $res = Database::query($sql);
776
        $count1 = Database::fetch_object($res);
777
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u $table ".
778
               "WHERE LENGTH(picture_uri) > 0 $url_condition2";
779
        $res = Database::query($sql);
780
        $count2 = Database::fetch_object($res);
781
        // #users without picture
782
        $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...
783
        $result[get_lang('Yes')] = $count2->n; // #users with picture
784
785
        self::printStats(get_lang('CountUsers').' ('.get_lang('UserPicture').')', $result, true);
786
    }
787
788
    /**
789
     * Important activities
790
     */
791
    public static function printActivitiesStats()
792
    {
793
        echo '<h4>'.get_lang('ImportantActivities').'</h4>';
794
        // Create a search-box
795
        $form = new FormValidator(
796
            'search_simple',
797
            'get',
798
            api_get_path(WEB_CODE_PATH).'admin/statistics/index.php',
799
            '',
800
            'width=200px',
801
            false
802
        );
803
        $renderer = & $form->defaultRenderer();
804
        $renderer->setCustomElementTemplate('<span>{element}</span> ');
805
        $form->addHidden('report', 'activities');
806
        $form->addHidden('activities_direction', 'DESC');
807
        $form->addHidden('activities_column', '4');
808
        $form->addElement('text', 'keyword', get_lang('Keyword'));
809
        $form->addButtonSearch(get_lang('Search'), 'submit');
810
        echo '<div class="actions">';
811
        $form->display();
812
        echo '</div>';
813
814
        $table = new SortableTable(
815
            'activities',
816
            ['Statistics', 'getNumberOfActivities'],
817
            ['Statistics', 'getActivitiesData'],
818
            7,
819
            50,
820
            'DESC'
821
        );
822
        $parameters = [];
823
824
        $parameters['report'] = 'activities';
825
        if (isset($_GET['keyword'])) {
826
            $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
827
        }
828
829
        $table->set_additional_parameters($parameters);
830
        $table->set_header(0, get_lang('EventType'));
831
        $table->set_header(1, get_lang('DataType'));
832
        $table->set_header(2, get_lang('Value'));
833
        $table->set_header(3, get_lang('Course'));
834
        $table->set_header(4, get_lang('Session'));
835
        $table->set_header(5, get_lang('UserName'));
836
        $table->set_header(6, get_lang('IPAddress'));
837
        $table->set_header(7, get_lang('Date'));
838
        $table->display();
839
    }
840
841
    /**
842
     * Shows statistics about the time of last visit to each course.
843
     */
844
    public static function printCourseLastVisit()
845
    {
846
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
847
        $current_url_id = api_get_current_access_url_id();
848
849
        $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...
850
        $columns[1] = 'access_date';
851
        $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...
852
        $sql_order[SORT_DESC] = 'DESC';
853
        $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
854
        $page_nr = isset($_GET['page_nr']) ? intval($_GET['page_nr']) : 1;
855
        $column = isset($_GET['column']) ? intval($_GET['column']) : 0;
856
        $direction = isset($_GET['direction']) ? $_GET['direction'] : SORT_ASC;
857
858
        if (!in_array($direction, [SORT_ASC, SORT_DESC])) {
859
            $direction = SORT_ASC;
860
        }
861
        $form = new FormValidator('courselastvisit', 'get');
862
        $form->addElement('hidden', 'report', 'courselastvisit');
863
        $form->addText('date_diff', get_lang('Days'), true);
864
        $form->addRule('date_diff', 'InvalidNumber', 'numeric');
865
        $form->addButtonSearch(get_lang('Search'), 'submit');
866
        if (!isset($_GET['date_diff'])) {
867
            $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...
868
        } else {
869
            $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']);
870
        }
871
        $form->setDefaults($defaults);
872
        $form->display();
873
        $values = $form->exportValues();
874
        $date_diff = $values['date_diff'];
875
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
876
        if (api_is_multiple_url_enabled()) {
877
            $sql = "SELECT * FROM $table t , $access_url_rel_course_table a
878
                   WHERE
879
                        t.c_id = a.c_id AND
880
                        access_url_id='".$current_url_id."'
881
                   GROUP BY t.c_id
882
                   HAVING t.c_id <> ''
883
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
884
        } else {
885
            $sql = "SELECT * FROM $table t
886
                   GROUP BY t.c_id
887
                   HAVING t.c_id <> ''
888
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
889
        }
890
        $sql .= ' ORDER BY '.$columns[$column].' '.$sql_order[$direction];
891
        $from = ($page_nr - 1) * $per_page;
892
        $sql .= ' LIMIT '.$from.','.$per_page;
893
894
        echo '<p>'.get_lang('LastAccess').' &gt;= '.$date_diff.' '.get_lang('Days').'</p>';
895
        $res = Database::query($sql);
896
        if (Database::num_rows($res) > 0) {
897
            $courses = [];
898
            while ($obj = Database::fetch_object($res)) {
899
                $courseInfo = api_get_course_info_by_id($obj->c_id);
900
                $course = [];
901
                $course[] = '<a href="'.api_get_path(WEB_COURSE_PATH).$courseInfo['code'].'">'.$courseInfo['code'].' <a>';
902
                // Allow sort by date hiding the numerical date
903
                $course[] = '<span style="display:none;">'.$obj->access_date.'</span>'.api_convert_and_format_date($obj->access_date);
904
                $courses[] = $course;
905
            }
906
            $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...
907
            $parameters['report'] = 'courselastvisit';
908
            $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...
909
            $table_header[] = [get_lang("LastAccess"), true];
910
            Display:: display_sortable_table(
911
                $table_header,
912
                $courses,
913
                ['column' => $column, 'direction' => $direction],
914
                [],
915
                $parameters
916
            );
917
        } else {
918
            echo get_lang('NoSearchResults');
919
        }
920
    }
921
922
    /**
923
     * Displays the statistics of the messages sent and received by each user in the social network
924
     * @param string    $messageType Type of message: 'sent' or 'received'
925
     * @return array    Message list
926
     */
927
    public static function getMessages($messageType)
928
    {
929
        $message_table = Database::get_main_table(TABLE_MESSAGE);
930
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
931
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
932
933
        $current_url_id = api_get_current_access_url_id();
934
935
        switch ($messageType) {
936
            case 'sent':
937
                $field = 'user_sender_id';
938
                break;
939
            case 'received':
940
                $field = 'user_receiver_id';
941
                break;
942
        }
943
944
        if (api_is_multiple_url_enabled()) {
945
            $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...
946
                FROM $access_url_rel_user_table as url, $message_table m 
947
                LEFT JOIN $user_table u ON m.$field = u.user_id 
948
                WHERE  url.user_id = m.$field AND  access_url_id='".$current_url_id."' 
949
                GROUP BY m.$field 
950
                ORDER BY count_message DESC ";
951
        } else {
952
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message 
953
                FROM $message_table m 
954
                LEFT JOIN $user_table u ON m.$field = u.user_id 
955
                GROUP BY m.$field ORDER BY count_message DESC ";
956
        }
957
        $res = Database::query($sql);
958
        $messages_sent = [];
959
        while ($messages = Database::fetch_array($res)) {
960
            if (empty($messages['username'])) {
961
                $messages['username'] = get_lang('Unknown');
962
            }
963
            $users = api_get_person_name(
964
                $messages['firstname'],
965
                $messages['lastname']
966
            ).'<br />('.$messages['username'].')';
967
            $messages_sent[$users] = $messages['count_message'];
968
        }
969
        return $messages_sent;
970
    }
971
972
    /**
973
     * Count the number of friends for social network users
974
     */
975
    public static function getFriends()
976
    {
977
        $user_friend_table = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
978
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
979
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
980
        $current_url_id = api_get_current_access_url_id();
981
982
        if (api_is_multiple_url_enabled()) {
983
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend 
984
                    FROM $access_url_rel_user_table as url, $user_friend_table uf 
985
                    LEFT JOIN $user_table u 
986
                    ON (uf.user_id = u.user_id) 
987
                    WHERE 
988
                        uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' AND 
989
                        uf.user_id = url.user_id AND  
990
                        access_url_id = '".$current_url_id."' 
991
                    GROUP BY uf.user_id 
992
                    ORDER BY count_friend DESC ";
993
        } else {
994
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend 
995
                    FROM $user_friend_table uf 
996
                    LEFT JOIN $user_table u 
997
                    ON (uf.user_id = u.user_id) 
998
                    WHERE uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' 
999
                    GROUP BY uf.user_id 
1000
                    ORDER BY count_friend DESC ";
1001
        }
1002
        $res = Database::query($sql);
1003
        $list_friends = [];
1004
        while ($friends = Database::fetch_array($res)) {
1005
            $users = api_get_person_name($friends['firstname'], $friends['lastname']).'<br />('.$friends['username'].')';
1006
            $list_friends[$users] = $friends['count_friend'];
1007
        }
1008
1009
        return $list_friends;
1010
    }
1011
1012
    /**
1013
     * Print the number of users that didn't login for a certain period of time
1014
     */
1015
    public static function printUsersNotLoggedInStats()
1016
    {
1017
        $totalLogin = [];
1018
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1019
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1020
        $current_url_id = api_get_current_access_url_id();
1021
        $total = self::countUsers();
1022
        if (api_is_multiple_url_enabled()) {
1023
            $table_url = ", $access_url_rel_user_table";
1024
            $where_url = " AND login_user_id=user_id AND access_url_id='".$current_url_id."'";
1025
        } else {
1026
            $table_url = '';
1027
            $where_url = '';
1028
        }
1029
        $now = api_get_utc_datetime();
1030
        $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...
1031
            "SELECT count(distinct(login_user_id)) AS number ".
1032
            " FROM $table $table_url ".
1033
            " WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
1034
        $sql[get_lang('Last7days')] =
1035
            "SELECT count(distinct(login_user_id)) AS number ".
1036
            " FROM $table $table_url ".
1037
            " WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
1038
        $sql[get_lang('Last31days')] =
1039
            "SELECT count(distinct(login_user_id)) AS number ".
1040
            " FROM $table $table_url ".
1041
            " WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
1042
        $sql[sprintf(get_lang('LastXMonths'), 6)] =
1043
            "SELECT count(distinct(login_user_id)) AS number ".
1044
            " FROM $table $table_url ".
1045
            " WHERE DATE_ADD(login_date, INTERVAL 6 MONTH) >= '$now' $where_url";
1046
        $sql[get_lang('NeverConnected')] =
1047
            "SELECT count(distinct(login_user_id)) AS number ".
1048
            " FROM $table $table_url WHERE 1=1 $where_url";
1049
        foreach ($sql as $index => $query) {
1050
            $res = Database::query($query);
1051
            $obj = Database::fetch_object($res);
1052
            $r = $total - $obj->number;
1053
            $totalLogin[$index] = $r < 0 ? 0 : $r;
1054
        }
1055
        self::printStats(
1056
            get_lang('StatsUsersDidNotLoginInLastPeriods'),
1057
            $totalLogin,
1058
            false
1059
        );
1060
    }
1061
}
1062