From 8df2670138ff4ec5837e3187200f4ce0088931d4 Mon Sep 17 00:00:00 2001 From: flavis Date: Fri, 14 Jan 2022 16:51:36 +0100 Subject: [PATCH] add congratulating top 10 members --- .../management/commands/notify_active.py | 48 ++++++++++ jdav_web/members/admin.py | 90 +----------------- jdav_web/members/models.py | 93 ++++++++++++++++++- 3 files changed, 143 insertions(+), 88 deletions(-) create mode 100644 jdav_web/mailer/management/commands/notify_active.py diff --git a/jdav_web/mailer/management/commands/notify_active.py b/jdav_web/mailer/management/commands/notify_active.py new file mode 100644 index 0000000..341ca14 --- /dev/null +++ b/jdav_web/mailer/management/commands/notify_active.py @@ -0,0 +1,48 @@ +from django.core.management.base import BaseCommand +from mailer.models import Message +from members.models import Member, annotate_activity_score +from django.db.models import Q +from mailer.mailutils import mail_root, send + +import re + +CONGRATULATE_MEMBERS_MAX = 10 +SENDING_ADDRESS = mail_root + + +class Command(BaseCommand): + help = 'Congratulates the most active members' + requires_system_checks = False + + def handle(self, *args, **options): + qs = annotate_activity_score(Member.objects.all())\ + .order_by('_activity_score')[:CONGRATULATE_MEMBERS_MAX] + for position, member in enumerate(qs): + positiontext = "{}. ".format(position + 1) if position > 0 else "" + score = member._activity_score + if score < 5: + level = 1 + elif score >= 5 and score < 10: + level = 2 + elif score >= 10 and score < 20: + level = 3 + elif score >= 20 and score < 30: + level = 4 + else: + level = 5 + content = "Hallo {}!\n\n"\ + "Herzlichen Glückwunsch, du hast im letzten Jahr zu den {} aktivsten "\ + "Mitgliedern der JDAV Ludwigsburg gehört! Um genau zu sein beträgt "\ + "dein Aktivitäts Wert "\ + "des letzten Jahres {} Punkte. Das entspricht {} Kletterer*innen. "\ + "Damit warst du im letzten Jahr "\ + "das {}aktivste Mitglied der JDAV Ludwigsburg.\n\n"\ + "Auf ein weiteres aktives Jahr in der JDAV Ludwigsburg\n"\ + "Dein*e Jugendreferent*in".format(member.prename, + CONGRATULATE_MEMBERS_MAX, + score, + level, + positiontext) + send("Herzlichen Glückwunsch {}".format(member.prename), + content, SENDING_ADDRESS, [member.email], + reply_to=["jugendreferent@jdav-ludwigsburgs.de"]) diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index e830af9..0a977ad 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -20,7 +20,8 @@ from django.forms import Textarea, RadioSelect, TypedChoiceField from django.shortcuts import render from .models import (Member, Group, Freizeit, MemberNoteList, NewMemberOnList, Klettertreff, - KlettertreffAttendee, ActivityCategory, OldMemberOnList, MemberList) + KlettertreffAttendee, ActivityCategory, OldMemberOnList, MemberList, + annotate_activity_score) from django.conf import settings #from easy_select2 import apply_select2 @@ -85,92 +86,7 @@ class MemberAdmin(admin.ModelAdmin): def get_queryset(self, request): queryset = super().get_queryset(request) - one_year_ago = datetime.now() - timedelta(days=365) - queryset = queryset.annotate( - _jugendleiter_freizeit_score_calc=Subquery( - Freizeit.objects.filter(jugendleiter=OuterRef('pk'), - date__gte=one_year_ago) - .values('jugendleiter') - .annotate(cnt=Count('pk', distinct=True)) - .values('cnt'), - output_field=IntegerField() - ), - # better solution but does not work in production apparently - #_jugendleiter_freizeit_score=Sum(Case( - # When( - # freizeit__date__gte=one_year_ago, - # then=1), - # default=0, - # output_field=IntegerField() - # ), - # distinct=True), - _jugendleiter_klettertreff_score_calc=Subquery( - Klettertreff.objects.filter(jugendleiter=OuterRef('pk'), - date__gte=one_year_ago) - .values('jugendleiter') - .annotate(cnt=Count('pk', distinct=True)) - .values('cnt'), - output_field=IntegerField() - ), - # better solution but does not work in production apparently - #_jugendleiter_klettertreff_score=Sum(Case( - # When( - # klettertreff__date__gte=one_year_ago, - # then=1), - # default=0, - # output_field=IntegerField() - # ), - # distinct=True), - _freizeit_score_calc=Subquery( - Freizeit.objects.filter(membersonlist__member=OuterRef('pk'), - date__gte=one_year_ago) - .values('membersonlist__member') - .annotate(cnt=Count('pk', distinct=True)) - .values('cnt'), - output_field=IntegerField() - ), - _klettertreff_score_calc=Subquery( - KlettertreffAttendee.objects.filter(member=OuterRef('pk'), - klettertreff__date__gte=one_year_ago) - .values('member') - .annotate(cnt=Count('pk', distinct=True)) - .values('cnt'), - output_field=IntegerField())) - queryset = queryset.annotate( - _jugendleiter_freizeit_score=Case( - When( - _jugendleiter_freizeit_score_calc=None, - then=0 - ), - default=F('_jugendleiter_freizeit_score_calc'), - output_field=IntegerField()), - _jugendleiter_klettertreff_score=Case( - When( - _jugendleiter_klettertreff_score_calc=None, - then=0 - ), - default=F('_jugendleiter_klettertreff_score_calc'), - output_field=IntegerField()), - _klettertreff_score=Case( - When( - _klettertreff_score_calc=None, - then=0 - ), - default=F('_klettertreff_score_calc'), - output_field=IntegerField()), - _freizeit_score=Case( - When( - _freizeit_score_calc=None, - then=0 - ), - default=F('_freizeit_score_calc'), - output_field=IntegerField())) - queryset = queryset.annotate( - #_activity_score=F('_jugendleiter_freizeit_score') - _activity_score=(F('_klettertreff_score') + 3 * F('_freizeit_score') - + F('_jugendleiter_klettertreff_score') + 3 * F('_jugendleiter_freizeit_score')) - ) - return queryset + return annotate_activity_score(queryset) def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 3a90fa5..0613a7d 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -1,6 +1,8 @@ -from datetime import datetime +from datetime import datetime, timedelta import uuid from django.db import models +from django.db.models import TextField, ManyToManyField, ForeignKey, Count,\ + Sum, Case, Q, F, When, Value, IntegerField, Subquery, OuterRef from django.utils.translation import gettext_lazy as _ from django.utils import timezone from django.urls import reverse @@ -373,3 +375,92 @@ class KlettertreffAttendee(models.Model): class Meta: verbose_name = _('Member') verbose_name_plural = _('Members') + + +def annotate_activity_score(queryset): + one_year_ago = datetime.now() - timedelta(days=365) + queryset = queryset.annotate( + _jugendleiter_freizeit_score_calc=Subquery( + Freizeit.objects.filter(jugendleiter=OuterRef('pk'), + date__gte=one_year_ago) + .values('jugendleiter') + .annotate(cnt=Count('pk', distinct=True)) + .values('cnt'), + output_field=IntegerField() + ), + # better solution but does not work in production apparently + #_jugendleiter_freizeit_score=Sum(Case( + # When( + # freizeit__date__gte=one_year_ago, + # then=1), + # default=0, + # output_field=IntegerField() + # ), + # distinct=True), + _jugendleiter_klettertreff_score_calc=Subquery( + Klettertreff.objects.filter(jugendleiter=OuterRef('pk'), + date__gte=one_year_ago) + .values('jugendleiter') + .annotate(cnt=Count('pk', distinct=True)) + .values('cnt'), + output_field=IntegerField() + ), + # better solution but does not work in production apparently + #_jugendleiter_klettertreff_score=Sum(Case( + # When( + # klettertreff__date__gte=one_year_ago, + # then=1), + # default=0, + # output_field=IntegerField() + # ), + # distinct=True), + _freizeit_score_calc=Subquery( + Freizeit.objects.filter(membersonlist__member=OuterRef('pk'), + date__gte=one_year_ago) + .values('membersonlist__member') + .annotate(cnt=Count('pk', distinct=True)) + .values('cnt'), + output_field=IntegerField() + ), + _klettertreff_score_calc=Subquery( + KlettertreffAttendee.objects.filter(member=OuterRef('pk'), + klettertreff__date__gte=one_year_ago) + .values('member') + .annotate(cnt=Count('pk', distinct=True)) + .values('cnt'), + output_field=IntegerField())) + queryset = queryset.annotate( + _jugendleiter_freizeit_score=Case( + When( + _jugendleiter_freizeit_score_calc=None, + then=0 + ), + default=F('_jugendleiter_freizeit_score_calc'), + output_field=IntegerField()), + _jugendleiter_klettertreff_score=Case( + When( + _jugendleiter_klettertreff_score_calc=None, + then=0 + ), + default=F('_jugendleiter_klettertreff_score_calc'), + output_field=IntegerField()), + _klettertreff_score=Case( + When( + _klettertreff_score_calc=None, + then=0 + ), + default=F('_klettertreff_score_calc'), + output_field=IntegerField()), + _freizeit_score=Case( + When( + _freizeit_score_calc=None, + then=0 + ), + default=F('_freizeit_score_calc'), + output_field=IntegerField())) + queryset = queryset.annotate( + #_activity_score=F('_jugendleiter_freizeit_score') + _activity_score=(F('_klettertreff_score') + 3 * F('_freizeit_score') + + F('_jugendleiter_klettertreff_score') + 3 * F('_jugendleiter_freizeit_score')) + ) + return queryset