bolster.data_sources.justice.pbni_caseload

Probation Board for Northern Ireland (PBNI) Caseload Statistics.

Provides access to PBNI’s annual and quarterly caseload bulletins, covering the people under probation supervision in Northern Ireland: how many there are, what orders they are serving, their demographics, risk assessments, and the victims registered with the Victim Information Scheme.

Two cadences are published:

  • Annual - a financial-year snapshot at 31 March, with five years of history and the widest breakdowns (offence classification, gender, directorate).

  • Quarterly - a snapshot at each quarter end, with roughly nine quarters of history and some extra flow measures (new caseload, reports completed).

Data Source:

PBNI publishes through GOV.UK, but the statistics themselves live in NISRA interactive data-visualisation pages at datavis.nisra.gov.uk/pbni/. Each figure on those pages embeds its underlying CSV as a base64 data URI, so the published numbers are recovered exactly rather than scraped from charts.

Publication URLs are discovered at runtime through the GOV.UK Search and Content APIs, so new releases are picked up without a code change.

Update Frequency: Quarterly, with an additional annual bulletin each spring Geographic Coverage: Northern Ireland Reference Period: Annual 2022 - present; quarterly 2024 - present

Note

The published CSVs are UTF-16LE encoded but use bare single-byte \r\n line terminators, which desynchronises any standard UTF-16 decoder after the first row. _decode_csv() works around this by stripping null bytes.

Example

>>> from bolster.data_sources.justice import pbni_caseload
>>> df = pbni_caseload.get_annual_caseload()
>>> {"date", "caseload", "service_users"}.issubset(df.columns)
True

Attributes

logger

SEARCH_API_URL

CONTENT_API_URL

DATAVIS_HOST

FREQUENCIES

Exceptions

PBNIDataError

Base exception for PBNI caseload data errors.

PBNIDataNotFoundError

Raised when a publication or figure cannot be located.

Functions

find_latest_publication([frequency])

Find the datavis URL of the most recent PBNI caseload bulletin.

list_dimensions([frequency])

List the dimensions available for a publication cadence.

get_latest_data([dimension, frequency, force_refresh])

Retrieve the latest PBNI caseload statistics.

get_annual_caseload([force_refresh])

Retrieve the annual caseload and service-user totals at each 31 March.

get_quarterly_caseload([force_refresh])

Retrieve the quarterly caseload and service-user totals at each quarter end.

validate_data(df)

Check that a caseload frame is structurally sound.

Module Contents

bolster.data_sources.justice.pbni_caseload.logger[source]
bolster.data_sources.justice.pbni_caseload.SEARCH_API_URL = 'https://www.gov.uk/api/search.json'[source]
bolster.data_sources.justice.pbni_caseload.CONTENT_API_URL = 'https://www.gov.uk/api/content'[source]
bolster.data_sources.justice.pbni_caseload.DATAVIS_HOST = 'datavis.nisra.gov.uk'[source]
bolster.data_sources.justice.pbni_caseload.FREQUENCIES = ('annual', 'quarterly')[source]
exception bolster.data_sources.justice.pbni_caseload.PBNIDataError[source]

Bases: Exception

Base exception for PBNI caseload data errors.

Initialize self. See help(type(self)) for accurate signature.

exception bolster.data_sources.justice.pbni_caseload.PBNIDataNotFoundError[source]

Bases: PBNIDataError

Raised when a publication or figure cannot be located.

Initialize self. See help(type(self)) for accurate signature.

bolster.data_sources.justice.pbni_caseload.find_latest_publication(frequency='annual')[source]

Find the datavis URL of the most recent PBNI caseload bulletin.

Parameters:

frequency (str) – Either "annual" or "quarterly".

Returns:

Absolute URL of the NISRA data-visualisation page holding the data.

Raises:
Return type:

str

Example

>>> find_latest_publication("annual").startswith("https://datavis.nisra.gov.uk/pbni/")
True
bolster.data_sources.justice.pbni_caseload.list_dimensions(frequency='annual')[source]

List the dimensions available for a publication cadence.

Parameters:

frequency (str) – Either "annual" or "quarterly".

Returns:

Sorted dimension names accepted by get_latest_data().

Return type:

list[str]

Example

>>> "caseload" in list_dimensions("annual")
True
bolster.data_sources.justice.pbni_caseload.get_latest_data(dimension='caseload', frequency='annual', force_refresh=False)[source]

Retrieve the latest PBNI caseload statistics.

Parameters:
  • dimension (str) – Which breakdown to return, or "all" for every dimension. See list_dimensions() for the options.

  • frequency (str) – Either "annual" or "quarterly".

  • force_refresh (bool) – Bypass the local cache and re-download the bulletin.

Returns:

A DataFrame for a single dimension, or a mapping of dimension name to DataFrame when dimension="all".

Raises:

ValueError – If frequency or dimension is not recognised.

Return type:

pandas.DataFrame | dict[str, pandas.DataFrame]

Example

>>> df = get_latest_data("order_type", frequency="annual")
>>> "Probation Order" in set(df["order_type"])
True
bolster.data_sources.justice.pbni_caseload.get_annual_caseload(force_refresh=False)[source]

Retrieve the annual caseload and service-user totals at each 31 March.

Parameters:

force_refresh (bool) – Bypass the local cache and re-download the bulletin.

Returns:

DataFrame with date, caseload and service_users columns.

Return type:

pandas.DataFrame

Example

>>> df = get_annual_caseload()
>>> (df["caseload"] >= df["service_users"]).all()
True
bolster.data_sources.justice.pbni_caseload.get_quarterly_caseload(force_refresh=False)[source]

Retrieve the quarterly caseload and service-user totals at each quarter end.

Parameters:

force_refresh (bool) – Bypass the local cache and re-download the bulletin.

Returns:

DataFrame with date, caseload and service_users columns.

Return type:

pandas.DataFrame

Example

>>> df = get_quarterly_caseload()
>>> len(df) >= 8
True
bolster.data_sources.justice.pbni_caseload.validate_data(df)[source]

Check that a caseload frame is structurally sound.

Parameters:

df (pandas.DataFrame) – A frame returned by get_annual_caseload() or get_quarterly_caseload().

Returns:

True if the frame passes every check.

Raises:

PBNIDataError – If the frame is empty, missing columns, or holds non-positive counts.

Return type:

bool

Example

>>> validate_data(get_annual_caseload())
True