Skip to content

Commit

Permalink
add celery notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
depsiatwal committed Feb 7, 2024
1 parent ffa40fe commit f54b121
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 1 deletion.
24 changes: 24 additions & 0 deletions api/cases/celery_tasks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime, time
from api.cases.ecju_query_notifications import send_chaser_notifications

from celery import shared_task
from celery.utils.log import get_task_logger
Expand Down Expand Up @@ -156,3 +157,26 @@ def update_cases_sla():

logger.info("SLA Update Not Performed. Non-working day")
return False


WORKING_DAYS_ECJU_CHASER_REMINDER = 15


@shared_task(
autoretry_for=(Exception,),
max_retries=MAX_ATTEMPTS,
retry_backoff=RETRY_BACKOFF,
)
def send_ecju_query_chaser_notification():
"""
Sends an ECJU 15 working days reminder
Runs as a background task daily at a given time.
Doesn't run on non-working days (bank-holidays & weekends)
"""
logger.info("Sending ECJU Query Chaser Started")

try:
send_chaser_notifications(WORKING_DAYS_ECJU_CHASER_REMINDER)
except Exception as e: # noqa
logger.error(e)
logger.info("Sending ECJU Query Chaser Finished")
14 changes: 14 additions & 0 deletions api/cases/ecju_query_notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from api.cases.models import EcjuQuery
from api.cases.notify import notify_exporter_ecju_query_chaser


def send_chaser_notifications(working_days_ecju_chaser_reminder=15):
case_id_reminders = set()
ecju_queries = EcjuQuery.objects.filter(is_query_closed=False)
for ecju_query in ecju_queries:
if ecju_query.open_working_days == working_days_ecju_chaser_reminder:
case_id_reminders.add(ecju_query.case_id)

for case_id_reminder in case_id_reminders:
# Now lets loop round and send the notifications
notify_exporter_ecju_query_chaser(case_id_reminder)
25 changes: 25 additions & 0 deletions api/cases/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from gov_notify.enums import TemplateType
from gov_notify.payloads import (
ExporterECJUQuery,
ExporterECJUQueryChaser,
ExporterLicenceIssued,
ExporterLicenceRefused,
ExporterNoLicenceRequired,
Expand Down Expand Up @@ -104,11 +105,35 @@ def notify_exporter_ecju_query(case_pk):
)


def notify_exporter_ecju_query_chaser(case_pk):
case_info = (
Case.objects.annotate(email=F("submitted_by__baseuser_ptr__email"))
.values("id", "email", "reference_code")
.get(id=case_pk)
)
# This deliberately avoids using a more specific URL since there are a few possible here,
# so the likelihood of them changing in the exporter app is higher
exporter_frontend_ecju_queries_url = get_exporter_frontend_url(f"/applications/{case_pk}/ecju-queries/")

_notify_exporter_ecju_query_chaser(
case_info["email"],
{
"case_reference": case_info["reference_code"],
"exporter_frontend_ecju_queries_url": exporter_frontend_ecju_queries_url,
},
)


def _notify_exporter_ecju_query(email, data):
payload = ExporterECJUQuery(**data)
send_email(email, TemplateType.EXPORTER_ECJU_QUERY, payload)


def _notify_exporter_ecju_query_chaser(email, data):
payload = ExporterECJUQueryChaser(**data)
send_email(email, TemplateType.EXPORTER_ECJU_QUERY_CHASER, payload)


def _notify_exporter_no_licence_required(email, data):
payload = ExporterNoLicenceRequired(**data)
send_email(email, TemplateType.EXPORTER_NO_LICENCE_REQUIRED, payload)
Expand Down
90 changes: 90 additions & 0 deletions api/cases/tests/test_case_ecju_queries.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import pytest
import datetime
from unittest import mock
from datetime import timedelta

from django.urls import reverse
from django.utils import timezone
from api.cases.celery_tasks import send_ecju_query_chaser_notification
from api.cases.tests.factories import EcjuQueryFactory
from api.users.models import BaseNotification
from faker import Faker
from gov_notify.payloads import ExporterECJUQueryChaser
from parameterized import parameterized
from rest_framework import status
from freezegun import freeze_time
Expand All @@ -23,6 +27,7 @@
from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status
from test_helpers.clients import DataTestClient
from api.users.tests.factories import ExporterUserFactory
from gov_notify.enums import TemplateType

faker = Faker()

