Notification history page added and pagination, tests all working.

This commit is contained in:
Nicholas Staples
2016-03-16 16:57:10 +00:00
parent f41106f4db
commit b0ca855ba8
13 changed files with 351 additions and 24 deletions

View File

@@ -2,7 +2,7 @@ import os
import re
import dateutil
from flask import (Flask, session, Markup, escape, render_template, make_response)
from flask import (Flask, session, Markup, escape, render_template, make_response, current_app)
from flask._compat import string_types
from flask_login import LoginManager
from flask_wtf import CsrfProtect
@@ -203,4 +203,7 @@ def register_errorhandlers(application):
@application.errorhandler(Exception)
def handle_bad_request(error):
# We want the Flask in browser stacktrace
if current_app.config.get('DEBUG', None):
raise error
return _error_response(500)

View File

@@ -0,0 +1,129 @@
/*
Taken from the GOV.UK component at
https://github.com/alphagov/static/blob/a9d462e71709d2ff6348bcce7e8c625af2b86114/app/assets/stylesheets/govuk-component/_previous-and-next-navigation.scss
and
https://github.com/alphagov/static/blob/da8aeeaa749093eab30286d7fc9f965533b66f47/app/assets/stylesheets/styleguide/_conditionals2.scss
*/
@import "_colours.scss";
@import "_typography.scss";
// Media query helpers. These make producing IE layouts
// super easy.
// These are desktop and down media queries
// There is also a local version of this in Smartanswers.
$is-ie: false !default;
@mixin media-down($size: false, $max-width: false, $min-width: false) {
@if $is-ie == false {
@if $size == mobile {
@media (max-width: 640px){
@content;
}
} @else if $size == tablet {
@media (max-width: 800px){
@content;
}
}
}
}
.govuk-previous-and-next-navigation {
@include media-down(mobile) {
margin: 2em 0 0 0;
}
display: block;
ul {
margin: 0;
padding: 0;
}
li {
@include core-16($line-height: (20 / 16));
float: left;
list-style: none;
text-align: right;
margin: 0;
padding: 0;
width: 49%;
a {
@include ie-lte(7) {
height: 4.5em;
}
display: block;
color: $link-colour;
text-decoration: none;
&:hover,
&:active {
background-color: $canvas-colour;
}
.pagination-part-title {
@include core-27($line-height: (33.75 / 27));
margin-bottom: 0.1em;
display: block;
}
}
&.next-page {
float: right;
text-align: right;
}
&.next-page a:before {
background: transparent file-url("arrow-sprite.png") no-repeat -102px -11px;
margin: -4px -32px 0 0;
display: block;
float: right;
width: 30px;
height: 38px;
content: " ";
}
&.previous-page a:before {
background: transparent file-url("arrow-sprite.png") no-repeat -20px -11px;
margin: -4px 0 0 -32px;
display: block;
float: left;
width: 30px;
height: 38px;
content: " ";
}
&.previous-page {
float: left;
text-align: left;
}
&.previous-page a {
padding: 0.75em 0 0.75em 3em;
}
&.next-page a {
padding: 0.75em 3em 0.75em 0;
}
@include media-down(mobile) {
&.previous-page,
&.next-page {
float: none;
width: 100%;
}
&.next-page a {
text-align: right;
}
}
}
}

View File

@@ -48,6 +48,7 @@ $path: '/static/images/';
@import 'components/email-message';
@import 'components/api-key';
@import 'components/yes-no';
@import "components/_previous-next-navigation";
@import 'views/job';
@import 'views/edit-template';

View File

@@ -5,7 +5,11 @@ import time
from flask import (
render_template,
abort,
jsonify
jsonify,
flash,
redirect,
request,
url_for
)
from flask_login import login_required
from utils.template import Template
@@ -14,6 +18,7 @@ from app import job_api_client, notification_api_client
from app.main import main
from app.main.dao import templates_dao
from app.main.dao import services_dao
from app.utils import (get_page_from_request, generate_previous_next_dict)
@main.route("/services/<service_id>/jobs")
@@ -86,6 +91,34 @@ def view_job_updates(service_id, job_id):
})
@main.route('/services/<service_id>/notifications')
@login_required
def view_notifications(service_id):
# TODO get the api to return count of pages as well.
page = get_page_from_request()
if page is None:
abort(404, "Invalid page argument ({}) reverting to page 1.".format(request.args['page'], None))
notifications = notification_api_client.get_notifications_for_service(service_id=service_id, page=page)
prev_page = None
if notifications['links'].get('prev', None):
prev_page = generate_previous_next_dict(
'main.view_notifications',
{'service_id': service_id}, page - 1, 'Previous page', 'page {}'.format(page - 1))
next_page = None
if notifications['links'].get('next', None):
next_page = generate_previous_next_dict(
'main.view_notifications',
{'service_id': service_id}, page + 1, 'Next page', 'page {}'.format(page + 1))
return render_template(
'views/notifications.html',
service_id=service_id,
notifications=notifications['notifications'],
page=page,
prev_page=prev_page,
next_page=next_page
)
@main.route("/services/<service_id>/jobs/<job_id>/notification/<string:notification_id>")
@login_required
def view_notification(service_id, job_id, notification_id):

