mirror of
https://github.com/RaspbianFrance/raspisms.git
synced 2025-04-21 08:56:27 +02:00
Improve dashboard rendering speed by using ajax for graphs. Improve perfs by using more index on query. Add function to find invalid numbers and export as csv
This commit is contained in:
parent
52c849e043
commit
2be8242d5e
16 changed files with 494 additions and 56 deletions
|
@ -121,13 +121,14 @@
|
|||
<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> SMS envoyés depuis le <?= $stats_start_date_formated; ?> : </h3>
|
||||
<span style="color: #5CB85C;">SMS envoyés (moyenne = <?php echo $avg_sendeds; ?> par jour).</span><br/>
|
||||
<span style="color: #5CB85C;">SMS envoyés (moyenne = <span id="avg_sendeds">0</span> par jour).</span><br/>
|
||||
<?php if ($quota_unused) { ?>
|
||||
<br/>
|
||||
<span style="color: #d9534f">Crédits restants : <?= $quota_unused; ?>.</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="morris-bar-chart-sended-loader" class="text-center"><div class="loader"></div></div>
|
||||
<div id="morris-bar-chart-sended"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -139,9 +140,10 @@
|
|||
<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> SMS reçus depuis le <?= $stats_start_date_formated; ?> : </h3>
|
||||
<span style="color: #EDAB4D">SMS reçus (moyenne = <?php echo $avg_receiveds; ?> par jour).</span>
|
||||
<span style="color: #EDAB4D">SMS reçus (moyenne = <span id="avg_receiveds">0</span> par jour).</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div id="morris-bar-chart-received-loader" class="text-center"><div class="loader"></div></div>
|
||||
<div id="morris-bar-chart-received"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -255,18 +257,23 @@
|
|||
|
||||
</div>
|
||||
<script>
|
||||
jQuery(document).ready(function()
|
||||
{
|
||||
async function drawChartSended() {
|
||||
let url = <?= json_encode(\descartes\Router::url('Dashboard', 'stats_sended'))?>;
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
|
||||
document.getElementById('avg_sendeds').textContent = data.avg_sendeds;
|
||||
|
||||
Morris.Bar({
|
||||
element: 'morris-bar-chart-sended',
|
||||
fillOpacity: 0.4,
|
||||
data: <?php echo $data_bar_chart_sended;?>,
|
||||
data: data.data_bar_chart_sended,
|
||||
xkey: 'period',
|
||||
parseTime: false,
|
||||
ykeys: ['sendeds_failed', 'sendeds_unknown', 'sendeds_delivered'],
|
||||
labels: ['SMS échoués', 'SMS inconnus', 'SMS délivrés'],
|
||||
barColors: ['#D9534F', '#337AB7', '#5CB85C'],
|
||||
goals: [<?php echo $avg_sendeds; ?>,],
|
||||
goals: [data.avg_sendeds],
|
||||
goalLineColors: ['#5CB85C'],
|
||||
goalStrokeWidth: 2,
|
||||
pointSize: 4,
|
||||
|
@ -290,22 +297,42 @@
|
|||
}
|
||||
});
|
||||
|
||||
Morris.Bar({
|
||||
document.getElementById('morris-bar-chart-sended-loader').classList.add('hidden');
|
||||
}
|
||||
|
||||
async function drawChartReceived() {
|
||||
let url = <?= json_encode(\descartes\Router::url('Dashboard', 'stats_received'))?>;
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
|
||||
|
||||
document.getElementById('avg_receiveds').textContent = data.avg_receiveds;
|
||||
Morris.Bar({
|
||||
element: 'morris-bar-chart-received',
|
||||
fillOpacity: 0.4,
|
||||
data: <?php echo $data_bar_chart_received;?>,
|
||||
data: data.data_bar_chart_received,
|
||||
xkey: 'period',
|
||||
parseTime: false,
|
||||
ykeys: ['receiveds'],
|
||||
labels: ['SMS reçus'],
|
||||
barColors: ['#EDAB4D'],
|
||||
goals: [<?php echo $avg_receiveds; ?>],
|
||||
goals: [data.avg_receiveds],
|
||||
goalLineColors: ['#EDAB4D'],
|
||||
goalStrokeWidth: 2,
|
||||
pointSize: 4,
|
||||
hideHover: 'auto',
|
||||
resize: true,
|
||||
});
|
||||
|
||||
document.getElementById('morris-bar-chart-received-loader').classList.add('hidden');
|
||||
}
|
||||
|
||||
|
||||
jQuery(document).ready(function()
|
||||
{
|
||||
drawChartSended();
|
||||
drawChartReceived();
|
||||
});
|
||||
</script>
|
||||
<!-- /#wrapper -->
|
||||
|
|
|
@ -64,7 +64,7 @@ jQuery(document).ready(function ()
|
|||
{
|
||||
jQuery('.datatable').DataTable({
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[25, 50, 100, 1000, 10000, -1], [25, 50, 100, 1000, 10000, "All"]],
|
||||
"lengthMenu": [[25, 50, 100, 1000], [25, 50, 100, 1000]],
|
||||
"language": {
|
||||
"url": HTTP_PWD + "/assets/js/datatables/french.json",
|
||||
},
|
||||
|
@ -73,7 +73,6 @@ jQuery(document).ready(function ()
|
|||
'targets': 'checkcolumn',
|
||||
'orderable': false,
|
||||
}],
|
||||
|
||||
"ajax": {
|
||||
'url': '<?php echo \descartes\Router::url('Discussion', 'list_json'); ?>',
|
||||
'dataSrc': 'data',
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<div class="col-lg-12">
|
||||
<h1 class="page-header">
|
||||
Dashboard <small>SMS envoyés</small>
|
||||
<a class="btn btn-warning float-right" id="btn-invalid-numbers" href="#"><span class="fa fa-eraser"></span> Télécharger les numéros invalides</a>
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
|
@ -65,9 +66,122 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" tabindex="-1" id="invalid-numbers-modal">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<form id="invalid-numbers-form" action="<?php $this->s(\descartes\Router::url('Api', 'get_invalid_numbers')); ?>" method="GET">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Télécharger les numéros invalides</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="help">Vous pouvez téléchager une liste de destinataires qui affichent un taux d'erreur anormal selon les critères de votre choix (liste limitée à 25 000 numéros).</p>
|
||||
<div class="form-group">
|
||||
<label>Volume minimum de SMS envoyés au numéros</label>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"><span class="fa fa-arrow-circle-up"></span></span>
|
||||
<input name="volume" class="form-control" type="number" min="1" step="1" placeholder="" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Pourcentage d'échecs minimum</label>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"><span class="fa fa-percent"></span></span>
|
||||
<input name="percent_failed" class="form-control" type="number" min="0" step="1" placeholder="" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Pourcentage d'inconnus minimum</label>
|
||||
<div class="form-group input-group">
|
||||
<span class="input-group-addon"><span class="fa fa-percent"></span></span>
|
||||
<input name="percent_unknown" class="form-control" type="number" min="0" step="1" placeholder="" autofocus required>
|
||||
</div>
|
||||
</div>
|
||||
<div id="invalid-numbers-loader" class="text-center hidden"><div class="loader"></div></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a type="button" class="btn btn-danger" data-dismiss="modal">Annuler</a>
|
||||
<input type="submit" class="btn btn-success" value="Valider" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(document).ready(function ()
|
||||
{
|
||||
jQuery('body').on('click', '#btn-invalid-numbers', function ()
|
||||
{
|
||||
jQuery('#invalid-numbers-modal').modal({'keyboard': true});
|
||||
});
|
||||
|
||||
jQuery('body').on('submit', '#invalid-numbers-form', function (e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
jQuery('#invalid-numbers-loader').removeClass('hidden');
|
||||
|
||||
const form = this;
|
||||
const formData = jQuery(form).serialize();
|
||||
|
||||
let invalidNumbers = []; // Array to store cumulative results
|
||||
|
||||
// Function to fetch data and handle pagination
|
||||
const fetchData = (url, limit = -1, params = null) => {
|
||||
if (params) {
|
||||
url += '?' + params;
|
||||
}
|
||||
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(jsonResponse => {
|
||||
invalidNumbers = invalidNumbers.concat(jsonResponse.response);
|
||||
|
||||
// Check if there is a "next" URL to fetch more data
|
||||
if (jsonResponse.next && limit != 0) {
|
||||
fetchData(jsonResponse.next, limit - 1); // Recursive call for next page
|
||||
} else {
|
||||
exportToCSV(invalidNumbers);
|
||||
jQuery('#invalid-numbers-loader').addClass('hidden');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was a problem with the fetch operation:', error);
|
||||
});
|
||||
};
|
||||
|
||||
// Function to export data to CSV
|
||||
const exportToCSV = (results) => {
|
||||
// Define the CSV headers
|
||||
let csvContent = "Destination,Total SMS Sent,Failed Percentage,Unknown Percentage\n";
|
||||
|
||||
// Append each row of data to the CSV content
|
||||
results.forEach(item => {
|
||||
csvContent += `${item.destination},${item.total_sms_sent},${item.failed_percentage},${item.unknown_percentage}\n`;
|
||||
});
|
||||
|
||||
// Create a downloadable link for the CSV file
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const downloadLink = document.createElement('a');
|
||||
downloadLink.href = url;
|
||||
downloadLink.download = 'invalid_numbers.csv';
|
||||
|
||||
// Trigger download
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink); // Clean up
|
||||
};
|
||||
|
||||
// Initial call to fetch data
|
||||
fetchData(form.action, 1000, formData);
|
||||
});
|
||||
|
||||
jQuery('.datatable').DataTable({
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[25, 50, 100, 1000, 10000, Math.pow(10, 10)], [25, 50, 100, 1000, 10000, "All"]],
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
<input type="submit" class="btn btn-success ml-4" value="Valider" />
|
||||
</form>
|
||||
<canvas id="bar-chart-sms-status"></canvas>
|
||||
<div id="bar-chart-sms-status-loader" class="text-center mb-5"><div class="loader"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -81,9 +82,8 @@
|
|||
const formatedEndDate = endDate.toISOString().split('T')[0]
|
||||
const id_phone = document.getElementById('id_phone').value;
|
||||
|
||||
const query_infos = <?= json_encode(['url' => \descartes\Router::url('Api', 'get_sms_status_stats')])?>;
|
||||
|
||||
let url = `${query_infos.url}?start=${formatedStartDate}&end=${formatedEndDate}`;
|
||||
let url = <?= json_encode(\descartes\Router::url('Api', 'get_sms_status_stats'))?>;
|
||||
url += `?start=${formatedStartDate}&end=${formatedEndDate}`;
|
||||
url += id_phone ? `&id_phone=${id_phone}` : '';
|
||||
const response = await fetch(url);
|
||||
const data = (await response.json()).response;
|
||||
|
@ -187,6 +187,8 @@
|
|||
plugins: [noDataPlugin],
|
||||
};
|
||||
|
||||
document.getElementById('bar-chart-sms-status-loader').classList.add('hidden');
|
||||
|
||||
// On first run create chart, after update
|
||||
if (!smsStatusChart) {
|
||||
smsStatusChart = new Chart(ctx, config);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue