add add one-off notification status

completely mimicks the job status page, and as such, all the code and
templates have been taken from the job page. This page performs
exactly the same as the job page for now

* total, sending, delivered, failed blue boxes (though they'll just
  read 0/1 for now.
* download report button (same as with job download, except without job
  or row number in file)
* removed references to scheduled
* kept references to help (aka tour/tutorial) as that'll eventually
  change over from a job to a one-off too
This commit is contained in:
Leo Hemsted
2017-06-12 17:21:25 +01:00
parent 388bb312c8
commit c5f92eabfb
9 changed files with 255 additions and 30 deletions

View File

@@ -29,4 +29,5 @@ from app.main.views import (
platform_admin,
letter_jobs,
conversation,
notifications
)

View File

@@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
import ago
import dateutil
from orderedset import OrderedSet
from datetime import datetime, timedelta, timezone
from itertools import chain
from flask import (
@@ -35,6 +32,7 @@ from app.utils import (
generate_notifications_csv,
get_help_argument,
get_template,
get_time_left,
REQUESTED_STATUSES,
FAILURE_STATUSES,
SENDING_STATUSES,
@@ -364,7 +362,7 @@ def get_job_partials(job):
)
return {
'counts': render_template(
'partials/jobs/count.html',
'partials/count.html',
counts=_get_job_counts(job, request.args.get('help', 0)),
status=filter_args['status']
),
@@ -388,16 +386,3 @@ def get_job_partials(job):
job=job
),
}
def get_time_left(job_created_at):
return ago.human(
(
datetime.now(timezone.utc).replace(hour=23, minute=59, second=59)
) - (
dateutil.parser.parse(job_created_at) + timedelta(days=8)
),
future_tense='Data available for {}',
past_tense='Data no longer available', # No-one should ever see this
precision=1
)

View File

@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
from flask import (
render_template,
jsonify,
request,
url_for,
current_app
)
from flask_login import login_required
from app import (
notification_api_client,
current_service
)
from app.main import main
from app.utils import (
user_has_permissions,
get_help_argument,
get_template,
get_time_left,
REQUESTED_STATUSES,
FAILURE_STATUSES,
SENDING_STATUSES,
DELIVERED_STATUSES,
)
def get_status_arg(filter_args):
if 'status' not in filter_args or not filter_args['status']:
return REQUESTED_STATUSES
elif filter_args['status'] == 'sending':
return SENDING_STATUSES
elif filter_args['status'] == 'delivered':
return DELIVERED_STATUSES
elif filter_args['status'] == 'failed':
return FAILURE_STATUSES
else:
current_app.logger.info('Unrecognised status filter: {}'.format(filter_args['status']))
return REQUESTED_STATUSES
@main.route("/services/<service_id>/one-off-notification/<notification_id>")
@login_required
@user_has_permissions('view_activity', admin_override=True)
def view_notification(service_id, notification_id):
notification = notification_api_client.get_notification(service_id, notification_id)
return render_template(
'views/notifications/notification.html',
finished=(notification['status'] in (DELIVERED_STATUSES + FAILURE_STATUSES)),
uploaded_file_name='Report',
template=get_template(
notification['template'],
current_service,
letter_preview_url=url_for(
'.view_template_version_preview',
service_id=service_id,
template_id=notification['template']['id'],
version=notification['template_version'],
filetype='png',
),
),
status=request.args.get('status'),
updates_url=url_for(
".view_notification_updates",
service_id=service_id,
notification_id=notification['id'],
status=request.args.get('status'),
help=get_help_argument()
),
partials=get_single_notification_partials(notification),
help=get_help_argument()
)
@main.route("/services/<service_id>/one-off-notification/<notification_id>.json")
@user_has_permissions('view_activity', admin_override=True)
def view_notification_updates(service_id, notification_id):
return jsonify(**get_single_notification_partials(
notification_api_client.get_notification(service_id, notification_id)
))
def _get_single_notification_counts(notification, help_argument):
return [
(
label,
query_param,
url_for(
".view_notification",
service_id=notification['service'],
notification_id=notification['id'],
status=query_param,
help=help_argument
),
count
) for label, query_param, count in [
[
'total', '',
1
],
[
'sending', 'sending',
int(notification['status'] in SENDING_STATUSES)
],
[
'delivered', 'delivered',
int(notification['status'] in DELIVERED_STATUSES)
],
[
'failed', 'failed',
int(notification['status'] in FAILURE_STATUSES)
]
]
]
def get_single_notification_partials(notification):
status_args = get_status_arg(request.args)
return {
'counts': render_template(
'partials/count.html',
counts=_get_single_notification_counts(notification, request.args.get('help', 0)),
status=status_args
),
'notifications': render_template(
'partials/notifications/notifications.html',
notification=notification,
more_than_one_page=False,
percentage_complete=100,
time_left=get_time_left(notification['created_at']),
),
'status': render_template(
'partials/notifications/status.html',
notification=notification
),
}

View File

@@ -55,7 +55,5 @@ class NotificationApiClient(NotifyAdminAPIClient):
params=params
)
def get_notification(self, service_id, notification_id):
return self.get(
url='/service/{}/notifications/{}'.format(service_id, notification_id)
)
def get_notification(self, service_id, notification_id):m
return self.get(url='/service/{}/notifications/{}'.format(service_id, notification_id))

