Passed
Pull Request — 1.11.x (#4625)
by Angel Fernando Quiroz
08:19
created

Statistics::countCoursesByVisibility()   B

Complexity

Conditions 7
Paths 33

Size

Total Lines 47
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
c 0
b 0
f 0
dl 0
loc 47
rs 8.5066
cc 7
nc 33
nop 3
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
5
6
/**
7
 * This class provides some functions for statistics.
8
 */
9
class Statistics
10
{
11
    /**
12
     * Converts a number of bytes in a formatted string.
13
     *
14
     * @param int $size
15
     *
16
     * @return string Formatted file size
17
     */
18
    public static function makeSizeString($size)
19
    {
20
        if ($size < pow(2, 10)) {
21
            return $size." bytes";
22
        }
23
        if ($size >= pow(2, 10) && $size < pow(2, 20)) {
24
            return round($size / pow(2, 10), 0)." KB";
25
        }
26
        if ($size >= pow(2, 20) && $size < pow(2, 30)) {
27
            return round($size / pow(2, 20), 1)." MB";
28
        }
29
        if ($size > pow(2, 30)) {
30
            return round($size / pow(2, 30), 2)." GB";
31
        }
32
    }
33
34
    /**
35
     * Count courses.
36
     *
37
     * @param string|null $categoryCode Code of a course category.
38
     *                                  Default: count all courses.
39
     * @param string|null $dateFrom     dateFrom
40
     * @param string|null $dateUntil    dateUntil
41
     *
42
     * @return int Number of courses counted
43
     */
44
    public static function countCourses(string $categoryCode = null, string $dateFrom = null, string $dateUntil = null)
45
    {
46
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
47
        $accessUrlRelCourseTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
48
        $urlId = api_get_current_access_url_id();
49
        if (api_is_multiple_url_enabled()) {
50
            $sql = "SELECT COUNT(*) AS number
51
                    FROM ".$courseTable." AS c, $accessUrlRelCourseTable AS u
52
                    WHERE u.c_id = c.id AND u.access_url_id = $urlId";
53
            if (isset($categoryCode)) {
54
                $sql .= " AND category_code = '".Database::escape_string($categoryCode)."'";
55
            }
56
        } else {
57
            $sql = "SELECT COUNT(*) AS number
58
                    FROM $courseTable AS c
59
                    WHERE 1 = 1";
60
            if (isset($categoryCode)) {
61
                $sql .= " AND c.category_code = '".Database::escape_string($categoryCode)."'";
62
            }
63
        }
64
65
        if (!empty($dateFrom)) {
66
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
67
            $sql .= " AND c.creation_date >= '$dateFrom' ";
68
        }
69
70
        if (!empty($dateUntil)) {
71
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
72
            $sql .= " AND c.creation_date <= '$dateUntil' ";
73
        }
74
        $res = Database::query($sql);
75
        $obj = Database::fetch_object($res);
76
77
        return $obj->number;
78
    }
79
80
    /**
81
     * Count courses by visibility.
82
     *
83
     * @param array|null  $visibility visibility (0 = closed, 1 = private, 2 = open, 3 = public) all courses
84
     * @param string|null $dateFrom   dateFrom
85
     * @param string|null $dateUntil  dateUntil
86
     *
87
     * @return int Number of courses counted
88
     */
89
    public static function countCoursesByVisibility(
90
        array $visibility = null,
91
        string $dateFrom = null,
92
        string $dateUntil = null
93
    ) {
94
        if (empty($visibility)) {
95
            return 0;
96
        } else {
97
            $visibilityString = '';
98
            $auxArrayVisibility = [];
99
            if (!is_array($visibility)) {
100
                $visibility = [$visibility];
101
            }
102
            foreach ($visibility as $item) {
103
                $auxArrayVisibility[] = (int) $item;
104
            }
105
            $visibilityString = implode(',', $auxArrayVisibility);
106
        }
107
108
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
109
        $accessUrlRelCourseTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
110
        $urlId = api_get_current_access_url_id();
111
        if (api_is_multiple_url_enabled()) {
112
            $sql = "SELECT COUNT(*) AS number
113
                    FROM $courseTable AS c, $accessUrlRelCourseTable AS u
114
                    WHERE u.c_id = c.id AND u.access_url_id = $urlId";
115
        } else {
116
            $sql = "SELECT COUNT(*) AS number
117
                    FROM $courseTable AS c
118
                    WHERE 1 = 1";
119
        }
120
        $sql .= " AND visibility IN ($visibilityString) ";
121
122
        if (!empty($dateFrom)) {
123
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
124
            $sql .= " AND c.creation_date >= '$dateFrom' ";
125
        }
126
127
        if (!empty($dateUntil)) {
128
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
129
            $sql .= " AND c.creation_date <= '$dateUntil' ";
130
        }
131
132
        $res = Database::query($sql);
133
        $obj = Database::fetch_object($res);
134
135
        return $obj->number;
136
    }
137
138
    /**
139
     * Count users.
140
     *
141
     * @param int    $status                user status (COURSEMANAGER or STUDENT) if not setted it'll count all users
142
     * @param string $categoryCode          course category code. Default: count only users without filtering category
143
     * @param bool   $countInvisibleCourses Count invisible courses (todo)
144
     * @param bool   $onlyActive            Count only active users (false to only return currently active users)
145
     *
146
     * @return int Number of users counted
147
     */
148
    public static function countUsers(
149
        $status = null,
150
        $categoryCode = null,
151
        $countInvisibleCourses = true,
152
        $onlyActive = false
153
    ) {
154
        // Database table definitions
155
        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
156
        $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
157
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
158
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
159
        $urlId = api_get_current_access_url_id();
160
        $active_filter = $onlyActive ? ' AND active=1' : '';
161
        $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
162
163
        if (api_is_multiple_url_enabled()) {
164
            $sql = "SELECT COUNT(DISTINCT(u.user_id)) AS number
165
                    FROM $user_table as u, $access_url_rel_user_table as url
166
                    WHERE
167
                        u.user_id = url.user_id AND
168
                        access_url_id = $urlId
169
                        $status_filter $active_filter";
170
            if (isset($categoryCode)) {
171
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
172
                        FROM $course_user_table cu, $course_table c, $access_url_rel_user_table as url
173
                        WHERE
174
                            c.id = cu.c_id AND
175
                            c.category_code = '".Database::escape_string($categoryCode)."' AND
176
                            cu.user_id = url.user_id AND
177
                            access_url_id = $urlId
178
                            $status_filter $active_filter";
179
            }
180
        } else {
181
            $sql = "SELECT COUNT(DISTINCT(user_id)) AS number
182
                    FROM $user_table
183
                    WHERE 1=1 $status_filter $active_filter";
184
            if (isset($categoryCode)) {
185
                $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
186
                $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
187
                        FROM $course_user_table cu, $course_table c
188
                        WHERE
189
                            c.id = cu.c_id AND
190
                            c.category_code = '".Database::escape_string($categoryCode)."'
191
                            $status_filter
192
                            $active_filter
193
                        ";
194
            }
195
        }
196
197
        $res = Database::query($sql);
198
        $obj = Database::fetch_object($res);
199
200
        return $obj->number;
201
    }
202
203
    /**
204
     * @param string $startDate
205
     * @param string $endDate
206
     *
207
     * @return array
208
     */
209
    public static function getCoursesWithActivity($startDate, $endDate)
210
    {
211
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
212
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
213
        $startDate = Database::escape_string($startDate);
214
        $endDate = Database::escape_string($endDate);
215
216
        $urlId = api_get_current_access_url_id();
217
218
        if (api_is_multiple_url_enabled()) {
219
            $sql = "SELECT DISTINCT(t.c_id) FROM $table t , $access_url_rel_course_table a
220
                    WHERE
221
                        t.c_id = a.c_id AND
222
                        access_url_id = $urlId AND
223
                        access_date BETWEEN '$startDate' AND '$endDate'
224
                    ";
225
        } else {
226
            $sql = "SELECT DISTINCT(t.c_id) FROM $table t
227
                   access_date BETWEEN '$startDate' AND '$endDate' ";
228
        }
229
230
        $result = Database::query($sql);
231
232
        return Database::store_result($result);
233
    }
234
235
    /**
236
     * Count activities from track_e_default_table.
237
     *
238
     * @return int Number of activities counted
239
     */
240
    public static function getNumberOfActivities($courseId = 0, $sessionId = 0)
241
    {
242
        // Database table definitions
243
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
244
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
245
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
246
        $urlId = api_get_current_access_url_id();
247
        if (api_is_multiple_url_enabled()) {
248
            $sql = "SELECT count(default_id) AS total_number_of_items
249
                    FROM $track_e_default, $table_user user, $access_url_rel_user_table url
250
                    WHERE
251
                        default_user_id = user.user_id AND
252
                        user.user_id=url.user_id AND
253
                        access_url_id = $urlId";
254
        } else {
255
            $sql = "SELECT count(default_id) AS total_number_of_items
256
                    FROM $track_e_default, $table_user user
257
                    WHERE default_user_id = user.user_id ";
258
        }
259
260
        if (!empty($courseId)) {
261
            $courseId = (int) $courseId;
262
            $sql .= " AND c_id = $courseId";
263
            $sql .= api_get_session_condition($sessionId);
264
        }
265
266
        if (isset($_GET['keyword'])) {
267
            $keyword = Database::escape_string(trim($_GET['keyword']));
268
            $sql .= " AND (
269
                        user.username LIKE '%".$keyword."%' OR
270
                        default_event_type LIKE '%".$keyword."%' OR
271
                        default_value_type LIKE '%".$keyword."%' OR
272
                        default_value LIKE '%".$keyword."%') ";
273
        }
274
        $res = Database::query($sql);
275
        $obj = Database::fetch_object($res);
276
277
        return $obj->total_number_of_items;
278
    }
279
280
    /**
281
     * Get activities data to display.
282
     *
283
     * @param int    $from
284
     * @param int    $numberOfItems
285
     * @param int    $column
286
     * @param string $direction
287
     * @param int    $courseId
288
     * @param int    $sessionId
289
     *
290
     * @return array
291
     */
292
    public static function getActivitiesData(
293
        $from,
294
        $numberOfItems,
295
        $column,
296
        $direction,
297
        $courseId = 0,
298
        $sessionId = 0
299
    ) {
300
        $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
301
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
302
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
303
        $urlId = api_get_current_access_url_id();
304
        $column = (int) $column;
305
        $from = (int) $from;
306
        $numberOfItems = (int) $numberOfItems;
307
        $direction = strtoupper($direction);
308
309
        if (!in_array($direction, ['ASC', 'DESC'])) {
310
            $direction = 'DESC';
311
        }
312
313
        if (api_is_multiple_url_enabled()) {
314
            $sql = "SELECT
315
                        default_event_type  as col0,
316
                        default_value_type    as col1,
317
                        default_value        as col2,
318
                        c_id         as col3,
319
                        session_id as col4,
320
                        user.username         as col5,
321
                        user.user_id         as col6,
322
                        default_date         as col7
323
                    FROM $track_e_default as track_default,
324
                    $table_user as user,
325
                    $access_url_rel_user_table as url
326
                    WHERE
327
                        track_default.default_user_id = user.user_id AND
328
                        url.user_id = user.user_id AND
329
                        access_url_id = $urlId";
330
        } else {
331
            $sql = "SELECT
332
                       default_event_type  as col0,
333
                       default_value_type    as col1,
334
                       default_value        as col2,
335
                       c_id         as col3,
336
                       session_id as col4,
337
                       user.username         as col5,
338
                       user.user_id         as col6,
339
                       default_date         as col7
340
                   FROM $track_e_default track_default, $table_user user
341
                   WHERE track_default.default_user_id = user.user_id ";
342
        }
343
344
        if (!empty($_GET['keyword'])) {
345
            $keyword = Database::escape_string(trim($_GET['keyword']));
346
            $sql .= " AND (user.username LIKE '%".$keyword."%' OR
347
                        default_event_type LIKE '%".$keyword."%' OR
348
                        default_value_type LIKE '%".$keyword."%' OR
349
                        default_value LIKE '%".$keyword."%') ";
350
        }
351
352
        if (!empty($courseId)) {
353
            $courseId = (int) $courseId;
354
            $sql .= " AND c_id = $courseId";
355
            $sql .= api_get_session_condition($sessionId);
356
        }
357
358
        if (!empty($column) && !empty($direction)) {
359
            $sql .= " ORDER BY col$column $direction";
360
        } else {
361
            $sql .= " ORDER BY col7 DESC ";
362
        }
363
        $sql .= " LIMIT $from, $numberOfItems ";
364
365
        $res = Database::query($sql);
366
        $activities = [];
367
        while ($row = Database::fetch_row($res)) {
368
            if (strpos($row[1], '_object') === false &&
369
                strpos($row[1], '_array') === false
370
            ) {
371
                $row[2] = $row[2];
372
            } else {
373
                if (!empty($row[2])) {
374
                    $originalData = str_replace('\\', '', $row[2]);
375
                    $row[2] = UnserializeApi::unserialize('not_allowed_classes', $originalData);
376
                    if (is_array($row[2]) && !empty($row[2])) {
377
                        $row[2] = implode_with_key(', ', $row[2]);
378
                    } else {
379
                        $row[2] = $originalData;
380
                    }
381
                }
382
            }
383
384
            if (!empty($row['default_date'])) {
385
                $row['default_date'] = api_get_local_time($row['default_date']);
386
            } else {
387
                $row['default_date'] = '-';
388
            }
389
390
            if (!empty($row[7])) {
391
                $row[7] = api_get_local_time($row[7]);
392
            } else {
393
                $row[7] = '-';
394
            }
395
396
            if (!empty($row[5])) {
397
                // Course
398
                if (!empty($row[3])) {
399
                    $row[3] = Display::url(
400
                        $row[3],
401
                        api_get_path(WEB_CODE_PATH).'admin/course_edit.php?id='.$row[3]
402
                    );
403
                } else {
404
                    $row[3] = '-';
405
                }
406
407
                // session
408
                if (!empty($row[4])) {
409
                    $row[4] = Display::url(
410
                        $row[4],
411
                        api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$row[4]
412
                    );
413
                } else {
414
                    $row[4] = '-';
415
                }
416
417
                // User id.
418
                $row[5] = Display::url(
419
                    $row[5],
420
                    api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$row[6],
421
                    ['class' => 'ajax']
422
                );
423
424
                $row[6] = Tracking::get_ip_from_user_event(
425
                    $row[6],
426
                    $row[7],
427
                    true
428
                );
429
                if (empty($row[6])) {
430
                    $row[6] = get_lang('Unknown');
431
                }
432
            }
433
            $activities[] = $row;
434
        }
435
436
        return $activities;
437
    }
438
439
    /**
440
     * Get all course categories.
441
     *
442
     * @return array All course categories (code => name)
443
     */
444
    public static function getCourseCategories()
445
    {
446
        $categoryTable = Database::get_main_table(TABLE_MAIN_CATEGORY);
447
        $sql = "SELECT code, name
448
                FROM $categoryTable
449
                ORDER BY tree_pos";
450
        $res = Database::query($sql);
451
        $categories = [];
452
        while ($category = Database::fetch_object($res)) {
453
            $categories[$category->code] = $category->name;
454
        }
455
456
        return $categories;
457
    }
458
459
    /**
460
     * Rescale data.
461
     *
462
     * @param array $data The data that should be rescaled
463
     * @param int   $max  The maximum value in the rescaled data (default = 500);
464
     *
465
     * @return array The rescaled data, same key as $data
466
     */
467
    public static function rescale($data, $max = 500)
468
    {
469
        $data_max = 1;
470
        foreach ($data as $index => $value) {
471
            $data_max = ($data_max < $value ? $value : $data_max);
472
        }
473
        reset($data);
474
        $result = [];
475
        $delta = $max / $data_max;
476
        foreach ($data as $index => $value) {
477
            $result[$index] = (int) round($value * $delta);
478
        }
479
480
        return $result;
481
    }
482
483
    /**
484
     * Get the number of users by access url .
485
     *
486
     * @param $currentmonth
487
     * @param $lastmonth
488
     * @param $invoicingMonth
489
     * @param $invoicingYear
490
     *
491
     * @return string
492
     */
493
    public static function printInvoicingByAccessUrl(
494
        $currentMonth,
495
        $lastMonth,
496
        $invoicingMonth,
497
        $invoicingYear
498
    ) {
499
        $tblTrackAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
500
        $tblAccessUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
501
        $tblAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL);
502
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
503
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
504
        $tblSession = Database::get_main_table(TABLE_MAIN_SESSION);
505
506
        $urls = api_get_access_url_from_user(api_get_user_id());
507
        $allowFullUrlAccess = array_search(1, $urls);
508
        $whereAccessUrl = '';
509
        if (!empty($urls) && false === $allowFullUrlAccess) {
510
            $whereAccessUrl = ' AND access_url_rel_user.access_url_id IN('.implode(',', $urls).')';
511
        }
512
513
        $sql = '
514
		    SELECT
515
		        DISTINCT access_url.description AS client,
516
		        user.lastname,
517
		        user.firstname,
518
		        course.code AS course_code,
519
		        MIN(DATE_FORMAT(track_e_access.access_date,"%d/%m/%Y")) AS start_date,
520
		        access_session_id,
