models: Add ScheduledEmail.realm.

The two extra queries in the test are due to the assert in
send_future_email.
This commit is contained in:
Rishi Gupta
2017-12-04 18:19:48 -08:00
committed by Greg Price
parent cc08becb2c
commit 869b4d41ef
9 changed files with 71 additions and 11 deletions

View File

@@ -255,6 +255,6 @@ def handle_digest_email(user_profile_id: int, cutoff: float) -> None:
new_streams_count, new_users_count):
logger.info("Sending digest email for %s" % (user_profile.email,))
# Send now, as a ScheduledEmail
send_future_email('zerver/emails/digest', to_user_id=user_profile.id,
send_future_email('zerver/emails/digest', user_profile.realm, to_user_id=user_profile.id,
from_name="Zulip Digest", from_address=FromAddress.NOREPLY,
context=context)

View File

@@ -453,10 +453,10 @@ def enqueue_welcome_emails(user: UserProfile) -> None:
'is_realm_admin': user.is_realm_admin,
})
send_future_email(
"zerver/emails/followup_day1", to_user_id=user.id, from_name=from_name,
"zerver/emails/followup_day1", user.realm, to_user_id=user.id, from_name=from_name,
from_address=from_address, context=context)
send_future_email(
"zerver/emails/followup_day2", to_user_id=user.id, from_name=from_name,
"zerver/emails/followup_day2", user.realm, to_user_id=user.id, from_name=from_name,
from_address=from_address, context=context, delay=datetime.timedelta(days=1))
def convert_html_to_markdown(html: Text) -> Text:

View File

