Merge pull request #1540 from alphagov/reply-inbound

Let users reply to an inbound message with an existing template
This commit is contained in:
Chris Hill-Scott
2017-10-18 12:33:42 +01:00
committed by GitHub
9 changed files with 234 additions and 10 deletions

View File

@@ -1,5 +1,7 @@
from flask import (
jsonify,
session,
redirect,
render_template,
url_for,
)
@@ -7,6 +9,7 @@ from flask_login import login_required
from notifications_utils.recipients import format_phone_number_human_readable
from notifications_utils.template import SMSPreviewTemplate
from app.main import main
from app.main.forms import SearchTemplatesForm
from app.utils import user_has_permissions
from app import notification_api_client, service_api_client
from notifications_python_client.errors import HTTPError
@@ -24,6 +27,7 @@ def conversation(service_id, notification_id):
user_number=user_number,
partials=get_conversation_partials(service_id, user_number),
updates_url=url_for('.conversation_updates', service_id=service_id, notification_id=notification_id),
notification_id=notification_id,
)
@@ -38,6 +42,50 @@ def conversation_updates(service_id, notification_id):
))
@main.route("/services/<service_id>/conversation/<notification_id>/reply-with")
@login_required
@user_has_permissions('send_texts', admin_override=True)
def conversation_reply(
service_id,
notification_id,
):
templates = [
template
for template in service_api_client.get_service_templates(service_id)['data']
if template['template_type'] == 'sms'
]
return render_template(
'views/templates/choose-reply.html',
templates=templates,
show_search_box=(len(templates) > 7),
template_type='sms',
search_form=SearchTemplatesForm(),
notification_id=notification_id,
)
@main.route("/services/<service_id>/conversation/<notification_id>/reply-with/<template_id>")
@login_required
@user_has_permissions('send_texts', admin_override=True)
def conversation_reply_with_template(
service_id,
notification_id,
template_id,
):
session['recipient'] = get_user_number(service_id, notification_id)
session['placeholders'] = {'phone number': session['recipient']}
return redirect(url_for(
'main.send_one_off_step',
service_id=service_id,
template_id=template_id,
step_index=1,
))
def get_conversation_partials(service_id, user_number):
return {

View File

@@ -82,6 +82,8 @@ def view_notification(service_id, notification_id):
created_at=notification['created_at'],
help=get_help_argument(),
estimated_letter_delivery_date=get_letter_timings(notification['created_at']).earliest_delivery,
notification_id=notification['id'],
can_receive_inbound=('inbound_sms' in current_service['permissions']),
)

View File

@@ -22,6 +22,12 @@
'messages',
) }}
{% if current_user.has_permissions(['send_texts'], admin_override=True) %}
<p>
<a href="{{ url_for('.conversation_reply', service_id=current_service.id, notification_id=notification_id) }}">Send a text message to this phone number</a>
</p>
{% endif %}
</div>
{% endblock %}

View File

@@ -42,4 +42,10 @@
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
{% endif %}
{% if current_user.has_permissions(['send_texts'], admin_override=True) and template.template_type == 'sms' and can_receive_inbound %}
<p>
<a href="{{ url_for('.conversation', service_id=current_service.id, notification_id=notification_id, _anchor='n{}'.format(notification_id)) }}">See all text messages sent to this phone number</a>
</p>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% if show_search_box %}
<div data-module="autofocus">
<div class="live-search" data-module="live-search" data-targets="#template-list .column-whole">
{{ textbox(
search_form.search,
width='1-1'
) }}
</div>
</div>
{% endif %}

View File

