Skip to content

Commit

Permalink
update send daily email function
Browse files Browse the repository at this point in the history
  • Loading branch information
em-shea committed Jan 8, 2022
1 parent 0cc97e6 commit ddce5d5
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 154 deletions.
40 changes: 40 additions & 0 deletions src/layer/python/all_users_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import boto3
from boto3.dynamodb.conditions import Key

import sys
sys.path.append('../tests/')

import format_user_data_service

table = boto3.resource('dynamodb', region_name=os.environ['AWS_REGION']).Table(os.environ['DYNAMODB_TABLE_NAME'])

def get_all_users():

dynamo_response = pull_all_users()
grouped_user_and_subs = group_users_and_subs(dynamo_response)
response = []
for user_id, user in grouped_user_and_subs.items():
response.append(format_user_data_service.format_user_data(user))

return response

def pull_all_users():

response = table.query(
IndexName='GSI1',
KeyConditionExpression=Key('GSI1PK').eq('USER')
)
# print(response['Items'])
return response['Items']

def group_users_and_subs(dynamo_response):

grouped_user_and_subs = {}
for item in dynamo_response:
if item['PK'] not in grouped_user_and_subs:
grouped_user_and_subs[item['PK']] = []
grouped_user_and_subs[item['PK']].append(item)

# print('grouped: ', grouped_user_and_subs)
return grouped_user_and_subs
77 changes: 77 additions & 0 deletions src/layer/python/format_user_data_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import datetime
from typing import List
from dataclasses import dataclass

@dataclass
class Subscription:
list_name: str
unique_list_id: str
list_id: str
character_set: str
status: str
date_subscribed: str
# def to_dict(self):
# return {
# 'list_name': self.list_name
# }

@dataclass
class User:
email_address: str
user_id: str
character_set_preference: str
date_created: datetime.datetime
user_alias: str
user_alias_pinyin: str
user_alias_emoji: str
subscriptions: List[Subscription]

def format_user_data(user_data):

user = None
subscription_list = []

# Loop through all users and subs
for item in user_data:
# print(item)

# If Dynamo item is user metadata, create User class
if 'Email address' in item:
print('user', item['Email address'])
user = User(
email_address = item['Email address'],
user_id = item['PK'][5:],
character_set_preference = item['Character set preference'],
date_created = item['Date created'],
user_alias = item['User alias'],
user_alias_pinyin = item['User alias pinyin'],
user_alias_emoji = item['User alias emoji'],
subscriptions = []
)

# If Dynamo item is a list subscription, add the list to the user's lists dict
if 'List name' in item:
print('list', item['List name'])
# Shortening list id from unique id (ex, LIST#1ebcad40-bb9e-6ece-a366-acde48001122#SIMPLIFIED)
if 'SIMPLIFIED' in item['SK']:
list_id = item['SK'][5:-11]
if 'TRADITIONAL' in item['SK']:
list_id = item['SK'][5:-12]

sub = Subscription(
list_name = item['List name'],
unique_list_id = item['SK'][5:],
list_id = list_id,
character_set = item['Character set'],
status = item['Status'],
date_subscribed = item['Date subscribed']
)
subscription_list.append(sub)

# Sort lists by list id to appear in order (Level 1, Level 2, etc.)
subscription_list = sorted(subscription_list, key=lambda k: k.list_id, reverse=False)

user.subscriptions = subscription_list

print('formatted user ', user)
return user
5 changes: 4 additions & 1 deletion src/layer/python/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
import sys
sys.path.append('../tests/')

import format_user_data_service

table = boto3.resource('dynamodb', region_name=os.environ['AWS_REGION']).Table(os.environ['DYNAMODB_TABLE_NAME'])

def get_user_data(cognito_id):

user_data = pull_user_data(cognito_id)
response = process_user_data(user_data)
# response = process_user_data(user_data)
response = format_user_data_service.format_user_data(user_data)

return response

Expand Down
50 changes: 50 additions & 0 deletions src/layer/python/vocab_list_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import sys
sys.path.append('../tests/')

# Temporary service until vocab lists are dynamic and stored in DynamoDB

