Make jobs filterable by notification status

We can filter all notifications by status already. This commit reuses
the same code to filter the notifications for a job by status.

This means that, visually we can show the count on a job the same as
we do for all notifications, which is similar to how we show the counts
on the dashboard, so hopefully it feels like a bit more of a solid
thing.

This also applies to CSV downloads and AJAX updates, which will inherit
any filtering that their parent page has applied.
This commit is contained in:
Chris Hill-Scott
2016-06-08 16:34:26 +01:00
parent 5f0dc749f5
commit 6d7d5a4e46
6 changed files with 128 additions and 35 deletions

View File

@@ -48,7 +48,7 @@ def _set_status_filters(filter_args):
all_failure_statuses = ['failed', 'temporary-failure', 'permanent-failure', 'technical-failure']
all_statuses = ['sending', 'delivered'] + all_failure_statuses
if filter_args.get('status'):
if 'processed' in filter_args.get('status'):
if 'processed' in filter_args.get('status') or not filter_args.get('status'):
filter_args['status'] = all_statuses
elif 'failed' in filter_args.get('status'):
filter_args['status'].extend(all_failure_statuses[1:])
@@ -75,7 +75,12 @@ def view_job(service_id, job_id):
template = service_api_client.get_service_template(service_id=service_id,
template_id=job['template'],
version=job['template_version'])['data']
notifications = notification_api_client.get_notifications_for_service(service_id, job_id)
filter_args = _parse_filter_args(request.args)
_set_status_filters(filter_args)
notifications = notification_api_client.get_notifications_for_service(
service_id, job_id, status=filter_args.get('status'),
)
finished = job['status'] == 'finished'
return render_template(
'views/jobs/job.html',
@@ -95,7 +100,9 @@ def view_job(service_id, job_id):
template=Template(
template,
prefix=current_service['name']
)
),
counts=_get_job_counts(job, request.args.get('help', 0)),
status=request.args.get('status', '')
)
@@ -109,12 +116,15 @@ def view_job_csv(service_id, job_id):
template_id=job['template'],
version=job['template_version']
)['data']
filter_args = _parse_filter_args(request.args)
_set_status_filters(filter_args)
return (
generate_notifications_csv(
notification_api_client.get_notifications_for_service(
service_id,
job_id,
status=filter_args.get('status'),
page_size=job['notification_count']
)['notifications']
),
@@ -134,7 +144,11 @@ def view_job_csv(service_id, job_id):
@user_has_permissions('view_activity', admin_override=True)
def view_job_updates(service_id, job_id):
job = job_api_client.get_job(service_id, job_id)['data']
notifications = notification_api_client.get_notifications_for_service(service_id, job_id)
filter_args = _parse_filter_args(request.args)
_set_status_filters(filter_args)
notifications = notification_api_client.get_notifications_for_service(
service_id, job_id, status=filter_args.get('status')
)
finished = (
job.get('notifications_sent', 0) -
job.get('notifications_delivered', 0) -
@@ -144,13 +158,16 @@ def view_job_updates(service_id, job_id):
'counts': render_template(
'partials/jobs/count.html',
job=job,
finished=finished
finished=finished,
counts=_get_job_counts(job, request.args.get('help', 0)),
status=request.args.get('status', '')
),
'notifications': render_template(
'partials/jobs/notifications.html',
job=job,
notifications=notifications['notifications'],
finished=finished
finished=finished,
status=request.args.get('status', '')
),
'status': render_template(
'partials/jobs/status.html',
@@ -269,3 +286,41 @@ def view_notification(service_id, job_id, notification_id):
uploaded_at=now,
job_id=job_id
)
def _get_job_counts(job, help_argument):
return [
(
label,
query_param,
url_for(
".view_job",
service_id=job['service'],
job_id=job['id'],
status=query_param,
help=help_argument
),
count
) for label, query_param, count in [
[
'Processed', '',
job.get('notifications_sent', 0)
],
[
'Sending', 'sending',
(
job.get('notifications_sent', 0) -
job.get('notifications_delivered', 0) -
job.get('notifications_failed', 0)
)
],
[
'Delivered', 'delivered',
job.get('notifications_delivered', 0)
],
[
'Failed', 'failed',
job.get('notifications_failed')
]
]
]

View File

@@ -14,9 +14,12 @@ class JobApiClient(BaseAPIClient):
self.client_id = app.config['ADMIN_CLIENT_USER_NAME']
self.secret = app.config['ADMIN_CLIENT_SECRET']
def get_job(self, service_id, job_id=None, limit_days=None):
def get_job(self, service_id, job_id=None, limit_days=None, status=None):
if job_id:
return self.get(url='/service/{}/job/{}'.format(service_id, job_id))
params = {}
if status is not None:
params['status'] = status
return self.get(url='/service/{}/job/{}'.format(service_id, job_id), params=params)
params = {}
if limit_days is not None:
params['limit_days'] = limit_days

View File

@@ -1,30 +1,16 @@
{% from "components/big-number.html" import big_number %}
{% from "components/pill.html" import pill %}
<div
{% if not finished %}
data-module="update-content"
data-resource="{{url_for(".view_job_updates", service_id=current_service.id, job_id=job.id)}}"
data-resource="{{url_for(".view_job_updates", service_id=current_service.id, job_id=job.id, status=status, help=request.args.get('help', 0))}}"
data-key="counts"
aria-live="polite"
{% endif %}
>
<ul class="grid-row job-totals">
<li class="column-one-quarter">
{{ big_number(
job.get('notifications_sent', 0) - job.get('notifications_delivered', 0) - job.get('notifications_failed', 0), 'sending'
)}}
</li>
<li class="column-one-quarter">
{{ big_number(
job.get('notifications_delivered', 0), 'delivered'
)}}
</li>
<li class="column-one-quarter">
{{ big_number(
job.notifications_failed, 'failed'
)}}
</li>
</ul>
<div class="bottom-gutter">
{{ pill('Status', counts, status) }}
</div>
</div>

View File

@@ -1,10 +1,12 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, date_field, row_heading %}
<div
class='dashboard-table'
{% if notifications %}
class='dashboard-table'
{% endif %}
{% if not finished %}
data-module="update-content"
data-resource="{{url_for(".view_job_updates", service_id=current_service.id, job_id=job.id)}}"
data-resource="{{url_for(".view_job_updates", service_id=current_service.id, job_id=job.id, status=request.args.get('status'), help=request.args.get('help'))}}"
data-key="notifications"
aria-live="polite"
{% endif %}
@@ -12,7 +14,7 @@
{% if notifications %}
<p class="bottom-gutter">
<a href="{{ request.url }}?&amp;download=csv" download="download" class="heading-small">Download as a CSV file</a>
<a href="{{ url_for('.view_job_csv', service_id=current_service.id, job_id=job.id, status=status) }}" download="download" class="heading-small">Download as a CSV file</a>
&emsp;
Delivery information is available for 7 days
</p>
@@ -22,7 +24,7 @@
notifications,
caption=uploaded_file_name,
caption_visible=False,
empty_message="No messages to show yet",
empty_message="No messages to show",
field_headings=[
'Recipient',
'Time',

View File

@@ -1,12 +1,12 @@
<div
{% if not finished %}
data-module="update-content"
data-resource="{{url_for(".view_job_updates", service_id=current_service.id, job_id=job.id)}}"
data-resource="{{url_for(".view_job_updates", service_id=current_service.id, job_id=job.id, status=status, help=request.args.get('help', 0))}}"
data-key="status"
aria-live="polite"
{% endif %}
>
<p class='heading-small'>
<p class='heading-small bottom-gutter'>
Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
</p>
</div>

View File

@@ -26,6 +26,30 @@ def test_should_return_list_of_all_jobs(app_,
assert len(jobs) == 5
@pytest.mark.parametrize(
"status_argument, expected_api_call", [
(
'',
['sending', 'delivered', 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure']
),
(
'processed',
['sending', 'delivered', 'failed', 'temporary-failure', 'permanent-failure', 'technical-failure']
),
(
'sending',
['sending']
),
(
'delivered',
['delivered']
),
(
'failed',
['failed', 'temporary-failure', 'permanent-failure', 'technical-failure']
)
]
)
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_show_page_for_one_job(
app_,
@@ -36,13 +60,20 @@ def test_should_show_page_for_one_job(
mock_get_job,
mocker,
mock_get_notifications,
fake_uuid
fake_uuid,
status_argument,
expected_api_call
):
file_name = mock_get_job(service_one['id'], fake_uuid)['data']['original_file_name']
with app_.test_request_context():
with app_.test_client() as client:
client.login(active_user_with_permissions, mocker, service_one)
response = client.get(url_for('main.view_job', service_id=service_one['id'], job_id=fake_uuid))
response = client.get(url_for(
'main.view_job',
service_id=service_one['id'],
job_id=fake_uuid,
status=status_argument
))
assert response.status_code == 200
content = response.get_data(as_text=True)
@@ -50,6 +81,22 @@ def test_should_show_page_for_one_job(
assert file_name in content
assert 'Delivered' in content
assert '11:10' in content
assert url_for(
'main.view_job_updates',
service_id=service_one['id'],
job_id=fake_uuid,
status=status_argument,
) in content
assert url_for(
'main.view_job_csv',
service_id=service_one['id'],
job_id=fake_uuid
) in content
mock_get_notifications.assert_called_with(
service_one['id'],
fake_uuid,
status=expected_api_call
)
@freeze_time("2016-01-01 11:09:00.061258")