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

Создание отчета по операторам, по различным показателям с использованием 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

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

Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments

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

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

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

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

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

ONLINE

10 доводов в пользу Asterisk

Распространяется бесплатно.

Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.

Безопасен в использовании.

Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.

Надежен в эксплуатации.

Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.

Гибкий в настройке.

Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

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

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.