Calculate threads and operator statistics by cron

This commit is contained in:
Dmitriy Simushev 2013-05-06 14:12:19 +00:00
parent 67de68b28b
commit 6ad1828079
6 changed files with 187 additions and 17 deletions

View File

@ -31,6 +31,7 @@ set_time_limit(0);
// Run cron jobs of the core // Run cron jobs of the core
cron_index_messages(); cron_index_messages();
cron_calculate_statistics();
// Trigger cron event // Trigger cron event
$dispatcher = EventDispatcher::getInstance(); $dispatcher = EventDispatcher::getInstance();

View File

@ -57,6 +57,16 @@ $dbtables = array(
"groupid" => "int references ${mysqlprefix}chatgroup(groupid)", "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( "${mysqlprefix}requestbuffer" => array(
"requestid" => "int NOT NULL auto_increment PRIMARY KEY", "requestid" => "int NOT NULL auto_increment PRIMARY KEY",
// Use MD5 hashes as keys // Use MD5 hashes as keys
@ -114,6 +124,15 @@ $dbtables = array(
"vcrestoretoken" => "varchar(64)", "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( "${mysqlprefix}chatrevision" => array(
"id" => "INT NOT NULL" "id" => "INT NOT NULL"
), ),
@ -180,6 +199,9 @@ $dbtables_indexes = array(
"${mysqlprefix}chatgroup" => array( "${mysqlprefix}chatgroup" => array(
"parent" => "parent" "parent" => "parent"
), ),
"${mysqlprefix}chatoperatorstatistics" => array(
"operatorid" => "operatorid"
),
"${mysqlprefix}chatgroupoperator" => array( "${mysqlprefix}chatgroupoperator" => array(
"groupid" => "groupid", "groupid" => "groupid",
"operatorid" => "operatorid" "operatorid" => "operatorid"
@ -208,10 +230,12 @@ $memtables = array();
$dbtables_can_update = array( $dbtables_can_update = array(
"${mysqlprefix}chatthread" => array("agentId", "userTyping", "agentTyping", "messageCount", "nextagent", "shownmessageid", "userid", "userAgent", "groupid", "dtmchatstarted"), "${mysqlprefix}chatthread" => array("agentId", "userTyping", "agentTyping", "messageCount", "nextagent", "shownmessageid", "userid", "userAgent", "groupid", "dtmchatstarted"),
"${mysqlprefix}chatthreadstatistics" => array(),
"${mysqlprefix}requestbuffer" => array("requestid", "requestkey", "request"), "${mysqlprefix}requestbuffer" => array("requestid", "requestkey", "request"),
"${mysqlprefix}chatmessage" => array("agentId"), "${mysqlprefix}chatmessage" => array("agentId"),
"${mysqlprefix}indexedchatmessage" => array(), "${mysqlprefix}indexedchatmessage" => array(),
"${mysqlprefix}chatoperator" => array("vcavatar", "vcjabbername", "iperm", "istatus", "idisabled", "vcemail", "dtmrestore", "vcrestoretoken"), "${mysqlprefix}chatoperator" => array("vcavatar", "vcjabbername", "iperm", "istatus", "idisabled", "vcemail", "dtmrestore", "vcrestoretoken"),
"${mysqlprefix}chatoperatorstatistics" => array(),
"${mysqlprefix}chatban" => array(), "${mysqlprefix}chatban" => array(),
"${mysqlprefix}chatgroup" => array("vcemail", "iweight", "parent", "vctitle", "vcchattitle", "vclogo", "vchosturl"), "${mysqlprefix}chatgroup" => array("vcemail", "iweight", "parent", "vctitle", "vcchattitle", "vclogo", "vchosturl"),
"${mysqlprefix}chatgroupoperator" => array(), "${mysqlprefix}chatgroupoperator" => array(),

View File

@ -237,6 +237,9 @@ if ($act == "silentcreateall") {
runsql("ALTER TABLE ${mysqlprefix}visitedpage ADD INDEX (visitorid)", $link); 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);
}
} }
} }

View File

@ -202,6 +202,123 @@ function cron_index_messages() {
$db->query('COMMIT'); $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 * Generates cron URI
* *

View File

@ -19,6 +19,7 @@ require_once('../libs/init.php');
require_once('../libs/chat.php'); require_once('../libs/chat.php');
require_once('../libs/operator.php'); require_once('../libs/operator.php');
require_once('../libs/statistics.php'); require_once('../libs/statistics.php');
require_once('../libs/cron.php');
$operator = check_login(); $operator = check_login();
force_password($operator); force_password($operator);
@ -35,6 +36,10 @@ $page['type'] = $statisticstype;
$page['showbydate'] = ($statisticstype == 'bydate'); $page['showbydate'] = ($statisticstype == 'bydate');
$page['showbyagent'] = ($statisticstype == 'byagent'); $page['showbyagent'] = ($statisticstype == 'byagent');
$page['showbypage'] = ($statisticstype == 'bypage'); $page['showbypage'] = ($statisticstype == 'bypage');
$page['noresults'] = false;
$page['cron_path'] = cron_get_uri(Settings::get('cron_key'));
$errors = array(); $errors = array();
if (isset($_GET['startday'])) { if (isset($_GET['startday'])) {
@ -74,45 +79,61 @@ $activetab = 0;
$db = Database::getInstance(); $db = Database::getInstance();
if ($statisticstype == 'bydate') { if ($statisticstype == 'bydate') {
$page['reportByDate'] = $db->query( $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 " . "SELECT DATE(FROM_UNIXTIME(date)) AS date, " .
"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 " . "threads, " .
"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", "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( array(
':kind_agent' => Thread::KIND_AGENT,
':kind_user' => Thread::KIND_USER,
':start' => $start, ':start' => $start,
':end' => $end ':end' => $end
), ),
array('return_rows' => Database::RETURN_ALL_ROWS) array('return_rows' => Database::RETURN_ALL_ROWS)
); );
$page['noresults'] = empty($page['reportByDate']);
$page['reportByDateTotal'] = $db->query( $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 " . "SELECT DATE(FROM_UNIXTIME(date)) AS date, " .
"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 " . "SUM(threads) AS threads, " .
"where m.threadid = t.threadid AND tmp.threadid = t.threadid AND t.dtmchatstarted <> 0 AND m.dtmcreated >= :start AND m.dtmcreated < :end", "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( array(
':kind_agent' => Thread::KIND_AGENT,
':kind_user' => Thread::KIND_USER,
':start' => $start, ':start' => $start,
':end' => $end ':end' => $end
), ),
array('return_rows' => Database::RETURN_ONE_ROW) array('return_rows' => Database::RETURN_ONE_ROW)
); );
$activetab = 0; $activetab = 0;
} elseif($statisticstype == 'byagent') { } elseif($statisticstype == 'byagent') {
$page['reportByAgent'] = $db->query( $page['reportByAgent'] = $db->query(
"select vclocalename as name, COUNT(distinct threadid) as threads, " . "SELECT o.vclocalename AS name, " .
"SUM(ikind = :kind_agent) as msgs, AVG(CHAR_LENGTH(tmessage)) as avglen " . "s.threads AS threads, " .
"from {indexedchatmessage}, {chatoperator} " . "s.messages AS msgs, " .
"where agentId = operatorid AND dtmcreated >= :start " . "s.averagelength AS avglen " .
"AND dtmcreated < :end group by operatorid", "FROM {chatoperatorstatistics} s, {chatoperator} o " .
"WHERE s.operatorid = o.operatorid " .
"AND s.date >= :start " .
"AND s.date < :end " .
"GROUP BY s.operatorid",
array( array(
':kind_agent' => Thread::KIND_AGENT,
':start' => $start, ':start' => $start,
':end' => $end ':end' => $end
), ),
array('return_rows' => Database::RETURN_ALL_ROWS) array('return_rows' => Database::RETURN_ALL_ROWS)
); );
$page['noresults'] = empty($page['reportByAgent']);
$activetab = 1; $activetab = 1;
} elseif($statisticstype == 'bypage') { } elseif($statisticstype == 'bypage') {
$page['reportByPage'] = $db->query( $page['reportByPage'] = $db->query(

View File

@ -24,6 +24,10 @@ $page['menuid'] = "statistics";
function tpl_content() { global $page, $webimroot, $errors; function tpl_content() { global $page, $webimroot, $errors;
?> ?>
<?php if($page['noresults']) { ?>
<div id="formmessage"><?php echo getlocal2("cron.check.setup", array($page['cron_path'])) ?></div>
<?php } ?>
<?php echo getlocal("statistics.description") ?> <?php echo getlocal("statistics.description") ?>
<br /> <br />
<br /> <br />