Passed
Push — 1.11.x ( 1b42b0...cba664 )
by Julito
13:24
created

Statistics::getLoginsByDate()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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