diff --git a/jdav_web/jdav_web/settings/components/texts.py b/jdav_web/jdav_web/settings/components/texts.py index 0f33cd9..4e8358c 100644 --- a/jdav_web/jdav_web/settings/components/texts.py +++ b/jdav_web/jdav_web/settings/components/texts.py @@ -52,7 +52,8 @@ Wenn du weiterhin auf der Warteliste bleiben möchtest, klicke auf den folgenden {link} -Falls du nicht mehr auf der Warteliste bleiben möchtest, musst du nichts machen. Du wirst automatisch entfernt. +Das ist Erinnerung Nummer {reminder} von {max_reminder_count}. Nach Erinnerung Nummer {max_reminder_count} wirst +du automatisch entfernt. Viele Grüße Deine JDAV %(SEKTION)s""" % { 'SEKTION': SEKTION } diff --git a/jdav_web/jdav_web/settings/local.py b/jdav_web/jdav_web/settings/local.py index 6aa0129..ba456ca 100644 --- a/jdav_web/jdav_web/settings/local.py +++ b/jdav_web/jdav_web/settings/local.py @@ -34,6 +34,8 @@ ADMINS = (('admin', 'christian@merten-moser.de'),) GRACE_PERIOD_WAITING_CONFIRMATION = 30 WAITING_CONFIRMATION_FREQUENCY = 90 +CONFIRMATION_REMINDER_FREQUENCY = 30 +MAX_REMINDER_COUNT = 3 # testing diff --git a/jdav_web/members/admin.py b/jdav_web/members/admin.py index 09ed001..ed42c06 100644 --- a/jdav_web/members/admin.py +++ b/jdav_web/members/admin.py @@ -414,13 +414,14 @@ class WaiterInviteForm(forms.Form): class MemberWaitingListAdmin(CommonAdminMixin, admin.ModelAdmin): fields = ['prename', 'lastname', 'email', 'birth_date', 'gender', 'application_text', - 'application_date', 'comments', 'invited_for_group'] + 'application_date', 'comments', 'invited_for_group', + 'sent_reminders'] list_display = ('name', 'birth_date', 'age', 'application_date', 'confirmed_mail', - 'waiting_confirmed') + 'waiting_confirmed', 'sent_reminders') search_fields = ('prename', 'lastname', 'email') list_filter = ('confirmed_mail',) actions = ['ask_for_registration', 'ask_for_wait_confirmation'] - readonly_fields= ['invited_for_group', 'application_date'] + readonly_fields= ['invited_for_group', 'application_date', 'sent_reminders'] def has_add_permission(self, request, obj=None): return False diff --git a/jdav_web/members/migrations/0019_memberwaitinglist_last_reminder_and_more.py b/jdav_web/members/migrations/0019_memberwaitinglist_last_reminder_and_more.py new file mode 100644 index 0000000..09043aa --- /dev/null +++ b/jdav_web/members/migrations/0019_memberwaitinglist_last_reminder_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.1 on 2024-10-27 19:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0018_group_add_times'), + ] + + operations = [ + migrations.AddField( + model_name='memberwaitinglist', + name='last_reminder', + field=models.DateTimeField(auto_now=True, verbose_name='Last reminder'), + ), + migrations.AddField( + model_name='memberwaitinglist', + name='sent_reminders', + field=models.IntegerField(default=0, verbose_name='Missed reminders'), + ), + ] diff --git a/jdav_web/members/models.py b/jdav_web/members/models.py index 24ffa4a..06f07ae 100644 --- a/jdav_web/members/models.py +++ b/jdav_web/members/models.py @@ -704,6 +704,9 @@ class MemberWaitingList(Person): wait_confirmation_key = models.CharField(max_length=32, default="") wait_confirmation_key_expire = models.DateTimeField(default=timezone.now) + last_reminder = models.DateTimeField(auto_now=True, verbose_name=_('Last reminder')) + sent_reminders = models.IntegerField(default=0, verbose_name=_('Missed reminders')) + registration_key = models.CharField(max_length=32, default="") registration_expire = models.DateTimeField(default=timezone.now) @@ -728,7 +731,8 @@ class MemberWaitingList(Person): def waiting_confirmation_needed(self): """Returns if person should be asked to confirm waiting status.""" return wait_confirmation_key is None \ - and last_wait_confirmation < timezone.now() - timezone.timedelta(days=90) + and last_wait_confirmation < timezone.now() -\ + timezone.timedelta(days=settings.WAITING_CONFIRMATION_FREQUENCY) def waiting_confirmed(self): """Returns if the persons waiting status is considered to be confirmed.""" @@ -742,9 +746,14 @@ class MemberWaitingList(Person): def ask_for_wait_confirmation(self): """Sends an email to the person asking them to confirm their intention to wait.""" + self.last_reminder = datetime.now() + self.sent_reminders += 1 + self.save() self.send_mail(_('Waiting confirmation needed'), settings.WAIT_CONFIRMATION_TEXT.format(name=self.prename, - link=get_wait_confirmation_link(self))) + link=get_wait_confirmation_link(self), + reminder=self.sent_reminders, + max_reminder_count=settings.MAX_REMINDER_COUNT)) def confirm_waiting(self, key): # if a wrong key is supplied, we return invalid @@ -755,6 +764,7 @@ class MemberWaitingList(Person): if timezone.now() < self.wait_confirmation_key_expire: self.last_wait_confirmation = timezone.now() self.wait_confirmation_key_expire = timezone.now() + self.sent_reminders = 0 self.save() return self.WAITING_CONFIRMATION_SUCCESS diff --git a/jdav_web/members/tasks.py b/jdav_web/members/tasks.py index 197e84a..adeb85a 100644 --- a/jdav_web/members/tasks.py +++ b/jdav_web/members/tasks.py @@ -5,6 +5,16 @@ from .models import MemberWaitingList @shared_task def ask_for_waiting_confirmation(): + reminder_cutoff = timezone.now() - timezone.timedelta(days=settings.CONFIRMATION_REMINDER_FREQUENCY) cutoff = timezone.now() - timezone.timedelta(days=settings.WAITING_CONFIRMATION_FREQUENCY) - for waiter in MemberWaitingList.objects.filter(last_wait_confirmation__lte=cutoff): + no = 0 + # we ask all waiters for wait confirmation whose last confirmed waiting status is at least + # settings.WAITING_CONFIRMATION_FREQUENCY days ago, who have not received a reminder + # in the last settings.CONFIRMATION_REMINDER_FREQUENCY days and + # who have yet received strictly less reminders then settings.MAX_REMINDER_COUNT. + for waiter in MemberWaitingList.objects.filter(last_wait_confirmation__lte=cutoff, + last_reminder__lte=reminder_cutoff, + sent_reminders__lt=settings.MAX_REMINDER_COUNT): waiter.ask_for_wait_confirmation() + no += 1 + return no