statistics.lib.php 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * This class provides some functions for statistics
  5. * @package chamilo.statistics
  6. */
  7. class Statistics
  8. {
  9. /**
  10. * Converts a number of bytes in a formatted string
  11. * @param int $size
  12. * @return string Formatted file size
  13. */
  14. public static function makeSizeString($size)
  15. {
  16. if ($size < pow(2, 10)) {
  17. return $size." bytes";
  18. }
  19. if ($size >= pow(2, 10) && $size < pow(2, 20)) {
  20. return round($size / pow(2, 10), 0)." KB";
  21. }
  22. if ($size >= pow(2, 20) && $size < pow(2, 30)) {
  23. return round($size / pow(2, 20), 1)." MB";
  24. }
  25. if ($size > pow(2, 30)) {
  26. return round($size / pow(2, 30), 2)." GB";
  27. }
  28. }
  29. /**
  30. * Count courses
  31. * @param string $categoryCode Code of a course category. Default: count all courses.
  32. * @return int Number of courses counted
  33. */
  34. public static function countCourses($categoryCode = null)
  35. {
  36. $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
  37. $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
  38. $current_url_id = api_get_current_access_url_id();
  39. if (api_is_multiple_url_enabled()) {
  40. $sql = "SELECT COUNT(*) AS number
  41. FROM ".$course_table." as c, ".$access_url_rel_course_table." as u
  42. WHERE u.c_id = c.id AND access_url_id='".$current_url_id."'";
  43. if (isset ($categoryCode)) {
  44. $sql .= " AND category_code = '".Database::escape_string($categoryCode)."'";
  45. }
  46. } else {
  47. $sql = "SELECT COUNT(*) AS number
  48. FROM ".$course_table." ";
  49. if (isset ($categoryCode)) {
  50. $sql .= " WHERE category_code = '".Database::escape_string($categoryCode)."'";
  51. }
  52. }
  53. $res = Database::query($sql);
  54. $obj = Database::fetch_object($res);
  55. return $obj->number;
  56. }
  57. /**
  58. * Count courses by visibility
  59. * @param int $visibility Visibility (0 = closed, 1 = private, 2 = open, 3 = public) all courses.
  60. * @return int Number of courses counted
  61. */
  62. public static function countCoursesByVisibility($visibility = null)
  63. {
  64. if (!isset($visibility)) {
  65. return 0;
  66. }
  67. $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
  68. $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
  69. $current_url_id = api_get_current_access_url_id();
  70. if (api_is_multiple_url_enabled()) {
  71. $sql = "SELECT COUNT(*) AS number
  72. FROM ".$course_table." as c, ".$access_url_rel_course_table." as u
  73. WHERE u.c_id = c.id AND access_url_id='".$current_url_id."'";
  74. if (isset ($visibility)) {
  75. $sql .= " AND visibility = ".intval($visibility);
  76. }
  77. } else {
  78. $sql = "SELECT COUNT(*) AS number FROM ".$course_table." ";
  79. if (isset ($visibility)) {
  80. $sql .= " WHERE visibility = ".intval($visibility);
  81. }
  82. }
  83. $res = Database::query($sql);
  84. $obj = Database::fetch_object($res);
  85. return $obj->number;
  86. }
  87. /**
  88. * Count users
  89. * @param int $status Optional user status (COURSEMANAGER or STUDENT), if it's not setted it'll count all users.
  90. * @param string $categoryCode Optional, code of a course category. Default: count only users without filtering category
  91. * @param bool $countInvisibleCourses Count invisible courses (todo)
  92. * @param bool $onlyActive Count only active users (false to only return currently active users)
  93. * @return int Number of users counted
  94. */
  95. public static function countUsers($status = null, $categoryCode = null, $countInvisibleCourses = true, $onlyActive = false)
  96. {
  97. // Database table definitions
  98. $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
  99. $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
  100. $user_table = Database::get_main_table(TABLE_MAIN_USER);
  101. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  102. $current_url_id = api_get_current_access_url_id();
  103. $active_filter = $onlyActive ? ' AND active=1' : '';
  104. $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
  105. if (api_is_multiple_url_enabled()) {
  106. $sql = "SELECT COUNT(DISTINCT(u.user_id)) AS number
  107. FROM $user_table as u, $access_url_rel_user_table as url
  108. WHERE
  109. u.user_id = url.user_id AND
  110. access_url_id = '".$current_url_id."'
  111. $status_filter $active_filter";
  112. if (isset($categoryCode)) {
  113. $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
  114. FROM $course_user_table cu, $course_table c, $access_url_rel_user_table as url
  115. WHERE
  116. c.id = cu.c_id AND
  117. c.category_code = '".Database::escape_string($categoryCode)."' AND
  118. cu.user_id = url.user_id AND
  119. access_url_id='".$current_url_id."'
  120. $status_filter $active_filter";
  121. }
  122. } else {
  123. $sql = "SELECT COUNT(DISTINCT(user_id)) AS number
  124. FROM $user_table
  125. WHERE 1=1 $status_filter $active_filter";
  126. if (isset($categoryCode)) {
  127. $status_filter = isset($status) ? ' AND status = '.intval($status) : '';
  128. $sql = "SELECT COUNT(DISTINCT(cu.user_id)) AS number
  129. FROM $course_user_table cu, $course_table c
  130. WHERE
  131. c.id = cu.c_id AND
  132. c.category_code = '".Database::escape_string($categoryCode)."'
  133. $status_filter
  134. $active_filter
  135. ";
  136. }
  137. }
  138. $res = Database::query($sql);
  139. $obj = Database::fetch_object($res);
  140. return $obj->number;
  141. }
  142. /**
  143. * Count sessions
  144. * @return int Number of sessions counted
  145. */
  146. public static function countSessions()
  147. {
  148. $session_table = Database::get_main_table(TABLE_MAIN_SESSION);
  149. $access_url_rel_session_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
  150. if (api_is_multiple_url_enabled()) {
  151. $current_url_id = api_get_current_access_url_id();
  152. $sql = "SELECT COUNT(id) AS number
  153. FROM ".$session_table." as s, ".$access_url_rel_session_table." as u
  154. WHERE u.session_id=s.id AND access_url_id='".$current_url_id."'";
  155. } else {
  156. $sql = "SELECT COUNT(id) AS number
  157. FROM ".$session_table." ";
  158. }
  159. $res = Database::query($sql);
  160. $obj = Database::fetch_object($res);
  161. return $obj->number;
  162. }
  163. /**
  164. * Count activities from track_e_default_table
  165. * @return int Number of activities counted
  166. */
  167. public static function getNumberOfActivities()
  168. {
  169. // Database table definitions
  170. $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
  171. $table_user = Database::get_main_table(TABLE_MAIN_USER);
  172. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  173. $current_url_id = api_get_current_access_url_id();
  174. if (api_is_multiple_url_enabled()) {
  175. $sql = "SELECT count(default_id) AS total_number_of_items
  176. FROM $track_e_default, $table_user user, $access_url_rel_user_table url
  177. WHERE
  178. default_user_id = user.user_id AND
  179. user.user_id=url.user_id AND
  180. access_url_id='".$current_url_id."'";
  181. } else {
  182. $sql = "SELECT count(default_id) AS total_number_of_items
  183. FROM $track_e_default, $table_user user
  184. WHERE default_user_id = user.user_id ";
  185. }
  186. if (isset($_GET['keyword'])) {
  187. $keyword = Database::escape_string(trim($_GET['keyword']));
  188. $sql .= " AND (
  189. user.username LIKE '%".$keyword."%' OR
  190. default_event_type LIKE '%".$keyword."%' OR
  191. default_value_type LIKE '%".$keyword."%' OR
  192. default_value LIKE '%".$keyword."%') ";
  193. }
  194. $res = Database::query($sql);
  195. $obj = Database::fetch_object($res);
  196. return $obj->total_number_of_items;
  197. }
  198. /**
  199. * Get activities data to display
  200. * @param int $from
  201. * @param int $numberOfItems
  202. * @param int $column
  203. * @param string $direction
  204. * @return array
  205. */
  206. public static function getActivitiesData($from, $numberOfItems, $column, $direction)
  207. {
  208. $track_e_default = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
  209. $table_user = Database::get_main_table(TABLE_MAIN_USER);
  210. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  211. $current_url_id = api_get_current_access_url_id();
  212. $column = intval($column);
  213. $from = intval($from);
  214. $numberOfItems = intval($numberOfItems);
  215. if (!in_array($direction, array('ASC', 'DESC'))) {
  216. $direction = 'DESC';
  217. }
  218. if (api_is_multiple_url_enabled()) {
  219. $sql = "SELECT
  220. default_event_type as col0,
  221. default_value_type as col1,
  222. default_value as col2,
  223. c_id as col3,
  224. session_id as col4,
  225. user.username as col5,
  226. user.user_id as col6,
  227. default_date as col7
  228. FROM $track_e_default as track_default,
  229. $table_user as user,
  230. $access_url_rel_user_table as url
  231. WHERE
  232. track_default.default_user_id = user.user_id AND
  233. url.user_id = user.user_id AND
  234. access_url_id= $current_url_id ";
  235. } else {
  236. $sql = "SELECT
  237. default_event_type as col0,
  238. default_value_type as col1,
  239. default_value as col2,
  240. c_id as col3,
  241. session_id as col4,
  242. user.username as col5,
  243. user.user_id as col6,
  244. default_date as col7
  245. FROM $track_e_default track_default, $table_user user
  246. WHERE track_default.default_user_id = user.user_id ";
  247. }
  248. if (isset($_GET['keyword'])) {
  249. $keyword = Database::escape_string(trim($_GET['keyword']));
  250. $sql .= " AND (user.username LIKE '%".$keyword."%' OR
  251. default_event_type LIKE '%".$keyword."%' OR
  252. default_value_type LIKE '%".$keyword."%' OR
  253. default_value LIKE '%".$keyword."%') ";
  254. }
  255. if (!empty($column) && !empty($direction)) {
  256. $sql .= " ORDER BY col$column $direction";
  257. } else {
  258. $sql .= " ORDER BY col5 DESC ";
  259. }
  260. $sql .= " LIMIT $from,$numberOfItems ";
  261. $res = Database::query($sql);
  262. $activities = array();
  263. while ($row = Database::fetch_row($res)) {
  264. if (strpos($row[1], '_object') === false && strpos($row[1], '_array') === false) {
  265. $row[2] = $row[2];
  266. } else {
  267. if (!empty($row[2])) {
  268. $originalData = str_replace('\\', '', $row[2]);
  269. $row[2] = unserialize($originalData);
  270. if (is_array($row[2]) && !empty($row[2])) {
  271. $row[2] = implode_with_key(', ', $row[2]);
  272. } else {
  273. $row[2] = $originalData;
  274. }
  275. }
  276. }
  277. if (!empty($row['default_date'])) {
  278. $row['default_date'] = api_get_local_time($row['default_date']);
  279. } else {
  280. $row['default_date'] = '-';
  281. }
  282. if (!empty($row[5])) {
  283. //course
  284. if (!empty($row[3])) {
  285. $row[3] = Display::url($row[3], api_get_path(WEB_CODE_PATH).'admin/course_edit.php?id='.$row[3]);
  286. } else {
  287. $row[3] = '-';
  288. }
  289. // session
  290. if (!empty($row[4])) {
  291. $row[4] = Display::url(
  292. $row[4],
  293. api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$row[4]
  294. );
  295. } else {
  296. $row[4] = '-';
  297. }
  298. // User id.
  299. $row[5] = Display::url(
  300. $row[5],
  301. api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$row[6],
  302. array('title' => get_lang('UserInfo'))
  303. );
  304. $row[6] = TrackingUserLog::get_ip_from_user_event($row[6], $row[7], true);
  305. if (empty($row[6])) {
  306. $row[6] = get_lang('Unknown');
  307. }
  308. }
  309. $activities[] = $row;
  310. }
  311. return $activities;
  312. }
  313. /**
  314. * Get all course categories
  315. * @return array All course categories (code => name)
  316. */
  317. public static function getCourseCategories()
  318. {
  319. $categoryTable = Database::get_main_table(TABLE_MAIN_CATEGORY);
  320. $sql = "SELECT code, name
  321. FROM $categoryTable
  322. ORDER BY tree_pos";
  323. $res = Database::query($sql);
  324. $categories = array();
  325. while ($category = Database::fetch_object($res)) {
  326. $categories[$category->code] = $category->name;
  327. }
  328. return $categories;
  329. }
  330. /**
  331. * Rescale data
  332. * @param array $data The data that should be rescaled
  333. * @param int $max The maximum value in the rescaled data (default = 500);
  334. * @return array The rescaled data, same key as $data
  335. */
  336. public static function rescale($data, $max = 500)
  337. {
  338. $data_max = 1;
  339. foreach ($data as $index => $value) {
  340. $data_max = ($data_max < $value ? $value : $data_max);
  341. }
  342. reset($data);
  343. $result = array();
  344. $delta = $max / $data_max;
  345. foreach ($data as $index => $value) {
  346. $result[$index] = (int) round($value * $delta);
  347. }
  348. return $result;
  349. }
  350. /**
  351. * Show statistics
  352. * @param string $title The title
  353. * @param array $stats
  354. * @param bool $showTotal
  355. * @param bool $isFileSize
  356. */
  357. public static function printStats($title, $stats, $showTotal = true, $isFileSize = false)
  358. {
  359. $total = 0;
  360. $data = self::rescale($stats);
  361. echo '<table class="data_table" cellspacing="0" cellpadding="3">
  362. <tr><th colspan="'.($showTotal ? '4' : '3').'">'.$title.'</th></tr>';
  363. $i = 0;
  364. foreach ($stats as $subtitle => $number) {
  365. $total += $number;
  366. }
  367. foreach ($stats as $subtitle => $number) {
  368. if (!$isFileSize) {
  369. $number_label = number_format($number, 0, ',', '.');
  370. } else {
  371. $number_label = self::makeSizeString($number);
  372. }
  373. $percentage = ($total > 0 ? number_format(100 * $number / $total, 1, ',', '.') : '0');
  374. echo '<tr class="row_'.($i % 2 == 0 ? 'odd' : 'even').'">
  375. <td width="150">'.$subtitle.'</td>
  376. <td width="550">'.Display::bar_progress($percentage, false).'</td>
  377. <td align="right">'.$number_label.'</td>';
  378. if ($showTotal) {
  379. echo '<td align="right"> '.$percentage.'%</td>';
  380. }
  381. echo '</tr>';
  382. $i++;
  383. }
  384. if ($showTotal) {
  385. if (!$isFileSize) {
  386. $total_label = number_format($total, 0, ',', '.');
  387. } else {
  388. $total_label = self::makeSizeString($total);
  389. }
  390. echo '<tr><th colspan="4" align="right">'.get_lang('Total').': '.$total_label.'</td></tr>';
  391. }
  392. echo '</table>';
  393. }
  394. /**
  395. * Show some stats about the number of logins
  396. * @param string $type month, hour or day
  397. */
  398. public static function printLoginStats($type)
  399. {
  400. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
  401. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  402. $current_url_id = api_get_current_access_url_id();
  403. $table_url = null;
  404. $where_url = null;
  405. $now = api_get_utc_datetime();
  406. $where_url_last = ' WHERE login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
  407. if (api_is_multiple_url_enabled()) {
  408. $table_url = ", $access_url_rel_user_table";
  409. $where_url = " WHERE login_user_id=user_id AND access_url_id='".$current_url_id."'";
  410. $where_url_last = ' AND login_date > DATE_SUB("'.$now.'",INTERVAL 1 %s)';
  411. }
  412. $period = get_lang('PeriodMonth');
  413. $periodCollection = api_get_months_long();
  414. $sql = "SELECT DATE_FORMAT( login_date, '%Y-%m' ) AS stat_date , count( login_id ) AS number_of_logins
  415. FROM ".$table.$table_url.$where_url."
  416. GROUP BY stat_date
  417. ORDER BY login_date DESC";
  418. $sql_last_x = null;
  419. switch ($type) {
  420. case 'hour':
  421. $period = get_lang('PeriodHour');
  422. $sql = "SELECT DATE_FORMAT( login_date, '%H' ) AS stat_date , count( login_id ) AS number_of_logins
  423. FROM $table.$table_url $where_url
  424. GROUP BY stat_date
  425. ORDER BY stat_date ";
  426. $sql_last_x = "SELECT DATE_FORMAT( login_date, '%H' ) AS stat_date , count( login_id ) AS number_of_logins
  427. FROM ".$table.$table_url.$where_url.sprintf($where_url_last, 'DAY')."
  428. GROUP BY stat_date
  429. ORDER BY stat_date ";
  430. break;
  431. case 'day':
  432. $periodCollection = api_get_week_days_long();
  433. $period = get_lang('PeriodDay');
  434. $sql = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date , count( login_id ) AS number_of_logins
  435. FROM ".$table.$table_url.$where_url."
  436. GROUP BY stat_date
  437. ORDER BY DATE_FORMAT( login_date, '%w' ) ";
  438. $sql_last_x = "SELECT DATE_FORMAT( login_date, '%w' ) AS stat_date, count( login_id ) AS number_of_logins
  439. FROM ".$table.$table_url.$where_url.sprintf($where_url_last, 'WEEK')."
  440. GROUP BY stat_date
  441. ORDER BY DATE_FORMAT( login_date, '%w' ) ";
  442. break;
  443. }
  444. if ($sql_last_x) {
  445. $res_last_x = Database::query($sql_last_x);
  446. $result_last_x = array();
  447. while ($obj = Database::fetch_object($res_last_x)) {
  448. $stat_date = ($type === 'day') ? $periodCollection[$obj->stat_date] : $obj->stat_date;
  449. $result_last_x[$stat_date] = $obj->number_of_logins;
  450. }
  451. self::printStats(get_lang('LastLogins').' ('.$period.')', $result_last_x, true);
  452. flush(); //flush web request at this point to see something already while the full data set is loading
  453. echo '<br />';
  454. }
  455. $res = Database::query($sql);
  456. $result = array();
  457. while ($obj = Database::fetch_object($res)) {
  458. $stat_date = $obj->stat_date;
  459. switch ($type) {
  460. case 'month':
  461. $stat_date = explode('-', $stat_date);
  462. $stat_date[1] = $periodCollection[$stat_date[1] - 1];
  463. $stat_date = implode(' ', $stat_date);
  464. break;
  465. case 'day':
  466. $stat_date = $periodCollection[$stat_date];
  467. break;
  468. }
  469. $result[$stat_date] = $obj->number_of_logins;
  470. }
  471. self::printStats(get_lang('AllLogins').' ('.$period.')', $result, true);
  472. }
  473. /**
  474. * Print the number of recent logins
  475. * @param bool $distinct Whether to only give distinct users stats, or *all* logins
  476. * @return void
  477. */
  478. public static function printRecentLoginStats($distinct = false)
  479. {
  480. $totalLogin = array();
  481. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
  482. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  483. $current_url_id = api_get_current_access_url_id();
  484. if (api_is_multiple_url_enabled()) {
  485. $table_url = ", $access_url_rel_user_table";
  486. $where_url = " AND login_user_id=user_id AND access_url_id='".$current_url_id."'";
  487. } else {
  488. $table_url = '';
  489. $where_url = '';
  490. }
  491. $now = api_get_utc_datetime();
  492. $field = 'login_user_id';
  493. if ($distinct) {
  494. $field = 'DISTINCT(login_user_id)';
  495. }
  496. $sql[get_lang('ThisDay')] = "SELECT count($field) AS number FROM $table $table_url WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
  497. $sql[get_lang('Last7days')] = "SELECT count($field) AS number FROM $table $table_url WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
  498. $sql[get_lang('Last31days')] = "SELECT count($field) AS number FROM $table $table_url WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
  499. $sql[get_lang('Total')] = "SELECT count($field) AS number FROM $table $table_url WHERE 1=1 $where_url";
  500. foreach ($sql as $index => $query) {
  501. $res = Database::query($query);
  502. $obj = Database::fetch_object($res);
  503. $totalLogin[$index] = $obj->number;
  504. }
  505. if ($distinct) {
  506. self::printStats(get_lang('DistinctUsersLogins'), $totalLogin, false);
  507. } else {
  508. self::printStats(get_lang('Logins'), $totalLogin, false);
  509. }
  510. }
  511. /**
  512. * get the number of recent logins
  513. * @param bool $distinct Whether to only give distinct users stats, or *all* logins
  514. * @return array
  515. */
  516. public static function getRecentLoginStats($distinct = false)
  517. {
  518. $totalLogin = [];
  519. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
  520. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  521. $current_url_id = api_get_current_access_url_id();
  522. if (api_is_multiple_url_enabled()) {
  523. $table_url = ", $access_url_rel_user_table";
  524. $where_url = " AND login_user_id=user_id AND access_url_id='".$current_url_id."'";
  525. } else {
  526. $table_url = '';
  527. $where_url = '';
  528. }
  529. $now = api_get_utc_datetime();
  530. $field = 'login_user_id';
  531. if ($distinct) {
  532. $field = 'DISTINCT(login_user_id)';
  533. }
  534. $sql = "SELECT count($field) AS number, date(login_date) as login_date
  535. FROM $table $table_url
  536. WHERE DATE_ADD(login_date, INTERVAL 15 DAY) >= '$now' $where_url
  537. GROUP BY date(login_date)";
  538. $res = Database::query($sql);
  539. while ($row = Database::fetch_array($res, 'ASSOC')) {
  540. $totalLogin[$row['login_date']] = $row['number'];
  541. }
  542. return $totalLogin;
  543. }
  544. /**
  545. * Show some stats about the accesses to the different course tools
  546. */
  547. public static function printToolStats()
  548. {
  549. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
  550. $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
  551. $current_url_id = api_get_current_access_url_id();
  552. $tools = array(
  553. 'announcement',
  554. 'assignment',
  555. 'calendar_event',
  556. 'chat',
  557. 'course_description',
  558. 'document',
  559. 'dropbox',
  560. 'group',
  561. 'learnpath',
  562. 'link',
  563. 'quiz',
  564. 'student_publication',
  565. 'user',
  566. 'forum'
  567. );
  568. $tool_names = array();
  569. foreach ($tools as $tool) {
  570. $tool_names[$tool] = get_lang(ucfirst($tool), '');
  571. }
  572. if (api_is_multiple_url_enabled()) {
  573. $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
  574. FROM $table t , $access_url_rel_course_table a
  575. WHERE
  576. access_tool IN ('".implode("','", $tools)."') AND
  577. t.c_id = a.c_id AND
  578. access_url_id='".$current_url_id."'
  579. GROUP BY access_tool
  580. ";
  581. } else {
  582. $sql = "SELECT access_tool, count( access_id ) AS number_of_logins
  583. FROM $table
  584. WHERE access_tool IN ('".implode("','", $tools)."')
  585. GROUP BY access_tool ";
  586. }
  587. $res = Database::query($sql);
  588. $result = array();
  589. while ($obj = Database::fetch_object($res)) {
  590. $result[$tool_names[$obj->access_tool]] = $obj->number_of_logins;
  591. }
  592. self::printStats(get_lang('PlatformToolAccess'), $result, true);
  593. }
  594. /**
  595. * Show some stats about the number of courses per language
  596. */
  597. public static function printCourseByLanguageStats()
  598. {
  599. $table = Database::get_main_table(TABLE_MAIN_COURSE);
  600. $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
  601. $current_url_id = api_get_current_access_url_id();
  602. if (api_is_multiple_url_enabled()) {
  603. $sql = "SELECT course_language, count( c.code ) AS number_of_courses
  604. FROM $table as c, $access_url_rel_course_table as u
  605. WHERE u.c_id = c.id AND access_url_id='".$current_url_id."'
  606. GROUP BY course_language
  607. ORDER BY number_of_courses DESC";
  608. } else {
  609. $sql = "SELECT course_language, count( code ) AS number_of_courses
  610. FROM $table GROUP BY course_language
  611. ORDER BY number_of_courses DESC";
  612. }
  613. $res = Database::query($sql);
  614. $result = array();
  615. while ($obj = Database::fetch_object($res)) {
  616. $result[$obj->course_language] = $obj->number_of_courses;
  617. }
  618. self::printStats(get_lang('CountCourseByLanguage'), $result, true);
  619. }
  620. /**
  621. * Shows the number of users having their picture uploaded in Dokeos.
  622. */
  623. public static function printUserPicturesStats()
  624. {
  625. $user_table = Database::get_main_table(TABLE_MAIN_USER);
  626. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  627. $current_url_id = api_get_current_access_url_id();
  628. $url_condition = null;
  629. $url_condition2 = null;
  630. $table = null;
  631. if (api_is_multiple_url_enabled()) {
  632. $url_condition = ", $access_url_rel_user_table as url WHERE url.user_id=u.user_id AND access_url_id='".$current_url_id."'";
  633. $url_condition2 = " AND url.user_id=u.user_id AND access_url_id='".$current_url_id."'";
  634. $table = ", $access_url_rel_user_table as url ";
  635. }
  636. $sql = "SELECT COUNT(*) AS n FROM $user_table as u ".$url_condition;
  637. $res = Database::query($sql);
  638. $count1 = Database::fetch_object($res);
  639. $sql = "SELECT COUNT(*) AS n FROM $user_table as u $table ".
  640. "WHERE LENGTH(picture_uri) > 0 $url_condition2";
  641. $res = Database::query($sql);
  642. $count2 = Database::fetch_object($res);
  643. // #users without picture
  644. $result[get_lang('No')] = $count1->n - $count2->n;
  645. $result[get_lang('Yes')] = $count2->n; // #users with picture
  646. self::printStats(get_lang('CountUsers').' ('.get_lang('UserPicture').')', $result, true);
  647. }
  648. /**
  649. * Important activities
  650. */
  651. public static function printActivitiesStats()
  652. {
  653. echo '<h4>'.get_lang('ImportantActivities').'</h4>';
  654. // Create a search-box
  655. $form = new FormValidator(
  656. 'search_simple',
  657. 'get',
  658. api_get_path(WEB_CODE_PATH).'admin/statistics/index.php',
  659. '',
  660. 'width=200px',
  661. false
  662. );
  663. $renderer = & $form->defaultRenderer();
  664. $renderer->setCustomElementTemplate('<span>{element}</span> ');
  665. $form->addElement('hidden', 'report', 'activities');
  666. $form->addElement('hidden', 'activities_direction', 'DESC');
  667. $form->addElement('hidden', 'activities_column', '4');
  668. $form->addElement('text', 'keyword', get_lang('Keyword'));
  669. $form->addButtonSearch(get_lang('Search'), 'submit');
  670. echo '<div class="actions">';
  671. $form->display();
  672. echo '</div>';
  673. $table = new SortableTable(
  674. 'activities',
  675. array('Statistics', 'getNumberOfActivities'),
  676. array('Statistics', 'getActivitiesData'),
  677. 5,
  678. 50,
  679. 'DESC'
  680. );
  681. $parameters = array();
  682. $parameters['report'] = 'activities';
  683. if (isset($_GET['keyword'])) {
  684. $parameters['keyword'] = Security::remove_XSS($_GET['keyword']);
  685. }
  686. $table->set_additional_parameters($parameters);
  687. $table->set_header(0, get_lang('EventType'));
  688. $table->set_header(1, get_lang('DataType'));
  689. $table->set_header(2, get_lang('Value'));
  690. $table->set_header(3, get_lang('Course'));
  691. $table->set_header(4, get_lang('Session'));
  692. $table->set_header(5, get_lang('UserName'));
  693. $table->set_header(6, get_lang('IPAddress'));
  694. $table->set_header(7, get_lang('Date'));
  695. $table->display();
  696. }
  697. /**
  698. * Shows statistics about the time of last visit to each course.
  699. */
  700. public static function printCourseLastVisit()
  701. {
  702. $access_url_rel_course_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
  703. $current_url_id = api_get_current_access_url_id();
  704. $columns[0] = 't.c_id';
  705. $columns[1] = 'access_date';
  706. $sql_order[SORT_ASC] = 'ASC';
  707. $sql_order[SORT_DESC] = 'DESC';
  708. $per_page = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
  709. $page_nr = isset($_GET['page_nr']) ? intval($_GET['page_nr']) : 1;
  710. $column = isset($_GET['column']) ? intval($_GET['column']) : 0;
  711. $date_diff = isset($_GET['date_diff']) ? intval($_GET['date_diff']) : 60;
  712. $direction = isset($_GET['direction']) ? $_GET['direction'] : SORT_ASC;
  713. if (!in_array($direction, array(SORT_ASC, SORT_DESC))) {
  714. $direction = SORT_ASC;
  715. }
  716. $form = new FormValidator('courselastvisit', 'get');
  717. $form->addElement('hidden', 'report', 'courselastvisit');
  718. $form->addText('date_diff', get_lang('Days'), true);
  719. $form->addRule('date_diff', 'InvalidNumber', 'numeric');
  720. $form->addButtonSearch(get_lang('Search'), 'submit');
  721. if (!isset($_GET['date_diff'])) {
  722. $defaults['date_diff'] = 60;
  723. } else {
  724. $defaults['date_diff'] = Security::remove_XSS($_GET['date_diff']);
  725. }
  726. $form->setDefaults($defaults);
  727. $form->display();
  728. $values = $form->exportValues();
  729. $date_diff = $values['date_diff'];
  730. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
  731. if (api_is_multiple_url_enabled()) {
  732. $sql = "SELECT * FROM $table t , $access_url_rel_course_table a
  733. WHERE
  734. t.c_id = a.c_id AND
  735. access_url_id='".$current_url_id."'
  736. GROUP BY t.c_id
  737. HAVING t.c_id <> ''
  738. AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
  739. } else {
  740. $sql = "SELECT * FROM $table t
  741. GROUP BY t.c_id
  742. HAVING t.c_id <> ''
  743. AND DATEDIFF( '".api_get_utc_datetime()."' , access_date ) <= ".$date_diff;
  744. }
  745. $sql .= ' ORDER BY '.$columns[$column].' '.$sql_order[$direction];
  746. $from = ($page_nr - 1) * $per_page;
  747. $sql .= ' LIMIT '.$from.','.$per_page;
  748. echo '<p>'.get_lang('LastAccess').' &gt;= '.$date_diff.' '.get_lang('Days').'</p>';
  749. $res = Database::query($sql);
  750. if (Database::num_rows($res) > 0) {
  751. $courses = array();
  752. while ($obj = Database::fetch_object($res)) {
  753. $courseInfo = api_get_course_info_by_id($obj->c_id);
  754. $course = array();
  755. $course[] = '<a href="'.api_get_path(WEB_COURSE_PATH).$courseInfo['code'].'">'.$courseInfo['code'].' <a>';
  756. // Allow sort by date hiding the numerical date
  757. $course[] = '<span style="display:none;">'.$obj->access_date.'</span>'.api_convert_and_format_date($obj->access_date);
  758. $courses[] = $course;
  759. }
  760. $parameters['date_diff'] = $date_diff;
  761. $parameters['report'] = 'courselastvisit';
  762. $table_header[] = array(get_lang("CourseCode"), true);
  763. $table_header[] = array(get_lang("LastAccess"), true);
  764. Display:: display_sortable_table(
  765. $table_header,
  766. $courses,
  767. array('column' => $column, 'direction' => $direction),
  768. array(),
  769. $parameters
  770. );
  771. } else {
  772. echo get_lang('NoSearchResults');
  773. }
  774. }
  775. /**
  776. * Displays the statistics of the messages sent and received by each user in the social network
  777. * @param string $messageType Type of message: 'sent' or 'received'
  778. * @return array Message list
  779. */
  780. public static function getMessages($messageType)
  781. {
  782. $message_table = Database::get_main_table(TABLE_MESSAGE);
  783. $user_table = Database::get_main_table(TABLE_MAIN_USER);
  784. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  785. $current_url_id = api_get_current_access_url_id();
  786. switch ($messageType) {
  787. case 'sent':
  788. $field = 'user_sender_id';
  789. break;
  790. case 'received':
  791. $field = 'user_receiver_id';
  792. break;
  793. }
  794. if (api_is_multiple_url_enabled()) {
  795. $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message
  796. FROM $access_url_rel_user_table as url, $message_table m
  797. LEFT JOIN $user_table u ON m.$field = u.user_id
  798. WHERE url.user_id = m.$field AND access_url_id='".$current_url_id."'
  799. GROUP BY m.$field
  800. ORDER BY count_message DESC ";
  801. } else {
  802. $sql = "SELECT lastname, firstname, username, COUNT($field) AS count_message
  803. FROM $message_table m
  804. LEFT JOIN $user_table u ON m.$field = u.user_id
  805. GROUP BY m.$field ORDER BY count_message DESC ";
  806. }
  807. $res = Database::query($sql);
  808. $messages_sent = array();
  809. while ($messages = Database::fetch_array($res)) {
  810. if (empty($messages['username'])) {
  811. $messages['username'] = get_lang('Unknown');
  812. }
  813. $users = api_get_person_name(
  814. $messages['firstname'],
  815. $messages['lastname']
  816. ).'<br />('.$messages['username'].')';
  817. $messages_sent[$users] = $messages['count_message'];
  818. }
  819. return $messages_sent;
  820. }
  821. /**
  822. * Count the number of friends for social network users
  823. */
  824. public static function getFriends()
  825. {
  826. $user_friend_table = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
  827. $user_table = Database::get_main_table(TABLE_MAIN_USER);
  828. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  829. $current_url_id = api_get_current_access_url_id();
  830. if (api_is_multiple_url_enabled()) {
  831. $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend
  832. FROM $access_url_rel_user_table as url, $user_friend_table uf
  833. LEFT JOIN $user_table u
  834. ON (uf.user_id = u.user_id)
  835. WHERE
  836. uf.relation_type <> '".USER_RELATION_TYPE_RRHH."' AND
  837. uf.user_id = url.user_id AND
  838. access_url_id = '".$current_url_id."'
  839. GROUP BY uf.user_id
  840. ORDER BY count_friend DESC ";
  841. } else {
  842. $sql = "SELECT lastname, firstname, username, COUNT(friend_user_id) AS count_friend
  843. FROM $user_friend_table uf
  844. LEFT JOIN $user_table u
  845. ON (uf.user_id = u.user_id)
  846. WHERE uf.relation_type <> '".USER_RELATION_TYPE_RRHH."'
  847. GROUP BY uf.user_id
  848. ORDER BY count_friend DESC ";
  849. }
  850. $res = Database::query($sql);
  851. $list_friends = array();
  852. while ($friends = Database::fetch_array($res)) {
  853. $users = api_get_person_name($friends['firstname'], $friends['lastname']).'<br />('.$friends['username'].')';
  854. $list_friends[$users] = $friends['count_friend'];
  855. }
  856. return $list_friends;
  857. }
  858. /**
  859. * Print the number of users that didn't login for a certain period of time
  860. */
  861. public static function printUsersNotLoggedInStats()
  862. {
  863. $totalLogin = array();
  864. $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
  865. $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
  866. $current_url_id = api_get_current_access_url_id();
  867. $total = self::countUsers();
  868. if (api_is_multiple_url_enabled()) {
  869. $table_url = ", $access_url_rel_user_table";
  870. $where_url = " AND login_user_id=user_id AND access_url_id='".$current_url_id."'";
  871. } else {
  872. $table_url = '';
  873. $where_url = '';
  874. }
  875. $now = api_get_utc_datetime();
  876. $sql[get_lang('ThisDay')] =
  877. "SELECT count(distinct(login_user_id)) AS number ".
  878. " FROM $table $table_url ".
  879. " WHERE DATE_ADD(login_date, INTERVAL 1 DAY) >= '$now' $where_url";
  880. $sql[get_lang('Last7days')] =
  881. "SELECT count(distinct(login_user_id)) AS number ".
  882. " FROM $table $table_url ".
  883. " WHERE DATE_ADD(login_date, INTERVAL 7 DAY) >= '$now' $where_url";
  884. $sql[get_lang('Last31days')] =
  885. "SELECT count(distinct(login_user_id)) AS number ".
  886. " FROM $table $table_url ".
  887. " WHERE DATE_ADD(login_date, INTERVAL 31 DAY) >= '$now' $where_url";
  888. $sql[sprintf(get_lang('LastXMonths'), 6)] =
  889. "SELECT count(distinct(login_user_id)) AS number ".
  890. " FROM $table $table_url ".
  891. " WHERE DATE_ADD(login_date, INTERVAL 6 MONTH) >= '$now' $where_url";
  892. $sql[get_lang('NeverConnected')] =
  893. "SELECT count(distinct(login_user_id)) AS number ".
  894. " FROM $table $table_url WHERE 1=1 $where_url";
  895. foreach ($sql as $index => $query) {
  896. $res = Database::query($query);
  897. $obj = Database::fetch_object($res);
  898. $r = $total - $obj->number;
  899. $totalLogin[$index] = $r < 0 ? 0 : $r;
  900. }
  901. self::printStats(
  902. get_lang('StatsUsersDidNotLoginInLastPeriods'),
  903. $totalLogin,
  904. false
  905. );
  906. }
  907. }