2016-03-15 06:53:06 +00:00
|
|
|
|
import json
|
2016-04-04 14:28:52 +01:00
|
|
|
|
import itertools
|
2016-06-17 11:54:01 +01:00
|
|
|
|
from datetime import datetime
|
2016-05-31 14:18:35 +01:00
|
|
|
|
from string import ascii_uppercase
|
|
|
|
|
|
|
2016-03-07 18:47:05 +00:00
|
|
|
|
from contextlib import suppress
|
2016-05-13 21:25:59 +01:00
|
|
|
|
from zipfile import BadZipFile
|
|
|
|
|
|
from xlrd.biffh import XLRDError
|
2016-01-11 15:00:51 +00:00
|
|
|
|
|
|
|
|
|
|
from flask import (
|
|
|
|
|
|
request,
|
|
|
|
|
|
render_template,
|
|
|
|
|
|
redirect,
|
|
|
|
|
|
url_for,
|
|
|
|
|
|
flash,
|
2016-02-01 11:28:36 +00:00
|
|
|
|
abort,
|
2016-02-02 22:26:49 +00:00
|
|
|
|
session,
|
2016-02-17 14:20:55 +00:00
|
|
|
|
current_app
|
2016-01-11 15:00:51 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2016-02-17 14:20:55 +00:00
|
|
|
|
from flask_login import login_required, current_user
|
2016-04-14 12:00:55 +01:00
|
|
|
|
from notifications_utils.template import Template
|
|
|
|
|
|
from notifications_utils.recipients import RecipientCSV, first_column_heading, validate_and_format_phone_number
|
2015-12-10 21:15:20 +00:00
|
|
|
|
|
|
|
|
|
|
from app.main import main
|
2016-01-11 15:00:51 +00:00
|
|
|
|
from app.main.forms import CsvUploadForm
|
2016-01-14 16:00:13 +00:00
|
|
|
|
from app.main.uploader import (
|
|
|
|
|
|
s3upload,
|
|
|
|
|
|
s3download
|
|
|
|
|
|
)
|
2016-04-22 09:36:42 +01:00
|
|
|
|
from app import job_api_client, service_api_client, current_service, user_api_client, statistics_api_client
|
Accept common spreadsheet formats, not just CSV
We require users to export their spreadsheets as CSV files before
uploading them. But this seems like the sort of thing a computer should
be able to do.
So this commit adds a wrapper class which:
- takes a the uploaded file
- returns it in a normalised format, or reads it using pyexcel[1]
- gives the data back in CSV format
This allows us to accept `.csv`, `.xlsx`, `.xls` (97 and 95), `.ods`,
`.xlsm` and `.tsv` files. We can upload the resultant CSV just like
normal, and process it for errors as before.
Testing
---
To test this I’ve added a selection of common spreadsheet files as test
data. They all contain the same data, so the tests look to see that the
resultant CSV output is the same for each.
UI changes
---
This commit doesn’t change the UI, apart from to give a different error
message if a user uploads a file type that we still don’t understand.
I intend to do this as a separate pull request, in order to fulfil
https://www.pivotaltracker.com/story/show/119371637
2016-05-05 15:41:11 +01:00
|
|
|
|
from app.utils import user_has_permissions, get_errors_for_csv, Spreadsheet
|
2016-02-22 17:17:18 +00:00
|
|
|
|
|
2016-03-01 15:53:58 +00:00
|
|
|
|
|
|
|
|
|
|
def get_page_headings(template_type):
|
2016-04-01 10:42:23 +01:00
|
|
|
|
return {
|
|
|
|
|
|
'email': 'Email templates',
|
|
|
|
|
|
'sms': 'Text message templates'
|
|
|
|
|
|
}[template_type]
|
2016-03-01 15:53:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-05-05 08:39:36 +01:00
|
|
|
|
def get_example_csv_fields(column_headers, use_example_as_example, submitted_fields):
|
|
|
|
|
|
if use_example_as_example:
|
|
|
|
|
|
return ["example" for header in column_headers]
|
|
|
|
|
|
elif submitted_fields:
|
|
|
|
|
|
return [submitted_fields.get(header) for header in column_headers]
|
|
|
|
|
|
else:
|
|
|
|
|
|
return list(column_headers)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_example_csv_rows(template, use_example_as_example=True, submitted_fields=False):
|
2016-04-01 14:31:28 +01:00
|
|
|
|
return [
|
2016-05-05 08:39:36 +01:00
|
|
|
|
{
|
|
|
|
|
|
'email': 'test@example.com' if use_example_as_example else current_user.email_address,
|
|
|
|
|
|
'sms': '07700 900321' if use_example_as_example else validate_and_format_phone_number(
|
|
|
|
|
|
current_user.mobile_number, human_readable=True
|
|
|
|
|
|
)
|
|
|
|
|
|
}[template.template_type]
|
|
|
|
|
|
] + get_example_csv_fields(template.placeholders, use_example_as_example, submitted_fields)
|
2016-04-01 14:31:28 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-02-22 14:45:13 +00:00
|
|
|
|
@main.route("/services/<service_id>/send/<template_type>", methods=['GET'])
|
2016-02-29 17:03:56 +00:00
|
|
|
|
@login_required
|
2016-03-29 13:23:36 +01:00
|
|
|
|
@user_has_permissions('view_activity',
|
|
|
|
|
|
'send_texts',
|
|
|
|
|
|
'send_emails',
|
|
|
|
|
|
'manage_templates',
|
|
|
|
|
|
'manage_api_keys',
|
2016-03-30 11:30:18 +01:00
|
|
|
|
admin_override=True, any_=True)
|
2016-02-22 14:45:13 +00:00
|
|
|
|
def choose_template(service_id, template_type):
|
|
|
|
|
|
if template_type not in ['email', 'sms']:
|
|
|
|
|
|
abort(404)
|
2016-02-08 16:36:53 +00:00
|
|
|
|
return render_template(
|
2016-04-12 09:55:51 +01:00
|
|
|
|
'views/templates/choose.html',
|
2016-02-08 16:36:53 +00:00
|
|
|
|
templates=[
|
2016-02-26 09:33:45 +00:00
|
|
|
|
Template(
|
|
|
|
|
|
template,
|
2016-04-04 16:53:52 +01:00
|
|
|
|
prefix=current_service['name']
|
2016-03-30 10:23:39 +01:00
|
|
|
|
) for template in service_api_client.get_service_templates(service_id)['data']
|
2016-02-22 14:45:13 +00:00
|
|
|
|
if template['template_type'] == template_type
|
2016-02-08 16:36:53 +00:00
|
|
|
|
],
|
2016-02-25 16:37:39 +00:00
|
|
|
|
template_type=template_type,
|
2016-04-04 16:53:52 +01:00
|
|
|
|
page_heading=get_page_headings(template_type)
|
2016-02-08 16:36:53 +00:00
|
|
|
|
)
|
2016-02-02 17:28:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-04-12 14:19:51 +01:00
|
|
|
|
@main.route("/services/<service_id>/send/<template_id>/csv", methods=['GET', 'POST'])
|
2016-01-11 15:00:51 +00:00
|
|
|
|
@login_required
|
2016-03-03 12:15:24 +00:00
|
|
|
|
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
|
2016-02-22 17:17:18 +00:00
|
|
|
|
def send_messages(service_id, template_id):
|
2016-03-29 15:59:53 +01:00
|
|
|
|
template = Template(
|
2016-03-30 10:23:39 +01:00
|
|
|
|
service_api_client.get_service_template(service_id, template_id)['data'],
|
2016-04-04 16:53:52 +01:00
|
|
|
|
prefix=current_service['name']
|
2016-03-29 15:59:53 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
2016-01-11 15:00:51 +00:00
|
|
|
|
form = CsvUploadForm()
|
2016-01-13 17:32:40 +00:00
|
|
|
|
if form.validate_on_submit():
|
2016-01-11 15:00:51 +00:00
|
|
|
|
try:
|
2016-05-15 07:47:50 +01:00
|
|
|
|
upload_id = s3upload(
|
2016-03-07 18:47:05 +00:00
|
|
|
|
service_id,
|
2016-05-15 10:47:52 +01:00
|
|
|
|
Spreadsheet.from_file(form.file.data, filename=form.file.data.filename).as_dict,
|
2016-03-07 18:47:05 +00:00
|
|
|
|
current_app.config['AWS_REGION']
|
|
|
|
|
|
)
|
|
|
|
|
|
session['upload_data'] = {
|
|
|
|
|
|
"template_id": template_id,
|
|
|
|
|
|
"original_file_name": form.file.data.filename
|
|
|
|
|
|
}
|
2016-02-22 17:17:18 +00:00
|
|
|
|
return redirect(url_for('.check_messages',
|
2016-01-13 17:32:40 +00:00
|
|
|
|
service_id=service_id,
|
2016-03-29 15:59:53 +01:00
|
|
|
|
upload_id=upload_id,
|
|
|
|
|
|
template_type=template.template_type))
|
2016-05-13 21:25:59 +01:00
|
|
|
|
except (UnicodeDecodeError, BadZipFile, XLRDError):
|
|
|
|
|
|
flash('Couldn’t read {}. Try using a different file format.'.format(
|
|
|
|
|
|
form.file.data.filename
|
|
|
|
|
|
))
|
2016-01-11 15:00:51 +00:00
|
|
|
|
|
2016-02-08 16:36:53 +00:00
|
|
|
|
return render_template(
|
2016-02-22 17:17:18 +00:00
|
|
|
|
'views/send.html',
|
2016-02-08 16:36:53 +00:00
|
|
|
|
template=template,
|
2016-05-31 14:18:35 +01:00
|
|
|
|
column_headings=list(ascii_uppercase[:len(template.placeholders) + 1]),
|
|
|
|
|
|
example=[
|
|
|
|
|
|
[first_column_heading[template.template_type]] + list(template.placeholders),
|
|
|
|
|
|
get_example_csv_rows(template)
|
|
|
|
|
|
],
|
2016-04-04 16:53:52 +01:00
|
|
|
|
form=form
|
2016-02-08 16:36:53 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-22 14:45:13 +00:00
|
|
|
|
@main.route("/services/<service_id>/send/<template_id>.csv", methods=['GET'])
|
2016-02-08 16:36:53 +00:00
|
|
|
|
@login_required
|
2016-03-30 11:30:18 +01:00
|
|
|
|
@user_has_permissions('send_texts', 'send_emails', 'send_letters', 'manage_templates', any_=True)
|
2016-05-05 08:39:36 +01:00
|
|
|
|
def get_example_csv(service_id, template_id):
|
2016-03-30 10:23:39 +01:00
|
|
|
|
template = Template(service_api_client.get_service_template(service_id, template_id)['data'])
|
2016-05-15 10:47:52 +01:00
|
|
|
|
return Spreadsheet.from_rows([
|
|
|
|
|
|
[first_column_heading[template.template_type]] + list(template.placeholders),
|
|
|
|
|
|
get_example_csv_rows(template)
|
|
|
|
|
|
]).as_csv_data, 200, {
|
|
|
|
|
|
'Content-Type': 'text/csv; charset=utf-8',
|
|
|
|
|
|
'Content-Disposition': 'inline; filename="{}.csv"'.format(template.name)
|
|
|
|
|
|
}
|
2015-12-10 21:15:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-05-05 08:39:36 +01:00
|
|
|
|
@main.route("/services/<service_id>/send/<template_id>/test", methods=['GET', 'POST'])
|
2016-02-12 10:55:21 +00:00
|
|
|
|
@login_required
|
2016-03-03 12:15:24 +00:00
|
|
|
|
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
|
2016-05-05 08:39:36 +01:00
|
|
|
|
def send_test(service_id, template_id):
|
2016-04-03 11:30:23 +01:00
|
|
|
|
|
2016-06-19 09:04:21 +01:00
|
|
|
|
file_name = current_app.config['TEST_MESSAGE_FILENAME']
|
2016-05-15 07:47:50 +01:00
|
|
|
|
|
2016-05-05 08:39:36 +01:00
|
|
|
|
template = Template(
|
|
|
|
|
|
service_api_client.get_service_template(service_id, template_id)['data'],
|
|
|
|
|
|
prefix=current_service['name']
|
|
|
|
|
|
)
|
2016-03-29 15:59:53 +01:00
|
|
|
|
|
2016-05-05 08:39:36 +01:00
|
|
|
|
if len(template.placeholders) == 0 or request.method == 'POST':
|
2016-05-15 10:47:52 +01:00
|
|
|
|
upload_id = s3upload(
|
|
|
|
|
|
service_id,
|
|
|
|
|
|
{
|
|
|
|
|
|
'file_name': file_name,
|
|
|
|
|
|
'data': Spreadsheet.from_rows([
|
|
|
|
|
|
[first_column_heading[template.template_type]] + list(template.placeholders),
|
|
|
|
|
|
get_example_csv_rows(template, use_example_as_example=False, submitted_fields=request.form)
|
|
|
|
|
|
]).as_csv_data
|
|
|
|
|
|
},
|
|
|
|
|
|
current_app.config['AWS_REGION']
|
|
|
|
|
|
)
|
|
|
|
|
|
session['upload_data'] = {
|
|
|
|
|
|
"template_id": template_id,
|
|
|
|
|
|
"original_file_name": file_name
|
|
|
|
|
|
}
|
|
|
|
|
|
return redirect(url_for(
|
|
|
|
|
|
'.check_messages',
|
|
|
|
|
|
upload_id=upload_id,
|
|
|
|
|
|
service_id=service_id,
|
|
|
|
|
|
template_type=template.template_type,
|
2016-05-20 12:05:05 +01:00
|
|
|
|
from_test=True,
|
|
|
|
|
|
help=2 if request.args.get('help') else 0
|
2016-05-15 10:47:52 +01:00
|
|
|
|
))
|
2016-02-12 10:55:21 +00:00
|
|
|
|
|
2016-05-05 08:39:36 +01:00
|
|
|
|
return render_template(
|
|
|
|
|
|
'views/send-test.html',
|
|
|
|
|
|
template=template,
|
|
|
|
|
|
recipient_column=first_column_heading[template.template_type],
|
|
|
|
|
|
example=[get_example_csv_rows(template, use_example_as_example=False)]
|
|
|
|
|
|
)
|
2016-02-12 10:55:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-03-15 06:53:06 +00:00
|
|
|
|
@main.route("/services/<service_id>/send/<template_id>/from-api", methods=['GET'])
|
|
|
|
|
|
@login_required
|
|
|
|
|
|
def send_from_api(service_id, template_id):
|
|
|
|
|
|
template = Template(
|
2016-04-18 11:10:47 +01:00
|
|
|
|
service_api_client.get_service_template(service_id, template_id)['data'],
|
|
|
|
|
|
prefix=current_service['name']
|
2016-03-15 06:53:06 +00:00
|
|
|
|
)
|
2016-04-18 11:10:47 +01:00
|
|
|
|
personalisation = {
|
|
|
|
|
|
placeholder: "..." for placeholder in template.placeholders
|
2016-03-15 06:53:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
'views/send-from-api.html',
|
|
|
|
|
|
template=template,
|
2016-04-18 11:10:47 +01:00
|
|
|
|
personalisation=json.dumps(personalisation, indent=4) if personalisation else None
|
2016-03-15 06:53:06 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-03-29 15:59:53 +01:00
|
|
|
|
@main.route("/services/<service_id>/<template_type>/check/<upload_id>", methods=['GET'])
|
2016-01-11 15:00:51 +00:00
|
|
|
|
@login_required
|
2016-03-03 12:15:24 +00:00
|
|
|
|
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
|
2016-03-29 15:59:53 +01:00
|
|
|
|
def check_messages(service_id, template_type, upload_id):
|
2016-06-17 11:54:01 +01:00
|
|
|
|
|
2016-03-29 15:59:53 +01:00
|
|
|
|
if not session.get('upload_data'):
|
|
|
|
|
|
return redirect(url_for('main.choose_template', service_id=service_id, template_type=template_type))
|
2016-02-22 17:17:18 +00:00
|
|
|
|
|
2016-04-04 14:28:52 +01:00
|
|
|
|
users = user_api_client.get_users_for_service(service_id=service_id)
|
2016-06-21 10:54:30 +01:00
|
|
|
|
today = datetime.utcnow().date().strftime('%Y-%m-%d')
|
2016-06-17 11:54:01 +01:00
|
|
|
|
|
|
|
|
|
|
statistics = statistics_api_client.get_statistics_for_service_for_day(service_id, today)
|
|
|
|
|
|
if not statistics:
|
|
|
|
|
|
statistics = {}
|
2016-04-04 14:28:52 +01:00
|
|
|
|
|
2016-03-07 18:47:05 +00:00
|
|
|
|
contents = s3download(service_id, upload_id)
|
|
|
|
|
|
if not contents:
|
|
|
|
|
|
flash('There was a problem reading your upload file')
|
2016-01-14 16:00:13 +00:00
|
|
|
|
|
2016-03-30 10:23:39 +01:00
|
|
|
|
template = service_api_client.get_service_template(
|
2016-03-09 12:18:11 +00:00
|
|
|
|
service_id,
|
|
|
|
|
|
session['upload_data'].get('template_id')
|
|
|
|
|
|
)['data']
|
|
|
|
|
|
|
2016-03-07 18:47:05 +00:00
|
|
|
|
template = Template(
|
2016-03-09 12:18:11 +00:00
|
|
|
|
template,
|
2016-04-04 16:53:52 +01:00
|
|
|
|
prefix=current_service['name']
|
2016-03-07 18:47:05 +00:00
|
|
|
|
)
|
2016-01-14 16:00:13 +00:00
|
|
|
|
|
2016-03-07 18:47:05 +00:00
|
|
|
|
recipients = RecipientCSV(
|
|
|
|
|
|
contents,
|
|
|
|
|
|
template_type=template.template_type,
|
|
|
|
|
|
placeholders=template.placeholders,
|
2016-04-07 14:30:51 +01:00
|
|
|
|
max_initial_rows_shown=50,
|
|
|
|
|
|
max_errors_shown=50,
|
2016-04-04 14:28:52 +01:00
|
|
|
|
whitelist=itertools.chain.from_iterable(
|
|
|
|
|
|
[user.mobile_number, user.email_address] for user in users
|
|
|
|
|
|
) if current_service['restricted'] else None
|
2016-02-26 10:54:06 +00:00
|
|
|
|
)
|
2016-03-07 18:47:05 +00:00
|
|
|
|
|
2016-07-01 07:04:55 +01:00
|
|
|
|
if request.args.get('from_test'):
|
2016-06-24 10:15:20 +01:00
|
|
|
|
extra_args = {'help': 1} if request.args.get('help', '0') != '0' else {}
|
2016-07-01 07:04:55 +01:00
|
|
|
|
if len(template.placeholders):
|
|
|
|
|
|
back_link = url_for(
|
|
|
|
|
|
'.send_test', service_id=service_id, template_id=template.id, **extra_args
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
back_link = url_for(
|
|
|
|
|
|
'.choose_template', service_id=service_id, template_type=template.template_type, **extra_args
|
|
|
|
|
|
)
|
2016-05-05 08:39:36 +01:00
|
|
|
|
else:
|
|
|
|
|
|
back_link = url_for('.send_messages', service_id=service_id, template_id=template.id)
|
|
|
|
|
|
|
2016-03-07 18:47:05 +00:00
|
|
|
|
with suppress(StopIteration):
|
|
|
|
|
|
template.values = next(recipients.rows)
|
2016-05-05 08:38:55 +01:00
|
|
|
|
first_recipient = template.values.get(recipients.recipient_column_header, '')
|
2016-03-07 18:47:05 +00:00
|
|
|
|
|
|
|
|
|
|
session['upload_data']['notification_count'] = len(list(recipients.rows))
|
|
|
|
|
|
session['upload_data']['valid'] = not recipients.has_errors
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
'views/check.html',
|
|
|
|
|
|
recipients=recipients,
|
2016-05-05 08:38:55 +01:00
|
|
|
|
first_recipient=first_recipient,
|
2016-03-07 18:47:05 +00:00
|
|
|
|
template=template,
|
2016-06-03 13:52:15 +01:00
|
|
|
|
errors=recipients.has_errors,
|
|
|
|
|
|
row_errors=get_errors_for_csv(recipients, template.template_type),
|
2016-03-07 18:47:05 +00:00
|
|
|
|
count_of_recipients=session['upload_data']['notification_count'],
|
2016-03-10 20:03:53 +00:00
|
|
|
|
count_of_displayed_recipients=(
|
|
|
|
|
|
len(list(recipients.initial_annotated_rows_with_errors))
|
2016-06-03 14:38:10 +01:00
|
|
|
|
if any(recipients.rows_with_errors) and not recipients.missing_column_headers else
|
2016-03-10 20:03:53 +00:00
|
|
|
|
len(list(recipients.initial_annotated_rows))
|
|
|
|
|
|
),
|
2016-03-07 18:47:05 +00:00
|
|
|
|
original_file_name=session['upload_data'].get('original_file_name'),
|
2016-03-29 15:59:53 +01:00
|
|
|
|
upload_id=upload_id,
|
2016-04-22 09:36:42 +01:00
|
|
|
|
form=CsvUploadForm(),
|
2016-05-05 08:39:36 +01:00
|
|
|
|
statistics=statistics,
|
|
|
|
|
|
back_link=back_link
|
2016-03-07 18:47:05 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-04-05 08:55:43 +01:00
|
|
|
|
@main.route("/services/<service_id>/<template_type>/check/<upload_id>", methods=['POST'])
|
|
|
|
|
|
@login_required
|
|
|
|
|
|
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
|
|
|
|
|
|
def recheck_messages(service_id, template_type, upload_id):
|
|
|
|
|
|
|
|
|
|
|
|
if not session.get('upload_data'):
|
|
|
|
|
|
return redirect(url_for('main.choose_template', service_id=service_id, template_type=template_type))
|
|
|
|
|
|
|
|
|
|
|
|
return send_messages(service_id, session['upload_data'].get('template_id'))
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-03-29 15:59:53 +01:00
|
|
|
|
@main.route("/services/<service_id>/start-job/<upload_id>", methods=['POST'])
|
2016-03-07 18:47:05 +00:00
|
|
|
|
@login_required
|
|
|
|
|
|
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
|
|
|
|
|
|
def start_job(service_id, upload_id):
|
|
|
|
|
|
|
|
|
|
|
|
upload_data = session['upload_data']
|
|
|
|
|
|
|
2016-03-08 17:50:18 +00:00
|
|
|
|
if request.files or not upload_data.get('valid'):
|
2016-03-07 18:47:05 +00:00
|
|
|
|
# The csv was invalid, validate the csv again
|
|
|
|
|
|
return send_messages(service_id, upload_data.get('template_id'))
|
|
|
|
|
|
|
|
|
|
|
|
session.pop('upload_data')
|
|
|
|
|
|
|
2016-03-08 17:50:18 +00:00
|
|
|
|
job_api_client.create_job(
|
|
|
|
|
|
upload_id,
|
|
|
|
|
|
service_id,
|
|
|
|
|
|
upload_data.get('template_id'),
|
|
|
|
|
|
upload_data.get('original_file_name'),
|
|
|
|
|
|
upload_data.get('notification_count')
|
|
|
|
|
|
)
|
2016-03-07 18:47:05 +00:00
|
|
|
|
|
|
|
|
|
|
return redirect(
|
2016-05-26 09:57:16 +01:00
|
|
|
|
url_for('main.view_job', job_id=upload_id, service_id=service_id, help=request.form.get('help'))
|
2016-02-17 15:49:07 +00:00
|
|
|
|
)
|
2016-07-01 10:49:54 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route("/services/<service_id>/end-tour/<example_template_id>")
|
|
|
|
|
|
@login_required
|
|
|
|
|
|
@user_has_permissions('manage_templates')
|
|
|
|
|
|
def go_to_dashboard_after_tour(service_id, example_template_id):
|
|
|
|
|
|
|
|
|
|
|
|
service_api_client.delete_service_template(service_id, example_template_id)
|
|
|
|
|
|
|
|
|
|
|
|
return redirect(
|
|
|
|
|
|
url_for('main.service_dashboard', service_id=service_id)
|
|
|
|
|
|
)
|