Passed
Push — master ( 755a75...b5a85d )
by Julito
08:47
created

Statistics::makeSizeString()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 8
nc 5
nop 1
dl 0
loc 13
rs 8.8333
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
use Chamilo\CoreBundle\Entity\UserRelUser;
6
7
/**
8
 * This class provides some functions for statistics.
9
 */
10
class Statistics
11
{
12
    /**
13
     * Converts a number of bytes in a formatted string.
14
     *
15
     * @param int $size
16
     *
17
     * @return string Formatted file size
18
     */
19
    public static function makeSizeString($size)
20
    {
21
        if ($size < pow(2, 10)) {
22
            return $size." bytes";
23
        }
24
        if ($size >= pow(2, 10) && $size < pow(2, 20)) {
25
            return round($size / pow(2, 10), 0)." KB";
26
        }
27
        if ($size >= pow(2, 20) && $size < pow(2, 30)) {
28
            return round($size / pow(2, 20), 1)." MB";
29
        }
30
        if ($size > pow(2, 30)) {
31
            return round($size / pow(2, 30), 2)." GB";
32
        }
33
    }
34
35
    /**
36
     * Count courses.
37
     *
38
     * @param string $categoryCode Code of a course category.
39
     *                             Default: count all courses.
40
     *
41
     * @return int Number of courses counted
42
     */
43
    public static function countCourses($categoryCode = null)
44
    {
45
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
46
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
47
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
48
        $urlId = api_get_current_access_url_id();
49
50
        $categoryJoin = '';
51
        $categoryCondition = '';
52
53
        if (!empty($categoryCode)) {
54
            //$categoryJoin = " LEFT JOIN $tblCourseCategory course_category ON course.category_id = course_category.id ";
55
            //$categoryCondition = " course_category.code = '".Database::escape_string($categoryCode)."' ";
56
        }
57
58
        if (api_is_multiple_url_enabled()) {
59
            $sql = "SELECT COUNT(*) AS number
60
                    FROM ".$course_table." as c, $access_url_rel_course_table as u
61
                    $categoryJoin
62
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'";
63
            if (isset($categoryCode)) {
64
                $sql .= " AND $categoryCondition";
65
            }
66
        } else {
67
            $sql = "SELECT COUNT(*) AS number
68
                    FROM $course_table $categoryJoin";
69
            if (isset($categoryCode)) {
70
                $sql .= " WHERE $categoryCondition";
71
            }
72
        }
73
74
        $res = Database::query($sql);
75
        $obj = Database::fetch_object($res);
76
77
        return $obj->number;
78
    }
79
80
    /**
81
     * Count courses by visibility.
82
     *
83
     * @param int $visibility visibility (0 = closed, 1 = private, 2 = open, 3 = public) all courses
84
     *
85
     * @return int Number of courses counted
86
     */
87
    public static function countCoursesByVisibility($visibility = null)
88
    {
89
        if (!isset($visibility)) {
90
            return 0;
91
        }
92
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
93
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
94
        $urlId = api_get_current_access_url_id();
95
        if (api_is_multiple_url_enabled()) {
96
            $sql = "SELECT COUNT(*) AS number
97
                    FROM $course_table as c, $access_url_rel_course_table as u
98
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'";
99
            if (isset($visibility)) {
100
                $sql .= " AND visibility = ".intval($visibility);
101
            }
102
        } else {
103
            $sql = "SELECT COUNT(*) AS number FROM $course_table ";
104
            if (isset($visibility)) {
105
                $sql .= " WHERE visibility = ".intval($visibility);
106
            }
107
        }
108
        $res = Database::query($sql);
109
        $obj = Database::fetch_object($res);
110
111
        return $obj->number;
112
    }
113
114
    /**
115
     * Count users.
116
     *
117
     * @param int    $status                user status (COURSEMANAGER or STUDENT) if not setted it'll count all users
118
     * @param string $categoryCode          course category code. Default: count only users without filtering category
119
     * @param bool   $countInvisibleCourses Count invisible courses (todo)
120
     * @param bool   $onlyActive            Count only active users (false to only return currently active users)
121
     *
122
     * @return int Number of users counted
123
     */
124
    public static function countUsers(
125
        $status = null,
126
        $categoryCode = null,
127
        $countInvisibleCourses = true,
128
        $onlyActive = false
129
    ) {
130
        // Database table definitions
131
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
132
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
133
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
134
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
135
        $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
136
        $urlId = api_get_current_access_url_id();
137
        $active_filter = $onlyActive ? ' AND active=1' : '';
138
        $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
139
140
        if (api_is_multiple_url_enabled()) {
141
            $sql = "SELECT COUNT(DISTINCT(u.id)) AS number
142
                    FROM $user_table as u, $access_url_rel_user_table as url
143
                    WHERE
144
                        u.id = url.user_id AND
145
                        access_url_id = '".$urlId."'
146
                        $status_filter $active_filter";
147
            if (isset($categoryCode)) {
148
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
149
                        FROM $course_user_table cu, $course_table c, $access_url_rel_user_table as url
150
                        INNER JOIN $tblCourseCategory course_category ON c.category_id = course_category.id
151
                        WHERE
152
                            c.id = cu.c_id AND
153
                            course_category.code = '".Database::escape_string($categoryCode)."' AND
154
                            cu.user_id = url.user_id AND
155
                            access_url_id='".$urlId."'
156
                            $status_filter $active_filter";
157
            }
158
        } else {
159
            $sql = "SELECT COUNT(DISTINCT(id)) AS number
160
                    FROM $user_table
161
                    WHERE 1=1 $status_filter $active_filter";
162
            if (isset($categoryCode)) {
163
                $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
164
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
165
                        FROM $course_user_table cu, $course_table c
166
                        INNER JOIN $tblCourseCategory course_category ON c.category_id = course_category.id
167
                        WHERE
168
                            c.id = cu.c_id AND
169
                            course_category.code = '".Database::escape_string($categoryCode)."'
170
                            $status_filter
171
                            $active_filter
172
                        ";
173
            }
174
        }
175
176
        $res = Database::query($sql);
177
        $obj = Database::fetch_object($res);
178
179
        return $obj->number;
180
    }
181
182
    /**
183
     * @param string $startDate
184
     * @param string $endDate
185
     *
186
     * @return array
187
     */
188
    public static function getCoursesWithActivity($startDate, $endDate)
189
    {
190
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
191
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
192
        $startDate = Database::escape_string($startDate);
193
        $endDate = Database::escape_string($endDate);
194
195
        $urlId = api_get_current_access_url_id();
196
197
        if (api_is_multiple_url_enabled()) {
198
            $sql = "SELECT DISTINCT(t.c_id) FROM $table t , $access_url_rel_course_table a
199
                    WHERE
200
                        t.c_id = a.c_id AND
201
                        access_url_id='".$urlId."' AND
202
                        access_date BETWEEN '$startDate' AND '$endDate'
203
                    ";
204
        } else {
205
            $sql = "SELECT DISTINCT(t.c_id) FROM $table t
206
                   access_date BETWEEN '$startDate' AND '$endDate' ";
207
        }
208
209
        $result = Database::query($sql);
210
211
        return Database::store_result($result);
212
    }
213
214
    /**
215
     * Count activities from track_e_default_table.
216
     *
217
     * @return int Number of activities counted
218
     */
219
    public static function getNumberOfActivities($courseId = 0, $sessionId = 0)
220
    {
221
        // Database table definitions
222
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
223
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
224
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
225
        $urlId = api_get_current_access_url_id();
226
        if (api_is_multiple_url_enabled()) {
227
            $sql = "SELECT count(default_id) AS total_number_of_items
228
                    FROM $track_e_default, $table_user user, $access_url_rel_user_table url
229
                    WHERE
230
                        default_user_id = user.id AND
231
                        user.id=url.user_id AND
232
                        access_url_id = '".$urlId."'";
233
        } else {
234
            $sql = "SELECT count(default_id) AS total_number_of_items
235
                    FROM $track_e_default, $table_user user
236
                    WHERE default_user_id = user.id ";
237
        }
238
239
        if (!empty($courseId)) {
240
            $courseId = (int) $courseId;
241
            $sql .= " AND c_id = $courseId";
242
            $sql .= api_get_session_condition($sessionId);
243
        }
244
245
        if (isset($_GET['keyword'])) {
246
            $keyword = Database::escape_string(trim($_GET['keyword']));
247
            $sql .= " AND (
248
                        user.username LIKE '%".$keyword."%' OR
249
                        default_event_type LIKE '%".$keyword."%' OR
250
                        default_value_type LIKE '%".$keyword."%' OR
251
                        default_value LIKE '%".$keyword."%') ";
252
        }
253
        $res = Database::query($sql);
254
        $obj = Database::fetch_object($res);
255
256
        return $obj->total_number_of_items;
257
    }
258
259
    /**
260
     * Get activities data to display.
261
     *
262
     * @param int    $from
263
     * @param int    $numberOfItems
264
     * @param int    $column
265
     * @param string $direction
266
     * @param int    $courseId
267
     * @param int    $sessionId
268
     *
269
     * @return array
270
     */
271
    public static function getActivitiesData(
272
        $from,
273
        $numberOfItems,
274
        $column,
275
        $direction,
276
        $courseId = 0,
277
        $sessionId = 0
278
    ) {
279
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
280
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
281
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
282
        $urlId = api_get_current_access_url_id();
283
        $column = (int) $column;
284
        $from = (int) $from;
285
        $numberOfItems = (int) $numberOfItems;
286
        $direction = strtoupper($direction);
287
288
        if (!in_array($direction, ['ASC', 'DESC'])) {
289
            $direction = 'DESC';
290
        }
291
292
        if (api_is_multiple_url_enabled()) {
293
            $sql = "SELECT
294
                        default_event_type  as col0,
295
                        default_value_type    as col1,
296
                        default_value        as col2,
297
                        c_id         as col3,
298
                        session_id as col4,
299
                        user.username         as col5,
300
                        user.id         as col6,
301
                        default_date         as col7
302
                    FROM $track_e_default as track_default,
303
                    $table_user as user,
304
                    $access_url_rel_user_table as url
305
                    WHERE
306
                        track_default.default_user_id = user.id AND
307
                        url.user_id = user.id AND
308
                        access_url_id= $urlId ";
309
        } else {
310
            $sql = "SELECT
311
                       default_event_type  as col0,
312
                       default_value_type    as col1,
313
                       default_value        as col2,
314
                       c_id         as col3,
315
                       session_id as col4,
316
                       user.username         as col5,
317
                       user.id         as col6,
318
                       default_date         as col7
319
                   FROM $track_e_default track_default, $table_user user
320
                   WHERE track_default.default_user_id = user.id ";
321
        }
322
323
        if (!empty($_GET['keyword'])) {
324
            $keyword = Database::escape_string(trim($_GET['keyword']));
325
            $sql .= " AND (user.username LIKE '%".$keyword."%' OR
326
                        default_event_type LIKE '%".$keyword."%' OR
327
                        default_value_type LIKE '%".$keyword."%' OR
328
                        default_value LIKE '%".$keyword."%') ";
329
        }
330
331
        if (!empty($courseId)) {
332
            $courseId = (int) $courseId;
333
            $sql .= " AND c_id = $courseId";
334
            $sql .= api_get_session_condition($sessionId);
335
        }
336
337
        if (!empty($column) && !empty($direction)) {
338
            $sql .= " ORDER BY col$column $direction";
339
        } else {
340
            $sql .= " ORDER BY col7 DESC ";
341
        }
342
        $sql .= " LIMIT $from, $numberOfItems ";
343
344
        $res = Database::query($sql);
345
        $activities = [];
346
        while ($row = Database::fetch_row($res)) {
347
            if (false === strpos($row[1], '_object') &&
348
                false === strpos($row[1], '_array')
349
            ) {
350
                $row[2] = $row[2];
351
            } else {
352
                if (!empty($row[2])) {
353
                    $originalData = str_replace('\\', '', $row[2]);
354
                    $row[2] = UnserializeApi::unserialize('not_allowed_classes', $originalData);
355
                    if (is_array($row[2]) && !empty($row[2])) {
356
                        $row[2] = implode_with_key(', ', $row[2]);
357
                    } else {
358
                        $row[2] = $originalData;
359
                    }
360
                }
361
            }
362
363
            if (!empty($row['default_date'])) {
364
                $row['default_date'] = api_get_local_time($row['default_date']);
365
            } else {
366
                $row['default_date'] = '-';
367
            }
368
369
            if (!empty($row[7])) {
370
                $row[7] = api_get_local_time($row[7]);
371
            } else {
372
                $row[7] = '-';
373
            }
374
375
            if (!empty($row[5])) {
376
                // Course
377
                if (!empty($row[3])) {
378
                    $row[3] = Display::url(
379
                        $row[3],
380
                        api_get_path(WEB_CODE_PATH).'admin/course_edit.php?id='.$row[3]
381
                    );
382
                } else {
383
                    $row[3] = '-';
384
                }
385
386
                // session
387
                if (!empty($row[4])) {
388
                    $row[4] = Display::url(
389
                        $row[4],
390
                        api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$row[4]
391
                    );
392
                } else {
393
                    $row[4] = '-';
394
                }
395
396
                // User id.
397
                $row[5] = Display::url(
398
                    $row[5],
399
                    api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$row[6],
400
                    ['class' => 'ajax']
401
                );
402
403
                $row[6] = Tracking::get_ip_from_user_event(
404
                    $row[6],
405
                    $row[7],
406
                    true
407
                );
408
                if (empty($row[6])) {
409
                    $row[6] = get_lang('Unknown');
410
                }
411
            }
412
            $activities[] = $row;
413
        }
