Александр Мисюрин
20.02.2019
266

Создание отчета по операторам, по различным показателям с использованием userfield

В данной статье рассмотрим возможность формирования отчетностей по операторам, с использованием дополнительного поля для записей в cdr – userfield. В отчетность можно добавлять различные показатели, такие как: Время до ответа Время разговора Кто положил трубку И многие другие. Добавлять показатели можно с помощью диалплана, в custom файлах (если используется FreePBX). Добавлять будем после завершения звонка. […]

В данной статье рассмотрим возможность формирования отчетностей по операторам, с использованием дополнительного поля для записей в cdr – userfield. В отчетность можно добавлять различные показатели, такие как:

  • Время до ответа
  • Время разговора
  • Кто положил трубку

И многие другие.

Добавлять показатели можно с помощью диалплана, в custom файлах (если используется FreePBX). Добавлять будем после завершения звонка.

Поскольку для примера используется система с FreePBX, изменять будем контекст macro-hangupcall, если у вас «чистый» Asterisk, можно использовать обработчик h в диалплане завершающего вызов контекста.

Для вывода информации, создадим небольшой web-интерфейс, с возможностью сохранить показатели в csv.

Время до ответа будем считать по разнице между общим временем и временем разговора с клиентом.

Источник можно получить несколькими способами, например из контекста откуда пришел вызов, либо из таблицы cel.

Информацию о том, кто положил трубку можно получить из переменных диалплана.

В диалплане внесем правки, для начала определить контекст, в котором будет происходить сбор переменных. Это можно делать в момент завершения вызова на локальный номер (контекст ext-local), в момент завершения вызова в очереди (ext-queues), в момент выполнения macro-hangupcall. Остановимся на последнем, поскольку для примера хватит и переменных оттуда.

В контексте для начала выводим необходимую информацию:

exten => s,n,NoOp(==${CALLERID(num)}==${EXTTOCALL}==${CDR(billsec)}==${CDR(duration)}==${CHANNEL(hangupsource)}==)
Вывод необходимых переменных

Для получения времени ожидания, используем функцию диалплана – MATH:

exten => s,n,Set(wait=${MATH(${CDR(duration)}-${CDR(billsec)},int)})

и добавим ее в вывод, пометив переменные для наглядности:

exten => s,n,NoOp(Caller=${CALLERID(num)}&Callee=${EXTTOCALL}&Billsec=${CDR(billsec)}&Duration=${CDR(duration)}&Res_Hangup=${CHANNEL(hangupsource)}&Wait=${wait})
Подсчет и вывод, с описанием

Как видно из gif, переменная, обозначающая завершившего канал, пишется в его канале, в cdr это будет отображено в его поле. На данном этапе добавим запись в userfield этих значений как есть.

Поле userfield в cdr
Следует помнить, что по умолчанию, поле userfield ограничено 256 символами. Если планируется выйти за эти пределы, то необходимо внести изменение в cdr таблицу.

Пример:

ALTER TABLE cdr MODIFY userfield varchar(512);

Добавляем переменные в поле userfield:

exten => s,n,Set(CDR(userfield)=Caller=${CALLERID(num)}&Callee=${EXTTOCALL}&Billsec=${CDR(billsec)}&Duration=${CDR(duration)}&Res_Hangup=${CHANNEL(hangupsource)}&Wait=${wait})
Запись в userfield и отображение в cdr reports

Чтобы не заполнять лишние переменные в поле, можно создать обработчик, например, использовать ExecIf. Также можно изменить переменную отвечающую за целевой номер, на ${CONNECTEDLINE(num)}.

Например, будем записывать поле userfield, только для того, кто положил трубку:

exten => s,n,ExecIf($["${CHANNEL(hangupsource)}"!=""]?Set(CDR(userfield)=Caller=${CALLERID(num)}&Callee=${CONNECTEDLINE(num)}&Billsec=${CDR(billsec)}&Duration=${CDR(duration)}&Res_Hangup=${CHANNEL(hangupsource)}&Wait=${wait}))
Запись только для завершающего канала
Запись можно вести для обоих каналов, просто убирать лишние переменные из них.

