From 7175dc534a4e4eb366a96703124b120c43da7233 Mon Sep 17 00:00:00 2001 From: Jessica McKellar Date: Mon, 8 Apr 2013 12:27:07 -0400 Subject: [PATCH] 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) --- zephyr/lib/actions.py | 15 +++++++++++++ .../commands/send_confirmation_emails.py | 21 +++++++++++++++++++ zephyr/models.py | 5 +++++ zephyr/views.py | 9 ++++---- 4 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 zephyr/management/commands/send_confirmation_emails.py diff --git a/zephyr/lib/actions.py b/zephyr/lib/actions.py index f9bccb2886..998dd9237d 100644 --- a/zephyr/lib/actions.py +++ b/zephyr/lib/actions.py @@ -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') diff --git a/zephyr/management/commands/send_confirmation_emails.py b/zephyr/management/commands/send_confirmation_emails.py new file mode 100644 index 0000000000..6c777ed734 --- /dev/null +++ b/zephyr/management/commands/send_confirmation_emails.py @@ -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() diff --git a/zephyr/models.py b/zephyr/models.py index 8d8ecbeb8e..e92da447d1 100644 --- a/zephyr/models.py +++ b/zephyr/models.py @@ -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. diff --git a/zephyr/views.py b/zephyr/views.py index 43bfab912e..e7d1c972b2 100644 --- a/zephyr/views.py +++ b/zephyr/views.py @@ -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},