@@ -4,7 +4,7 @@ from django.template import loader
from django.utils.timezone import now as timezone_now
from django.template.exceptions import TemplateDoesNotExist
from zerver.models import UserProfile, ScheduledEmail, get_user_profile_by_id, \
EMAIL_TYPES
EMAIL_TYPES, Realm
import datetime
from email.utils import parseaddr, formataddr
@@ -98,9 +98,9 @@ def send_email(template_prefix, to_user_id=None, to_email=None, from_name=None,
def send_email_from_dict(email_dict: Mapping[str, Any]) -> None:
send_email(**dict(email_dict))
def send_future_email(template_prefix, to_user_id=None, to_email=None, from_name=None,
def send_future_email(template_prefix, realm, to_user_id=None, to_email=None, from_name=None,
from_address=None, context={}, delay=datetime.timedelta(0)):
# type: (str, Optional[int], Optional[Text], Optional[Text], Optional[Text], Dict[str, Any], datetime.timedelta) -> None
# type: (str, Realm, Optional[int], Optional[Text], Optional[Text], Optional[Text], Dict[str, Any], datetime.timedelta) -> None
template_name = template_prefix.split('/')[-1]
email_fields = {'template_prefix': template_prefix, 'to_user_id': to_user_id, 'to_email': to_email,
'from_name': from_name, 'from_address': from_address, 'context': context}
@@ -112,6 +112,9 @@ def send_future_email(template_prefix, to_user_id=None, to_email=None, from_name
assert (to_user_id is None) ^ (to_email is None)
if to_user_id is not None:
# The realm is redundant if we have a to_user_id; this assert just
# expresses that fact
assert(UserProfile.objects.filter(id=to_user_id, realm=realm).exists())
to_field = {'user_id': to_user_id} # type: Dict[str, Any]
else:
to_field = {'address': parseaddr(to_email)[1]}
@@ -119,5 +122,6 @@ def send_future_email(template_prefix, to_user_id=None, to_email=None, from_name
ScheduledEmail.objects.create(
type=EMAIL_TYPES[template_name],
scheduled_timestamp=timezone_now() + delay,
realm=realm,
data=ujson.dumps(email_fields),
**to_field)

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-05 01:08
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
from django.db.migrations.state import StateApps
def set_realm_for_existing_scheduledemails(
apps: StateApps, schema_editor: DatabaseSchemaEditor) -> None:
scheduledemail_model = apps.get_model("zerver", "ScheduledEmail")
preregistrationuser_model = apps.get_model("zerver", "PreregistrationUser")
for scheduledemail in scheduledemail_model.objects.all():
if scheduledemail.type == 3: # ScheduledEmail.INVITATION_REMINDER
# Don't think this can be None, but just be safe
prereg = preregistrationuser_model.objects.filter(email=scheduledemail.address).first()
if prereg is not None:
scheduledemail.realm = prereg.realm
else:
scheduledemail.realm = scheduledemail.user.realm
scheduledemail.save(update_fields=['realm'])
# Shouldn't be needed, but just in case
scheduledemail_model.objects.filter(realm=None).delete()
class Migration(migrations.Migration):
dependencies = [
('zerver', '0127_disallow_chars_in_stream_and_user_name'),
]
operations = [
# Start with ScheduledEmail.realm being non-null
migrations.AddField(
model_name='scheduledemail',
name='realm',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='zerver.Realm'),
),
# Sets realm for existing ScheduledEmails
migrations.RunPython(set_realm_for_existing_scheduledemails,
reverse_code=migrations.RunPython.noop),
# Require ScheduledEmail.realm to be non-null
migrations.AlterField(
model_name='scheduledemail',
name='realm',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='zerver.Realm'),
),
]

View File

@@ -1726,6 +1726,7 @@ class AbstractScheduledJob(models.Model):
scheduled_timestamp = models.DateTimeField(db_index=True) # type: datetime.datetime
# JSON representation of arguments to consumer
data = models.TextField() # type: Text
realm = models.ForeignKey(Realm, on_delete=CASCADE) # type: Realm
class Meta:
abstract = True

View File

@@ -148,7 +148,8 @@ class RealmTest(ZulipTestCase):
def test_do_deactivate_realm_clears_scheduled_jobs(self) -> None:
user = self.example_user('hamlet')
send_future_email('zerver/emails/followup_day1', to_user_id=user.id, delay=datetime.timedelta(hours=1))
send_future_email('zerver/emails/followup_day1', user.realm,
to_user_id=user.id, delay=datetime.timedelta(hours=1))
self.assertEqual(ScheduledEmail.objects.count(), 1)
do_deactivate_realm(user.realm)
self.assertEqual(ScheduledEmail.objects.count(), 0)

View File

@@ -353,7 +353,7 @@ class LoginTest(ZulipTestCase):
with queries_captured() as queries:
self.register(self.nonreg_email('test'), "test")
# Ensure the number of queries we make is not O(streams)
self.assert_length(queries, 67)
self.assert_length(queries, 69)
user_profile = self.nonreg_user('test')
self.assertEqual(get_session_dict_user(self.client.session), user_profile.id)
self.assertFalse(user_profile.enable_stream_desktop_notifications)
@@ -831,7 +831,7 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
})
with self.settings(EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend'):
send_future_email(
"zerver/emails/invitation_reminder", to_email=data["email"],
"zerver/emails/invitation_reminder", referrer.realm, to_email=data["email"],
from_address=FromAddress.NOREPLY, context=context)
email_jobs_to_deliver = ScheduledEmail.objects.filter(
scheduled_timestamp__lte=timezone_now())
@@ -1179,7 +1179,8 @@ class EmailUnsubscribeTests(ZulipTestCase):
# Enqueue a fake digest email.
context = {'name': '', 'realm_uri': '', 'unread_pms': [], 'hot_conversations': [],
'new_users': [], 'new_streams': {'plain': []}, 'unsubscribe_link': ''}
send_future_email('zerver/emails/digest', to_user_id=user_profile.id, context=context)
send_future_email('zerver/emails/digest', user_profile.realm,
to_user_id=user_profile.id, context=context)
self.assertEqual(1, ScheduledEmail.objects.filter(user=user_profile).count())

View File

@@ -374,7 +374,8 @@ class ActivateTest(ZulipTestCase):
def test_clear_scheduled_jobs(self) -> None:
user = self.example_user('hamlet')
send_future_email('zerver/emails/followup_day1', to_user_id=user.id, delay=datetime.timedelta(hours=1))
send_future_email('zerver/emails/followup_day1', user.realm,
to_user_id=user.id, delay=datetime.timedelta(hours=1))
self.assertEqual(ScheduledEmail.objects.count(), 1)
do_deactivate_user(user)
self.assertEqual(ScheduledEmail.objects.count(), 0)

View File

@@ -246,6 +246,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
})
send_future_email(
"zerver/emails/invitation_reminder",
referrer.realm,
to_email=invitee.email,
from_address=FromAddress.NOREPLY,
context=context,