mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-05 06:00:53 -04:00
Merge pull request #579 from alphagov/usage
get usage stats from notification_history
This commit is contained in:
@@ -20,7 +20,7 @@ from notifications_utils.recipients import (
|
||||
)
|
||||
|
||||
from app.dao.templates_dao import dao_get_template_by_id
|
||||
from notifications_utils.template import Template
|
||||
from notifications_utils.template import Template, get_sms_fragment_count
|
||||
from notifications_utils.renderers import HTMLEmail, PlainTextEmail, SMSMessage
|
||||
|
||||
from app.models import SMS_TYPE, EMAIL_TYPE, KEY_TYPE_TEST
|
||||
@@ -68,6 +68,7 @@ def send_sms_to_provider(self, service_id, notification_id):
|
||||
send_sms_response.apply_async(
|
||||
(provider.get_name(), str(notification_id), notification.to), queue='research-mode'
|
||||
)
|
||||
notification.billable_units = 0
|
||||
else:
|
||||
provider.send_sms(
|
||||
to=validate_and_format_phone_number(notification.to),
|
||||
@@ -75,17 +76,17 @@ def send_sms_to_provider(self, service_id, notification_id):
|
||||
reference=str(notification_id),
|
||||
sender=service.sms_sender
|
||||
)
|
||||
notification.billable_units = get_sms_fragment_count(template.replaced_content_count)
|
||||
|
||||
update_provider_stats(
|
||||
notification_id,
|
||||
SMS_TYPE,
|
||||
provider.get_name(),
|
||||
content_char_count=template.replaced_content_count
|
||||
billable_units=notification.billable_units
|
||||
)
|
||||
|
||||
notification.sent_at = datetime.utcnow()
|
||||
notification.sent_by = provider.get_name(),
|
||||
notification.content_char_count = template.replaced_content_count
|
||||
notification.sent_by = provider.get_name()
|
||||
notification.status = 'sending'
|
||||
dao_update_notification(notification)
|
||||
except SmsClientException as e:
|
||||
@@ -163,7 +164,8 @@ def send_email_to_provider(self, service_id, notification_id):
|
||||
update_provider_stats(
|
||||
notification_id,
|
||||
EMAIL_TYPE,
|
||||
provider.get_name()
|
||||
provider.get_name(),
|
||||
billable_units=1
|
||||
)
|
||||
notification.reference = reference
|
||||
notification.sent_at = datetime.utcnow()
|
||||
|
||||
@@ -299,30 +299,22 @@ def update_provider_stats(
|
||||
id_,
|
||||
notification_type,
|
||||
provider_name,
|
||||
content_char_count=None):
|
||||
billable_units=1):
|
||||
notification = Notification.query.filter(Notification.id == id_).one()
|
||||
provider = ProviderDetails.query.filter_by(identifier=provider_name).one()
|
||||
|
||||
def unit_count():
|
||||
if notification_type == EMAIL_TYPE:
|
||||
return 1
|
||||
else:
|
||||
if (content_char_count):
|
||||
return get_sms_fragment_count(content_char_count)
|
||||
return get_sms_fragment_count(notification.content_char_count)
|
||||
|
||||
update_count = db.session.query(ProviderStatistics).filter_by(
|
||||
day=date.today(),
|
||||
service_id=notification.service_id,
|
||||
provider_id=provider.id
|
||||
).update({'unit_count': ProviderStatistics.unit_count + unit_count()})
|
||||
).update({'unit_count': ProviderStatistics.unit_count + billable_units})
|
||||
|
||||
if update_count == 0:
|
||||
provider_stats = ProviderStatistics(
|
||||
day=notification.created_at.date(),
|
||||
service_id=notification.service_id,
|
||||
provider_id=provider.id,
|
||||
unit_count=unit_count()
|
||||
unit_count=billable_units
|
||||
)
|
||||
|
||||
db.session.add(provider_stats)
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
from sqlalchemy import func
|
||||
from app.models import (ProviderStatistics, SMS_PROVIDERS, EMAIL_PROVIDERS, ProviderDetails)
|
||||
from sqlalchemy import func, cast, Float, case
|
||||
|
||||
from app import db
|
||||
from app.models import (
|
||||
ProviderStatistics,
|
||||
ProviderDetails,
|
||||
NotificationHistory,
|
||||
SMS_TYPE,
|
||||
EMAIL_TYPE,
|
||||
NOTIFICATION_STATUS_TYPES_BILLABLE,
|
||||
KEY_TYPE_TEST
|
||||
)
|
||||
|
||||
|
||||
def get_provider_statistics(service, **kwargs):
|
||||
return filter_query(ProviderStatistics.query, service, **kwargs)
|
||||
|
||||
|
||||
def get_fragment_count(service, date_from, date_to):
|
||||
sms_query = filter_query(
|
||||
ProviderStatistics.query,
|
||||
service,
|
||||
providers=SMS_PROVIDERS,
|
||||
date_from=date_from,
|
||||
date_to=date_to
|
||||
)
|
||||
email_query = filter_query(
|
||||
ProviderStatistics.query,
|
||||
service,
|
||||
providers=EMAIL_PROVIDERS,
|
||||
date_from=date_from,
|
||||
date_to=date_to
|
||||
)
|
||||
return {
|
||||
'sms_count': int(sms_query.with_entities(
|
||||
func.sum(ProviderStatistics.unit_count)).scalar()) if sms_query.count() > 0 else 0,
|
||||
'email_count': int(email_query.with_entities(
|
||||
func.sum(ProviderStatistics.unit_count)).scalar()) if email_query.count() > 0 else 0
|
||||
}
|
||||
|
||||
|
||||
def filter_query(query, service, **kwargs):
|
||||
query = query.filter_by(service=service)
|
||||
query = ProviderStatistics.query.filter_by(service=service)
|
||||
if 'providers' in kwargs:
|
||||
providers = ProviderDetails.query.filter(ProviderDetails.identifier.in_(kwargs['providers'])).all()
|
||||
provider_ids = [provider.id for provider in providers]
|
||||
query = query.filter(ProviderStatistics.provider_id.in_(provider_ids))
|
||||
if 'date_from' in kwargs:
|
||||
query.filter(ProviderStatistics.day >= kwargs['date_from'])
|
||||
if 'date_to' in kwargs:
|
||||
query.filter(ProviderStatistics.day <= kwargs['date_to'])
|
||||
return query
|
||||
|
||||
|
||||
def get_fragment_count(service_id):
|
||||
shared_filters = [
|
||||
NotificationHistory.service_id == service_id,
|
||||
NotificationHistory.status.in_(NOTIFICATION_STATUS_TYPES_BILLABLE),
|
||||
NotificationHistory.key_type != KEY_TYPE_TEST
|
||||
]
|
||||
|
||||
sms_count = db.session.query(
|
||||
func.sum(NotificationHistory.billable_units)
|
||||
).filter(
|
||||
NotificationHistory.notification_type == SMS_TYPE,
|
||||
*shared_filters
|
||||
)
|
||||
|
||||
email_count = db.session.query(
|
||||
func.count(NotificationHistory.id)
|
||||
).filter(
|
||||
NotificationHistory.notification_type == EMAIL_TYPE,
|
||||
*shared_filters
|
||||
)
|
||||
return {
|
||||
'sms_count': int(sms_count.scalar() or 0),
|
||||
'email_count': email_count.scalar() or 0
|
||||
}
|
||||
|
||||
@@ -329,9 +329,34 @@ class VerifyCode(db.Model):
|
||||
def check_code(self, cde):
|
||||
return check_hash(cde, self._code)
|
||||
|
||||
NOTIFICATION_CREATED = 'created'
|
||||
NOTIFICATION_SENDING = 'sending'
|
||||
NOTIFICATION_DELIVERED = 'delivered'
|
||||
NOTIFICATION_PENDING = 'pending'
|
||||
NOTIFICATION_FAILED = 'failed'
|
||||
NOTIFICATION_TECHNICAL_FAILURE = 'technical-failure'
|
||||
NOTIFICATION_TEMPORARY_FAILURE = 'temporary-failure'
|
||||
NOTIFICATION_PERMANENT_FAILURE = 'permanent-failure'
|
||||
|
||||
NOTIFICATION_STATUS_TYPES = ['created', 'sending', 'delivered', 'pending', 'failed',
|
||||
'technical-failure', 'temporary-failure', 'permanent-failure']
|
||||
NOTIFICATION_STATUS_TYPES_BILLABLE = [
|
||||
NOTIFICATION_SENDING,
|
||||
NOTIFICATION_DELIVERED,
|
||||
NOTIFICATION_FAILED,
|
||||
NOTIFICATION_TECHNICAL_FAILURE,
|
||||
NOTIFICATION_TEMPORARY_FAILURE,
|
||||
NOTIFICATION_PERMANENT_FAILURE
|
||||
]
|
||||
|
||||
NOTIFICATION_STATUS_TYPES = [
|
||||
NOTIFICATION_CREATED,
|
||||
NOTIFICATION_SENDING,
|
||||
NOTIFICATION_DELIVERED,
|
||||
NOTIFICATION_PENDING,
|
||||
NOTIFICATION_FAILED,
|
||||
NOTIFICATION_TECHNICAL_FAILURE,
|
||||
NOTIFICATION_TEMPORARY_FAILURE,
|
||||
NOTIFICATION_PERMANENT_FAILURE
|
||||
]
|
||||
NOTIFICATION_STATUS_TYPES_ENUM = db.Enum(*NOTIFICATION_STATUS_TYPES, name='notify_status_type')
|
||||
|
||||
|
||||
@@ -352,11 +377,11 @@ class Notification(db.Model):
|
||||
api_key_id = db.Column(UUID(as_uuid=True), db.ForeignKey('api_keys.id'), index=True, unique=False)
|
||||
api_key = db.relationship('ApiKey')
|
||||
key_type = db.Column(db.String, db.ForeignKey('key_types.name'), index=True, unique=False, nullable=False)
|
||||
content_char_count = db.Column(db.Integer, nullable=True)
|
||||
notification_type = db.Column(notification_types, nullable=False)
|
||||
billable_units = db.Column(db.Integer, nullable=False, default=0)
|
||||
notification_type = db.Column(notification_types, index=True, nullable=False)
|
||||
created_at = db.Column(
|
||||
db.DateTime,
|
||||
index=False,
|
||||
index=True,
|
||||
unique=False,
|
||||
nullable=False)
|
||||
sent_at = db.Column(
|
||||
@@ -371,7 +396,7 @@ class Notification(db.Model):
|
||||
unique=False,
|
||||
nullable=True,
|
||||
onupdate=datetime.datetime.utcnow)
|
||||
status = db.Column(NOTIFICATION_STATUS_TYPES_ENUM, nullable=False, default='created')
|
||||
status = db.Column(NOTIFICATION_STATUS_TYPES_ENUM, index=True, nullable=False, default='created')
|
||||
reference = db.Column(db.String, nullable=True, index=True)
|
||||
_personalisation = db.Column(db.String, nullable=True)
|
||||
|
||||
@@ -402,13 +427,13 @@ class NotificationHistory(db.Model):
|
||||
api_key_id = db.Column(UUID(as_uuid=True), db.ForeignKey('api_keys.id'), index=True, unique=False)
|
||||
api_key = db.relationship('ApiKey')
|
||||
key_type = db.Column(db.String, db.ForeignKey('key_types.name'), index=True, unique=False, nullable=False)
|
||||
content_char_count = db.Column(db.Integer, nullable=True)
|
||||
notification_type = db.Column(notification_types, nullable=False)
|
||||
created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
|
||||
billable_units = db.Column(db.Integer, nullable=False, default=0)
|
||||
notification_type = db.Column(notification_types, index=True, nullable=False)
|
||||
created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False)
|
||||
sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
|
||||
sent_by = db.Column(db.String, nullable=True)
|
||||
updated_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
|
||||
status = db.Column(NOTIFICATION_STATUS_TYPES_ENUM, nullable=False, default='created')
|
||||
status = db.Column(NOTIFICATION_STATUS_TYPES_ENUM, index=True, nullable=False, default='created')
|
||||
reference = db.Column(db.String, nullable=True, index=True)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -168,13 +168,7 @@ def remove_user_from_service(service_id, user_id):
|
||||
|
||||
@service.route('/<uuid:service_id>/fragment/aggregate_statistics')
|
||||
def get_service_provider_aggregate_statistics(service_id):
|
||||
service = dao_fetch_service_by_id(service_id)
|
||||
data = from_to_date_schema.load(request.args).data
|
||||
return jsonify(data=get_fragment_count(
|
||||
service,
|
||||
date_from=(data.pop('date_from') if 'date_from' in data else date.today()),
|
||||
date_to=(data.pop('date_to') if 'date_to' in data else date.today())
|
||||
))
|
||||
return jsonify(data=get_fragment_count(service_id))
|
||||
|
||||
|
||||
# This is placeholder get method until more thought
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from flask import jsonify, current_app, Blueprint
|
||||
from flask import jsonify, Blueprint
|
||||
from apispec import APISpec
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ api_spec = APISpec(
|
||||
)
|
||||
|
||||
api_spec.definition('NotificationWithTemplateSchema', properties={
|
||||
"content_char_count": {
|
||||
"billable_units": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
|
||||
101
migrations/versions/0045_billable_units.py
Normal file
101
migrations/versions/0045_billable_units.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 0045_billable_units
|
||||
Revises: 0044_jobs_to_notification_hist
|
||||
Create Date: 2016-08-02 16:36:42.455838
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0045_billable_units'
|
||||
down_revision = '0044_jobs_to_notification_hist'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from app.models import Service
|
||||
|
||||
def upgrade():
|
||||
op.add_column('notifications', sa.Column('billable_units', sa.Integer()))
|
||||
op.add_column('notification_history', sa.Column('billable_units', sa.Integer()))
|
||||
|
||||
op.execute('update notifications set billable_units = 0')
|
||||
op.execute('update notification_history set billable_units = 0')
|
||||
|
||||
op.alter_column('notifications', 'billable_units', nullable=False)
|
||||
op.alter_column('notification_history', 'billable_units', nullable=False)
|
||||
|
||||
|
||||
conn = op.get_bind()
|
||||
|
||||
# caveats
|
||||
# only adjusts notifications for services that have never been in research mode. On live, research mode was
|
||||
# limited to only services that we have set up ourselves so deemed this acceptable.
|
||||
billable_services = conn.execute('''
|
||||
SELECT id FROM services_history WHERE id not in (select id from services_history where research_mode)
|
||||
''')
|
||||
# set to 'null' if there are no billable services so we don't get a syntax error in the update statement
|
||||
service_ids = ','.join("'{}'".format(service.id) for service in billable_services) or 'null'
|
||||
|
||||
|
||||
update_statement = '''
|
||||
UPDATE {}
|
||||
SET billable_units = (
|
||||
CASE
|
||||
WHEN content_char_count <= 160 THEN 1
|
||||
ELSE ceil(content_char_count::float / 153::float)
|
||||
END
|
||||
)
|
||||
WHERE content_char_count is not null
|
||||
AND service_id in ({})
|
||||
AND notification_type = 'sms'
|
||||
'''
|
||||
|
||||
conn = op.get_bind()
|
||||
conn.execute(update_statement.format('notifications', service_ids))
|
||||
conn.execute(update_statement.format('notification_history', service_ids))
|
||||
op.drop_column('notifications', 'content_char_count')
|
||||
op.drop_column('notification_history', 'content_char_count')
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.add_column('notifications', sa.Column(
|
||||
'content_char_count',
|
||||
sa.INTEGER(),
|
||||
autoincrement=False,
|
||||
nullable=True)
|
||||
)
|
||||
op.add_column('notification_history', sa.Column(
|
||||
'content_char_count',
|
||||
sa.INTEGER(),
|
||||
autoincrement=False,
|
||||
nullable=True)
|
||||
)
|
||||
|
||||
conn = op.get_bind()
|
||||
|
||||
# caveats
|
||||
# only adjusts notifications for services that have never been in research mode. On live, research mode was
|
||||
# limited to only services that we have set up ourselves
|
||||
billable_services = conn.execute('''
|
||||
SELECT id FROM services_history WHERE id not in (select id from services_history where research_mode)
|
||||
''')
|
||||
# set to 'null' if there are no billable services so we don't get a syntax error in the update statement
|
||||
service_ids = ','.join("'{}'".format(service.id) for service in billable_services) or 'null'
|
||||
|
||||
# caveats:
|
||||
# only approximates character counts - billable * 153 to get at least a decent ballpark
|
||||
# research mode messages assumed to be one message length
|
||||
update_statement = '''
|
||||
UPDATE {}
|
||||
SET content_char_count = GREATEST(billable_units, 1) * 150
|
||||
WHERE service_id in ({})
|
||||
AND notification_type = 'sms'
|
||||
'''
|
||||
|
||||
conn = op.get_bind()
|
||||
conn.execute(update_statement.format('notifications', service_ids))
|
||||
conn.execute(update_statement.format('notification_history', service_ids))
|
||||
op.drop_column('notifications', 'billable_units')
|
||||
op.drop_column('notification_history', 'billable_units')
|
||||
@@ -111,7 +111,7 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist(
|
||||
assert notification.status == 'sending'
|
||||
assert notification.sent_at <= datetime.utcnow()
|
||||
assert notification.sent_by == 'mmg'
|
||||
assert notification.content_char_count == len("Sample service: Hello Jo\nYour thing is due soon")
|
||||
assert notification.billable_units == 1
|
||||
assert notification.personalisation == {"name": "Jo"}
|
||||
|
||||
|
||||
@@ -194,7 +194,6 @@ def test_send_sms_should_use_template_version_from_notification_not_latest(
|
||||
assert persisted_notification.template_id == sample_template.id
|
||||
assert persisted_notification.template_version == version_on_notification
|
||||
assert persisted_notification.template_version != sample_template.version
|
||||
assert persisted_notification.content_char_count == len("Sample service: This is a template:\nwith a newline")
|
||||
assert persisted_notification.status == 'sending'
|
||||
assert not persisted_notification.personalisation
|
||||
|
||||
@@ -546,3 +545,30 @@ def test_send_email_should_use_service_reply_to_email(
|
||||
html_body=ANY,
|
||||
reply_to_address=sample_service.reply_to_email_address
|
||||
)
|
||||
|
||||
|
||||
def test_should_not_set_billable_units_if_research_mode(notify_db, sample_service, sample_notification, mocker):
|
||||
mocker.patch('app.mmg_client.send_sms')
|
||||
mocker.patch('app.mmg_client.get_name', return_value="mmg")
|
||||
mocker.patch('app.celery.research_mode_tasks.send_sms_response.apply_async')
|
||||
|
||||
sample_service.research_mode = True
|
||||
notify_db.session.add(sample_service)
|
||||
notify_db.session.commit()
|
||||
|
||||
send_sms_to_provider(
|
||||
sample_notification.service_id,
|
||||
sample_notification.id
|
||||
)
|
||||
|
||||
persisted_notification = notifications_dao.get_notification(sample_service.id, sample_notification.id)
|
||||
assert persisted_notification.billable_units == 0
|
||||
|
||||
|
||||
def _get_provider_statistics(service, **kwargs):
|
||||
query = ProviderStatistics.query.filter_by(service=service)
|
||||
if 'providers' in kwargs:
|
||||
providers = ProviderDetails.query.filter(ProviderDetails.identifier.in_(kwargs['providers'])).all()
|
||||
provider_ids = [provider.id for provider in providers]
|
||||
query = query.filter(ProviderStatistics.provider_id.in_(provider_ids))
|
||||
return query
|
||||
|
||||
@@ -323,7 +323,7 @@ def sample_notification(notify_db,
|
||||
status='created',
|
||||
reference=None,
|
||||
created_at=None,
|
||||
content_char_count=160,
|
||||
billable_units=1,
|
||||
create=True,
|
||||
personalisation=None,
|
||||
api_key_id=None,
|
||||
@@ -356,7 +356,7 @@ def sample_notification(notify_db,
|
||||
'status': status,
|
||||
'reference': reference,
|
||||
'created_at': created_at,
|
||||
'content_char_count': content_char_count,
|
||||
'billable_units': billable_units,
|
||||
'personalisation': personalisation,
|
||||
'notification_type': template.template_type,
|
||||
'api_key_id': api_key_id,
|
||||
|
||||
@@ -1028,7 +1028,7 @@ def _notification_json(sample_template, job_id=None, id=None, status=None):
|
||||
'template_id': sample_template.id,
|
||||
'template_version': sample_template.version,
|
||||
'created_at': datetime.utcnow(),
|
||||
'content_char_count': 160,
|
||||
'billable_units': 1,
|
||||
'notification_type': sample_template.template_type,
|
||||
'key_type': KEY_TYPE_NORMAL
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from datetime import (date, timedelta)
|
||||
from app.models import ProviderStatistics
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models import NotificationHistory, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, NOTIFICATION_STATUS_TYPES
|
||||
from app.dao.notifications_dao import update_provider_stats
|
||||
from app.dao.provider_statistics_dao import (
|
||||
get_provider_statistics, get_fragment_count)
|
||||
from app.dao.provider_statistics_dao import get_provider_statistics, get_fragment_count
|
||||
from tests.app.conftest import sample_notification as create_sample_notification
|
||||
|
||||
|
||||
@@ -44,24 +47,24 @@ def test_should_update_provider_statistics_sms_multi(notify_db,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_template,
|
||||
content_char_count=160)
|
||||
update_provider_stats(n1.id, 'sms', mmg_provider.identifier)
|
||||
billable_units=1)
|
||||
update_provider_stats(n1.id, 'sms', mmg_provider.identifier, n1.billable_units)
|
||||
n2 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_template,
|
||||
content_char_count=161)
|
||||
update_provider_stats(n2.id, 'sms', mmg_provider.identifier)
|
||||
billable_units=2)
|
||||
update_provider_stats(n2.id, 'sms', mmg_provider.identifier, n2.billable_units)
|
||||
n3 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_template,
|
||||
content_char_count=307)
|
||||
update_provider_stats(n3.id, 'sms', mmg_provider.identifier)
|
||||
billable_units=4)
|
||||
update_provider_stats(n3.id, 'sms', mmg_provider.identifier, n3.billable_units)
|
||||
provider_stats = get_provider_statistics(
|
||||
sample_template.service,
|
||||
providers=[mmg_provider.identifier]).one()
|
||||
assert provider_stats.unit_count == 6
|
||||
assert provider_stats.unit_count == 7
|
||||
|
||||
|
||||
def test_should_update_provider_statistics_email_multi(notify_db,
|
||||
@@ -89,63 +92,66 @@ def test_should_update_provider_statistics_email_multi(notify_db,
|
||||
assert provider_stats.unit_count == 3
|
||||
|
||||
|
||||
def test_should_aggregate_fragment_count(notify_db,
|
||||
notify_db_session,
|
||||
sample_service,
|
||||
mmg_provider,
|
||||
firetext_provider,
|
||||
ses_provider):
|
||||
day = date.today()
|
||||
stats_mmg = ProviderStatistics(
|
||||
service=sample_service,
|
||||
day=day,
|
||||
provider_id=mmg_provider.id,
|
||||
unit_count=2
|
||||
)
|
||||
def test_get_fragment_count_with_no_data(sample_template):
|
||||
assert get_fragment_count(sample_template.service_id)['sms_count'] == 0
|
||||
assert get_fragment_count(sample_template.service_id)['email_count'] == 0
|
||||
|
||||
stats_firetext = ProviderStatistics(
|
||||
service=sample_service,
|
||||
day=day,
|
||||
provider_id=firetext_provider.id,
|
||||
unit_count=3
|
||||
)
|
||||
|
||||
stats_ses = ProviderStatistics(
|
||||
service=sample_service,
|
||||
day=day,
|
||||
provider_id=ses_provider.id,
|
||||
unit_count=1
|
||||
def test_get_fragment_count_separates_sms_and_email(notify_db, sample_template, sample_email_template):
|
||||
noti_hist(notify_db, sample_template)
|
||||
noti_hist(notify_db, sample_template)
|
||||
noti_hist(notify_db, sample_email_template)
|
||||
assert get_fragment_count(sample_template.service_id) == {
|
||||
'sms_count': 2,
|
||||
'email_count': 1
|
||||
}
|
||||
|
||||
|
||||
def test_get_fragment_count_filters_on_status(notify_db, sample_template):
|
||||
for status in NOTIFICATION_STATUS_TYPES:
|
||||
noti_hist(notify_db, sample_template, status=status)
|
||||
# sending, delivered, failed, technical-failure, temporary-failure, permanent-failure
|
||||
assert get_fragment_count(sample_template.service_id)['sms_count'] == 6
|
||||
|
||||
|
||||
def test_get_fragment_count_filters_on_service_id(notify_db, sample_template, service_factory):
|
||||
service_2 = service_factory.get('service 2', email_from='service.2')
|
||||
noti_hist(notify_db, sample_template)
|
||||
assert get_fragment_count(service_2.id)['sms_count'] == 0
|
||||
|
||||
|
||||
def test_get_fragment_count_sums_billable_units_for_sms(notify_db, sample_template):
|
||||
noti_hist(notify_db, sample_template, billable_units=1)
|
||||
noti_hist(notify_db, sample_template, billable_units=2)
|
||||
assert get_fragment_count(sample_template.service_id)['sms_count'] == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize('key_type,sms_count', [
|
||||
(KEY_TYPE_NORMAL, 1),
|
||||
(KEY_TYPE_TEAM, 1),
|
||||
(KEY_TYPE_TEST, 0),
|
||||
])
|
||||
def test_get_fragment_count_ignores_test_api_keys(notify_db, sample_template, key_type, sms_count):
|
||||
noti_hist(notify_db, sample_template, key_type=key_type)
|
||||
assert get_fragment_count(sample_template.service_id)['sms_count'] == sms_count
|
||||
|
||||
|
||||
def noti_hist(notify_db, template, status='delivered', billable_units=None, key_type=KEY_TYPE_NORMAL):
|
||||
if not billable_units and template.template_type == 'sms':
|
||||
billable_units = 1
|
||||
|
||||
notification_history = NotificationHistory(
|
||||
id=uuid.uuid4(),
|
||||
service=template.service,
|
||||
template=template,
|
||||
template_version=template.version,
|
||||
status=status,
|
||||
created_at=datetime.utcnow(),
|
||||
billable_units=billable_units,
|
||||
notification_type=template.template_type,
|
||||
key_type=key_type
|
||||
)
|
||||
notify_db.session.add(stats_mmg)
|
||||
notify_db.session.add(stats_firetext)
|
||||
notify_db.session.add(stats_ses)
|
||||
notify_db.session.add(notification_history)
|
||||
notify_db.session.commit()
|
||||
results = get_fragment_count(sample_service, day, day)
|
||||
assert results['sms_count'] == 5
|
||||
assert results['email_count'] == 1
|
||||
|
||||
|
||||
def test_should_aggregate_fragment_count_over_days(notify_db,
|
||||
notify_db_session,
|
||||
sample_service,
|
||||
mmg_provider):
|
||||
today = date.today()
|
||||
yesterday = today - timedelta(days=1)
|
||||
stats_today = ProviderStatistics(
|
||||
service=sample_service,
|
||||
day=today,
|
||||
provider_id=mmg_provider.id,
|
||||
unit_count=2
|
||||
)
|
||||
stats_yesterday = ProviderStatistics(
|
||||
service=sample_service,
|
||||
day=yesterday,
|
||||
provider_id=mmg_provider.id,
|
||||
unit_count=3
|
||||
)
|
||||
notify_db.session.add(stats_today)
|
||||
notify_db.session.add(stats_yesterday)
|
||||
notify_db.session.commit()
|
||||
results = get_fragment_count(sample_service, yesterday, today)
|
||||
assert results['sms_count'] == 5
|
||||
assert results['email_count'] == 0
|
||||
return notification_history
|
||||
@@ -1,77 +0,0 @@
|
||||
import json
|
||||
from datetime import (date, timedelta)
|
||||
from flask import url_for
|
||||
from tests import create_authorization_header
|
||||
|
||||
|
||||
def test_fragment_count(notify_api, sample_provider_statistics):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
endpoint = url_for(
|
||||
'service.get_service_provider_aggregate_statistics',
|
||||
service_id=str(sample_provider_statistics.service.id))
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.get(
|
||||
endpoint,
|
||||
headers=[auth_header]
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert json_resp['data']['sms_count'] == 1
|
||||
|
||||
|
||||
def test_fragment_count_from_to(notify_api, sample_provider_statistics):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
today_str = date.today().strftime('%Y-%m-%d')
|
||||
endpoint = url_for(
|
||||
'service.get_service_provider_aggregate_statistics',
|
||||
service_id=str(sample_provider_statistics.service.id),
|
||||
date_from=today_str,
|
||||
date_to=today_str)
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.get(
|
||||
endpoint,
|
||||
headers=[auth_header]
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert json_resp['data']['sms_count'] == 1
|
||||
|
||||
|
||||
def test_fragment_count_from_greater_than_to(notify_api, sample_provider_statistics):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
today_str = date.today().strftime('%Y-%m-%d')
|
||||
yesterday_str = date.today() - timedelta(days=1)
|
||||
endpoint = url_for(
|
||||
'service.get_service_provider_aggregate_statistics',
|
||||
service_id=str(sample_provider_statistics.service.id),
|
||||
date_from=today_str,
|
||||
date_to=yesterday_str)
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.get(
|
||||
endpoint,
|
||||
headers=[auth_header]
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert 'date_from needs to be greater than date_to' in json_resp['message']['_schema']
|
||||
|
||||
|
||||
def test_fragment_count_in_future(notify_api, sample_provider_statistics):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
tomorrow_str = (date.today() + timedelta(days=1)).strftime('%Y-%m-%d')
|
||||
endpoint = url_for(
|
||||
'service.get_service_provider_aggregate_statistics',
|
||||
service_id=str(sample_provider_statistics.service.id),
|
||||
date_from=tomorrow_str)
|
||||
auth_header = create_authorization_header()
|
||||
resp = client.get(
|
||||
endpoint,
|
||||
headers=[auth_header]
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert 'Date cannot be in the future' in json_resp['message']['date_from']
|
||||
Reference in New Issue
Block a user