Added a redis cache for the template usage stats.

Cache expires every 10 minutes, but will help with the every 2 second query, especially when a job is running.
There is some clean up and qa to do for this yet
This commit is contained in:
Rebecca Law
2017-02-13 18:47:29 +00:00
parent b2267ae5fc
commit 458adefcb8
9 changed files with 351 additions and 218 deletions

View File

@@ -65,6 +65,8 @@ def create_app(app_name=None):
aws_ses_client.init_app(application.config['AWS_REGION'], statsd_client=statsd_client) aws_ses_client.init_app(application.config['AWS_REGION'], statsd_client=statsd_client)
notify_celery.init_app(application) notify_celery.init_app(application)
encryption.init_app(application) encryption.init_app(application)
print(os.environ['REDIS_URL'])
print(application.config['REDIS_ENABLED'])
redis_store.init_app(application) redis_store.init_app(application)
performance_platform_client.init_app(application) performance_platform_client.init_app(application)
clients.init_app(sms_clients=[firetext_client, mmg_client, loadtest_client], email_clients=[aws_ses_client]) clients.init_app(sms_clients=[firetext_client, mmg_client, loadtest_client], email_clients=[aws_ses_client])
@@ -176,3 +178,7 @@ def process_user_agent(user_agent_string):
return "non-notify-user-agent" return "non-notify-user-agent"
else: else:
return "unknown" return "unknown"
def cache_key_for_service_template_counter(service_id, limit_days=7):
return "{}-template-counter-limit-{}-days".format(service_id, limit_days)

View File

@@ -49,6 +49,7 @@ class Config(object):
# URL of redis instance # URL of redis instance
REDIS_URL = os.getenv('REDIS_URL') REDIS_URL = os.getenv('REDIS_URL')
REDIS_ENABLED = os.getenv('REDIS_ENABLED') == '1' REDIS_ENABLED = os.getenv('REDIS_ENABLED') == '1'
EXPIRE_CACHE_IN_SECONDS = 600
# Performance platform # Performance platform
PERFORMANCE_PLATFORM_ENABLED = os.getenv('PERFORMANCE_PLATFORM_ENABLED') == '1' PERFORMANCE_PLATFORM_ENABLED = os.getenv('PERFORMANCE_PLATFORM_ENABLED') == '1'
@@ -185,6 +186,7 @@ class Development(Config):
Queue('research-mode', Exchange('default'), routing_key='research-mode') Queue('research-mode', Exchange('default'), routing_key='research-mode')
] ]
API_HOST_NAME = "http://localhost:6011" API_HOST_NAME = "http://localhost:6011"
REDIS_ENABLED = True
class Test(Config): class Test(Config):

View File

@@ -1,6 +1,7 @@
import uuid import uuid
from sqlalchemy import (asc, desc) import sqlalchemy
from sqlalchemy import (desc, cast, String, text)
from app import db from app import db
from app.models import (Template, TemplateHistory) from app.models import (Template, TemplateHistory)
@@ -56,3 +57,39 @@ def dao_get_template_versions(service_id, template_id):
).order_by( ).order_by(
desc(TemplateHistory.version) desc(TemplateHistory.version)
).all() ).all()
# def dao_get_templates_by_for_cache(cache):
# if not cache or len(cache) == 0:
# return []
# # First create a subquery that is a union select of the cache values
# # Then join templates to the subquery
# cache_queries = [
# db.session.query(sqlalchemy.sql.expression.bindparam("template_id" + str(i),
# template_id).label('template_id'),
# sqlalchemy.sql.expression.bindparam("count" + str(i), count).label('count'))
# 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,
# cache_subq.c.count.label('count')
# ).join(cache_subq,
# cast(Template.id, String) == cast(cache_subq.c.template_id, String)
# ).order_by(Template.name)
#
# return query.all()
def dao_get_templates_by_for_cache(cache):
if not cache or len(cache) == 0:
return []
txt = "( " + " Union all ".join(
"select '{}'::text as template_id, {} as count".format(x.decode(),
y.decode()) for x, y in cache) + " ) as cache"
txt = "Select t.id as template_id, t.template_type, t.name, cache.count from templates t, " + \
txt + " where t.id::text = cache.template_id order by t.name"
stmt = text(txt)
return db.session.execute(stmt).fetchall()

