From a99b40304b472c79c50469a7c753a164f7cd23dc Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Fri, 17 Jul 2020 08:07:44 +0100 Subject: [PATCH] Add button to approve broadcast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since new broadcasts will go into `pending-approval`, we now need a way of approving them. This commit adds a button to this page to start (or approve) the broadcast. This button is wrapped in a bordered box, to emphasise that it’s something consequential. --- app/assets/stylesheets/components/banner.scss | 4 + app/main/views/broadcast.py | 26 +++++ app/models/broadcast_message.py | 6 ++ app/navigation.py | 4 + .../views/broadcast/view-message.html | 15 ++- tests/app/main/views/test_broadcast.py | 102 ++++++++++++++++++ 6 files changed, 152 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/components/banner.scss b/app/assets/stylesheets/components/banner.scss index 8203dcfd2..faaee067a 100644 --- a/app/assets/stylesheets/components/banner.scss +++ b/app/assets/stylesheets/components/banner.scss @@ -24,6 +24,10 @@ @include copy-19; } + .page-footer { + margin-bottom: govuk-spacing(1); + } + } %banner-with-tick, diff --git a/app/main/views/broadcast.py b/app/main/views/broadcast.py index 956db3b5b..f45414200 100644 --- a/app/main/views/broadcast.py +++ b/app/main/views/broadcast.py @@ -169,6 +169,32 @@ def view_broadcast_message(service_id, broadcast_message_id): ) +@main.route('/services//broadcast/', methods=['POST']) +@user_has_permissions('send_messages') +@service_has_permission('broadcast') +def approve_broadcast_message(service_id, broadcast_message_id): + + broadcast_message = BroadcastMessage.from_id( + broadcast_message_id, + service_id=current_service.id, + ) + + if broadcast_message.status != 'pending-approval': + return redirect(url_for( + '.view_broadcast_message', + service_id=current_service.id, + broadcast_message_id=broadcast_message.id, + )) + + broadcast_message.approve_broadcast() + + return redirect(url_for( + '.view_broadcast_message', + service_id=current_service.id, + broadcast_message_id=broadcast_message.id, + )) + + @main.route('/services//broadcast//cancel') @user_has_permissions('send_messages') @service_has_permission('broadcast') diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index 27898ee92..bf6772979 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -138,6 +138,12 @@ class BroadcastMessage(JSONModel): ) self._set_status_to('pending-approval') + def approve_broadcast(self): + self._update( + starts_at=datetime.utcnow().isoformat(), + ) + self._set_status_to('broadcasting') + def cancel_broadcast(self): self._set_status_to('cancelled') diff --git a/app/navigation.py b/app/navigation.py index 4442829f6..7d1af6f4b 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -361,6 +361,7 @@ class HeaderNavigation(Navigation): 'remove_broadcast_area', 'preview_broadcast_message', 'view_broadcast_message', + 'approve_broadcast_message', 'cancel_broadcast_message', } @@ -419,6 +420,7 @@ class MainNavigation(Navigation): 'remove_broadcast_area', 'preview_broadcast_message', 'view_broadcast_message', + 'approve_broadcast_message', 'cancel_broadcast_message', }, 'uploads': { @@ -1012,6 +1014,7 @@ class CaseworkNavigation(Navigation): 'remove_broadcast_area', 'preview_broadcast_message', 'view_broadcast_message', + 'approve_broadcast_message', 'cancel_broadcast_message', } @@ -1333,5 +1336,6 @@ class OrgNavigation(Navigation): 'remove_broadcast_area', 'preview_broadcast_message', 'view_broadcast_message', + 'approve_broadcast_message', 'cancel_broadcast_message', } diff --git a/app/templates/views/broadcast/view-message.html b/app/templates/views/broadcast/view-message.html index 9d534473e..d38584460 100644 --- a/app/templates/views/broadcast/view-message.html +++ b/app/templates/views/broadcast/view-message.html @@ -1,7 +1,7 @@ {% from "components/button/macro.njk" import govukButton %} {% from "components/form.html" import form_wrapper %} {% from "components/page-header.html" import page_header %} -{% from "components/page-footer.html" import sticky_page_footer %} +{% from "components/page-footer.html" import page_footer %} {% extends "withnav_template.html" %} @@ -17,10 +17,15 @@ ) }} {% if broadcast_message.status == 'pending-approval' %} -

