From 6ad182807928ef9ebd325db1e3ca81689a157526 Mon Sep 17 00:00:00 2001 From: Dmitriy Simushev Date: Mon, 6 May 2013 14:12:19 +0000 Subject: [PATCH] Calculate threads and operator statistics by cron --- src/messenger/webim/cron.php | 1 + src/messenger/webim/install/dbinfo.php | 24 ++++ src/messenger/webim/install/dbperform.php | 3 + src/messenger/webim/libs/cron.php | 117 ++++++++++++++++++++ src/messenger/webim/operator/statistics.php | 55 ++++++--- src/messenger/webim/view/statistics.php | 4 + 6 files changed, 187 insertions(+), 17 deletions(-) diff --git a/src/messenger/webim/cron.php b/src/messenger/webim/cron.php index 2715a157..af5dbdf9 100644 --- a/src/messenger/webim/cron.php +++ b/src/messenger/webim/cron.php @@ -31,6 +31,7 @@ set_time_limit(0); // Run cron jobs of the core cron_index_messages(); +cron_calculate_statistics(); // Trigger cron event $dispatcher = EventDispatcher::getInstance(); diff --git a/src/messenger/webim/install/dbinfo.php b/src/messenger/webim/install/dbinfo.php index 0df07d1d..57771e8a 100644 --- a/src/messenger/webim/install/dbinfo.php +++ b/src/messenger/webim/install/dbinfo.php @@ -57,6 +57,16 @@ $dbtables = array( "groupid" => "int references ${mysqlprefix}chatgroup(groupid)", ), + "${mysqlprefix}chatthreadstatistics" => array( + "statid" => "int NOT NULL auto_increment PRIMARY KEY", + "date" => "int NOT NULL DEFAULT 0", + "threads" => "int NOT NULL DEFAULT 0", + "operatormessages" => "int NOT NULL DEFAULT 0", + "usermessages" => "int NOT NULL DEFAULT 0", + "averagewaitingtime" => "FLOAT(10, 1) NOT NULL DEFAULT 0", + "averagechattime" => "FLOAT(10, 1) NOT NULL DEFAULT 0" + ), + "${mysqlprefix}requestbuffer" => array( "requestid" => "int NOT NULL auto_increment PRIMARY KEY", // Use MD5 hashes as keys @@ -114,6 +124,15 @@ $dbtables = array( "vcrestoretoken" => "varchar(64)", ), + "${mysqlprefix}chatoperatorstatistics" => array( + "statid" => "int NOT NULL auto_increment PRIMARY KEY", + "date" => "int NOT NULL DEFAULT 0", + "operatorid" => "int NOT NULL", + "threads" => "int NOT NULL DEFAULT 0", + "messages" => "int NOT NULL DEFAULT 0", + "averagelength" => "FLOAT(10, 1) NOT NULL DEFAULT 0" + ), + "${mysqlprefix}chatrevision" => array( "id" => "INT NOT NULL" ), @@ -180,6 +199,9 @@ $dbtables_indexes = array( "${mysqlprefix}chatgroup" => array( "parent" => "parent" ), + "${mysqlprefix}chatoperatorstatistics" => array( + "operatorid" => "operatorid" + ), "${mysqlprefix}chatgroupoperator" => array( "groupid" => "groupid", "operatorid" => "operatorid" @@ -208,10 +230,12 @@ $memtables = array(); $dbtables_can_update = array( "${mysqlprefix}chatthread" => array("agentId", "userTyping", "agentTyping", "messageCount", "nextagent", "shownmessageid", "userid", "userAgent", "groupid", "dtmchatstarted"), + "${mysqlprefix}chatthreadstatistics" => array(), "${mysqlprefix}requestbuffer" => array("requestid", "requestkey", "request"), "${mysqlprefix}chatmessage" => array("agentId"), "${mysqlprefix}indexedchatmessage" => array(), "${mysqlprefix}chatoperator" => array("vcavatar", "vcjabbername", "iperm", "istatus", "idisabled", "vcemail", "dtmrestore", "vcrestoretoken"), + "${mysqlprefix}chatoperatorstatistics" => array(), "${mysqlprefix}chatban" => array(), "${mysqlprefix}chatgroup" => array("vcemail", "iweight", "parent", "vctitle", "vcchattitle", "vclogo", "vchosturl"), "${mysqlprefix}chatgroupoperator" => array(), diff --git a/src/messenger/webim/install/dbperform.php b/src/messenger/webim/install/dbperform.php index 8de2431a..5e1abe45 100644 --- a/src/messenger/webim/install/dbperform.php +++ b/src/messenger/webim/install/dbperform.php @@ -237,6 +237,9 @@ if ($act == "silentcreateall") { runsql("ALTER TABLE ${mysqlprefix}visitedpage ADD INDEX (visitorid)", $link); } + if (in_array("${mysqlprefix}chatoperatorstatistics.operatorid", $absent_indexes)) { + runsql("ALTER TABLE ${mysqlprefix}chatoperatorstatistics ADD INDEX (operatorid)", $link); + } } } diff --git a/src/messenger/webim/libs/cron.php b/src/messenger/webim/libs/cron.php index b886c827..6f206fa9 100644 --- a/src/messenger/webim/libs/cron.php +++ b/src/messenger/webim/libs/cron.php @@ -202,6 +202,123 @@ function cron_index_messages() { $db->query('COMMIT'); } +/** + * Calculate aggregated 'by thread' and 'by operator' statistics + */ +function cron_calculate_statistics() { + // Prepare database + $db = Database::getInstance(); + $db->throwExeptions(true); + + try { + // Start transaction + $db->query('START TRANSACTION'); + + // Build 'by thread' statistics + // Get last record date + $result = $db->query( + "SELECT MAX(date) as start FROM {chatthreadstatistics}", + array(), + array('return_rows' => Database::RETURN_ONE_ROW) + ); + + $start = empty($result['start']) ? 0 : $result['start']; + + // Reset statistics for the last day, because cron can be ran many + // times in a day. + $result = $db->query( + "DELETE FROM {chatthreadstatistics} WHERE date = :start", + array(':start' => $start) + ); + + // Calculate 'by thread' statistics + $db->query( + "INSERT INTO {chatthreadstatistics} ( " . + "date, threads, operatormessages, usermessages, " . + "averagewaitingtime, averagechattime " . + ") SELECT (FLOOR(t.dtmcreated / (24*60*60)) * 24*60*60) AS date, " . + "COUNT(distinct t.threadid) AS threads, " . + "SUM(m.ikind = :kind_agent) AS operators, " . + "SUM(m.ikind = :kind_user) AS users, " . + "ROUND(AVG(t.dtmchatstarted-t.dtmcreated),1) as avgwaitingtime, " . + // Prevent negative values of avgchattime field. + // If avgchattime < 0 it becomes to zero. + // For random value 'a' result of expression ((abs(a) + a) / 2) + // equals to 'a' if 'a' more than zero + // and equals to zero otherwise + "ROUND(AVG( " . + "ABS(tmp.lastmsgtime-t.dtmchatstarted) + " . + "(tmp.lastmsgtime-t.dtmchatstarted) " . + ")/2,1) as avgchattime " . + "FROM {indexedchatmessage} m, " . + "{chatthread} t, " . + "(SELECT i.threadid, MAX(i.dtmcreated) AS lastmsgtime " . + "FROM {indexedchatmessage} i " . + "WHERE (ikind = :kind_user OR ikind = :kind_agent) " . + "GROUP BY i.threadid) tmp " . + "WHERE m.threadid = t.threadid " . + "AND tmp.threadid = t.threadid " . + "AND t.dtmchatstarted <> 0 " . + "AND m.dtmcreated > :start " . + "GROUP BY date " . + "ORDER BY date", + array( + ':kind_agent' => Thread::KIND_AGENT, + ':kind_user' => Thread::KIND_USER, + ':start' => $start + ) + ); + + // Build 'by operator' statistics + // Get last record date + $result = $db->query( + "SELECT MAX(date) as start FROM {chatoperatorstatistics}", + array(), + array('return_rows' => Database::RETURN_ONE_ROW) + ); + + $start = empty($result['start']) ? 0 : $result['start']; + + // Reset statistics for the last day, because cron can be ran many + // times in a day. + $result = $db->query( + "DELETE FROM {chatoperatorstatistics} WHERE date = :start", + array(':start' => $start) + ); + + // Caclculate 'by operator' statistics + $db->query( + "INSERT INTO {chatoperatorstatistics} ( " . + "date, operatorid, threads, messages, averagelength" . + ") SELECT (FLOOR(m.dtmcreated / (24*60*60)) * 24*60*60) AS date, " . + "o.operatorid AS opid, " . + "COUNT(distinct m.threadid) AS threads, " . + "SUM(m.ikind = :kind_agent) AS msgs, " . + "AVG(CHAR_LENGTH(m.tmessage)) AS avglen " . + "FROM {indexedchatmessage} m, {chatoperator} o " . + "WHERE m.agentId = o.operatorid " . + "AND m.dtmcreated > :start " . + "GROUP BY date " . + "ORDER BY date", + array( + ':kind_agent' => Thread::KIND_AGENT, + ':start' => $start + ) + ); + } catch(Exception $e) { + // Something went wrong: warn and rollback transaction. + trigger_error( + 'Statistics calculating faild: ' . $e->getMessage(), + E_USER_WARNING + ); + $db->query('ROLLBACK'); + return; + } + + // Commit transaction + $db->query('COMMIT'); +} + /** * Generates cron URI * diff --git a/src/messenger/webim/operator/statistics.php b/src/messenger/webim/operator/statistics.php index 0f6b41e6..07c1ee52 100644 --- a/src/messenger/webim/operator/statistics.php +++ b/src/messenger/webim/operator/statistics.php @@ -19,6 +19,7 @@ require_once('../libs/init.php'); require_once('../libs/chat.php'); require_once('../libs/operator.php'); require_once('../libs/statistics.php'); +require_once('../libs/cron.php'); $operator = check_login(); force_password($operator); @@ -35,6 +36,10 @@ $page['type'] = $statisticstype; $page['showbydate'] = ($statisticstype == 'bydate'); $page['showbyagent'] = ($statisticstype == 'byagent'); $page['showbypage'] = ($statisticstype == 'bypage'); + +$page['noresults'] = false; +$page['cron_path'] = cron_get_uri(Settings::get('cron_key')); + $errors = array(); if (isset($_GET['startday'])) { @@ -74,45 +79,61 @@ $activetab = 0; $db = Database::getInstance(); if ($statisticstype == 'bydate') { $page['reportByDate'] = $db->query( - "select DATE(FROM_UNIXTIME(t.dtmcreated)) as date, COUNT(distinct t.threadid) as threads, SUM(m.ikind = :kind_agent) as agents, SUM(m.ikind = :kind_user) as users, ROUND(AVG(t.dtmchatstarted-t.dtmcreated),1) as avgwaitingtime, ROUND(AVG(tmp.lastmsgtime - t.dtmchatstarted),1) as avgchattime " . - "from {indexedchatmessage} m, {chatthread} t, (SELECT i.threadid, MAX(i.dtmcreated) AS lastmsgtime FROM {indexedchatmessage} i WHERE (ikind = :kind_user OR ikind = :kind_agent) GROUP BY i.threadid) tmp " . - "where m.threadid = t.threadid AND tmp.threadid = t.threadid AND t.dtmchatstarted <> 0 AND m.dtmcreated >= :start AND m.dtmcreated < :end group by DATE(FROM_UNIXTIME(m.dtmcreated)) order by m.dtmcreated desc", + "SELECT DATE(FROM_UNIXTIME(date)) AS date, " . + "threads, " . + "operatormessages AS agents, " . + "usermessages AS users, " . + "averagewaitingtime AS avgwaitingtime, " . + "averagechattime AS avgchattime " . + "FROM {chatthreadstatistics} s " . + "WHERE s.date >= :start " . + "AND s.date < :end " . + "ORDER BY s.date DESC", array( - ':kind_agent' => Thread::KIND_AGENT, - ':kind_user' => Thread::KIND_USER, ':start' => $start, ':end' => $end ), array('return_rows' => Database::RETURN_ALL_ROWS) ); - + + $page['noresults'] = empty($page['reportByDate']); + $page['reportByDateTotal'] = $db->query( - "select DATE(FROM_UNIXTIME(t.dtmcreated)) as date, COUNT(distinct t.threadid) as threads, SUM(m.ikind = :kind_agent) as agents, SUM(m.ikind = :kind_user) as users, ROUND(AVG(t.dtmchatstarted-t.dtmcreated),1) as avgwaitingtime, ROUND(AVG(tmp.lastmsgtime - t.dtmchatstarted),1) as avgchattime " . - "from {indexedchatmessage} m, {chatthread} t, (SELECT i.threadid, MAX(i.dtmcreated) AS lastmsgtime FROM {indexedchatmessage} i WHERE (ikind = :kind_user OR ikind = :kind_agent) GROUP BY i.threadid) tmp " . - "where m.threadid = t.threadid AND tmp.threadid = t.threadid AND t.dtmchatstarted <> 0 AND m.dtmcreated >= :start AND m.dtmcreated < :end", + "SELECT DATE(FROM_UNIXTIME(date)) AS date, " . + "SUM(threads) AS threads, " . + "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( - ':kind_agent' => Thread::KIND_AGENT, - ':kind_user' => Thread::KIND_USER, ':start' => $start, ':end' => $end ), array('return_rows' => Database::RETURN_ONE_ROW) ); + $activetab = 0; } elseif($statisticstype == 'byagent') { $page['reportByAgent'] = $db->query( - "select vclocalename as name, COUNT(distinct threadid) as threads, " . - "SUM(ikind = :kind_agent) as msgs, AVG(CHAR_LENGTH(tmessage)) as avglen " . - "from {indexedchatmessage}, {chatoperator} " . - "where agentId = operatorid AND dtmcreated >= :start " . - "AND dtmcreated < :end group by operatorid", + "SELECT o.vclocalename AS name, " . + "s.threads AS threads, " . + "s.messages AS msgs, " . + "s.averagelength AS avglen " . + "FROM {chatoperatorstatistics} s, {chatoperator} o " . + "WHERE s.operatorid = o.operatorid " . + "AND s.date >= :start " . + "AND s.date < :end " . + "GROUP BY s.operatorid", array( - ':kind_agent' => Thread::KIND_AGENT, ':start' => $start, ':end' => $end ), array('return_rows' => Database::RETURN_ALL_ROWS) ); + $page['noresults'] = empty($page['reportByAgent']); $activetab = 1; } elseif($statisticstype == 'bypage') { $page['reportByPage'] = $db->query( diff --git a/src/messenger/webim/view/statistics.php b/src/messenger/webim/view/statistics.php index adee96bb..6ffcc76e 100644 --- a/src/messenger/webim/view/statistics.php +++ b/src/messenger/webim/view/statistics.php @@ -24,6 +24,10 @@ $page['menuid'] = "statistics"; function tpl_content() { global $page, $webimroot, $errors; ?> + +
+ +