Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions manage_breast_screening/notifications/services/nhs_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from logging import getLogger
from smtplib import SMTP

from django.template.loader import render_to_string

logger = getLogger(__name__)

SMTP_SERVER = "smtp.office365.com"
Expand Down Expand Up @@ -55,7 +57,7 @@ def send_report_email(
def _get_email_content(self, attachment_data, attachment_filename, report_type):
todays_date = datetime.today().strftime("%d-%m-%Y")
content = (
self.failure_report_content(todays_date)
self.invites_not_sent_content(todays_date)
if report_type == "invites_not_sent"
else self.aggregate_report_content(todays_date)
)
Expand All @@ -76,19 +78,17 @@ def _get_email_content(self, attachment_data, attachment_filename, report_type):

return message

def failure_report_content(self, date) -> dict[str, str]:
def invites_not_sent_content(self, date) -> dict[str, str]:
return {
"subject": self._failure_report_subject_text(date),
"body": self._failure_report_body_text(),
"subject": self._invites_not_sent_subject_text(date),
"body": self._invites_not_sent_body_text(),
}

def _failure_report_subject_text(self, date) -> str:
def _invites_not_sent_subject_text(self, date) -> str:
return f"Breast screening digital comms invites not sent report – {date} – Birmingham (MCR)"

def _failure_report_body_text(self) -> str:
return f"""Hello \n
Please find invites not sent report attached. \n
For any questions please email {self._sender_email}"""
def _invites_not_sent_body_text(self) -> str:
return render_to_string("report_emails/invites_not_sent.html")

def aggregate_report_content(self, date) -> dict[str, str]:
return {
Expand All @@ -100,6 +100,4 @@ def _aggregate_report_subject_text(self, date) -> str:
return f"Breast screening digital comms daily aggregate report – {date} – Birmingham (MCR)"

def _aggregate_report_body_text(self) -> str:
return f"""Hello \n
Please find daily aggregate report attached. \n
For any questions please email {self._sender_email}"""
return render_to_string("report_emails/aggregate.html")
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html>
<body>
<p>Hello.</p>
<p>
Attached to the email is the daily report for breast screening digital
comms. For more information on the data in the report, please check the
guidance here.
</p>
<p>Thank you.</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<body>
<p>Hello.</p>
<p>
It's important that you take action when you receive this email.This is a
list of clients who we haven't been able to invite to their breast
screening appointment.
</p>
<p>Please contact them using your standard operating procedure</p>
<p>Thank you.</p>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import base64
import email
import smtplib
from datetime import datetime
from email.header import decode_header
from email.header import decode_header, make_header
from unittest.mock import ANY, MagicMock, patch

import pytest
Expand Down Expand Up @@ -54,22 +53,29 @@ def test_sends_a_failure_report_email(self, mock_smtp_server, csv_data):
mock_smtp_server.sendmail.assert_called_once_with(
"sender@nhsmail.net", ["recipient@nhsmail.net"], ANY
)

email_content = mock_smtp_server.sendmail.call_args[0][2]
mime_message = email.message_from_string(email_content)

decoded_email = email.message_from_string(email_content)
decoded_subject_line = decode_header(decoded_email["Subject"])[0][0].decode(
"utf-8"
)
decoded_subject_line = str(make_header(decode_header(mime_message["Subject"])))
assert (
"Breast screening digital comms invites not sent report – 11-10-2025 – Birmingham (MCR)"
in decoded_subject_line
)
assert "Please find invites not sent report attached." in email_content
assert (
"It's important that you take action when you receive this email"
in email_content
)

assert "filename.csv" in email_content
decoded_attachment_data = base64.b64encode(csv_data.encode("utf-8")).decode(
"utf-8"
decoded_attachment_data = (
mime_message.get_payload()[1] # type: ignore
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really delving deep into the complexities of email types... happy to take any suggestions if there's a simpler way of decoding 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably lacking a bit of context here but there's an iter_attachments method on email.message which might be a cleaner way to obtain the right part. Given this is a test I wouldn't be too concerned about the current code though.

.get_payload( # type: ignore
None, True
)
.decode("utf-8") # type: ignore
)
assert decoded_attachment_data in email_content
assert csv_data in decoded_attachment_data

def test_sends_an_aggregate_report_email(self, mock_smtp_server, csv_data):
subject = NhsMail()
Expand All @@ -80,23 +86,27 @@ def test_sends_an_aggregate_report_email(self, mock_smtp_server, csv_data):
"sender@nhsmail.net", ["recipient@nhsmail.net"], ANY
)
email_content = mock_smtp_server.sendmail.call_args[0][2]
mime_message = email.message_from_string(email_content)

decoded_email = email.message_from_string(email_content)
decoded_subject_line = decode_header(decoded_email["Subject"])[0][0].decode(
"utf-8"
)
decoded_subject_line = str(make_header(decode_header(mime_message["Subject"])))

assert (
"Breast screening digital comms daily aggregate report – 11-10-2025 – Birmingham (MCR)"
in decoded_subject_line
)
assert "Please find invites not sent report attached." not in email_content
assert "Please find daily aggregate report attached." in email_content
assert (
"Attached to the email is the daily report for breast screening digital"
in email_content
)
assert "filename.csv" in email_content
decoded_attachment_data = base64.b64encode(csv_data.encode("utf-8")).decode(
"utf-8"
decoded_attachment_data = (
mime_message.get_payload()[1] # type: ignore
.get_payload( # type: ignore
None, True
)
.decode("utf-8") # type: ignore
)
assert decoded_attachment_data in email_content
assert csv_data in decoded_attachment_data

def test_raises_exception_if_email_fails(self, mock_smtp_server, csv_data):
mock_smtp_server.sendmail.side_effect = smtplib.SMTPException
Expand Down