521
		        CONCAT(coach.lastname,\' \',coach.firstname) AS trainer
522
		    FROM '.$tblTrackAccess.' AS track_e_access
523
            JOIN '.$tblAccessUrlUser.' AS access_url_rel_user ON access_url_rel_user.user_id=track_e_access.access_user_id
524
            JOIN '.$tblAccessUrl.' AS access_url ON access_url.id=access_url_rel_user.access_url_id
525
            JOIN '.$tblUser.' AS user ON user.user_id=access_user_id
526
            JOIN '.$tblCourse.' AS course ON course.id=track_e_access.c_id
527
            JOIN '.$tblSession.' AS session ON session.id=track_e_access.access_session_id
528
            JOIN '.$tblUser.' AS coach ON coach.user_id=session.id_coach
529
            WHERE
530
                access_session_id > 0 AND
531
                access_date LIKE \''.$currentMonth.'%\' AND
532
                user.status = '.STUDENT.' AND
533
                CONCAT(access_user_id,\'-\',access_session_id) NOT IN (SELECT CONCAT(access_user_id,\'-\',access_session_id) FROM '.$tblTrackAccess.' WHERE access_session_id > 0
534
                AND access_date LIKE \''.$lastMonth.'%\')
535
                '.$whereAccessUrl.'
536
            GROUP BY
537
                user.lastname,code
538
            ORDER BY
539
                access_url.description,
540
                user.lastname,
541
                user.firstname,
542
                track_e_access.access_date
543
            DESC,course.code
544
		';
545
        $result = Database::query($sql);
546
547
        $monthList = api_get_months_long();
548
        array_unshift($monthList, '');
549
550
        $nMonth = (int) $invoicingMonth;
551
        $content = '<h2>'.get_lang('NumberOfUsers').' '.strtolower($monthList[$nMonth]).' '.$invoicingYear.'</h2><br>';
552
553
        $form = new FormValidator('invoice_month', 'get', api_get_self().'?report=invoicing&invoicing_month='.$invoicingMonth.'&invoicing_year='.$invoicingYear);
554
        $form->addSelect('invoicing_month', get_lang('Month'), $monthList);
555
        $currentYear = date("Y");
556
        $yearList = [''];
557
        for ($i = 0; $i < 3; $i++) {
558
            $y = $currentYear - $i;
559
            $yearList[$y] = $y;
560
        }
561
        $form->addSelect('invoicing_year', get_lang('Year'), $yearList);
562
        $form->addButtonSend(get_lang('Filter'));
563
        $form->addHidden('report', 'invoicing');
564
        $content .= $form->returnForm();
565
566
        $content .= '<br>';
567
        $content .= '<table border=1 class="table table-bordered data_table">';
568
        $content .= '<tr>
569
            <th class="th-header">'.get_lang('Portal').'</th>
570
            <th class="th-header">'.get_lang('LastName').'</th>
571
            <th class="th-header">'.get_lang('FirstName').'</th>
572
            <th class="th-header">'.get_lang('Code').'</th>
573
            <th class="th-header">'.get_lang('StartDate').'</th>
574
            <th class="th-header">'.get_lang('SessionId').'</th>
575
            <th class="th-header">'.get_lang('Trainer').'</th>
576
        </tr>';
577
        $countusers = 0;
578
        $lastname = '';
579
        $lastportal = '';
580
        while ($row = Database::fetch_array($result)) {
581
            if (($row['client'] != $lastportal) && ($countusers > 0)) {
582
                $content .= '<tr class="row_odd"><td colspan=7>'.get_lang('TotalUser').' '.$lastportal.' : '.$countusers.'</td></tr>';
583
                $countusers = 0;
584
            }
585
            $content .= '<tr>
586
                <td>'.$row['client'].'</td>
587
                <td>'.$row['lastname'].'</td>
588
                <td>'.$row['firstname'].'</td>
589
                <td>'.$row['course_code'].'</td>
590
                <td>'.$row['start_date'].'</td>
591
                <td>'.$row['access_session_id'].'</td>
592
                <td>'.$row['trainer'].'</td>
593
            </tr>';
594
            if ($lastname != $row['lastname'].$row['firstname']) {
595
                $countusers++;
596
            }
597
            $lastname = $row['lastname'].$row['firstname'];
598
            $lastportal = $row['client'];
599
        }
600
        $content .= '<tr class="row_odd">
601
            <td colspan=7>'.get_lang('TotalUser').' '.$lastportal.' : '.$countusers.'</td>
602
        </tr>';
603
        $content .= '</table>';
604
605
        return $content;
606
    }
607
608
    /**
609
     * Show statistics.
610
     *
611
     * @param string $title      The title
612
     * @param array  $stats
613
     * @param bool   $showTotal
614
     * @param bool   $isFileSize
615
     *
616
     * @return string HTML table
617
     */
618
    public static function printStats(
619
        $title,
620
        $stats,
621
        $showTotal = true,
622
        $isFileSize = false
623
    ) {
624
        $total = 0;
625
        $content = '<table class="table table-hover table-striped data_table" cellspacing="0" cellpadding="3" width="90%">
626
            <thead><tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr></thead><tbody>';
627
        $i = 0;
628
        foreach ($stats as $subtitle => $number) {
629
            $total += $number;
630
        }
631
632
        foreach ($stats as $subtitle => $number) {
633
            if (!$isFileSize) {
634
                $number_label = number_format($number, 0, ',', '.');
635
            } else {
636
                $number_label = self::makeSizeString($number);
637
            }
638
            $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
639
640
            $content .= '<tr class="row_'.($i % 2 == 0 ? 'odd' : 'even').'">
641
                    <td width="25%" style="vertical-align:top;">'.$subtitle.'</td>
642
                    <td width="60%">'.Display::bar_progress($percentage, false).'</td>
643
                    <td width="5%" align="right" style="vertical-align:top;">'.$number_label.'</td>';
644
            if ($showTotal) {
645
                $content .= '<td width="5%" align="right"> '.$percentage.'%</td>';
646
            }
647
            $content .= '</tr>';
648
            $i++;
649
        }
650
        $content .= '</tbody>';
651
        if ($showTotal) {
652
            if (!$isFileSize) {
653
                $total_label = number_format($total, 0, ',', '.');
654
            } else {
655
                $total_label = self::makeSizeString($total);
656
            }
657
            $content .= '
658
                <tfoot><tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr></tfoot>
659
            ';
660
        }
661
        $content .= '</table>';
662
663
        return $content;
664
    }
665
666
    /**
667
     * Show some stats about the number of logins.
668
     *
669
     * @param string $type month, hour or day
670
     */
671
    public static function printLoginStats($type)
672
    {
673
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
674
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
675
        $urlId = api_get_current_access_url_id();
676
677
        $table_url = null;
678
        $where_url = null;
679
        $now = api_get_utc_datetime();
680
        $where_url_last = ' WHERE login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
681
        if (api_is_multiple_url_enabled()) {
682
            $table_url = ", $access_url_rel_user_table";
683
            $where_url = " WHERE login_user_id=user_id AND access_url_id = $urlId";
684
            $where_url_last = ' AND login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
685
        }
686
687
        $period = get_lang('PeriodMonth');
688
        $periodCollection = api_get_months_long();
689
        $sql = "SELECT
690
                DATE_FORMAT( login_date, '%Y-%m' ) AS stat_date ,
691
                count( login_id ) AS number_of_logins
692
                FROM $table $table_url $where_url
693
                GROUP BY stat_date
694
                ORDER BY login_date DESC";
695
        $sql_last_x = null;
696
697
        switch ($type) {
698
            case 'hour':
699
                $period = get_lang('PeriodHour');
700
                $sql = "SELECT
701
                          DATE_FORMAT( login_date, '%H') AS stat_date,
702
                          count( login_id ) AS number_of_logins
703
                        FROM $table $table_url $where_url
704
                        GROUP BY stat_date
705
                        ORDER BY stat_date ";
706
                $sql_last_x = "SELECT
707
                                DATE_FORMAT( login_date, '%H' ) AS stat_date,
708
                                count( login_id ) AS number_of_logins
709
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'DAY')."
710
                               GROUP BY stat_date
711
                               ORDER BY stat_date ";
712
                break;
713
            case 'day':
714
                $periodCollection = api_get_week_days_long();
715
                $period = get_lang('PeriodDay');
716
                $sql = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date ,
717
                        count( login_id ) AS number_of_logins
718
                        FROM  $table $table_url $where_url
719
                        GROUP BY stat_date
720
                        ORDER BY DATE_FORMAT( login_date, '%w' ) ";
721
                $sql_last_x = "SELECT
722
                                DATE_FORMAT( login_date, '%w' ) AS stat_date,
723
                                count( login_id ) AS number_of_logins
724
                               FROM $table $table_url $where_url ".sprintf($where_url_last, 'WEEK')."
725
                               GROUP BY stat_date
726
                               ORDER BY DATE_FORMAT( login_date, '%w' ) ";
727
                break;
728
        }
729
730
        $content = '';
731
        if ($sql_last_x) {
732
            $res_last_x = Database::query($sql_last_x);
733
            $result_last_x = [];
734
            while ($obj = Database::fetch_object($res_last_x)) {
735
                $stat_date = ($type === 'day') ? $periodCollection[$obj->stat_date] : $obj->stat_date;
736
                $result_last_x[$stat_date] = $obj->number_of_logins;
737
            }
738
            $content .= self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true);
739
            flush(); //flush web request at this point to see something already while the full data set is loading
740
            $content .= '<br />';
741
        }
742
        $res = Database::query($sql);
743
        $result = [];
744
        while ($obj = Database::fetch_object($res)) {
745
            $stat_date = $obj->stat_date;
746
            switch ($type) {
747
                case 'month':
748
                    $stat_date = explode('-', $stat_date);
749
                    $stat_date[1] = $periodCollection[$stat_date[1] - 1];
750
                    $stat_date = implode(' ', $stat_date);
751
                    break;
752
                case 'day':
753
                    $stat_date = $periodCollection[$stat_date];
754
                    break;
755
            }
756
            $result[$stat_date] = $obj->number_of_logins;
757
        }
