* Posting conference news
* Posting confirmed sponsorship benefits
+* Sending reminders to speaker just before their presentation
### Posting conference news
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`
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)
--- /dev/null
+#
+# 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()
--- /dev/null
+# -*- 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'),
+ ),
+ ]
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)
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()!