Для отображения в отдельном окне выборки статистики, желательно записывать в оба поля, поскольку некоторые записи иначе будут потеряны.

Создадим небольшой веб-интерфейс отображения данных.

Для начала создадим скрипт в директории apache и дадим к нему доступ через нестандартный порт.

[root@localhost ~]# mkdir /var/www/html/userfield
[root@localhost ~]# touch /var/www/html/userfield/index.php
[root@localhost ~]# chown asterisk. /var/www/html/userfield/ -R

Параметры виртуального хоста в apache:

Listen 5446
NameVirtualHost *:5446
<VirtualHost *:5446>
        DocumentRoot /var/www/html/userfield/
        <Directory /var/www/html/userfield>
                Options +Indexes
                AllowOverride All
                Order deny,allow
                Deny from all
                Allow from all
                Satisfy all
                RewriteEngine off
        </Directory>
</VirtualHost>
Бессмертная классика

На первой странице необходимо реализовать выборку по операторам АТС. Выбирать будем, основываясь на номерах из таблицы asterisk.devices.

Добавляем функцию bd_bridge, для запросов в базу и получаем список операторов.

Таблица devices выбрана потому, что в ней находятся также и номера по технологии custom и виртуальные номера.
function bd_bridge($db,$q){
   $link = @mysqli_connect('localhost','freepbxuser','d45c7ea6a5460b03300977b1cd61f148',$db) or die("Error: ".@mysqli_connect_error($link));
   $rs = @mysqli_query($link, $q) or die("Error: ".@mysqli_error($link));
         if($rs){
            //echo "Qwerty complete: $q <br />";
            return $rs;
            @mysqli_free_result($rs);
         }
          else{
            //echo "Qwerty failure: $q <br />";
                }
            mysqli_close($link);
}
$result = bd_bridge('asterisk','SELECT id, description FROM devices ORDER BY id;');
echo "<form action='' method='post'><table>";
while ($row = mysqli_fetch_array($result))
{
	$CellName = $row['description'];
	$CellPhone = $row['id'];
	echo "<tr><td><input type='checkbox' name='my_array[]' value='".$row[0].":".$row[1]."' />";
  	echo $row[0].":".$row[1];
  	echo "</td></tr>";
}
echo "<tr><td> <button name='inb' type='submit'>Отобразить входящие</button>
<button name='out' type='submit'>Отобразить исходящие</button> </td></tr>
    </table>
    </form>";
Главное окно, со списком операторов

Список выведен, реализуем обработчик нажатия:

if (isset($_POST['inb']) && !empty($_POST['my_array'])) {
	foreach ( $_POST['my_array'] as $value) {
		$num=stristr($value,':',true);//получаем номер
		$name=stristr($value,':');
		$name=str_replace(":","",$name); //получаем имя
//запрос в базу за полем userfield
		$result = bd_bridge('asteriskcdrdb',"SELECT calldate, userfield FROM cdr where calldate like '2018-12-25%' and (dst='".$num."');");
		while ($row = mysqli_fetch_array($result)) {
		if ($row['userfield'] != "") {
			$arru=str_to_arr($row['userfield']);
		echo $arru['Caller']." - ".$arru['Callee']." - ".$arru['Billsec']." - ".$arru['Duration']." - ".$arru['Res_Hangup']." - ".$arru['Wait']."</br>";
			}
		}
	}
}

И организуем пока простой вывод информации на страницу.

Для вывода используется функция преобразования строки в массив, это необходимо для дальнейшего распределения по таблице.

Функция:

function str_to_arr($str){
    $piece=array();
    $string=array();
    $arr=array();
    $j=0;
    $string = explode ("&",$str);
    for($i=0;$i<count($string);$i++){
      $piece=explode("=",$string[$i],2);
      if (isset($piece[1])){
        $arr[trim($piece[0])]=trim($piece[1]);
      }
    }
    return $arr;
}
Отображение и вывод отладочной информации

