From ccfc0518e38b474aa1f7cef1af1db981991bc939 Mon Sep 17 00:00:00 2001 From: relikd Date: Sat, 10 Jun 2023 19:33:45 +0200 Subject: [PATCH] feat: add person stats to dashboard --- app/base/static/css/style.css | 7 +++ app/base/templates/dashboard.html | 40 ++++++++++++-- app/base/views/dashboard.py | 79 +++++++++++++++++++++------- app/base/views/model_views/person.py | 3 ++ 4 files changed, 106 insertions(+), 23 deletions(-) diff --git a/app/base/static/css/style.css b/app/base/static/css/style.css index 7c60c6a..b2b9a57 100755 --- a/app/base/static/css/style.css +++ b/app/base/static/css/style.css @@ -269,6 +269,13 @@ table.clickable tbody>tr:hover { .text-green { color: #23be58; } +.text-gray { + color: #aaa; +} + +#by_month_history td:first-of-type { + text-decoration: dotted underline; +} #profileMenu { position: absolute; diff --git a/app/base/templates/dashboard.html b/app/base/templates/dashboard.html index b3a9313..107ca99 100755 --- a/app/base/templates/dashboard.html +++ b/app/base/templates/dashboard.html @@ -3,6 +3,18 @@ {% block content %} +

Personen

+

+Personen gesamt: {{person.by_count.total}} +(davon haben {{person.by_count.single_visit}} die Werkstatt nur 1x besucht +und {{person.by_count.long_not_seen}} waren seit über einem Jahr nicht mehr da) +

+

+ {{person.by_count.no_booking}} haben noch nie Werkstattzeit gebucht. + {{person.by_count.no_course}} haben nie eine Einweisung gemacht. +

+ +

Nach Art

@@ -42,23 +54,41 @@

{{ head.bookings }}

- - {% for year, stat in booking.by_month.items reversed %} +
+ {% for year, stat in by_month %} + + + {% for sum, count, people in stat %} + + {% if people and not forloop.first %} + {{ people }} + {% else %} + {{ people }} + {% endif %} + + {% endfor %} + - {% for sum, count in stat %}{% endfor %} + {% for sum, count, people in stat %} + {{ count }} + {% endfor %} - {% for sum, count in stat %}{% endfor %} + {% for sum, count, people in stat %} + {{ sum|divide:count|floatformat:1 }} + {% endfor %} - {% for sum, count in stat %}{% endfor %} + {% for sum, count, people in stat %} + {{ sum }} + {% endfor %} {% endfor %} diff --git a/app/base/views/dashboard.py b/app/base/views/dashboard.py index 10ae118..4764813 100755 --- a/app/base/views/dashboard.py +++ b/app/base/views/dashboard.py @@ -1,8 +1,11 @@ from django.db.models import Q, F, Sum, Count, ExpressionWrapper, IntegerField from django.db.models.functions import Substr +from django.db.models.lookups import LessThan from django.views.generic import TemplateView -from app.base.models import Booking, BookingType, Trait, TraitMapping +from app.base.models import ( + Booking, BookingType, CourseVisit, Person, Trait, TraitMapping +) from app.base.views.login import LoginRequired from app.base.views.model_views.base import ViewOptions @@ -23,30 +26,20 @@ class DashboardView(DashboardOptions, TemplateView): book_types = dict(BookingType.objects.values_list('key', 'label')) trait_types = dict(Trait.objects.values_list('key', 'label')) - # Booking stats - - book_by_type = stats_for_booking('type') - book_by_month = {} - - for stat in stats_for_booking('month'): - year, month = stat['month'].split('-') - if year not in book_by_month: - book_by_month[year] = [[0, 0] for x in range(13)] - book_by_month[year][0][0] += stat['sum'] - book_by_month[year][0][1] += stat['count'] - book_by_month[year][int(month)][0] += stat['sum'] - book_by_month[year][int(month)][1] += stat['count'] - context['booking'] = { 'labels': book_types, - 'by_type': book_by_type, - 'by_month': book_by_month, + 'by_type': stats_for_booking('type'), } context['trait'] = { 'labels': trait_types, 'by_type': stats_for_traits(), } + context['person'] = { + 'by_count': stats_for_person_count(), + } + context['by_month'] = stats_by_month() context['head'] = { + 'person': Person._meta.verbose_name_plural, 'bookings': Booking._meta.verbose_name_plural, 'booking_types': BookingType._meta.verbose_name_plural, 'traits': Trait._meta.verbose_name_plural, @@ -55,9 +48,59 @@ class DashboardView(DashboardOptions, TemplateView): return context +def stats_for_person_count(): + today = date.today() + last_year = today.replace(year=today.year - 1) + + stats = Person.objects.aggregate( + total=Count(1), + single_visit=Sum(LessThan( + # TODO: other DBs dont use microseconds + ExpressionWrapper(F('last_visit') - F('created'), + output_field=IntegerField()), + 3 * 86_400_000_000)), # 3 days + long_not_seen=Sum(Q(last_visit__lt=last_year)), + ) + + stats['no_booking'] = stats['total'] - Booking.objects.aggregate( + c=Count('user', distinct=True))['c'] + stats['no_course'] = stats['total'] - CourseVisit.objects.aggregate( + c=Count('participant', distinct=True))['c'] + + return stats + + +def stats_by_month(): + stats = {} + + for stat in stats_for_person_by_month(): + year, month = stat['month'].split('-') + if year not in stats: + stats[year] = [[0, 0, 0] for x in range(13)] + stats[year][0][2] += stat['count'] + stats[year][int(month)][2] += stat['count'] + + for stat in stats_for_booking('month'): + year, month = stat['month'].split('-') + if year not in stats: + stats[year] = [[0, 0, 0] for x in range(13)] + stats[year][0][0] += stat['sum'] + stats[year][0][1] += stat['count'] + stats[year][int(month)][0] += stat['sum'] + stats[year][int(month)][1] += stat['count'] + + return sorted(stats.items(), reverse=True) + + +def stats_for_person_by_month(): + return Person.objects.values( + month=Substr('created', 1, 7), # YYYY-MM + ).annotate(count=Count(1)).order_by('month') + + def stats_for_booking(groupby: str): _Q = Booking.objects.filter(Q(end_time__isnull=False)).annotate( - # for whatever reason django uses nano seconds + # TODO: other DBs dont use microseconds diff=ExpressionWrapper( (F('end_time') - F('begin_time')) / 60_000_000, output_field=IntegerField()), diff --git a/app/base/views/model_views/person.py b/app/base/views/model_views/person.py index 11931eb..37edb5a 100755 --- a/app/base/views/model_views/person.py +++ b/app/base/views/model_views/person.py @@ -33,6 +33,9 @@ class PersonOptions(ViewOptions[Person], LoginRequired): list_filter = { 'trait': 'traits__pk', 'course': 'courses__pk', + 'created': 'created', + 'created.y': 'created__year', + 'created.m': 'created__month', } list_columns = ['display_name', 'birth_date', 'last_visit'] list_render = {

{{ year }}

JanFebMärAprMaiJunJulAugSepOktNovDez
Personen
Buchungen{{ count }}
Minuten (ø){{ sum|divide:count|floatformat:1 }}
Minuten{{ sum }}