def get_vocab_lists():
return [
{
"list_name": "HSK Level 1",
"list_id": "1ebcad3f-5dfd-6bfe-bda4-acde48001122",
"list_difficulty_level": "Beginner",
"date_created": "2018-12-16T23:06:48.467526",
"created_by": "admin"
},
{
"list_name": "HSK Level 2",
"list_id": "1ebcad3f-adc0-6f42-b8b1-acde48001122",
"list_difficulty_level": "Beginner",
"date_created": "2018-12-16T23:06:48.467526",
"created_by": "admin"
},
{
"list_name": "HSK Level 3",
"list_id": "1ebcad3f-f815-6b92-b3e8-acde48001122",
"list_difficulty_level": "Intermediate",
"date_created": "2018-12-16T23:06:48.467526",
"created_by": "admin"
},
{
"list_name": "HSK Level 4",
"list_id": "1ebcad40-414f-6bc8-859d-acde48001122",
"list_difficulty_level": "Intermediate",
"date_created": "2018-12-16T23:06:48.467526",
"created_by": "admin"
},
{
"list_name": "HSK Level 5",
"list_id": "1ebcad40-bb9e-6ece-a366-acde48001122",
"list_difficulty_level": "Advanced",
"date_created": "2018-12-16T23:06:48.467526",
"created_by": "admin"
},
{
"list_name": "HSK Level 6",
"list_id": "1ebcad41-197a-6700-95a3-acde48001122",
"list_difficulty_level": "Advanced",
"date_created": "2018-12-16T23:06:48.467526",
"created_by": "admin"
}
]
122 changes: 40 additions & 82 deletions src/send_daily_email/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import os
import json
import boto3
from random import randint
from datetime import datetime
from boto3.dynamodb.conditions import Key
from botocore.exceptions import ClientError

# TODO: Add cognito id to unsub URL params

import random_word_service
import vocab_list_service
import list_word_service
import all_users_service

# region_name specified in order to mock in unit tests
ses_client = boto3.client('ses', region_name=os.environ['AWS_REGION'])
Expand All @@ -28,15 +29,15 @@ def lambda_handler(event, context):

todays_announcement = get_announcement()

# Scan the contacts table for a list of all contacts
users_and_subscriptions = get_users_and_subscriptions()

users_and_subscriptions_grouped = process_users_and_subscriptions(users_and_subscriptions)
user_list = all_users_service.get_all_users()

email_counter = 0
for user_id, user in users_and_subscriptions_grouped.items():
# print('loop through users')
if len(user['lists'])>0:
for user in user_list:
active_subscription_count = 0
for subscription in user.subscriptions:
if subscription.status == 'subscribed':
active_subscription_count += 1
if active_subscription_count>0:
email_content = assemble_html_content(user, todays_words, todays_announcement)
try:
# print('send emails')
Expand Down Expand Up @@ -77,23 +78,23 @@ def get_announcement():
return announcement_html

def get_daily_words():
print('getting daily words...')

todays_words = {}

# Hard coding list names for now before refactoring list database
list_names = ['HSK Level 1', 'HSK Level 2', 'HSK Level 3', 'HSK Level 4', 'HSK Level 5', 'HSK Level 6']
all_lists = vocab_list_service.get_vocab_lists()

# Loop through all lists and select word
for list in list_names:
level = str(list_names.index(list)+1)
for list in all_lists:
try:
word = random_word_service.select_random_word(level)
todays_words[list] = word
all_words = list_word_service.get_words_in_list(list['list_id'])
random_number = randint(0,len(all_words)-1)
random_word = all_words[random_number]
todays_words[list['list_id']] = random_word
except Exception as e:
# Appending None to the list as a placeholder for the level's word. Emails will not send for this level.
todays_words[list] = None
todays_words[list['list_id']] = None
print(e)

print('daily words: ', todays_words)
return todays_words

def store_words(todays_words):
Expand All @@ -111,58 +112,14 @@ def store_words(todays_words):
)
print(response)

def get_users_and_subscriptions():

response = table.query(
IndexName='GSI1',
KeyConditionExpression=Key('GSI1PK').eq('USER')
)
# print(response['Items'])
return response['Items']

def process_users_and_subscriptions(users_and_subscriptions):

users_and_subscriptions_grouped = {}
# users_and_subscriptions_grouped = {
# user_id: {
# 'user_data': {user metadata},
# 'lists': [
# {sub metadata}
# ]
# }
# }

# Loop through all users and subs
for item in users_and_subscriptions:
# If user is not yet in the grouped dict, add a new dict for the user
if item['PK'] not in users_and_subscriptions_grouped:
users_and_subscriptions_grouped[item['PK']] = {'user_data': None, 'lists': []}

# If Dynamo item is a user, add user metadata to the user's dict
if 'Email address' in item:
print('user metadata', item['Email address'])
users_and_subscriptions_grouped[item['PK']]['user_data'] = item

# If Dynamo item is a list subscription and the status is subscribed, add the list to the user's dict
if 'List name' in item:
print('user list', item['List name'])
if item['Status'] == 'subscribed':
users_and_subscriptions_grouped[item['PK']]['lists'].append(item)
print('subscribed')
else:
print('unsubscribed')

