Send invitation e-mails asynchronously through RabbitMQ.

This avoids 10s of seconds of delay when you invite several people at
once through the web UI.

(imported from commit 75acdbdb04caf62bbb08affc7796330246d8a00e)
This commit is contained in:
Jessica McKellar 2013-04-08 12:27:07 -04:00
parent 69753f228c
commit 7175dc534a
4 changed files with 45 additions and 5 deletions

View File

@ -10,6 +10,9 @@ from django.db import transaction, IntegrityError
from django.db.models import F
from django.core.exceptions import ValidationError
from django.utils.importlib import import_module
from confirmation.models import Confirmation
session_engine = import_module(settings.SESSION_ENGINE)
from zephyr.lib.initial_password import initial_password
@ -801,3 +804,15 @@ def do_events_register(user_profile, apply_markdown=True, event_types=None):
ret['last_event_id'] = -1
return ret
def do_send_confirmation_email(invitee, referrer):
"""
Send the confirmation/welcome e-mail to an invited user.
`invitee` is a PreregistrationUser.
`referrer` is a UserProfile.
"""
Confirmation.objects.send_confirmation(
invitee, invitee.email, additional_context={'referrer': referrer},
subject_template_path='confirmation/invite_email_subject.txt',
body_template_path='confirmation/invite_email_body.txt')

View File

@ -0,0 +1,21 @@
from django.core.management.base import BaseCommand
from zephyr.models import get_user_profile_by_email, get_prereg_user_by_email
from zephyr.lib.queue import SimpleQueueClient
from zephyr.lib.actions import do_send_confirmation_email
class Command(BaseCommand):
"""
Send confirmation e-mails to invited users.
This command processes events from the `invites` queue.
"""
def subscribe(self, ch, method, properties, data):
invitee = get_prereg_user_by_email(data["email"])
referrer = get_user_profile_by_email(data["referrer_email"])
do_send_confirmation_email(invitee, referrer)
def handle(self, *args, **options):
q = SimpleQueueClient()
q.register_json_consumer("invites", self.subscribe)
q.start_consuming()

View File

@ -358,6 +358,11 @@ def get_user_profile_by_id(uid):
def get_user_profile_by_email(email):
return UserProfile.objects.select_related().get(email__iexact=email)
def get_prereg_user_by_email(email):
# A user can be invited many times, so only return the result of the latest
# invite.
return PreregistrationUser.objects.filter(email__iexact=email).latest("invited_at")
class Huddle(models.Model):
# TODO: We should consider whether using
# CommaSeparatedIntegerField would be better.

View File

@ -22,7 +22,7 @@ from zephyr.models import Message, UserProfile, Stream, Subscription, \
from zephyr.lib.actions import do_add_subscription, do_remove_subscription, \
do_change_password, create_mit_user_if_needed, do_change_full_name, \
do_change_enable_desktop_notifications, do_change_enter_sends, \
do_activate_user, do_create_user, check_send_message, \
do_send_confirmation_email, do_activate_user, do_create_user, check_send_message, \
log_subscription_property_change, internal_send_message, \
create_stream_if_needed, gather_subscriptions, subscribed_to_stream, \
update_user_presence, set_stream_color, get_stream_colors, update_message_flags, \
@ -352,10 +352,9 @@ def json_invite_users(request, user_profile, invitee_emails=POST):
# If we encounter an exception at any point before now, there are no unwanted side-effects,
# since it is totally fine to have duplicate PreregistrationUsers
for user in new_prereg_users:
Confirmation.objects.send_confirmation(user, user.email,
additional_context={'referrer': user_profile},
subject_template_path='confirmation/invite_email_subject.txt',
body_template_path='confirmation/invite_email_body.txt')
event = {"email": user.email, "referrer_email": user_profile.email}
queue_json_publish("invites", event,
lambda event: do_send_confirmation_email(user, user_profile))
if skipped:
return json_error(data={'errors': skipped},