mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-04 18:31:13 -05:00
Merge pull request #699 from alphagov/monthly-billable-units
Add DAO and endpoint for getting billable units/financial year
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import uuid
|
||||
import pytz
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
date
|
||||
)
|
||||
from itertools import groupby
|
||||
|
||||
from flask import current_app
|
||||
from werkzeug.datastructures import MultiDict
|
||||
from sqlalchemy import (desc, func, or_, and_, asc)
|
||||
from sqlalchemy import (desc, func, or_, and_, asc, cast, Text)
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app import db
|
||||
@@ -209,6 +211,30 @@ def get_notifications_for_job(service_id, job_id, filter_dict=None, page=1, page
|
||||
)
|
||||
|
||||
|
||||
@statsd(namespace="dao")
|
||||
def get_notification_billable_unit_count_per_month(service_id, year):
|
||||
start, end = get_financial_year(year)
|
||||
|
||||
notifications = db.session.query(
|
||||
NotificationHistory.created_at,
|
||||
NotificationHistory.billable_units
|
||||
).order_by(
|
||||
NotificationHistory.created_at
|
||||
).filter(
|
||||
NotificationHistory.billable_units != 0,
|
||||
NotificationHistory.service_id == service_id,
|
||||
NotificationHistory.created_at >= start,
|
||||
NotificationHistory.created_at < end
|
||||
)
|
||||
|
||||
return [
|
||||
(month, sum(count for _, count in row))
|
||||
for month, row in groupby(
|
||||
notifications, lambda row: get_bst_month(row[0])
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@statsd(namespace="dao")
|
||||
def get_notification_with_personalisation(service_id, notification_id, key_type):
|
||||
filter_dict = {'service_id': service_id, 'id': notification_id}
|
||||
@@ -319,3 +345,20 @@ def dao_timeout_notifications(timeout_period_in_seconds):
|
||||
update({'status': NOTIFICATION_TEMPORARY_FAILURE, 'updated_at': update_at}, synchronize_session=False)
|
||||
db.session.commit()
|
||||
return updated
|
||||
|
||||
|
||||
def get_financial_year(year):
|
||||
return get_april_fools(year), get_april_fools(year + 1)
|
||||
|
||||
|
||||
def get_april_fools(year):
|
||||
return datetime(
|
||||
year, 4, 1, 0, 0, 0, 0,
|
||||
pytz.timezone("Europe/London")
|
||||
).astimezone(pytz.utc)
|
||||
|
||||
|
||||
def get_bst_month(datetime):
|
||||
return pytz.utc.localize(datetime).astimezone(
|
||||
pytz.timezone("Europe/London")
|
||||
).strftime('%B')
|
||||
|
||||
@@ -310,3 +310,13 @@ def update_whitelist(service_id):
|
||||
else:
|
||||
dao_add_and_commit_whitelisted_contacts(whitelist_objs)
|
||||
return '', 204
|
||||
|
||||
|
||||
@service_blueprint.route('/<uuid:service_id>/billable-units')
|
||||
def get_billable_unit_count(service_id):
|
||||
try:
|
||||
return jsonify(notifications_dao.get_notification_billable_unit_count_per_month(
|
||||
service_id, int(request.args.get('year'))
|
||||
))
|
||||
except TypeError:
|
||||
return jsonify(result='error', message='No valid year provided'), 400
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from datetime import datetime, timedelta, date
|
||||
import pytz
|
||||
import uuid
|
||||
from functools import partial
|
||||
|
||||
@@ -31,13 +32,15 @@ from app.dao.notifications_dao import (
|
||||
delete_notifications_created_more_than_a_week_ago,
|
||||
get_notification_by_id,
|
||||
get_notification_for_job,
|
||||
get_notification_billable_unit_count_per_month,
|
||||
get_notification_with_personalisation,
|
||||
get_notifications_for_job,
|
||||
get_notifications_for_service,
|
||||
update_notification_status_by_id,
|
||||
update_notification_status_by_reference,
|
||||
dao_delete_notifications_and_history_by_id,
|
||||
dao_timeout_notifications)
|
||||
dao_timeout_notifications,
|
||||
get_financial_year)
|
||||
|
||||
from notifications_utils.template import get_sms_fragment_count
|
||||
|
||||
@@ -685,6 +688,48 @@ def test_get_all_notifications_for_job_by_status(notify_db, notify_db_session, s
|
||||
assert len(notifications(filter_dict={'status': NOTIFICATION_STATUS_TYPES[:3]}).items) == 3
|
||||
|
||||
|
||||
def test_get_notification_billable_unit_count_per_month(notify_db, notify_db_session, sample_service):
|
||||
|
||||
for year, month, day in (
|
||||
(2017, 1, 15), # ↓ 2016 financial year
|
||||
(2016, 8, 1),
|
||||
(2016, 7, 15),
|
||||
(2016, 4, 15),
|
||||
(2016, 4, 15),
|
||||
(2016, 4, 1), # ↓ 2015 financial year
|
||||
(2016, 3, 31),
|
||||
(2016, 1, 15)
|
||||
):
|
||||
sample_notification(
|
||||
notify_db, notify_db_session, service=sample_service,
|
||||
created_at=datetime(
|
||||
year, month, day, 0, 0, 0, 0
|
||||
) - timedelta(hours=1, seconds=1) # one second before midnight
|
||||
)
|
||||
|
||||
for financial_year, months in (
|
||||
(
|
||||
2017,
|
||||
[]
|
||||
),
|
||||
(
|
||||
2016,
|
||||
[('April', 2), ('July', 2), ('January', 1)]
|
||||
),
|
||||
(
|
||||
2015,
|
||||
[('January', 1), ('March', 2)]
|
||||
),
|
||||
(
|
||||
2014,
|
||||
[]
|
||||
)
|
||||
):
|
||||
assert get_notification_billable_unit_count_per_month(
|
||||
sample_service.id, financial_year
|
||||
) == months
|
||||
|
||||
|
||||
def test_update_notification(sample_notification, sample_template):
|
||||
assert sample_notification.status == 'created'
|
||||
sample_notification.status = 'failed'
|
||||
@@ -1158,3 +1203,11 @@ def test_should_exclude_test_key_notifications_by_default(
|
||||
|
||||
all_notifications = get_notifications_for_service(sample_service.id, limit_days=1, key_type=KEY_TYPE_TEST).items
|
||||
assert len(all_notifications) == 1
|
||||
|
||||
|
||||
def test_get_financial_year():
|
||||
start, end = get_financial_year(2000)
|
||||
assert start.tzinfo == pytz.utc
|
||||
assert start.isoformat() == '2000-04-01T00:01:00+00:00'
|
||||
assert end.tzinfo == pytz.utc
|
||||
assert end.isoformat() == '2001-04-01T00:01:00+00:00'
|
||||
|
||||
@@ -1300,3 +1300,27 @@ def test_get_detailed_services_only_includes_todays_notifications(notify_db, not
|
||||
'email': {'delivered': 0, 'failed': 0, 'requested': 0},
|
||||
'sms': {'delivered': 0, 'failed': 0, 'requested': 2}
|
||||
}
|
||||
|
||||
|
||||
@freeze_time('2012-12-12T12:00:01')
|
||||
def test_get_notification_billable_unit_count(client, notify_db, notify_db_session):
|
||||
notification = create_sample_notification(notify_db, notify_db_session)
|
||||
response = client.get(
|
||||
'/service/{}/billable-units?year=2012'.format(notification.service_id),
|
||||
headers=[create_authorization_header(service_id=notification.service_id)]
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert json.loads(response.get_data(as_text=True)) == {
|
||||
'December': 1
|
||||
}
|
||||
|
||||
|
||||
def test_get_notification_billable_unit_count_missing_year(client, sample_service):
|
||||
response = client.get(
|
||||
'/service/{}/billable-units'.format(sample_service.id),
|
||||
headers=[create_authorization_header(service_id=sample_service.id)]
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert json.loads(response.get_data(as_text=True)) == {
|
||||
'message': 'No valid year provided', 'result': 'error'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user