Files
notifications-admin/app/main/views/sms.py
Chris Hill-Scott 73deae9bff Preview service name when adding a new service
This commit adds a new page, which appears after a user enters the name for
their new service. It shows how the service name will appear in emails and
text messages.

This means that the new service is not created until after they have confirmed
that the name is appropriate in context.

This has also involved:
- visual changes to the ‘email template’ pattern, which wasn’t very refined
  before
- removing a bunch of words from the enter service name page, because most users
  don’t read them, and we reckon that showing a preview is a better way of
  getting them to understand what is meant by service name

Still to do:
- validating the the generated email address for a service is unique (on the
  API) side
- having the API return the generated email address, rather than determining it
  in the admin app
2016-02-19 16:38:46 +00:00

191 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import csv
import io
import uuid
import botocore
from datetime import date
from flask import (
request,
render_template,
redirect,
url_for,
flash,
abort,
session,
current_app
)
from flask_login import login_required, current_user
from werkzeug import secure_filename
from notifications_python_client.errors import HTTPError
from utils.template import Template, NeededByTemplateError, NoPlaceholderForDataError
from app.main import main
from app.main.forms import CsvUploadForm
from app.main.uploader import (
s3upload,
s3download
)
from app.main.dao import templates_dao
from app import job_api_client
from app.utils import (
validate_phone_number,
InvalidPhoneError
)
@main.route("/services/<service_id>/sms/send", methods=['GET'])
def choose_sms_template(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/<service_id>/sms/send/<template_id>", methods=['GET', 'POST'])
@login_required
def send_sms(service_id, template_id):
form = CsvUploadForm()
if form.validate_on_submit():
try:
csv_file = form.file.data
filedata = _get_filedata(csv_file)
upload_id = str(uuid.uuid4())
s3upload(upload_id, service_id, filedata, current_app.config['AWS_REGION'])
session['upload_data'] = {"template_id": template_id, "original_file_name": filedata['file_name']}
return redirect(url_for('.check_sms',
service_id=service_id,
upload_id=upload_id))
except ValueError as e:
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))
template = Template(
templates_dao.get_service_template_or_404(service_id, template_id)['data']
)
return render_template(
'views/send-sms.html',
template=template,
column_headers=['phone'] + template.placeholders_as_markup,
form=form,
service_id=service_id
)
@main.route("/services/<service_id>/sms/send/<template_id>.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']
placeholders = list(Template(template).placeholders)
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['phone'] + placeholders)
writer.writerow([current_user.mobile_number] + ["test {}".format(header) for header in placeholders])
return(output.getvalue(), 200, {'Content-Type': 'text/csv; charset=utf-8'})
@main.route("/services/<service_id>/sms/send/<template_id>/to-self", methods=['GET'])
@login_required
def send_sms_to_self(service_id, template_id):
template = templates_dao.get_service_template_or_404(service_id, template_id)['data']
placeholders = list(Template(template).placeholders)
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['phone'] + placeholders)
writer.writerow([current_user.mobile_number] + ["test {}".format(header) for header in placeholders])
filedata = {
'file_name': 'Test run',
'data': output.getvalue().splitlines()
}
upload_id = str(uuid.uuid4())
s3upload(upload_id, service_id, filedata, current_app.config['AWS_REGION'])
session['upload_data'] = {"template_id": template_id, "original_file_name": filedata['file_name']}
return redirect(url_for('.check_sms',
service_id=service_id,
upload_id=upload_id))
@main.route("/services/<service_id>/sms/check/<upload_id>",
methods=['GET', 'POST'])
@login_required
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_data = session['upload_data']
template_id = upload_data.get('template_id')
raw_template = templates_dao.get_service_template_or_404(service_id, template_id)['data']
upload_result = _get_rows(contents, raw_template)
print(upload_result)
template = Template(
raw_template,
values=upload_result['rows'][0] if upload_result['valid'] else {},
drop_values={'phone'}
)
return render_template(
'views/check-sms.html',
upload_result=upload_result,
template=template,
column_headers=['phone number'] + list(
template.placeholders if upload_result['valid'] else template.placeholders_as_markup
),
original_file_name=upload_data.get('original_file_name'),
service_id=service_id,
form=CsvUploadForm()
)
elif request.method == 'POST':
upload_data = session['upload_data']
original_file_name = upload_data.get('original_file_name')
template_id = upload_data.get('template_id')
session.pop('upload_data')
try:
job_api_client.create_job(upload_id, service_id, template_id, original_file_name)
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
flash('Weve started sending your messages', 'default_with_tick')
return redirect(url_for('main.view_job',
service_id=service_id,
job_id=upload_id))
def _get_filedata(file):
lines = file.read().decode('utf-8').splitlines()
if len(lines) < 2: # must be at least header and one line
message = 'The file {} contained no data'.format(file.filename)
raise ValueError(message)
return {'file_name': file.filename, 'data': lines}
def _get_rows(contents, raw_template):
reader = csv.DictReader(
contents.split('\n'),
lineterminator='\n',
quoting=csv.QUOTE_NONE,
skipinitialspace=True
)
valid = True
rows = []
for row in reader:
rows.append(row)
try:
validate_phone_number(row['phone'])
Template(raw_template, values=row, drop_values={'phone'}).replaced
except (InvalidPhoneError, NeededByTemplateError, NoPlaceholderForDataError):
valid = False
return {"valid": valid, "rows": rows}