414
415
        return $activities;
416
    }
417
418
    /**
419
     * Rescale data.
420
     *
421
     * @param array $data The data that should be rescaled
422
     * @param int   $max  The maximum value in the rescaled data (default = 500);
423
     *
424
     * @return array The rescaled data, same key as $data
425
     */
426
    public static function rescale($data, $max = 500)
427
    {
428
        $data_max = 1;
429
        foreach ($data as $index => $value) {
430
            $data_max = ($data_max < $value ? $value : $data_max);
431
        }
432
        reset($data);
433
        $result = [];
434
        $delta = $max / $data_max;
435
        foreach ($data as $index => $value) {
436
            $result[$index] = (int) round($value * $delta);
437
        }
438
439
        return $result;
440
    }
441
442
    /**
443
     * Show statistics.
444
     *
445
     * @param string $title      The title
446
     * @param array  $stats
447
     * @param bool   $showTotal
448
     * @param bool   $isFileSize
449
     *
450
     * @return string HTML table
451
     */
452
    public static function printStats(
453
        $title,
454
        $stats,
455
        $showTotal = true,
456
        $isFileSize = false
457
    ) {
458
        $total = 0;
459
        $content = '<table class="table table-hover table-striped data_table" cellspacing="0" cellpadding="3" width="90%">
460
            <thead><tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr></thead><tbody>';
461
        $i = 0;
462
        foreach ($stats as $subtitle => $number) {
463
            $total += $number;
464
        }
465
466
        foreach ($stats as $subtitle => $number) {
467
            if (!$isFileSize) {
468
                $number_label = number_format($number, 0, ',', '.');
469
            } else {
470
                $number_label = self::makeSizeString($number);
471
            }
472
            $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
473
474
            $content .= '<tr class="row_'.(0 == $i % 2 ? 'odd' : 'even').'">
475
                    <td width="25%" style="vertical-align:top;">'.$subtitle.'</td>
476
                    <td width="60%">'.Display::bar_progress($percentage, false).'</td>
477
                    <td width="5%" align="right" style="vertical-align:top;">'.$number_label.'</td>';
478
            if ($showTotal) {
479
                $content .= '<td width="5%" align="right"> '.$percentage.'%</td>';
480
            }
481
            $content .= '</tr>';
482
            $i++;
483
        }
484
        $content .= '</tbody>';
485
        if ($showTotal) {
486
            if (!$isFileSize) {
487
                $total_label = number_format($total, 0, ',', '.');
488
            } else {
489
                $total_label = self::makeSizeString($total);
490
            }
491
            $content .= '
492
                <tfoot><tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr></tfoot>
493
            ';
494
        }
495
        $content .= '</table>';
496
497
        return $content;
498
    }
499
500
    /**
501
     * Show some stats about the number of logins.
502
     *
503
     * @param string $type month, hour or day
504
     */
505
    public static function printLoginStats($type)
506
    {
507
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
508
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
509
        $urlId = api_get_current_access_url_id();
510
511
        $table_url = null;
512
        $where_url = null;
513
        $now = api_get_utc_datetime();
514
        $where_url_last = ' WHERE login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
515
        if (api_is_multiple_url_enabled()) {
516
            $table_url = ", $access_url_rel_user_table";
517
            $where_url = " WHERE login_user_id=user_id AND access_url_id='".$urlId."'";
518
            $where_url_last = ' AND login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
519
        }
520
521
        $period = get_lang('Month');
522
        $periodCollection = api_get_months_long();
523
        $sql = "SELECT
524
                DATE_FORMAT( login_date, '%Y-%m' ) AS stat_date ,
525
                count( login_id ) AS number_of_logins
526
                FROM $table $table_url $where_url
527
                GROUP BY stat_date
528
                ORDER BY login_date DESC";
529
        $sql_last_x = null;
530
531
        switch ($type) {
532
            case 'hour':
533
                $period = get_lang('Hour');
534
                $sql = "SELECT
535
                          DATE_FORMAT( login_date, '%H') AS stat_date,
536
                          count( login_id ) AS number_of_logins
537
                        FROM $table $table_url $where_url
538
                        GROUP BY stat_date
539
                        ORDER BY stat_date ";
540
                $sql_last_x = "SELECT
541
                                DATE_FORMAT( login_date, '%H' ) AS stat_date,
542
                                count( login_id ) AS number_of_logins
543
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'DAY')."
544
                               GROUP BY stat_date
545
                               ORDER BY stat_date ";
546
                break;
547
            case 'day':
548
                $periodCollection = api_get_week_days_long();
549
                $period = get_lang('Day');
550
                $sql = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date ,
551
                        count( login_id ) AS number_of_logins
552
                        FROM  $table $table_url $where_url
553
                        GROUP BY stat_date
554
                        ORDER BY DATE_FORMAT( login_date, '%w' ) ";
555
                $sql_last_x = "SELECT
556
                                DATE_FORMAT( login_date, '%w' ) AS stat_date,
557
                                count( login_id ) AS number_of_logins
558
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'WEEK')."
559
                               GROUP BY stat_date
560
                               ORDER BY DATE_FORMAT( login_date, '%w' ) ";
561
                break;
562
        }
563
564
        $content = '';
565
        if ($sql_last_x) {
566
            $res_last_x = Database::query($sql_last_x);
567
            $result_last_x = [];
568
            while ($obj = Database::fetch_object($res_last_x)) {
569
                $stat_date = ('day' === $type) ? $periodCollection[$obj->stat_date] : $obj->stat_date;
570
                $result_last_x[$stat_date] = $obj->number_of_logins;
571
            }
572
            $content .= self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true);
573
            flush(); //flush web request at this point to see something already while the full data set is loading
574
            $content .= '<br />';
575
        }
576
        $res = Database::query($sql);
577
        $result = [];