View File

@@ -2,7 +2,7 @@ from datetime import datetime
from flask import current_app from flask import current_app
from app import redis_store from app import redis_store, cache_key_for_service_template_counter
from app.celery import provider_tasks from app.celery import provider_tasks
from notifications_utils.clients import redis from notifications_utils.clients import redis
from app.dao.notifications_dao import dao_create_notification, dao_delete_notifications_and_history_by_id from app.dao.notifications_dao import dao_create_notification, dao_delete_notifications_and_history_by_id
@@ -63,6 +63,7 @@ def persist_notification(template_id,
if not simulated: if not simulated:
dao_create_notification(notification) dao_create_notification(notification)
redis_store.incr(redis.daily_limit_cache_key(service.id)) redis_store.incr(redis.daily_limit_cache_key(service.id))
redis_store.increment_hash_value(cache_key_for_service_template_counter(service.id), template_id)
current_app.logger.info( current_app.logger.info(
"{} {} created at {}".format(notification.notification_type, notification.id, notification.created_at) "{} {} created at {}".format(notification.notification_type, notification.id, notification.created_at)
) )

View File

@@ -1,14 +1,16 @@
from flask import ( from flask import (
Blueprint, Blueprint,
jsonify, jsonify,
request request,
) current_app)
from app import redis_store
from app.dao.notifications_dao import ( from app.dao.notifications_dao import (
dao_get_template_usage, dao_get_template_usage,
dao_get_last_template_usage) dao_get_last_template_usage)
from app.dao.templates_dao import dao_get_templates_by_for_cache
from app.schemas import notifications_filter_schema, NotificationWithTemplateSchema, notification_with_template_schema from app.schemas import notification_with_template_schema
template_statistics = Blueprint('template-statistics', template_statistics = Blueprint('template-statistics',
__name__, __name__,
@@ -30,7 +32,15 @@ def get_template_statistics_for_service_by_day(service_id):
raise InvalidRequest(message, status_code=400) raise InvalidRequest(message, status_code=400)
else: else:
limit_days = None limit_days = None
stats = dao_get_template_usage(service_id, limit_days=limit_days)
if limit_days == 7:
stats = get_template_statistics_for_7_days(limit_days, service_id)
print(stats)
# [(UUID('c2a331f8-e0b9-43de-9dd2-88300511a1d7'), 'Create with priority', 'sms', 1)]
else:
stats = dao_get_template_usage(service_id, limit_days=limit_days)
print(stats)
def serialize(data): def serialize(data):
return { return {
@@ -52,3 +62,18 @@ def get_template_statistics_for_template_id(service_id, template_id):
raise InvalidRequest(errors, status_code=404) raise InvalidRequest(errors, status_code=404)
data = notification_with_template_schema.dump(notification).data data = notification_with_template_schema.dump(notification).data
return jsonify(data=data) return jsonify(data=data)
def get_template_statistics_for_7_days(limit_days, service_id):
cache_key = "{}-template-counter-limit-7-days".format(service_id)
template_stats_by_id = redis_store.get_all_from_hash(cache_key)
if not template_stats_by_id:
print("populate cache")
stats = dao_get_template_usage(service_id, limit_days=limit_days)
cache_values = dict([(x.template_id, x.count) for x in stats])
redis_store.set_hash_and_expire(cache_key, cache_values, current_app.config.get('EXPIRE_CACHE_IN_SECONDS', 600))
current_app.logger.info('use redis-client: {}'.format(cache_key))
else:
print("template_stats_by_id: {}".format(template_stats_by_id))
stats = dao_get_templates_by_for_cache(template_stats_by_id.items())
return stats

View File

@@ -4,7 +4,8 @@ from app.dao.templates_dao import (
dao_get_template_by_id_and_service_id, dao_get_template_by_id_and_service_id,
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_by_for_cache)
from tests.app.conftest import sample_template as create_sample_template from tests.app.conftest import sample_template as create_sample_template
from app.models import Template, TemplateHistory from app.models import Template, TemplateHistory
import pytest import pytest
@@ -265,3 +266,54 @@ def test_get_template_versions(sample_template):
from app.schemas import template_history_schema from app.schemas import template_history_schema
v = template_history_schema.load(versions, many=True) v = template_history_schema.load(versions, many=True)
assert len(v) == 2 assert len(v) == 2
def test_get_templates_by_ids_successful(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"
)
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_by_for_cache(cache)
assert len(templates) == 2
assert [(template_1.id, template_1.template_type, template_1.name, 2),
(template_2.id, template_2.template_type, template_2.name, 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_by_for_cache(cache)
assert len(templates) == 1
assert [(template_1.id, template_1.template_type, template_1.name, 2)] == templates
def test_get_templates_by_ids_returns_empty_list():
assert dao_get_templates_by_for_cache({}) == []
assert dao_get_templates_by_for_cache(None) == []

View File

@@ -670,42 +670,41 @@ def test_should_persist_notification(notify_api, sample_template,
@pytest.mark.parametrize('template_type', @pytest.mark.parametrize('template_type',
['sms', 'email']) ['sms', 'email'])
def test_should_delete_notification_and_return_error_if_sqs_fails( def test_should_delete_notification_and_return_error_if_sqs_fails(
notify_api, client,
sample_email_template, sample_email_template,
sample_template, sample_template,
fake_uuid, fake_uuid,
mocker, mocker,
template_type): template_type):
with notify_api.test_request_context(), notify_api.test_client() as client: mocked = mocker.patch(
mocked = mocker.patch( 'app.celery.provider_tasks.deliver_{}.apply_async'.format(template_type),
'app.celery.provider_tasks.deliver_{}.apply_async'.format(template_type), side_effect=Exception("failed to talk to SQS")
side_effect=Exception("failed to talk to SQS") )
) mocker.patch('app.dao.notifications_dao.create_uuid', return_value=fake_uuid)
m1 = mocker.patch('app.dao.notifications_dao.create_uuid', return_value=fake_uuid) template = sample_template if template_type == 'sms' else sample_email_template
template = sample_template if template_type == 'sms' else sample_email_template to = sample_template.service.created_by.mobile_number if template_type == 'sms' \
to = sample_template.service.created_by.mobile_number if template_type == 'sms' \ else sample_email_template.service.created_by.email_address
else sample_email_template.service.created_by.email_address data = {
data = { 'to': to,
'to': to, 'template': template.id
'template': template.id }
} api_key = ApiKey(
api_key = ApiKey( service=template.service,
service=template.service, name='team_key',
name='team_key', created_by=template.created_by,
created_by=template.created_by, key_type=KEY_TYPE_TEAM)
key_type=KEY_TYPE_TEAM) save_model_api_key(api_key)
save_model_api_key(api_key) auth_header = create_jwt_token(secret=api_key.unsigned_secret, client_id=str(api_key.service_id))
auth_header = create_jwt_token(secret=api_key.unsigned_secret, client_id=str(api_key.service_id))
response = client.post( response = client.post(
path='/notifications/{}'.format(template_type), path='/notifications/{}'.format(template_type),
data=json.dumps(data), data=json.dumps(data),
headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))]) headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))])
mocked.assert_called_once_with([fake_uuid], queue='send-{}'.format(template_type)) mocked.assert_called_once_with([fake_uuid], queue='send-{}'.format(template_type))
assert response.status_code == 500 assert response.status_code == 500
assert not notifications_dao.get_notification_by_id(fake_uuid) assert not notifications_dao.get_notification_by_id(fake_uuid)
assert not NotificationHistory.query.get(fake_uuid) assert not NotificationHistory.query.get(fake_uuid)
@pytest.mark.parametrize('to_email', [ @pytest.mark.parametrize('to_email', [

View File

@@ -7,6 +7,7 @@ from sqlalchemy.exc import SQLAlchemyError
from freezegun import freeze_time from freezegun import freeze_time
from collections import namedtuple from collections import namedtuple
from app import cache_key_for_service_template_counter
from app.models import Template, Notification, NotificationHistory from app.models import Template, Notification, NotificationHistory
from app.notifications import SendNotificationToQueueError from app.notifications import SendNotificationToQueueError
from app.notifications.process_notifications import (create_content_for_notification, from app.notifications.process_notifications import (create_content_for_notification,
@@ -114,7 +115,8 @@ def test_exception_thown_by_redis_store_get_should_not_be_fatal(sample_template,
def test_cache_is_not_incremented_on_failure_to_persist_notification(sample_api_key, mocker): def test_cache_is_not_incremented_on_failure_to_persist_notification(sample_api_key, mocker):
mocked_redis = mocker.patch('app.notifications.process_notifications.redis_store.incr') mocked_redis = mocker.patch('app.redis_store.incr')
mocked_redis_hash = mocker.patch('app.redis_store.increment_hash_value')
with pytest.raises(SQLAlchemyError): with pytest.raises(SQLAlchemyError):
persist_notification(template_id=None, persist_notification(template_id=None,
template_version=None, template_version=None,
@@ -125,6 +127,7 @@ def test_cache_is_not_incremented_on_failure_to_persist_notification(sample_api_
api_key_id=sample_api_key.id, api_key_id=sample_api_key.id,
key_type=sample_api_key.key_type) key_type=sample_api_key.key_type)
mocked_redis.assert_not_called() mocked_redis.assert_not_called()
mocked_redis_hash.assert_not_called()
@freeze_time("2016-01-01 11:09:00.061258") @freeze_time("2016-01-01 11:09:00.061258")
@@ -132,6 +135,7 @@ def test_persist_notification_with_optionals(sample_job, sample_api_key, mocker)
assert Notification.query.count() == 0 assert Notification.query.count() == 0
assert NotificationHistory.query.count() == 0 assert NotificationHistory.query.count() == 0
mocked_redis = mocker.patch('app.notifications.process_notifications.redis_store.incr') mocked_redis = mocker.patch('app.notifications.process_notifications.redis_store.incr')
mocked_redis_hash = mocker.patch('app.notifications.process_notifications.redis_store.increment_hash_value')
n_id = uuid.uuid4() n_id = uuid.uuid4()
created_at = datetime.datetime(2016, 11, 11, 16, 8, 18) created_at = datetime.datetime(2016, 11, 11, 16, 8, 18)
persist_notification(template_id=sample_job.template.id, persist_notification(template_id=sample_job.template.id,
@@ -154,6 +158,8 @@ def test_persist_notification_with_optionals(sample_job, sample_api_key, mocker)
assert persisted_notification.job_row_number == 10 assert persisted_notification.job_row_number == 10
assert persisted_notification.created_at == created_at assert persisted_notification.created_at == created_at
mocked_redis.assert_called_once_with(str(sample_job.service_id) + "-2016-01-01-count") mocked_redis.assert_called_once_with(str(sample_job.service_id) + "-2016-01-01-count")
mocked_redis_hash.assert_called_once_with(cache_key_for_service_template_counter(sample_job.service_id),
sample_job.template.id)
assert persisted_notification.client_reference == "ref from client" assert persisted_notification.client_reference == "ref from client"
assert persisted_notification.reference is None assert persisted_notification.reference is None

View File

@@ -1,222 +1,227 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json import json
import pytest
from freezegun import freeze_time from freezegun import freeze_time
from tests import create_authorization_header from tests import create_authorization_header
from tests.app.conftest import sample_template as create_sample_template, sample_template, sample_notification, \ from tests.app.conftest import (sample_template as create_sample_template, sample_notification, sample_email_template)
sample_email_template
def test_get_all_template_statistics_with_bad_arg_returns_400(notify_api, sample_service): def test_get_all_template_statistics_with_bad_arg_returns_400(client, sample_service):
with notify_api.test_request_context(): auth_header = create_authorization_header()
with notify_api.test_client() as client:
auth_header = create_authorization_header()
response = client.get( response = client.get(
'/service/{}/template-statistics'.format(sample_service.id), '/service/{}/template-statistics'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header], headers=[('Content-Type', 'application/json'), auth_header],
query_string={'limit_days': 'blurk'} query_string={'limit_days': 'blurk'}
) )
assert response.status_code == 400 assert response.status_code == 400
json_resp = json.loads(response.get_data(as_text=True)) 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 json_resp['message'] == {'limit_days': ['blurk is not an integer']}
@freeze_time('2016-08-18') @freeze_time('2016-08-18')
def test_get_template_statistics_for_service(notify_db, notify_db_session, notify_api, sample_service): def test_get_template_statistics_for_service(notify_db, notify_db_session, client, mocker):
sms = sample_template(notify_db, notify_db_session, service=sample_service) email, sms = set_up_notifications(notify_db, notify_db_session)
email = sample_email_template(notify_db, notify_db_session, service=sample_service)
today = datetime.now()
sample_notification(notify_db, notify_db_session, created_at=today, service=sample_service, template=sms)
sample_notification(notify_db, notify_db_session, created_at=today, service=sample_service, template=sms)
sample_notification(notify_db, notify_db_session, created_at=today, service=sample_service, template=email)
sample_notification(notify_db, notify_db_session, created_at=today, service=sample_service, template=email)
with notify_api.test_request_context(): mocked_redis = mocker.patch('app.redis_store.get_all_from_hash')
with notify_api.test_client() as client:
auth_header = create_authorization_header()
response = client.get( auth_header = create_authorization_header()
'/service/{}/template-statistics'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header]
)
assert response.status_code == 200 response = client.get(
json_resp = json.loads(response.get_data(as_text=True)) '/service/{}/template-statistics'.format(email.service_id),
assert len(json_resp['data']) == 2 headers=[('Content-Type', 'application/json'), auth_header]
assert json_resp['data'][0]['count'] == 2 )
assert json_resp['data'][0]['template_id'] == str(email.id)
assert json_resp['data'][0]['template_name'] == email.name assert response.status_code == 200
assert json_resp['data'][0]['template_type'] == email.template_type json_resp = json.loads(response.get_data(as_text=True))
assert json_resp['data'][1]['count'] == 2 assert len(json_resp['data']) == 2
assert json_resp['data'][1]['template_id'] == str(sms.id) assert json_resp['data'][0]['count'] == 3
assert json_resp['data'][1]['template_name'] == sms.name assert json_resp['data'][0]['template_id'] == str(email.id)
assert json_resp['data'][1]['template_type'] == sms.template_type 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()
@freeze_time('2016-08-18') @freeze_time('2016-08-18')
def test_get_template_statistics_for_service_limited_by_day(notify_db, notify_db_session, notify_api, sample_service): def test_get_template_statistics_for_service_limited_1_day(notify_db, notify_db_session, client,
sms = sample_template(notify_db, notify_db_session, service=sample_service) mocker):
email = sample_email_template(notify_db, notify_db_session, service=sample_service) 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(
'/service/{}/template-statistics'.format(email.service_id),
headers=[('Content-Type', 'application/json'), auth_header],
query_string={'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('2'),
str.encode(str(email.id)): str.encode('2')} 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'] == 2
assert json_resp['data'][0]['template_name'] == 'Email Template Name'
assert json_resp['data'][1]['count'] == 2
assert json_resp['data'][1]['template_name'] == '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: 2, email.id: 2}, 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'] == 'Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == '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'] == 'Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == '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() today = datetime.now()
a_week_ago = datetime.now() - timedelta(days=7) a_week_ago = datetime.now() - timedelta(days=7)
a_month_ago = datetime.now() - timedelta(days=30) a_month_ago = datetime.now() - timedelta(days=30)
sample_notification(notify_db, notify_db_session, created_at=today, service=sample_service, template=sms) sample_notification(notify_db, notify_db_session, created_at=today, template=sms)
sample_notification(notify_db, notify_db_session, created_at=today, service=sample_service, template=email) sample_notification(notify_db, notify_db_session, created_at=today, template=email)
sample_notification(notify_db, notify_db_session, created_at=a_week_ago, service=sample_service, 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, service=sample_service, template=email) sample_notification(notify_db, notify_db_session, created_at=a_week_ago, template=email)
sample_notification(notify_db, notify_db_session, created_at=a_month_ago, service=sample_service, template=sms) 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, service=sample_service, template=email) sample_notification(notify_db, notify_db_session, created_at=a_month_ago, template=email)
return email, sms
with notify_api.test_request_context():
with notify_api.test_client() as client:
auth_header = create_authorization_header()
response = client.get(
'/service/{}/template-statistics'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header],
query_string={'limit_days': 1}
)
assert response.status_code == 200
json_resp = json.loads(response.get_data(as_text=True))
assert len(json_resp['data']) == 2
assert json_resp['data'][0]['count'] == 1
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'] == 1
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
response_for_a_week = client.get(
'/service/{}/template-statistics'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header],
query_string={'limit_days': 7}
)
assert response.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'] == 2
assert json_resp['data'][0]['template_name'] == 'Email Template Name'
assert json_resp['data'][1]['count'] == 2
assert json_resp['data'][1]['template_name'] == 'Template Name'
response_for_a_month = client.get(
'/service/{}/template-statistics'.format(sample_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'] == 'Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == 'Template Name'
response_for_all = client.get(
'/service/{}/template-statistics'.format(sample_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'] == 'Email Template Name'
assert json_resp['data'][1]['count'] == 3
assert json_resp['data'][1]['template_name'] == 'Template Name'
@freeze_time('2016-08-18') @freeze_time('2016-08-18')
def test_returns_empty_list_if_no_templates_used(notify_api, sample_service): def test_returns_empty_list_if_no_templates_used(client, sample_service):
with notify_api.test_request_context(): auth_header = create_authorization_header()
with notify_api.test_client() as client:
auth_header = create_authorization_header()
response = client.get( response = client.get(
'/service/{}/template-statistics'.format(sample_service.id), '/service/{}/template-statistics'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header] headers=[('Content-Type', 'application/json'), auth_header]
) )
assert response.status_code == 200 assert response.status_code == 200
json_resp = json.loads(response.get_data(as_text=True)) json_resp = json.loads(response.get_data(as_text=True))
assert len(json_resp['data']) == 0 assert len(json_resp['data']) == 0
def test_get_template_statistics_by_id_returns_last_notification( def test_get_template_statistics_by_id_returns_last_notification(
notify_db, notify_db,
notify_db_session, notify_db_session,
notify_api, client):
sample_service): sample_notification(notify_db, notify_db_session)
sample_notification(notify_db, notify_db_session)
notification_3 = sample_notification(notify_db, notify_db_session)
template = create_sample_template( auth_header = create_authorization_header()
notify_db,
notify_db_session, response = client.get(
template_name='Sample Template 1', '/service/{}/template-statistics/{}'.format(notification_3.service_id, notification_3.template_id),
service=sample_service headers=[('Content-Type', 'application/json'), auth_header],
) )
notification_1 = sample_notification( assert response.status_code == 200
notify_db, json_resp = json.loads(response.get_data(as_text=True))['data']
notify_db_session, assert json_resp['id'] == str(notification_3.id)
service=sample_service,
template=template)
notification_2 = sample_notification(
notify_db,
notify_db_session,
service=sample_service,
template=template)
notification_3 = sample_notification(
notify_db,
notify_db_session,
service=sample_service,
template=template)
with notify_api.test_request_context():
with notify_api.test_client() as client:
auth_header = create_authorization_header()
response = client.get(
'/service/{}/template-statistics/{}'.format(sample_service.id, template.id),
headers=[('Content-Type', 'application/json'), auth_header],
)
assert response.status_code == 200
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(
notify_db, client,
notify_db_session, sample_template,
notify_api,
sample_service
): ):
template = create_sample_template( auth_header = create_authorization_header()
notify_db,
notify_db_session, response = client.get(
template_name='Sample Template 1', '/service/{}/template-statistics/{}'.format(sample_template.service_id, sample_template.id),
service=sample_service headers=[('Content-Type', 'application/json'), auth_header],
) )
with notify_api.test_request_context(): assert response.status_code == 404
with notify_api.test_client() as client: json_resp = json.loads(response.get_data(as_text=True))
auth_header = create_authorization_header() assert json_resp['result'] == 'error'
assert json_resp['message']['template_id'] == ['No template found for id {}'.format(sample_template.id)]
response = client.get(
'/service/{}/template-statistics/{}'.format(sample_service.id, template.id),
headers=[('Content-Type', 'application/json'), auth_header],
)
assert response.status_code == 404
json_resp = json.loads(response.get_data(as_text=True))
assert json_resp['result'] == 'error'
assert json_resp['message']['template_id'] == ['No template found for id {}'.format(template.id)]