Stats page improvements
Various improvements to the stats page: Added a legend of top chart items to the footer of bands and modes charts. Fixes #408 Added a (cached) count of decoded frames to the Satellites table. Fixes #400 Added a note about the cached nature of stats. Fixes #403 Signed-off-by: Corey Shields <cshields@gmail.com>spacecruft
parent
2e25763185
commit
0dbb1df42d
|
@ -7,7 +7,7 @@ from decimal import Decimal
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models import Count, Max
|
from django.db.models import Count, Max, Q
|
||||||
from django.utils.timezone import make_aware
|
from django.utils.timezone import make_aware
|
||||||
from influxdb import InfluxDBClient
|
from influxdb import InfluxDBClient
|
||||||
from satnogsdecoders import __version__ as decoders_version
|
from satnogsdecoders import __version__ as decoders_version
|
||||||
|
@ -308,6 +308,8 @@ def cache_statistics():
|
||||||
satellites = Satellite.objects \
|
satellites = Satellite.objects \
|
||||||
.values('name', 'norad_cat_id', 'id') \
|
.values('name', 'norad_cat_id', 'id') \
|
||||||
.annotate(count=Count('telemetry_data'),
|
.annotate(count=Count('telemetry_data'),
|
||||||
|
decoded=Count('telemetry_data',
|
||||||
|
filter=Q(telemetry_data__is_decoded=True)),
|
||||||
latest_payload=Max('telemetry_data__timestamp')) \
|
latest_payload=Max('telemetry_data__timestamp')) \
|
||||||
.order_by('-count')
|
.order_by('-count')
|
||||||
|
|
||||||
|
|
|
@ -1,53 +1,53 @@
|
||||||
/* global Chart */
|
/* global Chart */
|
||||||
/* eslint new-cap: "off" */
|
/* eslint new-cap: "off" */
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
$('#sats').DataTable( {
|
$('#sats').DataTable({
|
||||||
// the dom field controls the layout and visibility of datatable items
|
// the dom field controls the layout and visibility of datatable items
|
||||||
// and is not intuitive at all. Without layout we have dom: 'ftrilp'
|
// and is not intuitive at all. Without layout we have dom: 'ftrilp'
|
||||||
// https://datatables.net/reference/option/dom
|
// https://datatables.net/reference/option/dom
|
||||||
dom: '<"row"<"d-none d-md-block col-md-6"><"col-sm-12 col-md-6"f>>' +
|
dom: '<"row"<"d-none d-md-block col-md-6"><"col-sm-12 col-md-6"f>>' +
|
||||||
'<"row"<"col-sm-12"tr>>' +
|
'<"row"<"col-sm-12"tr>>' +
|
||||||
'<"row"<"col-sm-12 col-xl-3 align-self-center"i><"col-sm-12 col-md-6 col-xl-3 align-self-center"l><"col-sm-12 col-md-6 col-xl-6"p>>',
|
'<"row"<"col-sm-12 col-xl-3 align-self-center"i><"col-sm-12 col-md-6 col-xl-3 align-self-center"l><"col-sm-12 col-md-6 col-xl-6"p>>',
|
||||||
responsive: {
|
responsive: {
|
||||||
details: {
|
details: {
|
||||||
display: $.fn.dataTable.Responsive.display.childRow,
|
display: $.fn.dataTable.Responsive.display.childRow,
|
||||||
type: 'column'
|
type: 'column'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
{
|
{
|
||||||
className: 'control',
|
className: 'control',
|
||||||
orderable: false,
|
orderable: false,
|
||||||
targets: 0
|
targets: 0
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
order: [ 3, 'desc' ],
|
order: [3, 'desc'],
|
||||||
pageLength: 25
|
pageLength: 25
|
||||||
} );
|
});
|
||||||
|
|
||||||
$('#stations').DataTable( {
|
$('#stations').DataTable({
|
||||||
// the dom field controls the layout and visibility of datatable items
|
// the dom field controls the layout and visibility of datatable items
|
||||||
// and is not intuitive at all. Without layout we have dom: 'ftrilp'
|
// and is not intuitive at all. Without layout we have dom: 'ftrilp'
|
||||||
// https://datatables.net/reference/option/dom
|
// https://datatables.net/reference/option/dom
|
||||||
dom: '<"row"<"d-none d-md-block col-md-6"><"col-sm-12 col-md-6"f>>' +
|
dom: '<"row"<"d-none d-md-block col-md-6"><"col-sm-12 col-md-6"f>>' +
|
||||||
'<"row"<"col-sm-12"tr>>' +
|
'<"row"<"col-sm-12"tr>>' +
|
||||||
'<"row"<"col-sm-12 col-xl-3 align-self-center"i><"col-sm-12 col-md-6 col-xl-3 align-self-center"l><"col-sm-12 col-md-6 col-xl-6"p>>',
|
'<"row"<"col-sm-12 col-xl-3 align-self-center"i><"col-sm-12 col-md-6 col-xl-3 align-self-center"l><"col-sm-12 col-md-6 col-xl-6"p>>',
|
||||||
responsive: {
|
responsive: {
|
||||||
details: {
|
details: {
|
||||||
display: $.fn.dataTable.Responsive.display.childRow,
|
display: $.fn.dataTable.Responsive.display.childRow,
|
||||||
type: 'column'
|
type: 'column'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
{
|
{
|
||||||
className: 'control',
|
className: 'control',
|
||||||
orderable: false,
|
orderable: false,
|
||||||
targets: 0
|
targets: 0
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
order: [ 2, 'desc' ],
|
order: [2, 'desc'],
|
||||||
pageLength: 25
|
pageLength: 25
|
||||||
} );
|
});
|
||||||
|
|
||||||
Chart.pluginService.register({
|
Chart.pluginService.register({
|
||||||
afterUpdate: function (chart) {
|
afterUpdate: function (chart) {
|
||||||
|
@ -115,7 +115,7 @@ $(document).ready(function() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
$.getJSON('/statistics/', function(data) {
|
$.getJSON('/statistics/', function (data) {
|
||||||
if (data.length == 0) {
|
if (data.length == 0) {
|
||||||
$('#transmitters-charts h2').text('still calculating...');
|
$('#transmitters-charts h2').text('still calculating...');
|
||||||
$('#transmitters-charts div').append('<p>please come back later</p>');
|
$('#transmitters-charts div').append('<p>please come back later</p>');
|
||||||
|
@ -139,9 +139,9 @@ $(document).ready(function() {
|
||||||
for (i = 0; i < data.mode_label.length; i++) {
|
for (i = 0; i < data.mode_label.length; i++) {
|
||||||
// Switching to HSL to stick with hue of LSF logo
|
// Switching to HSL to stick with hue of LSF logo
|
||||||
h = 235;
|
h = 235;
|
||||||
l = data.mode_data[i]/mode_total*100;
|
l = data.mode_data[i] / mode_total * 100;
|
||||||
l *= 3; // adjust for better visibility
|
l *= 3; // adjust for better visibility
|
||||||
s = data.mode_data[i]/mode_total*100;
|
s = data.mode_data[i] / mode_total * 100;
|
||||||
s *= 3; // adjust for better visibility
|
s *= 3; // adjust for better visibility
|
||||||
color = 'hsl(' + h + ',' + Math.floor(s) + '%,' + Math.floor(l) + '%)';
|
color = 'hsl(' + h + ',' + Math.floor(s) + '%,' + Math.floor(l) + '%)';
|
||||||
mode_colors.push(color);
|
mode_colors.push(color);
|
||||||
|
@ -151,9 +151,9 @@ $(document).ready(function() {
|
||||||
var band_colors = [];
|
var band_colors = [];
|
||||||
for (i = 0; i < data.band_label.length; i++) {
|
for (i = 0; i < data.band_label.length; i++) {
|
||||||
h = 235;
|
h = 235;
|
||||||
l = data.band_data[i]/band_total*100;
|
l = data.band_data[i] / band_total * 100;
|
||||||
l *= 1.25; // adjust for better visibility
|
l *= 1.25; // adjust for better visibility
|
||||||
s = data.band_data[i]/band_total*100;
|
s = data.band_data[i] / band_total * 100;
|
||||||
s *= 1.25; // adjust for better visibility
|
s *= 1.25; // adjust for better visibility
|
||||||
color = 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
color = 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
||||||
band_colors.push(color);
|
band_colors.push(color);
|
||||||
|
@ -161,13 +161,11 @@ $(document).ready(function() {
|
||||||
|
|
||||||
// Global chart configuration
|
// Global chart configuration
|
||||||
Chart.defaults.global.legend.display = false;
|
Chart.defaults.global.legend.display = false;
|
||||||
Chart.defaults.global.title.display = true;
|
Chart.defaults.global.title.display = false;
|
||||||
Chart.defaults.global.title.fontSize = 16;
|
|
||||||
Chart.defaults.global.title.fontColor= '#444';
|
|
||||||
|
|
||||||
//Mode Chart
|
//Mode Chart
|
||||||
var mode_c = document.getElementById('modes');
|
var mode_c = document.getElementById('modes');
|
||||||
new Chart(mode_c, {
|
var mode_chart = new Chart(mode_c, {
|
||||||
type: 'doughnut',
|
type: 'doughnut',
|
||||||
data: {
|
data: {
|
||||||
labels: data.mode_label,
|
labels: data.mode_label,
|
||||||
|
@ -189,13 +187,29 @@ $(document).ready(function() {
|
||||||
minFontSize: 1,
|
minFontSize: 1,
|
||||||
maxFontSize: 20,
|
maxFontSize: 20,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
legend: false,
|
||||||
|
legendCallback: function(chart) {
|
||||||
|
var legendHtml = [];
|
||||||
|
legendHtml.push('<dl class="row my-0">');
|
||||||
|
var item = chart.data.datasets[0];
|
||||||
|
for (var i=0; i < item.data.length; i++) {
|
||||||
|
if (item.data[i] > 9) {
|
||||||
|
legendHtml.push('<dt class="col-sm-8">' + chart.data.labels[i] + '</dt>');
|
||||||
|
legendHtml.push('<dd class="col-sm-4 my-0">' + item.data[i] + '</dd>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
legendHtml.push('</dl>');
|
||||||
|
return legendHtml.join('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$('#modes-footer').html(mode_chart.generateLegend());
|
||||||
|
|
||||||
//Band Chart
|
//Band Chart
|
||||||
var band_c = document.getElementById('bands');
|
var band_c = document.getElementById('bands');
|
||||||
new Chart(band_c, {
|
var band_chart = new Chart(band_c, {
|
||||||
type: 'doughnut',
|
type: 'doughnut',
|
||||||
data: {
|
data: {
|
||||||
labels: data.band_label,
|
labels: data.band_label,
|
||||||
|
@ -217,9 +231,25 @@ $(document).ready(function() {
|
||||||
minFontSize: 1,
|
minFontSize: 1,
|
||||||
maxFontSize: 20,
|
maxFontSize: 20,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
legend: false,
|
||||||
|
legendCallback: function(chart) {
|
||||||
|
var legendHtml = [];
|
||||||
|
legendHtml.push('<dl class="row my-0">');
|
||||||
|
var item = chart.data.datasets[0];
|
||||||
|
for (var i=0; i < item.data.length; i++) {
|
||||||
|
if (item.data[i] > 9) {
|
||||||
|
legendHtml.push('<dt class="col-sm-8">' + chart.data.labels[i] + '</dt>');
|
||||||
|
legendHtml.push('<dd class="col-sm-4 my-0">' + item.data[i] + '</dd>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
legendHtml.push('</dl>');
|
||||||
|
return legendHtml.join('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$('#bands-footer').html(band_chart.generateLegend());
|
||||||
|
|
||||||
//HUD Stats
|
//HUD Stats
|
||||||
$('#stats-alive').html(data.transmitters_alive);
|
$('#stats-alive').html(data.transmitters_alive);
|
||||||
|
@ -227,7 +257,7 @@ $(document).ready(function() {
|
||||||
$('#stats-satellites').html(data.total_satellites);
|
$('#stats-satellites').html(data.total_satellites);
|
||||||
$('#stats-data').html(data.total_data);
|
$('#stats-data').html(data.total_data);
|
||||||
}
|
}
|
||||||
}).fail(function() {
|
}).fail(function () {
|
||||||
$('.transmitters-charts').hide();
|
$('.transmitters-charts').hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
<!-- Dashboard panel -->
|
<!-- Dashboard panel -->
|
||||||
<div class="tab-pane fade show active" id="dashboard" role="tabpanel" aria-labelledby="dashboard-tab">
|
<div class="tab-pane fade show active" id="dashboard" role="tabpanel" aria-labelledby="dashboard-tab">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
<div class="row px-2 pb-2 text-sm">Please note that for performance reasons, some statistics may not be real time.</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
<div class="small-box small-box-satnogs">
|
<div class="small-box small-box-satnogs">
|
||||||
|
@ -108,6 +109,7 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<canvas id="modes" width="300" height="300"></canvas>
|
<canvas id="modes" width="300" height="300"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-footer text-sm" id="modes-footer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-xs-12 col-md-4">
|
||||||
|
@ -118,6 +120,7 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<canvas id="bands" width="300" height="300"></canvas>
|
<canvas id="bands" width="300" height="300"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-footer text-sm" id="bands-footer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -134,6 +137,7 @@
|
||||||
<th>Norad ID</th>
|
<th>Norad ID</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Data</th>
|
<th>Data</th>
|
||||||
|
<th>Decoded</th>
|
||||||
<th>Latest</th>
|
<th>Latest</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -148,6 +152,7 @@
|
||||||
<td>{{sat.name}}</td>
|
<td>{{sat.name}}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>{{sat.count}}</td>
|
<td>{{sat.count}}</td>
|
||||||
|
<td>{{sat.decoded}}</td>
|
||||||
<td>{{sat.latest_payload|date:"Y-m-d H:i:s"}}</td>
|
<td>{{sat.latest_payload|date:"Y-m-d H:i:s"}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
Loading…
Reference in New Issue