578
        while ($obj = Database::fetch_object($res)) {
579
            $stat_date = $obj->stat_date;
580
            switch ($type) {
581
                case 'month':
582
                    $stat_date = explode('-', $stat_date);
583
                    $stat_date[1] = $periodCollection[$stat_date[1] - 1];
584
                    $stat_date = implode(' ', $stat_date);
585
                    break;
586
                case 'day':
587
                    $stat_date = $periodCollection[$stat_date];
588
                    break;
589
            }
590
            $result[$stat_date] = $obj->number_of_logins;
591
        }
592
        $content .= self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true);
593
594
        return $content;
595
    }
596
597
    /**
598
     * Print the number of recent logins.
599
     *
600
     * @param bool  $distinct        whether to only give distinct users stats, or *all* logins
601
     * @param int   $sessionDuration Number of minutes a session must have lasted at a minimum to be taken into account
602
     * @param array $periods         List of number of days we want to query (default: [1, 7, 31] for last 1 day, last 7 days, last 31 days)
603
     *
604
     * @throws Exception
605
     *
606
     * @return string HTML table
607
     */
608
    public static function printRecentLoginStats($distinct = false, $sessionDuration = 0, $periods = [])
609
    {
610
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
611
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
612
        $urlId = api_get_current_access_url_id();
613
        $table_url = '';
614
        $where_url = '';
615
        if (api_is_multiple_url_enabled()) {
616
            $table_url = ", $access_url_rel_user_table";
617
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
618
        }
619
620
        $now = api_get_utc_datetime();
621
        $field = 'login_id';
622
        if ($distinct) {
623
            $field = 'DISTINCT(login_user_id)';
624
        }
625
626
        if (empty($periods)) {
627
            $periods = [1, 7, 31];
628
        }
629
        $sqlList = [];
630
631
        $sessionDuration = (int) $sessionDuration * 60; // convert from minutes to seconds
632
        foreach ($periods as $day) {
633
            $date = new DateTime($now);
634
            $startDate = $date->format('Y-m-d').' 00:00:00';
635
            $endDate = $date->format('Y-m-d').' 23:59:59';
636
637
            if ($day > 1) {
638
                $startDate = $date->sub(new DateInterval('P'.$day.'D'));
639
                $startDate = $startDate->format('Y-m-d').' 00:00:00';
640
            }
641
642
            $localDate = api_get_local_time($startDate, null, null, false, false);
643
            $localEndDate = api_get_local_time($endDate, null, null, false, false);
644
645
            $label = sprintf(get_lang('Last %s days'), $day);
646
            if (1 == $day) {
647
                $label = get_lang('Today');
648
            }
649
            $label .= " <span class=\"muted right\" style=\"float: right; margin-right: 5px;\">[$localDate - $localEndDate]</span>";
650
            $sql = "SELECT count($field) AS number
651
                    FROM $table $table_url
652
                    WHERE ";
653
            if (0 == $sessionDuration) {
654
                $sql .= " logout_date != login_date AND ";
655
            } else {
656
                $sql .= " UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND ";
657
            }
658
            $sql .= "login_date BETWEEN '$startDate' AND '$endDate'
659
                        $where_url";
660
            $sqlList[$label] = $sql;
661
        }
662
663
        $sql = "SELECT count($field) AS number
664
                FROM $table $table_url ";
665
        if (0 == $sessionDuration) {
666
            $sql .= " WHERE logout_date != login_date $where_url";
667
        } else {
668
            $sql .= " WHERE UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration $where_url";
669
        }
670
        $sqlList[get_lang('Total')] = $sql;
671
        $totalLogin = [];
672
        foreach ($sqlList as $label => $query) {
673
            $res = Database::query($query);
674
            $obj = Database::fetch_object($res);
675
            $totalLogin[$label] = $obj->number;
676
        }
677
678
        if ($distinct) {
679
            $content = self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false);
680
        } else {
681
            $content = self::printStats(get_lang('Logins'), $totalLogin, false);
682
        }
683
684
        return $content;
685
    }
686
687
    /**
688
     * Get the number of recent logins.
689
     *
690
     * @param bool $distinct            Whether to only give distinct users stats, or *all* logins
691
     * @param int  $sessionDuration     Number of minutes a session must have lasted at a minimum to be taken into account
692
     * @param bool $completeMissingDays Whether to fill the daily gaps (if any) when getting a list of logins
693
     *
694
     * @throws Exception
695
     *
696
     * @return array
697
     */
698
    public static function getRecentLoginStats($distinct = false, $sessionDuration = 0, $completeMissingDays = true)
699
    {
700
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
701
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
702
        $urlId = api_get_current_access_url_id();
703
        $table_url = '';
704
        $where_url = '';
705
        if (api_is_multiple_url_enabled()) {
706
            $table_url = ", $access_url_rel_user_table";
707
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
708
        }
709
710
        $now = api_get_utc_datetime();
711
        $date = new DateTime($now);
712
        $date->sub(new DateInterval('P31D'));
713
        $newDate = $date->format('Y-m-d h:i:s');
714
        $totalLogin = self::buildDatesArray($newDate, $now, true);
715
716
        $field = 'login_id';
717
        if ($distinct) {
718
            $field = 'DISTINCT(login_user_id)';
719
        }
720
        $sessionDuration = (int) $sessionDuration * 60; //Convert from minutes to seconds
721
722
        $sql = "SELECT count($field) AS number, date(login_date) as login_date
723
                FROM $table $table_url
724
                WHERE ";
725
        if (0 == $sessionDuration) {
726
            $sql .= " logout_date != login_date AND ";
727
        } else {
728
            $sql .= " UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND ";
729
        }
730
        $sql .= " login_date >= '$newDate' $where_url
731
                GROUP BY date(login_date)";
732
733
        $res = Database::query($sql);
734
        while ($row = Database::fetch_array($res, 'ASSOC')) {
735
            $monthAndDay = substr($row['login_date'], 5, 5);
736
            $totalLogin[$monthAndDay] = $row['number'];
737
        }
738
739
        return $totalLogin;
740
    }
741
742
    /**
743
     * Get course tools usage statistics for the whole platform (by URL if multi-url).
744
     */
745
    public static function getToolsStats()
