refactor template stats endpoint to read from new redis keys

New redis keys are partitioned per service per day. New process is as
follows:

* require a count of days to filter by. Currently admin always gives 7.
* for each day, check and see if there's anything in redis. There won't
  be if either a) redis is/was down or b) the service didn't send any
  notifications that day
  - if there isn't, go to the database and get a count out.
* combine all these stats together
* get the names/template types etc out of the DB at the end.
This commit is contained in:
Leo Hemsted
2018-04-12 10:46:22 +01:00
parent 5e702449cb
commit 9e8b6fd00d
7 changed files with 177 additions and 382 deletions

View File

@@ -50,7 +50,7 @@ from app.utils import convert_utc_to_bst, get_london_midnight_in_utc
@statsd(namespace="dao") @statsd(namespace="dao")
def dao_get_template_usage(service_id, limit_days=None, day=None): def dao_get_template_usage(service_id, *, limit_days=None, day=None):
if bool(limit_days) == bool(day): if bool(limit_days) == bool(day):
raise ValueError('Must filter on either limit_days or a specific day') raise ValueError('Must filter on either limit_days or a specific day')
@@ -75,8 +75,9 @@ def dao_get_template_usage(service_id, limit_days=None, day=None):
).group_by( ).group_by(
Notification.template_id Notification.template_id
).subquery() ).subquery()
query = db.session.query( query = db.session.query(
Template.id.label('template_id'), Template.id,
Template.name, Template.name,
Template.template_type, Template.template_type,
Template.is_precompiled_letter, Template.is_precompiled_letter,

View File

@@ -2,7 +2,6 @@ from datetime import datetime
import uuid import uuid
from sqlalchemy import asc, desc from sqlalchemy import asc, desc
from sqlalchemy.sql.expression import bindparam
from app import db from app import db
from app.models import ( from app.models import (
@@ -124,25 +123,16 @@ def dao_get_template_versions(service_id, template_id):
).all() ).all()
def dao_get_templates_for_cache(cache): def dao_get_multiple_template_details(template_ids):
if not cache or len(cache) == 0: query = db.session.query(
return [] Template.id,
Template.template_type,
# First create a subquery that is a union select of the cache values Template.name,
# Then join templates to the subquery Template.is_precompiled_letter
cache_queries = [ ).filter(
db.session.query(bindparam("template_id" + str(i), Template.id.in_(template_ids)
uuid.UUID(template_id.decode())).label('template_id'), ).order_by(
bindparam("count" + str(i), int(count.decode())).label('count')) Template.name
for i, (template_id, count) in enumerate(cache)] )
cache_subq = cache_queries[0].union(*cache_queries[1:]).subquery()
query = db.session.query(Template.id.label('template_id'),
Template.template_type,
Template.name,
Template.is_precompiled_letter,
cache_subq.c.count.label('count')
).join(cache_subq,
Template.id == cache_subq.c.template_id
).order_by(Template.name)
return query.all() return query.all()

View File

@@ -1,8 +1,8 @@
from flask import ( from flask import (
Blueprint, Blueprint,
jsonify, jsonify,
request, request
current_app) )
from app import redis_store from app import redis_store
from app.dao.notifications_dao import ( from app.dao.notifications_dao import (
@@ -10,15 +10,16 @@ from app.dao.notifications_dao import (
dao_get_last_template_usage dao_get_last_template_usage
) )
from app.dao.templates_dao import ( from app.dao.templates_dao import (
dao_get_templates_for_cache, dao_get_multiple_template_details,
dao_get_template_by_id_and_service_id dao_get_template_by_id_and_service_id
) )
from app.schemas import notification_with_template_schema from app.schemas import notification_with_template_schema
from app.utils import cache_key_for_service_template_counter from app.utils import cache_key_for_service_template_usage_per_day, last_n_days
from app.errors import register_errors, InvalidRequest from app.errors import register_errors, InvalidRequest
from collections import Counter
template_statistics = Blueprint('template-statistics', template_statistics = Blueprint('template_statistics',
__name__, __name__,
url_prefix='/service/<service_id>/template-statistics') url_prefix='/service/<service_id>/template-statistics')
@@ -27,31 +28,17 @@ register_errors(template_statistics)
@template_statistics.route('') @template_statistics.route('')
def get_template_statistics_for_service_by_day(service_id): def get_template_statistics_for_service_by_day(service_id):
if request.args.get('limit_days'): try:
try: limit_days = int(request.args.get('limit_days'))
limit_days = int(request.args['limit_days']) except ValueError:
except ValueError as e: error = '{} is not an integer'.format(request.args.get('limit_days'))
error = '{} is not an integer'.format(request.args['limit_days']) message = {'limit_days': [error]}
message = {'limit_days': [error]} raise InvalidRequest(message, status_code=400)
raise InvalidRequest(message, status_code=400)
else:
limit_days = 7
if limit_days == 7: if limit_days < 1 or limit_days > 7:
stats = get_template_statistics_for_7_days(limit_days, service_id) raise InvalidRequest({'limit_days': ['limit_days must be between 1 and 7']}, status_code=400)
else:
stats = dao_get_template_usage(service_id, limit_days=limit_days)
def serialize(data): return jsonify(data=get_template_statistics_for_last_n_days(service_id, limit_days))
return {
'count': data.count,
'template_id': str(data.template_id),
'template_name': data.name,
'template_type': data.template_type,
'is_precompiled_letter': data.is_precompiled_letter
}
return jsonify(data=[serialize(row) for row in stats])
@template_statistics.route('/<template_id>') @template_statistics.route('/<template_id>')
@@ -70,31 +57,30 @@ def get_template_statistics_for_template_id(service_id, template_id):
return jsonify(data=data) return jsonify(data=data)
def get_template_statistics_for_7_days(limit_days, service_id): def get_template_statistics_for_last_n_days(service_id, limit_days):
cache_key = cache_key_for_service_template_counter(service_id) template_stats_by_id = Counter()
template_stats_by_id = redis_store.get_all_from_hash(cache_key)
if not template_stats_by_id:
stats = dao_get_template_usage(service_id, limit_days=limit_days)
cache_values = dict([(x.template_id, x.count) for x in stats])
if cache_values:
redis_store.set_hash_and_expire(cache_key,
cache_values,
current_app.config['EXPIRE_CACHE_TEN_MINUTES'])
else:
stats = dao_get_templates_for_cache(template_stats_by_id.items())
return stats
# TODO: can only switch to this code when redis has been populated (either through time passing or a manual step) for day in last_n_days(limit_days):
# from collections import Counter # "{SERVICE_ID}-template-usage-{YYYY-MM-DD}"
# from notifications_utils.redis_client import RedisException key = cache_key_for_service_template_usage_per_day(service_id, day)
# template_stats_by_id = Counter() stats = redis_store.get_all_from_hash(key)
# for day in last_7_days: if not stats:
# # "<SERVICE_ID>-template-usage-{YYYY-MM-DD}" # key didn't exist (or redis was down) - lets populate from DB.
# key = cache_key_for_service_templates_used_per_day(service_id, limit_days) stats = {
# try: row.id: row.count for row in dao_get_template_usage(service_id, day=day)
# template_stats_by_id += Counter(redis_store.get_all_from_hash(key, raise_exception=True)) }
# except RedisException:
# # TODO: ???? template_stats_by_id += Counter(stats)
#
# # TODO: streamline db query and avoid weird unions if possible. # attach count from stats to name/type/etc from database
# return dao_get_templates_for_cache(template_stats_by_id.items()) template_details = dao_get_multiple_template_details(template_stats_by_id.keys())
return [
{
'count': template_stats_by_id[template.id],
'template_id': str(template.id),
'template_name': template.name,
'template_type': template.template_type,
'is_precompiled_letter': template.is_precompiled_letter
}
for template in template_details
]

View File

@@ -80,6 +80,9 @@ def cache_key_for_service_template_counter(service_id, limit_days=7):
def cache_key_for_service_template_usage_per_day(service_id, datetime): def cache_key_for_service_template_usage_per_day(service_id, datetime):
"""
You should probably pass a BST datetime into this function
"""
return "service-{}-template-usage-{}".format(service_id, datetime.date().isoformat()) return "service-{}-template-usage-{}".format(service_id, datetime.date().isoformat())
@@ -96,4 +99,19 @@ def days_ago(number_of_days):
""" """
Returns midnight a number of days ago. Takes care of daylight savings etc. Returns midnight a number of days ago. Takes care of daylight savings etc.
""" """
return get_london_midnight_in_utc(datetime.utcnow() - timedelta(number_of_days)) return get_london_midnight_in_utc(datetime.utcnow() - timedelta(days=number_of_days))
def last_n_days(limit_days):
"""
Returns the last n dates, oldest first. Takes care of daylight savings (but returns a date, be careful how you manipulate it later! Don't
directly use the date for comparing to UTC datetimes!). Includes today.
"""
return [
datetime.combine(
(convert_utc_to_bst(datetime.utcnow()) - timedelta(days=x)),
datetime.min.time()
)
# reverse the countdown, -1 from first two args to ensure it stays 0-indexed
for x in range(limit_days - 1, -1, -1)
]

View File

@@ -10,14 +10,13 @@ from app.dao.templates_dao import (
dao_get_all_templates_for_service, dao_get_all_templates_for_service,
dao_update_template, dao_update_template,
dao_get_template_versions, dao_get_template_versions,
dao_get_templates_for_cache, dao_get_multiple_template_details,
dao_redact_template, dao_update_template_reply_to dao_redact_template, dao_update_template_reply_to
) )
from app.models import ( from app.models import (
Template, Template,
TemplateHistory, TemplateHistory,
TemplateRedacted, TemplateRedacted
PRECOMPILED_TEMPLATE_NAME
) )
from tests.app.conftest import sample_template as create_sample_template from tests.app.conftest import sample_template as create_sample_template
@@ -481,77 +480,16 @@ def test_get_template_versions_is_empty_for_hidden_templates(notify_db, notify_d
assert len(versions) == 0 assert len(versions) == 0
def test_get_templates_by_ids_successful(notify_db, notify_db_session): def test_get_multiple_template_details_returns_templates_for_list_of_ids(sample_service):
template_1 = create_sample_template( t1 = create_template(sample_service)
notify_db, t2 = create_template(sample_service)
notify_db_session, create_template(sample_service) # t3
template_name='Sample Template 1',
template_type="sms",
content="Template content"
)
template_2 = create_sample_template(
notify_db,
notify_db_session,
template_name='Sample Template 2',
template_type="sms",
content="Template content"
)
create_sample_template(
notify_db,
notify_db_session,
template_name='Sample Template 3',
template_type="email",
content="Template content"
)
sample_cache_dict = {str.encode(str(template_1.id)): str.encode('2'),
str.encode(str(template_2.id)): str.encode('3')}
cache = [[k, v] for k, v in sample_cache_dict.items()]
templates = dao_get_templates_for_cache(cache)
assert len(templates) == 2
assert [(template_1.id, template_1.template_type, template_1.name, False, 2),
(template_2.id, template_2.template_type, template_2.name, False, 3)] == templates
res = dao_get_multiple_template_details([t1.id, t2.id])
def test_get_letter_templates_by_ids_successful(notify_db, notify_db_session): assert {x.id for x in res} == {t1.id, t2.id}
template_1 = create_sample_template( # make sure correct properties are on each row
notify_db, assert res[0].id
notify_db_session, assert res[0].template_type
template_name=PRECOMPILED_TEMPLATE_NAME, assert res[0].name
template_type="letter", assert not res[0].is_precompiled_letter
content="Template content",
hidden=True
)
template_2 = create_sample_template(
notify_db,
notify_db_session,
template_name='Sample Template 2',
template_type="letter",
content="Template content"
)
sample_cache_dict = {str.encode(str(template_1.id)): str.encode('2'),
str.encode(str(template_2.id)): str.encode('3')}
cache = [[k, v] for k, v in sample_cache_dict.items()]
templates = dao_get_templates_for_cache(cache)
assert len(templates) == 2
assert [(template_1.id, template_1.template_type, template_1.name, True, 2),
(template_2.id, template_2.template_type, template_2.name, False, 3)] == templates
def test_get_templates_by_ids_successful_for_one_cache_item(notify_db, notify_db_session):
template_1 = create_sample_template(
notify_db,
notify_db_session,
template_name='Sample Template 1',
template_type="sms",
content="Template content"
)
sample_cache_dict = {str.encode(str(template_1.id)): str.encode('2')}
cache = [[k, v] for k, v in sample_cache_dict.items()]
templates = dao_get_templates_for_cache(cache)
assert len(templates) == 1
assert [(template_1.id, template_1.template_type, template_1.name, False, 2)] == templates
def test_get_templates_by_ids_returns_empty_list():
assert dao_get_templates_for_cache({}) == []
assert dao_get_templates_for_cache(None) == []

View File

@@ -1,281 +1,129 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json import uuid
import pytest import pytest
from freezegun import freeze_time from freezegun import freeze_time
from app.dao.templates_dao import dao_update_template from tests.app.conftest import sample_notification
from tests import create_authorization_header
from tests.app.conftest import (
sample_template as create_sample_template,
sample_notification,
sample_notification_history,
sample_email_template
)
def test_get_all_template_statistics_with_bad_arg_returns_400(client, sample_service): # get_template_statistics_for_service_by_day
auth_header = create_authorization_header()
response = client.get( @pytest.mark.parametrize('query_string', [
'/service/{}/template-statistics'.format(sample_service.id), {},
headers=[('Content-Type', 'application/json'), auth_header], {'limit_days': 0},
query_string={'limit_days': 'blurk'} {'limit_days': 8},
{'limit_days': 3.5},
{'limit_days': 'blurk'},
])
def get_template_statistics_for_service_by_day_with_bad_arg_returns_400(admin_request, query_string):
json_resp = admin_request.get(
'template_statistics.get_template_statistics_for_service_by_day',
service_id=uuid.uuid4(),
**query_string,
_expected_status=400
) )
assert response.status_code == 400
json_resp = json.loads(response.get_data(as_text=True))
assert json_resp['result'] == 'error' assert json_resp['result'] == 'error'
assert json_resp['message'] == {'limit_days': ['blurk is not an integer']} assert 'limit_days' in json_resp['message']
@freeze_time('2016-08-18') def test_get_template_statistics_for_service_by_day_returns_template_info(admin_request, mocker, sample_notification):
def test_get_template_statistics_for_service(notify_db, notify_db_session, client, mocker): json_resp = admin_request.get(
email, sms = set_up_notifications(notify_db, notify_db_session) 'template_statistics.get_template_statistics_for_service_by_day',
service_id=sample_notification.service_id,
mocked_redis = mocker.patch('app.redis_store.get_all_from_hash') limit_days=1
auth_header = create_authorization_header()
response = client.get(
'/service/{}/template-statistics'.format(email.service_id),
headers=[('Content-Type', 'application/json'), auth_header]
) )
assert response.status_code == 200 assert len(json_resp) == 1
json_resp = json.loads(response.get_data(as_text=True))
assert len(json_resp['data']) == 2
assert json_resp['data'][0]['count'] == 3
assert json_resp['data'][0]['template_id'] == str(email.id)
assert json_resp['data'][0]['template_name'] == email.name
assert json_resp['data'][0]['template_type'] == email.template_type
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_id'] == str(sms.id)
assert json_resp['data'][1]['template_name'] == sms.name
assert json_resp['data'][1]['template_type'] == sms.template_type
mocked_redis.assert_not_called() assert json_resp['data'][0]['count'] == 1
assert json_resp['data'][0]['template_id'] == str(sample_notification.template_id)
assert json_resp['data'][0]['template_name'] == 'Template Name'
assert json_resp['data'][0]['template_type'] == 'sms'
@freeze_time('2016-08-18') def test_get_template_statistics_for_service_by_day_gets_out_of_redis_if_available(admin_request, mocker):
def test_get_template_statistics_for_service_limited_1_day(notify_db, notify_db_session, client, assert False
mocker):
email, sms = set_up_notifications(notify_db, notify_db_session)
mock_redis = mocker.patch('app.redis_store.get_all_from_hash')
auth_header = create_authorization_header()
response = client.get( def test_get_template_statistics_for_service_by_day_goes_to_db_if_not_in_redis(admin_request, mocker):
'/service/{}/template-statistics'.format(email.service_id), assert False
headers=[('Content-Type', 'application/json'), auth_header],
query_string={'limit_days': 1}
def test_get_template_statistics_for_service_by_day_gets_stats_for_correct_days(admin_request, mocker):
assert False
def test_get_template_statistics_for_service_by_day_returns_empty_list_if_no_templates(
admin_request,
mocker,
sample_service
):
json_resp = admin_request.get(
'template_statistics.get_template_statistics_for_service_by_day',
service_id=sample_service.id,
limit_days=1
) )
assert response.status_code == 200
json_resp = json.loads(response.get_data(as_text=True))['data']
assert len(json_resp) == 2
assert json_resp[0]['count'] == 1
assert json_resp[0]['template_id'] == str(email.id)
assert json_resp[0]['template_name'] == email.name
assert json_resp[0]['template_type'] == email.template_type
assert json_resp[1]['count'] == 1
assert json_resp[1]['template_id'] == str(sms.id)
assert json_resp[1]['template_name'] == sms.name
assert json_resp[1]['template_type'] == sms.template_type
mock_redis.assert_not_called()
@pytest.mark.parametrize("cache_values", [False, True])
@freeze_time('2016-08-18')
def test_get_template_statistics_for_service_limit_7_days(notify_db, notify_db_session, client,
mocker,
cache_values):
email, sms = set_up_notifications(notify_db, notify_db_session)
mock_cache_values = {str.encode(str(sms.id)): str.encode('3'),
str.encode(str(email.id)): str.encode('3')} if cache_values else None
mocked_redis_get = mocker.patch('app.redis_store.get_all_from_hash', return_value=mock_cache_values)
mocked_redis_set = mocker.patch('app.redis_store.set_hash_and_expire')
auth_header = create_authorization_header()
response_for_a_week = client.get(
'/service/{}/template-statistics'.format(email.service_id),
headers=[('Content-Type', 'application/json'), auth_header],
query_string={'limit_days': 7}
)
assert response_for_a_week.status_code == 200
json_resp = json.loads(response_for_a_week.get_data(as_text=True))
assert len(json_resp['data']) == 2
assert json_resp['data'][0]['count'] == 3
assert json_resp['data'][0]['template_name'] == 'New Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == 'New SMS Template Name'
mocked_redis_get.assert_called_once_with("{}-template-counter-limit-7-days".format(email.service_id))
if cache_values:
mocked_redis_set.assert_not_called()
else:
mocked_redis_set.assert_called_once_with("{}-template-counter-limit-7-days".format(email.service_id),
{sms.id: 3, email.id: 3}, 600)
@freeze_time('2016-08-18')
def test_get_template_statistics_for_service_limit_30_days(notify_db, notify_db_session, client,
mocker):
email, sms = set_up_notifications(notify_db, notify_db_session)
mock_redis = mocker.patch('app.redis_store.get_all_from_hash')
auth_header = create_authorization_header()
response_for_a_month = client.get(
'/service/{}/template-statistics'.format(email.service_id),
headers=[('Content-Type', 'application/json'), auth_header],
query_string={'limit_days': 30}
)
assert response_for_a_month.status_code == 200
json_resp = json.loads(response_for_a_month.get_data(as_text=True))
assert len(json_resp['data']) == 2
assert json_resp['data'][0]['count'] == 3
assert json_resp['data'][0]['template_name'] == 'New Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == 'New SMS Template Name'
mock_redis.assert_not_called()
@freeze_time('2016-08-18')
def test_get_template_statistics_for_service_no_limit(notify_db, notify_db_session, client,
mocker):
email, sms = set_up_notifications(notify_db, notify_db_session)
mock_redis = mocker.patch('app.redis_store.get_all_from_hash')
auth_header = create_authorization_header()
response_for_all = client.get(
'/service/{}/template-statistics'.format(email.service_id),
headers=[('Content-Type', 'application/json'), auth_header]
)
assert response_for_all.status_code == 200
json_resp = json.loads(response_for_all.get_data(as_text=True))
assert len(json_resp['data']) == 2
assert json_resp['data'][0]['count'] == 3
assert json_resp['data'][0]['template_name'] == 'New Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == 'New SMS Template Name'
mock_redis.assert_not_called()
def set_up_notifications(notify_db, notify_db_session):
sms = create_sample_template(notify_db, notify_db_session)
email = sample_email_template(notify_db, notify_db_session)
today = datetime.now()
a_week_ago = datetime.now() - timedelta(days=7)
a_month_ago = datetime.now() - timedelta(days=30)
sample_notification(notify_db, notify_db_session, created_at=a_month_ago, template=sms)
sample_notification(notify_db, notify_db_session, created_at=a_month_ago, template=email)
email.name = 'Updated Email Template Name'
dao_update_template(email)
sms.name = 'Updated SMS Template Name'
dao_update_template(sms)
sample_notification(notify_db, notify_db_session, created_at=a_week_ago, template=sms)
sample_notification(notify_db, notify_db_session, created_at=a_week_ago, template=email)
email.name = 'New Email Template Name'
dao_update_template(email)
sms.name = 'New SMS Template Name'
dao_update_template(sms)
sample_notification(notify_db, notify_db_session, created_at=today, template=sms)
sample_notification(notify_db, notify_db_session, created_at=today, template=email)
return email, sms
@freeze_time('2016-08-18')
def test_returns_empty_list_if_no_templates_used(client, sample_service, mocker):
auth_header = create_authorization_header()
mock_redis = mocker.patch('app.redis_store.set_hash_and_expire')
response = client.get(
'/service/{}/template-statistics'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header]
)
assert response.status_code == 200
json_resp = json.loads(response.get_data(as_text=True))
assert len(json_resp['data']) == 0 assert len(json_resp['data']) == 0
mock_redis.assert_not_called()
# get_template_statistics_for_template
def test_get_template_statistics_by_id_returns_last_notification( def test_get_template_statistics_for_template_returns_last_notification(
notify_db, notify_db,
notify_db_session, notify_db_session,
client): admin_request):
sample_notification(notify_db, notify_db_session) sample_notification(notify_db, notify_db_session)
sample_notification(notify_db, notify_db_session) sample_notification(notify_db, notify_db_session)
notification_3 = sample_notification(notify_db, notify_db_session) notification_3 = sample_notification(notify_db, notify_db_session)
auth_header = create_authorization_header() json_resp = admin_request.get(
'template_statistics.get_template_statistics_for_template_id',
response = client.get( service_id=notification_3.service_id,
'/service/{}/template-statistics/{}'.format(notification_3.service_id, notification_3.template_id), template_id=notification_3.template_id
headers=[('Content-Type', 'application/json'), auth_header],
) )
assert response.status_code == 200 assert json_resp['data']['id'] == str(notification_3.id)
json_resp = json.loads(response.get_data(as_text=True))['data']
assert json_resp['id'] == str(notification_3.id)
def test_get_template_statistics_for_template_returns_empty_if_no_statistics( def test_get_template_statistics_for_template_returns_empty_if_no_statistics(
client, admin_request,
sample_template, sample_template,
): ):
auth_header = create_authorization_header() json_resp = admin_request.get(
'template_statistics.get_template_statistics_for_template_id',
response = client.get( service_id=sample_template.service_id,
'/service/{}/template-statistics/{}'.format(sample_template.service_id, sample_template.id), template_id=sample_template.id
headers=[('Content-Type', 'application/json'), auth_header],
) )
assert response.status_code == 200
json_resp = json.loads(response.get_data(as_text=True))
assert not json_resp['data'] assert not json_resp['data']
def test_get_template_statistics_raises_error_for_nonexistent_template( def test_get_template_statistics_for_template_raises_error_for_nonexistent_template(
client, admin_request,
sample_service, sample_service,
fake_uuid fake_uuid
): ):
auth_header = create_authorization_header() json_resp = admin_request.get(
'template_statistics.get_template_statistics_for_template_id',
response = client.get( service_id=sample_service.id,
'/service/{}/template-statistics/{}'.format(sample_service.id, fake_uuid), template_id=fake_uuid,
headers=[('Content-Type', 'application/json'), auth_header], _expected_status=404
) )
assert response.status_code == 404
json_resp = json.loads(response.get_data(as_text=True))
assert json_resp['message'] == 'No result found' assert json_resp['message'] == 'No result found'
assert json_resp['result'] == 'error' assert json_resp['result'] == 'error'
def test_get_template_statistics_by_id_returns_empty_for_old_notification( def test_get_template_statistics_for_template_returns_empty_for_old_notification(
notify_db, admin_request,
notify_db_session, sample_notification_history
client,
sample_template
): ):
sample_notification_history(notify_db, notify_db_session, sample_template) json_resp = admin_request.get(
'template_statistics.get_template_statistics_for_template_id',
auth_header = create_authorization_header() service_id=sample_notification_history.service_id,
template_id=sample_notification_history.template_id
response = client.get(
'/service/{}/template-statistics/{}'.format(sample_template.service.id, sample_template.id),
headers=[('Content-Type', 'application/json'), auth_header],
) )
assert response.status_code == 200 assert not json_resp['data']
json_resp = json.loads(response.get_data(as_text=True))['data']
assert not json_resp

View File

@@ -8,7 +8,8 @@ from app.utils import (
get_midnight_for_day_before, get_midnight_for_day_before,
convert_utc_to_bst, convert_utc_to_bst,
convert_bst_to_utc, convert_bst_to_utc,
days_ago days_ago,
last_n_days
) )
@@ -66,3 +67,16 @@ def test_convert_bst_to_utc():
def test_days_ago(current_time, expected_datetime): def test_days_ago(current_time, expected_datetime):
with freeze_time(current_time): with freeze_time(current_time):
assert days_ago(1) == expected_datetime assert days_ago(1) == expected_datetime
def test_last_n_days():
with freeze_time('2018-03-27 12:00'):
res = last_n_days(5)
assert res == [
datetime(2018, 3, 23, 0, 0),
datetime(2018, 3, 24, 0, 0),
datetime(2018, 3, 25, 0, 0),
datetime(2018, 3, 26, 0, 0),
datetime(2018, 3, 27, 0, 0)
]