print(users_and_subscriptions_grouped)

return users_and_subscriptions_grouped

def assemble_html_content(user, todays_words, todays_announcement):

# Appends multiple words into the same email
# TODO: Order the lists in some way?
word_content = ""
for list in user['lists']:
word_content = word_content + assemble_word_html_content(user['user_data']['Email address'], list, todays_words)
for subscription in user.subscriptions:
if subscription.status == 'subscribed':
word_content = word_content + assemble_word_html_content(user.email_address, subscription, todays_words)

# Open HTML template file
# To run unit tests, we need to specify an absolute file path
Expand All @@ -172,9 +129,7 @@ def assemble_html_content(user, todays_words, todays_announcement):

# Hard coding HSK level before list database refactor
# Get first list user is subscribed to and use in unsub link
list_id_substring = user['lists'][0]['SK'].split('#')
print('substring, ', list_id_substring)
email_contents = email_template.replace("{unsubscribe_link}", "https://haohaotiantian.com/unsub?list=" + list_id_substring[1] + "&char=" + list_id_substring[2] + "&email=" + user['user_data']['Email address'])
email_contents = email_template.replace("{unsubscribe_link}", "https://haohaotiantian.com/unsub?list=" + user.subscriptions[0].list_id + "&char=" + user.subscriptions[0].character_set + "&email=" + user.email_address)

email_contents = email_contents.replace("{word_contents}", word_content)

Expand All @@ -185,20 +140,23 @@ def assemble_html_content(user, todays_words, todays_announcement):

return email_contents

def assemble_word_html_content(user_email, list, todays_words):
def assemble_word_html_content(user_email, subscription, todays_words):
print('assembling word content...')
print('list subscription: ', subscription)

word = todays_words[list['List name']]
word = todays_words[subscription.list_id]['word']
print('selected word, ', word)
if word is None:
return ""
else:
# Select simplified or traditional character
if list['Character set'] == "simplified":
selected_word = word["Word"]
if subscription.character_set == "simplified":
selected_word = word["Simplified"]
else:
selected_word = word["Word-Traditional"]
selected_word = word["Traditional"]

# Hard coding list names and sentence URLs before list database refactor
if list['List name'] in ['HSK Level 1', 'HSK Level 2', 'HSK Level 3']:
if subscription.list_name in ['HSK Level 1', 'HSK Level 2', 'HSK Level 3']:
example_link = "https://www.yellowbridge.com/chinese/sentsearch.php?word=" + selected_word
else:
example_link = "https://fanyi.baidu.com/#zh/en/" + selected_word
Expand All @@ -208,16 +166,16 @@ def assemble_word_html_content(user_email, list, todays_words):
word_template = fh.read()

# Hard coding HSK level before list database refactor
hsk_level = list['List name'][-1]
hsk_level = subscription.list_name[-1]

word_contents = word_template.replace("{word}", selected_word)
word_contents = word_contents.replace("{pronunciation}", word["Pronunciation"])
word_contents = word_contents.replace("{pronunciation}", word["Pinyin"])
word_contents = word_contents.replace("{definition}", word["Definition"])
word_contents = word_contents.replace("{link}", example_link)
word_contents = word_contents.replace("{list}", list['List name'])
word_contents = word_contents.replace("{quiz_link}", "https://haohaotiantian.com/quiz?list=HSKLevel" + hsk_level + "&days=14&ques=10&char=" + list['Character set'])
word_contents = word_contents.replace("{list}", subscription.list_name)
word_contents = word_contents.replace("{quiz_link}", "https://haohaotiantian.com/quiz?list=HSKLevel" + hsk_level + "&days=14&ques=10&char=" + subscription.character_set)
word_contents = word_contents.replace("{signin_link}", "https://haohaotiantian.com/quiz?email=" + user_email)
word_contents = word_contents.replace("{history_link}", "https://haohaotiantian.com/history?list=HSKLevel" + hsk_level + "&dates=30&char=" + list['Character set'])
word_contents = word_contents.replace("{history_link}", "https://haohaotiantian.com/history?list=HSKLevel" + hsk_level + "&dates=30&char=" + subscription.character_set)

return word_contents

Expand All @@ -227,7 +185,7 @@ def send_email(user, email_content):
Source = "Haohaotiantian <[email protected]>",
Destination = {
"ToAddresses" : [
user['user_data']['Email address']
user.email_address
]
},
Message = {
Expand Down
Loading

0 comments on commit ddce5d5

Please sign in to comment.