Show upcoming jobs on the dashboard

On the dashboard:
- adds a new ‘in the next 24 hours’ section to the dashboard which lists
  upcoming jobs
- tweaks some spacing on the dashboard so that it doesn’t look like too
  much of a mess
- don’t show scheduled jobs in the table of normal jobs

On the jobs page:
- don’t show scheduled jobs
This commit is contained in:
Chris Hill-Scott
2016-08-09 10:39:57 +01:00
parent 3d8d160d3e
commit 4342b721f1
14 changed files with 165 additions and 40 deletions

View File

@@ -40,7 +40,7 @@
@extend %big-number; @extend %big-number;
position: relative; position: relative;
margin-bottom: $gutter-half; margin-bottom: $gutter-two-thirds;
.big-number { .big-number {
padding: $gutter-half; padding: $gutter-half;

View File

@@ -1,3 +1,4 @@
%show-more,
.show-more { .show-more {
@include core-16; @include core-16;
@@ -8,7 +9,7 @@
border-top: 1px solid $border-colour; border-top: 1px solid $border-colour;
&:focus { &:focus {
outline: none; outline: none;
color: $text-colour; color: $text-colour;
box-shadow: 0 -10px 0 0 $yellow; box-shadow: 0 -10px 0 0 $yellow;
@@ -32,3 +33,8 @@
} }
} }
.show-more-empty {
@extend %show-more;
margin-top: -10px;
}

View File

@@ -10,11 +10,20 @@
.dashboard-table { .dashboard-table {
.heading-medium {
margin-bottom: 5px;
}
.table { .table {
table-layout: fixed; table-layout: fixed;
} }
.table-field-headings, .table-field-headings {
th {
font-size: 0;
}
}
.table-field-headings-visible { .table-field-headings-visible {
th { th {
padding-bottom: 5px; padding-bottom: 5px;

View File

@@ -116,9 +116,21 @@ def get_dashboard_partials(service_id):
lambda job: job['original_file_name'] != current_app.config['TEST_MESSAGE_FILENAME'], lambda job: job['original_file_name'] != current_app.config['TEST_MESSAGE_FILENAME'],
job_api_client.get_job(service_id, limit_days=7)['data'] job_api_client.get_job(service_id, limit_days=7)['data']
)) ))
scheduled_jobs = filter(
lambda job: job['job_status'] == 'scheduled',
jobs
)
immediate_jobs = filter(
lambda job: job['job_status'] != 'scheduled',
jobs
)
service = service_api_client.get_detailed_service(service_id) service = service_api_client.get_detailed_service(service_id)
return { return {
'upcoming': render_template(
'views/dashboard/_upcoming.html',
scheduled_jobs=scheduled_jobs
),
'totals': render_template( 'totals': render_template(
'views/dashboard/_totals.html', 'views/dashboard/_totals.html',
service_id=service_id, service_id=service_id,
@@ -134,7 +146,7 @@ def get_dashboard_partials(service_id):
'has_template_statistics': bool(template_statistics), 'has_template_statistics': bool(template_statistics),
'jobs': render_template( 'jobs': render_template(
'views/dashboard/_jobs.html', 'views/dashboard/_jobs.html',
jobs=jobs jobs=immediate_jobs
), ),
'has_jobs': bool(jobs), 'has_jobs': bool(jobs),
'usage': render_template( 'usage': render_template(

View File

@@ -65,7 +65,10 @@ def _set_status_filters(filter_args):
def view_jobs(service_id): def view_jobs(service_id):
return render_template( return render_template(
'views/jobs/jobs.html', 'views/jobs/jobs.html',
jobs=add_rate_to_jobs(job_api_client.get_job(service_id)['data']) jobs=filter(
lambda job: job['job_status'] != 'scheduled',
add_rate_to_jobs(job_api_client.get_job(service_id)['data'])
)
) )
@@ -274,6 +277,11 @@ def get_status_filters(service, message_type, statistics):
def _get_job_counts(job, help_argument): def _get_job_counts(job, help_argument):
sending = 0 if job['job_status'] == 'pending' else (
job.get('notification_count', 0) -
job.get('notifications_delivered', 0) -
job.get('notifications_failed', 0)
)
return [ return [
( (
label, label,
@@ -293,9 +301,7 @@ def _get_job_counts(job, help_argument):
], ],
[ [
'sending', 'sending', 'sending', 'sending',
job.get('notification_count', 0) - sending
job.get('notifications_delivered', 0) -
job.get('notifications_failed', 0)
], ],
[ [
'delivered', 'delivered', 'delivered', 'delivered',

View File

@@ -1,3 +1,7 @@
{% macro show_more(url, label) %} {% macro show_more(url=None, label=None) %}
<a href="{{ url }}" class="show-more"><span>{{ label }}</span></a> {% if url and label %}
<a href="{{ url }}" class="show-more"><span>{{ label }}</span></a>
{% else %}
<span class="show-more-empty"></span>
{% endif %}
{% endmacro %} {% endmacro %}

View File

@@ -10,7 +10,7 @@
Report is {{ "{:.0f}%".format(percentage_complete) }} complete… Report is {{ "{:.0f}%".format(percentage_complete) }} complete…
</p> </p>
{% elif notifications %} {% elif notifications %}
<p class="bottom-gutter-1-2"> <p class="bottom-gutter">
<a href="{{ download_link }}" download="download" class="heading-small">Download this report</a> <a href="{{ download_link }}" download="download" class="heading-small">Download this report</a>
&emsp; &emsp;
<span id="time-left">{{ time_left }}</span> <span id="time-left">{{ time_left }}</span>

View File

@@ -18,7 +18,11 @@
{% call row_heading() %} {% call row_heading() %}
<div class="file-list"> <div class="file-list">
<a class="file-list-filename" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a> <a class="file-list-filename" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a>
<span class="file-list-hint">Uploaded {{ item.created_at|format_datetime_short }}</span> <span class="file-list-hint">
Sent {{
item.scheduled_for|format_datetime_short if item.scheduled_for else item.created_at|format_datetime_short
}}
</span>
</div> </div>
{% endcall %} {% endcall %}
{% call field() %} {% call field() %}

View File

@@ -0,0 +1,38 @@
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading %}
{% from "components/big-number.html" import big_number %}
{% from "components/show-more.html" import show_more %}
{% if scheduled_jobs %}
<div class='dashboard-table'>
<h2 class="heading-medium">
In the next 24 hours
</h2>
{% call(item, row_number) list_table(
scheduled_jobs,
caption="In the next 24 hours",
caption_visible=False,
empty_message='Nothing to see here',
field_headings=[
'File',
'Messages to be sent'
],
field_headings_visible=True
) %}
{% call row_heading() %}
<div class="file-list">
<a class="file-list-filename" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a>
<span class="file-list-hint">
Sending at {{ item.scheduled_for|format_time }}
</span>
</div>
{% endcall %}
{% call field() %}
{{ big_number(
item.notification_count,
smallest=True
) }}
{% endcall %}
{% endcall %}
{{ show_more() }}
</div>
{% endif %}

View File

@@ -23,6 +23,8 @@
{% include 'views/dashboard/no-permissions-banner.html' %} {% include 'views/dashboard/no-permissions-banner.html' %}
{% endif %} {% endif %}
{{ ajax_block(partials, updates_url, 'upcoming') }}
<h2 class="heading-medium"> <h2 class="heading-medium">
In the last 7 days In the last 7 days
</h2> </h2>

View File

@@ -34,7 +34,7 @@
</div> </div>
{% if notifications %} {% if notifications %}
<p class="bottom-gutter-1-2"> <p class="bottom-gutter">
<a href="{{ download_link }}" download="download" class="heading-small">Download this report</a> <a href="{{ download_link }}" download="download" class="heading-small">Download this report</a>
&emsp; &emsp;
Data available for 7 days Data available for 7 days

View File

@@ -158,7 +158,8 @@ def job_json(
notification_count=1, notification_count=1,
notifications_sent=1, notifications_sent=1,
notifications_requested=1, notifications_requested=1,
job_status='Delivered' job_status='Delivered',
scheduled_for=''
): ):
if job_id is None: if job_id is None:
job_id = str(generate_uuid()) job_id = str(generate_uuid())
@@ -180,8 +181,10 @@ def job_json(
'created_by': created_by_json( 'created_by': created_by_json(
created_by.id, created_by.id,
created_by.name, created_by.name,
created_by.email_address) created_by.email_address
} ),
'scheduled_for': scheduled_for
}
return data return data

View File

@@ -107,7 +107,7 @@ def test_should_show_recent_templates_on_dashboard(app_,
assert 'Test Service' in headers assert 'Test Service' in headers
assert 'In the last 7 days' in headers assert 'In the last 7 days' in headers
table_rows = page.find_all('tbody')[0].find_all('tr') table_rows = page.find_all('tbody')[1].find_all('tr')
assert len(table_rows) == 2 assert len(table_rows) == 2
@@ -159,20 +159,20 @@ def test_should_show_all_templates_on_template_statistics_page(
@freeze_time("2016-01-01 11:09:00.061258") @freeze_time("2016-01-01 11:09:00.061258")
def test_should_show_recent_jobs_on_dashboard( def test_should_show_upcoming_jobs_on_dashboard(
app_, app_,
mocker, mocker,
api_user_active, api_user_active,
mock_get_service, mock_get_service,
mock_get_service_templates, mock_get_service_templates,
mock_get_user, mock_get_user,
mock_get_user_by_email, mock_get_user_by_email,
mock_login, mock_login,
mock_get_template_statistics, mock_get_template_statistics,
mock_get_detailed_service, mock_get_detailed_service,
mock_get_jobs, mock_get_jobs,
mock_has_permissions, mock_has_permissions,
mock_get_usage mock_get_usage
): ):
with app_.test_request_context(), app_.test_client() as client: with app_.test_request_context(), app_.test_client() as client:
client.login(api_user_active) client.login(api_user_active)
@@ -182,7 +182,40 @@ def test_should_show_recent_jobs_on_dashboard(
assert response.status_code == 200 assert response.status_code == 200
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
table_rows = page.find_all('tbody')[1].find_all('tr')
table_rows = page.find_all('tbody')[0].find_all('tr')
assert len(table_rows) == 1
assert 'send_me_later.csv' in table_rows[0].find_all('th')[0].text
assert 'Sending at 11:09am' in table_rows[0].find_all('th')[0].text
assert table_rows[0].find_all('td')[0].text.strip() == '1'
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_show_recent_jobs_on_dashboard(
app_,
mocker,
api_user_active,
mock_get_service,
mock_get_service_templates,
mock_get_user,
mock_get_user_by_email,
mock_login,
mock_get_template_statistics,
mock_get_detailed_service,
mock_get_jobs,
mock_has_permissions,
mock_get_usage
):
with app_.test_request_context(), app_.test_client() as client:
client.login(api_user_active)
response = client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
mock_get_jobs.assert_called_once_with(SERVICE_ONE_ID, limit_days=7)
assert response.status_code == 200
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
table_rows = page.find_all('tbody')[2].find_all('tr')
assert "Test message" not in page.text assert "Test message" not in page.text
assert len(table_rows) == 4 assert len(table_rows) == 4
@@ -194,7 +227,7 @@ def test_should_show_recent_jobs_on_dashboard(
"thisisatest.csv", "thisisatest.csv",
)): )):
assert filename in table_rows[index].find_all('th')[0].text assert filename in table_rows[index].find_all('th')[0].text
assert 'Uploaded 1 January at 11:09' in table_rows[index].find_all('th')[0].text assert 'Sent 1 January at 11:09' in table_rows[index].find_all('th')[0].text
for column_index, count in enumerate((1, 0, 0)): for column_index, count in enumerate((1, 0, 0)):
assert table_rows[index].find_all('td')[column_index].text.strip() == str(count) assert table_rows[index].find_all('td')[column_index].text.strip() == str(count)

View File

@@ -874,13 +874,21 @@ def mock_get_job_in_progress(mocker, api_user_active):
def mock_get_jobs(mocker, api_user_active): def mock_get_jobs(mocker, api_user_active):
def _get_jobs(service_id, limit_days=None): def _get_jobs(service_id, limit_days=None):
return {"data": [ return {"data": [
job_json(service_id, api_user_active, original_file_name=filename) job_json(
for filename in ( service_id,
"Test message", api_user_active,
"export 1/1/2016.xls", original_file_name=filename,
"all email addresses.xlsx", scheduled_for=scheduled_for,
"applicants.ods", job_status=job_status
"thisisatest.csv", )
for filename, scheduled_for, job_status in (
("Test message", '', ''),
("Test message", '2016-01-01 11:09:00.061258', 'scheduled'),
("export 1/1/2016.xls", '', ''),
("all email addresses.xlsx", '', 'pending'),
("applicants.ods", '', ''),
("thisisatest.csv", '', ''),
("send_me_later.csv", '2016-01-01 11:09:00.061258', 'scheduled')
) )
]} ]}