diff --git a/counter/static/js/dateformat.js b/counter/static/js/dateformat.js
new file mode 100644
index 0000000..627e3a9
--- /dev/null
+++ b/counter/static/js/dateformat.js
@@ -0,0 +1,55 @@
+'use strict';
+
+(function(globals) {
+ var replacements = {
+ 'd': 'DD',
+ 'D': 'ddd',
+ 'j': 'D',
+ 'l': 'dddd',
+ 'N': 'E',
+ 'S': 'o',
+ 'w': 'e',
+ 'z': 'DDD',
+ 'W': 'W',
+ 'F': 'MMMM',
+ 'm': 'MM',
+ 'M': 'MMM',
+ 'n': 'M',
+ 't': '', // no equivalent
+ 'L': '', // no equivalent
+ 'o': 'YYYY',
+ 'Y': 'YYYY',
+ 'y': 'YY',
+ 'a': 'a',
+ 'A': 'A',
+ 'B': '', // no equivalent
+ 'g': 'h',
+ 'G': 'H',
+ 'h': 'hh',
+ 'H': 'HH',
+ 'i': 'mm',
+ 's': 'ss',
+ 'u': 'SSS',
+ 'e': 'zz', // deprecated since version 1.6.0 of moment.js
+ 'I': '', // no equivalent
+ 'O': '', // no equivalent
+ 'P': '', // no equivalent
+ 'T': '', // no equivalent
+ 'Z': '', // no equivalent
+ 'c': '', // no equivalent
+ 'r': '', // no equivalent
+ 'U': 'X',
+ };
+
+ globals.djangoToMomentFmt = function(djangoFormat) {
+ djangoFormat = djangoFormat.replace(/(\w+)/g, function (f) {
+ return '[' + f + ']';
+ });
+ Object.keys(replacements).forEach(function(key) {
+ djangoFormat = djangoFormat.replace("[" + key + "]", replacements[key])
+ });
+ return djangoFormat;
+ };
+}(this));
+
+
diff --git a/counter/templates/menu.html b/counter/templates/menu.html
index c6397c4..2501658 100644
--- a/counter/templates/menu.html
+++ b/counter/templates/menu.html
@@ -12,6 +12,7 @@
{% if user.is_authenticated %}
Counts
+Weekly stats
{% if user.is_superuser %}
Admin
diff --git a/counter/templates/stats.html b/counter/templates/stats.html
new file mode 100644
index 0000000..0246741
--- /dev/null
+++ b/counter/templates/stats.html
@@ -0,0 +1,52 @@
+{% extends 'base.html' %}
+{% load static %}
+
+{% block title %}Weekly stats{% endblock %}
+
+{% block scripts %}
+
+
+
+
+
+{% endblock %}
+
+{% block content %}
+Weekly stats
+
+{% endblock %}
diff --git a/counter/urls.py b/counter/urls.py
index 7cf462d..dc284f8 100644
--- a/counter/urls.py
+++ b/counter/urls.py
@@ -4,6 +4,7 @@ from . import views
urlpatterns = [
path('', views.index, name='index'),
+ path('stats', views.count_stats, name='stats'),
path('count/list', views.arrow_count_list, name='count_list'),
path('count/new', login_required(views.NewArrowCount.as_view()),
name='count_new'),
diff --git a/counter/views.py b/counter/views.py
index f22111b..04c5cce 100644
--- a/counter/views.py
+++ b/counter/views.py
@@ -9,11 +9,43 @@ from django.contrib.auth.decorators import login_required
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 datetime import datetime, timedelta, date
+import json
+from django.core.serializers.json import DjangoJSONEncoder
+
+# https://stackoverflow.com/questions/5882405
+def tofirstdayinisoweek(year, week):
+ ret = datetime.strptime('%04d-%02d-1' % (year, week), '%Y-%W-%w')
+ if date(year, 1, 4).isoweekday() > 4:
+ ret -= timedelta(days=7)
+ return ret
def index(request):
template = loader.get_template('index.html')
return HttpResponse(template.render({}, request))
+def count_stats(request):
+ template = loader.get_template('stats.html')
+
+ # Group counts by week (extract isoyear works only on psql and DB2)
+ weeklyArrows = ArrowCount.objects \
+ .filter(user = request.user) \
+ .annotate(isoyear=Extract('date', lookup_name='isoyear')) \
+ .annotate(week=ExtractWeek('date')) \
+ .values('isoyear', 'week') \
+ .annotate(sum_count=Sum('count')) \
+ .order_by('-isoyear', '-week')
+
+ for w in weeklyArrows:
+ w['weekStarts'] = tofirstdayinisoweek(w['isoyear'], w['week'])
+ w['weekEnds'] = w['weekStarts'] + timedelta(days=6)
+
+ return HttpResponse(template.render({
+ 'weeklyArrows': json.dumps(list(weeklyArrows), cls=DjangoJSONEncoder)
+ }, request))
+
@login_required
def arrow_count_list(request):
page = request.GET.get('page')