746
    {
747
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
748
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
749
        $urlId = api_get_current_access_url_id();
750
751
        $tools = [
752
            'announcement',
753
            'assignment',
754
            'calendar_event',
755
            'chat',
756
            'course_description',
757
            'document',
758
            'dropbox',
759
            'group',
760
            'learnpath',
761
            'link',
762
            'quiz',
763
            'student_publication',
764
            'user',
765
            'forum',
766
        ];
767
        $tool_names = [];
768
        foreach ($tools as $tool) {
769
            $tool_names[$tool] = get_lang(ucfirst($tool), '');
770
        }
771
        if (api_is_multiple_url_enabled()) {
772
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
773
                    FROM $table t , $access_url_rel_course_table a
774
                    WHERE
775
                        access_tool IN ('".implode("','", $tools)."') AND
776
                        t.c_id = a.c_id AND
777
                        access_url_id='".$urlId."'
778
                        GROUP BY access_tool
779
                    ";
780
        } else {
781
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
782
                    FROM $table
783
                    WHERE access_tool IN ('".implode("','", $tools)."')
784
                    GROUP BY access_tool ";
785
        }
786
787
        $res = Database::query($sql);
788
        $result = [];
789
        while ($obj = Database::fetch_object($res)) {
790
            $result[$tool_names[$obj->access_tool]] = $obj->number_of_logins;
791
        }
792
793
        return $result;
794
    }
795
796
    /**
797
     * Show some stats about the accesses to the different course tools.
798
     *
799
     * @param array $result If defined, this serves as data. Otherwise, will get the data from getToolsStats()
800
     *
801
     * @return string HTML table
802
     */
803
    public static function printToolStats($result = null)
804
    {
805
        if (empty($result)) {
806
            $result = self::getToolsStats();
807
        }
808
809
        return self::printStats(get_lang('Tools access'), $result, true);
810
    }
811
812
    /**
813
     * Show some stats about the number of courses per language.
814
     */
815
    public static function printCourseByLanguageStats()
816
    {
817
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
818
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
819
        $urlId = api_get_current_access_url_id();
820
        if (api_is_multiple_url_enabled()) {
821
            $sql = "SELECT course_language, count( c.code ) AS number_of_courses
822
                    FROM $table as c, $access_url_rel_course_table as u
823
                    WHERE u.c_id = c.id AND access_url_id='".$urlId."'
824
                    GROUP BY course_language
825
                    ORDER BY number_of_courses DESC";
826
        } else {
827
            $sql = "SELECT course_language, count( code ) AS number_of_courses
828
                   FROM $table GROUP BY course_language
829
                   ORDER BY number_of_courses DESC";
830
        }
831
        $res = Database::query($sql);
832
        $result = [];
833
        while ($obj = Database::fetch_object($res)) {
834
            $result[$obj->course_language] = $obj->number_of_courses;
835
        }
836
837
        return $result;
838
    }
839
840
    /**
841
     * Shows the number of users having their picture uploaded in Dokeos.
842
     */
843
    public static function printUserPicturesStats()
844
    {
845
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
846
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
847
        $urlId = api_get_current_access_url_id();
848
        $url_condition = null;
849
        $url_condition2 = null;
850
        $table = null;
851
        if (api_is_multiple_url_enabled()) {
852
            $url_condition = ", $access_url_rel_user_table as url WHERE url.user_id=u.id AND access_url_id='".$urlId."'";
853
            $url_condition2 = " AND url.user_id=u.id AND access_url_id='".$urlId."'";
854
            $table = ", $access_url_rel_user_table as url ";
855
        }
856
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u ".$url_condition;
857
        $res = Database::query($sql);
858
        $count1 = Database::fetch_object($res);
859
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u $table ".
860
               "WHERE LENGTH(picture_uri) > 0 $url_condition2";
861
        $res = Database::query($sql);
862
        $count2 = Database::fetch_object($res);
863
        // #users without picture
864
        $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...
865
        $result[get_lang('Yes')] = $count2->n; // #users with picture
866
867
        return self::printStats(get_lang('Number of users').' ('.get_lang('Picture').')', $result, true);
868
    }
869
870
    /**
871
     * Important activities.
872
     */
873
    public static function printActivitiesStats()
874
    {
875
        $content = '<h4>'.get_lang('Important activities').'</h4>';
876
        // Create a search-box
877
        $form = new FormValidator(
878
            'search_simple',
879
            'get',
880
            api_get_path(WEB_CODE_PATH).'admin/statistics/index.php',
881
            '',
882
            'width=200px',
883
            false
884
        );
885
        $renderer = &$form->defaultRenderer();
886
        $renderer->setCustomElementTemplate('<span>{element}</span> ');
887
        $form->addHidden('report', 'activities');
888
        $form->addHidden('activities_direction', 'DESC');
889
        $form->addHidden('activities_column', '4');
890
        $form->addElement('text', 'keyword', get_lang('Keyword'));
891
        $form->addButtonSearch(get_lang('Search'), 'submit');
892
        $content .= '<div class="actions">';
893
        $content .= $form->returnForm();
894
        $content .= '</div>';
895
896
        $table = new SortableTable(
897
            'activities',
898
            ['Statistics', 'getNumberOfActivities'],
899
            ['Statistics', 'getActivitiesData'],
900
            7,
901
            50,
902
            'DESC'
903
        );
904
        $parameters = [];
905
906
        $parameters['report'] = 'activities';
907
        if (isset($_GET['keyword'])) {
908
            $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
909
        }
910
911
        $table->set_additional_parameters($parameters);
912
        $table->set_header(0, get_lang('Event type'));
913
        $table->set_header(1, get_lang('Data type'));
914
        $table->set_header(2, get_lang('Value'));
915
        $table->set_header(3, get_lang('Course'));
916
        $table->set_header(4, get_lang('Session'));
917
        $table->set_header(5, get_lang('Username'));
918
        $table->set_header(6, get_lang('IP address'));
919
        $table->set_header(7, get_lang('Date'));
920
        $content .= $table->return_table();
921
922
        return $content;
923
    }
924
925
    /**
926
     * Shows statistics about the time of last visit to each course.
927
     */
928
    public static function printCourseLastVisit()
