mirror of
https://github.com/sartography/uva-covid19-testing-communicator.git
synced 2025-02-24 12:58:05 +00:00
406 lines
12 KiB
HTML
406 lines
12 KiB
HTML
<!-- Header -->
|
|
<script src="{{url_for('static', filename='assets/js/graph.js')}}"></script>
|
|
|
|
<!-- Page content -->
|
|
<div class="container-fluid mt--7">
|
|
<div class="row">
|
|
<div class="col-xl-8 mb-4 mb-xl-0">
|
|
<div class="card bg-gradient-default shadow">
|
|
<div class="card-header bg-transparent">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h6 class="text-uppercase text-light ls-1 mb-1">Overview</h6>
|
|
<h2 id="chart-title" class="text-white mb-0">Location Activity</h2>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
|
|
<!-- Chart -->
|
|
<div class="chart">
|
|
<canvas id="data-chart" class="chart-canvas"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-4 mb-xl-0">
|
|
<div class="card shadow">
|
|
|
|
<form action="{{ action }}" method="post" id="pageForm">
|
|
{{ form.csrf_token() }}
|
|
<div class="card-header border-0">
|
|
<div class="row align-items-center">
|
|
<div class="card-header border-0">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h3 class="mb-0">Search</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="btn btn-sm btn-primary"
|
|
onclick="$('input[name=\'dateRange\']').val(getToday()+ ' - ' +getToday());submitform();">Today</button>
|
|
|
|
<button class="btn btn-sm btn-primary"
|
|
onclick="$('input[name=\'dateRange\']').val('10/05/2020 - ' + getToday());submitform();">All</button>
|
|
|
|
<button type="submit" class="btn btn-sm btn-primary">Run</button>
|
|
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<!-- Projects table -->
|
|
<table class="table align-items-center table-flush">
|
|
<tbody>
|
|
<input type="hidden" name="currently_showing" />
|
|
{% for field in form if field.name != "csrf_token" %}
|
|
<tr>
|
|
|
|
<div class="form-field {{ field.widget.input_type }}">
|
|
<td>
|
|
<div class="form-field-label">{{ field.label() }}:</div>
|
|
</td>
|
|
<td>
|
|
{% if field.name == "dateRange" %}
|
|
<input type="text" name="{{field.name}}" />
|
|
{%else%}
|
|
<div class="form-field-input">{{ field }}</div>
|
|
|
|
{% for error in field.errors %}
|
|
<div class="form-field-error">{{ error }}</div>
|
|
|
|
{% endfor %}
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="row mt-4">
|
|
<div class="col mb-5 mb-xl-0">
|
|
<div class="card shadow">
|
|
<div class="card-header border-0">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h3 class="mb-0">Additional Stats:</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body row">
|
|
<div class="chart col">
|
|
<canvas id="week-chart" class="chart-canvas"></canvas>
|
|
</div>
|
|
<div class="chart col">
|
|
<canvas id="time-chart" class="chart-canvas"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-4">
|
|
<div class="col mb-5 mb-xl-0">
|
|
<div class="card shadow">
|
|
<div class="card-header border-0">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h3 class="mb-0">Records to be processed</h3>{{pagination.info}}
|
|
</div>
|
|
<div class="col text-right">
|
|
<a href="{{ url_for('index') }}?download=true" class="btn btn-sm btn-primary">Download all</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
|
|
<!-- Projects table -->
|
|
{{ table }}
|
|
</div>
|
|
<div class="row justify-content-center">
|
|
{{ pagination.links }}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
|
|
<script>
|
|
var color_idx = 1;
|
|
var colors = {};
|
|
function getColor(key) {
|
|
if (!(key in colors)) {
|
|
const hue = color_idx * 137.508; // use golden angle approximation
|
|
if (color_idx == 15) {
|
|
color_idx = 1;
|
|
} else {
|
|
color_idx += 1;
|
|
}
|
|
colors[key] = `hsl(${Math.floor(hue)},100%,75%)`;
|
|
}
|
|
return colors[key];
|
|
}
|
|
|
|
function dict2datasets(data) {
|
|
var datasets = [];
|
|
Object.keys(data).forEach(function (key) {
|
|
datasets.push(
|
|
{
|
|
label: key,
|
|
fill: true,
|
|
borderColor: getColor(key),
|
|
borderSkipped: "bottom",
|
|
borderWidth: 4,
|
|
borderCapStyle: 'butt',
|
|
data: data[key],
|
|
})
|
|
});
|
|
return datasets;
|
|
}
|
|
|
|
var stacked_sum_labels = {
|
|
formatter: (value, ctx) => {
|
|
let datasets = ctx.chart.data.datasets;
|
|
if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
|
|
let sum = 0;
|
|
datasets.map(dataset => {
|
|
sum += dataset.data[ctx.dataIndex];
|
|
});
|
|
return sum;
|
|
}
|
|
else {
|
|
return '';
|
|
}
|
|
},
|
|
anchor: 'end',
|
|
align: 'end'
|
|
}
|
|
|
|
var stacked_bar_scale = {
|
|
xAxes: [{
|
|
ticks: {
|
|
beginAtZero: true
|
|
},
|
|
stacked: true
|
|
}],
|
|
yAxes: [{
|
|
ticks: {
|
|
beginAtZero: true
|
|
},
|
|
stacked: true
|
|
}]
|
|
};
|
|
function getToday() {
|
|
var today = new Date();
|
|
var dd = String(today.getDate()).padStart(2, '0');
|
|
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
|
|
var yyyy = today.getFullYear();
|
|
|
|
today = mm + '/' + dd + '/' + yyyy;
|
|
return today
|
|
}
|
|
/////////////////////////////
|
|
|
|
var overall_charts = {};
|
|
overall_charts.daily = dict2datasets(JSON.parse('{{overall_chart_data.daily | tojson }}'));
|
|
overall_charts.hourly = dict2datasets(JSON.parse('{{overall_chart_data.hourly | tojson }}'));
|
|
overall_charts.weekday = dict2datasets(JSON.parse('{{overall_chart_data.weekday | tojson }}'));
|
|
|
|
var hourly_charts = {};
|
|
var hourly_charts_data = JSON.parse('{{hourly_charts_data | tojson }}');
|
|
|
|
Object.keys(hourly_charts_data).forEach(function (location) {
|
|
hourly_charts[location] = dict2datasets(hourly_charts_data[location])
|
|
});
|
|
|
|
var weekday_charts = {};
|
|
var weekday_charts_data = JSON.parse('{{weekday_charts_data | tojson }}');
|
|
|
|
Object.keys(weekday_charts_data).forEach(function (location) {
|
|
weekday_charts[location] = dict2datasets(weekday_charts_data[location])
|
|
});
|
|
|
|
var location_charts = {};
|
|
var location_chart_data = JSON.parse('{{daily_charts_data | tojson }}');
|
|
|
|
Object.keys(location_chart_data).forEach(function (station) {
|
|
location_charts[station] = dict2datasets(location_chart_data[station])
|
|
});
|
|
|
|
var overall_totals_data = JSON.parse('{{overall_totals_data | tojson }}');
|
|
var location_stats_data = JSON.parse('{{location_stats_data | tojson }}');
|
|
|
|
var per_weekday = new Chart(document.getElementById('week-chart').getContext('2d'), {
|
|
type: 'horizontalBar',
|
|
data: {
|
|
labels: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"],
|
|
datasets: overall_charts.weekday
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
legend: {
|
|
display: false
|
|
},
|
|
scales: stacked_bar_scale,
|
|
title: {
|
|
display: true,
|
|
text: 'Average Count By Weekday For {{dates.search}}'
|
|
},
|
|
|
|
plugins: {
|
|
|
|
datalabels: stacked_sum_labels
|
|
}, layout: {
|
|
padding: {
|
|
left: 0,
|
|
right: 50,
|
|
top: 0,
|
|
bottom: 0
|
|
}
|
|
}
|
|
|
|
}
|
|
});
|
|
per_weekday.options.plugins.datalabels.color = "black";
|
|
per_weekday.update();
|
|
|
|
var per_hour = new Chart(document.getElementById('time-chart').getContext('2d'), {
|
|
type: 'horizontalBar',
|
|
data: {
|
|
labels: ["1 AM", "2 AM", "3 AM", "4 AM", "5 AM", "6 AM", "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 AM", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM", "12 PM",],
|
|
datasets: overall_charts.hourly
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: stacked_bar_scale,
|
|
legend: {
|
|
display: false
|
|
},
|
|
title: {
|
|
text: 'Average Count By Hour For {{dates.search}}'
|
|
},
|
|
plugins: {
|
|
datalabels: stacked_sum_labels
|
|
}, layout: {
|
|
padding: {
|
|
left: 0,
|
|
right: 50,
|
|
top: 0,
|
|
bottom: 0
|
|
}
|
|
}
|
|
}
|
|
});
|
|
per_hour.options.plugins.datalabels.color = "black";
|
|
per_hour.update();
|
|
|
|
var per_location = new Chart(document.getElementById('data-chart').getContext('2d'), {
|
|
// The type of chart we want to create
|
|
type: 'bar',
|
|
data: {
|
|
datasets: overall_charts.daily,
|
|
labels: JSON.parse('{{chart_ticks | tojson }}')
|
|
},
|
|
// Configuration options go here
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: stacked_bar_scale,
|
|
title: {
|
|
display: false,
|
|
},
|
|
legend: {
|
|
display: true,
|
|
position: "right",
|
|
onClick: location_legend,
|
|
labels: {
|
|
usePointStyle: true,
|
|
fontColor: '#FFFFFF',
|
|
fontSize: 15,
|
|
padding: 20,
|
|
fontStyle: 'bold'
|
|
}
|
|
},
|
|
plugins: {
|
|
datalabels: stacked_sum_labels
|
|
}, layout: {
|
|
padding: {
|
|
left: 0,
|
|
right: 0,
|
|
top: 20,
|
|
bottom: 0
|
|
}
|
|
}
|
|
}
|
|
});
|
|
per_location.options.plugins.datalabels.color = "white";
|
|
per_location.update();
|
|
function station_legend(e, legendItem) {
|
|
showLocation("");
|
|
};
|
|
|
|
function location_legend(e, legendItem) {
|
|
showLocation(per_location.data.datasets[legendItem.datasetIndex].label);
|
|
};
|
|
|
|
function showLocation(loc_code) {
|
|
if (loc_code != "") {
|
|
$('#chart-title').text("Station Activity @ Location " + loc_code);
|
|
per_location.config.data.datasets = location_charts[loc_code];
|
|
per_hour.config.data.datasets = hourly_charts[loc_code];
|
|
per_weekday.config.data.datasets = weekday_charts[loc_code];
|
|
|
|
per_location.config.options.legend.onClick = station_legend;
|
|
$('#card_1').text(location_stats_data[loc_code].search);
|
|
$('#card_2').text(location_stats_data[loc_code].one_week_ago);
|
|
$('#card_3').text(location_stats_data[loc_code].two_week_ago);
|
|
|
|
$('#location').val(loc_code);
|
|
} else {
|
|
per_location.config.data.datasets = overall_charts.daily;
|
|
per_hour.config.data.datasets = overall_charts.hourly;
|
|
per_weekday.config.data.datasets = overall_charts.weekday;
|
|
per_location.config.options.legend.onClick = location_legend;
|
|
|
|
$('#card_1').text(overall_totals_data.search);
|
|
$('#card_2').text(overall_totals_data.one_week_ago);
|
|
$('#card_3').text(overall_totals_data.two_week_ago);
|
|
|
|
$('#chart-title').text("Location Activity");
|
|
$('#location').val("");
|
|
|
|
}
|
|
per_location.update();
|
|
per_hour.update();
|
|
per_weekday.update();
|
|
}
|
|
</script>
|
|
<script>
|
|
function submitform() {
|
|
$("#pageForm").submit();
|
|
}
|
|
|
|
$(function () {
|
|
$('input[name="dateRange"]').daterangepicker({
|
|
opens: 'left',
|
|
locale: {
|
|
firstDay: 1
|
|
}
|
|
});
|
|
});
|
|
$('input[name="dateRange"]').val('{{dates.search}}');
|
|
</script> |