@@ -0,0 +1,47 @@
{% from "components/pill.html" import pill %}
{% from "components/message-count-label.html" import message_count_label %}
{% from "components/textbox.html" import textbox %}
{% extends "withnav_template.html" %}
{% block service_page_title %}
Choose a template
{% endblock %}
{% block maincolumn_content %}
<h1 class="heading-large">Choose a template</h1>
{% if not templates %}
{% if current_user.has_permissions(permissions=['manage_templates'], any_=True) %}
<p class="bottom-gutter">
You need a template before you can send text messages.
</p>
<a href="{{ url_for('.add_template_by_type', service_id=current_service.id) }}" class="button">Add a new template</a>
{% else %}
<p>
You need to ask your service manager to add templates before you
can send text messages.
</p>
{% endif %}
{% else %}
{% include "views/templates/_search-box.html" %}
<nav class="grid-row" id=template-list>
{% for template in templates %}
<div class="column-whole">
<h2 class="message-name">
<a href="{{ url_for('.conversation_reply_with_template', service_id=current_service.id, template_id=template.id, notification_id=notification_id) }}">{{ template.name }}</a>
</h2>
<p class="message-type">
{{ message_count_label(1, template.template_type, suffix='')|capitalize }} template
</p>
</div>
{% endfor %}
</nav>
{% endif %}
{% endblock %}

View File

@@ -54,16 +54,7 @@
</div>
{% endif %}
{% if show_search_box %}
<div data-module="autofocus">
<div class="live-search" data-module="live-search" data-targets="#template-list .column-whole">
{{ textbox(
search_form.search,
width='1-1'
) }}
</div>
</div>
{% endif %}
{% include "views/templates/_search-box.html" %}
<nav class="grid-row" id=template-list>
{% for template in templates %}

View File

@@ -214,3 +214,75 @@ def test_view_conversation_with_empty_inbound(
messages = page.select('.sms-message-wrapper')
assert len(messages) == 1
def test_conversation_links_to_reply(
client_request,
fake_uuid,
mock_get_notification,
mock_get_notifications,
mock_get_inbound_sms,
):
page = client_request.get(
'main.conversation',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
)
assert page.select('main p')[-1].select_one('a')['href'] == (
url_for(
'.conversation_reply',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
)
)
def test_conversation_reply_shows_templates(
client_request,
fake_uuid,
mock_get_service_templates,
):
page = client_request.get(
'main.conversation_reply',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
)
for index, expected in enumerate([
'sms_template_one',
'sms_template_two',
]):
link = page.select('.message-name')[index]
assert normalize_spaces(link.text) == expected
assert link.select_one('a')['href'].startswith(
url_for(
'main.conversation_reply_with_template',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
template_id='',
)
)
def test_conversation_reply_redirects_with_phone_number_from_notification(
client_request,
fake_uuid,
mock_get_notification,
mock_get_service_template,
):
page = client_request.get(
'main.conversation_reply_with_template',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
template_id=fake_uuid,
_follow_redirects=True,
)
for element, expected_text in [
('h1', 'Preview of Two week reminder'),
('.sms-message-recipient', 'To: 07123 456789'),
('.sms-message-wrapper', 'service one: Template <em>content</em> with & entity'),
]:
assert normalize_spaces(page.select_one(element).text) == expected_text

View File

@@ -161,3 +161,45 @@ def test_should_show_image_of_letter_notification(
assert response.get_data(as_text=True) == 'foo'
assert isinstance(mocked_preview.call_args[0][0], LetterImageTemplate)
assert mocked_preview.call_args[0][1] == 'png'
@pytest.mark.parametrize('service_permissions, template_type, link_expected', [
([], '', False),
(['inbound_sms'], 'email', False),
(['inbound_sms'], 'letter', False),
(['inbound_sms'], 'sms', True),
])
def test_notification_page_has_link_to_send_another_for_sms(
client_request,
mocker,
fake_uuid,
service_one,
service_permissions,
template_type,
link_expected,
):
service_one['permissions'] = service_permissions
mock_get_notification(mocker, fake_uuid, template_type=template_type)
page = client_request.get(
'main.view_notification',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
)
last_paragraph = page.select('main p')[-1]
if link_expected:
assert normalize_spaces(last_paragraph.text) == (
'See all text messages sent to this phone number'
)
assert last_paragraph.select_one('a')['href'] == url_for(
'.conversation',
service_id=SERVICE_ONE_ID,
notification_id=fake_uuid,
_anchor='n{}'.format(fake_uuid),
)
else:
# covers Delivered, Expected delivery date
assert 'deliver' in normalize_spaces(last_paragraph.text).lower()