email: Refactor calculation of delay for scheduled emails.

Refactors the logic for adjusting the delay for sending an email
to not land on a weekend so that it can be used to schedule any
number of onboarding emails we decide to send.

Consolidates duplicate testing into
`zerver/tests/test_email_notifications.py`. The initial test and
function were introduced in commit 610f2cbacf with the test
located in `zerver/tests/test_signup.py`.

Prep commit for adding new welcome / follow up email.
This commit is contained in:
Lauryn Menard 2023-03-15 18:09:26 +01:00 committed by Tim Abbott
parent daad0a6618
commit ab00648e3e
3 changed files with 103 additions and 65 deletions

View File

@ -679,24 +679,31 @@ def handle_missedmessage_emails(
)
def followup_day2_email_delay(user: UserProfile) -> timedelta:
days_to_delay = 2
def get_onboarding_email_schedule(user: UserProfile) -> Dict[str, timedelta]:
onboarding_emails = {
# The delay should be 1 hour before the below specified number of days
# as our goal is to maximize the chance that this email is near the top
# of the user's inbox when the user sits down to deal with their inbox,
# or comes in while they are dealing with their inbox.
"followup_day2": timedelta(days=2, hours=-1),
}
user_tz = user.timezone
if user_tz == "":
user_tz = "UTC"
signup_day = user.date_joined.astimezone(zoneinfo.ZoneInfo(user_tz)).isoweekday()
if signup_day == 5:
# If the day is Friday then delay should be till Monday
days_to_delay = 3
elif signup_day == 4:
# If the day is Thursday then delay should be till Friday
days_to_delay = 1
# The delay should be 1 hour before the above calculated delay as
# our goal is to maximize the chance that this email is near the top
# of the user's inbox when the user sits down to deal with their inbox,
# or comes in while they are dealing with their inbox.
return timedelta(days=days_to_delay, hours=-1)
# User signed up on Thursday
if signup_day == 4:
# Send followup_day2 on Friday
onboarding_emails["followup_day2"] = timedelta(days=1, hours=-1)
# User signed up on Friday
if signup_day == 5:
# Send followup_day2 on Monday
onboarding_emails["followup_day2"] = timedelta(days=3, hours=-1)
return onboarding_emails
def enqueue_welcome_emails(user: UserProfile, realm_creation: bool = False) -> None:
@ -754,6 +761,10 @@ def enqueue_welcome_emails(user: UserProfile, realm_creation: bool = False) -> N
context=context,
)
# Any emails scheduled below should be added to the logic in get_onboarding_email_schedule
# to determine how long to delay sending the email based on when the user signed up.
onboarding_email_schedule = get_onboarding_email_schedule(user)
if other_account_count == 0:
send_future_email(
"zerver/emails/followup_day2",
@ -762,7 +773,7 @@ def enqueue_welcome_emails(user: UserProfile, realm_creation: bool = False) -> N
from_name=from_name,
from_address=from_address,
context=context,
delay=followup_day2_email_delay(user),
delay=onboarding_email_schedule["followup_day2"],
)

View File