929
    {
930
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
931
        $urlId = api_get_current_access_url_id();
932
933
        $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...
934
        $columns[1] = 'access_date';
935
        $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...
936
        $sql_order[SORT_DESC] = 'DESC';
937
        $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
938
        $page_nr = isset($_GET['page_nr']) ? intval($_GET['page_nr']) : 1;
939
        $column = isset($_GET['column']) ? intval($_GET['column']) : 0;
940
        $direction = isset($_GET['direction']) ? $_GET['direction'] : SORT_ASC;
941
942
        if (!in_array($direction, [SORT_ASC, SORT_DESC])) {
943
            $direction = SORT_ASC;
944
        }
945
        $form = new FormValidator('courselastvisit', 'get');
946
        $form->addElement('hidden', 'report', 'courselastvisit');
947
        $form->addText('date_diff', get_lang('days'), true);
948
        $form->addRule('date_diff', 'InvalidNumber', 'numeric');
949
        $form->addButtonSearch(get_lang('Search'), 'submit');
950
        if (!isset($_GET['date_diff'])) {
951
            $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...
952
        } else {
953
            $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']);
954
        }
955
        $form->setDefaults($defaults);
956
        $content = $form->returnForm();
957
958
        $values = $form->exportValues();
959
        $date_diff = $values['date_diff'];
960
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
961
        if (api_is_multiple_url_enabled()) {
962
            $sql = "SELECT * FROM $table t , $access_url_rel_course_table a
963
                   WHERE
964
                        t.c_id = a.c_id AND
965
                        access_url_id='".$urlId."'
966
                   GROUP BY t.c_id
967
                   HAVING t.c_id <> ''
968
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
969
        } else {
970
            $sql = "SELECT * FROM $table t
971
                   GROUP BY t.c_id
972
                   HAVING t.c_id <> ''
973
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
974
        }
975
        $sql .= ' ORDER BY `'.$columns[$column].'` '.$sql_order[$direction];
976
        $from = ($page_nr - 1) * $per_page;
977
        $sql .= ' LIMIT '.$from.','.$per_page;
978
979
        $content .= '<p>'.get_lang('Latest access').' &gt;= '.$date_diff.' '.get_lang('days').'</p>';
980
        $res = Database::query($sql);
981
        if (Database::num_rows($res) > 0) {
982
            $courses = [];
983
            while ($obj = Database::fetch_object($res)) {
984
                $courseInfo = api_get_course_info_by_id($obj->c_id);
985
                $course = [];
986
                $course[] = '<a href="'.api_get_path(WEB_COURSE_PATH).$courseInfo['code'].'">'.$courseInfo['code'].' <a>';
987
                // Allow sort by date hiding the numerical date
988
                $course[] = '<span style="display:none;">'.$obj->access_date.'</span>'.api_convert_and_format_date($obj->access_date);
989
                $courses[] = $course;
990
            }
991
            $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...
992
            $parameters['report'] = 'courselastvisit';
993
            $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...
994
            $table_header[] = [get_lang("LastAccess"), true];
995
996
            ob_start();
997
            Display:: display_sortable_table(
998
                $table_header,
999
                $courses,
1000
                ['column' => $column, 'direction' => $direction],
1001
                [],
1002
                $parameters
1003
            );
1004
            $content .= ob_get_contents();
1005
            ob_end_clean();
1006
        } else {
1007
            $content = get_lang('NoSearchResults');
1008
        }
1009
1010
        return $content;
1011
    }
1012
1013
    /**
1014
     * Displays the statistics of the messages sent and received by each user in the social network.
1015
     *
1016
     * @param string $messageType Type of message: 'sent' or 'received'
1017
     *
1018
     * @return array Message list
1019
     */
1020
    public static function getMessages($messageType)
1021
    {
1022
        $message_table = Database::get_main_table(TABLE_MESSAGE);
1023
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1024
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1025
1026
        $urlId = api_get_current_access_url_id();
1027
1028
        switch ($messageType) {
1029
            case 'sent':
1030
                $field = 'user_sender_id';
1031
                break;
1032
            case 'received':
1033
                $field = 'user_receiver_id';
1034
                break;
1035
        }
1036
1037
        if (api_is_multiple_url_enabled()) {
1038
            $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...
1039
                FROM $access_url_rel_user_table as url, $message_table m
1040
                LEFT JOIN $user_table u ON m.$field = u.id
1041
                WHERE  url.user_id = m.$field AND  access_url_id='".$urlId."'
1042
                GROUP BY m.$field
1043
                ORDER BY count_message DESC ";
1044
        } else {
1045
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message
1046
                FROM $message_table m
1047
                LEFT JOIN $user_table u ON m.$field = u.id
1048
                GROUP BY m.$field ORDER BY count_message DESC ";
1049
        }
1050
        $res = Database::query($sql);
1051
        $messages_sent = [];
1052
        while ($messages = Database::fetch_array($res)) {
1053
            if (empty($messages['username'])) {
1054
                $messages['username'] = get_lang('Unknown');
1055
            }
1056
            $users = api_get_person_name(
1057
                $messages['firstname'],
1058
                $messages['lastname']
1059
            ).'<br />('.$messages['username'].')';
1060
            $messages_sent[$users] = $messages['count_message'];
1061
        }
1062
1063
        return $messages_sent;
1064
    }
1065
1066
    /**
1067
     * Count the number of friends for social network users.
1068
     */
1069
    public static function getFriends()
1070
    {
1071
        $user_friend_table = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
1072
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1073
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1074
        $urlId = api_get_current_access_url_id();
1075
1076
        if (api_is_multiple_url_enabled()) {
1077
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend
1078
                    FROM $access_url_rel_user_table as url, $user_friend_table uf
1079
                    LEFT JOIN $user_table u
1080
                    ON (uf.user_id = u.id)
1081
                    WHERE
1082
                        uf.relation_type <> '".UserRelUser::USER_RELATION_TYPE_RRHH."' AND
1083
                        uf.user_id = url.user_id AND
1084
                        access_url_id = '".$urlId."'
1085
                    GROUP BY uf.user_id
1086
                    ORDER BY count_friend DESC ";
1087
        } else {
1088
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend
1089
                    FROM $user_friend_table uf
1090
                    LEFT JOIN $user_table u
1091
                    ON (uf.user_id = u.id)
1092
                    WHERE uf.relation_type <> '".UserRelUser::USER_RELATION_TYPE_RRHH."'
1093
                    GROUP BY uf.user_id
1094
                    ORDER BY count_friend DESC ";
1095
        }
1096
        $res = Database::query($sql);
1097
        $list_friends = [];
1098
        while ($friends = Database::fetch_array($res)) {
1099
            $users = api_get_person_name($friends['firstname'], $friends['lastname']).'<br />('.$friends['username'].')';
1100
            $list_friends[$users] = $friends['count_friend'];
1101
        }
1102
1103
        return $list_friends;
1104
    }
1105
1106
    /**
1107
     * Print the number of users that didn't login for a certain period of time.
1108
     */
1109
    public static function printUsersNotLoggedInStats()
1110
    {
1111
        $totalLogin = [];
1112
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1113
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1114
        $urlId = api_get_current_access_url_id();
1115
        $total = self::countUsers();
1116
        if (api_is_multiple_url_enabled()) {
1117
            $table_url = ", $access_url_rel_user_table";
1118
            $where_url = " AND login_user_id=user_id AND access_url_id='".$urlId."'";
1119
        } else {
1120
            $table_url = '';
1121
            $where_url = '';
1122
        }
1123
        $now = api_get_utc_datetime();
1124
        $sql[get_lang('This day')] =
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...
1125
            "SELECT count(distinct(login_user_id)) AS number ".
1126
            " FROM $table $table_url ".
1127
            " WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
1128
        $sql[get_lang('In the last 7 days')] =
1129
            "SELECT count(distinct(login_user_id)) AS number ".
1130
            " FROM $table $table_url ".
1131
            " WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
1132
        $sql[get_lang('In the last 31 days')] =
1133
            "SELECT count(distinct(login_user_id)) AS number ".
1134
            " FROM $table $table_url ".
1135
            " WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
1136
        $sql[sprintf(get_lang('Last %i months'), 6)] =
1137
            "SELECT count(distinct(login_user_id)) AS number ".
1138
            " FROM $table $table_url ".
1139
            " WHERE DATE_ADD(login_date, INTERVAL 6 MONTH) >= '$now' $where_url";
1140
        $sql[get_lang('Never connected')] =
1141
            "SELECT count(distinct(login_user_id)) AS number ".
1142
            " FROM $table $table_url WHERE 1=1 $where_url";
1143
        foreach ($sql as $index => $query) {
1144
            $res = Database::query($query);
1145
            $obj = Database::fetch_object($res);
1146
            $r = $total - $obj->number;
1147
            $totalLogin[$index] = $r < 0 ? 0 : $r;
1148
        }
1149
1150
        return self::printStats(
1151
            get_lang('Not logged in for some time'),
1152
            $totalLogin,
1153
            false
1154
        );
1155
    }
1156
1157
    /**
1158
     * Returns an array with indexes as the 'yyyy-mm-dd' format of each date
1159
     * within the provided range (including limits). Dates are assumed to be
1160
     * given in UTC.
1161
     *
1162
     * @param string $startDate  Start date, in Y-m-d or Y-m-d h:i:s format
1163
     * @param string $endDate    End date, in Y-m-d or Y-m-d h:i:s format
1164
     * @param bool   $removeYear Whether to remove the year in the results (for easier reading)
1165
     *
1166
     * @return array|bool False on error in the params, array of [date1 => 0, date2 => 0, ...] otherwise
1167
     */
1168
    public static function buildDatesArray($startDate, $endDate, $removeYear = false)
1169
    {
1170
        if (strlen($startDate) > 10) {
1171
            $startDate = substr($startDate, 0, 10);
1172
        }
1173
        if (strlen($endDate) > 10) {
1174
            $endDate = substr($endDate, 0, 10);
1175
        }
1176
        if (!preg_match('/\d\d\d\d-\d\d-\d\d/', $startDate)) {
1177
            return false;
1178
        }
1179
        if (!preg_match('/\d\d\d\d-\d\d-\d\d/', $startDate)) {
1180
            return false;
1181
        }
1182
        $startTimestamp = strtotime($startDate);
1183
        $endTimestamp = strtotime($endDate);
1184
        $list = [];
1185
        for ($time = $startTimestamp; $time < $endTimestamp; $time += 86400) {
1186
            $datetime = api_get_utc_datetime($time);
1187
            if ($removeYear) {
1188
                $datetime = substr($datetime, 5, 5);
1189
            } else {
1190
                $dateTime = substr($datetime, 0, 10);
1191
            }
1192
            $list[$datetime] = 0;
1193
        }
1194
1195
        return $list;
1196
    }
1197
1198
    /**
1199
     * Prepare the JS code to load a chart.
1200
     *
1201
     * @param string $url     URL for AJAX data generator
1202
     * @param string $type    bar, line, pie, etc
1203
     * @param string $options Additional options to the chart (see chart-specific library)
1204
     * @param string A JS code for loading the chart together with a call to AJAX data generator
1205
     */
1206
    public static function getJSChartTemplate($url, $type = 'pie', $options = '', $elementId = 'canvas')
1207
    {
1208
        $chartCode = '
1209
        <script>
1210
        $(function() {
1211
            $.ajax({
1212
                url: "'.$url.'",
1213
                type: "POST",
1214
                success: function(data) {
1215
                    Chart.defaults.global.responsive = true;
1216
                    var ctx = document.getElementById("'.$elementId.'").getContext("2d");
1217
                    var chart = new Chart(ctx, {
1218
                        type: "'.$type.'",
1219
                        data: data,
1220
                        options: {'.$options.'}
1221
                    });
1222
                    var title = chart.options.title.text;
1223
                    $("#'.$elementId.'_title").html(title);
1224
                    $("#'.$elementId.'_table").html(data.table);
1225
                }
1226
            });
1227
        });
1228
        </script>';
1229
1230
        return $chartCode;
1231
    }
1232
1233
    public static function getJSChartTemplateWithData($data, $type = 'pie', $options = '', $elementId = 'canvas')
1234
    {
1235
        $data = json_encode($data);
1236
        $chartCode = '
1237
        <script>
1238
            $(function() {
1239
                Chart.defaults.global.responsive = true;
1240
                var ctx = document.getElementById("'.$elementId.'").getContext("2d");
1241
                var chart = new Chart(ctx, {
1242
                    type: "'.$type.'",
1243
                    data: '.$data.',
1244
                    options: {'.$options.'}
1245
                });
1246
            });
1247
        </script>';
1248
1249
        return $chartCode;
1250
    }
1251
1252
    public static function buildJsChartData($all, $chartName)
1253
    {
1254
        $list = [];
1255
        $palette = ChamiloApi::getColorPalette(true, true);
1256
        foreach ($all as $tick => $tock) {
1257
            $list['labels'][] = $tick;
1258
        }
1259
1260
        $list['datasets'][0]['label'] = $chartName;
1261
        $list['datasets'][0]['borderColor'] = 'rgba(255,255,255,1)';
1262
1263
        $i = 0;
1264
        foreach ($all as $tick => $tock) {
1265
            $j = $i % count($palette);
1266
            $list['datasets'][0]['data'][] = $tock;
1267
            $list['datasets'][0]['backgroundColor'][] = $palette[$j];
1268
            $i++;
1269
        }
1270
1271
        $scoreDisplay = ScoreDisplay::instance();
1272
        $table = new HTML_Table(['class' => 'data_table']);
1273
        $headers = [
1274
            get_lang('Name'),
1275
            get_lang('Count'),
1276
            get_lang('Percentage'),
1277
        ];
1278
        $row = 0;
1279
        $column = 0;
1280
        foreach ($headers as $header) {
1281
            $table->setHeaderContents($row, $column, $header);
1282
            $column++;
1283
        }
1284
1285
        $total = 0;
1286
        foreach ($all as $name => $value) {
1287
            $total += $value;
1288
        }
1289
        $row++;
1290
        foreach ($all as $name => $value) {
1291
            $table->setCellContents($row, 0, $name);
1292
            $table->setCellContents($row, 1, $value);
1293
            $table->setCellContents($row, 2, $scoreDisplay->display_score([$value, $total], SCORE_PERCENT));
1294
            $row++;
1295
        }
1296
        $table = Display::page_subheader2($chartName).$table->toHtml();
1297
1298
        return ['chart' => $list, 'table' => $table];
1299
    }
