Merge pull request #804 from alphagov/feat-add-perf-platform-client-and-job

Add client and job to update the performance platform daily
This commit is contained in:
imdadahad
2017-01-27 17:03:38 +00:00
committed by GitHub
13 changed files with 514 additions and 40 deletions

View File

@@ -1,18 +1,27 @@
from datetime import datetime, timedelta
from functools import partial
from flask import current_app
from freezegun import freeze_time
from app.celery.scheduled_tasks import s3
from app.celery import scheduled_tasks
from app.celery.scheduled_tasks import (delete_verify_codes,
remove_csv_files,
delete_successful_notifications,
delete_failed_notifications,
delete_invitations,
timeout_notifications,
run_scheduled_jobs)
from app.celery.scheduled_tasks import (
delete_verify_codes,
remove_csv_files,
delete_successful_notifications,
delete_failed_notifications,
delete_invitations,
timeout_notifications,
run_scheduled_jobs,
send_daily_performance_stats
)
from app.dao.jobs_dao import dao_get_job_by_id
from tests.app.conftest import sample_notification, sample_job
from app.utils import get_london_midnight_in_utc
from tests.app.conftest import (
sample_notification as create_sample_notification,
sample_job as create_sample_job,
sample_notification_history as create_notification_history
)
from unittest.mock import call
@@ -24,6 +33,7 @@ def test_should_have_decorated_tasks_functions():
assert delete_invitations.__wrapped__.__name__ == 'delete_invitations'
assert run_scheduled_jobs.__wrapped__.__name__ == 'run_scheduled_jobs'
assert remove_csv_files.__wrapped__.__name__ == 'remove_csv_files'
assert send_daily_performance_stats.__wrapped__.__name__ == 'send_daily_performance_stats'
def test_should_call_delete_notifications_more_than_week_in_task(notify_api, mocker):
@@ -58,7 +68,7 @@ def test_update_status_of_notifications_after_timeout(notify_api,
sample_template,
mmg_provider):
with notify_api.test_request_context():
not1 = sample_notification(
not1 = create_sample_notification(
notify_db,
notify_db_session,
service=sample_service,
@@ -66,7 +76,7 @@ def test_update_status_of_notifications_after_timeout(notify_api,
status='sending',
created_at=datetime.utcnow() - timedelta(
seconds=current_app.config.get('SENDING_NOTIFICATIONS_TIMEOUT_PERIOD') + 10))
not2 = sample_notification(
not2 = create_sample_notification(
notify_db,
notify_db_session,
service=sample_service,
@@ -74,7 +84,7 @@ def test_update_status_of_notifications_after_timeout(notify_api,
status='created',
created_at=datetime.utcnow() - timedelta(
seconds=current_app.config.get('SENDING_NOTIFICATIONS_TIMEOUT_PERIOD') + 10))
not3 = sample_notification(
not3 = create_sample_notification(
notify_db,
notify_db_session,
service=sample_service,
@@ -95,7 +105,7 @@ def test_not_update_status_of_notification_before_timeout(notify_api,
sample_template,
mmg_provider):
with notify_api.test_request_context():
not1 = sample_notification(
not1 = create_sample_notification(
notify_db,
notify_db_session,
service=sample_service,
@@ -111,7 +121,7 @@ def test_should_update_scheduled_jobs_and_put_on_queue(notify_db, notify_db_sess
mocked = mocker.patch('app.celery.tasks.process_job.apply_async')
one_minute_in_the_past = datetime.utcnow() - timedelta(minutes=1)
job = sample_job(notify_db, notify_db_session, scheduled_for=one_minute_in_the_past, job_status='scheduled')
job = create_sample_job(notify_db, notify_db_session, scheduled_for=one_minute_in_the_past, job_status='scheduled')
run_scheduled_jobs()
@@ -126,9 +136,24 @@ def test_should_update_all_scheduled_jobs_and_put_on_queue(notify_db, notify_db_
one_minute_in_the_past = datetime.utcnow() - timedelta(minutes=1)
ten_minutes_in_the_past = datetime.utcnow() - timedelta(minutes=10)
twenty_minutes_in_the_past = datetime.utcnow() - timedelta(minutes=20)
job_1 = sample_job(notify_db, notify_db_session, scheduled_for=one_minute_in_the_past, job_status='scheduled')
job_2 = sample_job(notify_db, notify_db_session, scheduled_for=ten_minutes_in_the_past, job_status='scheduled')
job_3 = sample_job(notify_db, notify_db_session, scheduled_for=twenty_minutes_in_the_past, job_status='scheduled')
job_1 = create_sample_job(
notify_db,
notify_db_session,
scheduled_for=one_minute_in_the_past,
job_status='scheduled'
)
job_2 = create_sample_job(
notify_db,
notify_db_session,
scheduled_for=ten_minutes_in_the_past,
job_status='scheduled'
)
job_3 = create_sample_job(
notify_db,
notify_db_session,
scheduled_for=twenty_minutes_in_the_past,
job_status='scheduled'
)
run_scheduled_jobs()
@@ -150,10 +175,42 @@ def test_will_remove_csv_files_for_jobs_older_than_seven_days(notify_db, notify_
midnight = datetime(2016, 10, 10, 0, 0, 0, 0)
one_millisecond_past_midnight = datetime(2016, 10, 10, 0, 0, 0, 1)
job_1 = sample_job(notify_db, notify_db_session, created_at=one_millisecond_before_midnight)
sample_job(notify_db, notify_db_session, created_at=midnight)
sample_job(notify_db, notify_db_session, created_at=one_millisecond_past_midnight)
job_1 = create_sample_job(notify_db, notify_db_session, created_at=one_millisecond_before_midnight)
create_sample_job(notify_db, notify_db_session, created_at=midnight)
create_sample_job(notify_db, notify_db_session, created_at=one_millisecond_past_midnight)
with freeze_time('2016-10-17T00:00:00'):
remove_csv_files()
s3.remove_job_from_s3.assert_called_once_with(job_1.service_id, job_1.id)
@freeze_time("2016-01-11 12:30:00")
def test_send_daily_performance_stats_calls_with_correct_totals(notify_db, notify_db_session, sample_template, mocker):
perf_mock = mocker.patch('app.celery.scheduled_tasks.performance_platform_client.send_performance_stats')
notification_history = partial(
create_notification_history,
notify_db,
notify_db_session,
sample_template,
status='delivered'
)
notification_history(notification_type='email')
notification_history(notification_type='sms')
# Create some notifications for the day before
yesterday = datetime(2016, 1, 10, 15, 30, 0, 0)
with freeze_time(yesterday):
notification_history(notification_type='sms')
notification_history(notification_type='sms')
notification_history(notification_type='email')
notification_history(notification_type='email')
notification_history(notification_type='email')
send_daily_performance_stats()
perf_mock.assert_has_calls([
call(get_london_midnight_in_utc(yesterday), 'sms', 2, 'day'),
call(get_london_midnight_in_utc(yesterday), 'email', 3, 'day')
])

View File

@@ -0,0 +1,120 @@
import requests_mock
import pytest
from datetime import datetime
from freezegun import freeze_time
from functools import partial
from app.clients.performance_platform.performance_platform_client import PerformancePlatformClient
from app.utils import (
get_london_midnight_in_utc,
get_midnight_for_day_before
)
from tests.app.conftest import sample_notification_history as create_notification_history
@pytest.fixture(scope='function')
def client(mocker):
client = PerformancePlatformClient()
current_app = mocker.Mock(config={
'PERFORMANCE_PLATFORM_ENABLED': True,
'PERFORMANCE_PLATFORM_URL': 'performance-platform-url',
'PERFORMANCE_PLATFORM_TOKEN': 'token'
})
client.init_app(current_app)
return client
def test_should_not_call_if_not_enabled(notify_api, client, mocker):
mocker.patch.object(client, '_send_stats_to_performance_platform')
client.active = False
client.send_performance_stats(
date='2016-10-16T00:00:00+00:00',
channel='sms',
count=142,
period='day'
)
client._send_stats_to_performance_platform.assert_not_called()
def test_should_call_if_enabled(notify_api, client, mocker):
mocker.patch.object(client, '_send_stats_to_performance_platform')
client.send_performance_stats(
date='2016-10-16T00:00:00+00:00',
channel='sms',
count=142,
period='day'
)
assert client._send_stats_to_performance_platform.call_count == 1
def test_send_platform_stats_creates_correct_call(notify_api, client):
with requests_mock.Mocker() as request_mock:
request_mock.post(
client.performance_platform_url,
json={},
status_code=200
)
client.send_performance_stats(
date='2016-10-16T00:00:00+00:00',
channel='sms',
count=142,
period='day'
)
assert request_mock.call_count == 1
assert request_mock.request_history[0].url == client.performance_platform_url
assert request_mock.request_history[0].method == 'POST'
request_args = request_mock.request_history[0].json()
assert request_args['dataType'] == 'notifications'
assert request_args['service'] == 'govuk-notify'
assert request_args['period'] == 'day'
assert request_args['channel'] == 'sms'
assert request_args['_timestamp'] == '2016-10-16T00:00:00+00:00'
assert request_args['count'] == 142
expected_base64_id = 'MjAxNi0xMC0xNlQwMDowMDowMCswMDowMGdvdnVrLW5vdGlmeXNtc25vdGlmaWNhdGlvbnNkYXk='
assert request_args['_id'] == expected_base64_id
@freeze_time("2016-01-11 12:30:00")
def test_get_total_sent_notifications_yesterday_returns_expected_totals_dict(
notify_db,
notify_db_session,
client,
sample_template
):
notification_history = partial(
create_notification_history,
notify_db,
notify_db_session,
sample_template,
status='delivered'
)
notification_history(notification_type='email')
notification_history(notification_type='sms')
# Create some notifications for the day before
yesterday = datetime(2016, 1, 10, 15, 30, 0, 0)
with freeze_time(yesterday):
notification_history(notification_type='sms')
notification_history(notification_type='sms')
notification_history(notification_type='email')
notification_history(notification_type='email')
notification_history(notification_type='email')
total_count_dict = client.get_total_sent_notifications_yesterday()
assert total_count_dict == {
"start_date": get_midnight_for_day_before(datetime.utcnow()),
"end_date": get_london_midnight_in_utc(datetime.utcnow()),
"email": {
"count": 3
},
"sms": {
"count": 2
}
}

View File

@@ -526,14 +526,21 @@ def mock_statsd_timing(mocker):
@pytest.fixture(scope='function')
def sample_notification_history(notify_db,
notify_db_session,
sample_template,
status='created',
created_at=None):
def sample_notification_history(
notify_db,
notify_db_session,
sample_template,
status='created',
created_at=None,
notification_type=None,
key_type=KEY_TYPE_NORMAL
):
if created_at is None:
created_at = datetime.utcnow()
if notification_type is None:
notification_type = sample_template.template_type
notification_history = NotificationHistory(
id=uuid.uuid4(),
service=sample_template.service,
@@ -541,8 +548,8 @@ def sample_notification_history(notify_db,
template_version=sample_template.version,
status=status,
created_at=created_at,
notification_type=sample_template.template_type,
key_type=KEY_TYPE_NORMAL
notification_type=notification_type,
key_type=key_type
)
notify_db.session.add(notification_history)
notify_db.session.commit()

View File

@@ -1,5 +1,4 @@
from datetime import datetime, timedelta, date
import pytz
import uuid
from functools import partial
@@ -35,6 +34,7 @@ from app.dao.notifications_dao import (
get_notification_with_personalisation,
get_notifications_for_job,
get_notifications_for_service,
get_total_sent_notifications_in_date_range,
update_notification_status_by_id,
update_notification_status_by_reference,
dao_delete_notifications_and_history_by_id,
@@ -43,9 +43,15 @@ from app.dao.notifications_dao import (
get_april_fools)
from app.dao.services_dao import dao_update_service
from tests.app.conftest import (sample_notification, sample_template, sample_email_template, sample_service, sample_job,
sample_api_key)
from tests.app.conftest import (
sample_notification,
sample_template,
sample_email_template,
sample_service,
sample_job,
sample_api_key,
sample_notification_history as create_notification_history
)
def test_should_have_decorated_notifications_dao_functions():
@@ -1306,3 +1312,113 @@ def test_get_april_fools():
april_fools = get_april_fools(2016)
assert str(april_fools) == '2016-03-31 23:00:00'
assert april_fools.tzinfo is None
@pytest.mark.parametrize('notification_type', ['sms', 'email'])
def test_get_total_sent_notifications_in_date_range_returns_only_in_date_range(
notify_db,
notify_db_session,
sample_template,
notification_type
):
notification_history = partial(
create_notification_history,
notify_db,
notify_db_session,
sample_template,
notification_type=notification_type,
status='delivered'
)
start_date = datetime(2000, 3, 30, 0, 0, 0, 0)
with freeze_time(start_date):
notification_history(created_at=start_date + timedelta(hours=3))
notification_history(created_at=start_date + timedelta(hours=5, minutes=10))
notification_history(created_at=start_date + timedelta(hours=11, minutes=59))
end_date = datetime(2000, 3, 31, 0, 0, 0, 0)
notification_history(created_at=end_date + timedelta(seconds=1))
notification_history(created_at=end_date + timedelta(minutes=10))
total_count = get_total_sent_notifications_in_date_range(start_date, end_date, notification_type)
assert total_count == 3
@pytest.mark.parametrize('notification_type', ['sms', 'email'])
def test_get_total_sent_notifications_in_date_range_excludes_test_key_notifications(
notify_db,
notify_db_session,
sample_template,
notification_type
):
notification_history = partial(
create_notification_history,
notify_db,
notify_db_session,
sample_template,
notification_type=notification_type,
status='delivered'
)
start_date = datetime(2000, 3, 30, 0, 0, 0, 0)
end_date = datetime(2000, 3, 31, 0, 0, 0, 0)
with freeze_time(start_date):
notification_history(key_type=KEY_TYPE_TEAM)
notification_history(key_type=KEY_TYPE_TEAM)
notification_history(key_type=KEY_TYPE_NORMAL)
notification_history(key_type=KEY_TYPE_TEST)
total_count = get_total_sent_notifications_in_date_range(start_date, end_date, notification_type)
assert total_count == 3
def test_get_total_sent_notifications_for_sms_excludes_email_counts(
notify_db,
notify_db_session,
sample_template
):
notification_history = partial(
create_notification_history,
notify_db,
notify_db_session,
sample_template,
status='delivered'
)
start_date = datetime(2000, 3, 30, 0, 0, 0, 0)
end_date = datetime(2000, 3, 31, 0, 0, 0, 0)
with freeze_time(start_date):
notification_history(notification_type='email')
notification_history(notification_type='email')
notification_history(notification_type='sms')
notification_history(notification_type='sms')
notification_history(notification_type='sms')
total_count = get_total_sent_notifications_in_date_range(start_date, end_date, 'sms')
assert total_count == 3
def test_get_total_sent_notifications_for_email_excludes_sms_counts(
notify_db,
notify_db_session,
sample_template
):
notification_history = partial(
create_notification_history,
notify_db,
notify_db_session,
sample_template,
status='delivered'
)
start_date = datetime(2000, 3, 30, 0, 0, 0, 0)
end_date = datetime(2000, 3, 31, 0, 0, 0, 0)
with freeze_time(start_date):
notification_history(notification_type='email')
notification_history(notification_type='email')
notification_history(notification_type='sms')
notification_history(notification_type='sms')
notification_history(notification_type='sms')
total_count = get_total_sent_notifications_in_date_range(start_date, end_date, 'email')
assert total_count == 2

25
tests/app/test_utils.py Normal file
View File

@@ -0,0 +1,25 @@
from datetime import datetime
import pytest
from app.utils import (
get_london_midnight_in_utc,
get_midnight_for_day_before
)
@pytest.mark.parametrize('date, expected_date', [
(datetime(2016, 1, 15, 0, 30), datetime(2016, 1, 15, 0, 0)),
(datetime(2016, 6, 15, 0, 0), datetime(2016, 6, 14, 23, 0)),
(datetime(2016, 9, 15, 11, 59), datetime(2016, 9, 14, 23, 0)),
])
def test_get_london_midnight_in_utc_returns_expected_date(date, expected_date):
assert get_london_midnight_in_utc(date) == expected_date
@pytest.mark.parametrize('date, expected_date', [
(datetime(2016, 1, 15, 0, 30), datetime(2016, 1, 14, 0, 0)),
(datetime(2016, 7, 15, 0, 0), datetime(2016, 7, 13, 23, 0)),
(datetime(2016, 8, 23, 11, 59), datetime(2016, 8, 21, 23, 0)),
])
def test_get_midnight_for_day_before_returns_expected_date(date, expected_date):
assert get_midnight_for_day_before(date) == expected_date