Files
notifications-admin/app/main/views/broadcast.py

482 lines
15 KiB
Python
Raw Normal View History

from flask import (
abort,
flash,
jsonify,
redirect,
render_template,
request,
url_for,
)
from app import current_service
from app.main import main
from app.main.forms import (
BroadcastAreaForm,
BroadcastAreaFormWithSelectAll,
BroadcastTemplateForm,
NewBroadcastForm,
SearchByNameForm,
)
from app.models.broadcast_message import BroadcastMessage, BroadcastMessages
from app.utils import service_has_permission, user_has_permissions
@main.route('/services/<uuid:service_id>/broadcast-tour/<int:step_index>')
@user_has_permissions()
@service_has_permission('broadcast')
def broadcast_tour(service_id, step_index):
if step_index not in (1, 2, 3, 4, 5, 6):
abort(404)
return render_template(
f'views/broadcast/tour/{step_index}.html'
)
@main.route('/services/<uuid:service_id>/current-alerts')
@user_has_permissions()
@service_has_permission('broadcast')
def broadcast_dashboard(service_id):
return render_template(
'views/broadcast/dashboard.html',
partials=get_broadcast_dashboard_partials(current_service.id),
)
@main.route('/services/<uuid:service_id>/previous-alerts')
@user_has_permissions()
@service_has_permission('broadcast')
def broadcast_dashboard_previous(service_id):
return render_template(
'views/broadcast/previous-broadcasts.html',
broadcasts=BroadcastMessages(service_id).with_status('cancelled', 'completed'),
empty_message='You do not have any previous alerts',
2020-10-26 11:09:13 +00:00
view_broadcast_endpoint='.view_previous_broadcast',
)
@main.route('/services/<uuid:service_id>/broadcast-dashboard.json')
@user_has_permissions()
@service_has_permission('broadcast')
def broadcast_dashboard_updates(service_id):
return jsonify(get_broadcast_dashboard_partials(current_service.id))
def get_broadcast_dashboard_partials(service_id):
broadcast_messages = BroadcastMessages(service_id)
return dict(
current_broadcasts=render_template(
'views/broadcast/partials/dashboard-table.html',
broadcasts=broadcast_messages.with_status('pending-approval', 'broadcasting'),
empty_message='You do not have any current alerts',
2020-10-26 11:09:13 +00:00
view_broadcast_endpoint='.view_current_broadcast',
),
)
@main.route('/services/<uuid:service_id>/new-broadcast', methods=['GET', 'POST'])
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def new_broadcast(service_id):
form = NewBroadcastForm()
if form.validate_on_submit():
if form.use_template:
return redirect(url_for(
'.choose_template',
service_id=current_service.id,
))
return redirect(url_for(
'.write_new_broadcast',
service_id=current_service.id,
))
return render_template(
'views/broadcast/new-broadcast.html',
form=form,
)
@main.route('/services/<uuid:service_id>/write-new-broadcast', methods=['GET', 'POST'])
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def write_new_broadcast(service_id):
form = BroadcastTemplateForm()
if form.validate_on_submit():
broadcast_message = BroadcastMessage.create_from_content(
service_id=current_service.id,
content=form.template_content.data,
reference=form.name.data,
)
return redirect(url_for(
'.preview_broadcast_areas',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
return render_template(
'views/broadcast/write-new-broadcast.html',
form=form,
)
@main.route('/services/<uuid:service_id>/new-broadcast/<uuid:template_id>')
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def broadcast(service_id, template_id):
return redirect(url_for(
'.preview_broadcast_areas',
service_id=current_service.id,
broadcast_message_id=BroadcastMessage.create(
service_id=service_id,
template_id=template_id,
).id,
))
@main.route('/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/areas')
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def preview_broadcast_areas(service_id, broadcast_message_id):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
if broadcast_message.template_id:
back_link = url_for(
'.view_template',
service_id=current_service.id,
template_id=broadcast_message.template_id,
)
else:
back_link = url_for(
'.write_new_broadcast',
service_id=current_service.id,
)
return render_template(
'views/broadcast/preview-areas.html',
broadcast_message=broadcast_message,
back_link=back_link,
)
@main.route('/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/libraries')
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def choose_broadcast_library(service_id, broadcast_message_id):
return render_template(
'views/broadcast/libraries.html',
libraries=BroadcastMessage.libraries,
broadcast_message=BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
),
)
@main.route(
'/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/libraries/<library_slug>',
methods=['GET', 'POST'],
)
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def choose_broadcast_area(service_id, broadcast_message_id, library_slug):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
library = BroadcastMessage.libraries.get(library_slug)
if library.is_group:
return render_template(
'views/broadcast/areas-with-sub-areas.html',
search_form=SearchByNameForm(),
show_search_form=(len(library) > 7),
library=library,
page_title=f'Choose a {library.name_singular.lower()}',
broadcast_message=broadcast_message,
)
form = BroadcastAreaForm.from_library(library)
if form.validate_on_submit():
broadcast_message.add_areas(*form.areas.data)
return redirect(url_for(
'.preview_broadcast_areas',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
return render_template(
'views/broadcast/areas.html',
form=form,
search_form=SearchByNameForm(),
show_search_form=(len(form.areas.choices) > 7),
page_title=f'Choose {library.name.lower()}',
2020-07-10 09:25:17 +01:00
broadcast_message=broadcast_message,
)
def _get_broadcast_sub_area_back_link(service_id, broadcast_message_id, library_slug):
prev_area_slug = request.args.get('prev_area_slug')
if prev_area_slug:
return url_for(
'.choose_broadcast_sub_area',
service_id=service_id,
broadcast_message_id=broadcast_message_id,
library_slug=library_slug,
area_slug=prev_area_slug,
)
else:
return url_for(
'.choose_broadcast_area',
service_id=service_id,
broadcast_message_id=broadcast_message_id,
library_slug=library_slug,
)
@main.route(
'/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/libraries/<library_slug>/<area_slug>',
methods=['GET', 'POST'],
)
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def choose_broadcast_sub_area(service_id, broadcast_message_id, library_slug, area_slug):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
area = BroadcastMessage.libraries.get_areas(area_slug)[0]
back_link = _get_broadcast_sub_area_back_link(service_id, broadcast_message_id, library_slug)
is_county = any(sub_area.sub_areas for sub_area in area.sub_areas)
form = BroadcastAreaFormWithSelectAll.from_library(
[] if is_county else area.sub_areas,
select_all_choice=(area.id, f'All of {area.name}'),
)
if form.validate_on_submit():
broadcast_message.add_areas(*form.selected_areas)
return redirect(url_for(
'.preview_broadcast_areas',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
if is_county:
# area = county. sub_areas = districts. they have wards, so link to individual district pages
return render_template(
'views/broadcast/counties.html',
form=form,
search_form=SearchByNameForm(),
show_search_form=(len(area.sub_areas) > 7),
library_slug=library_slug,
page_title=f'Choose an area of {area.name}',
broadcast_message=broadcast_message,
county=area,
back_link=back_link,
)
return render_template(
'views/broadcast/sub-areas.html',
form=form,
search_form=SearchByNameForm(),
show_search_form=(len(form.areas.choices) > 7),
library_slug=library_slug,
page_title=f'Choose an area of {area.name}',
broadcast_message=broadcast_message,
back_link=back_link,
)
@main.route('/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/remove/<area_slug>')
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def remove_broadcast_area(service_id, broadcast_message_id, area_slug):
BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
).remove_area(
area_slug
)
return redirect(url_for(
'.preview_broadcast_areas',
service_id=current_service.id,
broadcast_message_id=broadcast_message_id,
))
@main.route(
'/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/preview',
methods=['GET', 'POST'],
)
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def preview_broadcast_message(service_id, broadcast_message_id):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
Remove choice of ‘End time’ from broadcast journey Since we added the end time picker: - we have discovered that broadcasts can’t be longer than 24h - we have observed that most users confuse picking the end time for scheduling the message, or don’t understand exactly what it means for the broadcast to ‘end’ - we’ve developed the concept of ‘training mode’, which you should be going through before sending a real broadcast We also think that, for most scenarios, you won’t necessarily know when a broadcast should end at the time of starting it because the cause of the danger is not within your control. So giving you control of the end time before the broadcast has even been approved is a confusing distraction. Having to pick a time at all also makes the whole process feel more planned and less immediate. Whereas in reality all the phones in the area will be getting the message in seconds. It’s only people coming into the area later to whom the ‘ongoing’ aspect of the broadcast applies. The best place to explain what’s happening with the phones is at the approval stage and once you’ve sent your first (training mode) broadcast. It’s easier to explain what’s happened if it’s in direct response to something you’ve just done. Later on we should add some kind of email reminder after 12 hours to make sure you still want the broadcast live, again after 18 hours, etc. We could let you schedule an end time once the broadcast is live, but don’t think there’s a strong need. Knowing enough that you want to cancel is one thing, but knowing enough to want to cancel but wanting to wait a bit… nah.
2020-08-18 16:36:40 +01:00
if request.method == 'POST':
broadcast_message.request_approval()
return redirect(url_for(
'.view_current_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
return render_template(
'views/broadcast/preview-message.html',
broadcast_message=broadcast_message,
)
@main.route(
'/services/<uuid:service_id>/current-alerts/<uuid:broadcast_message_id>',
endpoint='view_current_broadcast',
)
@main.route(
'/services/<uuid:service_id>/previous-alerts/<uuid:broadcast_message_id>',
endpoint='view_previous_broadcast',
)
@user_has_permissions()
@service_has_permission('broadcast')
def view_broadcast(service_id, broadcast_message_id):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
if broadcast_message.status == 'draft':
abort(404)
if (
broadcast_message.status in {'completed', 'cancelled', 'rejected'}
and request.endpoint != 'main.view_previous_broadcast'
):
return redirect(url_for(
'.view_previous_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
if (
broadcast_message.status in {'broadcasting', 'pending-approval'}
and request.endpoint != 'main.view_current_broadcast'
):
return redirect(url_for(
'.view_current_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
back_link_endpoint = {
'main.view_current_broadcast': '.broadcast_dashboard',
'main.view_previous_broadcast': '.broadcast_dashboard_previous',
}[request.endpoint]
return render_template(
'views/broadcast/view-message.html',
broadcast_message=broadcast_message,
back_link=url_for(
back_link_endpoint,
service_id=current_service.id,
),
)
@main.route('/services/<uuid:service_id>/current-alerts/<uuid:broadcast_message_id>', methods=['POST'])
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def approve_broadcast_message(service_id, broadcast_message_id):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
if broadcast_message.status != 'pending-approval':
return redirect(url_for(
'.view_current_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
broadcast_message.approve_broadcast()
if current_service.trial_mode:
return redirect(url_for(
'.broadcast_tour',
service_id=current_service.id,
step_index=6,
))
return redirect(url_for(
'.view_current_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
@main.route('/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/reject')
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def reject_broadcast_message(service_id, broadcast_message_id):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
if broadcast_message.status != 'pending-approval':
return redirect(url_for(
'.view_current_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
broadcast_message.reject_broadcast()
return redirect(url_for(
'.broadcast_dashboard',
service_id=current_service.id,
))
@main.route(
'/services/<uuid:service_id>/broadcast/<uuid:broadcast_message_id>/cancel',
methods=['GET', 'POST'],
)
@user_has_permissions('send_messages')
@service_has_permission('broadcast')
def cancel_broadcast_message(service_id, broadcast_message_id):
broadcast_message = BroadcastMessage.from_id(
broadcast_message_id,
service_id=current_service.id,
)
if broadcast_message.status != 'broadcasting':
return redirect(url_for(
'.view_current_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
if request.method == 'POST':
broadcast_message.cancel_broadcast()
return redirect(url_for(
'.view_previous_broadcast',
service_id=current_service.id,
broadcast_message_id=broadcast_message.id,
))
flash([
'Are you sure you want to stop this broadcast now?'
], 'stop broadcasting')
return render_template(
'views/broadcast/view-message.html',
broadcast_message=broadcast_message,
hide_stop_link=True,
)