mirror of
https://github.com/fusionpbx/fusionpbx.git
synced 2026-01-05 03:03:49 +00:00
Merge pull request #5778 from demonspork/cdr-stats-improvements
Improved Missed Call accuracy, cdr statistics, and hide duplicated CDRs from Enterprise Ring Groups
This commit is contained in:
@@ -587,7 +587,7 @@
|
||||
|
||||
--if the recording is below the minimal length then re-record the message
|
||||
if (message_length > 2) then
|
||||
--continue
|
||||
session:setVariable("voicemail_message_seconds", message_length);
|
||||
else
|
||||
if (session:ready()) then
|
||||
--your recording is below the minimal acceptable length, please try again
|
||||
|
||||
@@ -197,6 +197,9 @@
|
||||
$apps[$x]['permissions'][$y]['name'] = "xml_cdr_lose_race";
|
||||
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
|
||||
$y++;
|
||||
$apps[$x]['permissions'][$y]['name'] = "xml_cdr_enterprise_leg";
|
||||
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
|
||||
$y++;
|
||||
$apps[$x]['permissions'][$y]['name'] = "xml_cdr_cc_agent_leg";
|
||||
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
|
||||
$y++;
|
||||
@@ -537,6 +540,12 @@
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = "char(1)";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "The leg of the call a or b.";
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = "originating_leg_uuid";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "uuid";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "text";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = "char(36)";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "Originating Leg UUID. Used to identify legs of an enterprise ring group - and exclude them ";
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = "pdd_ms";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "numeric";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "numeric";
|
||||
@@ -555,6 +564,12 @@
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type'] = "text";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "Save the last application data.";
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = "voicemail_message";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "boolean";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "text";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = "text";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "";
|
||||
$z++;
|
||||
$apps[$x]['db'][$y]['fields'][$z]['name'] = "missed_call";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "boolean";
|
||||
$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "text";
|
||||
|
||||
@@ -145,10 +145,12 @@ if (!class_exists('xml_cdr')) {
|
||||
$this->fields[] = "record_path";
|
||||
$this->fields[] = "record_name";
|
||||
$this->fields[] = "leg";
|
||||
$this->fields[] = "originating_leg_uuid";
|
||||
$this->fields[] = "pdd_ms";
|
||||
$this->fields[] = "rtp_audio_in_mos";
|
||||
$this->fields[] = "last_app";
|
||||
$this->fields[] = "last_arg";
|
||||
$this->fields[] = "voicemail_message";
|
||||
$this->fields[] = "cc_side";
|
||||
$this->fields[] = "cc_member_uuid";
|
||||
$this->fields[] = "cc_queue_joined_epoch";
|
||||
@@ -316,7 +318,7 @@ if (!class_exists('xml_cdr')) {
|
||||
|
||||
//set missed calls
|
||||
$missed_call = 'false';
|
||||
if (strlen($xml->variables->answer_stamp) == 0) {
|
||||
if ($xml->variables->cc_side != "agent" && strlen($xml->variables->originating_leg_uuid) == 0 && $xml->variables->call_direction != 'outbound' && strlen($xml->variables->answer_stamp) == 0) {
|
||||
$missed_call = 'true';
|
||||
}
|
||||
if ($xml->variables->missed_call == 'true') {
|
||||
@@ -390,6 +392,14 @@ if (!class_exists('xml_cdr')) {
|
||||
$this->array[$key]['last_app'] = urldecode($xml->variables->last_app);
|
||||
$this->array[$key]['last_arg'] = urldecode($xml->variables->last_arg);
|
||||
|
||||
//voicemail message success
|
||||
if ($xml->variables->voicemail_action == "save" && $xml->variables->voicemail_message_seconds > 0){
|
||||
$this->array[$key]['voicemail_message'] = "true";
|
||||
}
|
||||
else { //if ($xml->variables->voicemail_action == "save") {
|
||||
$this->array[$key]['voicemail_message'] = "false";
|
||||
}
|
||||
|
||||
//conference
|
||||
$this->array[$key]['conference_name'] = urldecode($xml->variables->conference_name);
|
||||
$this->array[$key]['conference_uuid'] = urldecode($xml->variables->conference_uuid);
|
||||
@@ -404,6 +414,9 @@ if (!class_exists('xml_cdr')) {
|
||||
//store the call leg
|
||||
$this->array[$key]['leg'] = $leg;
|
||||
|
||||
//store the originating leg uuid
|
||||
$this->array[$key]['originating_leg_uuid'] = urldecode($xml->variables->originating_leg_uuid);
|
||||
|
||||
//store post dial delay, in milliseconds
|
||||
$this->array[$key]['pdd_ms'] = urldecode($xml->variables->progress_mediamsec) + urldecode($xml->variables->progressmsec);
|
||||
|
||||
@@ -430,7 +443,7 @@ if (!class_exists('xml_cdr')) {
|
||||
if (strlen($domain_name) == 0) {
|
||||
$presence_id = urldecode($xml->variables->presence_id);
|
||||
if (strlen($presence_id) > 0) {
|
||||
$presence_array = explode($presence_id);
|
||||
$presence_array = explode($presence_id, '%40');
|
||||
$domain_name = $presence_array[1];
|
||||
}
|
||||
}
|
||||
@@ -918,13 +931,13 @@ if (!class_exists('xml_cdr')) {
|
||||
if (strlen($this->start_stamp_begin) > 0 || strlen($this->start_stamp_end) > 0) {
|
||||
unset($this->quick_select);
|
||||
if (strlen($this->start_stamp_begin) > 0 && strlen($this->start_stamp_end) > 0) {
|
||||
$sql_date_range .= " and start_stamp between :start_stamp_begin and :start_stamp_end \n";
|
||||
$sql_date_range = " and start_stamp between :start_stamp_begin and :start_stamp_end \n";
|
||||
$parameters['start_stamp_begin'] = $this->start_stamp_begin.':00.000';
|
||||
$parameters['start_stamp_end'] = $this->start_stamp_end.':59.999';
|
||||
}
|
||||
else {
|
||||
if (strlen($this->start_stamp_begin) > 0) {
|
||||
$sql_date_range .= "and start_stamp >= :start_stamp_begin \n";
|
||||
$sql_date_range = "and start_stamp >= :start_stamp_begin \n";
|
||||
$parameters['start_stamp_begin'] = $this->start_stamp_begin.':00.000';
|
||||
}
|
||||
if (strlen($this->start_stamp_end) > 0) {
|
||||
@@ -935,13 +948,13 @@ if (!class_exists('xml_cdr')) {
|
||||
}
|
||||
else {
|
||||
switch ($this->quick_select) {
|
||||
case 1: $sql_date_range .= "and start_stamp >= '".date('Y-m-d H:i:s.000', strtotime("-1 week"))."' \n"; break; //last 7 days
|
||||
case 2: $sql_date_range .= "and start_stamp >= '".date('Y-m-d H:i:s.000', strtotime("-1 hour"))."' \n"; break; //last hour
|
||||
case 3: $sql_date_range .= "and start_stamp >= '".date('Y-m-d')." "."00:00:00.000' \n"; break; //today
|
||||
case 4: $sql_date_range .= "and start_stamp between '".date('Y-m-d',strtotime("-1 day"))." "."00:00:00.000' and '".date('Y-m-d',strtotime("-1 day"))." "."23:59:59.999' \n"; break; //yesterday
|
||||
case 5: $sql_date_range .= "and start_stamp >= '".date('Y-m-d',strtotime("this week"))." "."00:00:00.000' \n"; break; //this week
|
||||
case 6: $sql_date_range .= "and start_stamp >= '".date('Y-m-')."01 "."00:00:00.000' \n"; break; //this month
|
||||
case 7: $sql_date_range .= "and start_stamp >= '".date('Y-')."01-01 "."00:00:00.000' \n"; break; //this year
|
||||
case 1: $sql_date_range = "and start_stamp >= '".date('Y-m-d H:i:s.000', strtotime("-1 week"))."' \n"; break; //last 7 days
|
||||
case 2: $sql_date_range = "and start_stamp >= '".date('Y-m-d H:i:s.000', strtotime("-1 hour"))."' \n"; break; //last hour
|
||||
case 3: $sql_date_range = "and start_stamp >= '".date('Y-m-d')." "."00:00:00.000' \n"; break; //today
|
||||
case 4: $sql_date_range = "and start_stamp between '".date('Y-m-d',strtotime("-1 day"))." "."00:00:00.000' and '".date('Y-m-d',strtotime("-1 day"))." "."23:59:59.999' \n"; break; //yesterday
|
||||
case 5: $sql_date_range = "and start_stamp >= '".date('Y-m-d',strtotime("this week"))." "."00:00:00.000' \n"; break; //this week
|
||||
case 6: $sql_date_range = "and start_stamp >= '".date('Y-m-')."01 "."00:00:00.000' \n"; break; //this month
|
||||
case 7: $sql_date_range = "and start_stamp >= '".date('Y-')."01-01 "."00:00:00.000' \n"; break; //this year
|
||||
}
|
||||
}
|
||||
|
||||
@@ -969,7 +982,10 @@ if (!class_exists('xml_cdr')) {
|
||||
$sql .= "filter ( \n";
|
||||
$sql .= " where c.extension_uuid = e.extension_uuid \n";
|
||||
$sql .= " and missed_call = true \n";
|
||||
if (!permission_exists('xml_cdr_lose_race')) {
|
||||
if (!permission_exists('xml_cdr_enterprise_leg')) {
|
||||
$sql .= " and originating_leg_uuid is null \n";
|
||||
}
|
||||
elseif (!permission_exists('xml_cdr_lose_race')) {
|
||||
$sql .= " and hangup_cause <> 'LOSE_RACE' \n";
|
||||
}
|
||||
if ($this->include_internal) {
|
||||
@@ -1025,7 +1041,10 @@ if (!class_exists('xml_cdr')) {
|
||||
$sql .= "count(*) \n";
|
||||
$sql .= "filter ( \n";
|
||||
$sql .= " where c.extension_uuid = e.extension_uuid \n";
|
||||
if (!permission_exists('xml_cdr_lose_race')) {
|
||||
if (!permission_exists('xml_cdr_enterprise_leg')) {
|
||||
$sql .= " and originating_leg_uuid is null \n";
|
||||
}
|
||||
elseif (!permission_exists('xml_cdr_lose_race')) {
|
||||
$sql .= " and hangup_cause <> 'LOSE_RACE' \n";
|
||||
}
|
||||
if ($this->include_internal) {
|
||||
@@ -1076,6 +1095,7 @@ if (!class_exists('xml_cdr')) {
|
||||
$sql .= " direction, \n";
|
||||
$sql .= " start_stamp, \n";
|
||||
$sql .= " hangup_cause, \n";
|
||||
$sql .= " originating_leg_uuid, \n";
|
||||
$sql .= " billsec \n";
|
||||
$sql .= " from v_xml_cdr \n";
|
||||
if (!($_GET['show'] === 'all' && permission_exists('xml_cdr_all'))) {
|
||||
@@ -1210,7 +1230,7 @@ if (!class_exists('xml_cdr')) {
|
||||
// If the range starts with an '-' we start from the beginning
|
||||
// If not, we forward the file pointer
|
||||
// And make sure to get the end byte if spesified
|
||||
if ($range0 == '-') {
|
||||
if ($range == '-') {
|
||||
// The n-number of the last bytes is requested
|
||||
$c_start = $size - substr($range, 1);
|
||||
}
|
||||
|
||||
@@ -198,6 +198,14 @@
|
||||
$database->fields['last_app'] = urldecode($xml->variables->last_app);
|
||||
$database->fields['last_arg'] = urldecode($xml->variables->last_arg);
|
||||
|
||||
//voicemail message success
|
||||
if ($xml->variables->voicemail_action == "save" && $xml->variables->voicemail_message_seconds > 0){
|
||||
$database->fields['voicemail_message'] = "true";
|
||||
}
|
||||
elseif ($xml->variables->voicemail_action == "save") {
|
||||
$database->fields['voicemail_message'] = "false";
|
||||
}
|
||||
|
||||
//conference
|
||||
$database->fields['conference_name'] = urldecode($xml->variables->conference_name);
|
||||
$database->fields['conference_uuid'] = urldecode($xml->variables->conference_uuid);
|
||||
@@ -211,7 +219,7 @@
|
||||
|
||||
//set missed calls
|
||||
$database->fields['missed_call'] = 'false';
|
||||
if (strlen($xml->variables->answer_stamp) == 0) {
|
||||
if ($xml->variables->cc_side != "agent" && strlen($xml->variables->originating_leg_uuid) == 0 && $xml->variables->call_direction != 'outbound' && strlen($xml->variables->answer_stamp) == 0) {
|
||||
$database->fields['missed_call'] = 'true';
|
||||
}
|
||||
if ($xml->variables->missed_call == 'true') {
|
||||
@@ -292,7 +300,7 @@
|
||||
if (strlen($domain_name) == 0) {
|
||||
$presence_id = urldecode($xml->variables->presence_id);
|
||||
if (strlen($presence_id) > 0) {
|
||||
$presence_array = explode($presence_id);
|
||||
$presence_array = explode($presence_id, '%40');
|
||||
$domain_name = $presence_array[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,7 +796,7 @@
|
||||
}
|
||||
//tta (time to answer)
|
||||
if (permission_exists('xml_cdr_tta')) {
|
||||
$content .= " <td class='middle right hide-md-dn'>".(($row['tta'] > 0) ? $row['tta']."s" : " ")."</td>\n";
|
||||
$content .= " <td class='middle right hide-md-dn'>".(($row['tta'] >= 0) ? $row['tta']."s" : " ")."</td>\n";
|
||||
}
|
||||
//duration
|
||||
if (permission_exists('xml_cdr_duration')) {
|
||||
@@ -823,14 +823,6 @@
|
||||
}
|
||||
|
||||
$content .= "</tr>\n";
|
||||
|
||||
if (!permission_exists('xml_cdr_lose_race') && $row['hangup_cause'] == 'LOSE_RACE') {
|
||||
$content = '';
|
||||
}
|
||||
//show agent originated legs only to those with the permission
|
||||
if (!permission_exists('xml_cdr_cc_agent_leg') && $row['cc_side'] == "agent") {
|
||||
$content = '';
|
||||
}
|
||||
//show the leg b only to those with the permission
|
||||
if ($row['leg'] == 'a') {
|
||||
echo $content;
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
$data_body[$p] .= '<td>'.format_phone($fields['destination_number']).'</td>';
|
||||
$data_body[$p] .= '<td>'.$fields['start_stamp'].'</td>';
|
||||
$total['tta'] += ($fields['tta'] > 0) ? $fields['tta'] : 0;
|
||||
$data_body[$p] .= '<td align="right">'.(($fields['tta'] > 0) ? $fields['tta'].'s' : null).'</td>';
|
||||
$data_body[$p] .= '<td align="right">'.(($fields['tta'] >= 0) ? $fields['tta'].'s' : null).'</td>';
|
||||
$seconds = ($fields['hangup_cause'] == "ORIGINATOR_CANCEL") ? $fields['duration'] : round(($fields['billmsec'] / 1000), 0, PHP_ROUND_HALF_UP);
|
||||
$total['duration'] += $seconds;
|
||||
$data_body[$p] .= '<td align="right">'.gmdate("G:i:s", $seconds).'</td>';
|
||||
|
||||
@@ -417,6 +417,13 @@
|
||||
$sql .= "and hangup_cause like :hangup_cause ";
|
||||
$parameters['hangup_cause'] = '%'.$hangup_cause.'%';
|
||||
}
|
||||
elseif (!permission_exists('xml_cdr_lose_race') && !permission_exists('xml_cdr_enterprise_leg')) {
|
||||
$sql .= "and hangup_cause != 'LOSE_RACE' ";
|
||||
}
|
||||
//exclude enterprise ring group legs
|
||||
if (!permission_exists('xml_cdr_enterprise_leg')) {
|
||||
$sql .= "and originating_leg_uuid IS NULL ";
|
||||
}
|
||||
if (strlen($call_result) > 0) {
|
||||
switch ($call_result) {
|
||||
case 'answered':
|
||||
@@ -430,10 +437,20 @@
|
||||
break;
|
||||
case 'cancelled':
|
||||
if ($direction == 'inbound' || $direction == 'local' || $call_result == 'missed') {
|
||||
$sql = "and (answer_stamp is null and bridge_uuid is null and sip_hangup_disposition <> 'send_refuse') ";
|
||||
$sql .= "
|
||||
and ((
|
||||
answer_stamp is null
|
||||
and bridge_uuid is null
|
||||
and sip_hangup_disposition <> 'send_refuse'
|
||||
)
|
||||
or (
|
||||
answer_stamp is not null
|
||||
and bridge_uuid is null
|
||||
and voicemail_message = false
|
||||
))";
|
||||
}
|
||||
else if ($direction == 'outbound') {
|
||||
$sql = "and (answer_stamp is null and bridge_uuid is not null) ";
|
||||
$sql .= "and (answer_stamp is null and bridge_uuid is not null) ";
|
||||
}
|
||||
else {
|
||||
$sql .= "
|
||||
@@ -447,10 +464,17 @@
|
||||
direction = 'outbound'
|
||||
and answer_stamp is null
|
||||
and bridge_uuid is not null
|
||||
)
|
||||
or (
|
||||
(direction = 'inbound' or direction = 'local')
|
||||
and answer_stamp is not null
|
||||
and bridge_uuid is null
|
||||
and voicemail_message = false
|
||||
))";
|
||||
}
|
||||
break;
|
||||
default: //failed
|
||||
default:
|
||||
$sql .= "and (answer_stamp is null and bridge_uuid is null and duration = 0) ";
|
||||
//$sql .= "and (answer_stamp is null and bridge_uuid is null and billsec = 0 and sip_hangup_disposition = 'send_refuse') ";
|
||||
}
|
||||
}
|
||||
@@ -507,6 +531,10 @@
|
||||
$sql .= "and (c.record_path is null or c.record_name is null) ";
|
||||
}
|
||||
}
|
||||
//show agent originated legs only to those with the permission
|
||||
if (!permission_exists('xml_cdr_cc_agent_leg')) {
|
||||
$sql .= "and (cc_side is null or cc_side != 'agent') ";
|
||||
}
|
||||
//end where
|
||||
if (strlen($order_by) > 0) {
|
||||
$sql .= order_by($order_by, $order);
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
$parameters['domain_uuid'] = $_SESSION['domain_uuid'];
|
||||
}
|
||||
if ($missed == true) {
|
||||
$sql_where_ands[] = "billsec = '0'";
|
||||
$sql_where_ands[] = "missed_call = true ";
|
||||
}
|
||||
if (strlen($start_epoch) > 0 && strlen($stop_epoch) > 0) {
|
||||
$sql_where_ands[] = "start_epoch between :start_epoch and :stop_epoch";
|
||||
@@ -280,10 +280,15 @@
|
||||
$sql_where_ands[] = "leg = :leg";
|
||||
$parameters['leg'] = $leg;
|
||||
}
|
||||
//Exclude enterprise ring group legs
|
||||
if (!permission_exists('xml_cdr_enterprise_leg')) {
|
||||
$sql_where_ands[] .= "originating_leg_uuid IS NULL";
|
||||
}
|
||||
//If you can't see lose_race, don't run stats on it
|
||||
if (!permission_exists('xml_cdr_lose_race')) {
|
||||
elseif (!permission_exists('xml_cdr_lose_race')) {
|
||||
$sql_where_ands[] = "hangup_cause != 'LOSE_RACE'";
|
||||
}
|
||||
|
||||
|
||||
//if not admin or superadmin, only show own calls
|
||||
if (!permission_exists('xml_cdr_domain')) {
|
||||
@@ -349,7 +354,7 @@
|
||||
|
||||
//get the call volume between a start end end time in seconds
|
||||
function get_call_volume_between($start, $end, $where, $parameters) {
|
||||
$sql = "select count(*) as count, sum(billsec) as seconds from v_xml_cdr ";
|
||||
$sql = "select count(*) as count, sum(billsec) as seconds, sum(answer_stamp - start_stamp) as tta from v_xml_cdr ";
|
||||
$sql .= $where." ";
|
||||
$sql .= "start_epoch between :start and :end ";
|
||||
$parameters['start'] = $start;
|
||||
@@ -360,6 +365,7 @@
|
||||
return array(
|
||||
'volume' => $row['count'],
|
||||
'seconds' => $row['seconds'],
|
||||
'tta' => $row['tta'],
|
||||
);
|
||||
}
|
||||
return false;
|
||||
@@ -379,14 +385,13 @@
|
||||
$stats[$i]['volume'] = $stat_range ? $stat_range['volume'] : 0;
|
||||
$stats[$i]['seconds'] = $stat_range ? $stat_range['seconds'] : 0;
|
||||
$stats[$i]['minutes'] = $stats[$i]['seconds'] / 60;
|
||||
$stats[$i]['avg_sec'] = $stats[$i]['volume'] == 0 ? 0 : $stats[$i]['seconds'] / $stats[$i]['volume'];
|
||||
|
||||
if ($missed) {
|
||||
//we select only missed calls at first place - no reasons to select it again
|
||||
$stats[$i]['missed'] = $stats[$i]['volume'];
|
||||
}
|
||||
else {
|
||||
$where = $sql_where."billsec = '0' and ";
|
||||
$where = $sql_where."missed_call = true and ";
|
||||
$stat_range = get_call_volume_between($stats[$i]['start_epoch'], $stats[$i]['stop_epoch'], $where, $parameters);
|
||||
$stats[$i]['missed'] = $stat_range ? $stat_range['volume'] : 0;
|
||||
}
|
||||
@@ -403,6 +408,9 @@
|
||||
//answer / seizure ratio
|
||||
$stats[$i]['asr'] = $stats[$i]['volume'] == 0 ? 0 : ($success_volume / $stats[$i]['volume'] * 100);
|
||||
|
||||
//average time to answer
|
||||
$stats[$i]['avg_tta'] = $stats[$i]['volume'] == 0 ? 0 : round($stat_range['tta'] / $success_volume);
|
||||
|
||||
//average length of call
|
||||
$stats[$i]['aloc'] = $success_volume == 0 ? 0 : $stats[$i]['minutes'] / $success_volume;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user