Add page and api for stats about phone sended sms
This commit is contained in:
parent
064d6fd941
commit
4f717ef849
|
@ -53,6 +53,81 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom utility classes for padding */
|
||||||
|
.py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; }
|
||||||
|
.py-2 { padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; }
|
||||||
|
.py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
|
||||||
|
.py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; }
|
||||||
|
.py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; }
|
||||||
|
|
||||||
|
.px-1 { padding-left: 0.25rem !important; padding-right: 0.25rem !important; }
|
||||||
|
.px-2 { padding-left: 0.5rem !important; padding-right: 0.5rem !important; }
|
||||||
|
.px-3 { padding-left: 1rem !important; padding-right: 1rem !important; }
|
||||||
|
.px-4 { padding-left: 1.5rem !important; padding-right: 1.5rem !important; }
|
||||||
|
.px-5 { padding-left: 3rem !important; padding-right: 3rem !important; }
|
||||||
|
|
||||||
|
.pt-1 { padding-top: 0.25rem !important; }
|
||||||
|
.pt-2 { padding-top: 0.5rem !important; }
|
||||||
|
.pt-3 { padding-top: 1rem !important; }
|
||||||
|
.pt-4 { padding-top: 1.5rem !important; }
|
||||||
|
.pt-5 { padding-top: 3rem !important; }
|
||||||
|
|
||||||
|
.pb-1 { padding-bottom: 0.25rem !important; }
|
||||||
|
.pb-2 { padding-bottom: 0.5rem !important; }
|
||||||
|
.pb-3 { padding-bottom: 1rem !important; }
|
||||||
|
.pb-4 { padding-bottom: 1.5rem !important; }
|
||||||
|
.pb-5 { padding-bottom: 3rem !important; }
|
||||||
|
|
||||||
|
.pl-1 { padding-left: 0.25rem !important; }
|
||||||
|
.pl-2 { padding-left: 0.5rem !important; }
|
||||||
|
.pl-3 { padding-left: 1rem !important; }
|
||||||
|
.pl-4 { padding-left: 1.5rem !important; }
|
||||||
|
.pl-5 { padding-left: 3rem !important; }
|
||||||
|
|
||||||
|
.pr-1 { padding-right: 0.25rem !important; }
|
||||||
|
.pr-2 { padding-right: 0.5rem !important; }
|
||||||
|
.pr-3 { padding-right: 1rem !important; }
|
||||||
|
.pr-4 { padding-right: 1.5rem !important; }
|
||||||
|
.pr-5 { padding-right: 3rem !important; }
|
||||||
|
|
||||||
|
/* Custom utility classes for margin */
|
||||||
|
.my-1 { margin-top: 0.25rem !important; margin-bottom: 0.25rem !important; }
|
||||||
|
.my-2 { margin-top: 0.5rem !important; margin-bottom: 0.5rem !important; }
|
||||||
|
.my-3 { margin-top: 1rem !important; margin-bottom: 1rem !important; }
|
||||||
|
.my-4 { margin-top: 1.5rem !important; margin-bottom: 1.5rem !important; }
|
||||||
|
.my-5 { margin-top: 3rem !important; margin-bottom: 3rem !important; }
|
||||||
|
|
||||||
|
.mx-1 { margin-left: 0.25rem !important; margin-right: 0.25rem !important; }
|
||||||
|
.mx-2 { margin-left: 0.5rem !important; margin-right: 0.5rem !important; }
|
||||||
|
.mx-3 { margin-left: 1rem !important; margin-right: 1rem !important; }
|
||||||
|
.mx-4 { margin-left: 1.5rem !important; margin-right: 1.5rem !important; }
|
||||||
|
.mx-5 { margin-left: 3rem !important; margin-right: 3rem !important; }
|
||||||
|
|
||||||
|
.mt-1 { margin-top: 0.25rem !important; }
|
||||||
|
.mt-2 { margin-top: 0.5rem !important; }
|
||||||
|
.mt-3 { margin-top: 1rem !important; }
|
||||||
|
.mt-4 { margin-top: 1.5rem !important; }
|
||||||
|
.mt-5 { margin-top: 3rem !important; }
|
||||||
|
|
||||||
|
.mb-1 { margin-bottom: 0.25rem !important; }
|
||||||
|
.mb-2 { margin-bottom: 0.5rem !important; }
|
||||||
|
.mb-3 { margin-bottom: 1rem !important; }
|
||||||
|
.mb-4 { margin-bottom: 1.5rem !important; }
|
||||||
|
.mb-5 { margin-bottom: 3rem !important; }
|
||||||
|
|
||||||
|
.ml-1 { margin-left: 0.25rem !important; }
|
||||||
|
.ml-2 { margin-left: 0.5rem !important; }
|
||||||
|
.ml-3 { margin-left: 1rem !important; }
|
||||||
|
.ml-4 { margin-left: 1.5rem !important; }
|
||||||
|
.ml-5 { margin-left: 3rem !important; }
|
||||||
|
|
||||||
|
.mr-1 { margin-right: 0.25rem !important; }
|
||||||
|
.mr-2 { margin-right: 0.5rem !important; }
|
||||||
|
.mr-3 { margin-right: 1rem !important; }
|
||||||
|
.mr-4 { margin-right: 1.5rem !important; }
|
||||||
|
.mr-5 { margin-right: 3rem !important; }
|
||||||
|
|
||||||
|
|
||||||
/** POPUPS ALERT **/
|
/** POPUPS ALERT **/
|
||||||
.popup-alerts-container
|
.popup-alerts-container
|
||||||
{
|
{
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -225,6 +225,22 @@ use Exception;
|
||||||
return $this->get_model()->get_last_for_destination_and_user($id_user, $destination);
|
return $this->get_model()->get_last_for_destination_and_user($id_user, $destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get number of sended SMS by day and status between two dates, possibly by sending phone.
|
||||||
|
*
|
||||||
|
* @param int $id_user : user id
|
||||||
|
* @param \DateTime $start_date : Date since which we want the messages
|
||||||
|
* @param \DateTime $end_date : Date until which we want the messages
|
||||||
|
* @param ?int $id_phone : Id of the phone to search sended for, null by default get all phones
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_sended_status_stats ($id_user, $start_date, $end_date, ?int $id_phone = null)
|
||||||
|
{
|
||||||
|
return $this->get_model()->get_sended_status_stats($id_user, $start_date, $end_date, $id_phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a SMS message.
|
* Send a SMS message.
|
||||||
*
|
*
|
||||||
|
|
|
@ -168,6 +168,26 @@ use BenMorel\GsmCharsetConverter\Converter;
|
||||||
return $logo;
|
return $logo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string is a valid PHP date
|
||||||
|
*
|
||||||
|
* @param string $date : Datestring to validate
|
||||||
|
*
|
||||||
|
* @return bool : True if a valid date, false else
|
||||||
|
*/
|
||||||
|
public static function is_valid_date($date)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new \DateTime($date);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (\Exception $e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cette fonction vérifie une date.
|
* Cette fonction vérifie une date.
|
||||||
*
|
*
|
||||||
|
|
|
@ -91,6 +91,10 @@ namespace controllers\publics;
|
||||||
{
|
{
|
||||||
$this->user = $this->internal_user->get_by_api_key($api_key);
|
$this->user = $this->internal_user->get_by_api_key($api_key);
|
||||||
}
|
}
|
||||||
|
elseif ($_SESSION['user'] ?? false)
|
||||||
|
{
|
||||||
|
$this->user = $this->internal_user->get($_SESSION['user']['id']);
|
||||||
|
}
|
||||||
|
|
||||||
if (!$this->user)
|
if (!$this->user)
|
||||||
{
|
{
|
||||||
|
@ -1068,4 +1072,76 @@ namespace controllers\publics;
|
||||||
|
|
||||||
return $this->json($return);
|
return $this->json($return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return statistics about status of sended sms for a period by phone
|
||||||
|
*
|
||||||
|
* @param string $_GET['start'] : Date from which to get sms volume, format Y-m-d H:i:s.
|
||||||
|
* @param string $_GET['end'] : Date up to which to get sms volume, format Y-m-d H:i:s.
|
||||||
|
* @param ?int $_GET['id_phone'] : Id of the phone we want to check the status for. Default to null will return stats for all phone.
|
||||||
|
*
|
||||||
|
* @return : List of entries
|
||||||
|
*/
|
||||||
|
public function get_sms_status_stats()
|
||||||
|
{
|
||||||
|
$start = $_GET['start'] ?? null;
|
||||||
|
$end = $_GET['end'] ?? null;
|
||||||
|
$id_phone = $_GET['id_phone'] ?? null;
|
||||||
|
|
||||||
|
if (!$start || !$end)
|
||||||
|
{
|
||||||
|
$return = self::DEFAULT_RETURN;
|
||||||
|
$return['error'] = self::ERROR_CODES['MISSING_PARAMETER'];
|
||||||
|
$return['message'] = self::ERROR_MESSAGES['MISSING_PARAMETER'] . 'start and end date are required.';
|
||||||
|
$this->auto_http_code(false);
|
||||||
|
|
||||||
|
return $this->json($return);
|
||||||
|
}
|
||||||
|
|
||||||
|
$return = self::DEFAULT_RETURN;
|
||||||
|
|
||||||
|
if (!\controllers\internals\Tool::is_valid_date($start))
|
||||||
|
{
|
||||||
|
$return = self::DEFAULT_RETURN;
|
||||||
|
$return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
|
||||||
|
$return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'start must be a date of format "Y-m-d H:i:s".';
|
||||||
|
$this->auto_http_code(false);
|
||||||
|
|
||||||
|
return $this->json($return);
|
||||||
|
}
|
||||||
|
$start = new \DateTime($start);
|
||||||
|
|
||||||
|
if (!\controllers\internals\Tool::is_valid_date($end))
|
||||||
|
{
|
||||||
|
$return = self::DEFAULT_RETURN;
|
||||||
|
$return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
|
||||||
|
$return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'end must be a date of format "Y-m-d H:i:s".';
|
||||||
|
$this->auto_http_code(false);
|
||||||
|
|
||||||
|
return $this->json($return);
|
||||||
|
}
|
||||||
|
$end = new \DateTime($end);
|
||||||
|
|
||||||
|
if ($id_phone)
|
||||||
|
{
|
||||||
|
$phone = $this->internal_phone->get_for_user($this->user['id'], $id_phone);
|
||||||
|
if (!$phone)
|
||||||
|
{
|
||||||
|
$return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
|
||||||
|
$return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'phone with id ' . $id_phone . ' does not exists.';
|
||||||
|
$this->auto_http_code(false);
|
||||||
|
|
||||||
|
return $this->json($return);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stats = $this->internal_sended->get_sended_status_stats($this->user['id'], $start, $end, $id_phone);
|
||||||
|
|
||||||
|
$return = self::DEFAULT_RETURN;
|
||||||
|
$return['response'] = $stats;
|
||||||
|
$this->auto_http_code(true);
|
||||||
|
|
||||||
|
return $this->json($return);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of RaspiSMS.
|
||||||
|
*
|
||||||
|
* (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
|
||||||
|
*
|
||||||
|
* This source file is subject to the GPL-3.0 license that is bundled
|
||||||
|
* with this source code in the file LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace controllers\publics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics pages
|
||||||
|
*/
|
||||||
|
class Stat extends \descartes\Controller
|
||||||
|
{
|
||||||
|
private $internal_sended;
|
||||||
|
private $internal_phone;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
|
||||||
|
|
||||||
|
$this->internal_sended = new \controllers\internals\Sended($bdd);
|
||||||
|
$this->internal_phone = new \controllers\internals\Phone($bdd);
|
||||||
|
|
||||||
|
\controllers\internals\Tool::verifyconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the stats about sms status for a period by phone
|
||||||
|
*
|
||||||
|
* @return void;
|
||||||
|
*/
|
||||||
|
public function sms_status()
|
||||||
|
{
|
||||||
|
$id_user = $_SESSION['user']['id'];
|
||||||
|
$phones = $this->internal_phone->gets_for_user($id_user);
|
||||||
|
|
||||||
|
$now = new \DateTime();
|
||||||
|
$seven_days_interval = new \DateInterval('P7D');
|
||||||
|
$seven_days_ago = clone($now);
|
||||||
|
$seven_days_ago->sub($seven_days_interval);
|
||||||
|
|
||||||
|
$this->render('stat/sms-status', [
|
||||||
|
'phones' => $phones,
|
||||||
|
'now' => $now,
|
||||||
|
'seven_days_ago' => $seven_days_ago,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -294,6 +294,50 @@ namespace models;
|
||||||
return $result[0] ?? [];
|
return $result[0] ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get number of sended SMS by day and status between two dates, possibly by sending phone.
|
||||||
|
*
|
||||||
|
* @param int $id_user : user id
|
||||||
|
* @param \DateTime $start_date : Date since which we want the messages
|
||||||
|
* @param \DateTime $end_date : Date until which we want the messages
|
||||||
|
* @param ?int $id_phone : Id of the phone to search sended for, null by default get all phones
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_sended_status_stats ($id_user, $start_date, $end_date, ?int $id_phone = null)
|
||||||
|
{
|
||||||
|
$params = [
|
||||||
|
'start_date' => $start_date->format('y-m-d H:i:s'),
|
||||||
|
'end_date' => $end_date->format('y-m-d H:i:s'),
|
||||||
|
'id_user' => $id_user,
|
||||||
|
];
|
||||||
|
|
||||||
|
$query = "
|
||||||
|
SELECT DATE_FORMAT(at, '%Y-%m-%d') as at_ymd, id_phone, status, COUNT(id) as nb
|
||||||
|
FROM sended
|
||||||
|
WHERE id_user = :id_user
|
||||||
|
AND id_phone IS NOT NULL
|
||||||
|
AND at >= :start_date
|
||||||
|
AND at <= :end_date
|
||||||
|
";
|
||||||
|
|
||||||
|
if ($id_phone)
|
||||||
|
{
|
||||||
|
$params['id_phone'] = $id_phone;
|
||||||
|
$query .= "
|
||||||
|
AND id_phone = :id_phone
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$query .= "
|
||||||
|
GROUP BY at_ymd, status, id_phone
|
||||||
|
ORDER BY at_ymd, id_phone, status
|
||||||
|
";
|
||||||
|
|
||||||
|
|
||||||
|
return $this->_run_query($query, $params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return table name.
|
* Return table name.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -202,6 +202,10 @@
|
||||||
'inbound_call' => '/callback/inbound_call/{id_phone}/',
|
'inbound_call' => '/callback/inbound_call/{id_phone}/',
|
||||||
'end_call' => '/callback/end_call/{id_phone}/',
|
'end_call' => '/callback/end_call/{id_phone}/',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'Stat' => [
|
||||||
|
'sms_status' => '/stats/sms-status/',
|
||||||
|
],
|
||||||
|
|
||||||
'Api' => [
|
'Api' => [
|
||||||
'get_entries' => [
|
'get_entries' => [
|
||||||
|
@ -209,6 +213,7 @@
|
||||||
'/api/list/{entry_type}/{page}/',
|
'/api/list/{entry_type}/{page}/',
|
||||||
],
|
],
|
||||||
'get_usage' => '/api/usage/',
|
'get_usage' => '/api/usage/',
|
||||||
|
'get_sms_status_stats' => '/api/stats/sms-status/',
|
||||||
'post_scheduled' => [
|
'post_scheduled' => [
|
||||||
'/api/scheduled/',
|
'/api/scheduled/',
|
||||||
],
|
],
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
<script src="<?php echo HTTP_PWD_JS; ?>/datatables/datatables.min.js"></script>
|
<script src="<?php echo HTTP_PWD_JS; ?>/datatables/datatables.min.js"></script>
|
||||||
<!-- Qrcode lib -->
|
<!-- Qrcode lib -->
|
||||||
<script src="<?php echo HTTP_PWD_JS; ?>/qrcode.min.js"></script>
|
<script src="<?php echo HTTP_PWD_JS; ?>/qrcode.min.js"></script>
|
||||||
|
<!-- Chartjs -->
|
||||||
|
<script src="<?php echo HTTP_PWD_JS; ?>/chart.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<!-- Custom JS -->
|
<!-- Custom JS -->
|
||||||
<script src="<?php echo HTTP_PWD_JS; ?>/custom.js"></script>
|
<script src="<?php echo HTTP_PWD_JS; ?>/custom.js"></script>
|
||||||
|
|
|
@ -122,6 +122,11 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
<?php if (!in_array('stats', json_decode($_SESSION['user']['settings']['hide_menus'], true) ?? [])) { ?>
|
||||||
|
<li <?php echo $page == 'settings' ? 'class="active"' : ''; ?>>
|
||||||
|
<a href="<?php echo \descartes\Router::url('Stat', 'sms_status'); ?>"><i class="fa fa-fw fa-area-chart"></i> Statistiques</a>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
<?php if (!in_array('settings', json_decode($_SESSION['user']['settings']['hide_menus'], true) ?? [])) { ?>
|
<?php if (!in_array('settings', json_decode($_SESSION['user']['settings']['hide_menus'], true) ?? [])) { ?>
|
||||||
<li <?php echo $page == 'settings' ? 'class="active"' : ''; ?>>
|
<li <?php echo $page == 'settings' ? 'class="active"' : ''; ?>>
|
||||||
<a href="<?php echo \descartes\Router::url('Setting', 'show'); ?>"><i class="fa fa-fw fa-cogs"></i> Réglages</a>
|
<a href="<?php echo \descartes\Router::url('Setting', 'show'); ?>"><i class="fa fa-fw fa-cogs"></i> Réglages</a>
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
<?php
|
||||||
|
//Template dashboard
|
||||||
|
|
||||||
|
$this->render('incs/head')
|
||||||
|
?>
|
||||||
|
<div id="wrapper">
|
||||||
|
<?php
|
||||||
|
$this->render('incs/nav', ['page' => 'dashboard'])
|
||||||
|
?>
|
||||||
|
<div id="page-wrapper">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- Page Heading -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<h1 class="page-header">
|
||||||
|
Statistiques <small>Statistiques avancées</small>
|
||||||
|
</h1>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="active">
|
||||||
|
<i class="fa fa-dashboard"></i> Statistiques avancées
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="panel panel-default dashboard-panel-chart">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title"><i class="fa fa-area-chart fa-fw"></i> Status des SMS envoyés par téléphone : </h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<form id="sms-status-form" class="form-inline text-right mb-3" action="" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="id_phone">Téléphone : </label>
|
||||||
|
<div class="form-group">
|
||||||
|
<select id="id_phone" name="id_phone" class="form-control">
|
||||||
|
<option value="">Tous les téléphones</option>
|
||||||
|
<?php foreach ($phones as $phone) { ?>
|
||||||
|
<option value="<?php $this->s($phone['id']); ?>"><?php $this->s($phone['name']); ?></option>
|
||||||
|
<?php } ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group ml-4">
|
||||||
|
<label for="start">Période : </label>
|
||||||
|
<input id="start" name="start" class="form-control form-date auto-width" type="date" value="<?php $this->s($seven_days_ago->format('Y-m-d')) ?>">
|
||||||
|
- <input id="end" name="end" class="form-control form-date auto-width" type="date" value="<?php $this->s($now->format('Y-m-d')) ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" class="btn btn-success ml-4" value="Valider" />
|
||||||
|
</form>
|
||||||
|
<canvas id="bar-chart-sms-status"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- /.row -->
|
||||||
|
</div>
|
||||||
|
<!-- /.container-fluid -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /#page-wrapper -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
smsStatusChart = null;
|
||||||
|
const phones = {};
|
||||||
|
for (const phone of <?= json_encode($phones); ?>) {
|
||||||
|
phones[phone.id] = phone;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function drawChart(e = null) {
|
||||||
|
const startDate = new Date(document.getElementById('start').value);
|
||||||
|
const formatedStartDate = startDate.toISOString().split('T')[0]
|
||||||
|
const endDate = new Date(document.getElementById('end').value);
|
||||||
|
const formatedEndDate = endDate.toISOString().split('T')[0]
|
||||||
|
const id_phone = document.getElementById('id_phone').value;
|
||||||
|
|
||||||
|
let url = `http://127.0.0.1/raspisms/api/stats/sms-status?api_key=a25cc40e2ffcc661170e28dc56724f67&start=${formatedStartDate}&end=${formatedEndDate}`;
|
||||||
|
url = (id_phone ? `${url}&id_phone=${id_phone}` : url );
|
||||||
|
const response = await fetch(url);
|
||||||
|
const data = (await response.json()).response;
|
||||||
|
|
||||||
|
// Get all dates to avoid holes in data
|
||||||
|
const dates = [];
|
||||||
|
let currentDate = new Date(startDate);
|
||||||
|
while (currentDate <= endDate) {
|
||||||
|
const formated_date = (new Date(currentDate)).toISOString().split('T')[0]
|
||||||
|
dates.push(formated_date);
|
||||||
|
currentDate.setDate(currentDate.getDate() + 1);
|
||||||
|
}
|
||||||
|
const empty_dataset = Array(dates.length + 1).fill(0)
|
||||||
|
|
||||||
|
const colors = {'failed': '#d9534f', 'unknown': '#337ab7', 'delivered': '#5cb85c'};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let datasets = {};
|
||||||
|
for (const entry of data) {
|
||||||
|
if (!datasets[entry.id_phone]) {
|
||||||
|
datasets[entry.id_phone] = {
|
||||||
|
'failed': {
|
||||||
|
'data': [...empty_dataset],
|
||||||
|
'label': `Phone ${phones[entry.id_phone]['name']} - Failed`,
|
||||||
|
'backgroundColor': colors['failed'],
|
||||||
|
'stack': entry.id_phone,
|
||||||
|
},
|
||||||
|
'unknown': {
|
||||||
|
'data': [...empty_dataset],
|
||||||
|
'label': `Phone ${phones[entry.id_phone]['name']} - Unknown`,
|
||||||
|
'backgroundColor': colors['unknown'],
|
||||||
|
'stack': entry.id_phone,
|
||||||
|
},
|
||||||
|
'delivered': {
|
||||||
|
'data': [...empty_dataset],
|
||||||
|
'label': `Phone ${phones[entry.id_phone]['name']} - Delivered`,
|
||||||
|
'backgroundColor': colors['delivered'],
|
||||||
|
'stack': entry.id_phone,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const date_index = dates.indexOf(entry.at_ymd);
|
||||||
|
|
||||||
|
// This should never happen, but better be sure
|
||||||
|
if (date_index == -1) {
|
||||||
|
throw Error('Data for a date not in dates array');
|
||||||
|
}
|
||||||
|
|
||||||
|
datasets[entry.id_phone][entry.status]['data'][date_index] = entry.nb;
|
||||||
|
}
|
||||||
|
// Pass all from dict to array
|
||||||
|
const formated_datasets = [];
|
||||||
|
for (const key in datasets) {
|
||||||
|
formated_datasets.push(datasets[key]['failed']);
|
||||||
|
formated_datasets.push(datasets[key]['unknown']);
|
||||||
|
formated_datasets.push(datasets[key]['delivered']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom plugin to display "Pas de données sur cette période"
|
||||||
|
const noDataPlugin = {
|
||||||
|
id: 'noDataPlugin',
|
||||||
|
afterDraw: (chart) => {
|
||||||
|
const datasets = chart.data.datasets;
|
||||||
|
const hasData = datasets.some(dataset => dataset.data.some(value => value !== null && value !== undefined && value !== 0));
|
||||||
|
|
||||||
|
if (!hasData) {
|
||||||
|
const ctx = chart.ctx;
|
||||||
|
const { width, height } = chart;
|
||||||
|
ctx.save();
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.font = '3em Helvetica';
|
||||||
|
ctx.fillText('Pas de données sur cette période', width / 2, height / 2);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the chart
|
||||||
|
const ctx = document.getElementById('bar-chart-sms-status');
|
||||||
|
const config = {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: dates,
|
||||||
|
datasets: formated_datasets,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [noDataPlugin],
|
||||||
|
};
|
||||||
|
|
||||||
|
// On first run create chart, after update
|
||||||
|
if (!smsStatusChart) {
|
||||||
|
smsStatusChart = new Chart(ctx, config);
|
||||||
|
} else {
|
||||||
|
for (const key in config) {
|
||||||
|
smsStatusChart[key] = config[key];
|
||||||
|
}
|
||||||
|
smsStatusChart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery(document).ready(function()
|
||||||
|
{
|
||||||
|
drawChart();
|
||||||
|
});
|
||||||
|
|
||||||
|
jQuery('#sms-status-form').on('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
drawChart();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<!-- /#wrapper -->
|
||||||
|
<?php
|
||||||
|
$this->render('incs/footer');
|
Loading…
Reference in New Issue