Далее формируем таблицу:

Если в источнике завершения вызова пусто, то сброс произвел не оператор, в таком случае можно оставить пометку «сбросил клиент», либо указать источником номер звонящего.
if ($arru['Res_Hangup']=="") $arru['Res_Hangup']=$arru['Caller'];
echo "<tr>
	<td>".$row['calldate']."</td>
	<td>".$arru['Caller']."</td>
	<td>".$arru['Callee']."</td>
	<td>".$arru['Billsec']."</td>
	<td>".$arru['Duration']."</td>
	<td>".$arru['Wait']."</td>
	<td>".$arru['Res_Hangup']."</td>
</tr>";

Шапку таблицы укажем при входе в обработчик:

echo "
<table border=1>
<tr>
	<td>Время</td>
	<td>Номер источник</td>
	<td>Номер направление</td>
	<td>Время разговора</td>
	<td>Общее время</td>
	<td>Время ожидания</td>
	<td>Источник завершения вызова</td>
</tr>";

Аналогично напишем обработчик исходящих вызовов:

if (isset($_POST['out']) && !empty($_POST['my_array'])) {
		//echo "<pre>".print_r($_POST,true)."</pre>";
echo "
	<table border=1>
	<tr>
		<td>Время</td>
		<td>Номер источник</td>
		<td>Номер направление</td>
		<td>Время разговора</td>
		<td>Общее время</td>
		<td>Время ожидания</td>
		<td>Источник завершения вызова</td></tr>";
		foreach ( $_POST['my_array'] as $value) {
	$num=stristr($value,':',true);
	$name=stristr($value,':');
	$name=str_replace(":","",$name);
	$result = bd_bridge('asteriskcdrdb',"SELECT calldate, userfield FROM cdr where calldate like '2018-12-25%' and (src='".$num."');");
while ($row = mysqli_fetch_array($result)) {
	if ($row['userfield'] != "") {
		$arru=str_to_arr($row['userfield']);
		//echo "<pre>".print_r($arru,true)."</pre>";
		if ($arru['Res_Hangup']=="") $arru['Res_Hangup']=$arru['Callee'];
	echo "
	<tr>
		<td>".$row['calldate']."</td>
		<td>".$arru['Caller']."</td>
		<td>".$arru['Callee']."</td>
		<td>".$arru['Billsec']."</td>
		<td>".$arru['Duration']."</td>
		<td>".$arru['Wait']."</td>
		<td>".$arru['Res_Hangup']."</td>
	</tr>";
			}
		}
	}
}
Вывод таблиц
Внимательные заметили, что в скрипте стоит заглушка, данные собираются только за один день, для того, чтобы данные собирались за все время, нужно убрать из select: calldate like …

Если необходимо собирать информацию только за определенное время, необходима доработка, рассмотрим ее.

Для начала необходимо вывести форму выбора интервала времени:

Даты:

<input name=d_first type='date' value=$date_start required> - <input name=d_second type='date' value=$date_start required> <br />

Поля должны быть обязательными для заполнения, также эти поля ввода необходимо поместить в форму отправки предыдущих значений.

В соответствии с датой, меняем запрос в БД за информацией:

$result = bd_bridge('asteriskcdrdb',"SELECT calldate, userfield FROM cdr where calldate >= '".$_POST[d_first]." 00:00:00' and calldate <= '".$_POST[d_second]." 23:59:59' and (dst='".$num."');");

Изменения внесены, можно проверять.

Вывод таблиц с использованием дат

Следующим шагом, реализуем возможность выгрузки статистики в csv.

Выгрузку будем организовывать в формат csv, поскольку созданный xls некорректно выглядит для обработчика Excel.

Форма отправки та же самая, указываем две кнопки: для входящей и исходщей.

echo "<tr>
	<td><button name='inb' type='submit'>Отобразить входящие</button>
	<button name='out' type='submit'>Отобразить исходящие</button></td>
	</tr>
