From 3e01b983881b81ddc1be02cefe4a7a3cefb70d77 Mon Sep 17 00:00:00 2001 From: Dmitriy Simushev Date: Fri, 16 May 2014 08:28:25 +0000 Subject: [PATCH] Add "statistics" controller --- .../Mibew/Controller/StatisticsController.php | 190 +++++++++++++++++ src/mibew/libs/routing.yml | 9 + src/mibew/libs/statistics.php | 149 ++++++++++--- src/mibew/operator/statistics.php | 200 ------------------ .../server_side/_menu.handlebars | 2 +- .../server_side/index.handlebars | 2 +- .../server_side/statistics.handlebars | 2 +- 7 files changed, 324 insertions(+), 230 deletions(-) create mode 100644 src/mibew/libs/classes/Mibew/Controller/StatisticsController.php delete mode 100644 src/mibew/operator/statistics.php diff --git a/src/mibew/libs/classes/Mibew/Controller/StatisticsController.php b/src/mibew/libs/classes/Mibew/Controller/StatisticsController.php new file mode 100644 index 00000000..8ea927d8 --- /dev/null +++ b/src/mibew/libs/classes/Mibew/Controller/StatisticsController.php @@ -0,0 +1,190 @@ +attributes->get('_operator'); + $statistics_type = $request->attributes->get('type'); + setlocale(LC_TIME, getstring("time.locale")); + + $page = array(); + $page['operator'] = get_operator_name($operator); + $page['availableDays'] = range(1, 31); + $page['availableMonth'] = get_month_selection( + time() - 400 * 24 * 60 * 60, + time() + 50 * 24 * 60 * 60 + ); + $page['showresults'] = false; + $page['type'] = $statistics_type; + $page['showbydate'] = ($statistics_type == self::TYPE_BY_DATE); + $page['showbyagent'] = ($statistics_type == self::TYPE_BY_OPERATOR); + $page['showbypage'] = ($statistics_type == self::TYPE_BY_PAGE); + + $page['pageDescription'] = getlocal2( + 'statistics.description.full', + array( + date_to_text(Settings::get('_last_cron_run')), + cron_get_uri(Settings::get('cron_key')), + ) + ); + + $page['show_invitations_info'] = (bool) Settings::get('enabletracking'); + $page['errors'] = array(); + + // Get and validate time interval + $time_interval = $this->extractTimeInterval($request); + $start = $time_interval['start']; + $end = $time_interval['end']; + if ($start > $end) { + $page['errors'][] = getlocal('statistics.wrong.dates'); + } + + $page = array_merge( + $page, + set_form_date($start, 'start'), + set_form_date($end - 24 * 60 * 60, 'end') + ); + + // Get statistics info + if ($statistics_type == self::TYPE_BY_DATE) { + $statistics = get_by_date_statistics($start, $end); + $page['reportByDate'] = $statistics['records']; + $page['reportByDateTotal'] = $statistics['total']; + } elseif ($statistics_type == self::TYPE_BY_OPERATOR) { + $page['reportByAgent'] = get_by_operator_statistics($start, $end); + } elseif ($statistics_type == self::TYPE_BY_PAGE) { + $page['reportByPage'] = get_by_page_statistics($start, $end); + } + + $page['showresults'] = count($page['errors']) == 0; + $page['title'] = getlocal("statistics.title"); + $page['menuid'] = "statistics"; + $page = array_merge($page, prepare_menu($operator)); + $page['tabs'] = $this->buildTabs($request); + + return $this->render('statistics', $page); + } + + /** + * Builds list of the statistics tabs. + * + * @param Request $request Current request. + * @return array Tabs list. The keys of the array are tabs titles and the + * values are tabs URLs. + */ + protected function buildTabs(Request $request) + { + $tabs = array(); + $args = $request->query->all(); + $type = $request->attributes->get('type'); + + $tabs[getlocal('report.bydate.title')] = $type != self::TYPE_BY_DATE + ? $this->generateUrl('statistics', ($args + array('type' => self::TYPE_BY_DATE))) + : ''; + + $tabs[getlocal('report.byoperator.title')] = $type != self::TYPE_BY_OPERATOR + ? $this->generateUrl('statistics', ($args + array('type' => self::TYPE_BY_OPERATOR))) + : ''; + + if (Settings::get('enabletracking')) { + $tabs[getlocal('report.bypage.title')] = $type != self::TYPE_BY_PAGE + ? $this->generateUrl('statistics', ($args + array('type' => self::TYPE_BY_PAGE))) + : ''; + } + + return $tabs; + } + + /** + * Extracts start and end timestamps from the interval related with the + * request. + * + * @param Request $request Incoming request + * @return array Associative array with the following keys: + * - "start": int, timestamp for beginning of the interval. + * - "end": int, timestamp for ending of the interval. + */ + protected function extractTimeInterval(Request $request) + { + if ($request->query->has('startday')) { + // The request contains info about interval. + $start_day = $request->query->get('startday'); + $start_month = $request->query->get('startmonth'); + $end_day = $request->query->get('endday'); + $end_month = $request->query->get('endmonth'); + + // Check if all necessary info is specified. + $bad_request = !preg_match("/^\d+$/", $start_day) + || !preg_match("/^\d{2}.\d{2}$/", $start_month) + || !preg_match("/^\d+$/", $end_day) + || !preg_match("/^\d{2}.\d{2}$/", $end_month); + if ($bad_request) { + throw new BadRequestException(); + } + + return array( + 'start' => get_form_date($start_day, $start_month), + 'end' => get_form_date($end_day, $end_month) + 24 * 60 * 60, + ); + } + + // The request does not contain info about interval. Use defaults. + $curr = getdate(time()); + if ($curr['mday'] < 7) { + // Use previous month if it is the first week of the month + if ($curr['mon'] == 1) { + $month = 12; + $year = $curr['year'] - 1; + } else { + $month = $curr['mon'] - 1; + $year = $curr['year']; + } + + return array( + 'start' => mktime(0, 0, 0, $month, 1, $year), + 'end' => mktime(0, 0, 0, $month, date('t', $start), $year) + 24 * 60 * 60, + ); + } + + // Use the current month + return array( + 'start' => mktime(0, 0, 0, $curr['mon'], 1, $curr['year']), + 'end' => time() + 24 * 60 * 60, + ); + } +} diff --git a/src/mibew/libs/routing.yml b/src/mibew/libs/routing.yml index ef0049f7..aa597080 100644 --- a/src/mibew/libs/routing.yml +++ b/src/mibew/libs/routing.yml @@ -44,6 +44,15 @@ password_recovery_reset: defaults: _controller: Mibew\Controller\PasswordRecoveryController::resetAction +statistics: + path: /operator/statistics/{type} + defaults: + type: "by-date" + _controller: Mibew\Controller\StatisticsController::indexAction + _access_check: Mibew\AccessControl\Check\LoggedInCheck + requirements: + type: by-date|by-operator|by-page + updates: path: /operator/updates defaults: diff --git a/src/mibew/libs/statistics.php b/src/mibew/libs/statistics.php index e67fc989..f511e1a6 100644 --- a/src/mibew/libs/statistics.php +++ b/src/mibew/libs/statistics.php @@ -20,44 +20,139 @@ use Mibew\Database; use Mibew\Settings; use Mibew\Thread; -function get_statistics_query($type) +/** + * Loads statistics about system usage aggregated by dates for the time between + * $start and $end timestamps. + * + * @param int $start Timestamp for beginning of the interval of interest. + * @param int $end Timestamp for ending of the interval of interest. + * @return array Associative array with the following keys: + * - "records": array of associative arrays, set of statistics records + * - "total": associative array, aggregated statistics for the interval of + * interest. + */ +function get_by_date_statistics($start, $end) { - $query = $_SERVER['QUERY_STRING']; - if (!empty($query)) { - $query = '?' . $query; - $query = preg_replace("/\?type=\w+\&/", "?", $query); - $query = preg_replace("/(\?|\&)type=\w+/", "", $query); - } - $query .= strstr($query, "?") ? "&type=$type" : "?type=$type"; + $db = Database::getInstance(); - return $query; + // Get statistics records aggregated by date + $records = $db->query( + ("SELECT DATE(FROM_UNIXTIME(date)) AS date, " + . "threads, " + . "missedthreads, " + . "sentinvitations, " + . "acceptedinvitations, " + . "rejectedinvitations, " + . "ignoredinvitations, " + . "operatormessages AS agents, " + . "usermessages AS users, " + . "averagewaitingtime AS avgwaitingtime, " + . "averagechattime AS avgchattime " + . "FROM {chatthreadstatistics} s " + . "WHERE s.date >= :start " + . "AND s.date < :end " + . "GROUP BY DATE(FROM_UNIXTIME(date)) " + . "ORDER BY s.date DESC"), + array( + ':start' => $start, + ':end' => $end, + ), + array('return_rows' => Database::RETURN_ALL_ROWS) + ); + + // Get statistics aggregated for all accessed interval + $total = $db->query( + ("SELECT DATE(FROM_UNIXTIME(date)) AS date, " + . "SUM(threads) AS threads, " + . "SUM(missedthreads) AS missedthreads, " + . "SUM(sentinvitations) AS sentinvitations, " + . "SUM(acceptedinvitations) AS acceptedinvitations, " + . "SUM(rejectedinvitations) AS rejectedinvitations, " + . "SUM(ignoredinvitations) AS ignoredinvitations, " + . "SUM(operatormessages) AS agents, " + . "SUM(usermessages) AS users, " + . "ROUND(SUM(averagewaitingtime * s.threads) / SUM(s.threads),1) AS avgwaitingtime, " + . "ROUND(SUM(averagechattime * s.threads) / SUM(s.threads),1) AS avgchattime " + . "FROM {chatthreadstatistics} s " + . "WHERE s.date >= :start " + . "AND s.date < :end"), + array( + ':start' => $start, + ':end' => $end, + ), + array('return_rows' => Database::RETURN_ONE_ROW) + ); + + return array( + 'records' => $records, + 'total' => $total, + ); } /** - * Builds list of the statistics tabs. The keys of the resulting array are - * tabs titles and the values are tabs URLs. + * Loads statistics about operators for the time between $start and $end + * timestamps. * - * @param int $active Number of the active tab. The count starts from 0. - * @return array Tabs list + * @param int $start Timestamp for beginning of the interval of interest. + * @param int $end Timestamp for ending of the interval of interest. + * @return array Set of statistics records */ -function setup_statistics_tabs($active) +function get_by_operator_statistics($start, $end) { - $tabs = array( - getlocal("report.bydate.title") => ($active != 0 - ? (MIBEW_WEB_ROOT . "/operator/statistics.php" . get_statistics_query('bydate')) - : ""), - getlocal("report.byoperator.title") => ($active != 1 - ? (MIBEW_WEB_ROOT . "/operator/statistics.php" . get_statistics_query('byagent')) - : "") + $db = Database::getInstance(); + $result = $db->query( + ("SELECT o.vclocalename AS name, " + . "SUM(s.threads) AS threads, " + . "SUM(s.messages) AS msgs, " + . "ROUND( " + . "SUM(s.averagelength * s.messages) / SUM(s.messages), " + . "1) AS avglen, " + . "SUM(sentinvitations) AS sentinvitations, " + . "SUM(acceptedinvitations) AS acceptedinvitations, " + . "SUM(rejectedinvitations) AS rejectedinvitations, " + . "SUM(ignoredinvitations) AS ignoredinvitations " + . "FROM {chatoperatorstatistics} s, {chatoperator} o " + . "WHERE s.operatorid = o.operatorid " + . "AND s.date >= :start " + . "AND s.date < :end " + . "GROUP BY s.operatorid"), + array( + ':start' => $start, + ':end' => $end, + ), + array('return_rows' => Database::RETURN_ALL_ROWS) ); - if (Settings::get('enabletracking')) { - $tabs[getlocal("report.bypage.title")] = ($active != 2 - ? (MIBEW_WEB_ROOT . "/operator/statistics.php" . get_statistics_query('bypage')) - : ""); - } + return $result; +} - return $tabs; +/** + * Loads statistics about pages for the time between $start and $end timestamps. + * + * @param int $start Timestamp for beginning of the interval of interest. + * @param int $end Timestamp for ending of the interval of interest. + * @return array Set of statistics records + */ +function get_by_page_statistics($start, $end) +{ + $db = Database::getInstance(); + $result = $db->query( + ("SELECT SUM(visits) as visittimes, " + . "address, " + . "SUM(chats) as chattimes, " + . "SUM(sentinvitations) AS sentinvitations, " + . "SUM(acceptedinvitations) AS acceptedinvitations, " + . "SUM(rejectedinvitations) AS rejectedinvitations, " + . "SUM(ignoredinvitations) AS ignoredinvitations " + . "FROM {visitedpagestatistics} " + . "WHERE date >= :start " + . "AND date < :end " + . "GROUP BY address"), + array(':start' => $start, ':end' => $end), + array('return_rows' => Database::RETURN_ALL_ROWS) + ); + + return $result; } /** diff --git a/src/mibew/operator/statistics.php b/src/mibew/operator/statistics.php deleted file mode 100644 index 599ea840..00000000 --- a/src/mibew/operator/statistics.php +++ /dev/null @@ -1,200 +0,0 @@ - $end) { - $page['errors'][] = getlocal("statistics.wrong.dates"); -} - -$active_tab = 0; -$db = Database::getInstance(); -if ($statistics_type == 'bydate') { - $page['reportByDate'] = $db->query( - ("SELECT DATE(FROM_UNIXTIME(date)) AS date, " - . "threads, " - . "missedthreads, " - . "sentinvitations, " - . "acceptedinvitations, " - . "rejectedinvitations, " - . "ignoredinvitations, " - . "operatormessages AS agents, " - . "usermessages AS users, " - . "averagewaitingtime AS avgwaitingtime, " - . "averagechattime AS avgchattime " - . "FROM {chatthreadstatistics} s " - . "WHERE s.date >= :start " - . "AND s.date < :end " - . "GROUP BY DATE(FROM_UNIXTIME(date)) " - . "ORDER BY s.date DESC"), - array( - ':start' => $start, - ':end' => $end, - ), - array('return_rows' => Database::RETURN_ALL_ROWS) - ); - - $page['reportByDateTotal'] = $db->query( - ("SELECT DATE(FROM_UNIXTIME(date)) AS date, " - . "SUM(threads) AS threads, " - . "SUM(missedthreads) AS missedthreads, " - . "SUM(sentinvitations) AS sentinvitations, " - . "SUM(acceptedinvitations) AS acceptedinvitations, " - . "SUM(rejectedinvitations) AS rejectedinvitations, " - . "SUM(ignoredinvitations) AS ignoredinvitations, " - . "SUM(operatormessages) AS agents, " - . "SUM(usermessages) AS users, " - . "ROUND(SUM(averagewaitingtime * s.threads) / SUM(s.threads),1) AS avgwaitingtime, " - . "ROUND(SUM(averagechattime * s.threads) / SUM(s.threads),1) AS avgchattime " - . "FROM {chatthreadstatistics} s " - . "WHERE s.date >= :start " - . "AND s.date < :end"), - array( - ':start' => $start, - ':end' => $end, - ), - array('return_rows' => Database::RETURN_ONE_ROW) - ); - - $active_tab = 0; -} elseif ($statistics_type == 'byagent') { - $page['reportByAgent'] = $db->query( - ("SELECT o.vclocalename AS name, " - . "SUM(s.threads) AS threads, " - . "SUM(s.messages) AS msgs, " - . "ROUND( " - . "SUM(s.averagelength * s.messages) / SUM(s.messages), " - . "1) AS avglen, " - . "SUM(sentinvitations) AS sentinvitations, " - . "SUM(acceptedinvitations) AS acceptedinvitations, " - . "SUM(rejectedinvitations) AS rejectedinvitations, " - . "SUM(ignoredinvitations) AS ignoredinvitations " - . "FROM {chatoperatorstatistics} s, {chatoperator} o " - . "WHERE s.operatorid = o.operatorid " - . "AND s.date >= :start " - . "AND s.date < :end " - . "GROUP BY s.operatorid"), - array( - ':start' => $start, - ':end' => $end, - ), - array('return_rows' => Database::RETURN_ALL_ROWS) - ); - - // We need to pass operator name through "to_page" function because we - // cannot do it in a template. - // TODO: Remove this block when "to_page" function will be removed. - foreach ($page['reportByAgent'] as &$row) { - $row['name'] = $row['name']; - } - unset($row); - - $active_tab = 1; -} elseif ($statistics_type == 'bypage') { - $page['reportByPage'] = $db->query( - ("SELECT SUM(visits) as visittimes, " - . "address, " - . "SUM(chats) as chattimes, " - . "SUM(sentinvitations) AS sentinvitations, " - . "SUM(acceptedinvitations) AS acceptedinvitations, " - . "SUM(rejectedinvitations) AS rejectedinvitations, " - . "SUM(ignoredinvitations) AS ignoredinvitations " - . "FROM {visitedpagestatistics} " - . "WHERE date >= :start " - . "AND date < :end " - . "GROUP BY address"), - array(':start' => $start, ':end' => $end), - array('return_rows' => Database::RETURN_ALL_ROWS) - ); - $active_tab = 2; -} -$page['showresults'] = count($page['errors']) == 0; - -$page['title'] = getlocal("statistics.title"); -$page['menuid'] = "statistics"; - -$page = array_merge($page, prepare_menu($operator)); - -$page['tabs'] = setup_statistics_tabs($active_tab); - -$page_style = new PageStyle(PageStyle::getCurrentStyle()); -$page_style->render('statistics', $page); diff --git a/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars index 02298e4d..31471b2f 100644 --- a/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars +++ b/src/mibew/styles/pages/default/templates_src/server_side/_menu.handlebars @@ -16,7 +16,7 @@ {{l10n "topMenu.users"}} ({{l10n "topMenu.users.nomenu"}}) {{l10n "page_analysis.search.title"}} {{#if showstat}} - {{l10n "statistics.title"}} + {{l10n "statistics.title"}} {{/if}} {{#if showban}} {{l10n "menu.blocked"}} diff --git a/src/mibew/styles/pages/default/templates_src/server_side/index.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/index.handlebars index a27ce5c6..53c744b5 100644 --- a/src/mibew/styles/pages/default/templates_src/server_side/index.handlebars +++ b/src/mibew/styles/pages/default/templates_src/server_side/index.handlebars @@ -50,7 +50,7 @@
- + {{l10n "statistics.title"}} {{l10n "statistics.description"}} diff --git a/src/mibew/styles/pages/default/templates_src/server_side/statistics.handlebars b/src/mibew/styles/pages/default/templates_src/server_side/statistics.handlebars index da836d8a..1ffaa3b4 100644 --- a/src/mibew/styles/pages/default/templates_src/server_side/statistics.handlebars +++ b/src/mibew/styles/pages/default/templates_src/server_side/statistics.handlebars @@ -9,7 +9,7 @@ {{> _errors}} -
+ {{> _tabs}}