Added better stats page and tweaked css

This commit is contained in:
Claudio Maggioni (maggicl) 2019-08-06 14:51:29 +02:00
parent 3a359cce40
commit 2680e485cd
7 changed files with 206 additions and 48 deletions

View file

@ -140,3 +140,22 @@ form .card-action button {
.inline-block {
display: inline-block;
}
.chart.scrollable.wrapper {
overflow-x: scroll;
}
.chart.wrapper {
position: relative;
max-width: 100%;
height: 25em;
}
.chart.area-wrapper {
height: 100%;
}
.chart-title {
margin-top: 0;
margin-bottom: 2rem;
}

View file

@ -1,43 +1,144 @@
'use strict';
// vim: set ts=2 sw=2 tw=80 et:
$(document).ready(function() {
var data = JSON.parse($('#data').text()),
format = djangoToMomentFmt(django.get_format('SHORT_DATE_FORMAT'));
var chart = $('#chart');
var labels = [], dataset = [];
/**
* Given an id of a `<script type="application/json">` node, returns the parsed
* JSON inside as an object.
*
* @param {String} id the id of the node from which to parse
* @return {Object} the object parsed
*/
function parseJSONData(id) {
return JSON.parse(document.getElementById(id).innerHTML);
}
// Set the chart height in em according to length of dataset
chart.css("height", "" + (2 * data.length) + "rem");
// Force height in pixels in order to fix infinite growth resizing bug of
// chart.js
chart.attr("height", chart.height());
data.forEach(function(d) {
labels.push(moment(d.weekStarts).format(format) + ' - ' +
moment(d.weekEnds).format(format));
dataset.push(d.sum_count);
/**
* Given two arrays, one for labels and one for data, to fill, fill the arrays
* with weekly arrowcount records for the weekly activity chart.
*
* @param {Array} labels an empty array for labels
* @param {Array} data an empty array for data
*/
function buildWeeklyDataset(labels, data) {
parseJSONData('data-weekly').forEach(function(d) {
labels.unshift(moment(d.weekStarts));
data.unshift(d.sum_count);
});
}
var chart = new Chart(chart, {
type: 'horizontalBar',
/**
* Given two arrays, one for labels and one for data, to fill, fill the arrays
* with cumulative total count records for the cumulative chart.
*
* @param {Array} labels an empty array for labels
* @param {Array} data an empty array for data
*/
function buildCumulativeDataset(labels, data) {
parseJSONData('data-cumulative').forEach(function(d) {
labels.push(moment(d.date));
data.push(d.count_inc);
});
}
/**
* Given a ChartJS object's parent node, sets up the width of the chart
* according to the data given and scrolls to the end of the chart.
*
* @param {Node} wrapper the parent node of the chart.
* @param {number} length how much bars/points are in the chart.
*/
function setUpWrapper(wrapper, length) {
wrapper.css("width", 25 * length + 'px');
// scroll to end of chart
wrapper.parent().scrollLeft(wrapper.width());
}
$(document).ready(function() {
var weeklyChart = $('#chart-weekly'),
weeklyWrapper = weeklyChart.parent();
var weeklyLabels = [], weeklyData = [];
buildWeeklyDataset(weeklyLabels, weeklyData);
var weekly = new Chart(weeklyChart, {
type: 'bar',
data: {
labels: labels,
labels: weeklyLabels,
datasets: [{
label: '# of Arrows',
data: dataset,
borderWidth: 1
label: gettext('# of Arrows'),
data: weeklyData,
backgroundColor: '#ffc107', // amber
borderWidth: 0
}]
},
options: {
maintainAspectRatio: false,
scales: {
xAxes: [{
yAxes: [{
ticks: {
beginAtZero: true
min: 0,
stepSize: 100,
}
}]
}
}
}],
xAxes: [{
type: 'time',
distribution: 'linear',
time: {
stepSize: 1,
max: new Date(),
unit: 'month',
}
}],
},
legend: {
display: false,
},
},
});
setUpWrapper(weeklyWrapper, weeklyData.length);
var cumulativeChart = $('#chart-cumulative'),
cumulativeWrapper = cumulativeChart.parent();
var cumulativeLabels = [], cumulativeData = [];
buildCumulativeDataset(cumulativeLabels, cumulativeData);
var weekly = new Chart(cumulativeChart, {
type: 'line',
data: {
labels: cumulativeLabels,
datasets: [{
label: gettext('Yearly arrow count up to the date'),
data: cumulativeData,
steppedLine: true,
backgroundColor: '#009688', // teal
borderWidth: 0
}]
},
options: {
maintainAspectRatio: false,
scales: {
yAxes: [{
ticks: {
min: 0,
}
}],
xAxes: [{
type: 'time',
distribution: 'linear',
bounds: 'ticks',
time: {
stepSize: 1,
min: new Date(new Date().getFullYear(), 0, 1),
max: new Date(),
unit: 'month',
}
}],
},
legend: {
display: false,
},
},
});
});

View file

@ -14,7 +14,7 @@
rel="stylesheet">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-rc.2/css/materialize.min.css">
<link rel="stylesheet" href="{% static "css/main.css" %}">
<link rel="stylesheet" href="{% static "css/main.css" %}?v=2">
{% block style %}{% endblock %}
</head>

View file

@ -18,7 +18,7 @@
{% if user.is_authenticated %}
<li><a href="{% url "count_list" %}">{% trans "Counts" %}</a></li>
<li><a href="{% url "target_edit" %}">{% trans "Set yearly target" %}</a></li>
<li><a href="{% url "stats" %}">{% trans "Weekly stats" %}</a></li>
<li><a href="{% url "stats" %}">{% trans "Stats" %}</a></li>
<li class="divider" tabindex="-1"></li>
{% if user.is_superuser %}
<li><a href="{% url "admin:index" %}">{% trans "Admin" %}</a></li>

View file

@ -4,19 +4,35 @@
{% load i18n %}
{% load static %}
{% block title %}{% trans "Weekly stats" %}{% endblock %}
{% block title %}{% trans "Stats" %}{% endblock %}
{% block scripts %}
<script id="data" type="application/json">
{% autoescape off %}{{ weeklyArrows }}{% endautoescape %}
<script id="data-weekly" type="application/json">
{% autoescape off %}{{ data_weekly }}{% endautoescape %}
</script>
<script id="data-cumulative" type="application/json">
{% autoescape off %}{{ data_cumulative }}{% endautoescape %}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="{% static 'js/dateformat.js' %}"></script>
<script src="{% static 'js/weeklystats.js' %}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="{% static 'js/dateformat.js' %}?v=2"></script>
<script src="{% static 'js/weeklystats.js' %}?v=2"></script>
{% endblock %}
{% block content %}
<h1 class="center">{% trans "Weekly stats" %}</h1>
<canvas id="chart"></canvas>
<h1 class="center">{% trans "Stats" %}</h1>
<div class="card-panel">
<h4 class="center chart-title">{% trans "Arrows shot by week" %}</h4>
<div class="chart scrollable wrapper">
<div class="chart area-wrapper">
<canvas id="chart-weekly"></canvas>
</div>
</div>
</div>
<div class="card-panel">
<h4 class="center chart-title">{% trans "Arrows shot this year" %}</h4>
<div class="chart wrapper">
<canvas id="chart-cumulative"></canvas>
</div>
</div>
{% endblock %}

View file

@ -12,7 +12,7 @@ from django.conf import settings
from django.core.exceptions import SuspiciousOperation
from django.utils.translation import gettext as _
from django.db.models.functions import Extract, ExtractWeek
from django.db.models import Sum
from django.db.models import Sum, Func
from datetime import datetime, timedelta, date
import json
from django.core.serializers.json import DjangoJSONEncoder
@ -76,12 +76,23 @@ def count_stats(request):
.annotate(sum_count=Sum('count')) \
.order_by('-isoyear', '-week')
incArrows = ArrowCount.objects \
.filter(user = request.user) \
.filter(date__gte = date(datetime.today().year, 1, 1)) \
.annotate(count_inc=Func(
Sum('count'),
template='%(expressions)s OVER (ORDER BY %(order_by)s)',
order_by="date"
)).values('date', 'count_inc') \
.order_by('date')
for w in weeklyArrows:
w['weekStarts'] = tofirstdayinisoweek(w['isoyear'], w['week'])
w['weekEnds'] = w['weekStarts'] + timedelta(days=6)
return render(request, 'stats.html', {
'weeklyArrows': json.dumps(list(weeklyArrows), cls=DjangoJSONEncoder)
'data_weekly': json.dumps(list(weeklyArrows), cls=DjangoJSONEncoder),
'data_cumulative': json.dumps(list(incArrows), cls=DjangoJSONEncoder)
})
@login_required

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Arrowcounter\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-05 15:54+0200\n"
"POT-Creation-Date: 2019-08-06 14:44+0200\n"
"PO-Revision-Date: 2019-07-27 13:00+0200\n"
"Last-Translator: Claudio Maggioni <maggicl@kolabnow.ch>\n"
"Language-Team: Claudio Maggioni <maggicl@kolabnow.ch>\n"
@ -148,9 +148,9 @@ msgid "Set yearly target"
msgstr "Imposta obiettivo annuale"
#: counter/templates/menu.html:21 counter/templates/stats.html:7
#: counter/templates/stats.html:20
msgid "Weekly stats"
msgstr "Statistiche settimanali"
#: counter/templates/stats.html:23
msgid "Stats"
msgstr "Statistiche"
#: counter/templates/menu.html:24
msgid "Admin"
@ -173,30 +173,41 @@ msgstr "Accedi"
msgid "Register"
msgstr "Registrati"
#: counter/templates/stats.html:25
msgid "Arrows shot by week"
msgstr "Frecce tirate questa settimana"
#: counter/templates/stats.html:33
msgid "Arrows shot this year"
msgstr "Frecce tirate quest'anno"
#: counter/templates/target/edit.html:22
msgid "Remove"
msgstr "Rimuovi"
#: counter/views.py:116
#: counter/views.py:127
msgid "page is negative or 0"
msgstr "pagina negativa o uguale a 0"
#: counter/views.py:210
#: counter/views.py:221
msgid "ArrowCount instance not found or from different user"
msgstr "istanza ArrowCount non trovata o appartenente ad altro utente"
#: counter/views.py:222
#: counter/views.py:233
msgid "mode not valid"
msgstr "campo 'mode' non valido"
#: counter/views.py:231
#: counter/views.py:242
msgid "value field is not a number"
msgstr "il campo 'value' non è un numero"
#: counter/views.py:247
#: counter/views.py:258
msgid "count is negative or 0"
msgstr "count è negativo o uguale a 0"
#: counter/views.py:255
#: counter/views.py:266
msgid "count too big"
msgstr "conteggio troppo alto"
#~ msgid "Weekly stats"
#~ msgstr "Statistiche settimanali"