1300
1301
    /**
1302
     * Display the Logins By Date report and allow export its result to XLS.
1303
     */
1304
    public static function printLoginsByDate()
1305
    {
1306
        if (isset($_GET['export']) && 'xls' === $_GET['export']) {
1307
            $result = self::getLoginsByDate($_GET['start'], $_GET['end']);
1308
            $data = [[get_lang('Username'), get_lang('First name'), get_lang('Last name'), get_lang('Total time')]];
1309
1310
            foreach ($result as $i => $item) {
1311
                $data[] = [
1312
                    $item['username'],
1313
                    $item['firstname'],
1314
                    $item['lastname'],
1315
                    api_time_to_hms($item['time_count']),
1316
                ];
1317
            }
1318
1319
            Export::arrayToXls($data);
1320
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1321
        }
1322
1323
        $content = Display::page_header(get_lang('Logins by date'));
1324
1325
        $actions = '';
1326
        $form = new FormValidator('frm_logins_by_date', 'get');
1327
        $form->addDateRangePicker(
1328
            'daterange',
1329
            get_lang('Date range'),
1330
            true,
1331
            ['format' => 'YYYY-MM-DD', 'timePicker' => 'false', 'validate_format' => 'Y-m-d']
1332
        );
1333
        $form->addHidden('report', 'logins_by_date');
1334
        $form->addButtonFilter(get_lang('Search'));
1335
1336
        if ($form->validate()) {
1337
            $values = $form->exportValues();
1338
1339
            $result = self::getLoginsByDate($values['daterange_start'], $values['daterange_end']);
1340
1341
            if (!empty($result)) {
1342
                $actions = Display::url(
1343
                    Display::return_icon('excel.png', get_lang('ExportToXls'), [], ICON_SIZE_MEDIUM),
1344
                    api_get_self().'?'.http_build_query(
1345
                        [
1346
                            'report' => 'logins_by_date',
1347
                            'export' => 'xls',
1348
                            'start' => Security::remove_XSS($values['daterange_start']),
1349
                            'end' => Security::remove_XSS($values['daterange_end']),
1350
                        ]
1351
                    )
1352
                );
1353
            }
1354
1355
            $table = new HTML_Table(['class' => 'data_table']);
1356
            $table->setHeaderContents(0, 0, get_lang('Username'));
1357
            $table->setHeaderContents(0, 1, get_lang('First name'));
1358
            $table->setHeaderContents(0, 2, get_lang('Last name'));
1359
            $table->setHeaderContents(0, 3, get_lang('Total time'));
1360
1361
            foreach ($result as $i => $item) {
1362
                $table->setCellContents($i + 1, 0, $item['username']);
1363
                $table->setCellContents($i + 1, 1, $item['firstname']);
1364
                $table->setCellContents($i + 1, 2, $item['lastname']);
1365
                $table->setCellContents($i + 1, 3, api_time_to_hms($item['time_count']));
1366
            }
1367
1368
            $table->setColAttributes(0, ['class' => 'text-center']);
1369
            $table->setColAttributes(3, ['class' => 'text-center']);
1370
            $content = $table->toHtml();
1371
        }
1372
1373
        $content .= $form->returnForm();
1374
1375
        if (!empty($actions)) {
1376
            $content .= Display::toolbarAction('logins_by_date_toolbar', [$actions]);
1377
        }
1378
1379
        return $content;
1380
    }
1381
1382
    public static function getBossTable($bossId)
1383
    {
1384
        $students = UserManager::getUsersFollowedByStudentBoss($bossId);
1385
1386
        if (!empty($students)) {
1387
            $table = new HTML_Table(['class' => 'table table-responsive', 'id' => 'table_'.$bossId]);
1388
            $headers = [
1389
                get_lang('Name'),
1390
                //get_lang('LastName'),
1391
            ];
1392
            $row = 0;
1393
            $column = 0;
1394
            foreach ($headers as $header) {
1395
                $table->setHeaderContents($row, $column, $header);
1396
                $column++;
1397
            }
1398
            $row++;
1399
            foreach ($students as $student) {
1400
                $column = 0;
1401
                $content = api_get_person_name($student['firstname'], $student['lastname']).'';
1402
                $content = '<div style="width: 200px; overflow-wrap: break-word;">'.$content.'</div>';
1403
                $table->setCellContents(
1404
                    $row,
1405
                    $column++,
1406
                    $content
1407
                );
1408
                $row++;
1409
            }
1410
1411
            return $table->toHtml();
1412
        }
1413
1414
        return '<table id="table_'.$bossId.'"></table>';
1415
    }
1416
1417
    /**
1418
     * @param string $startDate
1419
     * @param string $endDate
1420
     *
1421
     * @return array
1422
     */
1423
    private static function getLoginsByDate($startDate, $endDate)
1424
    {
1425
        $startDate = api_get_utc_datetime("$startDate 00:00:00");
1426
        $endDate = api_get_utc_datetime("$endDate 23:59:59");
1427
1428
        if (empty($startDate) || empty($endDate)) {
1429
            return [];
1430
        }
1431
1432
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1433
        $tblLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1434
        $urlJoin = '';
1435
        $urlWhere = '';
1436
1437
        if (api_is_multiple_url_enabled()) {
1438
            $tblUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1439
1440
            $urlJoin = "INNER JOIN $tblUrlUser au ON u.id = au.user_id";
1441
            $urlWhere = 'AND au.access_url_id = '.api_get_current_access_url_id();
1442
        }
1443
1444
        $sql = "SELECT u.id,
1445
                    u.firstname,
1446
                    u.lastname,
1447
                    u.username,
1448
                    SUM(TIMESTAMPDIFF(SECOND, l.login_date, l.logout_date)) AS time_count
1449
                FROM $tblUser u
1450
                INNER JOIN $tblLogin l
1451
                ON u.id = l.login_user_id
1452
                $urlJoin
1453
                WHERE l.login_date BETWEEN '$startDate' AND '$endDate'
1454
                $urlWhere
1455
                GROUP BY u.id";
1456
1457
        $stmt = Database::query($sql);
1458
1459
        return Database::store_result($stmt, 'ASSOC');
1460
    }
1461
}
1462