diff --git a/app/assets/stylesheets/components/table.scss b/app/assets/stylesheets/components/table.scss index 3b4a4a49b..d69b25af5 100644 --- a/app/assets/stylesheets/components/table.scss +++ b/app/assets/stylesheets/components/table.scss @@ -49,6 +49,10 @@ } } + .big-number-label { + padding-bottom: 0; + } + } .template-statistics-table { diff --git a/app/assets/stylesheets/components/vendor/previous-next-navigation.scss b/app/assets/stylesheets/components/vendor/previous-next-navigation.scss index 8fc2213a0..5db0b9be8 100644 --- a/app/assets/stylesheets/components/vendor/previous-next-navigation.scss +++ b/app/assets/stylesheets/components/vendor/previous-next-navigation.scss @@ -34,6 +34,7 @@ $is-ie: false !default; margin-bottom: $gutter; margin-left: -$gutter-half; margin-right: -$gutter-half; + overflow: hidden; ul { margin: 0; diff --git a/app/assets/stylesheets/views/dashboard.scss b/app/assets/stylesheets/views/dashboard.scss index b300d5be2..8faef80c7 100644 --- a/app/assets/stylesheets/views/dashboard.scss +++ b/app/assets/stylesheets/views/dashboard.scss @@ -56,6 +56,18 @@ margin-top: -10px; } + &-filename-large { + @include bold-24; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-bottom: 30px; + padding-top: 10px; + margin-bottom: -30px; + margin-top: -10px; + } + &-filename-unlinked { @include core-19; } diff --git a/app/main/views/uploads.py b/app/main/views/uploads.py index 65edfcfc2..a6b32f28d 100644 --- a/app/main/views/uploads.py +++ b/app/main/views/uploads.py @@ -46,20 +46,22 @@ def uploads(service_id): uploads = current_service.get_page_of_uploads(page=request.args.get('page')) prev_page = None - if uploads.next_page: + if uploads.prev_page: prev_page = generate_previous_dict('main.uploads', service_id, uploads.current_page) next_page = None - if uploads.prev_page: + if uploads.next_page: next_page = generate_next_dict('main.uploads', service_id, uploads.current_page) + if uploads.current_page == 1: + listed_uploads = current_service.scheduled_jobs + uploads + else: + listed_uploads = uploads + return render_template( 'views/jobs/jobs.html', - jobs=uploads, + jobs=listed_uploads, prev_page=prev_page, next_page=next_page, - show_scheduled_jobs=( - uploads.current_page == 1 and current_service.scheduled_jobs - ), ) diff --git a/app/models/__init__.py b/app/models/__init__.py index 4f8850e9d..3a01e8a81 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -75,6 +75,9 @@ class ModelList(ABC, Sequence): def __add__(self, other): return list(self) + list(other) + def __radd__(self, other): + return list(other) + list(self) + class InviteTokenError(Exception): pass diff --git a/app/models/job.py b/app/models/job.py index 5cd92c3a0..02d40159e 100644 --- a/app/models/job.py +++ b/app/models/job.py @@ -30,6 +30,8 @@ class Job(JSONModel): 'processing_started', 'notification_count', 'created_by', + 'template_type', + 'recipient', } @classmethod @@ -56,6 +58,10 @@ class Job(JSONModel): def upload_type(self): return self._dict.get('upload_type') + @property + def pdf_letter(self): + return self.upload_type == 'letter' + @property def processing_started(self): if not self._dict.get('processing_started'): @@ -132,10 +138,6 @@ class Job(JSONModel): version=self.template_version, )['data'] - @property - def template_type(self): - return self.template['template_type'] - @property def percentage_complete(self): return self.notifications_requested / self.notification_count * 100 diff --git a/app/templates/partials/jobs/notifications.html b/app/templates/partials/jobs/notifications.html index 842a9d805..86c52fb57 100644 --- a/app/templates/partials/jobs/notifications.html +++ b/app/templates/partials/jobs/notifications.html @@ -25,21 +25,21 @@
+
Report is {{ "{:.0f}%".format(job.percentage_complete * 0.99) }} complete…
{% elif notifications %} -+
Download this report {{ time_left }}
{% endif %} - {% if template.template_type == 'letter' %} + {% if job.template_type == 'letter' %}
+ {% for line in item.recipient.splitlines() %}
+ {% if loop.index < 3 %}
+ {{ line }}
+ {% endif %}
+ {% endfor %}
+
+ You have not uploaded any files yet. +
+ {% if current_service.has_permission('upload_letters') %} ++ Upload a letter and Notify will print, pack and post it for you. +
+ {% endif %} ++ To upload a list of contact details, first choose a template. +
{% endif %} {{ previous_next_navigation(prev_page, next_page) }} {% if current_service.can_upload_letters and current_user.has_permissions('send_messages') %} diff --git a/tests/__init__.py b/tests/__init__.py index 70f04ed12..eaaf86886 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -354,6 +354,7 @@ def job_json( job_id=None, template_id=None, template_version=1, + template_type='sms', created_at=None, bucket_name='', original_file_name="thisisatest.csv", @@ -375,6 +376,7 @@ def job_json( 'service': service_id, 'template': template_id, 'template_version': template_version, + 'template_type': template_type, 'original_file_name': original_file_name, 'created_at': created_at, 'notification_count': notification_count, diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 9e47e946e..b9fb1b01c 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -937,8 +937,11 @@ def test_should_show_recent_jobs_on_dashboard( )): assert filename in table_rows[index].find_all('th')[0].text assert 'Sent today at 11:09' in table_rows[index].find_all('th')[0].text - for column_index, count in enumerate((1, 0, 0)): - assert table_rows[index].find_all('td')[column_index].text.strip() == str(count) + assert normalize_spaces( + table_rows[index].select_one('td').text + ) == ( + '1 sending 0 delivered 0 failed' + ) @pytest.mark.parametrize('extra_permissions', ( diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py index 2a9e7b69e..9dfbd5b9a 100644 --- a/tests/app/main/views/test_jobs.py +++ b/tests/app/main/views/test_jobs.py @@ -21,23 +21,23 @@ from tests.conftest import ( @pytest.mark.parametrize('user, expected_rows', [ (create_active_user_with_permissions(), ( ( - 'File Sending Delivered Failed' + 'File Status' ), ( 'export 1/1/2016.xls ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'all email addresses.xlsx ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'applicants.ods ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'thisisatest.csv ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), )), (create_active_caseworking_user(), ( @@ -53,23 +53,23 @@ from tests.conftest import ( 'Sending 1 January 2016 at 11:09pm 1' ), ( - 'File Sending Delivered Failed' + 'File Status' ), ( 'export 1/1/2016.xls ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'all email addresses.xlsx ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'applicants.ods ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'thisisatest.csv ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), )), ]) @@ -123,23 +123,23 @@ def test_jobs_page_doesnt_show_scheduled_on_page_2( for index, row in enumerate(( ( - 'File Sending Delivered Failed' + 'File Status' ), ( 'export 1/1/2016.xls ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'all email addresses.xlsx ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'applicants.ods ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), ( 'thisisatest.csv ' - 'Sent today at 12:12pm 1 0 0' + 'Sent today at 12:12pm 1 sending 0 delivered 0 failed' ), )): assert normalize_spaces(page.select('tr')[index].text) == row @@ -398,7 +398,7 @@ def test_should_show_old_job( def test_should_show_letter_job( client_request, mock_get_service_letter_template, - mock_get_job, + mock_get_letter_job, mock_get_service_data_retention, fake_uuid, mocker, @@ -460,7 +460,7 @@ def test_should_show_letter_job( def test_should_show_letter_job_with_banner_after_sending_before_1730( client_request, mock_get_service_letter_template, - mock_get_job, + mock_get_letter_job, mock_get_notifications, mock_get_service_data_retention, fake_uuid, @@ -483,7 +483,7 @@ def test_should_show_letter_job_with_banner_after_sending_before_1730( def test_should_show_letter_job_with_banner_when_there_are_multiple_CSV_rows( client_request, mock_get_service_letter_template, - mock_get_job_in_progress, + mock_get_letter_job_in_progress, mock_get_notifications, mock_get_service_data_retention, fake_uuid, @@ -506,7 +506,7 @@ def test_should_show_letter_job_with_banner_when_there_are_multiple_CSV_rows( def test_should_show_letter_job_with_banner_after_sending_after_1730( client_request, mock_get_service_letter_template, - mock_get_job, + mock_get_letter_job, mock_get_notifications, mock_get_service_data_retention, fake_uuid, @@ -601,7 +601,8 @@ def test_should_cancel_letter_job( active_user_with_permissions, job_id=job_id, created_at="2019-06-20T15:30:00.000001+00:00", - job_status="finished" + job_status="finished", + template_type="letter", ) mocker.patch('app.job_api_client.get_job', side_effect=[{"data": job}]) notifications_json = notification_json(SERVICE_ONE_ID, job=job, status="created", template_type="letter") @@ -819,7 +820,7 @@ def test_time_left(job_created_at, expected_message): def test_should_show_letter_job_with_first_class_if_notifications_are_first_class( client_request, mock_get_service_letter_template, - mock_get_job, + mock_get_letter_job, mock_get_service_data_retention, fake_uuid, mocker, @@ -840,7 +841,7 @@ def test_should_show_letter_job_with_first_class_if_notifications_are_first_clas def test_should_show_letter_job_with_first_class_if_no_notifications( client_request, service_one, - mock_get_job, + mock_get_letter_job, fake_uuid, mock_get_notifications_with_no_notifications, mock_get_service_data_retention, diff --git a/tests/app/main/views/test_uploads.py b/tests/app/main/views/test_uploads.py index cbb7a5396..943e5b205 100644 --- a/tests/app/main/views/test_uploads.py +++ b/tests/app/main/views/test_uploads.py @@ -39,10 +39,13 @@ def test_no_upload_letters_button_without_permission( @pytest.mark.parametrize('extra_permissions, expected_empty_message', ( (['letter'], ( - 'You have not uploaded any files yet' + 'You have not uploaded any files yet. ' + 'To upload a list of contact details, first choose a template.' )), (['letter', 'upload_letters'], ( - 'Upload a letter and Notify will print, pack and post it for you.' + 'You have not uploaded any files yet. ' + 'Upload a letter and Notify will print, pack and post it for you. ' + 'To upload a list of contact details, first choose a template.' )), )) def test_get_upload_hub_with_no_uploads( @@ -56,9 +59,9 @@ def test_get_upload_hub_with_no_uploads( mocker.patch('app.job_api_client.get_jobs', return_value={'data': []}) service_one['permissions'] += extra_permissions page = client_request.get('main.uploads', service_id=SERVICE_ONE_ID) - assert normalize_spaces( - page.select_one('.table-empty-message').text - ) == expected_empty_message + assert normalize_spaces(' '.join( + paragraph.text for paragraph in page.select('main p') + )) == expected_empty_message assert not page.select('.file-list-filename') @@ -76,13 +79,32 @@ def test_get_upload_hub_page( 'main.upload_letter', service_id=SERVICE_ONE_ID ) - assert page.find_all( - 'a', {'class': 'file-list-filename'} - )[0].attrs['href'] == '/services/{}/jobs/job_id_1'.format(SERVICE_ONE_ID) + uploads = page.select('tbody tr') - assert page.find_all( - 'a', {'class': 'file-list-filename'} - )[1].attrs['href'] == '/services/{}/notification/letter_id_1'.format(SERVICE_ONE_ID) + assert normalize_spaces(uploads[0].text.strip()) == ( + 'some.csv ' + 'Sent 1 January 2016 at 11:09am ' + '0 sending 8 delivered 2 failed' + ) + assert uploads[0].select_one('a.file-list-filename-large')['href'] == ( + '/services/{}/jobs/job_id_1'.format(SERVICE_ONE_ID) + ) + + assert normalize_spaces(uploads[1].text.strip()) == ( + 'some.pdf ' + 'Sent 1 January 2016 at 11:09am ' + 'Firstname Lastname ' + '123 Example Street' + ) + assert normalize_spaces(str(uploads[1].select_one('.govuk-body'))) == ( + ' '
+ 'Firstname Lastname
'
+ '123 Example Street
'
+ '