From 4c37c8bdbb2ae74f0ae9b5b44d5b39930e785dd9 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 26 Apr 2017 15:31:25 +0100 Subject: [PATCH] New query to get billing data per month. --- app/dao/notification_usage_dao.py | 92 ++++++++++++++++---- tests/app/dao/test_notification_usage_dao.py | 66 +++++++++++++- 2 files changed, 140 insertions(+), 18 deletions(-) diff --git a/app/dao/notification_usage_dao.py b/app/dao/notification_usage_dao.py index fbd53681c..b1db43329 100644 --- a/app/dao/notification_usage_dao.py +++ b/app/dao/notification_usage_dao.py @@ -1,3 +1,4 @@ +from datetime import datetime from decimal import Decimal from sqlalchemy import func @@ -11,6 +12,7 @@ from app.models import (NotificationHistory, SMS_TYPE, EMAIL_TYPE) from app.statsd_decorators import statsd +from app.utils import get_london_month_from_utc_column @statsd(namespace="dao") @@ -31,21 +33,26 @@ def get_yearly_billing_data(service_id, year): def billing_data_filter(notification_type, start_date, end_date, service_id): - return [NotificationHistory.notification_type == notification_type, - NotificationHistory.created_at >= start_date, - NotificationHistory.created_at < end_date, - NotificationHistory.service_id == service_id, - NotificationHistory.status.in_(NOTIFICATION_STATUS_TYPES_BILLABLE), - NotificationHistory.key_type != KEY_TYPE_TEST - ] + return [ + NotificationHistory.notification_type == notification_type, + NotificationHistory.created_at >= start_date, + NotificationHistory.created_at < end_date, + NotificationHistory.service_id == service_id, + NotificationHistory.status.in_(NOTIFICATION_STATUS_TYPES_BILLABLE), + NotificationHistory.key_type != KEY_TYPE_TEST + ] def email_billing_data_query(service_id, start_date, end_date): - result = db.session.query(func.count(NotificationHistory.id), - NotificationHistory.notification_type, - "0" - ).filter(*billing_data_filter(EMAIL_TYPE, start_date, end_date, service_id) - ).group_by(NotificationHistory.notification_type).first() + result = db.session.query( + func.count(NotificationHistory.id), + NotificationHistory.notification_type, + "0" + ).filter( + *billing_data_filter(EMAIL_TYPE, start_date, end_date, service_id) + ).group_by( + NotificationHistory.notification_type + ).first() if not result: return 0, EMAIL_TYPE, Decimal("0") else: @@ -53,11 +60,15 @@ def email_billing_data_query(service_id, start_date, end_date): def sms_billing_data_query(rate, service_id, start_date, end_date): - result = db.session.query(func.sum(NotificationHistory.billable_units), - NotificationHistory.notification_type, - rate - ).filter(*billing_data_filter(SMS_TYPE, start_date, end_date, service_id) - ).group_by(NotificationHistory.notification_type).first() + result = db.session.query( + func.sum(NotificationHistory.billable_units), + NotificationHistory.notification_type, + rate + ).filter( + *billing_data_filter(SMS_TYPE, start_date, end_date, service_id) + ).group_by( + NotificationHistory.notification_type + ).first() if not result: return 0, SMS_TYPE, Decimal("0") else: @@ -67,3 +78,50 @@ def sms_billing_data_query(rate, service_id, start_date, end_date): def get_rates_for_year(start_date, end_date, notification_type): return Rate.query.filter(Rate.valid_from >= start_date, Rate.valid_from < end_date, Rate.notification_type == notification_type).order_by(Rate.valid_from).all() + + +def sms_billing_data_per_month_query(rate, service_id, start_date, end_date): + month = get_london_month_from_utc_column(NotificationHistory.created_at) + return db.session.query( + month, + func.sum(NotificationHistory.billable_units), + NotificationHistory.notification_type, + rate + ).filter( + *billing_data_filter(SMS_TYPE, start_date, end_date, service_id) + ).group_by( + NotificationHistory.notification_type, month + ).order_by( + month + ).all() + + +def email_billing_data_per_month_query(rate, service_id, start_date, end_date): + month = get_london_month_from_utc_column(NotificationHistory.created_at) + return db.session.query( + month, + func.count(NotificationHistory.id), + NotificationHistory.notification_type, + rate + ).filter( + *billing_data_filter(EMAIL_TYPE, start_date, end_date, service_id) + ).group_by( + NotificationHistory.notification_type, month + ).order_by( + month + ).all() + + +@statsd(namespace="dao") +def get_notification_billing_data_per_month(service_id, year): + start_date, end_date = get_financial_year(year) + rates = get_rates_for_year(start_date, end_date, SMS_TYPE) + + result = [] + for r, n in zip(rates, rates[1:]): + result.extend(sms_billing_data_per_month_query(str(r.rate), service_id, r.valid_from, n.valid_from)) + result.extend(sms_billing_data_per_month_query(str(rates[-1].rate), service_id, rates[-1].valid_from, end_date)) + + result.extend(email_billing_data_per_month_query("0", service_id, start_date, end_date)) + + return [(datetime.strftime(x[0], "%B"), x[1:]) for x in result] diff --git a/tests/app/dao/test_notification_usage_dao.py b/tests/app/dao/test_notification_usage_dao.py index 39525338b..0386d5f85 100644 --- a/tests/app/dao/test_notification_usage_dao.py +++ b/tests/app/dao/test_notification_usage_dao.py @@ -3,7 +3,8 @@ from datetime import datetime from decimal import Decimal -from app.dao.notification_usage_dao import (get_rates_for_year, get_yearly_billing_data) +from app.dao.notification_usage_dao import (get_rates_for_year, get_yearly_billing_data, + get_notification_billing_data_per_month) from app.models import Rate from tests.app.db import create_notification @@ -92,6 +93,69 @@ def test_get_yearly_billing_data_with_no_sms_notifications(notify_db, notify_db_ assert results[1] == (2, 'email', Decimal('0')) +def test_get_notification_billing_data_per_month(notify_db, notify_db_session, sample_template, sample_email_template): + set_up_rate(notify_db, datetime(2016, 4, 1), 1.40) + # previous year + create_notification(template=sample_template, created_at=datetime(2016, 3, 31), sent_at=datetime(2016, 3, 31), + status='sending', billable_units=1) + # current year + create_notification(template=sample_template, created_at=datetime(2016, 4, 2), sent_at=datetime(2016, 4, 2), + status='sending', billable_units=1) + create_notification(template=sample_template, created_at=datetime(2016, 5, 18), sent_at=datetime(2016, 5, 18), + status='sending', billable_units=2) + create_notification(template=sample_template, created_at=datetime(2016, 7, 22), sent_at=datetime(2016, 7, 22), + status='sending', billable_units=3) + create_notification(template=sample_template, created_at=datetime(2016, 7, 30), sent_at=datetime(2016, 7, 22), + status='sending', billable_units=4) + create_notification(template=sample_email_template, created_at=datetime(2016, 8, 22), sent_at=datetime(2016, 7, 22), + status='sending', billable_units=0) + create_notification(template=sample_email_template, created_at=datetime(2016, 8, 30), sent_at=datetime(2016, 7, 22), + status='sending', billable_units=0) + # next year + create_notification(template=sample_template, created_at=datetime(2017, 3, 31, 23, 00, 00), + sent_at=datetime(2017, 3, 31), status='sending', billable_units=6) + results = get_notification_billing_data_per_month(sample_template.service_id, 2016) + assert len(results) == 4 + assert results[0] == ('April', (1, 'sms', Decimal('1.4'))) + assert results[1] == ('May', (2, 'sms', Decimal('1.4'))) + assert results[2] == ('July', (7, 'sms', Decimal('1.4'))) + assert results[3] == ('August', (2, 'email', Decimal('0'))) + + +def test_get_notification_billing_data_per_month_with_multiple_rates(notify_db, notify_db_session, sample_template, + sample_email_template): + set_up_rate(notify_db, datetime(2016, 4, 1), 1.40) + set_up_rate(notify_db, datetime(2016, 6, 5), 1.75) + # previous year + create_notification(template=sample_template, created_at=datetime(2016, 3, 31), sent_at=datetime(2016, 3, 31), + status='sending', billable_units=1) + # current year + create_notification(template=sample_template, created_at=datetime(2016, 4, 2), sent_at=datetime(2016, 4, 2), + status='sending', billable_units=1) + create_notification(template=sample_template, created_at=datetime(2016, 5, 18), sent_at=datetime(2016, 5, 18), + status='sending', billable_units=2) + create_notification(template=sample_template, created_at=datetime(2016, 6, 1), sent_at=datetime(2016, 6, 1), + status='sending', billable_units=3) + create_notification(template=sample_template, created_at=datetime(2016, 6, 15), sent_at=datetime(2016, 6, 15), + status='sending', billable_units=4) + create_notification(template=sample_email_template, created_at=datetime(2016, 8, 22), + sent_at=datetime(2016, 7, 22), + status='sending', billable_units=0) + create_notification(template=sample_email_template, created_at=datetime(2016, 8, 30), + sent_at=datetime(2016, 7, 22), + status='sending', billable_units=0) + # next year + create_notification(template=sample_template, created_at=datetime(2017, 3, 31, 23, 00, 00), + sent_at=datetime(2017, 3, 31), status='sending', billable_units=6) + results = get_notification_billing_data_per_month(sample_template.service_id, 2016) + assert len(results) == 5 + assert results[0] == ('April', (1, 'sms', Decimal('1.4'))) + assert results[1] == ('May', (2, 'sms', Decimal('1.4'))) + assert results[2] == ('June', (3, 'sms', Decimal('1.4'))) + assert results[3] == ('June', (4, 'sms', Decimal('1.75'))) + assert results[4] == ('August', (2, 'email', Decimal('0'))) + + def set_up_rate(notify_db, start_date, value): rate = Rate(id=uuid.uuid4(), valid_from=start_date, rate=value, notification_type='sms') notify_db.session.add(rate)