Merge pull request #1255 from alphagov/add-international-usage-stats

Add international usage stats
This commit is contained in:
kentsanggds
2017-05-02 16:21:17 +01:00
committed by GitHub
4 changed files with 207 additions and 53 deletions

View File

@@ -113,7 +113,7 @@ def usage(service_id):
start=current_financial_year - 1,
end=current_financial_year + 1,
),
**calculate_usage(service_api_client.get_service_usage(service_id, year)['data'])
**calculate_usage(service_api_client.get_service_usage(service_id, year))
)
@@ -189,7 +189,7 @@ def get_dashboard_partials(service_id):
**calculate_usage(service_api_client.get_service_usage(
service_id,
get_current_financial_year(),
)['data'])
))
),
}
@@ -204,10 +204,11 @@ def get_dashboard_totals(statistics):
def calculate_usage(usage):
# TODO: Don't hardcode these - get em from the API
sms_free_allowance = 250000
sms_rate = 0.0165
sms_sent = usage.get('sms_count', 0)
emails_sent = usage.get('email_count', 0)
sms_rate = 0 if len(usage) == 0 else usage[0].get("rate", 0)
sms_sent = get_sum_billing_units(breakdown for breakdown in usage if breakdown['notification_type'] == 'sms')
emails = [breakdown["billing_units"] for breakdown in usage if breakdown['notification_type'] == 'email']
emails_sent = 0 if len(emails) == 0 else emails[0]
return {
'emails_sent': emails_sent,
@@ -215,7 +216,7 @@ def calculate_usage(usage):
'sms_sent': sms_sent,
'sms_allowance_remaining': max(0, (sms_free_allowance - sms_sent)),
'sms_chargeable': max(0, sms_sent - sms_free_allowance),
'sms_rate': sms_rate
'sms_rate': sms_rate,
}
@@ -262,14 +263,21 @@ def get_months_for_year(start, end, year):
return [datetime(year, month, 1) for month in range(start, end)]
def get_free_paid_breakdown_for_billable_units(year, billable_units):
def get_sum_billing_units(billing_units, month=None):
if month:
return sum(b['billing_units'] * b.get('rate_multiplier', 1) for b in billing_units if b['month'] == month)
return sum(b['billing_units'] * b.get('rate_multiplier', 1) for b in billing_units)
def get_free_paid_breakdown_for_billable_units(year, billing_units):
cumulative = 0
for month in get_months_for_financial_year(year):
previous_cumulative = cumulative
monthly_usage = billable_units.get(month, 0)
monthly_usage = get_sum_billing_units(billing_units, month)
cumulative += monthly_usage
breakdown = get_free_paid_breakdown_for_month(
cumulative, previous_cumulative, monthly_usage
cumulative, previous_cumulative,
[billing_month for billing_month in billing_units if billing_month['month'] == month]
)
yield {
'name': month,
@@ -285,20 +293,23 @@ def get_free_paid_breakdown_for_month(
):
allowance = 250000
total_monthly_billing_units = get_sum_billing_units(monthly_usage)
if cumulative < allowance:
return {
'paid': 0,
'free': monthly_usage,
'free': total_monthly_billing_units,
}
elif previous_cumulative < allowance:
remaining_allowance = allowance - previous_cumulative
return {
'paid': monthly_usage - (allowance - previous_cumulative),
'free': allowance - previous_cumulative
'paid': total_monthly_billing_units - remaining_allowance,
'free': remaining_allowance,
}
else:
return {
'paid': monthly_usage,
'free': 0
'paid': total_monthly_billing_units,
'free': 0,
}

View File

@@ -217,7 +217,7 @@ class ServiceAPIClient(NotifyAdminAPIClient):
def get_service_usage(self, service_id, year=None):
return self.get(
'/service/{0}/fragment/aggregate_statistics'.format(service_id),
'/service/{0}/yearly-usage'.format(service_id),
params=dict(year=year)
)
@@ -231,7 +231,10 @@ class ServiceAPIClient(NotifyAdminAPIClient):
return self.put(url='/service/{}/whitelist'.format(service_id), data=data)
def get_billable_units(self, service_id, year):
return self.get(url='/service/{}/billable-units?year={}'.format(service_id, year))
return self.get(
'/service/{0}/monthly-usage'.format(service_id),
params=dict(year=year)
)
class ServicesBrowsableItem(BrowsableItem):

View File

@@ -223,21 +223,22 @@ def test_usage_page(
assert normalize_spaces(nav_links[0].text) == '2010 to 2011 financial year'
assert normalize_spaces(nav.find('li', {'aria-selected': 'true'}).text) == '2011 to 2012 financial year'
assert normalize_spaces(nav_links[1].text) == '2012 to 2013 financial year'
assert '123' in cols[0].text
assert 'Emails' in cols[0].text
assert '456,123' in cols[1].text
assert '252,190' in cols[1].text
assert 'Text messages' in cols[1].text
table = page.find('table').text.strip()
assert '249,860 free text messages' in table
assert '40 free text messages' in table
assert '960 text messages at 1.65p' in table
assert 'April' in table
assert 'February' in table
assert 'March' in table
assert '123 free text messages' in table
assert '£3,403.06' in table
assert '249,877 free text messages' in table
assert '206,246 text messages at 1.65p' in table
assert '£15.84' in table
assert '140 free text messages' in table
assert '£20.30' in table
assert '1,230 text messages at 1.65p' in table
def test_usage_page_with_year_argument(
@@ -256,6 +257,18 @@ def test_usage_page_for_invalid_year(
assert logged_in_client.get(url_for('main.usage', service_id=SERVICE_ONE_ID, year='abcd')).status_code == 404
@freeze_time("2012-03-31 12:12:12")
def test_future_usage_page(
logged_in_client,
mock_get_future_usage,
mock_get_future_billable_units,
):
assert logged_in_client.get(url_for('main.usage', service_id=SERVICE_ONE_ID, year=2014)).status_code == 200
mock_get_future_billable_units.assert_called_once_with(SERVICE_ONE_ID, 2014)
mock_get_future_usage.assert_called_once_with(SERVICE_ONE_ID, 2014)
def _test_dashboard_menu(mocker, app_, usr, service, permissions):
with app_.test_request_context():
with app_.test_client() as client:
@@ -552,26 +565,39 @@ def test_aggregate_status_types(dict_in, expected_failed, expected_requested):
)
def test_get_free_paid_breakdown_for_billable_units(now, expected_number_of_months):
with now:
assert list(get_free_paid_breakdown_for_billable_units(
2016, {
'April': 100000,
'May': 100000,
'June': 100000,
'February': 1234
}
)) == [
{'name': 'April', 'free': 100000, 'paid': 0},
{'name': 'May', 'free': 100000, 'paid': 0},
{'name': 'June', 'free': 50000, 'paid': 50000},
{'name': 'July', 'free': 0, 'paid': 0},
{'name': 'August', 'free': 0, 'paid': 0},
{'name': 'September', 'free': 0, 'paid': 0},
{'name': 'October', 'free': 0, 'paid': 0},
{'name': 'November', 'free': 0, 'paid': 0},
{'name': 'December', 'free': 0, 'paid': 0},
{'name': 'January', 'free': 0, 'paid': 0},
{'name': 'February', 'free': 0, 'paid': 1234},
{'name': 'March', 'free': 0, 'paid': 0}
billing_units = get_free_paid_breakdown_for_billable_units(
2016, [
{
'month': 'April', 'international': False, 'rate_multiplier': 1,
'notification_type': 'sms', 'rate': 1.65, 'billing_units': 100000
},
{
'month': 'May', 'international': False, 'rate_multiplier': 1,
'notification_type': 'sms', 'rate': 1.65, 'billing_units': 100000
},
{
'month': 'June', 'international': False, 'rate_multiplier': 1,
'notification_type': 'sms', 'rate': 1.65, 'billing_units': 100000
},
{
'month': 'February', 'international': False, 'rate_multiplier': 1,
'notification_type': 'sms', 'rate': 1.65, 'billing_units': 2000
},
]
)
assert list(billing_units) == [
{'free': 100000, 'name': 'April', 'paid': 0},
{'free': 100000, 'name': 'May', 'paid': 0},
{'free': 50000, 'name': 'June', 'paid': 50000},
{'free': 0, 'name': 'July', 'paid': 0},
{'free': 0, 'name': 'August', 'paid': 0},
{'free': 0, 'name': 'September', 'paid': 0},
{'free': 0, 'name': 'October', 'paid': 0},
{'free': 0, 'name': 'November', 'paid': 0},
{'free': 0, 'name': 'December', 'paid': 0},
{'free': 0, 'name': 'January', 'paid': 0},
{'free': 0, 'name': 'February', 'paid': 2000},
{'free': 0, 'name': 'March', 'paid': 0}
][:expected_number_of_months]

View File

@@ -1315,10 +1315,18 @@ def mock_get_template_statistics_for_template(mocker, service_one):
@pytest.fixture(scope='function')
def mock_get_usage(mocker, service_one, fake_uuid):
def _get_usage(service_id, year=None):
return {'data': {
"sms_count": 456123,
"email_count": 123
}}
return [
{"international": False, "rate": 0.0165, "rate_multiplier": 1,
"notification_type": "sms", "billing_units": 251500},
{"international": True, "rate": 0.0165, "rate_multiplier": 1,
"notification_type": "sms", "billing_units": 300},
{"international": True, "rate": 0.0165, "rate_multiplier": 2,
"notification_type": "sms", "billing_units": 150},
{"international": True, "rate": 0.0165, "rate_multiplier": 3,
"notification_type": "sms", "billing_units": 30},
{"international": False, "rate": 0.0165, "notification_type": "email",
"rate_multiplier": None, "billing_units": 1000}
]
return mocker.patch(
'app.service_api_client.get_service_usage', side_effect=_get_usage)
@@ -1327,10 +1335,116 @@ def mock_get_usage(mocker, service_one, fake_uuid):
@pytest.fixture(scope='function')
def mock_get_billable_units(mocker):
def _get_usage(service_id, year):
return {
"April": 123,
"March": 456123
}
return [
{
'month': 'April',
'international': False,
'rate_multiplier': 1,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 249500
},
{
'month': 'April',
'international': True,
'rate_multiplier': 1,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 100
},
{
'month': 'April',
'international': True,
'rate_multiplier': 2,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 100
},
{
'month': 'April',
'international': True,
'rate_multiplier': 3,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 20
},
{
'month': 'March',
'international': False,
'rate_multiplier': 1,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 1000
},
{
'month': 'March',
'international': True,
'rate_multiplier': 1,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 100
},
{
'month': 'March',
'international': True,
'rate_multiplier': 2,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 50
},
{
'month': 'March',
'international': True,
'rate_multiplier': 3,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 10
},
{
'month': 'February',
'international': False,
'rate_multiplier': 1,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 1000
},
{
'month': 'February',
'international': True,
'rate_multiplier': 1,
'notification_type': 'sms',
'rate': 1.65,
'billing_units': 100
},
]
return mocker.patch(
'app.service_api_client.get_billable_units', side_effect=_get_usage)
@pytest.fixture(scope='function')
def mock_get_future_usage(mocker, service_one, fake_uuid):
def _get_usage(service_id, year=None):
return [
{
'notification_type': 'sms', 'international': False,
'credits': 0, 'rate_multiplier': 1, 'rate': 1.58, 'billing_units': 0
},
{
'notification_type': 'email', 'international': False,
'credits': 0, 'rate_multiplier': 1, 'rate': 0, 'billing_units': 0
}
]
return mocker.patch(
'app.service_api_client.get_service_usage', side_effect=_get_usage)
@pytest.fixture(scope='function')
def mock_get_future_billable_units(mocker):
def _get_usage(service_id, year):
return []
return mocker.patch(
'app.service_api_client.get_billable_units', side_effect=_get_usage)