diff --git a/app/__init__.py b/app/__init__.py index e85140659..c0c431c0b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -62,8 +62,6 @@ def create_app(config_name, config_overrides=None): application.session_interface = ItsdangerousSessionInterface() - application.add_template_filter(placeholders) - application.add_template_filter(replace_placeholders) application.add_template_filter(nl2br) application.add_template_filter(format_datetime) application.add_template_filter(syntax_highlight_json) @@ -123,16 +121,6 @@ def convert_to_boolean(value): return value -def placeholders(value): - if not value: - return value - return Markup(re.sub( - r"\(\(([^\)]+)\)\)", # anything that looks like ((registration number)) - lambda match: "{}".format(match.group(1)), - value - )) - - def nl2br(value): _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}') @@ -141,16 +129,6 @@ def nl2br(value): return Markup(result) -def replace_placeholders(template, values): - if not template: - return template - return Markup(re.sub( - r"\(\(([^\)]+)\)\)", # anything that looks like ((registration number)) - lambda match: values.get(match.group(1), ''), - template - )) - - def syntax_highlight_json(code): return Markup(highlight(code, JavascriptLexer(), HtmlFormatter(noclasses=True))) diff --git a/app/main/dao/templates_dao.py b/app/main/dao/templates_dao.py index 72d47bcd2..4eb05ed9d 100644 --- a/app/main/dao/templates_dao.py +++ b/app/main/dao/templates_dao.py @@ -1,4 +1,4 @@ -from flask import url_for +from flask import url_for, abort from app import notifications_api_client from app.main.utils import BrowsableItem @@ -17,9 +17,24 @@ def get_service_templates(service_id): return notifications_api_client.get_service_templates(service_id) -def get_service_template(service_id, template_id): - return notifications_api_client.get_service_template( - service_id, template_id) +def get_service_templates_or_404(service_id): + try: + get_service_templates(service_id) + except HTTPError as e: + if e.status_code == 404: + abort(404) + else: + raise e + + +def get_service_template_or_404(service_id, template_id): + try: + return notifications_api_client.get_service_template(service_id, template_id) + except HTTPError as e: + if e.status_code == 404: + abort(404) + else: + raise e def delete_service_template(service_id, template_id): diff --git a/app/main/views/jobs.py b/app/main/views/jobs.py index d19e29cd3..dd5e45e04 100644 --- a/app/main/views/jobs.py +++ b/app/main/views/jobs.py @@ -8,6 +8,7 @@ from flask import ( ) from flask_login import login_required from notifications_python_client.errors import HTTPError +from utils.template import Template from app import job_api_client from app.main import main @@ -38,7 +39,6 @@ def view_jobs(service_id): def view_job(service_id, job_id): try: job = job_api_client.get_job(service_id, job_id)['data'] - template = templates_dao.get_service_template(service_id, job['template'])['data'] messages = [] return render_template( 'views/job.html', @@ -55,7 +55,9 @@ def view_job(service_id, job_id): cost=u'£0.00', uploaded_file_name=job['original_file_name'], uploaded_file_time=job['created_at'], - template=template, + template=Template( + templates_dao.get_service_template_or_404(service_id, job['template'])['data'] + ), service_id=service_id ) except HTTPError as e: diff --git a/app/main/views/sms.py b/app/main/views/sms.py index c7c7f75c1..94a5b19f6 100644 --- a/app/main/views/sms.py +++ b/app/main/views/sms.py @@ -1,6 +1,8 @@ import csv import uuid import botocore +import re +import io from datetime import date @@ -12,12 +14,14 @@ from flask import ( flash, abort, session, - current_app + current_app, + Markup ) from flask_login import login_required from werkzeug import secure_filename from notifications_python_client.errors import HTTPError +from utils.template import Template from app.main import main from app.main.forms import CsvUploadForm @@ -35,22 +39,19 @@ from app.main.utils import ( @main.route("/services//sms/send", methods=['GET']) def choose_sms_template(service_id): - try: - templates = templates_dao.get_service_templates(service_id)['data'] - except HTTPError as e: - if e.status_code == 404: - abort(404) - else: - raise e - - return render_template('views/choose-sms-template.html', - templates=templates, - service_id=service_id) + return render_template( + 'views/choose-sms-template.html', + templates=[ + Template(template) for template in templates_dao.get_service_templates(service_id)['data'] + ], + service_id=service_id + ) @main.route("/services//sms/send/", methods=['GET', 'POST']) @login_required def send_sms(service_id, template_id): + form = CsvUploadForm() if form.validate_on_submit(): try: @@ -63,24 +64,33 @@ def send_sms(service_id, template_id): service_id=service_id, upload_id=upload_id)) except ValueError as e: - message = 'There was a problem uploading: {}'.format( - csv_file.filename) - flash(message) + flash('There was a problem uploading: {}'.format(csv_file.filename)) flash(str(e)) return redirect(url_for('.send_sms', service_id=service_id, template_id=template_id)) - try: - template = templates_dao.get_service_template(service_id, template_id)['data'] - except HTTPError as e: - if e.status_code == 404: - abort(404) - else: - raise e + template = Template( + templates_dao.get_service_template_or_404(service_id, template_id)['data'] + ) - return render_template('views/send-sms.html', - template=template, - form=form, - service_id=service_id) + return render_template( + 'views/send-sms.html', + template=template, + column_headers=['phone number'] + template.placeholders_as_markup, + form=form, + service_id=service_id + ) + + +@main.route("/services//sms/send/.csv", methods=['GET']) +@login_required +def get_example_csv(service_id, template_id): + template = templates_dao.get_service_template_or_404(service_id, template_id)['data'] + output = io.StringIO() + csv.writer(output).writerow( + ['phone number'] + Template(template).list_placeholders + ) + + return(output.getvalue(), 200, {'Content-Type': 'text/csv; charset=utf-8'}) @main.route("/services//sms/check/", @@ -89,21 +99,23 @@ def send_sms(service_id, template_id): def check_sms(service_id, upload_id): if request.method == 'GET': - contents = s3download(service_id, upload_id) if not contents: flash('There was a problem reading your upload file') upload_result = _get_numbers(contents) upload_data = session['upload_data'] - original_file_name = upload_data.get('original_file_name') template_id = upload_data.get('template_id') - template = templates_dao.get_service_template(service_id, template_id)['data'] + template = Template( + templates_dao.get_service_template_or_404(service_id, template_id)['data'], + values=upload_result['valid'][0] if upload_result['valid'] else {}, + drop_values={'phone'} + ) return render_template( 'views/check-sms.html', upload_result=upload_result, - message_template=template['content'], - original_file_name=original_file_name, - template_id=template_id, + template=template, + column_headers=['phone number'] + template.placeholders_as_markup, + original_file_name=upload_data.get('original_file_name'), service_id=service_id ) elif request.method == 'POST': @@ -153,3 +165,16 @@ def _get_numbers(contents): except InvalidPhoneError: rejects.append({"line_number": i+2, "phone": row['phone']}) return {"valid": valid, "rejects": rejects} + + +def _get_column_headers(template_content, marked_up=False): + headers = re.findall( + r"\(\(([^\)]+)\)\)", # anything that looks like ((registration number)) + template_content + ) + if marked_up: + return [ + Markup("{}".format(header)) + for header in headers + ] + return headers diff --git a/app/main/views/templates.py b/app/main/views/templates.py index 33d93e93a..0b3f492ff 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -1,13 +1,15 @@ from flask import request, render_template, redirect, url_for, flash, abort from flask_login import login_required +from notifications_python_client.errors import HTTPError +from utils.template import Template + from app.main import main from app.main.forms import TemplateForm from app import job_api_client from app.main.dao.services_dao import get_service_by_id from app.main.dao import templates_dao as tdao from app.main.dao import services_dao as sdao -from notifications_python_client.errors import HTTPError @main.route("/services//templates") @@ -15,7 +17,6 @@ from notifications_python_client.errors import HTTPError def manage_service_templates(service_id): try: jobs = job_api_client.get_job(service_id)['data'] - templates = tdao.get_service_templates(service_id)['data'] except HTTPError as e: if e.status_code == 404: abort(404) @@ -25,7 +26,11 @@ def manage_service_templates(service_id): 'views/manage-templates.html', service_id=service_id, has_jobs=bool(jobs), - templates=[tdao.TemplatesBrowsableItem(x) for x in templates]) + templates=[ + Template(template) + for template in tdao.get_service_templates(service_id)['data'] + ] + ) @main.route("/services//templates/add", methods=['GET', 'POST']) @@ -56,14 +61,7 @@ def add_service_template(service_id): @main.route("/services//templates/", methods=['GET', 'POST']) @login_required def edit_service_template(service_id, template_id): - try: - template = tdao.get_service_template(service_id, template_id)['data'] - except HTTPError as e: - if e.status_code == 404: - abort(404) - else: - raise e - + template = tdao.get_service_template_or_404(service_id, template_id)['data'] template['template_content'] = template['content'] form = TemplateForm(**template) @@ -84,21 +82,14 @@ def edit_service_template(service_id, template_id): @main.route("/services//templates//delete", methods=['GET', 'POST']) @login_required def delete_service_template(service_id, template_id): - try: - template = tdao.get_service_template(service_id, template_id)['data'] - except HTTPError as e: - if e.status_code == 404: - abort(404) - else: - raise e - - template['template_content'] = template['content'] - form = TemplateForm(**template) + template = tdao.get_service_template_or_404(service_id, template_id)['data'] if request.method == 'POST': tdao.delete_service_template(service_id, template_id) return redirect(url_for('.manage_service_templates', service_id=service_id)) + template['template_content'] = template['content'] + form = TemplateForm(**template) flash('Are you sure you want to delete ‘{}’?'.format(form.name.data), 'delete') return render_template( 'views/edit-template.html', diff --git a/app/templates/components/email-message.html b/app/templates/components/email-message.html index 2a8f65eb8..6d9920121 100644 --- a/app/templates/components/email-message.html +++ b/app/templates/components/email-message.html @@ -10,10 +10,10 @@ {% endif %} {% endmacro %} diff --git a/app/templates/components/sms-message.html b/app/templates/components/sms-message.html index 044f3f5c7..490254485 100644 --- a/app/templates/components/sms-message.html +++ b/app/templates/components/sms-message.html @@ -11,7 +11,7 @@ {% endif %}
- {{ body|placeholders }} + {{ body }}
{% if recipient %}

diff --git a/app/templates/views/check-sms.html b/app/templates/views/check-sms.html index 2a61a83f2..d4b761075 100644 --- a/app/templates/views/check-sms.html +++ b/app/templates/views/check-sms.html @@ -17,14 +17,14 @@ {% for rejected in upload_result.rejects %}

Line {{rejected.line_number}}: {{rejected.phone }} {% endfor %} -

Go back and resolve errors

+

Go back and resolve errors

{% else %}
{{ sms_message( - message_template|replace_placeholders(upload_result.valid[0]) + template.replaced )}}
@@ -32,13 +32,13 @@
- Back + Back
{% call(item) list_table( upload_result.valid, caption=original_file_name, - field_headings=['Phone number'] + field_headings=column_headers ) %} {% call field() %} {{ item.phone }} diff --git a/app/templates/views/job.html b/app/templates/views/job.html index c72e6d34f..40af13526 100644 --- a/app/templates/views/job.html +++ b/app/templates/views/job.html @@ -17,7 +17,7 @@
{{ sms_message( - template['content'], + template.formatted_as_markup, )}}
diff --git a/app/templates/views/manage-templates.html b/app/templates/views/manage-templates.html index 2d3da827e..346c87739 100644 --- a/app/templates/views/manage-templates.html +++ b/app/templates/views/manage-templates.html @@ -26,19 +26,19 @@ Manage templates – GOV.UK Notify
{% for template in templates %} - {% if template.get_field('template_type') == 'sms' %} - {{ sms_message( - template.get_field('content'), - name=template.title, - id=template.get_field('id'), - edit_link=url_for('.edit_service_template', service_id=template.get_field('service'), template_id=template.get_field('id')) - ) }} - {% elif template.get_field('template_type') == 'email' %} + {% if template.template_type == 'email' %} {{ email_message( template.get_field('subject'), template.get_field('content'), name=template.get_field('name'), - edit_link=url_for('.edit_service_template', service_id=template.get_field('service'), template_id=template.get_field('id')) + edit_link=url_for('.edit_service_template', service_id=service_id, template_id=template.id) + ) }} + {% else %} + {{ sms_message( + template.formatted_as_markup, + name=template.name, + id=template.id, + edit_link=url_for('.edit_service_template', service_id=service_id, template_id=template.id) ) }} {% endif %} {% endfor %} diff --git a/app/templates/views/send-sms.html b/app/templates/views/send-sms.html index 66f7491ee..c43c52265 100644 --- a/app/templates/views/send-sms.html +++ b/app/templates/views/send-sms.html @@ -2,6 +2,7 @@ {% from "components/sms-message.html" import sms_message %} {% from "components/page-footer.html" import page_footer %} {% from "components/file-upload.html" import file_upload %} +{% from "components/table.html" import list_table %} {% block page_title %} Send text messages – GOV.UK Notify @@ -32,5 +33,20 @@ "Continue to preview" ) }} + {% if column_headers %} + {% call(item) list_table( + [], + caption='Preview', + field_headings=column_headers, + field_headings_visible=True, + caption_visible=False, + empty_message="Your data here" + ) %} + {% endcall %} + {% endif %} + + {% endblock %} diff --git a/requirements.txt b/requirements.txt index 2c5fed09d..631ea6965 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,4 @@ Pygments==2.0.2 git+https://github.com/alphagov/notifications-python-client.git@0.2.5#egg=notifications-python-client==0.2.5 -git+https://github.com/alphagov/notifications-utils.git@0.0.3#egg=notifications-utils==0.0.3 +git+https://github.com/alphagov/notifications-utils.git@0.1.0#egg=notifications-utils==0.1.0 diff --git a/tests/app/main/views/test_sms.py b/tests/app/main/views/test_sms.py index 9f4c4c158..57af2f99e 100644 --- a/tests/app/main/views/test_sms.py +++ b/tests/app/main/views/test_sms.py @@ -8,9 +8,8 @@ def test_choose_sms_template(app_, api_user_active, mock_login, mock_get_user, - mock_get_service_templates, mock_check_verify_code, - mock_get_service_template): + mock_get_service_templates): with app_.test_request_context(): with app_.test_client() as client: client.login(api_user_active)