Skip to content
This repository was archived by the owner on Jul 12, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 192 additions & 83 deletions cmd/server/assets/realmadmin/_stats_codes.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,31 @@
Codes issued, claimed, & invalid
</div>
<div id="dashboard_div">
<div id="chart_div" class="h-100 w-100" style="min-height:325px;">
<div id="realm_chart_div" class="h-100 w-100" style="min-height:325px;">
<p class="text-center font-italic w-100 mt-5">Loading chart...</p>
</div>
<div id="filter_div" class="text-right" style="height: 75px;"></div>
</div>

<div class="card-body">
<h5 class="card-title">Issue to claim age distribution <span class="sum-title">(7 day sum)</span></h5>
<h5 class="card-title">Issue-to-claim age distribution <span class="sum-title">(7 day sum)</span></h5>
</div>
<div id="issue_age_dist_chart_div" class="h-100 w-100" style="min-height:325px;">
<p class="text-center font-italic w-100 mt-5">Loading chart...</p>
</div>
<div class="slidecontainer" id="issue_age_slide_div">
</div>

<div class="card-body">
<h5 class="card-title">Mean issue-to-claim age</h5>
</div>
<div id="mean_claim_age_div">
<div id="mean_claim_age_chart_div" class="h-100 w-100" style="min-height:325px;">
<p class="text-center font-italic w-100 mt-5">Loading chart...</p>
</div>
<div id="mean_claim_age_filter_div" class="text-right" style="height: 75px;"></div>
</div>

<small class="card-footer d-flex justify-content-between text-muted">
<a href="#" data-toggle="modal" data-target="#realm-codes-modal">Learn more about this chart</a>
<span>
Expand Down Expand Up @@ -104,110 +116,206 @@ <h5 class="modal-title">Codes issued, claimed, &amp; invalid</h5>
dataType: 'json',
})
.done(function(data, status, xhr) {
if (data.statistics) {
$issueAgeSlider.attr('min', Math.min(smoothing, data.statistics.length));
$issueAgeSlider.attr('max', data.statistics.length);
$issueAgeSlider.val(data.statistics.length);
}

drawClaimChart(data);
drawMeanClaimAgeChart(data);
drawClaimAgeChart(data);
}).fail(function(xhr, status, err) {
flash.error('Failed to render realm stats: ' + err);
});
}

function drawClaimChart(data) {
let $realmChartDiv = $('#realm_chart');
function drawClaimChart(data) {
let $realmChartDiv = $('#realm_chart_div');

if (!data.statistics) {
$realmChartDiv.find('p').text('No data yet.');
return;
}
if (!data.statistics) {
$realmChartDiv.find('p').text('No data yet.');
return;
}

let tenDaysAgo = new Date(data.statistics[data.statistics.length-10].date);
let tenDaysAgo = new Date(data.statistics[data.statistics.length-10].date);

let dataTable = new google.visualization.DataTable();
dataTable.addColumn('date', 'Date');
dataTable.addColumn('number', 'Issued');
dataTable.addColumn('number', 'Claimed');
dataTable.addColumn('number', 'Invalid');
let dataTable = new google.visualization.DataTable();
dataTable.addColumn('date', 'Date');
dataTable.addColumn('number', 'Issued');
dataTable.addColumn('number', 'Claimed');
dataTable.addColumn('number', 'Invalid');

data.statistics.reverse().forEach(function(row) {
dataTable.addRow([utcDate(row.date), row.data.codes_issued, row.data.codes_claimed, row.data.codes_invalid]);
});
data.statistics.reverse().forEach(function(row) {
dataTable.addRow([utcDate(row.date), row.data.codes_issued, row.data.codes_claimed, row.data.codes_invalid]);
});

let dateFormatter = new google.visualization.DateFormat({
pattern: 'MMM dd',
});
dateFormatter.format(dataTable, 0);
let dateFormatter = new google.visualization.DateFormat({
pattern: 'MMM dd',
});
dateFormatter.format(dataTable, 0);

let dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div'));
let dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div'));

let filter = new google.visualization.ControlWrapper({
controlType: 'ChartRangeFilter',
containerId: 'filter_div',
state: {
range: {
start: tenDaysAgo,
},
},
options: {
filterColumnIndex: 0,
series: {
0: {
opacity: 0,
}
let filter = new google.visualization.ControlWrapper({
controlType: 'ChartRangeFilter',
containerId: 'filter_div',
state: {
range: {
start: tenDaysAgo,
},
},
ui: {
chartType: 'LineChart',
chartOptions: {
colors: ['#dddddd'],
chartArea: {
width: '100%',
height: '100%',
top: 0,
right: 40,
bottom: 20,
left: 60,
},
hAxis: { format: 'M/d' },
options: {
filterColumnIndex: 0,
series: {
0: {
opacity: 0,
}
},
chartView: {
columns: [0,1],
ui: {
chartType: 'LineChart',
chartOptions: {
colors: ['#dddddd'],
chartArea: {
width: '100%',
height: '100%',
top: 0,
right: 40,
bottom: 20,
left: 60,
},
hAxis: { format: 'M/d' },
},
chartView: {
columns: [0,1],
},
},
},
});

let realmChart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'realm_chart_div',
options: {
colors: ['#007bff', '#28a745', '#dc3545'],
chartArea: {
left: 60,
right: 40,
bottom: 5,
top: 40,
width: '100%',
height: '300',
},
hAxis: { textPosition: 'none' },
legend: { position: 'top' },
width: '100%',
},
});

let realmChart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'chart_div',
options: {
colors: ['#007bff', '#28a745', '#dc3545'],
chartArea: {
left: 60,
right: 40,
bottom: 5,
top: 40,
dashboard.bind(filter, realmChart);
dashboard.draw(dataTable);
chartData.push({
chart: dashboard,
data: dataTable,
});
}

function drawMeanClaimAgeChart(data) {
let $realmChartDiv = $('#mean_claim_age_chart_div');

if (!data.statistics) {
$realmChartDiv.find('p').text('No data yet.');
return;
}

let tenDaysAgo = new Date(data.statistics[data.statistics.length-10].date);

let dataTable = new google.visualization.DataTable();
dataTable.addColumn('date', 'Date');
dataTable.addColumn('number', 'Mean issue-claim age');

data.statistics.reverse().forEach(function(row) {
dataTable.addRow([utcDate(row.date), row.data.code_claim_mean_age_seconds]);
});

let dateFormatter = new google.visualization.DateFormat({
pattern: 'MMM dd',
});
dateFormatter.format(dataTable, 0);

let dashboard = new google.visualization.Dashboard(document.getElementById('mean_claim_age_chart_div'));

let filter = new google.visualization.ControlWrapper({
controlType: 'ChartRangeFilter',
containerId: 'mean_claim_age_filter_div',
state: {
range: {
start: tenDaysAgo,
},
},
options: {
filterColumnIndex: 0,
series: {
0: {
opacity: 0,
}
},
ui: {
chartType: 'LineChart',
chartOptions: {
colors: ['#dddddd'],
chartArea: {
width: '100%',
height: '100%',
top: 0,
right: 40,
bottom: 20,
left: 60,
},
hAxis: { format: 'M/d' },
},
chartView: {
columns: [0,1],
},
},
},
});

let realmChart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'mean_claim_age_chart_div',
options: {
colors: ['#007bff'],
chartArea: {
left: 60,
right: 40,
bottom: 5,
top: 40,
width: '100%',
height: '300',
},
hAxis: { textPosition: 'none' },
vAxis: { title: 'Seconds' },
legend: { position: 'top' },
width: '100%',
height: '300',
},
hAxis: { textPosition: 'none' },
legend: { position: 'top' },
width: '100%',
},
});
});

dashboard.bind(filter, realmChart);
dashboard.draw(dataTable);
chartData.push({
chart: dashboard,
data: dataTable,
});
}
dashboard.bind(filter, realmChart);
dashboard.draw(dataTable);
chartData.push({
chart: dashboard,
data: dataTable,
});
}

let ageChart;
let ageOptions;
let ageData;
function drawClaimAgeChart(data) {
let $div = $('#issue_age_dist_chart_div');

if (!data.statistics || !data.statistics[0].code_claim_age_distribution) {
if (!data.statistics || !data.statistics[0].data.code_claim_age_distribution) {
$div.find('p').text('No data yet.');
return;
}
Expand All @@ -227,7 +335,8 @@ <h5 class="modal-title">Codes issued, claimed, &amp; invalid</h5>
hAxis: {
title: 'Days from issue to claim',
gridlines: { color: 'transparent' },
ticks: [1,2,3,4,5,6,7,8,9,10],
ticks: [{v:1, f:'1m'}, {v:2, f:'5m'}, {v:3, f:'15m'}, {v:4, f:'30m'}, {v:5, f:'1h'},
{v:6, f:'2h'}, {v:7, f:'3h'}, {v:8, f:'6h'}, {v:9, f:'24h'}],
showTextEvery: 1,
},
titlePosition: "out",
Expand All @@ -252,10 +361,10 @@ <h5 class="modal-title">Codes issued, claimed, &amp; invalid</h5>
dataTable.addColumn({type:'string', role:'annotation'})

// sum over last ${smoothing} days
let table = new Array(claimStats[dateIndex].code_claim_age_distribution.length).fill(0);
let table = new Array(claimStats[dateIndex].data.code_claim_age_distribution.length).fill(0);
let i;
for (i = dateIndex; i <= dateIndex + smoothing && i < claimStats.length; i++) {
let row = claimStats[i];
let row = claimStats[i].data;
let j;
for (j = 0; j < row.code_claim_age_distribution.length; j++) {
table[j] += row.code_claim_age_distribution[j];
Expand All @@ -264,7 +373,7 @@ <h5 class="modal-title">Codes issued, claimed, &amp; invalid</h5>

for (i = 0; i < table.length; i++) {
let r = [i+1, table[i],"",""]
if (i == 11) {
if (i == 9) {
r[2] = "#6c757d";
r[3] = ">1 day";
}
Expand All @@ -279,15 +388,15 @@ <h5 class="modal-title">Codes issued, claimed, &amp; invalid</h5>
if (!claimStats) {
return
}
let indx = stats.length - $issueAgeSlider.val();
onsetOptions.title = `${smoothing} days from ` + stats[indx].day.split("T")[0];
onsetOptions.animation = {
let indx = claimStats.length - $issueAgeSlider.val();
ageOptions.title = `${smoothing} days from ` + claimStats[indx].date.split("T")[0];
ageOptions.animation = {
startup: false,
duration: 500,
easing: 'inAndOut',
};

ageData = getClaimAgeDataTable(0);
ageData = getClaimAgeDataTable(indx);
ageChart.draw(ageData, ageOptions);
});
});
Expand Down
Loading