Added better stats page and tweaked css
This commit is contained in:
parent
3a359cce40
commit
2680e485cd
7 changed files with 206 additions and 48 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue