Skip to content
This repository was archived by the owner on Dec 8, 2022. It is now read-only.

Commit 6aa070b

Browse files
authored
Merge pull request #54 from codewizardshq/daily-email
scaffolding for daily email
2 parents add1219 + 7035a2d commit 6aa070b

File tree

8 files changed

+1161
-106
lines changed

8 files changed

+1161
-106
lines changed

CodeChallenge/api/eb.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
from flask import Blueprint
1+
from hmac import compare_digest
2+
3+
from flask import Blueprint, request, current_app, render_template
4+
from flask_mail import Message
5+
6+
from .. import core
7+
from ..mail import mail
28

39
bp = Blueprint("awsebapi", __name__, url_prefix="/api/v1/eb")
410

@@ -7,3 +13,32 @@
713
@bp.route("/health", methods=["GET"])
814
def eb_health_check():
915
return "OK", 200
16+
17+
18+
# POST request from an AWS Lambda function once per day
19+
# any daily tasks should be placed here
20+
@bp.route("/worker", methods=["POST"])
21+
def worker():
22+
try:
23+
password = request.json["password"]
24+
except (TypeError, KeyError):
25+
return "", 400
26+
27+
if not compare_digest(password,
28+
current_app.config["WORKER_PASSWORD"]):
29+
return "", 401
30+
31+
# send daily reminder emails only while challenge is active
32+
33+
if core.day_number() >= 1 and not core.challenge_ended():
34+
msg = Message("New code challenge question is unlocked!",
35+
sender=current_app.config["MAIL_DEFAULT_SENDER"],
36+
recipients=[current_app.config["MG_LIST"]])
37+
38+
msg.html = render_template("challenge_daily_email.html",
39+
name="%recipient_fname%")
40+
msg.extra_headers = {"List-Unsubscribe": "%unsubscribe_email%"}
41+
42+
mail.send(msg)
43+
44+
return "", 200

CodeChallenge/api/users.py

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,33 @@ def register():
134134
f"{new_u.studentfirstname} {new_u.studentlastname}",
135135
data=mg_vars)
136136

137-
msg = Message("Welcome Pilgrim! You have accepted the Code Challenge",
138-
sender=current_app.config["MAIL_DEFAULT_SENDER"],
139-
recipients=[new_u.parent_email])
137+
rcpts = [new_u.parent_email]
138+
if new_u.student_email:
139+
rcpts.append(new_u.student_email)
140+
141+
# account confirmation email
142+
# only contains login/password
143+
confirm_email = Message("Your Code Challenge Account",
144+
sender=current_app.config["MAIL_DEFAULT_SENDER"],
145+
recipients=rcpts)
140146
name = new_u.studentfirstname or new_u.parentfirstname
141-
msg.html = render_template("challenge_account_confirm.html",
142-
name=name)
143-
msg.extra_headers = {"List-Unsubscribe": "%unsubscribe_email%"}
144-
mail.send(msg)
147+
confirm_email.html = render_template("challenge_account_confirm.html",
148+
name=name,
149+
username=new_u.username,
150+
password=password)
151+
confirm_email.extra_headers = {"List-Unsubscribe": "%unsubscribe_email%"}
152+
153+
# welcome email
154+
# more in depth
155+
welcome_email = Message("Welcome Pilgrim! You have accepted the Code Challenge",
156+
sender=current_app.config["MAIL_DEFAULT_SENDER"],
157+
recipients=rcpts)
158+
welcome_email.html = render_template("challenge_welcome.html", name=name)
159+
welcome_email.extra_headers = {"List-Unsubscribe": "%unsubscribe_email%"}
160+
161+
# send emails
162+
mail.send(confirm_email)
163+
mail.send(welcome_email)
145164

146165
return jsonify({"status": "success"})
147166

@@ -176,29 +195,33 @@ def forgot_password():
176195
return jsonify(status="error",
177196
reason="no account with that email"), 400
178197

179-
for user in users:
198+
multiple_accounts = len(users) > 1
199+
200+
for user in users: # type: Users
180201
token = password_reset_token(user)
181-
msg = Message(subject="Password Reset",
182-
body="You are receiving this message because a password "
183-
"reset request has been issued for your account. If you "
184-
"did not make this request, you can ignore this email. "
185-
"To reset your password, use this link within 24 hours. "
186-
f"\n\n{current_app.config['EXTERNAL_URL']}/reset-password/{token}"
187-
f"\n\nAccount Username: {user.username}",
188-
recipients=[user.parent_email])
202+
203+
rcpts = [user.parent_email]
204+
if user.student_email:
205+
rcpts.append(user.student_email)
206+
207+
if user.studentfirstname:
208+
name = user.studentfirstname
209+
else:
210+
name = user.username
211+
212+
msg = Message(subject="Reset your Code Challenge password",
213+
html=render_template("challenge_password_reset.html",
214+
name=name,
215+
token=token,
216+
multiple=multiple_accounts),
217+
recipients=rcpts)
189218