- {{ broadcast_message.created_by.name }} wants to broadcast this - message until {{ broadcast_message.finishes_at|format_datetime_relative }}. -

+ {% call form_wrapper(class="banner govuk-!-margin-bottom-6") %} +

+ {{ broadcast_message.created_by.name }} wants to broadcast this + message until {{ broadcast_message.finishes_at|format_datetime_relative }}. +

+ {{ page_footer( + "Start broadcasting now" + ) }} + {% endcall %} {% else %}

Created by {{ broadcast_message.created_by.name }} and approved by diff --git a/tests/app/main/views/test_broadcast.py b/tests/app/main/views/test_broadcast.py index e306a30be..ec8826be9 100644 --- a/tests/app/main/views/test_broadcast.py +++ b/tests/app/main/views/test_broadcast.py @@ -400,6 +400,108 @@ def test_view_broadcast_message_page( ] == expected_paragraphs +@freeze_time('2020-02-22T22:22:22.000000') +def test_view_pending_broadcast( + mocker, + client_request, + service_one, + mock_get_broadcast_template, + fake_uuid, +): + mocker.patch( + 'app.broadcast_message_api_client.get_broadcast_message', + return_value=broadcast_message_json( + id_=fake_uuid, + service_id=SERVICE_ONE_ID, + template_id=fake_uuid, + created_by_id=fake_uuid, + finishes_at='2020-02-23T23:23:23.000000', + status='pending-approval', + ), + ) + service_one['permissions'] += ['broadcast'] + + page = client_request.get( + '.view_broadcast_message', + service_id=SERVICE_ONE_ID, + broadcast_message_id=fake_uuid, + ) + + assert ( + normalize_spaces(page.select_one('.banner').text) + ) == ( + 'Test User wants to broadcast this message until tomorrow at 11:23pm. ' + 'Start broadcasting now' + ) + + form = page.select_one('form.banner') + assert form['method'] == 'post' + assert 'action' not in form + assert form.select_one('button[type=submit]') + + +@pytest.mark.parametrize('initial_status, expected_approval', ( + ('draft', False,), + ('pending-approval', True), + ('rejected', False), + ('broadcasting', False), + ('cancelled', False), +)) +@freeze_time('2020-02-22T22:22:22.000000') +def test_approve_broadcast( + mocker, + client_request, + service_one, + mock_get_broadcast_template, + fake_uuid, + mock_update_broadcast_message, + mock_update_broadcast_message_status, + initial_status, + expected_approval, +): + mocker.patch( + 'app.broadcast_message_api_client.get_broadcast_message', + return_value=broadcast_message_json( + id_=fake_uuid, + service_id=SERVICE_ONE_ID, + template_id=fake_uuid, + created_by_id=fake_uuid, + finishes_at='2020-02-23T23:23:23.000000', + status=initial_status, + ), + ) + service_one['permissions'] += ['broadcast'] + + client_request.post( + '.view_broadcast_message', + service_id=SERVICE_ONE_ID, + broadcast_message_id=fake_uuid, + _expected_redirect=url_for( + '.view_broadcast_message', + service_id=SERVICE_ONE_ID, + broadcast_message_id=fake_uuid, + _external=True, + ) + ) + + if expected_approval: + mock_update_broadcast_message.assert_called_once_with( + service_id=SERVICE_ONE_ID, + broadcast_message_id=fake_uuid, + data={ + 'starts_at': '2020-02-22T22:22:22', + }, + ) + mock_update_broadcast_message_status.assert_called_once_with( + 'broadcasting', + service_id=SERVICE_ONE_ID, + broadcast_message_id=fake_uuid, + ) + else: + assert mock_update_broadcast_message.called is False + assert mock_update_broadcast_message_status.called is False + + def test_no_view_page_for_draft( client_request, service_one,