2018-02-20 11:22:17 +00:00
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
2016-12-12 11:44:11 +00:00
|
|
|
|
import pytz
|
2018-02-20 11:22:17 +00:00
|
|
|
|
from flask import abort, redirect, render_template, request, session, url_for
|
2016-12-12 11:31:26 +00:00
|
|
|
|
from flask_login import current_user
|
2018-01-16 11:50:29 +00:00
|
|
|
|
|
2018-02-20 11:22:17 +00:00
|
|
|
|
from app import (
|
|
|
|
|
|
convert_to_boolean,
|
|
|
|
|
|
current_service,
|
|
|
|
|
|
service_api_client,
|
2018-04-24 17:37:15 +01:00
|
|
|
|
zendesk_client,
|
2018-02-20 11:22:17 +00:00
|
|
|
|
)
|
2016-04-26 10:32:26 +01:00
|
|
|
|
from app.main import main
|
2018-02-20 11:22:17 +00:00
|
|
|
|
from app.main.forms import Feedback, Problem, SupportType, Triage
|
2018-01-16 11:50:29 +00:00
|
|
|
|
|
2017-11-28 11:59:11 +00:00
|
|
|
|
QUESTION_TICKET_TYPE = 'ask-question-give-feedback'
|
|
|
|
|
|
PROBLEM_TICKET_TYPE = "report-problem"
|
|
|
|
|
|
|
2016-04-26 10:32:26 +01:00
|
|
|
|
|
2018-03-20 10:41:58 +00:00
|
|
|
|
def get_prefilled_message():
|
|
|
|
|
|
return {
|
|
|
|
|
|
'agreement': (
|
2018-05-08 14:28:08 +01:00
|
|
|
|
'Please can you tell me if there’s an agreement in place '
|
|
|
|
|
|
'between GOV.UK Notify and my organisation?'
|
2018-03-20 10:41:58 +00:00
|
|
|
|
),
|
2018-08-21 09:23:32 +01:00
|
|
|
|
'letter-branding': (
|
|
|
|
|
|
'I would like my own logo on my letter templates.'
|
|
|
|
|
|
),
|
2018-03-20 10:41:58 +00:00
|
|
|
|
}.get(
|
|
|
|
|
|
request.args.get('body'), ''
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-12-12 11:18:25 +00:00
|
|
|
|
@main.route('/support', methods=['GET', 'POST'])
|
|
|
|
|
|
def support():
|
2016-12-12 11:25:43 +00:00
|
|
|
|
form = SupportType()
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
return redirect(url_for(
|
|
|
|
|
|
'.feedback',
|
|
|
|
|
|
ticket_type=form.support_type.data,
|
|
|
|
|
|
))
|
|
|
|
|
|
return render_template('views/support/index.html', form=form)
|
2016-12-12 11:18:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
2016-12-12 11:44:11 +00:00
|
|
|
|
@main.route('/support/triage', methods=['GET', 'POST'])
|
|
|
|
|
|
def triage():
|
|
|
|
|
|
form = Triage()
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
|
|
return redirect(url_for(
|
|
|
|
|
|
'.feedback',
|
2017-11-28 11:59:11 +00:00
|
|
|
|
ticket_type=PROBLEM_TICKET_TYPE,
|
2017-03-07 09:44:21 +00:00
|
|
|
|
severe=form.severe.data
|
2016-12-12 11:44:11 +00:00
|
|
|
|
))
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
'views/support/triage.html',
|
|
|
|
|
|
form=form
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-11-28 11:59:11 +00:00
|
|
|
|
@main.route('/support/<ticket_type>', methods=['GET', 'POST'])
|
|
|
|
|
|
def feedback(ticket_type):
|
2016-12-21 14:55:49 +00:00
|
|
|
|
try:
|
|
|
|
|
|
form = {
|
2017-11-28 11:59:11 +00:00
|
|
|
|
QUESTION_TICKET_TYPE: Feedback,
|
|
|
|
|
|
PROBLEM_TICKET_TYPE: Problem,
|
2016-12-21 14:55:49 +00:00
|
|
|
|
}[ticket_type]()
|
|
|
|
|
|
except KeyError:
|
2016-12-12 11:25:43 +00:00
|
|
|
|
abort(404)
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
2016-12-12 14:11:34 +00:00
|
|
|
|
if not form.feedback.data:
|
|
|
|
|
|
form.feedback.data = session.pop('feedback_message', '')
|
|
|
|
|
|
|
2017-02-02 10:42:17 +00:00
|
|
|
|
if request.args.get('severe') in ['yes', 'no']:
|
|
|
|
|
|
severe = convert_to_boolean(request.args.get('severe'))
|
|
|
|
|
|
else:
|
|
|
|
|
|
severe = None
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
2018-04-26 10:52:28 +01:00
|
|
|
|
p1 = False
|
|
|
|
|
|
urgent = False
|
|
|
|
|
|
|
|
|
|
|
|
if in_business_hours():
|
|
|
|
|
|
# if we're in the office, it's urgent (aka we'll get back in 30 mins)
|
|
|
|
|
|
urgent = True
|
|
|
|
|
|
elif ticket_type == PROBLEM_TICKET_TYPE and severe:
|
|
|
|
|
|
# out of hours, it's only a p1 and it's only urgent if it's a p1
|
|
|
|
|
|
urgent = True
|
|
|
|
|
|
p1 = True
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
2017-02-02 09:15:48 +00:00
|
|
|
|
anonymous = (
|
|
|
|
|
|
(not form.email_address.data) and
|
|
|
|
|
|
(not current_user.is_authenticated)
|
|
|
|
|
|
)
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
if needs_triage(ticket_type, severe):
|
2016-12-12 14:11:34 +00:00
|
|
|
|
session['feedback_message'] = form.feedback.data
|
2016-12-12 11:44:11 +00:00
|
|
|
|
return redirect(url_for('.triage'))
|
|
|
|
|
|
|
|
|
|
|
|
if needs_escalation(ticket_type, severe):
|
|
|
|
|
|
return redirect(url_for('.bat_phone'))
|
|
|
|
|
|
|
2016-12-21 14:55:49 +00:00
|
|
|
|
if current_user.is_authenticated:
|
|
|
|
|
|
form.email_address.data = current_user.email_address
|
|
|
|
|
|
form.name.data = current_user.name
|
|
|
|
|
|
|
2016-04-26 10:32:26 +01:00
|
|
|
|
if form.validate_on_submit():
|
2016-12-21 14:55:49 +00:00
|
|
|
|
user_email = form.email_address.data
|
|
|
|
|
|
user_name = form.name.data or None
|
2017-01-30 14:18:19 +00:00
|
|
|
|
if current_service:
|
2018-06-15 15:30:45 +01:00
|
|
|
|
service_string = 'Service: "{name}"\n{url}\n'.format(
|
2018-07-20 08:42:01 +01:00
|
|
|
|
name=current_service.name,
|
|
|
|
|
|
url=url_for('main.service_dashboard', service_id=current_service.id, _external=True)
|
2017-01-30 14:18:19 +00:00
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
service_string = ''
|
|
|
|
|
|
|
2018-06-15 15:30:45 +01:00
|
|
|
|
feedback_msg = '{}\n{}{}'.format(
|
2018-06-15 16:03:45 +01:00
|
|
|
|
form.feedback.data,
|
2017-01-30 14:18:19 +00:00
|
|
|
|
service_string,
|
2018-06-15 16:03:45 +01:00
|
|
|
|
'' if user_email else '{} (no email address supplied)'.format(form.name.data)
|
2016-08-04 18:01:08 +01:00
|
|
|
|
)
|
2018-01-16 11:50:29 +00:00
|
|
|
|
|
2018-04-26 10:52:28 +01:00
|
|
|
|
zendesk_client.create_ticket(
|
|
|
|
|
|
subject='Notify feedback',
|
2018-06-15 15:33:21 +01:00
|
|
|
|
message=feedback_msg,
|
2018-04-26 10:52:28 +01:00
|
|
|
|
ticket_type=ticket_type,
|
|
|
|
|
|
p1=p1,
|
|
|
|
|
|
user_email=user_email,
|
|
|
|
|
|
user_name=user_name
|
|
|
|
|
|
)
|
2016-12-12 12:29:29 +00:00
|
|
|
|
return redirect(url_for('.thanks', urgent=urgent, anonymous=anonymous))
|
2016-04-26 10:32:26 +01:00
|
|
|
|
|
2018-03-14 14:42:25 +00:00
|
|
|
|
if not form.feedback.data:
|
2018-03-20 10:41:58 +00:00
|
|
|
|
form.feedback.data = get_prefilled_message()
|
2018-03-14 14:42:25 +00:00
|
|
|
|
|
2016-12-12 11:25:43 +00:00
|
|
|
|
return render_template(
|
|
|
|
|
|
'views/support/{}.html'.format(ticket_type),
|
|
|
|
|
|
form=form,
|
2016-12-12 11:44:11 +00:00
|
|
|
|
ticket_type=ticket_type,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route('/support/escalate', methods=['GET', 'POST'])
|
|
|
|
|
|
def bat_phone():
|
|
|
|
|
|
|
|
|
|
|
|
if current_user.is_authenticated:
|
2017-11-28 11:59:11 +00:00
|
|
|
|
return redirect(url_for('main.feedback', ticket_type=PROBLEM_TICKET_TYPE))
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
return render_template('views/support/bat-phone.html')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@main.route('/support/thanks', methods=['GET', 'POST'])
|
|
|
|
|
|
def thanks():
|
|
|
|
|
|
return render_template(
|
|
|
|
|
|
'views/support/thanks.html',
|
2016-12-12 12:29:29 +00:00
|
|
|
|
urgent=convert_to_boolean(request.args.get('urgent')),
|
|
|
|
|
|
anonymous=convert_to_boolean(request.args.get('anonymous')),
|
2017-02-02 09:18:15 +00:00
|
|
|
|
logged_in=current_user.is_authenticated,
|
2016-12-12 11:44:11 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def in_business_hours():
|
|
|
|
|
|
|
2017-02-02 09:35:18 +00:00
|
|
|
|
now = datetime.utcnow().replace(tzinfo=pytz.utc)
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
if is_weekend(now) or is_bank_holiday(now):
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2017-02-02 09:35:18 +00:00
|
|
|
|
return london_time_today_as_utc(9, 30) <= now < london_time_today_as_utc(17, 30)
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
2017-02-02 09:35:18 +00:00
|
|
|
|
|
|
|
|
|
|
def london_time_today_as_utc(hour, minute):
|
|
|
|
|
|
return pytz.timezone('Europe/London').localize(
|
|
|
|
|
|
datetime.now().replace(hour=hour, minute=minute)
|
|
|
|
|
|
).astimezone(pytz.utc)
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_weekend(time):
|
|
|
|
|
|
return time.strftime('%A') in {
|
|
|
|
|
|
'Saturday',
|
|
|
|
|
|
'Sunday',
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_bank_holiday(time):
|
2018-01-16 12:46:46 +00:00
|
|
|
|
return time.strftime('%Y-%m-%d') in {
|
|
|
|
|
|
# taken from https://www.gov.uk/bank-holidays.json
|
|
|
|
|
|
"2016-01-01",
|
|
|
|
|
|
"2016-03-25",
|
|
|
|
|
|
"2016-03-28",
|
|
|
|
|
|
"2016-05-02",
|
|
|
|
|
|
"2016-05-30",
|
|
|
|
|
|
"2016-08-29",
|
|
|
|
|
|
"2016-12-26",
|
|
|
|
|
|
"2016-12-27",
|
|
|
|
|
|
"2017-01-02",
|
|
|
|
|
|
"2017-04-14",
|
|
|
|
|
|
"2017-04-17",
|
|
|
|
|
|
"2017-05-01",
|
|
|
|
|
|
"2017-05-29",
|
|
|
|
|
|
"2017-08-28",
|
|
|
|
|
|
"2017-12-25",
|
|
|
|
|
|
"2017-12-26",
|
|
|
|
|
|
"2018-01-01",
|
|
|
|
|
|
"2018-03-30",
|
|
|
|
|
|
"2018-04-02",
|
|
|
|
|
|
"2018-05-07",
|
|
|
|
|
|
"2018-05-28",
|
|
|
|
|
|
"2018-08-27",
|
|
|
|
|
|
"2018-12-25",
|
|
|
|
|
|
"2018-12-26",
|
|
|
|
|
|
"2019-01-01",
|
|
|
|
|
|
"2019-04-19",
|
|
|
|
|
|
"2019-04-22",
|
|
|
|
|
|
"2019-05-06",
|
|
|
|
|
|
"2019-05-27",
|
|
|
|
|
|
"2019-08-26",
|
|
|
|
|
|
"2019-12-25",
|
|
|
|
|
|
"2019-12-26",
|
2016-12-12 11:44:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def has_live_services(user_id):
|
|
|
|
|
|
return any(
|
|
|
|
|
|
service['restricted'] is False
|
|
|
|
|
|
for service in service_api_client.get_services({'user_id': user_id})['data']
|
2016-12-12 11:25:43 +00:00
|
|
|
|
)
|
2016-12-12 11:44:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def needs_triage(ticket_type, severe):
|
|
|
|
|
|
return all((
|
2017-11-28 11:59:11 +00:00
|
|
|
|
ticket_type == PROBLEM_TICKET_TYPE,
|
2016-12-12 11:44:11 +00:00
|
|
|
|
severe is None,
|
|
|
|
|
|
(
|
|
|
|
|
|
not current_user.is_authenticated or has_live_services(current_user.id)
|
|
|
|
|
|
),
|
|
|
|
|
|
not in_business_hours(),
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def needs_escalation(ticket_type, severe):
|
|
|
|
|
|
return all((
|
2017-11-28 11:59:11 +00:00
|
|
|
|
ticket_type == PROBLEM_TICKET_TYPE,
|
2017-02-02 10:42:17 +00:00
|
|
|
|
severe,
|
2016-12-12 11:44:11 +00:00
|
|
|
|
not current_user.is_authenticated,
|
|
|
|
|
|
not in_business_hours(),
|
|
|
|
|
|
))
|