Completed
Push — master ( 966b12...7d9ab3 )
by Julito
08:22
created

Statistics::printCourseLastVisit()   C

Complexity

Conditions 10
Paths 256

Size

Total Lines 83
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 61
nc 256
nop 0
dl 0
loc 83
rs 5.7575
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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