mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-25 08:44:23 -04:00
List and individual job pages now fetch data from api.
Few bug fixes around job uuid.
This commit is contained in:
@@ -1,17 +1,13 @@
|
|||||||
import uuid
|
|
||||||
|
|
||||||
from boto3 import resource
|
from boto3 import resource
|
||||||
|
|
||||||
|
|
||||||
def s3upload(service_id, filedata):
|
def s3upload(upload_id, service_id, filedata):
|
||||||
upload_id = str(uuid.uuid4())
|
|
||||||
s3 = resource('s3')
|
s3 = resource('s3')
|
||||||
bucket_name = 'service-{}-{}-notify'.format(service_id, upload_id)
|
bucket_name = 'service-{}-{}-notify'.format(service_id, upload_id)
|
||||||
s3.create_bucket(Bucket=bucket_name)
|
s3.create_bucket(Bucket=bucket_name)
|
||||||
contents = '\n'.join(filedata['data'])
|
contents = '\n'.join(filedata['data'])
|
||||||
key = s3.Object(bucket_name, upload_id)
|
key = s3.Object(bucket_name, upload_id)
|
||||||
key.put(Body=contents, ServerSideEncryption='AES256')
|
key.put(Body=contents, ServerSideEncryption='AES256')
|
||||||
return upload_id
|
|
||||||
|
|
||||||
|
|
||||||
def s3download(service_id, upload_id):
|
def s3download(service_id, upload_id):
|
||||||
|
|||||||
@@ -4,84 +4,64 @@ import time
|
|||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
render_template,
|
render_template,
|
||||||
session
|
abort
|
||||||
)
|
)
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
from client.errors import HTTPError
|
||||||
|
|
||||||
|
from app import job_api_client
|
||||||
from app.main import main
|
from app.main import main
|
||||||
from ._jobs import jobs
|
|
||||||
|
|
||||||
now = time.strftime('%H:%M')
|
now = time.strftime('%H:%M')
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
'phone': '+44 7700 900 579',
|
|
||||||
'message': 'Vehicle tax: Your vehicle tax for LV75 TDG expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa
|
|
||||||
'status': 'Delivered',
|
|
||||||
'time': now,
|
|
||||||
'id': '0'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'phone': '+44 7700 900 306',
|
|
||||||
'message': 'Vehicle tax: Your vehicle tax for PL53 GBD expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa
|
|
||||||
'status': 'Delivered',
|
|
||||||
'time': now,
|
|
||||||
'id': '1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'phone': '+44 7700 900 454',
|
|
||||||
'message': 'Vehicle tax: Your vehicle tax for LV75 TDG expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa
|
|
||||||
'status': 'Delivered',
|
|
||||||
'time': now,
|
|
||||||
'id': '2'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'phone': '+44 7700 900 522',
|
|
||||||
'message': 'Vehicle tax: Your vehicle tax for RE67 PLM expires on 18 January 2016. Renew at www.gov.uk/vehicletax', # noqa
|
|
||||||
'status': 'Failed',
|
|
||||||
'time': now,
|
|
||||||
'id': '3'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<int:service_id>/jobs")
|
@main.route("/services/<int:service_id>/jobs")
|
||||||
@login_required
|
@login_required
|
||||||
def view_jobs(service_id):
|
def view_jobs(service_id):
|
||||||
return render_template(
|
try:
|
||||||
'views/jobs.html',
|
jobs = job_api_client.get_job(service_id)['data']
|
||||||
jobs=[], # use `jobs` for placeholder data
|
return render_template(
|
||||||
service_id=service_id
|
'views/jobs.html',
|
||||||
)
|
jobs=jobs,
|
||||||
|
service_id=service_id
|
||||||
|
)
|
||||||
|
except HTTPError as e:
|
||||||
|
if e.status_code == 404:
|
||||||
|
abort(404)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<int:service_id>/jobs/<job_id>")
|
@main.route("/services/<int:service_id>/jobs/<job_id>")
|
||||||
@login_required
|
@login_required
|
||||||
def view_job(service_id, job_id):
|
def view_job(service_id, job_id):
|
||||||
|
try:
|
||||||
# TODO the uploaded file name could be part of job definition
|
job = job_api_client.get_job(service_id, job_id)['data']
|
||||||
# so won't need to be passed on from last view via session
|
messages = []
|
||||||
uploaded_file_name = session.get(job_id)
|
return render_template(
|
||||||
|
'views/job.html',
|
||||||
return render_template(
|
messages=messages,
|
||||||
'views/job.html',
|
counts={
|
||||||
messages=messages,
|
'total': len(messages),
|
||||||
counts={
|
'delivered': len([
|
||||||
'total': len(messages),
|
message for message in messages if message['status'] == 'Delivered'
|
||||||
'delivered': len([
|
]),
|
||||||
message for message in messages if message['status'] == 'Delivered'
|
'failed': len([
|
||||||
]),
|
message for message in messages if message['status'] == 'Failed'
|
||||||
'failed': len([
|
])
|
||||||
message for message in messages if message['status'] == 'Failed'
|
},
|
||||||
])
|
cost=u'£0.00',
|
||||||
},
|
uploaded_file_name=job['original_file_name'],
|
||||||
cost=u'£0.00',
|
uploaded_file_time=job['created_at'],
|
||||||
uploaded_file_name=uploaded_file_name,
|
template_used=job['template'],
|
||||||
uploaded_file_time=now,
|
flash_message="We’ve accepted {} for processing".format(job['original_file_name']),
|
||||||
template_used='Test message 1',
|
service_id=service_id
|
||||||
flash_message=u'We’ve started sending your messages',
|
)
|
||||||
service_id=service_id
|
except HTTPError as e:
|
||||||
)
|
if e.status_code == 404:
|
||||||
|
abort(404)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<int:service_id>/jobs/<job_id>/notification/<string:notification_id>")
|
@main.route("/services/<int:service_id>/jobs/<job_id>/notification/<string:notification_id>")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import csv
|
import csv
|
||||||
import re
|
import re
|
||||||
|
import uuid
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
@@ -34,7 +35,8 @@ def send_sms(service_id):
|
|||||||
try:
|
try:
|
||||||
csv_file = form.file.data
|
csv_file = form.file.data
|
||||||
filedata = _get_filedata(csv_file)
|
filedata = _get_filedata(csv_file)
|
||||||
upload_id = s3upload(service_id, filedata)
|
upload_id = str(uuid.uuid4())
|
||||||
|
s3upload(upload_id, service_id, filedata)
|
||||||
return redirect(url_for('.check_sms',
|
return redirect(url_for('.check_sms',
|
||||||
service_id=service_id,
|
service_id=service_id,
|
||||||
upload_id=upload_id,
|
upload_id=upload_id,
|
||||||
@@ -85,7 +87,7 @@ def check_sms(service_id, upload_id):
|
|||||||
# that will be done in another story
|
# that will be done in another story
|
||||||
template_id = 1
|
template_id = 1
|
||||||
|
|
||||||
job_api_client.create_job(service_id, template_id, file_name)
|
job_api_client.create_job(upload_id, service_id, template_id, file_name)
|
||||||
return redirect(url_for('main.view_job',
|
return redirect(url_for('main.view_job',
|
||||||
service_id=service_id,
|
service_id=service_id,
|
||||||
job_id=upload_id))
|
job_id=upload_id))
|
||||||
|
|||||||
@@ -14,8 +14,13 @@ class JobApiClient(BaseAPIClient):
|
|||||||
self.client_id = app.config['ADMIN_CLIENT_USER_NAME']
|
self.client_id = app.config['ADMIN_CLIENT_USER_NAME']
|
||||||
self.secret = app.config['ADMIN_CLIENT_SECRET']
|
self.secret = app.config['ADMIN_CLIENT_SECRET']
|
||||||
|
|
||||||
def create_job(self, service_id, template_id, file_name):
|
def get_job(self, service_id, job_id=None):
|
||||||
job_id = str(uuid.uuid4())
|
if job_id:
|
||||||
|
return self.get(url='/service/{}/job/{}'.format(service_id, job_id))
|
||||||
|
else:
|
||||||
|
return self.get(url='/service/{}/job'.format(service_id))
|
||||||
|
|
||||||
|
def create_job(self, job_id, service_id, template_id, file_name):
|
||||||
data = {
|
data = {
|
||||||
"id": job_id,
|
"id": job_id,
|
||||||
"service": service_id,
|
"service": service_id,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ GOV.UK Notify | Notifications activity
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Sent with template <a href="{{ url_for('.edit_service_template', service_id=service_id, template_id=1) }}">{{ template_used }}</a> at {{ uploaded_file_time }}
|
Sent with template <a href="{{ url_for('.edit_service_template', service_id=service_id, template_id=1) }}">{{ template_used }}</a> on {{ uploaded_file_time | format_datetime}}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% call(item) list_table(
|
{% call(item) list_table(
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ GOV.UK Notify | Notifications activity
|
|||||||
field_headings=['Job', 'File', 'Time', 'Status']
|
field_headings=['Job', 'File', 'Time', 'Status']
|
||||||
) %}
|
) %}
|
||||||
{% call field() %}
|
{% call field() %}
|
||||||
<a href="{{ url_for('.view_job', service_id=service_id, job_id=456) }}">{{ item.file }}</a>
|
<a href="{{ url_for('.view_job', service_id=service_id, job_id=item.id) }}">{{ item.id }}</a>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% call field() %}
|
{% call field() %}
|
||||||
<a href="{{ url_for('.view_job', service_id=service_id, job_id=456) }}">{{ item.job }}</a>
|
<a href="{{ url_for('.view_job', service_id=service_id, job_id=item.id) }}">{{ item.original_file_name }}</a>
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% call field() %}
|
{% call field() %}
|
||||||
{{ item.time }}
|
{{ item.created_at | format_datetime}}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
{% call field() %}
|
{% call field() %}
|
||||||
{{ item.status }}
|
{{ item.status }}
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ pytest==2.8.1
|
|||||||
pytest-mock==0.8.1
|
pytest-mock==0.8.1
|
||||||
moto==0.4.19
|
moto==0.4.19
|
||||||
httpretty==0.8.10
|
httpretty==0.8.10
|
||||||
|
beautifulsoup4==4.4.1
|
||||||
|
|||||||
@@ -80,3 +80,21 @@ def create_another_test_user(state):
|
|||||||
def get_test_user():
|
def get_test_user():
|
||||||
from app.main.dao import users_dao
|
from app.main.dao import users_dao
|
||||||
return users_dao.get_user_by_email(TEST_USER_EMAIL)
|
return users_dao.get_user_by_email(TEST_USER_EMAIL)
|
||||||
|
|
||||||
|
|
||||||
|
def job_json():
|
||||||
|
import uuid
|
||||||
|
import datetime
|
||||||
|
uuid.uuid4()
|
||||||
|
job_id = str(uuid.uuid4())
|
||||||
|
created_at = str(datetime.datetime.now().time())
|
||||||
|
data = {
|
||||||
|
'id': str(job_id),
|
||||||
|
'service': 1,
|
||||||
|
'template': 1,
|
||||||
|
'original_file_name': 'thisisatest.csv',
|
||||||
|
'bucket_name': 'service-1-{}-notify'.format(job_id),
|
||||||
|
'file_name': '{}.csv'.format(job_id),
|
||||||
|
'created_at': created_at
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
def test_should_return_list_of_all_jobs(app_,
|
def test_should_return_list_of_all_jobs(app_,
|
||||||
@@ -6,14 +7,18 @@ def test_should_return_list_of_all_jobs(app_,
|
|||||||
api_user_active,
|
api_user_active,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
mock_get_user_by_email,
|
mock_get_user_by_email,
|
||||||
mock_login):
|
mock_login,
|
||||||
|
mock_get_jobs):
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
with app_.test_client() as client:
|
with app_.test_client() as client:
|
||||||
client.login(api_user_active)
|
client.login(api_user_active)
|
||||||
response = client.get(url_for('main.view_jobs', service_id=101))
|
response = client.get(url_for('main.view_jobs', service_id=101))
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert 'You haven’t sent any notifications yet' in response.get_data(as_text=True)
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
assert page.h1.string == 'Notifications activity'
|
||||||
|
jobs = page.tbody.find_all('tr')
|
||||||
|
assert len(jobs) == 5
|
||||||
|
|
||||||
|
|
||||||
def test_should_show_page_for_one_job(app_,
|
def test_should_show_page_for_one_job(app_,
|
||||||
@@ -21,35 +26,17 @@ def test_should_show_page_for_one_job(app_,
|
|||||||
api_user_active,
|
api_user_active,
|
||||||
mock_login,
|
mock_login,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
mock_get_user_by_email):
|
mock_get_user_by_email,
|
||||||
with app_.test_request_context():
|
job_data,
|
||||||
with app_.test_client() as client:
|
mock_get_job):
|
||||||
# TODO filename will be part of job metadata not in session
|
service_id = job_data['service']
|
||||||
with client.session_transaction() as s:
|
job_id = job_data['id']
|
||||||
s[456] = 'dispatch_20151114.csv'
|
file_name = job_data['original_file_name']
|
||||||
client.login(api_user_active)
|
|
||||||
response = client.get(url_for('main.view_job', service_id=123, job_id=456))
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert 'dispatch_20151114.csv' in response.get_data(as_text=True)
|
|
||||||
assert 'Test message 1' in response.get_data(as_text=True)
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_show_page_for_one_notification(app_,
|
|
||||||
service_one,
|
|
||||||
api_user_active,
|
|
||||||
mock_get_user,
|
|
||||||
mock_get_user_by_email,
|
|
||||||
mock_login):
|
|
||||||
with app_.test_request_context():
|
with app_.test_request_context():
|
||||||
with app_.test_client() as client:
|
with app_.test_client() as client:
|
||||||
client.login(api_user_active)
|
client.login(api_user_active)
|
||||||
response = client.get(url_for(
|
response = client.get(url_for('main.view_job', service_id=service_id, job_id=job_id))
|
||||||
'main.view_notification',
|
|
||||||
service_id=101,
|
|
||||||
job_id=123,
|
|
||||||
notification_id=3))
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert 'Text message' in response.get_data(as_text=True)
|
assert file_name in response.get_data(as_text=True)
|
||||||
assert '+44 7700 900 522' in response.get_data(as_text=True)
|
|
||||||
|
|||||||
@@ -124,10 +124,12 @@ def test_create_job_should_call_api(app_,
|
|||||||
mock_get_user_by_email,
|
mock_get_user_by_email,
|
||||||
mock_login,
|
mock_login,
|
||||||
job_data,
|
job_data,
|
||||||
mock_create_job):
|
mock_create_job,
|
||||||
|
mock_get_job):
|
||||||
|
|
||||||
|
import uuid
|
||||||
service_id = service_one['id']
|
service_id = service_one['id']
|
||||||
job_id = job_data['id']
|
job_id = str(uuid.uuid4())
|
||||||
file_name = job_data['file_name']
|
file_name = job_data['file_name']
|
||||||
|
|
||||||
# TODO - template id should come from form but is not wired in yet.
|
# TODO - template id should come from form but is not wired in yet.
|
||||||
@@ -141,5 +143,5 @@ def test_create_job_should_call_api(app_,
|
|||||||
response = client.post(url, data=job_data, follow_redirects=True)
|
response = client.post(url, data=job_data, follow_redirects=True)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
mock_create_job.assert_called_with(service_id, template_id, file_name)
|
mock_create_job.assert_called_with(job_id, service_id, template_id, file_name)
|
||||||
assert job_data['bucket_name'] == "service-{}-{}-notify".format(service_id, job_id)
|
assert job_data['bucket_name'] == "service-{}-{}-notify".format(service_id, job_id)
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import date
|
from datetime import date
|
||||||
import pytest
|
import pytest
|
||||||
from alembic.command import upgrade
|
|
||||||
from alembic.config import Config
|
|
||||||
from flask.ext.migrate import Migrate, MigrateCommand
|
|
||||||
from flask.ext.script import Manager
|
|
||||||
from sqlalchemy.schema import MetaData
|
|
||||||
|
|
||||||
from app import create_app
|
from app import create_app
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
create_test_user, service_json, TestClient,
|
service_json,
|
||||||
get_test_user, template_json, api_key_json)
|
TestClient,
|
||||||
|
template_json,
|
||||||
|
api_key_json,
|
||||||
|
job_json
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
@@ -422,24 +421,41 @@ def mock_check_verify_code_code_expired(mocker):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def job_data(mocker):
|
def job_data():
|
||||||
import uuid
|
return job_json()
|
||||||
job_id = uuid.uuid4()
|
|
||||||
file_name = 'thisisatest.csv'
|
|
||||||
data = {
|
|
||||||
'id': str(job_id),
|
|
||||||
'file_name': file_name,
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def mock_create_job(mocker, job_data):
|
def mock_create_job(mocker, job_data):
|
||||||
def _create(service_id, template_id, file_name):
|
def _create(job_id, service_id, template_id, file_name):
|
||||||
|
job_data['id'] = job_id
|
||||||
job_data['service'] = service_id
|
job_data['service'] = service_id
|
||||||
job_data['template'] = template_id
|
job_data['template'] = template_id
|
||||||
job_data['bucket_name'] = 'service-{}-{}-notify'.format(service_id, job_data['id'])
|
job_data['bucket_name'] = 'service-{}-{}-notify'.format(service_id, job_id)
|
||||||
job_data['original_file_name'] = file_name
|
job_data['original_file_name'] = file_name
|
||||||
job_data['file_name'] = '{}.csv'.format(job_data['id'])
|
job_data['file_name'] = '{}.csv'.format(job_id)
|
||||||
return job_data
|
return job_data
|
||||||
return mocker.patch('app.job_api_client.create_job', side_effect=_create)
|
return mocker.patch('app.job_api_client.create_job', side_effect=_create)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def mock_get_job(mocker, job_data):
|
||||||
|
def _get_job(service_id, job_id):
|
||||||
|
job_data['id'] = job_id
|
||||||
|
job_data['service'] = service_id
|
||||||
|
return {"data": job_data}
|
||||||
|
return mocker.patch('app.job_api_client.get_job', side_effect=_get_job)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def mock_get_jobs(mocker):
|
||||||
|
def _get_jobs(service_id):
|
||||||
|
import uuid
|
||||||
|
data = []
|
||||||
|
for i in range(5):
|
||||||
|
job_data = job_json()
|
||||||
|
job_data['id'] = str(uuid.uuid4())
|
||||||
|
job_data['service'] = service_id
|
||||||
|
data.append(job_data)
|
||||||
|
return {"data": data}
|
||||||
|
return mocker.patch('app.job_api_client.get_job', side_effect=_get_jobs)
|
||||||
|
|||||||
Reference in New Issue
Block a user