diff --git a/measures/jinja2/measures/search.jinja b/measures/jinja2/measures/search.jinja
index bf280f75f..3c6334149 100644
--- a/measures/jinja2/measures/search.jinja
+++ b/measures/jinja2/measures/search.jinja
@@ -18,7 +18,6 @@
-
Search and filter
diff --git a/measures/tests/test_forms.py b/measures/tests/test_forms.py
index 5b1c5be2e..ac3343734 100644
--- a/measures/tests/test_forms.py
+++ b/measures/tests/test_forms.py
@@ -1590,3 +1590,29 @@ def test_measure_review_form_validates_components_applicability_exclusivity(
"A duty cannot be specified on both commodities and conditions"
in form.errors["__all__"]
)
+
+
+def test_measure_geographical_area_exclusions_form_valid_choice():
+ """Tests that `MeasureGeographicalAreaExclusionsForm` is valid when an
+ available geographical area is selected."""
+ geo_area = factories.GeographicalAreaFactory.create()
+ data = {
+ "excluded_area": geo_area.pk,
+ }
+ with override_current_transaction(geo_area.transaction):
+ form = forms.MeasureGeographicalAreaExclusionsForm(data)
+ assert form.is_valid()
+ assert form.cleaned_data["excluded_area"] == geo_area
+
+
+def test_measure_geographical_area_exclusions_form_invalid_choice():
+ """Tests that `MeasureGeographicalAreaExclusionsForm` raises an raises an
+ invalid choice error when an unavailable geographical area is selected."""
+ data = {
+ "excluded_area": "geo_area",
+ }
+ form = forms.MeasureGeographicalAreaExclusionsForm(data)
+ assert not form.is_valid()
+ assert form.errors["excluded_area"] == [
+ "Select a valid choice. That choice is not one of the available choices.",
+ ]
diff --git a/measures/tests/test_views.py b/measures/tests/test_views.py
index e09a28733..99469d3ed 100644
--- a/measures/tests/test_views.py
+++ b/measures/tests/test_views.py
@@ -47,6 +47,17 @@
pytestmark = pytest.mark.django_db
+@pytest.fixture()
+def mocked_diff_components():
+ """Mocks `diff_components()` inside `update_measure_components()` in
+ `MeasureEditWizard` to prevent parsing errors where test measures lack a
+ duty sentence."""
+ with patch(
+ "measures.views.MeasureEditWizard.update_measure_components",
+ ) as update_measure_components:
+ yield update_measure_components
+
+
def test_measure_footnotes_update_get_delete_key():
footnote_key = "form-0-footnote"
expected = "form-0-DELETE"
@@ -1718,6 +1729,7 @@ def test_measuretype_api_list_view(valid_user_client):
def test_multiple_measure_start_and_end_date_edit_functionality(
valid_user_client,
session_workbasket,
+ mocked_diff_components,
):
"""Tests that MeasureEditWizard takes a list of measures, and sets their
update type to update, updates their end dates and start dates, and clears
@@ -1841,6 +1853,7 @@ def test_multiple_measure_edit_single_form_functionality(
data,
valid_user_client,
session_workbasket,
+ mocked_diff_components,
):
"""Tests that MeasureEditWizard takes a list of measures, and sets their
update type to update, updates their end dates and start dates, and clears
@@ -1913,6 +1926,7 @@ def test_multiple_measure_edit_single_form_functionality(
def test_multiple_measure_edit_only_regulation(
valid_user_client,
session_workbasket,
+ mocked_diff_components,
):
"""Tests the regulation step in MeasureEditWizard."""
measure_1 = factories.MeasureFactory.create()
@@ -2147,6 +2161,7 @@ def test_measure_list_selected_measures_list(valid_user_client):
def test_multiple_measure_edit_only_quota_order_number(
valid_user_client,
session_workbasket,
+ mocked_diff_components,
):
"""Tests the regulation step in MeasureEditWizard."""
measure_1 = factories.MeasureFactory.create()
@@ -2298,6 +2313,7 @@ def test_multiple_measure_edit_only_duties(
def test_multiple_measure_edit_preserves_footnote_associations(
valid_user_client,
session_workbasket,
+ mocked_diff_components,
):
"""Tests that footnote associations are preserved in MeasureEditWizard."""
@@ -2375,6 +2391,84 @@ def test_multiple_measure_edit_preserves_footnote_associations(
assert footnote in expected_footnotes
+def test_multiple_measure_edit_geographical_area_exclusions(
+ valid_user_client,
+ session_workbasket,
+ mocked_diff_components,
+):
+ """Tests that the geographical area exclusions of multiple measures can be
+ edited in `MeasureEditWizard`."""
+ measure_1 = factories.MeasureFactory.create(with_exclusion=True)
+ measure_2 = factories.MeasureFactory.create()
+ new_excluded_area = factories.CountryFactory.create()
+
+ url = reverse("measure-ui-edit-multiple")
+ session = valid_user_client.session
+ session.update(
+ {
+ "workbasket": {
+ "id": session_workbasket.pk,
+ },
+ "MULTIPLE_MEASURE_SELECTIONS": {
+ measure_1.pk: 1,
+ measure_2.pk: 1,
+ },
+ },
+ )
+ session.save()
+
+ STEP_KEY = "measure_edit_wizard-current_step"
+
+ wizard_data = [
+ {
+ "data": {
+ STEP_KEY: START,
+ "start-fields_to_edit": [MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS],
+ },
+ "next_step": MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS,
+ },
+ {
+ "data": {
+ STEP_KEY: MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS,
+ "form-0-excluded_area": new_excluded_area.pk,
+ },
+ "next_step": "complete",
+ },
+ ]
+ for step_data in wizard_data:
+ url = reverse(
+ "measure-ui-edit-multiple",
+ kwargs={"step": step_data["data"][STEP_KEY]},
+ )
+ response = valid_user_client.get(url)
+ assert response.status_code == 200
+
+ response = valid_user_client.post(url, step_data["data"])
+ assert response.status_code == 302
+
+ assert response.url == reverse(
+ "measure-ui-edit-multiple",
+ kwargs={"step": step_data["next_step"]},
+ )
+
+ complete_response = valid_user_client.get(response.url)
+ assert complete_response.status_code == 302
+ assert valid_user_client.session["MULTIPLE_MEASURE_SELECTIONS"] == {}
+
+ workbasket_measures = Measure.objects.filter(
+ transaction__workbasket=session_workbasket,
+ )
+ assert workbasket_measures
+
+ with override_current_transaction(Transaction.objects.last()):
+ for measure in workbasket_measures:
+ assert measure.update_type == UpdateType.UPDATE
+ assert (
+ measure.exclusions.current().first().excluded_geographical_area
+ == new_excluded_area
+ )
+
+
def test_measure_list_redirects_to_search_with_no_params(valid_user_client):
response = valid_user_client.get(reverse("measure-ui-list"))
assert response.status_code == 302
diff --git a/measures/views.py b/measures/views.py
index ff13f6daa..ff7423c8f 100644
--- a/measures/views.py
+++ b/measures/views.py
@@ -3,6 +3,7 @@
from itertools import groupby
from operator import attrgetter
from typing import Any
+from typing import List
from typing import Type
from urllib.parse import urlencode
@@ -34,6 +35,8 @@
from common.views import TamatoListView
from common.views import TrackedModelDetailMixin
from common.views import TrackedModelDetailView
+from geo_areas.models import GeographicalArea
+from geo_areas.utils import get_all_members_of_geo_groups
from measures import forms
from measures.constants import START
from measures.constants import MeasureEditSteps
@@ -44,6 +47,7 @@
from measures.models import Measure
from measures.models import MeasureActionPair
from measures.models import MeasureConditionComponent
+from measures.models import MeasureExcludedGeographicalArea
from measures.models import MeasureType
from measures.pagination import MeasurePaginator
from measures.parsers import DutySentenceParser
@@ -267,10 +271,15 @@ class MeasureEditWizard(
(MeasureEditSteps.QUOTA_ORDER_NUMBER, forms.MeasureQuotaOrderNumberForm),
(MeasureEditSteps.REGULATION, forms.MeasureRegulationForm),
(MeasureEditSteps.DUTIES, forms.MeasureDutiesForm),
+ (
+ MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS,
+ forms.MeasureGeographicalAreaExclusionsFormSet,
+ ),
]
templates = {
START: "measures/edit-multiple-start.jinja",
+ MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS: "measures/edit-multiple-formset.jinja",
}
step_metadata = {
@@ -293,6 +302,9 @@ class MeasureEditWizard(
MeasureEditSteps.DUTIES: {
"title": "Edit the duties",
},
+ MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS: {
+ "title": "Edit the geographical area exclusions",
+ },
}
def get_template_names(self):
@@ -313,7 +325,11 @@ def get_context_data(self, form, **kwargs):
def get_form_kwargs(self, step):
kwargs = {}
- if step not in [START, MeasureEditSteps.QUOTA_ORDER_NUMBER]:
+ if step not in [
+ START,
+ MeasureEditSteps.QUOTA_ORDER_NUMBER,
+ MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS,
+ ]:
kwargs["selected_measures"] = self.get_queryset()
if step == MeasureEditSteps.DUTIES:
@@ -328,6 +344,103 @@ def get_form_kwargs(self, step):
return kwargs
+ def update_measure_components(
+ self,
+ measure: Measure,
+ duties: str,
+ workbasket: WorkBasket,
+ ):
+ """Updates the measure components associated to the measure."""
+ diff_components(
+ instance=measure,
+ duty_sentence=duties if duties else measure.duty_sentence,
+ start_date=measure.valid_between.lower,
+ workbasket=workbasket,
+ transaction=workbasket.current_transaction,
+ )
+
+ def update_measure_condition_components(
+ self,
+ measure: Measure,
+ workbasket: WorkBasket,
+ ):
+ """Updates the measure condition components associated to the
+ measure."""
+ conditions = measure.conditions.current()
+ for condition in conditions:
+ condition.new_version(
+ dependent_measure=measure,
+ workbasket=workbasket,
+ )
+
+ def update_measure_excluded_geographical_areas(
+ self,
+ edited: bool,
+ measure: Measure,
+ exclusions: List[GeographicalArea],
+ workbasket: WorkBasket,
+ ):
+ """Updates the excluded geographical areas associated to the measure."""
+ existing_exclusions = measure.exclusions.current()
+
+ # Update any exclusions to new measure version
+ if not edited:
+ for exclusion in existing_exclusions:
+ exclusion.new_version(
+ modified_measure=measure,
+ workbasket=workbasket,
+ )
+ return
+
+ new_excluded_areas = get_all_members_of_geo_groups(
+ validity=measure.valid_between,
+ geo_areas=exclusions,
+ )
+
+ for geo_area in new_excluded_areas:
+ existing_exclusion = existing_exclusions.filter(
+ excluded_geographical_area=geo_area,
+ ).first()
+ if existing_exclusion:
+ existing_exclusion.new_version(
+ modified_measure=measure,
+ workbasket=workbasket,
+ )
+ else:
+ MeasureExcludedGeographicalArea.objects.create(
+ modified_measure=measure,
+ excluded_geographical_area=geo_area,
+ update_type=UpdateType.CREATE,
+ transaction=workbasket.new_transaction(),
+ )
+
+ removed_excluded_areas = {
+ e.excluded_geographical_area for e in existing_exclusions
+ }.difference(set(exclusions))
+
+ exclusions_to_remove = [
+ existing_exclusions.get(excluded_geographical_area__id=geo_area.id)
+ for geo_area in removed_excluded_areas
+ ]
+
+ for exclusion in exclusions_to_remove:
+ exclusion.new_version(
+ update_type=UpdateType.DELETE,
+ modified_measure=measure,
+ workbasket=workbasket,
+ )
+
+ def update_measure_footnote_associations(self, measure, workbasket):
+ """Updates the footnotes associated to the measure."""
+ footnote_associations = FootnoteAssociationMeasure.objects.current().filter(
+ footnoted_measure__sid=measure.sid,
+ )
+ for fa in footnote_associations:
+ fa.new_version(
+ footnoted_measure=measure,
+ workbasket=workbasket,
+ )
+
def done(self, form_list, **kwargs):
cleaned_data = self.get_all_cleaned_data()
selected_measures = self.get_queryset()
@@ -349,6 +462,10 @@ def done(self, form_list, **kwargs):
new_quota_order_number = cleaned_data.get("order_number", None)
new_generating_regulation = cleaned_data.get("generating_regulation", None)
new_duties = cleaned_data.get("duties", None)
+ new_exclusions = [
+ e["excluded_area"]
+ for e in cleaned_data.get("formset-geographical_area_exclusions", [])
+ ]
for measure in selected_measures:
new_measure = measure.new_version(
workbasket=workbasket,
@@ -366,24 +483,27 @@ def done(self, form_list, **kwargs):
if new_generating_regulation
else measure.generating_regulation,
)
- if new_duties:
- diff_components(
- instance=new_measure,
- duty_sentence=new_duties,
- start_date=new_measure.valid_between.lower,
- workbasket=workbasket,
- transaction=workbasket.current_transaction,
- )
- footnote_associations = FootnoteAssociationMeasure.objects.current().filter(
- footnoted_measure=measure,
+ self.update_measure_components(
+ measure=new_measure,
+ duties=new_duties,
+ workbasket=workbasket,
)
- for fa in footnote_associations:
- fa.new_version(
- workbasket=workbasket,
- update_type=UpdateType.UPDATE,
- footnoted_measure=new_measure,
- )
- self.session_store.clear()
+ self.update_measure_condition_components(
+ measure=new_measure,
+ workbasket=workbasket,
+ )
+ self.update_measure_excluded_geographical_areas(
+ edited="geographical_area_exclusions"
+ in cleaned_data.get("fields_to_edit", []),
+ measure=new_measure,
+ exclusions=new_exclusions,
+ workbasket=workbasket,
+ )
+ self.update_measure_footnote_associations(
+ measure=new_measure,
+ workbasket=workbasket,
+ )
+ self.session_store.clear()
return redirect(reverse("workbaskets:review-workbasket"))
diff --git a/notifications/models.py b/notifications/models.py
index c9faad050..59ebf4069 100644
--- a/notifications/models.py
+++ b/notifications/models.py
@@ -283,7 +283,7 @@ def notified_object(self) -> models.Model:
from publishing.models import PackagedWorkBasket
return (
- PackagedWorkBasket.objects.get(self.notified_object_pk)
+ PackagedWorkBasket.objects.get(pk=self.notified_object_pk)
if self.notified_object_pk
else None
)
@@ -393,9 +393,9 @@ def __init__(self, notified_object_pk: int):
)
def get_personalisation(self) -> dict:
- self.notified_object()
+ crown_dependicies_envelope = self.notified_object()
personalisation = {
- "envelope_id": self.packagedworkbaskets.last().envelope.envelope_id,
+ "envelope_id": crown_dependicies_envelope.packagedworkbaskets.last().envelope.envelope_id,
}
return personalisation
diff --git a/notifications/tests/conftest.py b/notifications/tests/conftest.py
index d771afd97..9849cb7a4 100644
--- a/notifications/tests/conftest.py
+++ b/notifications/tests/conftest.py
@@ -2,17 +2,20 @@
from common.tests import factories
from importer.models import ImportBatchStatus
+from publishing.models import PackagedWorkBasket
@pytest.fixture()
def goods_report_notification():
+ present_email = f"goods_report@email.co.uk" # /PS-IGNORE
+ not_present_email = f"no_goods_report@email.co.uk" # /PS-IGNORE
factories.NotifiedUserFactory(
- email="goods_report@email.co.uk", # /PS-IGNORE
+ email=present_email,
enrol_packaging=False,
enrol_goods_report=True,
)
factories.NotifiedUserFactory(
- email="no_goods_report@email.co.uk", # /PS-IGNORE
+ email=not_present_email,
)
import_batch = factories.ImportBatchFactory.create(
status=ImportBatchStatus.SUCCEEDED,
@@ -20,37 +23,137 @@ def goods_report_notification():
taric_file="goods.xml",
)
- return factories.GoodsSuccessfulImportNotificationFactory(
- notified_object_pk=import_batch.id,
+ return (
+ factories.GoodsSuccessfulImportNotificationFactory(
+ notified_object_pk=import_batch.id,
+ ),
+ present_email,
+ not_present_email,
)
@pytest.fixture()
def ready_for_packaging_notification(published_envelope_factory):
+ present_email = f"packaging@email.co.uk" # /PS-IGNORE
+ not_present_email = f"no_packaging@email.co.uk" # /PS-IGNORE
factories.NotifiedUserFactory(
- email="packaging@email.co.uk", # /PS-IGNORE
+ email=present_email,
)
factories.NotifiedUserFactory(
- email="no_packaging@email.co.uk", # /PS-IGNORE
+ email=not_present_email,
enrol_packaging=False,
)
packaged_wb = published_envelope_factory()
- return factories.EnvelopeReadyForProcessingNotificationFactory(
- notified_object_pk=packaged_wb.id,
+ return (
+ factories.EnvelopeReadyForProcessingNotificationFactory(
+ notified_object_pk=packaged_wb.id,
+ ),
+ present_email,
+ not_present_email,
)
@pytest.fixture()
def successful_publishing_notification(crown_dependencies_envelope_factory):
+ present_email = f"publishing@email.co.uk" # /PS-IGNORE
+ not_present_email = f"no_publishing@email.co.uk" # /PS-IGNORE
factories.NotifiedUserFactory(
- email="publishing@email.co.uk", # /PS-IGNORE
+ email=present_email,
enrol_packaging=False,
enrol_api_publishing=True,
)
factories.NotifiedUserFactory(
- email="no_publishing@email.co.uk", # /PS-IGNORE
+ email=not_present_email,
)
cde = crown_dependencies_envelope_factory()
- return factories.CrownDependenciesEnvelopeSuccessNotificationFactory(
- notified_object_pk=cde.id,
+ return (
+ factories.CrownDependenciesEnvelopeSuccessNotificationFactory(
+ notified_object_pk=cde.id,
+ ),
+ present_email,
+ not_present_email,
+ )
+
+
+@pytest.fixture()
+def failed_publishing_notification(successful_envelope_factory, settings):
+ present_email = f"publishing@email.co.uk" # /PS-IGNORE
+ not_present_email = f"no_publishing@email.co.uk" # /PS-IGNORE
+ factories.NotifiedUserFactory(
+ email=present_email,
+ enrol_packaging=False,
+ enrol_api_publishing=True,
+ )
+ factories.NotifiedUserFactory(
+ email=not_present_email,
+ )
+
+ settings.ENABLE_PACKAGING_NOTIFICATIONS = False
+ successful_envelope_factory()
+ pwb = PackagedWorkBasket.objects.get_unpublished_to_api().last()
+ crown_dependencies_envelope = factories.CrownDependenciesEnvelopeFactory(
+ packaged_work_basket=pwb,
+ )
+
+ crown_dependencies_envelope.publishing_failed()
+ return (
+ factories.CrownDependenciesEnvelopeFailedNotificationFactory(
+ notified_object_pk=crown_dependencies_envelope.id,
+ ),
+ present_email,
+ not_present_email,
+ )
+
+
+@pytest.fixture()
+def accepted_packaging_notification(successful_envelope_factory, settings):
+ present_email = f"packaging@email.co.uk" # /PS-IGNORE
+ not_present_email = f"no_packaging@email.co.uk" # /PS-IGNORE
+ factories.NotifiedUserFactory(
+ email=present_email,
+ )
+ factories.NotifiedUserFactory(
+ email=not_present_email,
+ enrol_packaging=False,
+ )
+
+ ### disable so it doesn't create it's own notification
+ settings.ENABLE_PACKAGING_NOTIFICATIONS = False
+ envelope = successful_envelope_factory()
+ packaged_wb = PackagedWorkBasket.objects.get(
+ envelope=envelope,
+ )
+ return (
+ factories.EnvelopeAcceptedNotificationFactory(
+ notified_object_pk=packaged_wb.id,
+ ),
+ present_email,
+ not_present_email,
+ )
+
+
+@pytest.fixture()
+def rejected_packaging_notification(failed_envelope_factory, settings):
+ present_email = f"packaging@email.co.uk" # /PS-IGNORE
+ not_present_email = f"no_packaging@email.co.uk" # /PS-IGNORE
+ factories.NotifiedUserFactory(
+ email=present_email,
+ )
+ factories.NotifiedUserFactory(
+ email=not_present_email,
+ enrol_packaging=False,
+ )
+
+ ### disable so it doesn't create it's own notification
+ settings.ENABLE_PACKAGING_NOTIFICATIONS = False
+ envelope = failed_envelope_factory()
+ packaged_wb = PackagedWorkBasket.objects.get(
+ envelope=envelope,
+ )
+ return (
+ factories.EnvelopeRejectedNotificationFactory(
+ notified_object_pk=packaged_wb.id,
+ ),
+ present_email,
+ not_present_email,
)
diff --git a/notifications/tests/test_models.py b/notifications/tests/test_models.py
index 145981eb2..04ec867d2 100644
--- a/notifications/tests/test_models.py
+++ b/notifications/tests/test_models.py
@@ -14,16 +14,19 @@
def test_create_goods_report_notification(goods_report_notification):
"""Test that the creating a notification correctly assigns users."""
- expected_present_email = f"goods_report@email.co.uk" # /PS-IGNORE
- expected_not_present_email = f"no_goods_report@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = goods_report_notification
- users = goods_report_notification.notified_users()
+ users = notification.notified_users()
for user in users:
assert user.email == expected_present_email
assert user.email != expected_not_present_email
- import_batch = goods_report_notification.notified_object()
+ import_batch = notification.notified_object()
assert isinstance(import_batch, ImportBatch)
return_value = {
@@ -36,7 +39,7 @@ def test_create_goods_report_notification(goods_report_notification):
"notifications.models.prepare_link_to_file",
return_value=return_value,
) as mocked_prepare_link_to_file:
- personalisation = goods_report_notification.get_personalisation()
+ personalisation = notification.get_personalisation()
assert personalisation == {
"tgb_id": import_batch.name,
@@ -47,21 +50,24 @@ def test_create_goods_report_notification(goods_report_notification):
def test_create_packaging_notification(ready_for_packaging_notification):
"""Test that the creating a notification correctly assigns users."""
- expected_present_email = f"packaging@email.co.uk" # /PS-IGNORE
- expected_not_present_email = f"no_packaging@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = ready_for_packaging_notification
- users = ready_for_packaging_notification.notified_users()
+ users = notification.notified_users()
for user in users:
assert user.email == expected_present_email
assert user.email != expected_not_present_email
assert isinstance(
- ready_for_packaging_notification.notified_object(),
+ notification.notified_object(),
PackagedWorkBasket,
)
- content = ready_for_packaging_notification.get_personalisation()
+ content = notification.get_personalisation()
assert content == {
"envelope_id": "230001",
"description": "",
@@ -73,30 +79,123 @@ def test_create_packaging_notification(ready_for_packaging_notification):
}
+def test_create_accepted_envelope(accepted_packaging_notification):
+ """Test that the creating a notification correctly assigns users."""
+
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = accepted_packaging_notification
+
+ users = notification.notified_users()
+
+ for user in users:
+ assert user.email == expected_present_email
+ assert user.email != expected_not_present_email
+
+ assert isinstance(
+ notification.notified_object(),
+ PackagedWorkBasket,
+ )
+
+ content = notification.get_personalisation()
+ assert "envelope_id" in content and content["envelope_id"] == "230001"
+ assert "transaction_count" in content and content["transaction_count"] == 1
+ assert (
+ "loading_report_message" in content
+ and content["loading_report_message"]
+ == "Loading report(s): REPORT_DBT23000.html"
+ )
+ assert "comments" in content
+
+
+def test_create_rejected_envelope(rejected_packaging_notification):
+ """Test that the creating a notification correctly assigns users."""
+
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = rejected_packaging_notification
+
+ users = notification.notified_users()
+
+ for user in users:
+ assert user.email == expected_present_email
+ assert user.email != expected_not_present_email
+
+ assert isinstance(
+ notification.notified_object(),
+ PackagedWorkBasket,
+ )
+
+ content = notification.get_personalisation()
+ assert "envelope_id" in content and content["envelope_id"] == "230001"
+ assert "transaction_count" in content and content["transaction_count"] == 1
+ assert (
+ "loading_report_message" in content
+ and content["loading_report_message"]
+ == "Loading report(s): REPORT_DBT23001.html"
+ )
+ assert "comments" in content
+
+
def test_create_successful_publishing_notification(successful_publishing_notification):
"""Test that the creating a notification correctly assigns users."""
- expected_present_email = f"publishing@email.co.uk" # /PS-IGNORE
- expected_not_present_email = f"no_publishing@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = successful_publishing_notification
- users = successful_publishing_notification.notified_users()
+ users = notification.notified_users()
for user in users:
assert user.email == expected_present_email
assert user.email != expected_not_present_email
assert isinstance(
- successful_publishing_notification.notified_object(),
+ notification.notified_object(),
CrownDependenciesEnvelope,
)
- content = successful_publishing_notification.get_personalisation()
+ content = notification.get_personalisation()
+ assert content == {"envelope_id": "230001"}
+
+
+def test_create_failed_publishing_notification(failed_publishing_notification):
+ """Test that the creating a notification correctly assigns users."""
+
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = failed_publishing_notification
+
+ users = notification.notified_users()
+
+ for user in users:
+ assert user.email == expected_present_email
+ assert user.email != expected_not_present_email
+
+ assert isinstance(
+ notification.notified_object(),
+ CrownDependenciesEnvelope,
+ )
+
+ content = notification.get_personalisation()
assert content == {"envelope_id": "230001"}
def test_send_notification_emails(ready_for_packaging_notification):
- expected_present_email = f"packaging@email.co.uk" # /PS-IGNORE
- expected_not_present_email = f"no_packaging@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = ready_for_packaging_notification
+
with patch(
"notifications.models.send_emails",
return_value={
@@ -105,11 +204,11 @@ def test_send_notification_emails(ready_for_packaging_notification):
"failed_recipients": "",
},
) as mocked_send_emails:
- ready_for_packaging_notification.send_notification_emails()
+ notification.send_notification_emails()
mocked_send_emails.assert_called_once()
log_success = NotificationLog.objects.get(
- notification=ready_for_packaging_notification,
+ notification=notification,
recipients=expected_present_email,
success=True,
)
@@ -124,11 +223,11 @@ def test_send_notification_emails(ready_for_packaging_notification):
"failed_recipients": " \n".join([expected_present_email]),
},
) as mocked_send_emails:
- ready_for_packaging_notification.send_notification_emails()
+ notification.send_notification_emails()
mocked_send_emails.assert_called_once()
log_fail = NotificationLog.objects.get(
- notification=ready_for_packaging_notification,
+ notification=notification,
success=False,
)
diff --git a/notifications/tests/test_tasks.py b/notifications/tests/test_tasks.py
index a0e82bd00..6b2a37284 100644
--- a/notifications/tests/test_tasks.py
+++ b/notifications/tests/test_tasks.py
@@ -14,8 +14,11 @@ def test_send_emails_goods_report_notification(
):
"""Tests that email notifications are only sent to users subscribed to email
type and that a log is created with this user's email."""
- expected_present_email = "goods_report@email.co.uk" # /PS-IGNORE
- expected_unenrolled_email = "no_goods_report@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = goods_report_notification
return_value = {
"file": "VGVzdA==",
@@ -37,19 +40,20 @@ def test_send_emails_goods_report_notification(
) as mocked_send_emails:
tasks.send_emails_task.apply(
kwargs={
- "notification_pk": goods_report_notification.id,
+ "notification_pk": notification.id,
},
)
mocked_send_emails.assert_called_once()
mocked_prepare_link_to_file.assert_called_once()
log = models.NotificationLog.objects.get(
- notification=goods_report_notification,
+ notification=notification,
recipients=expected_present_email,
success=True,
)
- assert expected_unenrolled_email not in log.recipients
+ assert expected_present_email in log.recipients
+ assert expected_not_present_email not in log.recipients
def test_send_emails_packaging_notification(
@@ -58,8 +62,11 @@ def test_send_emails_packaging_notification(
"""Tests that email notifications are only sent to users subscribed to email
type and that a log is created with this user's email."""
- expected_present_email = "packaging@email.co.uk" # /PS-IGNORE
- expected_unenrolled_email = "no_packaging@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = ready_for_packaging_notification
with patch(
"notifications.models.send_emails",
@@ -71,18 +78,19 @@ def test_send_emails_packaging_notification(
) as mocked_send_emails:
tasks.send_emails_task.apply(
kwargs={
- "notification_pk": ready_for_packaging_notification.id,
+ "notification_pk": notification.id,
},
)
mocked_send_emails.assert_called_once()
log = models.NotificationLog.objects.get(
- notification=ready_for_packaging_notification,
+ notification=notification,
recipients=expected_present_email,
success=True,
)
- assert expected_unenrolled_email not in log.recipients
+ assert expected_present_email in log.recipients
+ assert expected_not_present_email not in log.recipients
def test_send_emails_publishing_notification(
@@ -92,8 +100,11 @@ def test_send_emails_publishing_notification(
"""Tests that email notifications are only sent to users subscribed to email
type and that a log is created with this user's email."""
- expected_present_email = "publishing@email.co.uk" # /PS-IGNORE
- expected_unenrolled_email = "no_publishing@email.co.uk" # /PS-IGNORE
+ (
+ notification,
+ expected_present_email,
+ expected_not_present_email,
+ ) = successful_publishing_notification
with patch(
"notifications.models.send_emails",
@@ -105,15 +116,16 @@ def test_send_emails_publishing_notification(
) as mocked_send_emails:
tasks.send_emails_task.apply(
kwargs={
- "notification_pk": successful_publishing_notification.id,
+ "notification_pk": notification.id,
},
)
mocked_send_emails.assert_called_once()
log = models.NotificationLog.objects.get(
- notification=successful_publishing_notification,
+ notification=notification,
recipients=expected_present_email,
success=True,
)
- assert expected_unenrolled_email not in log.recipients
+ assert expected_present_email in log.recipients
+ assert expected_not_present_email not in log.recipients
diff --git a/publishing/models/crown_dependencies_envelope.py b/publishing/models/crown_dependencies_envelope.py
index 487e58dee..aab4640c7 100644
--- a/publishing/models/crown_dependencies_envelope.py
+++ b/publishing/models/crown_dependencies_envelope.py
@@ -158,7 +158,6 @@ def notify_publishing_success(self):
notification = CrownDependenciesEnvelopeSuccessNotification(
notified_object_pk=self.pk,
)
- print(notification)
notification.save()
notification.schedule_send_emails()
diff --git a/requirements.txt b/requirements.txt
index cd109a217..5a3798ba8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -39,7 +39,7 @@ elastic-apm==6.7.2
factory-boy==3.2.0
flower==1.2.0
freezegun==1.1.0
-gevent==21.12.0
+gevent==23.9.1
govuk-frontend-jinja @ git+https://github.com/alphagov/govuk-frontend-jinja.git@15845e4cca3a05df72c6e13ec6a7e35acc682f52
govuk-tech-docs-sphinx-theme==1.0.0
gunicorn==20.1.0