Functional Weave
Code in TypeScript

insurance.age-banding@1.0.0

impl/python.py

1,900 bytes · the Python implementation · view raw

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 Sequence

from .dates_age import age  ← from dates.age ^1.0.0 · built alongside by fune
from .insurance_age_banding_types import AgeBand, AgeBanding


def _is_int(value: object) -> bool:
    return isinstance(value, int) and not isinstance(value, bool)


def age_band(birth_date: str, inception_date: str, bands: Sequence[AgeBand]) -> AgeBanding:
    """The policyholder's age on the inception date and the band it falls in.

    Age is completed years by dates.age, so a 29 February birthday ages on
    1 March in other years. The bands are checked before use: youngest first,
    inclusive at both ends, no overlaps, and only the last may be open-ended.
    An age no band covers is an error, not the nearest band.
    """
    if len(bands) == 0:
        raise ValueError("bands must not be empty")
    previous_max = -1
    for i, band in enumerate(bands):
        if not _is_int(band.min_age) or band.min_age < 0:
            raise ValueError("minAge must be a whole number of 0 or more, received %s" % (band.min_age,))
        is_open = band.max_age is None
        if is_open and i != len(bands) - 1:
            raise ValueError('only the last band may have no maxAge: "%s" is not last' % (band.label,))
        if not is_open and (not _is_int(band.max_age) or band.max_age < band.min_age):
            raise ValueError('maxAge must be a whole number no less than minAge in band "%s"' % (band.label,))
        if band.min_age <= previous_max:
            raise ValueError(
                'bands must be youngest first and not overlap: "%s" starts at %d' % (band.label, band.min_age)
            )
        previous_max = 2 ** 62 if is_open else band.max_age
    years = age(birth_date, inception_date)
    for band in bands:
        if years >= band.min_age and (band.max_age is None or years <= band.max_age):
            return AgeBanding(age=years, label=band.label)
    raise ValueError("no band for age %d" % (years,))