<tr>
	<td><button name='get_csv_inb' type='submit'>Выгрузить входящие</button>
	<button name='get_csv_out' type='submit'>Выгрузить исходящие</button></td>
</tr>
    </table>
    </form>";

Обработчик нажатия кнопки выгрузки выполняет почти те же действия что и вывод таблицы, однако выводит не на страницу, а в файл и впоследствии предлагает его выгрузить.

Для выгрузки файла использовалась функция file_force_download, описание которой можно найти <a href="https://habr.com/ru/post/151795/">на сайте Habr’a</a>.
if (isset($_POST['get_csv_inb'])&& !empty($_POST['my_array'])) {
	$fp = fopen('statistic_inb.csv', 'w');
	fputcsv($fp, array('Время','Номер источник','Номер направление','Время разговора','Общее время','Время ожидания','Источник завершения вызова'));
	foreach ( $_POST['my_array'] as $value) {
	$num=stristr($value,':',true);
	$name=stristr($value,':');
	$name=str_replace(":","",$name);
$result = bd_bridge('asteriskcdrdb',"SELECT calldate, userfield FROM cdr where calldate >= '".$_POST['d_first']." 00:00:00' and calldate <= '".$_POST['d_second']." 23:59:59' and (dst='".$num."');");
while ($row = mysqli_fetch_array($result)) {
if ($row['userfield'] != "") {
	$arru=str_to_arr($row['userfield']);
	//echo "<pre>".print_r($arru,true)."</pre>";
if ($arru['Res_Hangup']=="") $arru['Res_Hangup']=$arru['Caller'];
	fputcsv($fp, array($row['calldate'],$arru['Caller'],$arru['Callee'],$arru['Billsec'],$arru['Duration'],$arru['Wait'],$arru['Res_Hangup']));
			}
		}
	}fclose($fp);
file_force_download('statistic_inb.cs

Для выгрузки исходящей, обработчик аналогичен:

if (isset($_POST['get_csv_out'])&& !empty($_POST['my_array'])) {
	$fp = fopen('statistic_out.csv', 'w');
	fputcsv($fp, array('Время','Номер источник','Номер направление','Время разговора','Общее время','Время ожидания','Источник завершения вызова'));
	foreach ( $_POST['my_array'] as $value) {
	$num=stristr($value,':',true);
	$name=stristr($value,':');
	$name=str_replace(":","",$name);
$result = bd_bridge('asteriskcdrdb',"SELECT calldate, userfield FROM cdr where calldate >= '".$_POST['d_first']." 00:00:00' and calldate <= '".$_POST['d_second']." 23:59:59' and (dst='".$num."');");
while ($row = mysqli_fetch_array($result)) {
if ($row['userfield'] != "") {
	$arru=str_to_arr($row['userfield']);
	//echo "<pre>".print_r($arru,true)."</pre>";
if ($arru['Res_Hangup']=="") $arru['Res_Hangup']=$arru['Callee'];
	fputcsv($fp, array($row['calldate'],$arru['Caller'],$arru['Callee'],$arru['Billsec'],$arru['Duration'],$arru['Wait'],$arru['Res_Hangup']));
			}
		}
	}fclose($fp);
file_force_download('statistic_out.csv');
}
Выгрузка в csv

Функционал вывода реализован. Если текущих параметров недостаточно, можно добавить в заполнение поля необходимые данные, учитывая текущий формат записи.

 
avatar
  Подписаться  
Уведомление о

Остались вопросы?

Я - Виталий Шелест, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование

ближайшие курсы

ближайшие Вебинары

ONLINE

Why Choose HUGE?

Unlimited pre-designed elements

Each and every design element is designed for retina ready display on all kind of devices

User friendly interface and design

Each and every design element is designed for retina ready display on all kind of devices

100% editable layered PSD files

Each and every design element is designed for retina ready display on all kind of devices

Created using shape layers

Each and every design element is designed for retina ready display on all kind of devices