mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-07-12 09:24:15 -04:00
Add tests for sms breakdown adjusted free allowance
This commit is contained in:
@@ -102,18 +102,19 @@ def template_history(service_id):
|
||||
@user_has_permissions('manage_settings', admin_override=True)
|
||||
def usage(service_id):
|
||||
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(
|
||||
'views/usage.html',
|
||||
months=list(get_free_paid_breakdown_for_billable_units(
|
||||
year, service_api_client.get_billable_units(service_id, year)
|
||||
)),
|
||||
months=list(monthly_breakdown),
|
||||
selected_year=year,
|
||||
years=get_tuples_of_financial_years(
|
||||
partial(url_for, '.usage', service_id=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(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
|
||||
|
||||
|
||||
def calculate_usage(usage):
|
||||
def calculate_usage(usage, monthly_breakdown):
|
||||
# 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)
|
||||
sms_sent = get_sum_units(usage.get('sms_breakdown', []))
|
||||
emails_sent = usage.get('email_count', 0)
|
||||
|
||||
sms_breakdown = get_sms_breakdown_adjusted_free_allowance(
|
||||
usage.get('sms_breakdown', []), sms_free_allowance)
|
||||
return {
|
||||
'emails_sent': emails_sent,
|
||||
'sms_free_allowance': sms_free_allowance,
|
||||
'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,
|
||||
'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)]
|
||||
|
||||
|
||||
def get_total_units_for_month(monthly_usage):
|
||||
total = 0
|
||||
for rate_group in monthly_usage:
|
||||
total += rate_group['units'] * rate_group['multiplier']
|
||||
return total
|
||||
def get_sum_billable_units(usage):
|
||||
return sum(rate['units'] * rate['multiplier'] for rate in usage)
|
||||
|
||||
|
||||
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):
|
||||
cumulative = 0
|
||||
for month in get_months_for_financial_year(year):
|
||||
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
|
||||
breakdown = get_free_paid_breakdown_for_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,
|
||||
'paid': breakdown['paid'],
|
||||
'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):
|
||||
rate_groups = []
|
||||
def get_sms_breakdown_adjusted_free_allowance(breakdown_list, free_allowance=0):
|
||||
sms_breakdown = []
|
||||
|
||||
for rate_group in monthly_usage:
|
||||
for breakdown in breakdown_list:
|
||||
free_units = 0
|
||||
units = rate_group["units"]
|
||||
if free_allowance > 0:
|
||||
units = rate_group["units"] * rate_group["multiplier"] - free_allowance
|
||||
if units < 0:
|
||||
free_units = rate_group["units"] * rate_group["multiplier"]
|
||||
free_allowance = abs(units)
|
||||
units = 0
|
||||
normalised_units = breakdown["units"] * breakdown["multiplier"]
|
||||
billable_units = normalised_units - free_allowance
|
||||
|
||||
if billable_units <= 0:
|
||||
free_units = breakdown["units"]
|
||||
free_allowance = abs(billable_units)
|
||||
else:
|
||||
free_units = free_allowance
|
||||
free_allowance = 0
|
||||
free_units = int(free_allowance / breakdown["multiplier"])
|
||||
|
||||
rate_group = {
|
||||
"international": rate_group["international"],
|
||||
if free_allowance < normalised_units:
|
||||
free_allowance = 0
|
||||
else:
|
||||
free_allowance -= normalised_units
|
||||
|
||||
adj_breakdown = {
|
||||
"international": breakdown["international"],
|
||||
"free_units": free_units,
|
||||
"units": units,
|
||||
"multiplier": rate_group["multiplier"]
|
||||
"units": breakdown["units"] - free_units,
|
||||
"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(
|
||||
@@ -320,25 +334,25 @@ def get_free_paid_breakdown_for_month(
|
||||
):
|
||||
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:
|
||||
return {
|
||||
'paid': 0,
|
||||
'free': total_monthly_units,
|
||||
'free': total_monthly_billable_units,
|
||||
}
|
||||
elif previous_cumulative < allowance:
|
||||
remaining_allowance = allowance - previous_cumulative
|
||||
return {
|
||||
'paid': total_monthly_units - remaining_allowance,
|
||||
'paid': total_monthly_billable_units - 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:
|
||||
return {
|
||||
'paid': total_monthly_units,
|
||||
'paid': total_monthly_billable_units,
|
||||
'free': 0,
|
||||
'rate_groups': get_rate_groups(monthly_usage)
|
||||
'sms_breakdown': get_sms_breakdown_adjusted_free_allowance(monthly_usage)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ from app.main.views.dashboard import (
|
||||
get_dashboard_totals,
|
||||
format_monthly_stats_to_list,
|
||||
get_free_paid_breakdown_for_billable_units,
|
||||
get_sms_breakdown_adjusted_free_allowance,
|
||||
aggregate_status_types,
|
||||
format_template_stats_to_list,
|
||||
get_tuples_of_financial_years,
|
||||
@@ -301,20 +302,17 @@ def test_international_usage_page(
|
||||
nav = page.find('ul', {'class': 'pill', 'role': 'tablist'})
|
||||
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
|
||||
|
||||
table = page.find('table').text.strip()
|
||||
|
||||
print(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 '250000 free allowance used' in table
|
||||
assert '20 international text at 4.95p' in table
|
||||
assert 'April' in table
|
||||
assert 'March' in table
|
||||
assert '£0.99' in table
|
||||
assert '£20.30' in table
|
||||
assert '£19.14' in table
|
||||
|
||||
|
||||
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]
|
||||
|
||||
|
||||
@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():
|
||||
assert list(format_template_stats_to_list({})) == []
|
||||
|
||||
|
||||
@@ -1338,9 +1338,40 @@ def mock_get_billable_units(mocker):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
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):
|
||||
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
|
||||
}}
|
||||
|
||||
@@ -1357,13 +1388,13 @@ def mock_get_billable_international_units(mocker):
|
||||
"international": False,
|
||||
"rate": 1.65,
|
||||
"multiplier": 1,
|
||||
"units": 249900
|
||||
"units": 249700
|
||||
},
|
||||
{
|
||||
"international": True,
|
||||
"rate": 1.65,
|
||||
"multiplier": 1,
|
||||
"units": 1000
|
||||
"units": 100
|
||||
},
|
||||
{
|
||||
"international": True,
|
||||
|
||||
Reference in New Issue
Block a user