@ -25,7 +25,7 @@ from zerver.lib.email_notifications import (
enqueue_welcome_emails,
fix_emojis,
fix_spoilers_in_html,
followup_day2_email_delay,
get_onboarding_email_schedule,
handle_missedmessage_emails,
include_realm_name_in_missedmessage_emails_subject,
relative_to_full_url,
@ -1749,14 +1749,84 @@ class TestMissedMessages(ZulipTestCase):
class TestFollowupEmailDelay(ZulipTestCase):
def test_followup_day2_email_delay(self) -> None:
def test_get_onboarding_email_schedule(self) -> None:
user_profile = self.example_user("hamlet")
# Test date_joined == Thursday
user_profile.date_joined = datetime(2018, 1, 4, 1, 0, 0, 0, tzinfo=timezone.utc)
self.assertEqual(followup_day2_email_delay(user_profile), timedelta(days=1, hours=-1))
# Test date_joined == Friday
dates_joined = {
"Monday": datetime(2018, 1, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
"Tuesday": datetime(2018, 1, 2, 1, 0, 0, 0, tzinfo=timezone.utc),
"Wednesday": datetime(2018, 1, 3, 1, 0, 0, 0, tzinfo=timezone.utc),
"Thursday": datetime(2018, 1, 4, 1, 0, 0, 0, tzinfo=timezone.utc),
"Friday": datetime(2018, 1, 5, 1, 0, 0, 0, tzinfo=timezone.utc),
"Saturday": datetime(2018, 1, 6, 1, 0, 0, 0, tzinfo=timezone.utc),
"Sunday": datetime(2018, 1, 7, 1, 0, 0, 0, tzinfo=timezone.utc),
}
user_profile.date_joined = dates_joined["Monday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Wednesday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=2, hours=-1),
)
user_profile.date_joined = dates_joined["Tuesday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Thursday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=2, hours=-1),
)
user_profile.date_joined = dates_joined["Wednesday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Friday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=2, hours=-1),
)
user_profile.date_joined = dates_joined["Thursday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Friday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=1, hours=-1),
)
user_profile.date_joined = dates_joined["Friday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Monday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=3, hours=-1),
)
user_profile.date_joined = dates_joined["Saturday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Monday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=2, hours=-1),
)
user_profile.date_joined = dates_joined["Sunday"]
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Tuesday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=2, hours=-1),
)
# Time offset of America/Phoenix is -07:00
user_profile.timezone = "America/Phoenix"
# Test date_joined == Friday in UTC, but Thursday in the user's time zone
user_profile.date_joined = datetime(2018, 1, 5, 1, 0, 0, 0, tzinfo=timezone.utc)
self.assertEqual(followup_day2_email_delay(user_profile), timedelta(days=3, hours=-1))
onboarding_email_schedule = get_onboarding_email_schedule(user_profile)
# followup_day2 email sent on Friday
self.assertEqual(
onboarding_email_schedule["followup_day2"],
timedelta(days=1, hours=-1),
)
class TestCustomEmailSender(ZulipTestCase):

View File

@ -38,7 +38,7 @@ from zerver.actions.realm_settings import (
from zerver.actions.users import change_user_is_active, do_change_user_role, do_deactivate_user
from zerver.decorator import do_two_factor_login
from zerver.forms import HomepageForm, check_subdomain_available
from zerver.lib.email_notifications import enqueue_welcome_emails, followup_day2_email_delay
from zerver.lib.email_notifications import enqueue_welcome_emails
from zerver.lib.i18n import get_default_language_for_new_user
from zerver.lib.initial_password import initial_password
from zerver.lib.mobile_auth_otp import (
@ -4170,49 +4170,6 @@ class MobileAuthOTPTest(ZulipTestCase):
self.assertEqual(decrypted, api_key)
class FollowupEmailTest(ZulipTestCase):
def test_followup_day2_email(self) -> None:
user_profile = self.example_user("hamlet")
# Test date_joined == Sunday
user_profile.date_joined = datetime.datetime(
2018, 1, 7, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
)
self.assertEqual(
followup_day2_email_delay(user_profile), datetime.timedelta(days=2, hours=-1)
)
# Test date_joined == Tuesday
user_profile.date_joined = datetime.datetime(
2018, 1, 2, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
)
self.assertEqual(
followup_day2_email_delay(user_profile), datetime.timedelta(days=2, hours=-1)
)
# Test date_joined == Thursday
user_profile.date_joined = datetime.datetime(
2018, 1, 4, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
)
self.assertEqual(
followup_day2_email_delay(user_profile), datetime.timedelta(days=1, hours=-1)
)
# Test date_joined == Friday
user_profile.date_joined = datetime.datetime(
2018, 1, 5, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
)
self.assertEqual(
followup_day2_email_delay(user_profile), datetime.timedelta(days=3, hours=-1)
)
# Time offset of America/Phoenix is -07:00
user_profile.timezone = "America/Phoenix"
# Test date_joined == Friday in UTC, but Thursday in the user's time zone
user_profile.date_joined = datetime.datetime(
2018, 1, 5, 1, 0, 0, 0, tzinfo=datetime.timezone.utc
)
self.assertEqual(
followup_day2_email_delay(user_profile), datetime.timedelta(days=1, hours=-1)
)
class NoReplyEmailTest(ZulipTestCase):
def test_noreply_email_address(self) -> None:
self.assertTrue(