Skip to content

Commit

Permalink
send_welcome_emails command
Browse files Browse the repository at this point in the history
Sends emails to users who have a Marker with send_welcome_email set to
True. This is an improvement on the previous send_password_reset_emails
as it makes it more explicit who should be getting emails, and can also
send emails to already registered users, who get a slightly different
email. Also enables restriction by stage and marking session as an extra
safeguard

Fixes #183
  • Loading branch information
struan committed Sep 19, 2024
1 parent edac52c commit 81d369e
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 0 deletions.
59 changes: 59 additions & 0 deletions crowdsourcer/fixtures/welcome_email_users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[
{
"model": "auth.user",
"pk": 101,
"fields": {
"password": "thisisasecret",
"last_login": null,
"is_superuser": true,
"username": "already_registered",
"first_name": "",
"last_name": "",
"email": "[email protected]",
"is_staff": false,
"is_active": true,
"date_joined": "2022-12-19T13:40:36.075Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "auth.user",
"pk": 102,
"fields": {
"password": "",
"last_login": null,
"is_superuser": false,
"username": "new_marker",
"first_name": "Anew",
"last_name": "Marker",
"email": "[email protected]",
"is_staff": false,
"is_active": true,
"date_joined": "2022-12-19T13:40:36.075Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "crowdsourcer.marker",
"pk": 101,
"fields": {
"user_id": 101,
"response_type_id": 1,
"authority_id": 2,
"send_welcome_email": true,
"marking_session": [1]
}
},
{
"model": "crowdsourcer.marker",
"pk": 102,
"fields": {
"user_id": 102,
"response_type_id": 1,
"send_welcome_email": true,
"marking_session": [1]
}
}
]
105 changes: 105 additions & 0 deletions crowdsourcer/management/commands/send_welcome_emails.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from time import sleep

from django.contrib.auth.forms import PasswordResetForm
from django.core.management.base import BaseCommand
from django.http import HttpRequest
from django.utils.crypto import get_random_string

from crowdsourcer.models import Marker, MarkingSession, ResponseType

YELLOW = "\033[33m"
NOBOLD = "\033[0m"


class Command(BaseCommand):
help = "Emails password reset instructions to all users"

new_user_template = "registration/initial_password_email.html"
previous_user_template = "registration/repeat_password_email.html"

def add_arguments(self, parser):
parser.add_argument("--send_emails", action="store_true", help="Send emails")

parser.add_argument(
"--stage", action="store", help="Only send emails to people in this stage"
)

parser.add_argument(
"--session",
action="store",
help="Only send emails to people in this session",
)

def handle(self, *args, **kwargs):
if not kwargs["send_emails"]:
self.stdout.write(
f"{YELLOW}Not sending emails. Call with --send_emails to send{NOBOLD}"
)

users = Marker.objects.filter(send_welcome_email=True).select_related("user")

if kwargs["stage"]:
try:
rt = ResponseType.objects.get(type=kwargs["stage"])
users = users.filter(response_type=rt)
except ResponseType.NotFoundException:
self.stderr.write(f"{YELLOW}No such stage: {kwargs['stage']}{NOBOLD}")
return

if kwargs["session"]:
try:
rt = MarkingSession.objects.get(label=kwargs["session"])
users = users.filter(marking_session=rt)
except ResponseType.NotFoundException:
self.stderr.write(
f"{YELLOW}No such session: {kwargs['session']}{NOBOLD}"
)
return

user_count = users.count()
self.stdout.write(f"Sending emails for {user_count} users")
count = 0
for marker in users:
user = marker.user
try:
if user.email:
self.stdout.write(f"Sending email for to this email: {user.email}")
if kwargs["send_emails"]:
template = self.new_user_template
if user.password == "":
user.set_password(get_random_string(length=20))
user.save()
else:
template = self.previous_user_template

form = PasswordResetForm({"email": user.email})
assert form.is_valid()
request = HttpRequest()
request.META["SERVER_NAME"] = (
"marking.councilclimatescorecards.uk"
)
request.META["SERVER_PORT"] = 443
form.save(
request=request,
domain_override="marking.councilclimatescorecards.uk",
use_https=True,
from_email="CEUK Scorecards Marking <[email protected]>",
subject_template_name="registration/initial_password_email_subject.txt",
email_template_name=template,
)
marker.send_welcome_email = False
marker.save()
sleep(1)
count = count + 1
except Exception as e:
print(e)
continue

if kwargs["send_emails"]:
self.stdout.write(f"Sent {count} emails")
else:
self.stdout.write(
f"{YELLOW}Dry Run{NOBOLD}. Live would have sent {count} emails"
)

return "done"
91 changes: 91 additions & 0 deletions crowdsourcer/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest import skip

from django.contrib.auth.models import User
from django.core import mail
from django.core.management import call_command
from django.test import TestCase

Expand Down Expand Up @@ -516,3 +517,93 @@ def test_multiple_choice_question(self):
self.assertIsNotNone(r.multi_option)

self.assertEquals(r.multi_option.all()[0].description, "Car share")


class SendWelcomeEmails(BaseCommandTestCase):
fixtures = [
"basics.json",
"authorities.json",
"users.json",
"welcome_email_users.json",
]

def test_basic_run(self):
self.assertEquals(len(mail.outbox), 0)
self.call_command(
"send_welcome_emails",
)
self.assertEquals(len(mail.outbox), 0)

self.call_command(
"send_welcome_emails",
send_emails=True,
)
self.assertEquals(len(mail.outbox), 2)

self.call_command(
"send_welcome_emails",
send_emails=True,
)
self.assertEquals(len(mail.outbox), 2)

def test_only_sends_if_flag_set(self):
marker = Marker.objects.get(user__email="[email protected]")
marker.send_welcome_email = False
marker.save()

self.assertEquals(len(mail.outbox), 0)

self.call_command(
"send_welcome_emails",
send_emails=True,
)
self.assertEquals(len(mail.outbox), 1)
email = mail.outbox[0]
self.assertEquals(email.to, ["[email protected]"])

def test_email_comtent(self):
self.call_command(
"send_welcome_emails",
send_emails=True,
)

emails = mail.outbox

text = "your previous password"
for m in emails:
if m.to[0] == "[email protected]":
self.assertTrue(m.body.rfind(text) >= 0)
else:
self.assertTrue(m.body.rfind(text) == -1)

def test_limit_stage(self):
self.assertEquals(len(mail.outbox), 0)
self.call_command(
"send_welcome_emails",
send_emails=True,
stage="Audit",
)
self.assertEquals(len(mail.outbox), 0)

self.call_command(
"send_welcome_emails",
send_emails=True,
stage="First Mark",
)
self.assertEquals(len(mail.outbox), 2)

def test_limit_session(self):
self.assertEquals(len(mail.outbox), 0)
self.call_command(
"send_welcome_emails",
send_emails=True,
session="Second Session",
)
self.assertEquals(len(mail.outbox), 0)

self.call_command(
"send_welcome_emails",
send_emails=True,
session="Default",
)
self.assertEquals(len(mail.outbox), 2)
22 changes: 22 additions & 0 deletions templates/registration/repeat_password_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% autoescape off %}
Hi,

You're receiving this email because you registered to volunteer for the CEUK Council Climate Scorecards. This email allows you to log into the online data collection system that we will use to score Councils.

If you can remember your previous password from the 2023 Scorecards then you can log in with your username at

{{ protocol }}://{{ domain }}

If you cannot remember your password then please go to the following page and choose a new password:
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}

Your username is {{ user.get_username }}

Thanks for volunteering!

The CEUK team.

{% endautoescape %}

0 comments on commit 81d369e

Please sign in to comment.