Skip to content

Commit

Permalink
fix: move verification emails to Dotdigital
Browse files Browse the repository at this point in the history
  • Loading branch information
evemartin committed Mar 28, 2024
1 parent cd5d67b commit 6226ef9
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 40 deletions.
67 changes: 27 additions & 40 deletions cfl_common/common/helpers/emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,8 @@
import jwt
from common import app_settings
from common.app_settings import domain
from common.email_messages import (
emailChangeNotificationEmail,
emailChangeVerificationEmail,
emailVerificationNeededEmail,
parentsEmailVerificationNeededEmail,
)
from common.models import Teacher, Student
from common.mail import send_dotdigital_email
from django.conf import settings
from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives
Expand Down Expand Up @@ -123,55 +118,43 @@ def send_verification_email(request, user, data, new_email=None, age=None):

# if the user is a teacher
if age is None:
message = emailVerificationNeededEmail(request, verification)
send_email(
VERIFICATION_EMAIL,
url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"

send_dotdigital_email(
1551577,
[user.email],
message["subject"],
message["message"],
message["subject"],
replace_url=message["url"],
personalization_values={"VERIFICATION_LINK": url}
)

if _newsletter_ticked(data):
add_to_dotmailer(user.first_name, user.last_name, user.email, DotmailerUserType.TEACHER)
# if the user is an independent student
else:
if age < 13:
message = parentsEmailVerificationNeededEmail(request, user, verification)
send_email(
VERIFICATION_EMAIL,
url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
send_dotdigital_email(
1551587,
[user.email],
message["subject"],
message["message"],
message["subject"],
replace_url=message["url"],
personalization_values={"FIRST_NAME": user.first_name, "ACTIVATION_LINK": url}
)
else:
message = emailVerificationNeededEmail(request, verification)
send_email(
VERIFICATION_EMAIL,
url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
send_dotdigital_email(
1551577,
[user.email],
message["subject"],
message["message"],
message["subject"],
replace_url=message["url"],
personalization_values={"VERIFICATION_LINK": url}
)

if _newsletter_ticked(data):
add_to_dotmailer(user.first_name, user.last_name, user.email, DotmailerUserType.STUDENT)
# verifying change of email address.
else:
verification = generate_token(user, new_email)

message = emailChangeVerificationEmail(request, verification)
send_email(
VERIFICATION_EMAIL,
url = f"{request.build_absolute_uri(reverse('verify_email', kwargs={'token': verification}))}"
send_dotdigital_email(
1551594,
[new_email],
message["subject"],
message["message"],
message["subject"],
replace_url=message["url"],
personalization_values={"VERIFICATION_LINK": url}
)


Expand Down Expand Up @@ -281,8 +264,11 @@ def update_indy_email(user, request, data):
changing_email = True
users_with_email = User.objects.filter(email=new_email)

message = emailChangeNotificationEmail(request, new_email)
send_email(VERIFICATION_EMAIL, [user.email], message["subject"], message["message"], message["subject"])
send_dotdigital_email(
1551600,
[user.email],
personalization_values={"NEW_EMAIL_ADDRESS": new_email}
)

# email is available
if not users_with_email.exists():
Expand All @@ -299,9 +285,10 @@ def update_email(user: Teacher or Student, request, data):
changing_email = True
users_with_email = User.objects.filter(email=new_email)

message = emailChangeNotificationEmail(request, new_email)
send_email(
VERIFICATION_EMAIL, [user.new_user.email], message["subject"], message["message"], message["subject"]
send_dotdigital_email(
1551600,
[user.new_user.email],
personalization_values={"NEW_EMAIL_ADDRESS": new_email}
)

# email is available
Expand Down
111 changes: 111 additions & 0 deletions cfl_common/common/mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import os
import typing as t
from dataclasses import dataclass

import requests


def add_contact(email: str):
"""Add a new contact to Dotdigital."""
# TODO: implement


def remove_contact(email: str):
"""Remove an existing contact from Dotdigital."""
# TODO: implement


@dataclass
class EmailAttachment:
"""An email attachment for a Dotdigital triggered campaign."""

file_name: str
mime_type: str
content: str


# pylint: disable-next=too-many-arguments
def send_dotdigital_email(
campaign_id: int,
to_addresses: t.List[str],
cc_addresses: t.Optional[t.List[str]] = None,
bcc_addresses: t.Optional[t.List[str]] = None,
from_address: t.Optional[str] = None,
personalization_values: t.Optional[t.Dict[str, str]] = None,
metadata: t.Optional[str] = None,
attachments: t.Optional[t.List[EmailAttachment]] = None,
region: str = "r1",
auth: t.Optional[str] = None,
timeout: int = 30,
):
# pylint: disable=line-too-long
"""Send a triggered email campaign using DotDigital's API.
https://developer.dotdigital.com/reference/send-transactional-email-using-a-triggered-campaign
Args:
campaign_id: The ID of the triggered campaign, which needs to be included within the request body.
to_addresses: The email address(es) to send to.
cc_addresses: The CC email address or address to to send to. separate email addresses with a comma. Maximum: 100.
bcc_addresses: The BCC email address or address to to send to. separate email addresses with a comma. Maximum: 100.
from_address: The From address for your email. Note: The From address must already be added to your account. Otherwise, your account's default From address is used.
personalization_values: Each personalisation value is a key-value pair; the placeholder name of the personalization value needs to be included in the request body.
metadata: The metadata for your email. It can be either a single value or a series of values in a JSON object.
attachments: A Base64 encoded string. All attachment types are supported. Maximum file size: 15 MB.
region: The Dotdigital region id your account belongs to e.g. r1, r2 or r3.
auth: The authorization header used to enable API access. If None, the value will be retrieved from the DOTDIGITAL_AUTH environment variable.
timeout: Send timeout to avoid hanging.
Raises:
AssertionError: If failed to send email.
"""
# pylint: enable=line-too-long

if auth is None:
auth = os.environ["DOTDIGITAL_AUTH"]

body = {
"campaignId": campaign_id,
"toAddresses": to_addresses,
}
if cc_addresses is not None:
body["ccAddresses"] = cc_addresses
if bcc_addresses is not None:
body["bccAddresses"] = bcc_addresses
if from_address is not None:
body["fromAddress"] = from_address
if personalization_values is not None:
body["personalizationValues"] = [
{
"name": key,
"value": value,
}
for key, value in personalization_values.items()
]
if metadata is not None:
body["metadata"] = metadata
if attachments is not None:
body["attachments"] = [
{
"fileName": attachment.file_name,
"mimeType": attachment.mime_type,
"content": attachment.content,
}
for attachment in attachments
]

response = requests.post(
url=f"https://{region}-api.dotdigital.com/v2/email/triggered-campaign",
json=body,
headers={
"accept": "text/plain",
"authorization": auth,
},
timeout=timeout,
)

assert response.ok, (
"Failed to send email."
f" Reason: {response.reason}."
f" Text: {response.text}."
)

0 comments on commit 6226ef9

Please sign in to comment.