758
        $content .= self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true);
759
760
        return $content;
761
    }
762
763
    /**
764
     * Print the number of recent logins.
765
     *
766
     * @param bool  $distinct        whether to only give distinct users stats, or *all* logins
767
     * @param int   $sessionDuration Number of minutes a session must have lasted at a minimum to be taken into account
768
     * @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)
769
     *
770
     * @throws Exception
771
     *
772
     * @return string HTML table
773
     */
774
    public static function printRecentLoginStats($distinct = false, $sessionDuration = 0, $periods = [])
775
    {
776
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
777
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
778
        $urlId = api_get_current_access_url_id();
779
        $table_url = '';
780
        $where_url = '';
781
        if (api_is_multiple_url_enabled()) {
782
            $table_url = ", $access_url_rel_user_table";
783
            $where_url = " AND login_user_id=user_id AND access_url_id = $urlId";
784
        }
785
786
        $now = api_get_utc_datetime();
787
        $field = 'login_id';
788
        if ($distinct) {
789
            $field = 'DISTINCT(login_user_id)';
790
        }
791
792
        if (empty($periods)) {
793
            $periods = [1, 7, 31];
794
        }
795
        $sqlList = [];
796
797
        $sessionDuration = (int) $sessionDuration * 60; // convert from minutes to seconds
798
        foreach ($periods as $day) {
799
            $date = new DateTime($now);
800
            $startDate = $date->format('Y-m-d').' 00:00:00';
801
            $endDate = $date->format('Y-m-d').' 23:59:59';
802
803
            if ($day > 1) {
804
                $startDate = $date->sub(new DateInterval('P'.$day.'D'));
805
                $startDate = $startDate->format('Y-m-d').' 00:00:00';
806
            }
807
808
            $localDate = api_get_local_time($startDate, null, null, false, false);
809
            $localEndDate = api_get_local_time($endDate, null, null, false, false);
810
811
            $label = sprintf(get_lang('LastXDays'), $day);
812
            if ($day == 1) {
813
                $label = get_lang('Today');
814
            }
815
            $label .= " <span class=\"muted right\" style=\"float: right; margin-right: 5px;\">[$localDate - $localEndDate]</span>";
816
            $sql = "SELECT count($field) AS number
817
                    FROM $table $table_url
818
                    WHERE ";
819
            if ($sessionDuration == 0) {
820
                $sql .= " logout_date != login_date AND ";
821
            } else {
822
                $sql .= " UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND ";
823
            }
824
            $sql .= "login_date BETWEEN '$startDate' AND '$endDate'
825
                        $where_url";
826
            $sqlList[$label] = $sql;
827
        }
828
829
        $sql = "SELECT count($field) AS number
830
                FROM $table $table_url ";
831
        if ($sessionDuration == 0) {
832
            $sql .= " WHERE logout_date != login_date $where_url";
833
        } else {
834
            $sql .= " WHERE UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration $where_url";
835
        }
836
        $sqlList[get_lang('Total')] = $sql;
837
        $totalLogin = [];
838
        foreach ($sqlList as $label => $query) {
839
            $res = Database::query($query);
840
            $obj = Database::fetch_object($res);
841
            $totalLogin[$label] = $obj->number;
842
        }
843
844
        if ($distinct) {
845
            $content = self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false);
846
        } else {
847
            $content = self::printStats(get_lang('Logins'), $totalLogin, false);
848
        }
849
850
        return $content;
851
    }
852
853
    /**
854
     * Get the number of recent logins.
855
     *
856
     * @param bool $distinct            Whether to only give distinct users stats, or *all* logins
857
     * @param int  $sessionDuration     Number of minutes a session must have lasted at a minimum to be taken into account
858
     * @param bool $completeMissingDays Whether to fill the daily gaps (if any) when getting a list of logins
859
     *
860
     * @throws Exception
861
     *
862
     * @return array
863
     */
864
    public static function getRecentLoginStats($distinct = false, $sessionDuration = 0, $completeMissingDays = true)
865
    {
866
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
867
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
868
        $urlId = api_get_current_access_url_id();
869
        $table_url = '';
870
        $where_url = '';
871
        if (api_is_multiple_url_enabled()) {
872
            $table_url = ", $access_url_rel_user_table";
873
            $where_url = " AND login_user_id=user_id AND access_url_id = $urlId";
874
        }
875
876
        $now = api_get_utc_datetime();
877
        $date = new DateTime($now);
878
        $date->sub(new DateInterval('P31D'));
879
        $newDate = $date->format('Y-m-d h:i:s');
880
        $totalLogin = self::buildDatesArray($newDate, $now, true);
881
882
        $field = 'login_id';
883
        if ($distinct) {
884
            $field = 'DISTINCT(login_user_id)';
885
        }
886
        $sessionDuration = (int) $sessionDuration * 60; //Convert from minutes to seconds
887
888
        $sql = "SELECT count($field) AS number, date(login_date) as login_date
889
                FROM $table $table_url
890
                WHERE ";
891
        if ($sessionDuration == 0) {
892
            $sql .= " logout_date != login_date AND ";
893
        } else {
894
            $sql .= " UNIX_TIMESTAMP(logout_date) - UNIX_TIMESTAMP(login_date) > $sessionDuration AND ";
895
        }
896
        $sql .= " login_date >= '$newDate' $where_url
897
                GROUP BY date(login_date)";
898
899
        $res = Database::query($sql);
900
        while ($row = Database::fetch_array($res, 'ASSOC')) {
901
            $monthAndDay = substr($row['login_date'], 5, 5);
902
            $totalLogin[$monthAndDay] = $row['number'];
903
        }
904
905
        return $totalLogin;
906
    }
907
908
    /**
909
     * Get course tools usage statistics for the whole platform (by URL if multi-url).
910
     */
911
    public static function getToolsStats()
912
    {
913
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
914
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
915
        $urlId = api_get_current_access_url_id();
916
917
        $tools = [
918
            'announcement',
919
            'assignment',
920
            'calendar_event',
921
            'chat',
922
            'course_description',
923
            'document',
924
            'dropbox',
925
            'group',
926
            'learnpath',
927
            'link',
928
            'quiz',
929
            'student_publication',
930
            'user',
931
            'forum',
932
        ];
933
        $tool_names = [];
934
        foreach ($tools as $tool) {
935
            $tool_names[$tool] = get_lang(ucfirst($tool), '');
936
        }
937
        if (api_is_multiple_url_enabled()) {
938
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
939
                    FROM $table t , $access_url_rel_course_table a
940
                    WHERE
941
                        access_tool IN ('".implode("','", $tools)."') AND
942
                        t.c_id = a.c_id AND
943
                        access_url_id = $urlId
944
                        GROUP BY access_tool
945
                    ";
946
        } else {
947
            $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
948
                    FROM $table
949
                    WHERE access_tool IN ('".implode("','", $tools)."')
950
                    GROUP BY access_tool ";
951
        }
952
953
        $res = Database::query($sql);
954
        $result = [];
955
        while ($obj = Database::fetch_object($res)) {
956
            $result[$tool_names[$obj->access_tool]] = $obj->number_of_logins;
957
        }
958
959
        return $result;
960
    }
961
962
    /**
963
     * Show some stats about the accesses to the different course tools.
964
     *
965
     * @param array $result If defined, this serves as data. Otherwise, will get the data from getToolsStats()
966
     *
967
     * @return string HTML table
968
     */
969
    public static function printToolStats($result = null)
970
    {
971
        if (empty($result)) {
972
            $result = self::getToolsStats();
973
        }
974
975
        return self::printStats(get_lang('PlatformToolAccess'), $result, true);
976
    }
977
978
    /**
979
     * Show some stats about the number of courses per language.
980
     */
981
    public static function printCourseByLanguageStats()
982
    {
983
        $table = Database::get_main_table(TABLE_MAIN_COURSE);
984
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
985
        $urlId = api_get_current_access_url_id();
986
        if (api_is_multiple_url_enabled()) {
987
            $sql = "SELECT course_language, count( c.code ) AS number_of_courses
988
                    FROM $table as c, $access_url_rel_course_table as u
989
                    WHERE u.c_id = c.id AND access_url_id = $urlId
990
                    GROUP BY course_language
991
                    ORDER BY number_of_courses DESC";
992
        } else {
993
            $sql = "SELECT course_language, count( code ) AS number_of_courses
994
                   FROM $table GROUP BY course_language
995
                   ORDER BY number_of_courses DESC";
996
        }
997
        $res = Database::query($sql);
998
        $result = [];
999
        while ($obj = Database::fetch_object($res)) {
1000
            $result[$obj->course_language] = $obj->number_of_courses;
1001
        }
1002
1003
        return $result;
1004
    }
1005
1006
    /**
1007
     * Shows the number of users having their picture uploaded in Dokeos.
1008
     */
1009
    public static function printUserPicturesStats()
