Skip to content

Commit

Permalink
feature(email): Design User Invite Email template
Browse files Browse the repository at this point in the history
This change addresses the need by:

1. Create html email template
2. Implement functionality to send email invite notification to users in the same category when a new event under the category is created.

Delivers #161750658
  • Loading branch information
ipheghe committed Apr 12, 2019
1 parent f7763bf commit 65e2194
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 35 deletions.
10 changes: 5 additions & 5 deletions server/api/fixtures/initial.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
"password": "pbkdf2_sha256$36000$N92TfR4PrqJ7$DZSbvw4wIWbDjrZ+xfDisnqCNwVoyoAdx/meV7pqkHY=",
"last_login": "2018-09-09T00:35:53.883Z",
"is_superuser": false,
"username": "mayowa.makinde",
"first_name": "Mayowa",
"last_name": "Makinde",
"email": "mayowa.makinde@andela.com",
"username": "olusola.oguntimehin",
"first_name": "Olusola",
"last_name": "Oguntimehin",
"email": "olusola.oguntimehin@andela.com",
"is_staff": false,
"is_active": true,
"date_joined": "2018-09-09T00:35:28.114Z"
Expand Down Expand Up @@ -232,4 +232,4 @@
"follower_category": 1
}
}
]
]
34 changes: 9 additions & 25 deletions server/graphql_schemas/event/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from dateutil.parser import parse
from django.forms.models import model_to_dict

from django.core.mail import EmailMessage
from django.core.exceptions import ObjectDoesNotExist
from django.template.loader import get_template
from django.utils import timezone
Expand All @@ -17,6 +16,8 @@
from graphql_schemas.utils.helpers import (is_not_admin,
update_instance,
send_calendar_invites,
send_event_invite_email,
send_event_invite_emails,
raise_calendar_error,
not_valid_timezone)
from graphql_schemas.utils.hasher import Hasher
Expand Down Expand Up @@ -86,15 +87,16 @@ def mutate_and_get_payload(cls, root, info, **input):
user_profile = AndelaUserProfile.objects.get(
user=info.context.user
)
new_event = CreateEvent.create_event(
category, user_profile, **input)
# Send email invites in background
BackgroundTaskWorker.start_work(send_event_invite_emails,
(user_profile, new_event, info.context))
if user_profile.credential and user_profile.credential.valid:
new_event = CreateEvent.create_event(
category, user_profile, **input)
# Send calender invite in background
BackgroundTaskWorker.start_work(send_calendar_invites,
(user_profile, new_event))
(user_profile, new_event))
else:
new_event = CreateEvent.create_event(
category, user_profile, **input)
CreateEvent.notify_event_in_slack(category, input, new_event)
raise_calendar_error(user_profile)

Expand Down Expand Up @@ -244,25 +246,7 @@ def mutate_and_get_payload(cls, root, info, **input):
raise GraphQLError(
"User cannot invite self")

invite_hash = Hasher.gen_hash([
event.id, receiver.user.id, sender.user.id])
invite_url = info.context.build_absolute_uri(
f"/invite/{invite_hash}")
data_values = {
'title': event.title,
'imgUrl': event.featured_image,
'venue': event.venue,
'startDate': event.start_date,
'url': invite_url
}
message = get_template('event_invite.html').render(data_values)
msg = EmailMessage(
f"You have been invited to an event by {sender.user.username}",
message,
to=[receiver_email]
)
msg.content_subtype = 'html'
sent = msg.send()
sent = send_event_invite_email(sender, event, receiver, info.context)

if sent:
return cls(message="Event invite delivered")
Expand Down
175 changes: 175 additions & 0 deletions server/graphql_schemas/templates/event_email_invite.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<title> Andela Socials </title>
<style type="text/css">
table {
padding: 0;
margin: 0;
width: 100%;
}

table tr {
width: 100%;
margin-bottom: 10px;
}

td {
width: 100%;
padding: 5px 60px;
}

.email-template__container {
position: relative;
width: 765px;
min-height: 100vh;
border: 2px solid #000;
}

.email-template__header {
margin: 0;
width: 100%;
height: 15%;
border: none;
padding: 20px;
}

img {
background-color: #0000FF;
height: 100%;
}

table.email-template__body {
margin: 0;
padding: 25px 50px;
width: 100%;
border: none;
border-top: 1px solid #000;
text-align: center;
}

a.email-template__body__btn {
padding: 15px 40px;
max-height: 52px;
color: #FFF;
background-color: #3359DB;
text-decoration: none;
margin-top: 30px;
}

p.email-template__body__text {
font-size: 22px;
font-weight: 300;
margin-bottom: 10px;
color: #000;
}

.email-template__body__text.bold-text {
font-weight: bold;
}

table.email-template__footer {
position: absolute;
bottom: 0;
height: 15%;
width: 100%;
margin: 0;
padding: 10px 50px;
background-color: #F1F9FF;
border: none;
border-top: 1px solid #000;
text-align: center;

}

.email-template__footer td {
width: 100%;
padding: 5px 60px;
}

