Use the new error messages when uploading a letter

We now use the pattern of showing a box at the top of the page with the
error. The error message has a heading and can have additional details.
Error messages and the invalid pages get stored in the S3 metadata.
This commit is contained in:
Katie Smith
2019-10-15 15:56:06 +01:00
parent 3223956ba2
commit 3b3f74bbf0
7 changed files with 122 additions and 43 deletions

View File

@@ -1111,8 +1111,8 @@ class PDFUploadForm(StripWhitespaceForm):
file = FileField_wtf(
'Upload a letter in PDF format',
validators=[
FileAllowed(['pdf'], 'Letters must be saved as a PDF'),
DataRequired(message="You need to upload a file to submit")
FileAllowed(['pdf'], 'Save your letter as a PDF and try again.'),
DataRequired(message="You need to choose a file to upload")
]
)

View File

@@ -1,11 +1,11 @@
import base64
import json
import uuid
from io import BytesIO
from flask import (
abort,
current_app,
flash,
redirect,
render_template,
request,
@@ -26,7 +26,11 @@ from app.s3_client.s3_letter_upload_client import (
upload_letter_to_s3,
)
from app.template_previews import TemplatePreview, sanitise_letter
from app.utils import get_template, user_has_permissions
from app.utils import (
get_letter_validation_error,
get_template,
user_has_permissions,
)
MAX_FILE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
@@ -41,6 +45,7 @@ def uploads(service_id):
@user_has_permissions('send_messages')
def upload_letter(service_id):
form = PDFUploadForm()
error = {}
if form.validate_on_submit():
pdf_file_bytes = form.file.data.read()
@@ -48,17 +53,20 @@ def upload_letter(service_id):
virus_free = antivirus_client.scan(BytesIO(pdf_file_bytes))
if not virus_free:
return invalid_upload_error('Your file has failed the virus check')
return invalid_upload_error('Your file contains a virus')
if len(pdf_file_bytes) > MAX_FILE_UPLOAD_SIZE:
return invalid_upload_error('Your file must be smaller than 2MB')
return invalid_upload_error('Your file is too big', 'Files must be smaller than 2MB.')
try:
# TODO: get page count from the sanitise response once template preview handles malformed files nicely
page_count = pdf_page_count(BytesIO(pdf_file_bytes))
except PdfReadError:
current_app.logger.info('Invalid PDF uploaded for service_id: {}'.format(service_id))
return invalid_upload_error('Your file must be a valid PDF')
return invalid_upload_error(
"Theres a problem with your file",
'Notify cannot read this PDF.<br>Save a new copy of your file and try again.'
)
upload_id = uuid.uuid4()
file_location = get_transient_letter_file_location(service_id, upload_id)
@@ -68,13 +76,18 @@ def upload_letter(service_id):
response.raise_for_status()
except RequestException as ex:
if ex.response is not None and ex.response.status_code == 400:
validation_failed_message = response.json().get('message')
invalid_pages = response.json().get('invalid_pages')
status = 'invalid'
upload_letter_to_s3(
pdf_file_bytes,
file_location=file_location,
status=status,
page_count=page_count,
filename=original_filename)
filename=original_filename,
message=validation_failed_message,
invalid_pages=invalid_pages)
else:
raise ex
else:
@@ -95,12 +108,33 @@ def upload_letter(service_id):
)
)
return render_template('views/uploads/choose-file.html', form=form)
if form.file.errors:
error = _get_error_from_upload_form(form.file.errors[0])
return render_template(
'views/uploads/choose-file.html',
error=error,
form=form
)
def invalid_upload_error(message):
flash(message, 'dangerous')
return render_template('views/uploads/choose-file.html', form=PDFUploadForm()), 400
def invalid_upload_error(error_title, error_detail=None):
return render_template(
'views/uploads/choose-file.html',
error={'title': error_title, 'detail': error_detail},
form=PDFUploadForm()
), 400
def _get_error_from_upload_form(form_errors):
error = {}
if 'PDF' in form_errors:
error['title'] = 'Wrong file type'
error['detail'] = form_errors
else: # No file was uploaded error
error['title'] = form_errors
return error
@main.route("/services/<service_id>/preview-letter/<file_id>")
@@ -110,7 +144,13 @@ def uploaded_letter_preview(service_id, file_id):
original_filename = metadata.get('filename')
page_count = metadata.get('page_count')
status = metadata.get('status')
error_message = metadata.get('message')
invalid_pages = metadata.get('invalid_pages')
if invalid_pages:
invalid_pages = json.loads(invalid_pages)
error = get_letter_validation_error(error_message, invalid_pages, page_count)
template_dict = service_api_client.get_precompiled_template(service_id)
template = get_template(
@@ -130,6 +170,7 @@ def uploaded_letter_preview(service_id, file_id):
template=template,
status=status,
file_id=file_id,
error=error,
)

View File

@@ -4,10 +4,11 @@
button_text="Choose file",
alternate_link=None,
alternate_link_text=None,
hint=None
hint=None,
show_errors=True
) %}
<form method="post" enctype="multipart/form-data" {% if action %}action="{{ action }}"{% endif %} class="{% if field.errors %}form-group-error{% endif %}" data-module="file-upload">
<form method="post" enctype="multipart/form-data" {% if action %}action="{{ action }}"{% endif %} class="{% if field.errors and show_errors %}form-group-error{% endif %}" data-module="file-upload">
<label class="file-upload-label" for="{{ field.name }}">
<span class="visually-hidden">{{ field.label.text }}</span>
{% if hint %}
@@ -15,7 +16,7 @@
{{ hint }}
</span>
{% endif %}
{% if field.errors %}
{% if field.errors and show_errors %}
<span class="error-message">
{{ field.errors[0] }}
</span>

View File

@@ -1,4 +1,5 @@
{% extends "withnav_template.html" %}
{% from "components/banner.html" import banner_wrapper %}
{% from "components/file-upload.html" import file_upload %}
{% from "components/page-header.html" import page_header %}
@@ -9,15 +10,26 @@
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-five-sixths">
{{ page_header(
{% if error %}
{% call banner_wrapper(type='dangerous') %}
<h1 class="banner-title">{{ error.title }}</h1>
{% if error.detail %}
<p>{{ error.detail | safe }}</p>
{% endif %}
{% endcall %}
{% else %}
{{ page_header(
'Upload a letter',
back_link=url_for('main.uploads', service_id=current_service.id)
) }}
{% endif %}
<p>
{{ file_upload(
form.file,
action = url_for('main.upload_letter', service_id=current_service.id),
action=url_for('main.upload_letter', service_id=current_service.id),
button_text='Upload your file again' if error else 'Choose file',
show_errors=False
)}}
</p>
<p>You can upload a single letter as a PDF.</p>

View File

@@ -1,4 +1,5 @@
{% extends "withnav_template.html" %}
{% from "components/banner.html" import banner_wrapper %}
{% from "components/page-header.html" import page_header %}
{% block service_page_title %}
@@ -6,16 +7,19 @@
{% endblock %}
{% block maincolumn_content %}
{{ page_header(
{% if status == 'invalid' and error %}
{% call banner_wrapper(type='dangerous') %}
<h1 class="banner-title">{{ error.title }}</h1>
{% if error.detail %}
<p>{{ error.detail | safe }}</p>
{% endif %}
{% endcall %}
{% else %}
{{ page_header(
original_filename,
back_link=url_for('main.upload_letter', service_id=current_service.id)
) }}
{% if status == 'invalid' %}
<p class="notification-status-cancelled" id="validation-error-message">
Validation failed
</p>
{% endif %}
) }}
{% endif %}
<div class="letter-sent">
{{ template|string }}