View File

@@ -0,0 +1,39 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, notification_status_field %}
{% from "components/page-footer.html" import page_footer %}
<div class="ajax-block-container" aria-labelledby='pill-selected-item'>
<div class="dashboard-table bottom-gutter-3-2">
{% if not help %}
<p class="bottom-gutter">
<a href="{{ download_link }}" download="download" class="heading-small">Download this report</a>
&emsp;
<span id="time-left">{{ time_left }}</span>
</p>
{% endif %}
{% call(item, row_number) list_table(
[notification],
caption=None,
caption_visible=False,
empty_message=None,
field_headings=[
'Recipient',
'Status'
],
field_headings_visible=False
) %}
{% call row_heading() %}
<p>{{ item.to }}</p>
{% endcall %}
{{ notification_status_field(item) }}
{% endcall %}
{% if more_than_one_page %}
<p class="table-show-more-link">
Only showing the first 50 rows
</p>
{% endif %}
</div>
</div>

View File

@@ -0,0 +1,6 @@
<div class="ajax-block-container">
<p class='heading-small bottom-gutter'>
Sent {% if notification.created_by %}by {{ notification.created_by.name }} {% endif %}
on {{ notification.created_at|format_datetime_short }}
</p>
</div>

View File

@@ -0,0 +1,27 @@
{% extends "withnav_template.html" %}
{% from "components/banner.html" import banner %}
{% from "components/ajax-block.html" import ajax_block %}
{% from "components/page-footer.html" import page_footer %}
{% block service_page_title %}
Report
{% endblock %}
{% block maincolumn_content %}
<h1 class="heading-large">
Report
</h1>
{{ template|string }}
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
{{ ajax_block(partials, updates_url, 'counts', finished=finished) }}
{{ ajax_block(partials, updates_url, 'notifications', finished=finished) }}
{{ page_footer(
secondary_link=url_for('.view_template', service_id=current_service.id, template_id=template.id),
secondary_link_text='Back to {}'.format(template.name)
) }}
{% endblock %}

View File

@@ -1,11 +1,13 @@
import re
import csv
from io import StringIO, BytesIO
from io import StringIO
from os import path
from functools import wraps
import unicodedata
from datetime import datetime
from datetime import datetime, timedelta, timezone
import dateutil
import ago
from flask import (
abort,
current_app,
@@ -15,6 +17,11 @@ from flask import (
url_for
)
from flask_login import current_user
import pyexcel
import pyexcel.ext.io
import pyexcel.ext.xls
import pyexcel.ext.xlsx
import pyexcel.ext.ods3
from notifications_utils.template import (
SMSPreviewTemplate,
@@ -23,12 +30,6 @@ from notifications_utils.template import (
LetterPreviewTemplate,
)
import pyexcel
import pyexcel.ext.io
import pyexcel.ext.xls
import pyexcel.ext.xlsx
import pyexcel.ext.ods3
SENDING_STATUSES = ['created', 'pending', 'sending']
DELIVERED_STATUSES = ['delivered', 'sent']
@@ -144,13 +145,31 @@ def generate_notifications_csv(**kwargs):
notification['status'],
notification['created_at']
]
line = ','.join([str(i) for i in values]) + '\n'
line = ','.join(str(i) for i in values) + '\n'
yield line
if notifications_resp['links'].get('next'):
kwargs['page'] += 1
else:
return
raise Exception("Should never reach here")
def generate_single_notification_csv(notification):
fieldnames = ['Recipient', 'Template', 'Type', 'Status', 'Time']
yield ','.join(fieldnames) + '\n'
values = [
notification['to'],
notification['template']['name'],
notification['template']['template_type'],
notification['status'],
notification['created_at']
]
line = ','.join(str(i) for i in values) + '\n'
yield line
return
def get_page_from_request():
@@ -301,3 +320,16 @@ def get_current_financial_year():
current_month = int(now.strftime('%-m'))
current_year = int(now.strftime('%Y'))
return current_year if current_month > 3 else current_year - 1
def get_time_left(created_at):
return ago.human(
(
datetime.now(timezone.utc).replace(hour=23, minute=59, second=59)
) - (
dateutil.parser.parse(created_at) + timedelta(days=8)
),
future_tense='Data available for {}',
past_tense='Data no longer available', # No-one should ever see this
precision=1
)