Add tutorial code for email generation
parent
1aefe9824e
commit
da0d37178d
@ -0,0 +1 @@
|
|||||||
|
# Cuckoo
|
@ -0,0 +1,151 @@
|
|||||||
|
# This file should help you with the commands we'll be working through in this module.
|
||||||
|
# I'll also include documentation and notes where I think they might be useful.
|
||||||
|
# Commands will be unindented and uncommented (no # symbol at the beginning)
|
||||||
|
|
||||||
|
# Part 1 - S3 Uploads
|
||||||
|
# When you open up your demos file you'll see a templates folder.
|
||||||
|
# Along with licence information are three modified html email templates.
|
||||||
|
# Each template is ready to use with Jinja2 but we'll need to move it to an S3 bucket.
|
||||||
|
# Keep in mind that bucket names are *globally* unique. Meaning you can't use mine!
|
||||||
|
# Replace gpc-email-templates with your bucket name.
|
||||||
|
# Also use the region you've been using throughout the course.
|
||||||
|
# The \ character is to escape line breaks and make the code more readable
|
||||||
|
# Here is the command to create a bucket with the AWS CLI:
|
||||||
|
|
||||||
|
aws s3 mb s3://gpc-email-templates
|
||||||
|
|
||||||
|
# Confirm the bucket was created
|
||||||
|
|
||||||
|
aws s3 ls
|
||||||
|
|
||||||
|
# Upload the templates to your s3 bucket
|
||||||
|
# Make sure you're in the /templates folder in your terminal first
|
||||||
|
|
||||||
|
aws s3 cp ./templates s3://gpc-email-templates --recursive
|
||||||
|
|
||||||
|
# Confirm the files were moved to s3
|
||||||
|
|
||||||
|
aws s3 ls gpc-email-templates
|
||||||
|
|
||||||
|
# Notes:
|
||||||
|
# Keep in mind that Lambda cron() scheduling is done on UTC.
|
||||||
|
# To get a cron expression in your timezone you may need to change the values
|
||||||
|
# 7am EST - cron(0 12 ? * MON-FRI *)
|
||||||
|
# Noon EST - cron(0 17 ? * MON-FRI *)
|
||||||
|
# 5pm EST - cron(0 22 ? * MON-FRI *)
|
||||||
|
# Keep in mind time zone differences like Daylight Savings!
|
||||||
|
|
||||||
|
# To add CloudWatch Events rules:
|
||||||
|
aws events put-rule \
|
||||||
|
--name come_to_work \
|
||||||
|
--schedule-expression 'cron(0 12 ? * MON-FRI *)'
|
||||||
|
|
||||||
|
aws events put-rule \
|
||||||
|
--name daily_tasks \
|
||||||
|
--schedule-expression 'cron(0 17 ? * MON-FRI *)'
|
||||||
|
|
||||||
|
aws events put-rule \
|
||||||
|
--name pickup \
|
||||||
|
--schedule-expression 'cron(0 22 ? * MON-FRI *)'
|
||||||
|
|
||||||
|
|
||||||
|
# With the events created make sure to modify your code skeleton
|
||||||
|
# Come back for these commands when you need to test locally
|
||||||
|
|
||||||
|
# Testing the function from the command line:
|
||||||
|
# Make sure you're in the directory with the cuckoo.py file
|
||||||
|
# Open a Python shell
|
||||||
|
|
||||||
|
python3
|
||||||
|
|
||||||
|
# In the python shell I'll put commands after >>>
|
||||||
|
# import the function
|
||||||
|
|
||||||
|
>>> import cuckoo
|
||||||
|
|
||||||
|
# Call the handler function and give it a sample event
|
||||||
|
# This event can either be the event ARN text or something else
|
||||||
|
# that contains text that contains some of the text that the
|
||||||
|
# function is looking for.
|
||||||
|
# The text should contain:
|
||||||
|
# 'come_to_work' or 'daily_tasks' or 'pickup'
|
||||||
|
# We'll also put them into the following format to match
|
||||||
|
# the way they will be passed to the function by the AWS event
|
||||||
|
|
||||||
|
>>> cuckoo.handler({'resources':['some text containing the word pickup']},'context')
|
||||||
|
|
||||||
|
>>> cuckoo.handler({'resources':['arn:aws:events:us-east-1:444510800003:rule/come_to_work']}, 'context')
|
||||||
|
|
||||||
|
>>> cuckoo.handler({'resources':['a great daily_tasks test']},'context')
|
||||||
|
|
||||||
|
# If successful you won't see any output so check your email for the results!
|
||||||
|
|
||||||
|
# Setup and Deployment
|
||||||
|
#
|
||||||
|
# To take a look at creating our function package locally you can open
|
||||||
|
# the setup.sh file in a text editor.
|
||||||
|
#
|
||||||
|
# Deploying your function with the AWS CLI
|
||||||
|
# First make sure you've created your zip function package
|
||||||
|
# and you're in the same directory as that package.zip file
|
||||||
|
# Then list the roles on your account.
|
||||||
|
# We'll need to look for the role we created for this function earlier
|
||||||
|
|
||||||
|
aws iam list-roles
|
||||||
|
|
||||||
|
# We'll see something like this in the output:
|
||||||
|
# arn:aws:iam::444510800003:role/gpc_cuckoo_role
|
||||||
|
# Copy that role ARN because you'll need to replace the value
|
||||||
|
# after --role in the below command:
|
||||||
|
|
||||||
|
aws lambda create-function \
|
||||||
|
--function-name gpc_cuckoo \
|
||||||
|
--runtime python3.7 \
|
||||||
|
--role arn:aws:iam::444510800003:role/gpc_cuckoo_role \
|
||||||
|
--handler cuckoo.handler \
|
||||||
|
--zip-file fileb://./package.zip
|
||||||
|
|
||||||
|
# Configure events to trigger the function
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# List events
|
||||||
|
|
||||||
|
aws events list-rules
|
||||||
|
|
||||||
|
# We'll have the function trust events from each
|
||||||
|
# of these Cloudwatch Events
|
||||||
|
|
||||||
|
aws lambda add-permission \
|
||||||
|
--function-name gpc_cuckoo \
|
||||||
|
--statement-id 1 \
|
||||||
|
--action 'lambda:InvokeFunction' \
|
||||||
|
--principal events.amazonaws.com \
|
||||||
|
--source-arn arn:aws:events:us-east-1:444510800003:rule/pickup
|
||||||
|
|
||||||
|
aws lambda add-permission \
|
||||||
|
--function-name gpc_cuckoo \
|
||||||
|
--statement-id 2 \
|
||||||
|
--action 'lambda:InvokeFunction' \
|
||||||
|
--principal events.amazonaws.com \
|
||||||
|
--source-arn arn:aws:events:us-east-1:444510800003:rule/daily_tasks
|
||||||
|
|
||||||
|
aws lambda add-permission \
|
||||||
|
--function-name gpc_cuckoo \
|
||||||
|
--statement-id 3 \
|
||||||
|
--action 'lambda:InvokeFunction' \
|
||||||
|
--principal events.amazonaws.com \
|
||||||
|
--source-arn arn:aws:events:us-east-1:444510800003:rule/come_to_work
|
||||||
|
|
||||||
|
# This will add the lambda function to each of the rules to be triggered
|
||||||
|
aws events put-targets \
|
||||||
|
--rule daily_tasks \
|
||||||
|
--targets '{"Id" : "1", "Arn": "arn:aws:lambda:us-east-1:444510800003:function:gpc_cuckoo"}'
|
||||||
|
|
||||||
|
aws events put-targets \
|
||||||
|
--rule come_to_work \
|
||||||
|
--targets '{"Id" : "1", "Arn": "arn:aws:lambda:us-east-1:444510800003:function:gpc_cuckoo"}'
|
||||||
|
|
||||||
|
aws events put-targets \
|
||||||
|
--rule pickup \
|
||||||
|
--targets '{"Id" : "1", "Arn": "arn:aws:lambda:us-east-1:444510800003:function:gpc_cuckoo"}'
|
||||||
|
|
@ -0,0 +1,159 @@
|
|||||||
|
import datetime
|
||||||
|
import boto3
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
# Start of some things you need to change
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Recipient emails or domains in the AWS Email Sandbox must be verified
|
||||||
|
# You'll want to change this to the email you verify in SES
|
||||||
|
FROM_ADDRESS = 'globomanticspetcare@gmail.com'
|
||||||
|
REPLY_TO_ADDRESS = 'globomanticspetcare@gmail.com'
|
||||||
|
|
||||||
|
CLIENTS = [
|
||||||
|
{
|
||||||
|
# You'll need to verify this email
|
||||||
|
'email': 'f_mcorey@yahoo.com',
|
||||||
|
'first_name': 'Fernando',
|
||||||
|
'last_name': 'Medina Corey',
|
||||||
|
'pet_name': 'Riley'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
EMPLOYEES = [
|
||||||
|
{
|
||||||
|
# You'll need to verify this email
|
||||||
|
'email': 'springfield.homer@yahoo.com',
|
||||||
|
'first_name': 'Homer',
|
||||||
|
'last_name': 'Simpson'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# Change to the bucket you create on your AWS account
|
||||||
|
TEMPLATE_S3_BUCKET = 'gpc-email-templates'
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# End of things you need to change
|
||||||
|
|
||||||
|
def get_template_from_s3(key):
|
||||||
|
"""Loads and returns html template from Amazon S3"""
|
||||||
|
s3 = boto3.client('s3')
|
||||||
|
s3_file = s3.get_object(
|
||||||
|
Bucket=TEMPLATE_S3_BUCKET,
|
||||||
|
Key=key
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
template = Template(s3_file['Body'].read().decode('utf-8'))
|
||||||
|
except Exception as e:
|
||||||
|
print('Failed to load template')
|
||||||
|
raise e
|
||||||
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
def render_come_to_work_template(employee_first_name):
|
||||||
|
template = get_template_from_s3('come_to_work.html')
|
||||||
|
html_email = template.render(first_name=employee_first_name)
|
||||||
|
plaintext_email = 'Hello {0}, \nPlease remember to be into work by 8am'.format(employee_first_name)
|
||||||
|
return html_email, plaintext_email
|
||||||
|
|
||||||
|
|
||||||
|
def render_daily_tasks_template():
|
||||||
|
template = get_template_from_s3('daily_tasks.html')
|
||||||
|
tasks = {
|
||||||
|
'Monday': '- Clean the dog areas\n',
|
||||||
|
'Tuesday': '- Clean the cat areas\n',
|
||||||
|
'Wednesday': '- Feed the aligator\n',
|
||||||
|
'Thursday': '- Clean the dog areas\n',
|
||||||
|
'Friday': '- Clean the cat areas\n',
|
||||||
|
'Saturday': '- Relax! Play with the puppies! It\'s the weekend!',
|
||||||
|
'Sunday': '- Relax! Play with the puppies! It\'s the weekend!'
|
||||||
|
}
|
||||||
|
# Gets an integer value from 0 to 6 for today (Monday - Sunday)
|
||||||
|
# Keep in mind this will run in GMT and you will need to adjust runtimes accordingly
|
||||||
|
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||||
|
today = days[datetime.date.today().weekday()]
|
||||||
|
html_email = template.render(
|
||||||
|
day_of_week=today,
|
||||||
|
daily_tasks=tasks[today]
|
||||||
|
)
|
||||||
|
plaintext_email = (
|
||||||
|
"Remember to do all of these today:\n"
|
||||||
|
"- Feed the dogs\n"
|
||||||
|
"- Feed the rabbits\n"
|
||||||
|
"- Feed the cats\n"
|
||||||
|
"- Feed the turtles\n"
|
||||||
|
"- Walk the dogs\n"
|
||||||
|
"- Empty cat litterboxes\n"
|
||||||
|
"{0}".format(tasks[today])
|
||||||
|
)
|
||||||
|
return html_email, plaintext_email
|
||||||
|
|
||||||
|
|
||||||
|
def render_pickup_template(client_first_name, client_pet_name):
|
||||||
|
template = get_template_from_s3('pickup.html')
|
||||||
|
html_email = template.render(
|
||||||
|
first_name=client_first_name,
|
||||||
|
pet_name=client_pet_name
|
||||||
|
)
|
||||||
|
plaintext_email = (
|
||||||
|
'Hello {0}, \nPlease remember to '
|
||||||
|
'pickup {1} by 7pm!'.format(
|
||||||
|
client_first_name,
|
||||||
|
client_pet_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return html_email, plaintext_email
|
||||||
|
|
||||||
|
|
||||||
|
def send_email(html_email, plaintext_email, subject, recipients):
|
||||||
|
try:
|
||||||
|
ses = boto3.client('ses')
|
||||||
|
response = ses.send_email(
|
||||||
|
Source=FROM_ADDRESS,
|
||||||
|
Destination={
|
||||||
|
'ToAddresses': [recipients],
|
||||||
|
'CcAddresses': [],
|
||||||
|
'BccAddresses': []
|
||||||
|
},
|
||||||
|
Message={
|
||||||
|
'Subject': {
|
||||||
|
'Data': subject,
|
||||||
|
},
|
||||||
|
'Body': {
|
||||||
|
'Text': {
|
||||||
|
'Data': plaintext_email
|
||||||
|
},
|
||||||
|
'Html': {
|
||||||
|
'Data': html_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReplyToAddresses=[
|
||||||
|
REPLY_TO_ADDRESS,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print('Failed to send message via SES')
|
||||||
|
print(e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
def handler(event, context):
|
||||||
|
event_trigger = event['resources'][0]
|
||||||
|
print('event triggered by ' + event_trigger)
|
||||||
|
if 'come_to_work' in event_trigger:
|
||||||
|
for employee in EMPLOYEES:
|
||||||
|
html_email, plaintext_email = render_come_to_work_template(employee['first_name'])
|
||||||
|
send_email(html_email, plaintext_email, 'Work Schedule Reminder', employee['email'])
|
||||||
|
elif 'daily_tasks' in event_trigger:
|
||||||
|
for employee in EMPLOYEES:
|
||||||
|
html_email, plaintext_email = render_daily_tasks_template()
|
||||||
|
send_email(html_email, plaintext_email, 'Daily Tasks Reminder', employee['email'])
|
||||||
|
elif 'pickup' in event_trigger:
|
||||||
|
for client in CLIENTS:
|
||||||
|
html_email, plaintext_email = render_pickup_template(client['first_name'], client['pet_name'])
|
||||||
|
send_email(html_email, plaintext_email, 'Pickup Reminder', client['email'])
|
||||||
|
else:
|
||||||
|
return 'No template for this trigger!'
|
@ -0,0 +1,2 @@
|
|||||||
|
Jinja2==2.11.1
|
||||||
|
MarkupSafe==1.1.1
|
@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Create and initialize a Python Virtual Environment
|
||||||
|
echo "Creating virtual environment - .venv"
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
echo "sourcing virtual environment - .venv"
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Create a directory to put things in
|
||||||
|
echo "Creating 'setup' directory"
|
||||||
|
mkdir setup
|
||||||
|
|
||||||
|
# Move the relevant files into setup directory
|
||||||
|
echo "Moving function file(s) to setup dir"
|
||||||
|
cp cuckoo.py setup/
|
||||||
|
cd ./setup
|
||||||
|
|
||||||
|
# Install requirements
|
||||||
|
echo "pip installing requirements from requirements file in target directory"
|
||||||
|
pip install -r ../requirements.txt -t .
|
||||||
|
|
||||||
|
# Prepares the deployment package
|
||||||
|
echo "Zipping package"
|
||||||
|
zip -r ../package.zip ./*
|
||||||
|
|
||||||
|
# Remove the setup directory used
|
||||||
|
echo "Removing setup directory and virtual environment"
|
||||||
|
cd ..
|
||||||
|
rm -r ./setup
|
||||||
|
deactivate
|
||||||
|
rm -r .venv
|
||||||
|
# changing dirs back to dir from before
|
||||||
|
echo "Opening folder containg function package - 'package.zip'"
|
||||||
|
open .
|
@ -0,0 +1,37 @@
|
|||||||
|
rem Create and initialize a Python Virtual Environment
|
||||||
|
echo "Creating the virtual environment - .venv"
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
echo "starting the virtual environment - .venv"
|
||||||
|
call .venv\Scripts\activate.bat
|
||||||
|
|
||||||
|
rem Create a directory to put things in
|
||||||
|
echo "Creating 'setup' directory"
|
||||||
|
mkdir setup
|
||||||
|
|
||||||
|
rem Move the relevant files into setup directory
|
||||||
|
echo "Moving function file(s) to setup dir"
|
||||||
|
xcopy cuckoo.py setup\ /Q /R /Y
|
||||||
|
cd .\setup
|
||||||
|
|
||||||
|
rem Install requirements
|
||||||
|
echo "pip installing requirements from requirements file in target directory"
|
||||||
|
pip install -r ..\requirements.txt -t .
|
||||||
|
|
||||||
|
rem Prepares the deployment package
|
||||||
|
echo "Setting up your 7zip PATH - This assumes the installation location of 7zip is in C:\Program Files\7-Zip\"
|
||||||
|
set PATH=%PATH%;C:\Program Files\7-Zip\
|
||||||
|
|
||||||
|
echo "Zipping package"
|
||||||
|
7z a -r ..\package.zip .\*
|
||||||
|
|
||||||
|
rem Remove the setup directory used
|
||||||
|
echo "Removing setup directory and virtual environment"
|
||||||
|
cd ..
|
||||||
|
rd /Q /S .\setup
|
||||||
|
call .venv\Scripts\deactivate.bat
|
||||||
|
rd /Q /S .\.venv
|
||||||
|
|
||||||
|
rem changing dirs back to dir from before
|
||||||
|
echo "Opening folder containg function package - 'package.zip'"
|
||||||
|
explorer .
|
Loading…
Reference in New Issue