Let users select electoral wards of local authorities

If a library has groups, we should show a link instead of selecting the
group directly.

Then we can give the user the choice of selecting the whole of that
group, or specific areas within the group.

For now the only libraries we have with groups are local authorities,
which group electoral wards.
This commit is contained in:
Chris Hill-Scott
2020-07-31 16:20:37 +01:00
parent 32cc168c23
commit 8570901731
6 changed files with 273 additions and 1 deletions

View File

@@ -2161,3 +2161,23 @@ class BroadcastAreaForm(StripWhitespaceForm):
return cls(choices=[
(area.id, area.name) for area in sorted(library)
])
class BroadcastAreaFormWithSelectAll(BroadcastAreaForm):
select_all = govukCheckboxField('Select all')
@classmethod
def from_library(cls, library, select_all_choice):
instance = super().from_library(library)
(
instance.select_all.area_slug,
instance.select_all.label.text,
) = select_all_choice
return instance
@property
def selected_areas(self):
if self.select_all.data:
return [self.select_all.area_slug]
return self.areas.data

View File

@@ -12,6 +12,7 @@ from app import current_service
from app.main import main
from app.main.forms import (
BroadcastAreaForm,
BroadcastAreaFormWithSelectAll,
ChooseBroadcastDurationForm,
SearchByNameForm,
)
@@ -118,6 +119,16 @@ def choose_broadcast_area(service_id, broadcast_message_id, library_slug):
service_id=current_service.id,
)
library = BroadcastMessage.libraries.get(library_slug)
if library.is_group:
return render_template(
'views/broadcast/areas-with-sub-areas.html',
search_form=SearchByNameForm(),
show_search_form=(len(library) > 7),
library=library,
broadcast_message=broadcast_message,
)
form = BroadcastAreaForm.from_library(library)
if form.validate_on_submit():
broadcast_message.add_areas(*form.areas.data)
@@ -136,6 +147,41 @@ def choose_broadcast_area(service_id, broadcast_message_id, library_slug):
)
@main.route(
'/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/libraries/<library_slug>/<area_slug>',
methods=['GET', 'POST'],
)
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def choose_broadcast_sub_area(service_id, broadcast_message_id, library_slug, area_slug):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
area = BroadcastMessage.libraries.get_areas(area_slug)[0]
form = BroadcastAreaFormWithSelectAll.from_library(
area.sub_areas,
select_all_choice=(area.id, f'All of {area.name}'),
)
if form.validate_on_submit():
broadcast_message.add_areas(*form.selected_areas)
return redirect(url_for(
'.preview_broadcast_areas',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
return render_template(
'views/broadcast/sub-areas.html',
form=form,
search_form=SearchByNameForm(),
show_search_form=(len(form.areas.choices) > 7),
library_slug=library_slug,
page_title=f'Choose an area of {area.name}',
broadcast_message=broadcast_message,
)
@main.route('/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/remove/<area_slug>')
@user_has_permissions('send_messages')
@service_has_permission('broadcast')

View File

@@ -359,6 +359,7 @@ class HeaderNavigation(Navigation):
'preview_broadcast_areas',
'choose_broadcast_library',
'choose_broadcast_area',
'choose_broadcast_sub_area',
'remove_broadcast_area',
'preview_broadcast_message',
'view_broadcast_message',
@@ -420,6 +421,7 @@ class MainNavigation(Navigation):
'preview_broadcast_areas',
'choose_broadcast_library',
'choose_broadcast_area',
'choose_broadcast_sub_area',
'remove_broadcast_area',
'preview_broadcast_message',
'view_broadcast_message',
@@ -1016,6 +1018,7 @@ class CaseworkNavigation(Navigation):
'preview_broadcast_areas',
'choose_broadcast_library',
'choose_broadcast_area',
'choose_broadcast_sub_area',
'remove_broadcast_area',
'preview_broadcast_message',
'view_broadcast_message',
@@ -1340,6 +1343,7 @@ class OrgNavigation(Navigation):
'preview_broadcast_areas',
'choose_broadcast_library',
'choose_broadcast_area',
'choose_broadcast_sub_area',
'remove_broadcast_area',
'preview_broadcast_message',
'view_broadcast_message',

View File

@@ -0,0 +1,25 @@
{% from "components/page-header.html" import page_header %}
{% from "components/live-search.html" import live_search %}
{% extends "withnav_template.html" %}
{% block service_page_title %}
{{ library.name }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
library.name,
back_link=url_for('.choose_broadcast_library', service_id=current_service.id, broadcast_message_id=broadcast_message.id),
)}}
{{ live_search(target_selector='.file-list-item', show=show_search_form, form=search_form, label='Search by name') }}
{% for area in library|sort %}
<div class="file-list-item">
<a class="file-list-filename-large govuk-link govuk-link--no-visited-state" href="{{ url_for('.choose_broadcast_sub_area', service_id=current_service.id, broadcast_message_id=broadcast_message.id, library_slug=library.id, area_slug=area.id) }}">{{ area.name }}</a>
</div>
{% endfor %}
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% from "components/page-header.html" import page_header %}
{% from "components/page-footer.html" import sticky_page_footer %}
{% from "components/form.html" import form_wrapper %}
{% from "components/live-search.html" import live_search %}
{% extends "withnav_template.html" %}
{% block service_page_title %}
{{ page_title }}
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
page_title,
back_link=url_for('.choose_broadcast_library', service_id=current_service.id, broadcast_message_id=broadcast_message.id),
)}}
{% call form_wrapper() %}
<div class="form-group govuk-!-margin-bottom-4">
{{ form.select_all }}
</div>
{{ live_search(target_selector='.multiple-choice', show=show_search_form, form=search_form, label='Or by electoral ward') }}
{{ form.areas }}
{{ sticky_page_footer('Add to broadcast') }}
{% endcall %}
{% endblock %}