1010
    {
1011
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1012
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1013
        $urlId = api_get_current_access_url_id();
1014
        $url_condition = null;
1015
        $url_condition2 = null;
1016
        $table = null;
1017
        if (api_is_multiple_url_enabled()) {
1018
            $url_condition = ", $access_url_rel_user_table as url WHERE url.user_id=u.user_id AND access_url_id = $urlId";
1019
            $url_condition2 = " AND url.user_id=u.user_id AND access_url_id = $urlId";
1020
            $table = ", $access_url_rel_user_table as url ";
1021
        }
1022
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u ".$url_condition;
1023
        $res = Database::query($sql);
1024
        $count1 = Database::fetch_object($res);
1025
        $sql = "SELECT COUNT(*) AS n FROM $user_table as u $table ".
1026
               "WHERE LENGTH(picture_uri) > 0 $url_condition2";
1027
        $res = Database::query($sql);
1028
        $count2 = Database::fetch_object($res);
1029
        // #users without picture
1030
        $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...
1031
        $result[get_lang('Yes')] = $count2->n; // #users with picture
1032
1033
        return self::printStats(get_lang('CountUsers').' ('.get_lang('UserPicture').')', $result, true);
1034
    }
1035
1036
    /**
1037
     * Important activities.
1038
     */
1039
    public static function printActivitiesStats()
1040
    {
1041
        $content = '<h4>'.get_lang('ImportantActivities').'</h4>';
1042
        // Create a search-box
1043
        $form = new FormValidator(
1044
            'search_simple',
1045
            'get',
1046
            api_get_path(WEB_CODE_PATH).'admin/statistics/index.php',
1047
            '',
1048
            'width=200px',
1049
            false
1050
        );
1051
        $renderer = &$form->defaultRenderer();
1052
        $renderer->setCustomElementTemplate('<span>{element}</span> ');
1053
        $form->addHidden('report', 'activities');
1054
        $form->addHidden('activities_direction', 'DESC');
1055
        $form->addHidden('activities_column', '4');
1056
        $form->addElement('text', 'keyword', get_lang('Keyword'));
1057
        $form->addButtonSearch(get_lang('Search'), 'submit');
1058
        $content .= '<div class="actions">';
1059
        $content .= $form->returnForm();
1060
        $content .= '</div>';
1061
1062
        $table = new SortableTable(
1063
            'activities',
1064
            ['Statistics', 'getNumberOfActivities'],
1065
            ['Statistics', 'getActivitiesData'],
1066
            7,
1067
            50,
1068
            'DESC'
1069
        );
1070
        $parameters = [];
1071
1072
        $parameters['report'] = 'activities';
1073
        if (isset($_GET['keyword'])) {
1074
            $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
1075
        }
1076
1077
        $table->set_additional_parameters($parameters);
1078
        $table->set_header(0, get_lang('EventType'));
1079
        $table->set_header(1, get_lang('DataType'));
1080
        $table->set_header(2, get_lang('Value'));
1081
        $table->set_header(3, get_lang('Course'));
1082
        $table->set_header(4, get_lang('Session'));
1083
        $table->set_header(5, get_lang('UserName'));
1084
        $table->set_header(6, get_lang('IPAddress'));
1085
        $table->set_header(7, get_lang('Date'));
1086
        $content .= $table->return_table();
1087
1088
        return $content;
1089
    }
1090
1091
    /**
1092
     * Shows statistics about the time of last visit to each course.
1093
     */
1094
    public static function printCourseLastVisit()
1095
    {
1096
        $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
1097
        $urlId = api_get_current_access_url_id();
1098
1099
        $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...
1100
        $columns[1] = 'access_date';
1101
        $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...
1102
        $sql_order[SORT_DESC] = 'DESC';
1103
        $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
1104
        $page_nr = isset($_GET['page_nr']) ? intval($_GET['page_nr']) : 1;
1105
        $column = isset($_GET['column']) ? intval($_GET['column']) : 0;
1106
        $direction = isset($_GET['direction']) ? $_GET['direction'] : SORT_ASC;
1107
1108
        if (!in_array($direction, [SORT_ASC, SORT_DESC])) {
1109
            $direction = SORT_ASC;
1110
        }
1111
        $form = new FormValidator('courselastvisit', 'get');
1112
        $form->addElement('hidden', 'report', 'courselastvisit');
1113
        $form->addText('date_diff', get_lang('Days'), true);
1114
        $form->addRule('date_diff', 'InvalidNumber', 'numeric');
1115
        $form->addButtonSearch(get_lang('Search'), 'submit');
1116
        if (!isset($_GET['date_diff'])) {
1117
            $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...
1118
        } else {
1119
            $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']);
1120
        }
1121
        $form->setDefaults($defaults);
1122
        $content = $form->returnForm();
1123
1124
        $values = $form->exportValues();
1125
        $date_diff = $values['date_diff'];
1126
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
1127
        if (api_is_multiple_url_enabled()) {
1128
            $sql = "SELECT * FROM $table t , $access_url_rel_course_table a
1129
                   WHERE
1130
                        t.c_id = a.c_id AND
1131
                        access_url_id = $urlId
1132
                   GROUP BY t.c_id
1133
                   HAVING t.c_id <> ''
1134
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
1135
        } else {
1136
            $sql = "SELECT * FROM $table t
1137
                   GROUP BY t.c_id
1138
                   HAVING t.c_id <> ''
1139
                   AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
1140
        }
1141
        $sql .= ' ORDER BY `'.$columns[$column].'` '.$sql_order[$direction];
1142
        $from = ($page_nr - 1) * $per_page;
1143
        $sql .= ' LIMIT '.$from.','.$per_page;
1144
1145
        $content .= '<p>'.get_lang('LastAccess').' &gt;= '.$date_diff.' '.get_lang('Days').'</p>';
1146
        $res = Database::query($sql);
1147
        if (Database::num_rows($res) > 0) {
1148
            $courses = [];
1149
            while ($obj = Database::fetch_object($res)) {
1150
                $courseInfo = api_get_course_info_by_id($obj->c_id);
1151
                $course = [];
1152
                $course[] = '<a href="'.api_get_path(WEB_COURSE_PATH).$courseInfo['code'].'">'.$courseInfo['code'].' <a>';
1153
                // Allow sort by date hiding the numerical date
1154
                $course[] = '<span style="display:none;">'.$obj->access_date.'</span>'.api_convert_and_format_date($obj->access_date);
1155
                $courses[] = $course;
1156
            }
1157
            $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...
1158
            $parameters['report'] = 'courselastvisit';
1159
            $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...
1160
            $table_header[] = [get_lang("LastAccess"), true];
1161
1162
            ob_start();
1163
            Display::display_sortable_table(
1164
                $table_header,
1165
                $courses,
1166
                ['column' => $column, 'direction' => $direction],
1167
                [],
1168
                $parameters
1169
            );
1170
            $content .= ob_get_contents();
1171
            ob_end_clean();
1172
        } else {
1173
            $content = get_lang('NoSearchResults');
1174
        }
1175
1176
        return $content;
1177
    }
1178
1179
    /**
1180
     * Displays the statistics of the messages sent and received by each user in the social network.
1181
     *
1182
     * @param string $messageType Type of message: 'sent' or 'received'
1183
     *
1184
     * @return array Message list
1185
     */
1186
    public static function getMessages($messageType)
1187
    {
1188
        $message_table = Database::get_main_table(TABLE_MESSAGE);
1189
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1190
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1191
1192
        $urlId = api_get_current_access_url_id();
1193
1194
        switch ($messageType) {
1195
            case 'sent':
1196
                $field = 'user_sender_id';
1197
                break;
1198
            case 'received':
1199
                $field = 'user_receiver_id';
1200
                break;
1201
        }
1202
1203
        if (api_is_multiple_url_enabled()) {
1204
            $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...
1205
                FROM $access_url_rel_user_table as url, $message_table m
1206
                LEFT JOIN $user_table u ON m.$field = u.user_id
1207
                WHERE  url.user_id = m.$field AND  access_url_id = $urlId
1208
                GROUP BY m.$field
1209
                ORDER BY count_message DESC ";
1210
        } else {
1211
            $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message
1212
                FROM $message_table m
1213
                LEFT JOIN $user_table u ON m.$field = u.user_id
1214
                GROUP BY m.$field ORDER BY count_message DESC ";
1215
        }
1216
        $res = Database::query($sql);
1217
        $messages_sent = [];
1218
        while ($messages = Database::fetch_array($res)) {
1219
            if (empty($messages['username'])) {
1220
                $messages['username'] = get_lang('Unknown');
1221
            }
1222
            $users = api_get_person_name(
1223
                $messages['firstname'],
1224
                $messages['lastname']
1225
            ).'<br />('.$messages['username'].')';
1226
            $messages_sent[$users] = $messages['count_message'];
1227
        }
1228
1229
        return $messages_sent;
1230
    }
1231
1232
    /**
1233
     * Count the number of friends for social network users.
1234
     */
1235
    public static function getFriends()
