From d1e9106183607fb819860d83cb71f9e42335264c Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Mon, 15 Oct 2018 19:06:48 +0200 Subject: [PATCH] Support automated speaker reminders 10-15 minutes before a presentation, a reminder is sent to registered speakers, letting them know that their presentation is coming up, and which room it's scheduled in. Currently reminders are only sent over Twitter Direct Message, but the system is set up so that other integrations can be provided in the future. --- docs/confreg/integrations.md | 12 ++++ postgresqleu/confreg/backendforms.py | 2 +- .../commands/confreg_frequent_reminders.py | 68 +++++++++++++++++++ .../migrations/0033_speaker_reminders.py | 25 +++++++ postgresqleu/confreg/models.py | 2 + 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 postgresqleu/confreg/management/commands/confreg_frequent_reminders.py create mode 100644 postgresqleu/confreg/migrations/0033_speaker_reminders.py diff --git a/docs/confreg/integrations.md b/docs/confreg/integrations.md index 7bd72dd..4f9c779 100644 --- a/docs/confreg/integrations.md +++ b/docs/confreg/integrations.md @@ -9,6 +9,7 @@ The twitter integration supports: * Posting conference news * Posting confirmed sponsorship benefits +* Sending reminders to speaker just before their presentation ### Posting conference news @@ -28,6 +29,17 @@ will be generated with that template and sent. Normally this is used for things like "when the logo benefit is confirmed, post a welcome tweet", but it can be used for any defined benefit. +### Sending reminders to speaker just before their presentation + +If enabled, each speaker that have given their twitter username during +registration (regular registration, not call for papers) will get a +twitter DM sent between 10 and 15 minutes before their presentation +(depending on cronjob time) reminding them that their presentation +will begin soon, and which room it's in. + +This of course requires that the speaker follows the conference +account, or that they have public DMs open. + ### Setting up To set up the twitter integration, first configure `TWITTER_CLIENT` diff --git a/postgresqleu/confreg/backendforms.py b/postgresqleu/confreg/backendforms.py index 6e7385b..151164c 100644 --- a/postgresqleu/confreg/backendforms.py +++ b/postgresqleu/confreg/backendforms.py @@ -775,7 +775,7 @@ class BackendCopySelectConferenceForm(django.forms.Form): class TwitterForm(ConcurrentProtectedModelForm): class Meta: model = Conference - fields = ['twittersync_active', ] + fields = ['twittersync_active', 'twitterreminders_active'] class TwitterTestForm(django.forms.Form): recipient = django.forms.CharField(max_length=64) diff --git a/postgresqleu/confreg/management/commands/confreg_frequent_reminders.py b/postgresqleu/confreg/management/commands/confreg_frequent_reminders.py new file mode 100644 index 0000000..3533d0d --- /dev/null +++ b/postgresqleu/confreg/management/commands/confreg_frequent_reminders.py @@ -0,0 +1,68 @@ +# +# Send frequent reminders using interfaces like twitter DMs +# +# For now this only means sending a reminder to speakers 10-15 minutes +# before their session begins. +# +# Intended to run every 2-3 minutes from cron. + +from django.core.management.base import BaseCommand +from django.db import transaction, connection +from django.conf import settings + +from datetime import datetime, timedelta + +from postgresqleu.confreg.models import Conference, ConferenceSession +from postgresqleu.confreg.models import ConferenceRegistration + +from postgresqleu.util.messaging.twitter import Twitter + +class Command(BaseCommand): + help = 'Send confreg frequent reminders' + + def handle(self, *args, **options): + if not settings.TWITTER_CLIENT or not settings.TWITTER_CLIENTSECRET: + return + + curs = connection.cursor() + curs.execute("SELECT pg_try_advisory_lock(94012426)") + if not curs.fetchall()[0][0]: + raise CommandError("Failed to get advisory lock, existing frequent reminder process stuck?") + + # Only conferences that are actually running right now need to be considered. + # Normally this is likely just one. + # We can also filter for conferences that actually have reminders active. + # Right now that's only twitter reminders, butin the future there cna be + # more plugins. + for conference in Conference.objects.filter(twitterreminders_active=True, + startdate__lte=datetime.today()+timedelta(days=1), + enddate__gte=datetime.today()-timedelta(days=1)) \ + .exclude(twitter_token='') \ + .exclude(twitter_secret=''): + tw = Twitter(conference) + with transaction.atomic(): + # Sessions that can take reminders (yes we could make a more complete join at one + # step here, but that will likely fall apart later with more integrations anyway) + for s in ConferenceSession.objects.select_related('room') \ + .filter(conference=conference, + starttime__gt=datetime.now()-timedelta(hours=conference.timediff), + starttime__lt=datetime.now()-timedelta(hours=conference.timediff)+timedelta(minutes=15), + status=1, + reminder_sent=False): + for reg in ConferenceRegistration.objects.filter( + conference=conference, + attendee__speaker__conferencesession=s): + + msg = """Hello! We'd like to remind you that your session "{0}" is starting soon (at {1}) in room {2}.""".format( + s.title, + s.starttime.strftime("%H:%M"), + s.room.roomname, + ) + if reg.twittername: + # Twitter name registered, so send reminder + ok, err = tw.send_message(reg.twittername, msg) + if not ok: + print("Failed to send twitter DM to {0}: {1}".format(reg.twittername, err)) + + s.reminder_sent=True + s.save() diff --git a/postgresqleu/confreg/migrations/0033_speaker_reminders.py b/postgresqleu/confreg/migrations/0033_speaker_reminders.py new file mode 100644 index 0000000..9ac0ba2 --- /dev/null +++ b/postgresqleu/confreg/migrations/0033_speaker_reminders.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-10-15 14:19 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('confreg', '0032_tweetqueue'), + ] + + operations = [ + migrations.AddField( + model_name='conference', + name='twitterreminders_active', + field=models.BooleanField(default=False, verbose_name=b'Twitter reminder DMs active'), + ), + migrations.AddField( + model_name='conferencesession', + name='reminder_sent', + field=models.BooleanField(default=False, verbose_name=b'Speaker reminder(s) sent'), + ), + ] diff --git a/postgresqleu/confreg/models.py b/postgresqleu/confreg/models.py index d44923a..4d9c7a9 100644 --- a/postgresqleu/confreg/models.py +++ b/postgresqleu/confreg/models.py @@ -121,6 +121,7 @@ class Conference(models.Model): pixelsperminute = models.FloatField(blank=False, default=1.5, null=False, verbose_name="Vertical pixels per minute") confurl = models.CharField(max_length=128, blank=False, null=False, validators=[validate_lowercase,], verbose_name="Conference URL") twittersync_active = models.BooleanField(null=False, default=False, verbose_name='Twitter posting active') + twitterreminders_active = models.BooleanField(null=False, default=False, verbose_name='Twitter reminder DMs active') twitter_user = models.CharField(max_length=32, blank=True, null=False) twitter_token = models.CharField(max_length=128, blank=True, null=False) twitter_secret = models.CharField(max_length=128, blank=True, null=False) @@ -671,6 +672,7 @@ class ConferenceSession(models.Model): tentativescheduleslot = models.ForeignKey(ConferenceSessionScheduleSlot, null=True, blank=True, on_delete=models.CASCADE) tentativeroom = models.ForeignKey(Room, null=True, blank=True, related_name='tentativeroom', on_delete=models.CASCADE) lastmodified = models.DateTimeField(auto_now=True, null=False, blank=False) + reminder_sent = models.BooleanField(null=False, default=False, verbose_name='Speaker reminder(s) sent') # NOTE! Any added fields need to be considered for inclusion in # forms.CallForPapersForm and in views.callforpapers_copy()! -- 2.39.5