Expand Down Expand Up @@ -681,3 +686,88 @@ def test_exporter_cannot_delete_documents_of_closed_query(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()
self.assertIsNotNone(response["document"]["id"])


class ECJUQueriesChaserNotificationTests(DataTestClient):
@freeze_time("2024-02-06 12:00:00")
def setUp(self):
super().setUp()
self.case = self.create_standard_application_case(self.organisation)
self.date_15_working_days_from_today = datetime.datetime(2024, 1, 16, 12, 00)

EcjuQueryFactory(
question="ECJU Query 15 days",
case=self.case,
raised_by_user=self.gov_user,
response=None,
created_at=self.date_15_working_days_from_today,
)

@freeze_time("2024-02-06 12:00:00")
@mock.patch("api.cases.notify.send_email")
def test_send_ecju_query_notification_filters(self, mock_send_email):

self.case2 = self.create_standard_application_case(self.organisation)

EcjuQueryFactory(
question="ECJU Query 2 15 days",
case=self.case,
response="I have a response",
raised_by_user=self.gov_user,
responded_by_user=self.base_user,
query_type=PicklistType.ECJU,
created_at=self.date_15_working_days_from_today,
)

EcjuQueryFactory(
question="ECJU Query 15 days same case",
case=self.case,
raised_by_user=self.gov_user,
response=None,
created_at=self.date_15_working_days_from_today,
)

EcjuQueryFactory(
question="ECJU Query 15 days case 2",
case=self.case2,
raised_by_user=self.gov_user,
created_at=self.date_15_working_days_from_today,
)
EcjuQueryFactory(
question="ECJU Query 16 days",
case=self.case,
raised_by_user=self.gov_user,
created_at=datetime.datetime(2024, 1, 15, 12, 00),
)
EcjuQueryFactory(
question="ECJU Query 14 days",
case=self.case,
raised_by_user=self.gov_user,
created_at=datetime.datetime(2024, 1, 17, 12, 00),
)

send_ecju_query_chaser_notification.apply()
assert mock_send_email.call_count == 2

@freeze_time("2024-02-06 12:00:00")
@mock.patch("api.cases.notify.send_email")
def test_send_ecju_query_notification_params(self, mock_send_email):
send_ecju_query_chaser_notification.apply()
assert mock_send_email.call_count == 1
expected_payload = ExporterECJUQueryChaser(
case_reference=self.case.reference_code,
exporter_frontend_ecju_queries_url=f"https://exporter.lite.service.localhost.uktrade.digital/applications/{self.case.pk}/ecju-queries/",
)
mock_send_email.assert_called_with(
self.case.submitted_by.email,
TemplateType.EXPORTER_ECJU_QUERY_CHASER,
expected_payload,
)

@freeze_time("2024-02-06 12:00:00")
@mock.patch("api.cases.notify.send_email")
def test_send_ecju_query_notification_raises_exception(self, mock_send_email):
mock_send_email.side_effect = Exception("Failed to send email")
with pytest.raises(Exception):
assert mock_send_email.call_count == 1
send_ecju_query_chaser_notification.apply()
2 changes: 2 additions & 0 deletions gov_notify/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class TemplateType(Enum):
EXPORTER_ORGANISATION_REJECTED = "exporter_organisation_rejected"
EXPORTER_CASE_OPENED_FOR_EDITING = "exporter_editing"
EXPORTER_ECJU_QUERY = "exporter_ecju_query"
EXPORTER_ECJU_QUERY_CHASER = "exporter_ecju_query_chaser"
EXPORTER_NO_LICENCE_REQUIRED = "exporter_no_licence_required"
EXPORTER_INFORM_LETTER = "exporter_inform_letter"
EXPORTER_APPEAL_ACKNOWLEDGEMENT = "exporter_appeal_acknowledgement"
Expand All @@ -33,6 +34,7 @@ def template_id(self):
self.EXPORTER_ORGANISATION_APPROVED: "d5e94717-ae78-4d18-8064-ecfcd99143f1",
self.EXPORTER_ORGANISATION_REJECTED: "1dec3acd-94b0-47bb-832a-384ba5c6f51a",
self.EXPORTER_ECJU_QUERY: "84431173-72a9-43a1-8926-b43dec7871f9",
self.EXPORTER_ECJU_QUERY_CHASER: "3ba8579c-ba2a-40bc-a302-9429cc465c96",
self.EXPORTER_CASE_OPENED_FOR_EDITING: "73121bc2-2f03-4c66-8e88-61a156c05559",
self.EXPORTER_NO_LICENCE_REQUIRED: "d84d1843-882c-440e-9cd4-84972ba612e6",
self.EXPORTER_INFORM_LETTER: "7b63296f-af08-46bf-961e-19bdde93761c",
Expand Down
6 changes: 6 additions & 0 deletions gov_notify/payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ class ExporterECJUQuery(EmailData):
exporter_frontend_url: str


@dataclass(frozen=True)
class ExporterECJUQueryChaser(EmailData):
case_reference: str
exporter_frontend_ecju_queries_url: str


@dataclass(frozen=True)
class CaseWorkerNewRegistration(EmailData):
organisation_name: str
Expand Down
1 change: 0 additions & 1 deletion gov_notify/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def send_email(email_address, template_type, data: Optional[EmailData] = None):
if not settings.GOV_NOTIFY_ENABLED:
logging.info({"gov_notify": "disabled"})
return

data = data.as_dict() if data else None
logger.info("sending email via celery")
return celery_send_email.apply_async([email_address, template_type.template_id, data])

0 comments on commit f54b121

Please sign in to comment.