Skip to content

Commit

Permalink
Send gift card claim code in email, too.
Browse files Browse the repository at this point in the history
  • Loading branch information
davepeck committed Apr 19, 2024
1 parent 437f682 commit f27f9b8
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 12 deletions.
33 changes: 27 additions & 6 deletions server/vb/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def _get_claim_code(gift_card: GiftCard) -> str:

def get_or_issue_gift_card(
student: Student, contest: Contest, when: datetime.datetime | None = None
) -> tuple[GiftCard, str]:
) -> tuple[GiftCard, str, bool]:
"""
Issue a gift card to a student for a contest.
Expand All @@ -72,8 +72,8 @@ def get_or_issue_gift_card(
raise a GiftCardPreconditionError. If another error occurs, raise
an arbitrary exception.
Returns a tuple of the gift card and, if the gift card was issued,
the claim code.
Returns a tuple of the gift card, gift code, and True if the gift card
was newly issued.
"""
# Precondition: student must have a validated email address.
if not student.is_validated:
Expand All @@ -95,13 +95,14 @@ def get_or_issue_gift_card(

if gift_card is not None:
claim_code = _get_claim_code(gift_card)
return gift_card, claim_code
return gift_card, claim_code, False

# Precondition: the contest must be ongoing to truly issue a gift card.
if not contest.is_ongoing(when):
raise GiftCardPreconditionError(f"Contest '{contest.name}' is not ongoing")

return _issue_gift_card(student, contest)
gift_card, claim_code = _issue_gift_card(student, contest)
return gift_card, claim_code, True


def get_or_create_student(
Expand Down Expand Up @@ -136,9 +137,29 @@ def send_validation_link_email(
"contest": contest,
"email": email,
"link": link,
"title": f"Get my ${contest.amount} gift card",
"button_text": f"Get my ${contest.amount} gift card",
},
)
if not success:
logger.error(f"Failed to send email validation link to {email}")
return link


def send_gift_card_email(
student: Student,
gift_card: GiftCard,
claim_code: str,
email: str,
) -> None:
"""Send a gift card email to a student."""
success = send_template_email(
to=email,
template_base="email/code",
context={
"student": student,
"gift_card": gift_card,
"claim_code": claim_code,
},
)
if not success:
logger.error(f"Failed to send gift card email to {email}")
16 changes: 16 additions & 0 deletions server/vb/templates/email/code/body.dhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends "email/base/body.dhtml" %}

{% block message %}
{% include "email/base/p.html" %}
Hi {{ student.first_name }},
{% include "email/base/p_close.html" %}
{% include "email/base/p.html" %}
Your ${{ gift_card.amount }} Amazon gift code is: <strong>{{ gift_card.code }}</strong>
{% include "email/base/p_close.html" %}
{% include "email/base/p.html" %}
Copy and paste it into Amazon's gift card redemption page to claim your gift.
{% include "email/base/p_close.html" %}
{% include "email/base/p.html" %}
And: be sure to tell your friends at {{ school.short_name }} to visit VoterBowl, too!
{% include "email/base/p_close.html" %}
{% endblock message %}
9 changes: 9 additions & 0 deletions server/vb/templates/email/code/body.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% extends "email/base/body.txt" %}

{% block message %}Hi {{ student.first_name }},
Your ${{ gift_card.amount }} Amazon gift code is: {{ gift_card.code }}

Copy and paste it into Amazon's gift card redemption page to claim your gift.

And: be sure to tell your friends at {{ school.short_name }} to visit VoterBowl, too!
{% endblock message %}
1 change: 1 addition & 0 deletions server/vb/templates/email/code/subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Your ${{ gift_card.amount }} Amazon gift card
2 changes: 1 addition & 1 deletion server/vb/templates/email/validate/body.dhtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
{% include "email/base/p.html" %}
Thanks for checking your voter registration status! Click this button to get your gift card.
{% include "email/base/p_close.html" %}
{% include "email/base/button.html" with url=link.relative_url title=title %}
{% include "email/base/button.html" with url=link.relative_url title=button_text %}
{% endblock message %}
41 changes: 36 additions & 5 deletions server/vb/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .ops import (
get_or_create_student,
get_or_issue_gift_card,
send_gift_card_email,
send_validation_link_email,
)

Expand All @@ -22,7 +23,17 @@ def home(request: HttpRequest) -> HttpResponse:

@require_GET
def school(request: HttpRequest, slug: str) -> HttpResponse:
"""Render a school page."""
"""
Render a school landing page.
Show details about the current contest, if there is one.
If not, show details about the most recently completed contest,
if there is one.
Otherwise, show generic text encouraging the visitor to check their
voter registration anyway.
"""
school = get_object_or_404(School, slug=slug)
current_contest = school.contests.current()
return render(
Expand All @@ -32,7 +43,11 @@ def school(request: HttpRequest, slug: str) -> HttpResponse:

@require_GET
def check(request: HttpRequest, slug: str) -> HttpResponse:
"""Render a school-specific check registration page."""
"""
Render a school-specific 'check voter registration' form page.
This does something useful whether or not the school has a current contest.
"""
school = get_object_or_404(School, slug=slug)
current_contest = school.contests.current()
return render(
Expand Down Expand Up @@ -74,7 +89,14 @@ def clean_email(self):
@require_POST
@csrf_exempt # CONSIDER: maybe use Django's CSRF protection even here?
def finish_check(request: HttpRequest, slug: str) -> HttpResponse:
"""Handle a check registration form submission."""
"""
View that is POSTed to when a student has completed a registration check.
There may or may not be a current contest associated with the check.
In addition, while we know the student's email ends with *.edu, we do not
yet know if it is a valid email address for the school.
"""
school = get_object_or_404(School, slug=slug)
current_contest = school.contests.current()
if not current_contest:
Expand Down Expand Up @@ -111,15 +133,24 @@ def finish_check(request: HttpRequest, slug: str) -> HttpResponse:

@require_GET
def validate_email(request: HttpRequest, slug: str, token: str) -> HttpResponse:
"""Handle a student email validation link."""
"""
View visited when a user clicks on a validation link in their email.
There may or may not be a current contest associated with the validation.
If the student reaches this point, we know they have a valid email that
matches the school in question.
"""
link = get_object_or_404(EmailValidationLink, token=token)
school = get_object_or_404(School, slug=slug)
if link.school != school:
raise PermissionDenied("Invalid email validation link URL")

# It's money time!
link.consume()
gift_card, claim_code = get_or_issue_gift_card(link.student, link.contest)
gift_card, claim_code, created = get_or_issue_gift_card(link.student, link.contest)
if created:
send_gift_card_email(link.student, gift_card, claim_code, link.email)

return render(
request,
Expand Down

0 comments on commit f27f9b8

Please sign in to comment.