1236
    {
1237
        $user_friend_table = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
1238
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
1239
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1240
        $urlId = api_get_current_access_url_id();
1241
1242
        if (api_is_multiple_url_enabled()) {
1243
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend
1244
                    FROM $access_url_rel_user_table as url, $user_friend_table uf
1245
                    LEFT JOIN $user_table u
1246
                    ON (uf.user_id = u.user_id)
1247
                    WHERE
1248
                        uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' AND
1249
                        uf.user_id = url.user_id AND
1250
                        access_url_id = $urlId
1251
                    GROUP BY uf.user_id
1252
                    ORDER BY count_friend DESC ";
1253
        } else {
1254
            $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend
1255
                    FROM $user_friend_table uf
1256
                    LEFT JOIN $user_table u
1257
                    ON (uf.user_id = u.user_id)
1258
                    WHERE uf.relation_type <> '".USER_RELATION_TYPE_RRHH."'
1259
                    GROUP BY uf.user_id
1260
                    ORDER BY count_friend DESC ";
1261
        }
1262
        $res = Database::query($sql);
1263
        $list_friends = [];
1264
        while ($friends = Database::fetch_array($res)) {
1265
            $users = api_get_person_name($friends['firstname'], $friends['lastname']).'<br />('.$friends['username'].')';
1266
            $list_friends[$users] = $friends['count_friend'];
1267
        }
1268
1269
        return $list_friends;
1270
    }
1271
1272
    /**
1273
     * Print the number of users that didn't login for a certain period of time.
1274
     */
1275
    public static function printUsersNotLoggedInStats()
1276
    {
1277
        $totalLogin = [];
1278
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1279
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1280
        $urlId = api_get_current_access_url_id();
1281
        $total = self::countUsers();
1282
        if (api_is_multiple_url_enabled()) {
1283
            $table_url = ", $access_url_rel_user_table";
1284
            $where_url = " AND login_user_id=user_id AND access_url_id = $urlId";
1285
        } else {
1286
            $table_url = '';
1287
            $where_url = '';
1288
        }
1289
        $now = api_get_utc_datetime();
1290
        $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...
1291
            "SELECT count(distinct(login_user_id)) AS number ".
1292
            " FROM $table $table_url ".
1293
            " WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
1294
        $sql[get_lang('Last7days')] =
1295
            "SELECT count(distinct(login_user_id)) AS number ".
1296
            " FROM $table $table_url ".
1297
            " WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
1298
        $sql[get_lang('Last31days')] =
1299
            "SELECT count(distinct(login_user_id)) AS number ".
1300
            " FROM $table $table_url ".
1301
            " WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
1302
        $sql[sprintf(get_lang('LastXMonths'), 6)] =
1303
            "SELECT count(distinct(login_user_id)) AS number ".
1304
            " FROM $table $table_url ".
1305
            " WHERE DATE_ADD(login_date, INTERVAL 6 MONTH) >= '$now' $where_url";
1306
        $sql[get_lang('NeverConnected')] =
1307
            "SELECT count(distinct(login_user_id)) AS number ".
1308
            " FROM $table $table_url WHERE 1=1 $where_url";
1309
        foreach ($sql as $index => $query) {
1310
            $res = Database::query($query);
1311
            $obj = Database::fetch_object($res);
1312
            $r = $total - $obj->number;
1313
            $totalLogin[$index] = $r < 0 ? 0 : $r;
1314
        }
1315
1316
        return self::printStats(
1317
            get_lang('StatsUsersDidNotLoginInLastPeriods'),
1318
            $totalLogin,
1319
            false
1320
        );
1321
    }
1322
1323
    /**
1324
     * Returns an array with indexes as the 'yyyy-mm-dd' format of each date
1325
     * within the provided range (including limits). Dates are assumed to be
1326
     * given in UTC.
1327
     *
1328
     * @param string $startDate  Start date, in Y-m-d or Y-m-d h:i:s format
1329
     * @param string $endDate    End date, in Y-m-d or Y-m-d h:i:s format
1330
     * @param bool   $removeYear Whether to remove the year in the results (for easier reading)
1331
     *
1332
     * @return array|bool False on error in the params, array of [date1 => 0, date2 => 0, ...] otherwise
1333
     */
1334
    public static function buildDatesArray($startDate, $endDate, $removeYear = false)
1335
    {
1336
        if (strlen($startDate) > 10) {
1337
            $startDate = substr($startDate, 0, 10);
1338
        }
1339
        if (strlen($endDate) > 10) {
1340
            $endDate = substr($endDate, 0, 10);
1341
        }
1342
        if (!preg_match('/\d\d\d\d-\d\d-\d\d/', $startDate)) {
1343
            return false;
1344
        }
1345
        if (!preg_match('/\d\d\d\d-\d\d-\d\d/', $startDate)) {
1346
            return false;
1347
        }
1348
        $startTimestamp = strtotime($startDate);
1349
        $endTimestamp = strtotime($endDate);
1350
        $list = [];
1351
        for ($time = $startTimestamp; $time < $endTimestamp; $time += 86400) {
1352
            $datetime = api_get_utc_datetime($time);
1353
            if ($removeYear) {
1354
                $datetime = substr($datetime, 5, 5);
1355
            } else {
1356
                $dateTime = substr($datetime, 0, 10);
1357
            }
1358
            $list[$datetime] = 0;
1359
        }
1360
1361
        return $list;
1362
    }
1363
1364
    /**
1365
     * Prepare the JS code to load a chart.
1366
     *
1367
     * @param string $url     URL for AJAX data generator
1368
     * @param string $type    bar, line, pie, etc
1369
     * @param string $options Additional options to the chart (see chart-specific library)
1370
     * @param string A JS code for loading the chart together with a call to AJAX data generator
1371
     */
1372
    public static function getJSChartTemplate($url, $type = 'pie', $options = '', $elementId = 'canvas')
1373
    {
1374
        $chartCode = '
1375
        <script>
1376
        $(function() {
1377
            $.ajax({
1378
                url: "'.$url.'",
1379
                type: "POST",
1380
                success: function(data) {
1381
                    Chart.defaults.global.responsive = true;
1382
                    var ctx = document.getElementById("'.$elementId.'").getContext("2d");
1383
                    var chart = new Chart(ctx, {
1384
                        type: "'.$type.'",
1385
                        data: data,
1386
                        options: {'.$options.'}
1387
                    });
1388
                    var title = chart.options.title.text;
1389
                    $("#'.$elementId.'_title").html(title);
1390
                    $("#'.$elementId.'_table").html(data.table);
1391
                }
1392
            });
1393
        });
1394
        </script>';
1395
1396
        return $chartCode;
1397
    }
1398
1399
    public static function getJSChartTemplateWithData($data, $type = 'pie', $options = '', $elementId = 'canvas')
1400
    {
1401
        $data = json_encode($data);
1402
        $chartCode = '
1403
        <script>
1404
            $(function() {
1405
                Chart.defaults.global.responsive = true;
1406
                var ctx = document.getElementById("'.$elementId.'").getContext("2d");
1407
                var chart = new Chart(ctx, {
1408
                    type: "'.$type.'",
1409
                    data: '.$data.',
1410
                    options: {'.$options.'}
1411
                });
1412
            });
1413
        </script>';
1414
1415
        return $chartCode;
1416
    }
1417
1418
    public static function buildJsChartData($all, $chartName)
1419
    {
1420
        $list = [];
1421
        $palette = ChamiloApi::getColorPalette(true, true);
1422
        foreach ($all as $tick => $tock) {
1423
            $list['labels'][] = $tick;
1424
        }
1425
1426
        $list['datasets'][0]['label'] = $chartName;
1427
        $list['datasets'][0]['borderColor'] = 'rgba(255,255,255,1)';
1428
1429
        $i = 0;
1430
        foreach ($all as $tick => $tock) {
1431
            $j = $i % count($palette);
1432
            $list['datasets'][0]['data'][] = $tock;
1433
            $list['datasets'][0]['backgroundColor'][] = $palette[$j];
1434
            $i++;
1435
        }
1436
1437
        $scoreDisplay = ScoreDisplay::instance();
1438
        $table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
1439
        $headers = [
1440
            get_lang('Name'),
1441
            get_lang('Count'),
1442
            get_lang('Percentage'),
1443
        ];
1444
        $row = 0;
1445
        $column = 0;
1446
        foreach ($headers as $header) {
1447
            $table->setHeaderContents($row, $column, $header);
1448
            $column++;
1449
        }
1450
1451
        $total = 0;
1452
        foreach ($all as $name => $value) {
1453
            $total += $value;
1454
        }
1455
        $row++;
1456
        foreach ($all as $name => $value) {
1457
            $table->setCellContents($row, 0, $name);
1458
            $table->setCellContents($row, 1, $value);
1459
            $table->setCellContents($row, 2, $scoreDisplay->display_score([$value, $total], SCORE_PERCENT));
1460
            $row++;
1461
        }
1462
        $table = Display::page_subheader2($chartName).$table->toHtml();
1463
1464
        return ['chart' => $list, 'table' => $table];
1465
    }
1466
1467
    /**
1468
     * It displays learnpath results from lti provider.
1469
     *
1470
     * @return false|string
1471
     */
1472
    public static function printLtiLearningPath()