View File

@@ -0,0 +1,22 @@
{% macro previous_next_navigation(previous_page, next_page) %}
<nav class="govuk-previous-and-next-navigation" role="navigation" aria-label="Pagination">
<ul class="group">
{% if previous_page %}
<li class="previous-page">
<a href="{{previous_page['url']}}" rel="previous" >
<span class="pagination-part-title">{{previous_page['title']}}</span>
<span class="pagination-label">{{previous_page['label']}}</span>
</a>
</li>
{% endif %}
{% if next_page %}
<li class="next-page">
<a href="{{next_page['url']}}" rel="next">
<span class="pagination-part-title">{{next_page['title']}}</span>
<span class="pagination-label">{{next_page['label']}}</span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% endmacro %}

View File

@@ -25,4 +25,8 @@
<li><a href="{{ url_for('.documentation', service_id=service_id) }}">Developer documentation</a></li>
</ul>
{% endif %}
<ul>
<li><a href="{{ url_for('.view_notifications', service_id=service_id) }}">Notification history</a></li>
<li><a href="{{ url_for('.view_jobs', service_id=service_id) }}">Job history</a><li>
</ul>
</nav>

View File

@@ -0,0 +1,42 @@
{% extends "withnav_template.html" %}
{% from "components/table.html" import list_table, field, right_aligned_field_heading %}
{% from "components/previous-next-navigation.html" import previous_next_navigation %}
{% block page_title %}
Notifications activity GOV.UK Notify
{% endblock %}
{% block maincolumn_content %}
<h1 class="heading-large">Notifications activity</h1>
{% call(item) list_table(
notifications,
caption="Recent activity",
caption_visible=False,
empty_message='You havent sent any notifications yet',
field_headings=['Recipient', 'Template', 'Type', 'Job', 'Status', 'Time'])
%}
{% call field() %}
{{ item.to }}
{% endcall %}
{% call field() %}
<a href="{{ url_for(".edit_service_template", service_id=service_id, template_id=item.template.id) }}">{{ item.template.name }}</a>
{% endcall %}
{% call field() %}
{{ item.template.template_type }}
{% endcall %}
{% call field() %}
{% if item.job %}
<a href="{{ url_for(".view_job", service_id=service_id, job_id=item.job.id) }}">{{ item.job.original_file_name }}</a>
{% endif %}
{% endcall %}
{% call field() %}
{{ item.status }}
{% endcall %}
{% call field() %}
{{ item.created_at | format_datetime}}
{% endcall %}
{% endcall %}
{{ previous_next_navigation(prev_page, next_page) }}
{% endblock %}

View File

@@ -65,7 +65,7 @@
{% if more_jobs_to_show %}
{% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %}
<p class="table-show-more-link">
<a href="{{ url_for('.view_jobs', service_id=service_id) }}">See all sent text messages</a>
<a href="{{ url_for('.view_notifications', service_id=service_id) }}">See all sent text messages</a>
</p>
{% endif %}
{% endif %}

View File

@@ -1,7 +1,7 @@
import re
from functools import wraps
from flask import (abort, session)
from flask import (abort, session, request, url_for)
class BrowsableItem(object):
@@ -82,3 +82,21 @@ def get_errors_for_csv(recipients, template_type):
errors.append("fill in {} empty cells".format(number_of_rows_with_missing_data))
return errors
def get_page_from_request():
if 'page' in request.args:
try:
return int(request.args['page'])
except ValueError:
return None
else:
return 1
def generate_previous_next_dict(view, view_dict, page, title, label):
return {
'url': url_for(view, **view_dict, page=page),
'title': title,
'label': label
}