View File

@@ -1,5 +1,6 @@
import json
import uuid
from functools import partial
import pytest
from flask import url_for
@@ -257,12 +258,104 @@ def test_choose_broadcast_area_page(
fake_uuid,
):
service_one['permissions'] += ['broadcast']
client_request.get(
page = client_request.get(
'.choose_broadcast_area',
service_id=SERVICE_ONE_ID,
broadcast_message_id=fake_uuid,
library_slug='countries',
)
assert [
(
choice.select_one('input')['value'],
normalize_spaces(choice.select_one('label').text),
)
for choice in page.select('form[method=post] .govuk-checkboxes__item')
] == [
('countries-E92000001', 'England'),
('countries-N92000002', 'Northern Ireland'),
('countries-S92000003', 'Scotland'),
('countries-W92000004', 'Wales'),
]
def test_choose_broadcast_area_page_for_area_with_sub_areas(
client_request,
service_one,
mock_get_draft_broadcast_message,
fake_uuid,
):
service_one['permissions'] += ['broadcast']
page = client_request.get(
'.choose_broadcast_area',
service_id=SERVICE_ONE_ID,
broadcast_message_id=fake_uuid,
library_slug='electoral-wards-of-the-united-kingdom',
)
partial_url_for = partial(
url_for,
'main.choose_broadcast_sub_area',
service_id=SERVICE_ONE_ID,
broadcast_message_id=fake_uuid,
library_slug='electoral-wards-of-the-united-kingdom',
)
choices = [
(
choice.select_one('a.file-list-filename-large')['href'],
normalize_spaces(choice.text),
)
for choice in page.select('.file-list-item')
]
assert len(choices) == 379
assert choices[:2] == [
(
partial_url_for(area_slug='electoral-wards-of-the-united-kingdom-S12000033'),
'Aberdeen City',
),
(
partial_url_for(area_slug='electoral-wards-of-the-united-kingdom-S12000034'),
'Aberdeenshire',
),
]
assert choices[-1:] == [
(
partial_url_for(area_slug='electoral-wards-of-the-united-kingdom-E06000014'),
'York',
),
]
def test_choose_broadcast_sub_area_page(
client_request,
service_one,
mock_get_draft_broadcast_message,
fake_uuid,
):
service_one['permissions'] += ['broadcast']
page = client_request.get(
'main.choose_broadcast_sub_area',
service_id=SERVICE_ONE_ID,
broadcast_message_id=fake_uuid,
library_slug='electoral-wards-of-the-united-kingdom',
area_slug='electoral-wards-of-the-united-kingdom-S12000033',
)
assert normalize_spaces(page.select_one('h1').text) == (
'Choose an area of Aberdeen City'
)
choices = [
(
choice.select_one('input')['value'],
normalize_spaces(choice.select_one('label').text),
)
for choice in page.select('form[method=post] .govuk-checkboxes__item')
]
assert choices[:3] == [
('y', 'All of Aberdeen City'),
('electoral-wards-of-the-united-kingdom-S13002845', 'Airyhall/Broomhill/Garthdee'),
('electoral-wards-of-the-united-kingdom-S13002836', 'Bridge of Don'),
]
assert choices[-1:] == [
('electoral-wards-of-the-united-kingdom-S13002846', 'Torry/Ferryhill'),
]
def test_add_broadcast_area(
@@ -291,6 +384,57 @@ def test_add_broadcast_area(
)
@pytest.mark.parametrize('post_data, expected_selected', (
({
'select_all': 'y',
'areas': [
'electoral-wards-of-the-united-kingdom-S13002845',
]
}, [
'electoral-wards-of-the-united-kingdom-S12000033',
# S13002845 is ignored because the user chose Select all…
]),
({
'areas': [
'electoral-wards-of-the-united-kingdom-S13002845',
'electoral-wards-of-the-united-kingdom-S13002836',
]
}, [
'electoral-wards-of-the-united-kingdom-S13002845',
'electoral-wards-of-the-united-kingdom-S13002836',
]),
))
def test_add_broadcast_sub_area(
client_request,
service_one,
mock_get_draft_broadcast_message,
mock_update_broadcast_message,
fake_uuid,
post_data,
expected_selected,
):
service_one['permissions'] += ['broadcast']
client_request.post(
'.choose_broadcast_sub_area',
service_id=SERVICE_ONE_ID,
broadcast_message_id=fake_uuid,
library_slug='countries',
area_slug='electoral-wards-of-the-united-kingdom-S12000033',
_data=post_data,
)
mock_update_broadcast_message.assert_called_once_with(
service_id=SERVICE_ONE_ID,
broadcast_message_id=fake_uuid,
data={
'areas': [
# These two areas are on the broadcast already
'countries-E92000001',
'countries-S92000003',
] + expected_selected
},
)
def test_remove_broadcast_area_page(
client_request,
service_one,