Add tests for sms breakdown adjusted free allowance

This commit is contained in:
Ken Tsang
2017-04-26 17:08:33 +01:00
parent 260dfb1e32
commit 79b4b37b0e
3 changed files with 174 additions and 48 deletions

View File

@@ -102,18 +102,19 @@ def template_history(service_id):
@user_has_permissions('manage_settings', admin_override=True) @user_has_permissions('manage_settings', admin_override=True)
def usage(service_id): def usage(service_id):
year, current_financial_year = requested_and_current_financial_year(request) year, current_financial_year = requested_and_current_financial_year(request)
monthly_breakdown = get_free_paid_breakdown_for_billable_units(
year, service_api_client.get_billable_units(service_id, year))
return render_template( return render_template(
'views/usage.html', 'views/usage.html',
months=list(get_free_paid_breakdown_for_billable_units( months=list(monthly_breakdown),
year, service_api_client.get_billable_units(service_id, year)
)),
selected_year=year, selected_year=year,
years=get_tuples_of_financial_years( years=get_tuples_of_financial_years(
partial(url_for, '.usage', service_id=service_id), partial(url_for, '.usage', service_id=service_id),
start=current_financial_year - 1, start=current_financial_year - 1,
end=current_financial_year + 1, end=current_financial_year + 1,
), ),
**calculate_usage(service_api_client.get_service_usage(service_id, year)['data']) # **calculate_usage(dict(monthly_breakdown))
**calculate_usage(service_api_client.get_service_usage(service_id, year)['data'], monthly_breakdown)
) )
@@ -201,21 +202,24 @@ def get_dashboard_totals(statistics):
return statistics return statistics
def calculate_usage(usage): def calculate_usage(usage, monthly_breakdown):
# TODO: Don't hardcode these - get em from the API # TODO: Don't hardcode these - get em from the API
sms_free_allowance = 250000 sms_free_allowance = 250000
sms_rate = 0.0165 sms_rate = 0.0165
sms_sent = usage.get('sms_count', 0) sms_sent = get_sum_units(usage.get('sms_breakdown', []))
emails_sent = usage.get('email_count', 0) emails_sent = usage.get('email_count', 0)
sms_breakdown = get_sms_breakdown_adjusted_free_allowance(
usage.get('sms_breakdown', []), sms_free_allowance)
return { return {
'emails_sent': emails_sent, 'emails_sent': emails_sent,
'sms_free_allowance': sms_free_allowance, 'sms_free_allowance': sms_free_allowance,
'sms_sent': sms_sent, 'sms_sent': sms_sent,
'sms_allowance_remaining': max(0, (sms_free_allowance - sms_sent)), 'sms_allowance_remaining': max(0, (sms_free_allowance - sms_sent)),
'sms_chargeable': max(0, sms_sent - sms_free_allowance), 'sms_chargeable': max(0, sms_sent - sms_free_allowance),
'sms_rate': sms_rate 'sms_rate': sms_rate,
'sms_breakdown': sms_breakdown,
'sms_free_units_used': usage.get('free_units_used', 0)
} }
@@ -262,18 +266,23 @@ def get_months_for_year(start, end, year):
return [datetime(year, month, 1) for month in range(start, end)] return [datetime(year, month, 1) for month in range(start, end)]
def get_total_units_for_month(monthly_usage): def get_sum_billable_units(usage):
total = 0 return sum(rate['units'] * rate['multiplier'] for rate in usage)
for rate_group in monthly_usage:
total += rate_group['units'] * rate_group['multiplier']
return total def get_sum_units(usage):
return sum(rate['units'] for rate in usage)
def get_sum_free_units(usage):
return sum(rate.get('free_units', 0) * rate['multiplier'] for rate in usage)
def get_free_paid_breakdown_for_billable_units(year, billable_units): def get_free_paid_breakdown_for_billable_units(year, billable_units):
cumulative = 0 cumulative = 0
for month in get_months_for_financial_year(year): for month in get_months_for_financial_year(year):
previous_cumulative = cumulative previous_cumulative = cumulative
monthly_usage = get_total_units_for_month(billable_units.get(month, [])) monthly_usage = get_sum_billable_units(billable_units.get(month, []))
cumulative += monthly_usage cumulative += monthly_usage
breakdown = get_free_paid_breakdown_for_month( breakdown = get_free_paid_breakdown_for_month(
cumulative, previous_cumulative, billable_units.get(month, []) cumulative, previous_cumulative, billable_units.get(month, [])
@@ -282,35 +291,40 @@ def get_free_paid_breakdown_for_billable_units(year, billable_units):
'name': month, 'name': month,
'paid': breakdown['paid'], 'paid': breakdown['paid'],
'free': breakdown['free'], 'free': breakdown['free'],
'rate_groups': breakdown['rate_groups'] 'sms_breakdown': breakdown.get('sms_breakdown', []),
'free_units_used': get_sum_free_units(breakdown.get('sms_breakdown', []))
} }
def get_rate_groups(monthly_usage, free_allowance=0): def get_sms_breakdown_adjusted_free_allowance(breakdown_list, free_allowance=0):
rate_groups = [] sms_breakdown = []
for rate_group in monthly_usage: for breakdown in breakdown_list:
free_units = 0 free_units = 0
units = rate_group["units"]
if free_allowance > 0: if free_allowance > 0:
units = rate_group["units"] * rate_group["multiplier"] - free_allowance normalised_units = breakdown["units"] * breakdown["multiplier"]
if units < 0: billable_units = normalised_units - free_allowance
free_units = rate_group["units"] * rate_group["multiplier"]
free_allowance = abs(units) if billable_units <= 0:
units = 0 free_units = breakdown["units"]
free_allowance = abs(billable_units)
else: else:
free_units = free_allowance free_units = int(free_allowance / breakdown["multiplier"])
free_allowance = 0
rate_group = { if free_allowance < normalised_units:
"international": rate_group["international"], free_allowance = 0
else:
free_allowance -= normalised_units
adj_breakdown = {
"international": breakdown["international"],
"free_units": free_units, "free_units": free_units,
"units": units, "units": breakdown["units"] - free_units,
"multiplier": rate_group["multiplier"] "multiplier": breakdown["multiplier"]
} }
rate_groups.append(rate_group) sms_breakdown.append(adj_breakdown)
return rate_groups return sms_breakdown
def get_free_paid_breakdown_for_month( def get_free_paid_breakdown_for_month(
@@ -320,25 +334,25 @@ def get_free_paid_breakdown_for_month(
): ):
allowance = 250000 allowance = 250000
total_monthly_units = get_total_units_for_month(monthly_usage) total_monthly_billable_units = get_sum_billable_units(monthly_usage)
if cumulative < allowance: if cumulative < allowance:
return { return {
'paid': 0, 'paid': 0,
'free': total_monthly_units, 'free': total_monthly_billable_units,
} }
elif previous_cumulative < allowance: elif previous_cumulative < allowance:
remaining_allowance = allowance - previous_cumulative remaining_allowance = allowance - previous_cumulative
return { return {
'paid': total_monthly_units - remaining_allowance, 'paid': total_monthly_billable_units - remaining_allowance,
'free': remaining_allowance, 'free': remaining_allowance,
'rate_groups': get_rate_groups(monthly_usage, remaining_allowance) 'sms_breakdown': get_sms_breakdown_adjusted_free_allowance(monthly_usage, remaining_allowance)
} }
else: else:
return { return {
'paid': total_monthly_units, 'paid': total_monthly_billable_units,
'free': 0, 'free': 0,
'rate_groups': get_rate_groups(monthly_usage) 'sms_breakdown': get_sms_breakdown_adjusted_free_allowance(monthly_usage)
} }

View File

@@ -11,6 +11,7 @@ from app.main.views.dashboard import (
get_dashboard_totals, get_dashboard_totals,
format_monthly_stats_to_list, format_monthly_stats_to_list,
get_free_paid_breakdown_for_billable_units, get_free_paid_breakdown_for_billable_units,
get_sms_breakdown_adjusted_free_allowance,
aggregate_status_types, aggregate_status_types,
format_template_stats_to_list, format_template_stats_to_list,
get_tuples_of_financial_years, get_tuples_of_financial_years,
@@ -301,20 +302,17 @@ def test_international_usage_page(
nav = page.find('ul', {'class': 'pill', 'role': 'tablist'}) nav = page.find('ul', {'class': 'pill', 'role': 'tablist'})
nav_links = nav.find_all('a') nav_links = nav.find_all('a')
assert '252,390' in cols[1].text assert '252,180' in cols[1].text
assert 'Text messages' in cols[1].text assert 'Text messages' in cols[1].text
table = page.find('table').text.strip() table = page.find('table').text.strip()
print(table) assert '250000 free allowance used' in table
assert '20 international text at 4.95p' in table
assert '249,900 UK free text messages' in table
assert '100 international free text messages' in table
assert '900 international text messages at 1.65p' in table
assert 'April' in table assert 'April' in table
assert 'March' in table assert 'March' in table
assert '£0.99' in table
assert '£20.30' in table assert '£20.30' in table
assert '£19.14' in table
def test_usage_page_with_year_argument( def test_usage_page_with_year_argument(
@@ -652,6 +650,89 @@ def test_get_free_paid_breakdown_for_billable_units(now, expected_number_of_mont
][:expected_number_of_months] ][:expected_number_of_months]
@pytest.mark.parametrize(
'usage, expected_breakdown', [
(
[
{"international": False, "multiplier": 1, "units": 240000},
{"international": True, "multiplier": 2, "units": 20},
],
[
{'units': 0, 'free_units': 240000, 'international': False, 'multiplier': 1},
{'units': 0, 'free_units': 20, 'international': True, 'multiplier': 2}
]
),
(
[
{"international": False, "multiplier": 1, "units": 250000},
{"international": True, "multiplier": 2, "units": 20},
],
[
{'units': 0, 'free_units': 250000, 'international': False, 'multiplier': 1},
{'units': 20, 'free_units': 0, 'international': True, 'multiplier': 2}
]
),
(
[
{"international": False, "multiplier": 1, "units": 300000},
{"international": True, "multiplier": 2, "units": 20},
],
[
{'units': 50000, 'free_units': 250000, 'international': False, 'multiplier': 1},
{'units': 20, 'free_units': 0, 'international': True, 'multiplier': 2}
]
),
(
[
{"international": False, "rate": 1.65, "multiplier": 1, "units": 249700},
{"international": True, "rate": 1.65, "multiplier": 1, "units": 100},
{"international": True, "rate": 1.65, "multiplier": 2, "units": 100},
{"international": True, "rate": 1.65, "multiplier": 3, "units": 20},
],
[
{'international': False, 'free_units': 249700, 'units': 0, 'multiplier': 1},
{'international': True, 'free_units': 100, 'units': 0, 'multiplier': 1},
{'international': True, 'free_units': 100, 'units': 0, 'multiplier': 2},
{'international': True, 'free_units': 0, 'units': 20, 'multiplier': 3}
]
),
(
[
{"international": False, "rate": 1.65, "multiplier": 1, "units": 249600},
{"international": True, "rate": 1.65, "multiplier": 1, "units": 100},
{"international": True, "rate": 1.65, "multiplier": 2, "units": 100},
{"international": True, "rate": 1.65,"multiplier": 3, "units": 20},
],
[
{'units': 0, 'free_units': 249600, 'international': False, 'multiplier': 1},
{'units': 0, 'free_units': 100, 'international': True, 'multiplier': 1},
{'units': 0, 'free_units': 100, 'international': True, 'multiplier': 2},
{'units': 0, 'free_units': 20, 'international': True, 'multiplier': 3}
]
),
(
[
{"international": False, "rate": 1.65, "multiplier": 1, "units": 249800},
{"international": True, "rate": 1.65, "multiplier": 1, "units": 100},
{"international": True, "rate": 1.65, "multiplier": 2, "units": 100},
{"international": True, "rate": 1.65,"multiplier": 3, "units": 20},
],
[
{'units': 0, 'free_units': 249800, 'international': False, 'multiplier': 1},
{'units': 0, 'free_units': 100, 'international': True, 'multiplier': 1},
{'units': 50, 'free_units': 50, 'international': True, 'multiplier': 2},
{'units': 20, 'free_units': 0, 'international': True, 'multiplier': 3}
]
)
]
)
def test_get_sms_breakdown_adjusted_free_allowance(usage, expected_breakdown):
free_allowance = 250000
breakdown = get_sms_breakdown_adjusted_free_allowance(usage, free_allowance)
print(breakdown)
assert breakdown == expected_breakdown
def test_format_template_stats_to_list_with_no_stats(): def test_format_template_stats_to_list_with_no_stats():
assert list(format_template_stats_to_list({})) == [] assert list(format_template_stats_to_list({})) == []

View File

@@ -1338,9 +1338,40 @@ def mock_get_billable_units(mocker):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def mock_get_international_usage(mocker, service_one, fake_uuid): def mock_get_international_usage(mocker, service_one, fake_uuid):
# def _get_usage(service_id, year=None):
# return {'data': {
# "sms_count": 252390,
# "email_count": 0
# }}
def _get_usage(service_id, year=None): def _get_usage(service_id, year=None):
return {'data': { return {'data': {
"sms_count": 252390, "sms_breakdown": [
{
"international": False,
"rate": 1.65,
"multiplier": 1,
"units": 250900
},
{
"international": True,
"rate": 1.65,
"multiplier": 1,
"units": 1100
},
{
"international": True,
"rate": 1.65,
"multiplier": 2,
"units": 150
},
{
"international": True,
"rate": 1.65,
"multiplier": 3,
"units": 30
}
],
"email_count": 0 "email_count": 0
}} }}
@@ -1357,13 +1388,13 @@ def mock_get_billable_international_units(mocker):
"international": False, "international": False,
"rate": 1.65, "rate": 1.65,
"multiplier": 1, "multiplier": 1,
"units": 249900 "units": 249700
}, },
{ {
"international": True, "international": True,
"rate": 1.65, "rate": 1.65,
"multiplier": 1, "multiplier": 1,
"units": 1000 "units": 100
}, },
{ {
"international": True, "international": True,