1473
    {
1474
        $pluginLtiProvider = ('true' === api_get_plugin_setting('lti_provider', 'enabled'));
1475
1476
        if (!$pluginLtiProvider) {
1477
            return false;
1478
        }
1479
1480
        $content = Display::page_header(get_lang('LearningPathLTI'));
1481
        $actions = '';
1482
        $form = new FormValidator('frm_lti_tool_lp', 'get');
1483
        $form->addDateRangePicker(
1484
            'daterange',
1485
            get_lang('DateRange'),
1486
            true,
1487
            ['format' => 'YYYY-MM-DD', 'timePicker' => 'false', 'validate_format' => 'Y-m-d']
1488
        );
1489
        $form->addHidden('report', 'lti_tool_lp');
1490
        $form->addButtonFilter(get_lang('Search'));
1491
1492
        if ($form->validate()) {
1493
            $values = $form->exportValues();
1494
1495
            $result = self::getLtiLearningPathByDate($values['daterange_start'], $values['daterange_end']);
1496
1497
            $table = new HTML_Table(['class' => 'table table-bordered data_table']);
1498
            $table->setHeaderContents(0, 0, get_lang('URL'));
1499
            $table->setHeaderContents(0, 1, get_lang('ToolLp'));
1500
            $table->setHeaderContents(0, 2, get_lang('LastName'));
1501
            $table->setHeaderContents(0, 3, get_lang('FirstName'));
1502
            $table->setHeaderContents(0, 4, get_lang('FirstAccess'));
1503
            $i = 1;
1504
            foreach ($result as $item) {
1505
                if (!empty($item['learnpaths'])) {
1506
                    foreach ($item['learnpaths'] as $lpId => $lpValues) {
1507
                        $lpName = learnpath::getLpNameById($lpId);
1508
                        if (count($lpValues['users']) > 0) {
1509
                            foreach ($lpValues['users'] as $user) {
1510
                                $table->setCellContents($i, 0, $item['issuer']);
1511
                                $table->setCellContents($i, 1, $lpName);
1512
                                $table->setCellContents($i, 2, $user['lastname']);
1513
                                $table->setCellContents($i, 3, $user['firstname']);
1514
                                $table->setCellContents($i, 4, $user['first_access']);
1515
                                $i++;
1516
                            }
1517
                        }
1518
                    }
1519
                }
1520
            }
1521
            $content = $table->toHtml();
1522
        }
1523
1524
        $content .= $form->returnForm();
1525
        if (!empty($actions)) {
1526
            $content .= Display::toolbarAction('lti_tool_lp_toolbar', [$actions]);
1527
        }
1528
1529
        return $content;
1530
    }
1531
1532
    /**
1533
     * Display the Logins By Date report and allow export its result to XLS.
1534
     */
1535
    public static function printLoginsByDate()
1536
    {
1537
        if (isset($_GET['export']) && 'xls' === $_GET['export']) {
1538
            $result = self::getLoginsByDate($_GET['start'], $_GET['end']);
1539
            $data = [[get_lang('Username'), get_lang('FirstName'), get_lang('LastName'), get_lang('TotalTime')]];
1540
1541
            foreach ($result as $i => $item) {
1542
                $data[] = [
1543
                    $item['username'],
1544
                    $item['firstname'],
1545
                    $item['lastname'],
1546
                    api_time_to_hms($item['time_count']),
1547
                ];
1548
            }
1549
1550
            Export::arrayToXls($data);
1551
            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...
1552
        }
1553
1554
        $content = Display::page_header(get_lang('LoginsByDate'));
1555
1556
        $actions = '';
1557
        $form = new FormValidator('frm_logins_by_date', 'get');
1558
        $form->addDateRangePicker(
1559
            'daterange',
1560
            get_lang('DateRange'),
1561
            true,
1562
            ['format' => 'YYYY-MM-DD', 'timePicker' => 'false', 'validate_format' => 'Y-m-d']
1563
        );
1564
        $form->addHidden('report', 'logins_by_date');
1565
        $form->addButtonFilter(get_lang('Search'));
1566
1567
        if ($form->validate()) {
1568
            $values = $form->exportValues();
1569
1570
            $result = self::getLoginsByDate($values['daterange_start'], $values['daterange_end']);
1571
1572
            if (!empty($result)) {
1573
                $actions = Display::url(
1574
                    Display::return_icon('excel.png', get_lang('ExportToXls'), [], ICON_SIZE_MEDIUM),
1575
                    api_get_self().'?'.http_build_query(
1576
                        [
1577
                            'report' => 'logins_by_date',
1578
                            'export' => 'xls',
1579
                            'start' => Security::remove_XSS($values['daterange_start']),
1580
                            'end' => Security::remove_XSS($values['daterange_end']),
1581
                        ]
1582
                    )
1583
                );
1584
            }
1585
1586
            $table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
1587
            $table->setHeaderContents(0, 0, get_lang('Username'));
1588
            $table->setHeaderContents(0, 1, get_lang('FirstName'));
1589
            $table->setHeaderContents(0, 2, get_lang('LastName'));
1590
            $table->setHeaderContents(0, 3, get_lang('TotalTime'));
1591
1592
            foreach ($result as $i => $item) {
1593
                $table->setCellContents($i + 1, 0, $item['username']);
1594
                $table->setCellContents($i + 1, 1, $item['firstname']);
1595
                $table->setCellContents($i + 1, 2, $item['lastname']);
1596
                $table->setCellContents($i + 1, 3, api_time_to_hms($item['time_count']));
1597
            }
1598
1599
            $table->setColAttributes(0, ['class' => 'text-center']);
1600
            $table->setColAttributes(3, ['class' => 'text-center']);
1601
            $content = $table->toHtml();
1602
        }
1603
1604
        $content .= $form->returnForm();
1605
1606
        if (!empty($actions)) {
1607
            $content .= Display::toolbarAction('logins_by_date_toolbar', [$actions]);
1608
        }
1609
1610
        return $content;
1611
    }
1612
1613
    public static function getBossTable($bossId)
1614
    {
1615
        $students = UserManager::getUsersFollowedByStudentBoss($bossId);
1616
1617
        if (!empty($students)) {
1618
            $table = new HTML_Table(['class' => 'table table-responsive', 'id' => 'table_'.$bossId]);
1619
            $headers = [
1620
                get_lang('Name'),
1621
                //get_lang('LastName'),
1622
            ];
1623
            $row = 0;
1624
            $column = 0;
1625
            foreach ($headers as $header) {
1626
                $table->setHeaderContents($row, $column, $header);
1627
                $column++;
1628
            }
1629
            $row++;
1630
            foreach ($students as $student) {
1631
                $column = 0;
1632
                $content = api_get_person_name($student['firstname'], $student['lastname']).'';
1633
                $content = '<div style="width: 200px; overflow-wrap: break-word;">'.$content.'</div>';
1634
                $table->setCellContents(
1635
                    $row,
1636
                    $column++,
1637
                    $content
1638
                );
1639
                $row++;
1640
            }
1641
1642
            return $table->toHtml();
1643
        }
1644
1645
        return '<table id="table_'.$bossId.'"></table>';
1646
    }
1647
1648
    /**
1649
     * @param string $startDate
1650
     * @param string $endDate
1651
     *
1652
     * @return array
1653
     */
1654
    public static function getLoginsByDate($startDate, $endDate)
1655
    {
1656
        /** @var DateTime $startDate */
1657
        $startDate = api_get_utc_datetime("$startDate 00:00:00");
1658
        /** @var DateTime $endDate */
1659
        $endDate = api_get_utc_datetime("$endDate 23:59:59");
1660
1661
        if (empty($startDate) || empty($endDate)) {
1662
            return [];
1663
        }
1664
1665
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
1666
        $tblLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1667
        $urlJoin = '';
1668
        $urlWhere = '';
1669
1670
        if (api_is_multiple_url_enabled()) {
1671
            $tblUrlUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1672
1673
            $urlJoin = "INNER JOIN $tblUrlUser au ON u.id = au.user_id";
1674
            $urlWhere = 'AND au.access_url_id = '.api_get_current_access_url_id();
1675
        }
1676
1677
        $sql = "SELECT u.id,
1678
                    u.firstname,
1679
                    u.lastname,
1680
                    u.username,
1681
                    SUM(TIMESTAMPDIFF(SECOND, l.login_date, l.logout_date)) AS time_count
1682
                FROM $tblUser u
1683
                INNER JOIN $tblLogin l ON u.id = l.login_user_id
1684
                $urlJoin
1685
                WHERE l.login_date BETWEEN '$startDate' AND '$endDate'
1686
                $urlWhere
1687
                GROUP BY u.id";
1688
1689
        $stmt = Database::query($sql);
1690
        $result = Database::store_result($stmt, 'ASSOC');
1691
1692
        return $result;
1693
    }
1694
1695
    /**
1696
     * Return de number of certificates generated.
1697
     * This function is resource intensive.
1698
     */
1699
    public static function countCertificatesByQuarter(string $dateFrom = null, string $dateUntil = null): int
1700
    {
1701
        $tableGradebookCertificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
1702
1703
        $condition = "";
1704
1705
        if (!empty($dateFrom) && !empty($dateUntil)) {
1706
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
1707
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
1708
            $condition = "WHERE (created_at BETWEEN '$dateFrom' AND '$dateUntil')";
1709
        } elseif (!empty($dateFrom)) {
1710
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
1711
            $condition = "WHERE created_at >= '$dateFrom'";
1712
        } elseif (!empty($dateUntil)) {
1713
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
1714
            $condition = "WHERE created_at <= '$dateUntil'";
1715
        }
1716
1717
        $sql = "
1718
            SELECT count(*) AS count
1719
            FROM $tableGradebookCertificate
1720
            $condition
1721
        ";
1722
1723
        $response = Database::query($sql);
1724
        $obj = Database::fetch_object($response);
1725
1726
        return $obj->count;
1727
    }
