mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-20 14:29:51 -04:00
Show jobs on contact list
It’s a bit unintuitive that starting a job from a contact list makes a copy of the file, which has no relationship to the list it was copied from. This is more of an implementation detail, rather than something that comes from people’s mental models of what is going on. Or at least that’s what I hypothesise. I think it’s clearer to show jobs that come from contact lists within the lists that they were created from. By naming the jobs by template this gives a clearer view of what messages have been sent to the group over time.
This commit is contained in:
@@ -513,9 +513,11 @@ def save_contact_list(service_id, upload_id):
|
||||
@main.route("/services/<uuid:service_id>/contact-list/<uuid:contact_list_id>", methods=['GET'])
|
||||
@user_has_permissions('send_messages')
|
||||
def contact_list(service_id, contact_list_id):
|
||||
contact_list = ContactList.from_id(contact_list_id, service_id=service_id)
|
||||
return render_template(
|
||||
'views/uploads/contact-list/contact-list.html',
|
||||
contact_list=ContactList.from_id(contact_list_id, service_id=service_id),
|
||||
contact_list=contact_list,
|
||||
jobs=contact_list.get_jobs(page=1),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from notifications_utils.timezones import utc_string_to_aware_gmt_datetime
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from app.models import JSONModel, ModelList
|
||||
from app.models.job import PaginatedJobs
|
||||
from app.models.job import PaginatedJobsAndScheduledJobs
|
||||
from app.notify_client.contact_list_api_client import contact_list_api_client
|
||||
from app.s3_client.s3_csv_client import (
|
||||
get_csv_metadata,
|
||||
@@ -136,7 +136,7 @@ class ContactList(JSONModel):
|
||||
return f'{file_name}.csv'
|
||||
|
||||
def get_jobs(self, *, page):
|
||||
return PaginatedJobs(
|
||||
return PaginatedJobsAndScheduledJobs(
|
||||
self.service_id,
|
||||
contact_list_id=self.id,
|
||||
page=page,
|
||||
|
||||
@@ -233,9 +233,19 @@ class ScheduledJobs(ImmediateJobs):
|
||||
|
||||
class PaginatedJobs(PaginatedModelList, ImmediateJobs):
|
||||
client_method = job_api_client.get_page_of_jobs
|
||||
statuses = None
|
||||
|
||||
def __init__(self, service_id, *, contact_list_id=None, page=None):
|
||||
super().__init__(service_id, contact_list_id=contact_list_id, page=page)
|
||||
super().__init__(
|
||||
service_id,
|
||||
contact_list_id=contact_list_id,
|
||||
statuses=self.statuses,
|
||||
page=page,
|
||||
)
|
||||
|
||||
|
||||
class PaginatedJobsAndScheduledJobs(PaginatedJobs):
|
||||
statuses = job_api_client.NON_CANCELLED_JOB_STATUSES
|
||||
|
||||
|
||||
class PaginatedUploads(PaginatedModelList, ImmediateJobs):
|
||||
|
||||
@@ -16,6 +16,7 @@ class JobApiClient(NotifyAdminAPIClient):
|
||||
}
|
||||
SCHEDULED_JOB_STATUS = 'scheduled'
|
||||
CANCELLED_JOB_STATUS = 'cancelled'
|
||||
NON_CANCELLED_JOB_STATUSES = JOB_STATUSES - {CANCELLED_JOB_STATUS}
|
||||
NON_SCHEDULED_JOB_STATUSES = JOB_STATUSES - {SCHEDULED_JOB_STATUS, CANCELLED_JOB_STATUS}
|
||||
|
||||
def get_job(self, service_id, job_id):
|
||||
@@ -52,10 +53,10 @@ class JobApiClient(NotifyAdminAPIClient):
|
||||
if job['job_status'] != 'cancelled'
|
||||
)
|
||||
|
||||
def get_page_of_jobs(self, service_id, *, page, contact_list_id=None):
|
||||
def get_page_of_jobs(self, service_id, *, page, statuses=None, contact_list_id=None):
|
||||
return self.get_jobs(
|
||||
service_id,
|
||||
statuses=self.NON_SCHEDULED_JOB_STATUSES,
|
||||
statuses=statuses or self.NON_SCHEDULED_JOB_STATUSES,
|
||||
page=page,
|
||||
contact_list_id=contact_list_id,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/big-number.html" import big_number %}
|
||||
{% from "components/radios.html" import radio_select %}
|
||||
{% from "components/table.html" import list_table, field, text_field, index_field, hidden_field_heading %}
|
||||
{% from "components/table.html" import list_table, field, text_field, index_field, hidden_field_heading, row, row_heading %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/message-count-label.html" import message_count_label, recipient_count_label %}
|
||||
{% from "components/message-count-label.html" import message_count_label, recipient_count, recipient_count_label, iteration_count %}
|
||||
{% from "components/button/macro.njk" import govukButton %}
|
||||
|
||||
{% block service_page_title %}
|
||||
@@ -18,41 +19,124 @@
|
||||
) }}
|
||||
|
||||
<p class="govuk-body">
|
||||
Uploaded by {{ contact_list.created_by }} {{ contact_list.created_at|format_datetime_human }}
|
||||
Uploaded by {{ contact_list.created_by }} {{ contact_list.created_at|format_datetime_human }}.
|
||||
</p>
|
||||
|
||||
<p class="govuk-body">
|
||||
<a class="govuk-link govuk-link--no-visited-state heading-small" download href="{{ url_for('main.download_contact_list', service_id=current_service.id, contact_list_id=contact_list.id) }}">Download this list</a> 
|
||||
{{ contact_list.recipients|length|format_thousands }}
|
||||
{{ recipient_count_label(contact_list.recipients|length, contact_list.recipients.template_type) }}
|
||||
</p>
|
||||
{% if jobs %}
|
||||
<p class="govuk-body">
|
||||
Used {{ iteration_count(jobs|length) }}
|
||||
in the last {{ current_service.get_days_of_retention(contact_list.template_type) }}
|
||||
days.
|
||||
</p>
|
||||
<div class='dashboard-table ajax-block-container'>
|
||||
{% call(item, row_number) list_table(
|
||||
jobs,
|
||||
caption="Messages sent from this contact list",
|
||||
caption_visible=False,
|
||||
empty_message='',
|
||||
field_headings=[
|
||||
'Template',
|
||||
'Status'
|
||||
],
|
||||
field_headings_visible=False
|
||||
) %}
|
||||
{% call row_heading() %}
|
||||
<div class="file-list">
|
||||
<a class="file-list-filename-large govuk-link govuk-link--no-visited-state" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.template_name }}</a>
|
||||
{% if item.scheduled %}
|
||||
<span class="file-list-hint-large">
|
||||
Sending {{
|
||||
item.scheduled_for|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="file-list-hint-large">
|
||||
Sent {{
|
||||
(item.scheduled_for or item.created_at)|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% set recipient_column = contact_list.recipients.column_headers[0] %}
|
||||
|
||||
{% call(item, row_number) list_table(
|
||||
contact_list.recipients.displayed_rows,
|
||||
caption=recipient_count_label(contact_list.recipients|length, contact_list.template_type)|capitalize,
|
||||
caption_visible=False,
|
||||
field_headings=['1', recipient_column],
|
||||
) %}
|
||||
{{ index_field(row_number) }}
|
||||
{{ text_field(item[recipient_column].data) }}
|
||||
{% endcall %}
|
||||
|
||||
{% if contact_list.recipients.displayed_rows|list|length < contact_list.recipients|length %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the first {{ contact_list.recipients.displayed_rows|list|length }} rows
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{% if item.scheduled %}
|
||||
{{ big_number(
|
||||
item.notification_count,
|
||||
smallest=True,
|
||||
label=message_count_label(
|
||||
item.notification_count,
|
||||
item.template_type,
|
||||
suffix='waiting to send'
|
||||
)
|
||||
) }}
|
||||
{% else %}
|
||||
<div class="govuk-grid-row">
|
||||
<div class="govuk-grid-column-one-third">
|
||||
{{ big_number(
|
||||
item.notifications_sending,
|
||||
smallest=True,
|
||||
label='sending',
|
||||
) }}
|
||||
</div>
|
||||
<div class="govuk-grid-column-one-third">
|
||||
{{ big_number(item.notifications_delivered, smallest=True, label='delivered') }}
|
||||
</div>
|
||||
<div class="govuk-grid-column-one-third">
|
||||
{{ big_number(item.notifications_failed, smallest=True, label='failed') }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="govuk-body">
|
||||
{% if contact_list.has_jobs %}
|
||||
Not used in the last {{ current_service.get_days_of_retention(contact_list.template_type) }} days.
|
||||
{% else %}
|
||||
Not used yet.
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if not confirm_delete_banner %}
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<div class="page-footer">
|
||||
<h2 class="govuk-heading-m govuk-!-margin-bottom-2">
|
||||
{{ recipient_count(contact_list.recipients|length, contact_list.template_type, prefix='saved') }}
|
||||
</h2>
|
||||
|
||||
{% set recipient_column = contact_list.recipients.column_headers[0] %}
|
||||
|
||||
<div class="body-copy-table">
|
||||
{% call(item, row_number) list_table(
|
||||
contact_list.recipients.displayed_rows,
|
||||
caption=recipient_count_label(contact_list.recipients|length, contact_list.template_type)|capitalize,
|
||||
caption_visible=False,
|
||||
field_headings=[recipient_column],
|
||||
field_headings_visible=False,
|
||||
) %}
|
||||
{% if not loop.first %}
|
||||
{{ text_field(item[recipient_column].data) }}
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
|
||||
{% if contact_list.recipients.displayed_rows|list|length < contact_list.recipients|length %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the first {{ contact_list.recipients.displayed_rows|list|length }} rows
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<div class="page-footer">
|
||||
{% if not confirm_delete_banner %}
|
||||
<span class="page-footer-delete-link page-footer-delete-link-without-button">
|
||||
<a class="govuk-link govuk-link--destructive" href="{{ url_for('main.delete_contact_list', service_id=current_service.id, contact_list_id=contact_list.id) }}">Delete this contact list</a>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a class="govuk-link govuk-link--no-visited-state page-footer-right-aligned-link-without-button" download href="{{ url_for('main.download_contact_list', service_id=current_service.id, contact_list_id=contact_list.id) }}">Download this contact list</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user