Build rest endpoint to read service stats

- get stats by service id
- returns a list of stats objects

Not paginated - have 1 row per day.
This commit is contained in:
Martyn Inglis
2016-03-08 16:34:03 +00:00
parent f5f50e00ff
commit 67c4bd2263
8 changed files with 174 additions and 66 deletions

View File

@@ -49,6 +49,7 @@ def create_app():
from app.invite.rest import invite as invite_blueprint
from app.permission.rest import permission as permission_blueprint
from app.accept_invite.rest import accept_invite
from app.notifications_statistics.rest import notifications_statistics as notifications_statistics_blueprint
application.register_blueprint(service_blueprint, url_prefix='/service')
application.register_blueprint(user_blueprint, url_prefix='/user')
@@ -59,6 +60,7 @@ def create_app():
application.register_blueprint(invite_blueprint)
application.register_blueprint(permission_blueprint, url_prefix='/permission')
application.register_blueprint(accept_invite, url_prefix='/invite')
application.register_blueprint(notifications_statistics_blueprint)
return application

View File

@@ -1,34 +1,24 @@
from flask import current_app
from app import db
from app.models import Notification, Job, ServiceNotificationStats, TEMPLATE_TYPE_SMS, TEMPLATE_TYPE_EMAIL
from app.models import Notification, Job, NotificationStatistics, TEMPLATE_TYPE_SMS, TEMPLATE_TYPE_EMAIL
from sqlalchemy import desc
from datetime import datetime
def dao_get_notification_statistics_for_service(service_id):
return NotificationStatistics.query.filter_by(
service_id=service_id
).order_by(desc(NotificationStatistics.day)).all()
def dao_create_notification(notification, notification_type):
try:
if notification.job_id:
update_job_sent_count(notification)
day = datetime.utcnow().strftime('%Y-%m-%d')
if notification_type == TEMPLATE_TYPE_SMS:
update = {
ServiceNotificationStats.sms_requested: ServiceNotificationStats.sms_requested + 1
}
else:
update = {
ServiceNotificationStats.emails_requested: ServiceNotificationStats.emails_requested + 1
}
result = db.session.query(ServiceNotificationStats).filter_by(
day=day,
service_id=notification.service_id
).update(update)
if result == 0:
stats = ServiceNotificationStats(
day=day,
if update_notification_stats(notification, notification_type) == 0:
stats = NotificationStatistics(
day=notification.created_at.strftime('%Y-%m-%d'),
service_id=notification.service_id,
sms_requested=1 if notification_type == TEMPLATE_TYPE_SMS else 0,
emails_requested=1 if notification_type == TEMPLATE_TYPE_EMAIL else 0
@@ -41,6 +31,22 @@ def dao_create_notification(notification, notification_type):
raise
def update_notification_stats(notification, notification_type):
if notification_type == TEMPLATE_TYPE_SMS:
update = {
NotificationStatistics.sms_requested: NotificationStatistics.sms_requested + 1
}
else:
update = {
NotificationStatistics.emails_requested: NotificationStatistics.emails_requested + 1
}
return db.session.query(NotificationStatistics).filter_by(
day=notification.created_at.strftime('%Y-%m-%d'),
service_id=notification.service_id
).update(update)
def update_job_sent_count(notification):
db.session.query(Job).filter_by(
id=notification.job_id

View File

@@ -107,8 +107,8 @@ class ApiKey(db.Model):
)
class ServiceNotificationStats(db.Model):
__tablename__ = 'service_notification_stats'
class NotificationStatistics(db.Model):
__tablename__ = 'notification_statistics'
id = db.Column(db.Integer, primary_key=True)
day = db.Column(db.String(255), nullable=False)

View File

View File

@@ -0,0 +1,25 @@
from flask import (
Blueprint,
jsonify,
)
from app.dao.notifications_dao import (
dao_get_notification_statistics_for_service
)
from app.schemas import notifications_statistics_schema
notifications_statistics = Blueprint(
'notifications-statistics',
__name__, url_prefix='/service/<service_id>/notifications-statistics'
)
from app.errors import register_errors
register_errors(notifications_statistics)
@notifications_statistics.route('', methods=['GET'])
def get_all_templates_for_service(service_id):
templates = dao_get_notification_statistics_for_service(service_id=service_id)
data, errors = notifications_statistics_schema.dump(templates, many=True)
return jsonify(data=data)

View File

@@ -34,19 +34,6 @@ class BaseSchema(ma.ModelSchema):
assert key is not None, "Envelope key undefined"
return key
# Code to envelope the input and response.
# TOBE added soon.
# @pre_load(pass_many=True)
# def unwrap_envelope(self, data, many):
# key = self.get_envelope_key(many)
# return data[key]
# @post_dump(pass_many=True)
# def wrap_with_envelope(self, data, many):
# key = self.get_envelope_key(many)
# return {key: data}
@post_load
def make_instance(self, data):
"""Deserialize data to an instance of the model. Update an existing row
@@ -91,6 +78,11 @@ class TemplateSchema(BaseSchema):
exclude = ("updated_at", "created_at", "service_id", "jobs")
class NotificationsStatisticsSchema(BaseSchema):
class Meta:
model = models.NotificationStatistics
class ApiKeySchema(BaseSchema):
class Meta:
model = models.ApiKey
@@ -216,3 +208,4 @@ notification_status_schema = NotificationStatusSchema()
notification_status_schema_load_json = NotificationStatusSchema(load_json=True)
invited_user_schema = InvitedUserSchema()
permission_schema = PermissionSchema()
notifications_statistics_schema = NotificationsStatisticsSchema()

View File

@@ -15,8 +15,7 @@ import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('service_notification_stats',
op.create_table('notification_statistics',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('day', sa.String(length=255), nullable=False),
sa.Column('service_id', postgresql.UUID(as_uuid=True), nullable=False),
@@ -30,12 +29,9 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('service_id', 'day', name='uix_service_to_day')
)
op.create_index(op.f('ix_service_notification_stats_service_id'), 'service_notification_stats', ['service_id'], unique=False)
### end Alembic commands ###
op.create_index(op.f('ix_service_notification_stats_service_id'), 'notification_statistics', ['service_id'], unique=False)
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_service_notification_stats_service_id'), table_name='service_notification_stats')
op.drop_table('service_notification_stats')
### end Alembic commands ###
op.drop_index(op.f('ix_service_notification_stats_service_id'), table_name='notification_statistics')
op.drop_table('notification_statistics')

View File

@@ -1,20 +1,106 @@
import pytest
import uuid
from freezegun import freeze_time
import random
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
from app.models import Notification, Job, ServiceNotificationStats
from datetime import datetime
from app.models import Notification, Job, NotificationStatistics
from datetime import datetime, timedelta
from app.dao.notifications_dao import (
dao_create_notification,
dao_update_notification,
get_notification,
get_notification_for_job,
get_notifications_for_job
get_notifications_for_job,
dao_get_notification_statistics_for_service
)
from tests.app.conftest import sample_job
def test_should_be_able_to_get_statistics_for_a_service(sample_template):
data = {
'to': '+44709123456',
'service': sample_template.service,
'service_id': sample_template.service.id,
'template': sample_template,
'created_at': datetime.utcnow()
}
notification = Notification(**data)
dao_create_notification(notification, sample_template.template_type)
stats = dao_get_notification_statistics_for_service(sample_template.service.id)
assert len(stats) == 1
assert stats[0].emails_requested == 0
assert stats[0].sms_requested == 1
assert stats[0].day == notification.created_at.strftime('%Y-%m-%d')
assert stats[0].service_id == notification.service_id
def test_should_be_able_to_get_all_statistics_for_a_service(sample_template):
data = {
'to': '+44709123456',
'service': sample_template.service,
'service_id': sample_template.service.id,
'template': sample_template,
'created_at': datetime.utcnow()
}
notification_1 = Notification(**data)
notification_2 = Notification(**data)
notification_3 = Notification(**data)
dao_create_notification(notification_1, sample_template.template_type)
dao_create_notification(notification_2, sample_template.template_type)
dao_create_notification(notification_3, sample_template.template_type)
stats = dao_get_notification_statistics_for_service(sample_template.service.id)
assert len(stats) == 1
assert stats[0].emails_requested == 0
assert stats[0].sms_requested == 3
def test_should_be_able_to_get_all_statistics_for_a_service_for_several_days(sample_template):
data = {
'to': '+44709123456',
'service': sample_template.service,
'service_id': sample_template.service.id,
'template': sample_template
}
today = datetime.utcnow()
yesterday = datetime.utcnow() - timedelta(days=1)
two_days_ago = datetime.utcnow() - timedelta(days=2)
data.update({
'created_at': today
})
notification_1 = Notification(**data)
data.update({
'created_at': yesterday
})
notification_2 = Notification(**data)
data.update({
'created_at': two_days_ago
})
notification_3 = Notification(**data)
dao_create_notification(notification_1, sample_template.template_type)
dao_create_notification(notification_2, sample_template.template_type)
dao_create_notification(notification_3, sample_template.template_type)
stats = dao_get_notification_statistics_for_service(sample_template.service.id)
assert len(stats) == 3
assert stats[0].emails_requested == 0
assert stats[0].sms_requested == 1
assert stats[0].day == today.strftime('%Y-%m-%d')
assert stats[1].emails_requested == 0
assert stats[1].sms_requested == 1
assert stats[1].day == yesterday.strftime('%Y-%m-%d')
assert stats[2].emails_requested == 0
assert stats[2].sms_requested == 1
assert stats[2].day == two_days_ago.strftime('%Y-%m-%d')
def test_should_be_empty_list_if_no_statistics_for_a_service(sample_service):
assert len(dao_get_notification_statistics_for_service(sample_service.id)) == 0
def test_save_notification_and_create_sms_stats(sample_template, sample_job):
assert Notification.query.count() == 0
data = {
@@ -40,8 +126,8 @@ def test_save_notification_and_create_sms_stats(sample_template, sample_job):
assert 'sent' == notification_from_db.status
assert Job.query.get(sample_job.id).notifications_sent == 1
stats = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_template.service.id
stats = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_template.service.id
).first()
assert stats.emails_requested == 0
@@ -73,8 +159,8 @@ def test_save_notification_and_create_email_stats(sample_email_template, sample_
assert 'sent' == notification_from_db.status
assert Job.query.get(sample_job.id).notifications_sent == 1
stats = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_email_template.service.id
stats = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_email_template.service.id
).first()
assert stats.emails_requested == 1
@@ -98,8 +184,8 @@ def test_save_notification_handles_midnight_properly(sample_template, sample_job
assert Notification.query.count() == 1
stats = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_template.service.id
stats = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_template.service.id
).first()
assert stats.day == '2016-01-01'
@@ -122,8 +208,8 @@ def test_save_notification_handles_just_before_midnight_properly(sample_template
assert Notification.query.count() == 1
stats = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_template.service.id
stats = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_template.service.id
).first()
assert stats.day == '2016-01-01'
@@ -146,8 +232,8 @@ def test_save_notification_and_increment_email_stats(sample_email_template, samp
assert Notification.query.count() == 1
stats1 = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_email_template.service.id
stats1 = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_email_template.service.id
).first()
assert stats1.emails_requested == 1
@@ -157,8 +243,8 @@ def test_save_notification_and_increment_email_stats(sample_email_template, samp
assert Notification.query.count() == 2
stats2 = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_email_template.service.id
stats2 = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_email_template.service.id
).first()
assert stats2.emails_requested == 2
@@ -182,8 +268,8 @@ def test_save_notification_and_increment_sms_stats(sample_template, sample_job):
assert Notification.query.count() == 1
stats1 = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_template.service.id
stats1 = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_template.service.id
).first()
assert stats1.emails_requested == 0
@@ -193,8 +279,8 @@ def test_save_notification_and_increment_sms_stats(sample_template, sample_job):
assert Notification.query.count() == 2
stats2 = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_template.service.id
stats2 = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_template.service.id
).first()
assert stats2.emails_requested == 0
@@ -220,7 +306,7 @@ def test_not_save_notification_and_not_create_stats_on_commit_error(sample_templ
assert Notification.query.count() == 0
assert Job.query.get(sample_job.id).notifications_sent == 0
assert ServiceNotificationStats.query.count() == 0
assert NotificationStatistics.query.count() == 0
def test_save_notification_and_increment_job(sample_template, sample_job):
@@ -389,8 +475,8 @@ def test_get_all_notifications_for_job(notify_db, notify_db_session, sample_job)
notifcations_from_db = get_notifications_for_job(sample_job.service.id, sample_job.id).items
assert len(notifcations_from_db) == 5
stats = ServiceNotificationStats.query.filter(
ServiceNotificationStats.service_id == sample_job.service.id
stats = NotificationStatistics.query.filter(
NotificationStatistics.service_id == sample_job.service.id
).first()
assert stats.emails_requested == 0