Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import Optional
from .math_round_div import round_div ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import Money, assert_same_currency, money ← from money.amount ^1.0.0 · built alongside by fune
from .telecoms_data_allowance_types import DataAllowance
def _is_count(value: object) -> bool:
return not isinstance(value, bool) and isinstance(value, int) and value >= 0
def data_allowance(
allowance: int, used: int, overage_block: int, price_per_block: Money, overage_cap: Optional[Money]
) -> DataAllowance:
"""Usage against a bundle: remaining allowance, overage in whole blocks
priced and limited to an optional cap, and the share used, rounded down so
a bundle with anything left never reads 100%.
"""
if not _is_count(allowance) or not _is_count(used):
raise ValueError(
"allowance and usage must be whole numbers of 0 or more, received %r and %r" % (allowance, used)
)
if not _is_count(overage_block) or overage_block < 1:
raise ValueError("overage block must be 1 or more, received %r" % (overage_block,))
if price_per_block.minor < 0:
raise ValueError("price per block must not be negative, received %d" % (price_per_block.minor,))
if overage_cap is not None:
assert_same_currency(price_per_block, overage_cap)
if overage_cap.minor < 0:
raise ValueError("overage cap must not be negative, received %d" % (overage_cap.minor,))
overage = max(used - allowance, 0)
overage_blocks = round_div(overage, overage_block, "up")
uncapped = overage_blocks * price_per_block.minor
capped = overage_cap is not None and uncapped > overage_cap.minor
return DataAllowance(
used=used,
remaining=max(allowance - used, 0),
overage=overage,
overage_blocks=overage_blocks,
overage_charge=money(overage_cap.minor if capped else uncapped, price_per_block.currency),
capped=capped,
percent_used_basis_points=None if allowance == 0 else round_div(used * 10000, allowance, "down"),
)