.email-template__footer__text {
font-size: 14px;
font-weight: 300;
color: #000;
}
</style>
</head>
<body>
<div class="email-template__container">
<div class="email-template__header">
<img
src="http://res.cloudinary.com/dd3lv0o93/image/upload/v1531322847/Andela_Logo_3_jkv99w.png"
alt="andela-logo"
/>
</div>
<table class="email-template__body">
<tr>
<td>
<p class="email-template__body__text bold-text">Hi {{receiverName}}!</p>
</td>
</tr>
<tr>
<td>
<p class="email-template__body__text">You have received an invite from
<span class="email-template__body__text bold-text">{{senderName}}</span> to attend the
<span class="email-template__body__text bold-text">{{title}}</span>
</p>
</td>
</tr>
<tr>
<td>
<p class="email-template__body__text bold-text">Date</p>
</td>
</tr>
<tr>
<td>
<p class="email-template__body__text">{{startDate}}</p>
</td>
</tr>
<tr>
<td>
<p class="email-template__body__text bold-text">Venue</>
</td>
</tr>
<tr>
<td>
<p class="email-template__body__text">{{venue}}</p>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td>
<a
type="button"
class="email-template__body__btn"
target="_blank"
href="{{url}}"
>
RSVP Now
</a>
</td>
</tr>
</table>
<table class="email-template__footer">
<tr>
<td>
<p class="email-template__footer__text">Have a question?</p>
</td>
</tr>
<tr>
<td>
<p class="email-template__footer__text">[email protected]</p>
</td>
</tr>
<tr>
<td>
<p class="email-template__footer__text">unsubscribe</p>
</td>
</tr>
</table>
</div>
</body>
</html>
97 changes: 92 additions & 5 deletions server/graphql_schemas/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
import dateutil.parser as parser
from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.core import mail
from django.core.exceptions import ObjectDoesNotExist
from django.template.loader import get_template

from api.utils.oauth_helper import get_auth_url
from api.models import Interest
from api.models import Interest, AndelaUserProfile
from googleapiclient.discovery import build
from google.cloud import storage
from graphql_schemas.utils.hasher import Hasher

from django.conf import settings

Expand Down Expand Up @@ -55,8 +59,8 @@ def raise_calendar_error(user_profile):
:param user_profile:
"""
auth_url = get_auth_url(user_profile)
raise UnauthorizedCalendarError(message="Calendar API not authorized",
auth_url=auth_url)
raise UnauthorizedCalendarError(
message="Calendar API not authorized", auth_url=auth_url)


async def send_calendar_invites(andela_user, event):
Expand All @@ -71,9 +75,92 @@ async def send_calendar_invites(andela_user, event):
payload = build_event(event, invitee_list)

calendar = build('calendar', 'v3', credentials=andela_user.credential)

if settings.ENVIRONMENT == "production":
calendar.events().insert(calendarId='primary', sendNotifications=True,
body=payload).execute()
calendar.events().insert(calendarId='primary',
sendNotifications=True, body=payload).execute()


def send_event_invite_email(andela_user, event, receiver, info):
"""
Send single email
:param andela_user:
:param event:
:param receiver
:param info:
"""
sender = andela_user
invite_hash = Hasher.gen_hash([
event.id, receiver.user.id, sender.user.id])
invite_url = info.build_absolute_uri(
f"/invite/{invite_hash}")

data_values = {
'title': event.title,
'imgUrl': event.featured_image,
'venue': event.venue,
'startDate': parser.parse(str(
event.start_date)).strftime("%a, %b %d %Y %H:%M"),
'url': invite_url,
'senderName': sender.user.first_name,
'receiverName': receiver.user.first_name,
}
message = get_template('event_email_invite.html').render(data_values)
msg = mail.EmailMessage(
f"You have been invited to an event by {sender.user.username}",
message,
to=[receiver.user.email]
)
msg.content_subtype = 'html'
sent = msg.send()

return sent


async def send_event_invite_emails(andela_user, event, info):
"""
Send mass emails asynchronously
:param andela_user:
:param event:
:param info:
"""
message_list = []
sender = andela_user
invitees = Interest.objects.filter(follower_category=event.social_event)
invitee_list = [{'email': invitee.follower.user.email}
for invitee in invitees]

for invitee in invitee_list:
receiver = AndelaUserProfile.objects.get(
user__email=invitee['email'])
invite_hash = Hasher.gen_hash([
event.id, receiver.user.id, sender.user.id])
invite_url = info.build_absolute_uri(
f"/invite/{invite_hash}")
data_values = {
'title': event.title,
'imgUrl': event.featured_image,
'venue': event.venue,
'startDate': parser.parse(str(
event.start_date)).strftime("%a, %b %d %Y %H:%M"),
'url': invite_url,
'senderName': sender.user.first_name,
'receiverName': receiver.user.first_name,
}
message = get_template('event_email_invite.html').render(data_values)
msg = mail.EmailMessage(
f"You have been invited to an event by {sender.user.username}",
message,
sender.user.email,
to=[receiver.user.email],
)
msg.content_subtype = 'html'
message_list.append(msg)

connection = mail.get_connection()
connection.open()
connection.send_messages(message_list)
connection.close()


def build_event(event, invitees):
Expand Down

0 comments on commit 65e2194

Please sign in to comment.