Files
notifications-admin/app/main/views/feedback.py
Leo Hemsted f6367f2278 move (non-api) clients (inc redis) from app/__init__.py to extensions
when clients are defined in app/__init__.py, it increases the chance of 
cyclical imports. By moving module level client singletons out to a 
separate extensions file, we stop cyclical imports, but keep the same 
code flow - the clients are still initialised in `create_app` in 
`__init__.py`.

The redis client in particular is no longer separate - previously redis 
was set up on the `NotifyAdminAPIClient` base class, but now there's one 
singleton in `app.extensions`. This was done so that we can access redis 
from outside of the existing clients.
2019-02-15 11:44:08 +00:00

243 lines
6.5 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.
from datetime import datetime
import pytz
from flask import abort, redirect, render_template, request, session, url_for
from flask_login import current_user
from app import convert_to_boolean, current_service, service_api_client
from app.extensions import zendesk_client
from app.main import main
from app.main.forms import Feedback, Problem, SupportType, Triage
QUESTION_TICKET_TYPE = 'ask-question-give-feedback'
PROBLEM_TICKET_TYPE = "report-problem"
def get_prefilled_message():
return {
'agreement': (
'Please can you tell me if theres an agreement in place '
'between GOV.UK Notify and my organisation?'
),
'letter-branding': (
'I would like my own logo on my letter templates.'
),
}.get(
request.args.get('body'), ''
)
@main.route('/support', methods=['GET', 'POST'])
def support():
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)
@main.route('/support/triage', methods=['GET', 'POST'])
def triage():
form = Triage()
if form.validate_on_submit():
return redirect(url_for(
'.feedback',
ticket_type=PROBLEM_TICKET_TYPE,
severe=form.severe.data
))
return render_template(
'views/support/triage.html',
form=form
)
@main.route('/support/<ticket_type>', methods=['GET', 'POST'])
def feedback(ticket_type):
try:
form = {
QUESTION_TICKET_TYPE: Feedback,
PROBLEM_TICKET_TYPE: Problem,
}[ticket_type]()
except KeyError:
abort(404)
if not form.feedback.data:
form.feedback.data = session.pop('feedback_message', '')
if request.args.get('severe') in ['yes', 'no']:
severe = convert_to_boolean(request.args.get('severe'))
else:
severe = None
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
anonymous = (
(not form.email_address.data) and
(not current_user.is_authenticated)
)
if needs_triage(ticket_type, severe):
session['feedback_message'] = form.feedback.data
return redirect(url_for('.triage'))
if needs_escalation(ticket_type, severe):
return redirect(url_for('.bat_phone'))
if current_user.is_authenticated:
form.email_address.data = current_user.email_address
form.name.data = current_user.name
if form.validate_on_submit():
user_email = form.email_address.data
user_name = form.name.data or None
if current_service:
service_string = 'Service: "{name}"\n{url}\n'.format(
name=current_service.name,
url=url_for('main.service_dashboard', service_id=current_service.id, _external=True)
)
else:
service_string = ''
feedback_msg = '{}\n{}{}'.format(
form.feedback.data,
service_string,
'' if user_email else '{} (no email address supplied)'.format(form.name.data)
)
zendesk_client.create_ticket(
subject='Notify feedback',
message=feedback_msg,
ticket_type=ticket_type,
p1=p1,
user_email=user_email,
user_name=user_name
)
return redirect(url_for('.thanks', urgent=urgent, anonymous=anonymous))
if not form.feedback.data:
form.feedback.data = get_prefilled_message()
return render_template(
'views/support/{}.html'.format(ticket_type),
form=form,
ticket_type=ticket_type,
)
@main.route('/support/escalate', methods=['GET', 'POST'])
def bat_phone():
if current_user.is_authenticated:
return redirect(url_for('main.feedback', ticket_type=PROBLEM_TICKET_TYPE))
return render_template('views/support/bat-phone.html')
@main.route('/support/thanks', methods=['GET', 'POST'])
def thanks():
return render_template(
'views/support/thanks.html',
urgent=convert_to_boolean(request.args.get('urgent')),
anonymous=convert_to_boolean(request.args.get('anonymous')),
logged_in=current_user.is_authenticated,
)
def in_business_hours():
now = datetime.utcnow().replace(tzinfo=pytz.utc)
if is_weekend(now) or is_bank_holiday(now):
return False
return london_time_today_as_utc(9, 30) <= now < london_time_today_as_utc(17, 30)
def london_time_today_as_utc(hour, minute):
return pytz.timezone('Europe/London').localize(
datetime.now().replace(hour=hour, minute=minute)
).astimezone(pytz.utc)
def is_weekend(time):
return time.strftime('%A') in {
'Saturday',
'Sunday',
}
def is_bank_holiday(time):
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",
}
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']
)
def needs_triage(ticket_type, severe):
return all((
ticket_type == PROBLEM_TICKET_TYPE,
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((
ticket_type == PROBLEM_TICKET_TYPE,
severe,
not current_user.is_authenticated,
not in_business_hours(),
))