190219
if current_app.config.get("TESTING", False):
191220
msg.extra_headers = {"X-Password-Reset-Token": token}
192221

193-
if len(users) > 1:
194-
msg.body += "\n\nNOTICE: Your email address matched multiple " \
195-
"student accounts. Double check to make sure you " \
196-
"are resetting the intended account, as an email " \
197-
"was sent for all matching accounts."
198-
199222
mail.send(msg)
200223

201-
return jsonify(status="success", reason="password reset email sent", multiple=len(users) > 1)
224+
return jsonify(status="success", reason="password reset email sent", multiple=multiple_accounts)
202225

203226

204227
@bp.route("/reset-password", methods=["POST"])

CodeChallenge/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class DefaultConfig:
2929
MAIL_SUPPRESS_SEND = True
3030
MG_PRIVATE_KEY = os.getenv("MG_PRIVATE_KEY")
3131
MG_LIST = "codechallenge@school.codewizardshq.com"
32+
WORKER_PASSWORD = os.getenv("WORKER_PASSWORD")
3233

3334
# no trailing /
3435
EXTERNAL_URL = "https://challenge.codewizardshq.com"

CodeChallenge/templates/challenge_account_confirm.html

Lines changed: 60 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
<head>
44
<meta name="viewport" content="width=device-width" />
55
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6-
<title>CodeWizardsHQ Code Challenge Welcome Email</title>
6+
<title>CodeWizardsHQ Code Challenge Daily Email</title>
77
<style>
8-
/* -------------------------------------
8+
/* -------------------------------------
99
GLOBAL
1010
A very basic CSS reset
1111
------------------------------------- */
@@ -39,11 +39,11 @@
3939
BODY & CONTAINER
4040
------------------------------------- */
4141
body {
42-
background-color: #f6f6f6;
42+
background-color: #353535;
4343
}
4444

4545
.body-wrap {
46-
background-color: #f6f6f6;
46+
background-color: #353535;
4747
width: 100%;
4848
}
4949

@@ -67,7 +67,6 @@
6767
------------------------------------- */
6868
.main {
6969
background-color: #fff;
70-
border: 1px solid #e9e9e9;
7170
border-radius: 3px;
7271
}
7372

@@ -83,7 +82,7 @@
8382
width: 100%;
8483
margin-bottom: 20px;
8584
background-color: #353535;
86-
padding: 60px 40px;
85+
padding: 20px;
8786
color: #fff;
8887
}
8988

@@ -108,7 +107,7 @@
108107
h1, h2, h3 {
109108
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
110109
color: #000;
111-
margin: 40px 0 0;
110+
margin: 20px 0 0;
112111
line-height: 1.2em;
113112
font-weight: 600;
114113
}
@@ -278,79 +277,60 @@
278277
<body itemscope itemtype="http://schema.org/EmailMessage">
279278

280279
<table class="body-wrap">
281-
<tr>
282-
<td></td>
283-
<td class="container" width="800">
284-
<div class="content">
285-
<table class="main" width="100%" cellpadding="0" cellspacing="0">
286-
<tr>
287-
<td class="header aligncenter">
288-
<img src="https://challenge.codewizardshq.com/images/logo-small.png">
289-
<h3>{{name}}, you've accepted the CodeWizardsHQ Code Challenge! </h3>
290-
<br>
291-
<a href="https://challenge.codewizardshq.com/login" class="btn-primary">SIGN IN</a>
292-
</td>
293-
</tr>
294-
<tr>
295-
<td class="content-wrap">
296-
<table width="100%" cellpadding="0" cellspacing="0">
297-
<tr>
298-
<td class="content-block">
299-
300-
</td>
301-
</tr>
302-
<tr>
303-
<td class="content-block">
304-
<p>Welcome {{name}},</p>
305-
<br/>
306-
307-
<p>Your mission is to defeat the evil dragon who has invaded CWHQ land. Only the bravest and brightest kid coders, like you, are prepared for this quest. </p>
308-
309-
<h3>How to play The Dragon Quest? </h3>
310-
<br/>
311-
312-
<p>To prove yourself worthy, you must log in every day between March 1 and March 21 to answer the Code Challenge question. </p>
313-
314-
<p>When you have answered the final question, you will take on the mighty dragon in the boss level by writing a piece of code in Python or JavaScript. If your answer unlocks the correct answer, you are worthy to be called Code Challenge champion and a chance to win a $100 cash prize!</p>
315-
316-
<p>See the <a href="http://codewizardshq.com/challenge">full FAQ</a> for more answers.</p>
317-
318-
<h3>Who will win the challenge?</h3>
319-
<br/>
320-
321-
<p>The final champions will be determined by public vote from March 23-29 so share the challenge with your family and friends.</p>
322-
323-
<p>Visit the <a href="http://codewizardshq.com/challenge">Code Challenge</a> page for the latest details and updates.</p>
324-
</td>
325-
</tr>
326-
<tr>
327-
<td class="content-block">
328-
<p>Good luck and safe travels! </p>
329-
</td>
330-
</tr>
331-
</table>
332-
</td>
333-
</tr>
334-
<tr>
335-
<td class="alert alert-warning">
336-
<a href="https://challenge.codewizardshq.com/login">Share With Friends</a>
337-
<br/>
338-
<img class="social" src="https://codewizardshq.com/wp-content/uploads/2020/01/challenge_icon_fb.jpg">
339-
<img class="social" src="https://codewizardshq.com/wp-content/uploads/2020/01/challenge_icon_twitter.jpg">
340-
<img class="social" src="https://codewizardshq.com/wp-content/uploads/2020/01/challenge_icon_email.jpg">
341-
</td>
342-
</tr>
343-
</table>
344-
<div class="footer">
345-
<table width="100%">
346-
<tr>
347-
<td class="aligncenter content-block"><a href="%unsubscribe_url%">Unsubscribe</a> from code challenge updates.</td>
348-
</tr>
349-
</table>
350-
</div></div>
351-
</td>
352-
<td></td>
353-
</tr>
280+
<tr>
281+
<td></td>
282+
<td class="container" width="800">
283+
<div class="content">
284+
<table class="main alignleft" width="100%" cellpadding="0" cellspacing="0">
285+
<tr>
286+
<td class="header aligncenter">
287+
<img src="https://challenge.codewizardshq.com/images/logo-small.png">
288+
</td>
289+
</tr>
290+
<tr>
291+
<td class="content-wrap">
292+
<table width="100%" cellpadding="0" cellspacing="0">
293+
<tr>
294+
<td class="content-block">
295+
<p><b>{{name}}, your account has been created.</b></p>
296+
<br/>
297+
<p>Login: {{username}}</p>
298+
<p>Password: {{password}}</p>
299+
<br/>
300+
<p>Forgot your password? <a href="https://challenge.codewizardshq.com/forgot-password">Reset your password.</a></p>
301+
</td>
302+
</tr>
303+
<tr>
304+
<td>
305+
<p><a href="https://challenge.codewizardshq.com/login">Sign in to play</a><br/>
306+
<a href="http://codewizardshq.com/challenge">Challenge Details</a><br/>
307+
<a href="http://codewizardshq.com/challenge">Prizes</a><br/>
308+
<a href="http://codewizardshq.com/challenge">Frequently Asked Questions</a></p>
309+
</td>
310+
</tr>
311+
</table>
312+
</td>
313+
</tr>
314+
<tr>
315+
<td class="alert alert-warning">
316+
<a href="https://challenge.codewizardshq.com/login">Share With Friends</a>
317+
<br/>
318+
<img class="social" src="https://codewizardshq.com/wp-content/uploads/2020/01/challenge_icon_fb.jpg">
319+
<img class="social" src="https://codewizardshq.com/wp-content/uploads/2020/01/challenge_icon_twitter.jpg">
320+
<img class="social" src="https://codewizardshq.com/wp-content/uploads/2020/01/challenge_icon_email.jpg">
321+
</td>
322+
</tr>
323+
</table>
324+
<div class="footer">
325+
<table width="100%">
326+
<tr>
327+
<td class="aligncenter content-block"><a href="%unsubscribe_url%">Unsubscribe</a> from code challenge updates.</td>
328+
</tr>
329+
</table>
330+
</div></div>
331+
</td>
332+
<td></td>
333+
</tr>
354334
</table>
355335

356336
</body>

0 commit comments

Comments
 (0)