1728
1729
    /**
1730
     * Get the number of logins by dates.
1731
     * This function is resource intensive.
1732
     */
1733
    public static function getSessionsByDuration(string $dateFrom, string $dateUntil): array
1734
    {
1735
        $results = [
1736
            '0' => 0,
1737
            '5' => 0,
1738
            '10' => 0,
1739
            '15' => 0,
1740
            '30' => 0,
1741
            '60' => 0,
1742
        ];
1743
1744
        if (!empty($dateFrom) && !empty($dateUntil)) {
1745
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1746
            $accessUrlRelUserTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1747
            $urlId = api_get_current_access_url_id();
1748
            $tableUrl = '';
1749
            $whereUrl = '';
1750
1751
            $dateFrom = api_get_utc_datetime("$dateFrom 00:00:00");
1752
            $dateUntil = api_get_utc_datetime("$dateUntil 23:59:59");
1753
1754
            if (api_is_multiple_url_enabled()) {
1755
                $tableUrl = ", $accessUrlRelUserTable";
1756
                $whereUrl = " AND login_user_id = user_id AND access_url_id = $urlId";
1757
            }
1758
1759
            $sql = "SELECT login_id, TIMESTAMPDIFF(SECOND, login_date, logout_date) AS duration
1760
            FROM $table $tableUrl
1761
            WHERE login_date >= '$dateFrom'
1762
            AND logout_date <= '$dateUntil'
1763
            $whereUrl
1764
            ";
1765
1766
            $res = Database::query($sql);
1767
1768
            while ($session = Database::fetch_array($res)) {
1769
                if ($session['duration'] > 3600) {
1770
                    $results['60']++;
1771
                } elseif ($session['duration'] > 1800) {
1772
                    $results['30']++;
1773
                } elseif ($session['duration'] > 900) {
1774
                    $results['15']++;
1775
                } elseif ($session['duration'] > 600) {
1776
                    $results['10']++;
1777
                } elseif ($session['duration'] > 300) {
1778
                    $results['5']++;
1779
                } else {
1780
                    $results['0']++;
1781
                }
1782
            }
1783
        }
1784
1785
        return $results;
1786
    }
1787
1788
    public static function returnDuplicatedUsersTable(): string
1789
    {
1790
        $additionalProfileField = $_GET['additional_profile_field'] ?? [];
1791
1792
        $additionalExtraFieldsInfo = [];
1793
1794
        $objExtraField = new ExtraField('user');
1795
1796
        foreach ($additionalProfileField as $fieldId) {
1797
            $additionalExtraFieldsInfo[$fieldId] = $objExtraField->getFieldInfoByFieldId($fieldId);
1798
        }
1799
1800
        $usersInfo = Statistics::getDuplicatedUsers($additionalExtraFieldsInfo);
1801
1802
        $column = 0;
1803
1804
        $table = new SortableTableFromArray(array_values($usersInfo));
1805
        $table->set_additional_parameters([
1806
            'report' => 'duplicated_users',
1807
            'additional_profile_field' => $additionalProfileField,
1808
        ]);
1809
        $table->set_header($column++, get_lang('Id'));
1810
1811
        if (api_is_western_name_order()) {
1812
            $table->set_header($column++, get_lang('FirstName'));
1813
            $table->set_header($column++, get_lang('LastName'));
1814
        } else {
1815
            $table->set_header($column++, get_lang('LastName'));
1816
            $table->set_header($column++, get_lang('FirstName'));
1817
        }
1818
1819
        $table->set_header($column++, get_lang('Email'));
1820
        $table->set_header($column++, get_lang('RegistrationDate'));
1821
        $table->set_column_filter(
1822
            $column - 1,
1823
            function ($value) {
1824
                return api_convert_and_format_date($value, DATE_TIME_FORMAT_LONG);
1825
            }
1826
        );
1827
        $table->set_header($column++, get_lang('FirstLoginInPlatform'));
1828
        $table->set_header($column++, get_lang('LatestLoginInPlatform'));
1829
        $table->set_header($column++, get_lang('Role'));
1830
        $table->set_column_filter(
1831
            $column - 1,
1832
            function ($value) {
1833
                return api_get_status_langvars()[$value];
1834
            }
1835
        );
1836
        $table->set_header(
1837
            $column++,
1838
            get_lang('Courses').' <small class="block">'.get_lang('SubscriptionCount').'<small>'
1839
        );
1840
        $table->set_header(
1841
            $column++,
1842
            get_lang('Sessions').' <small class="block">'.get_lang('SubscriptionCount').'<small>'
1843
        );
1844
1845
        foreach ($additionalExtraFieldsInfo as $fieldInfo) {
1846
            $table->set_header($column++, $fieldInfo['display_text']);
1847
        }
1848
1849
        $table->set_header($column, get_lang('Active'));
1850
        $table->set_column_filter(
1851
            $column,
1852
            [UserManager::class, 'getActiveFilterForTable']
1853
        );
1854
        $table->setHideColumn(0);
1855
1856
        return TrackingCourseLog::displayAdditionalProfileFields([], api_get_self())
1857
            .PHP_EOL
1858
            .$table->return_table();
1859
    }
1860
1861
    /**
1862
     * It gets lti learnpath results by date.
1863
     *
1864
     * @param $startDate
1865
     * @param $endDate
1866
     *
1867
     * @return array
1868
     */
1869
    private static function getLtiLearningPathByDate($startDate, $endDate)
1870
    {
1871
        /** @var DateTime $startDate */
1872
        $startDate = api_get_utc_datetime("$startDate 00:00:00");
1873
        /** @var DateTime $endDate */
1874
        $endDate = api_get_utc_datetime("$endDate 23:59:59");
1875
1876
        if (empty($startDate) || empty($endDate)) {
1877
            return [];
1878
        }
1879
1880
        require_once api_get_path(SYS_PLUGIN_PATH).'lti_provider/LtiProviderPlugin.php';
1881
1882
        $plugin = LtiProviderPlugin::create();
1883
1884
        $result = $plugin->getToolLearnPathResult($startDate, $endDate);
1885
1886
        return $result;
1887
    }
1888
1889
    private static function getDuplicatedUsers(array $additionalExtraFieldsInfo): array
1890
    {
1891
        $sql = "SELECT firstname, lastname, COUNT(*) as count
1892
            FROM user
1893
            GROUP BY firstname, lastname
1894
            HAVING count > 1
1895
            ORDER BY lastname, firstname"
1896
        ;
1897
1898
        $result = Database::query($sql);
1899
1900
        if (1 > Database::num_rows($result)) {
1901
            return [];
1902
        }
1903
1904
        $usersInfo = [];
1905
1906
        while ($rowStat = Database::fetch_assoc($result)) {
1907
            $sql = "SELECT id, email, registration_date, status, active
1908
                FROM user WHERE firstname = '{$rowStat['firstname']}' AND lastname = '{$rowStat['lastname']}'"
1909
            ;
1910
1911
            $result = Database::query($sql);
1912
1913
            if (1 > Database::num_rows($result)) {
1914
                continue;
1915
            }
1916
1917
            $objExtraValue = new ExtraFieldValue('user');
1918
1919
            while ($rowUser = Database::fetch_assoc($result)) {
1920
                $studentId = $rowUser['id'];
1921
1922
                $usersInfo[$studentId] = [];
1923
                $usersInfo[$studentId][] = $rowUser['id'];
1924
1925
                if (api_is_western_name_order()) {
1926
                    $usersInfo[$studentId][] = $rowStat['firstname'];
1927
                    $usersInfo[$studentId][] = $rowStat['lastname'];
1928
                } else {
1929
                    $usersInfo[$studentId][] = $rowStat['lastname'];
1930
                    $usersInfo[$studentId][] = $rowStat['firstname'];
1931
                }
1932
1933
                $usersInfo[$studentId][] = $rowUser['email'];
1934
                $usersInfo[$studentId][] = $rowUser['registration_date'];
1935
                $usersInfo[$studentId][] = Tracking::get_first_connection_date(
1936
                    $studentId,
1937
                    DATE_TIME_FORMAT_LONG
1938
                );
1939
                $usersInfo[$studentId][] = Tracking::get_last_connection_date(
1940
                    $studentId,
1941
                    true,
1942
                    false,
1943
                    DATE_TIME_FORMAT_LONG
1944
                );
1945
                $usersInfo[$studentId][] = $rowUser['status'];
1946
                $usersInfo[$studentId][] = Tracking::count_course_per_student($studentId);
1947
                $usersInfo[$studentId][] = Tracking::countSessionsPerStudent($studentId);
1948
1949
                foreach ($additionalExtraFieldsInfo as $fieldInfo) {
1950
                    $extraValue = $objExtraValue->get_values_by_handler_and_field_id($studentId, $fieldInfo['id'], true);
1951
                    $usersInfo[$studentId][] = $extraValue['value'] ?? null;
1952
                }
1953
1954
                $usersInfo[$studentId][] = $rowUser['active'];
1955
            }
1956
        }
1957
1958
        return $usersInfo;
1959
    }
1960
}
1961