Dashboard widgets

Base & mixin classes

All widgets should extend BaseDashboardWidget. In addition StandaloneDashboardWidgetMixin should be used with some exceptions of hardcoded-only widgets.

Notable mixins:

StandaloneDashboardWidgetMixin

Makes widget “Standalone” (widget can calculate its own data) by standardizing its constructor arguments and providing common methods and properties for filtering and working with audit forms, QIP issues, observations, etc.

Note

All new widgets must include this mixin unless already included by its subclass

ConfigSchemaWidgetMixin

Allows widget to define its own configuration schema using django-jsonform library to present configuration options to the user in a form style rather than ras json.

Tip

Add this mixin to make it more user-friendly and enable it to be edited by end users in the new edit pop-up

WidgetPermissionsMixin

Allows implementing widgets to specify permissions required to use the widget. If user does not have the required permissions, the widget will not display.

ActionMenuMixin

Adds menu options to the widget. This is included by default in BaseDashboardWidget.

TimeGranularityWidgetMixin

For time-scale / over-time widgets, allows user to specify time granularity (defined in SUPPORTED_GRANULARITIES) and exposes granularity property to the widget.

Base classes

dashboard_widgets.widgets.base.widget_action(label: str, target: str = '_self', show_in_menu: bool = True)

A Widget Action method that will appear in the widget’s menu.

Parameters:
  • label – label displayed to the user

  • target – if widget returns a http response, whether the address to be opened in a new tab

  • show_in_menu – whether this action should appear in the widget menu (default: True)

Example:

>>> @widget_action(label=_('Test Action'))
>>> def action_download_csv(self, request, **kwargs):
>>>     return HttpResponse("Test complete")
class dashboard_widgets.widgets.base.ActionMenuMixin

Mixin class for BaseDashboardWidget that provides dropdown menu with actions to the widget

allow_download = True

whether this widget should have “download as image” option

get_actions() Iterator[Callable[[], HttpResponseBase | None]]

Yields widget actions defined as widget methods. Such actions should be decorated with widget_action() and their names should be prefixed with action_

get_menu_options() Iterator[WidgetMenuOption]

Iterates all the menu options available in this widget

Yield:

widget menu options

class dashboard_widgets.widgets.base.FreezeWidgetHeaderMixin

A mixin designed for table widgets which gives the user a new widget menu/action item to freeze the widgets table header. Freezing makes the header sticky which means it stays visible when scrolling down the table.

property widget_config_id: int | None

Attemps to find a fixed widget ID by looking for the the widget’s config as its id is fixed

ID used to create cookie to persist frozen/unfrozen table header. A widget configs pk is used if availible as it is stable.

Expiry for widgets cookie in days. If using a fixed id (widget_config_id) set to a long expiry otherwise short

get_menu_options() Iterator[WidgetMenuOption]

Adds freeze header menu option to widget. Includes onclick function defined in JS which adds / removes ‘freeze-table-header’ class

class dashboard_widgets.widgets.base.BaseDashboardWidget(**kwargs)
full_size: bool = False

Should this widget take up full width and display more detail

widget_config: WidgetConfig | None = None

For custom dashboards, this field holds instance of the widget model

widget_id: int | None = None

if this widget is displayed in a group, this is widget’s order number within the group

show_comment_box: bool = True

Whether comment box should be shown in print view underneath this widget

allow_lazy: bool = True

Whether this widget can be lazy-loaded can be set to false for widgets that don’t do any expensive computations

property html_selector: str

css/jquery selector for the base html element of this widget; E.g. #widget-12345

render_content() str

Render contents inside the widget :return: html safe string

get_size() int

Gets size of this widget in terms of bootstrap grid system sizing (1-12)

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

is_compact() bool

Returns wheter this widget will be compact or standard. Compact widgets have a minimum height which is half of a standard widget

get_context_data() Dict[str, Any]

Context data for rendered widget template

validate() None

Validates widget configuration before rendering.

Raises:

ConfigurationError – when the widget configuration is invalid.

render() str

Renders widget’s html and js content in-line

Returns:

rendered html as text

add_selected_filters_breadcrumb() None

Adds a Sentry breadcrumb with the selected dashboard filters.

render_error(error: ConfigurationError) str

Renders error widget in place of this widget to report config error to the user.

dashboard_widgets.widgets.base.CustomDataSet(**kwargs) dict

A wrapper for jchart’s DataSet which accept a meta dict. Meta data can be used when scripting chart configuration on the frontend: https://chartjs-plugin-datalabels.netlify.app/guide/options.html#scriptable-options

Widget mix-ins

exception dashboard_widgets.widgets.mixin.WidgetHasManyForms

Exception raised when trying to get form in a widget that has more than one form

class dashboard_widgets.widgets.mixin.FormWidgetMixin

Mixin providing form tools to the widget. Essential to working with issues and observations

support_multi_form = True

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

audit_forms: tuple[AuditForm]

forms passed to this widget. Can be empty or any number of forms if support_multi_form is True, this is expected to hold exactly one form

property audit_form: AuditForm

Gets single form instance if is_single_form is set.

Returns:

single form instance

Raises:

WidgetHasManyForms – if this widget supports multiple forms

property form_config: AuditFormConfig

Gets current form’s configuration. If there’s no single form, defaults to a blank form config with model-level defaults.

property forms_grouped: Iterator[tuple[ObservationModel, Iterable[AuditForm]]]

Generates tuples of observation model class and iterable of forms with that observation model

property is_using_custom_date_filter_field: bool

Returns true if the current widget has just one form, with a custom date filter field.

property date_filter_field_name: str

Gets the correct date field name for the current audit form. For multi form widgets, returns the default session end time field name.

property date_filter_field: CustomField | Field | None

Gets the correct date field name for the current audit form. For multi form widgets, returns the default session end time field name.

class dashboard_widgets.widgets.mixin.ObservationWidgetMixin

Mix-in for widgets working with observations

include_own: bool = False

when True, includes the auditor’s own submissions regardless of ward ringfencing

property base_observations_qs: ObservationQuerySet

Returns the base, permission-filtered ObservationQuerySet for the widget.

This queryset is filtered by: - The widget’s selected audit form(s). - The current user’s permissions (wards, forms via .for_auditor_forms). - A pre-set ._observations queryset, if provided.

It does not include widget-specific filters (see .apply_observation_filters). :raises TypeError: if widget does not show single form and some of the forms use non-custom observation model

property observations_qs: ObservationQuerySet

Single filtered queryset containing all observations within selected form and filters. Note that with multi-form widgets, all forms must use CustomObservation model. To get all observations of any type, use observations_iter instead.

Raises:

TypeError – if widget does not show single form and some of the forms use non-custom observation model

property using_counter_logic: bool

Property which defines if a widget has counter_logic in the schema config

Returns:

True if the widget configuration contains the counter logic setting, False otherwise.

property counter_logic: Literal['audits', 'observations']

Property which defines the counter_logic selected in the widget by the user

Returns:

True if the widget configuration contains the counter logic setting, False otherwise.

property num_observations: int

Returns a count of either audit sessions or observations depending on counter logic

Returns:

The total count of observations or audit sessions, based on the configured counter logic.

property observations_iter: Iterator[Observation]

Iterate observations without caching

class dashboard_widgets.widgets.mixin.TargetActionMixin

Adds the target action property to target widgets that use counter logic. Converts the counter logic configuration into the equivalent target action.

class dashboard_widgets.widgets.mixin.DynamicFieldWidgetMixin

Mixin for widget methods used when a widget allows dynamically switching the field name without changing the widget config.

active_fields_override: list[str] | None = None

The dynamic choice(s) selected by a user, set in an AJAX request

dynamic_field_target: str | None = None

Key of the field in the widgets config which is to be used for dynamic fields

get_dynamic_field_name() str | None

Gets field name passed globally via GET argument from reloadWithDynamicFieldSet js function. This value is validated by ensuring it is one of the field choices

Returns:

field name if a valid one was passed, or null if no field was passed, or the field is not one of the valid choices

property dynamic_field_id: int | None

Dynamic ID used to find this widget in the dashboard for rerendering. Using an id which doesnt change the widgets widget_config ID.

property using_dynamic_filtering: bool

Determines if this widget is using dynamic filtering. Even if widget includes DynamicFieldWidgetMixin, the particular subclass may not support it. The widget needs to have dynamic_filtering enabled and field_names in the widgets schema to use dynamic filtering. Also, the widget needs to allow lazy rendering

Returns:

True if the widget configuration contains the dynamic filtering setting, False otherwise.

property field_name_choices: list[ChoiceSchema]

Gets fields (choices) shown in dynamic field dropdown in dashboard for this widget. If compliance is enabled it filters choices by fields used to calculate compliance. If subforms are select it filters choices by fields in them subformsIt also filters choices by compatible widget types using DYNAMIC_FIELD_COMPATIBALE_WIDGETS

property dynamic_filtering: bool

Property which defines the dynamic_filtering selected in the widget by the user. Throws an error if the choice is not a bool.

Returns:

dynamic_filtering choice selected in the widgets config

property field_names: list[str] | None

Determines the list of currently active fields names.

It prioritizes dynamic override fields from a possible AJAX request, otherwise it falls back to the default fields

property field_name: str | None

Determines the currently active field name.

If any override fields are present the first is selected.

property active_fields: list[str] | None

Returns the currently selected fields for display in dynamic field dropdown.

class dashboard_widgets.widgets.mixin.QipDateFilterWidgetMixin

Mixin to override the time_range filter for the observation queryset in widgets that focus on QIP issues. This is for when the observation queryset is used to filter the Issues, and the time_range filter is intended to filter the Issues. e.g. QIP Issues - Ordinarily a time_filter of ‘today’ would exclude an issue created today if the linked observation was dated yesterday (session end date).

class dashboard_widgets.widgets.mixin.QipWidgetMixin

Mixin for widgets working with QIP issues

filter_issues_by_observations: bool

Whether to filter issues by observations in this widget. this requires that the class also extends ObservationWidgetMixin.

class dashboard_widgets.widgets.mixin.StandaloneDashboardWidgetMixin(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Mixin for widget classes that provides all parameters for the widget to calculate its data and render itself.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

widget_config: 'WidgetConfig' | None

If created off widget config model, the model instance is referenced by the widget_config field

user_config: dict[str, Any]

user config holds all variable configuration for this widget coming either from widget_config instance or passed from the dashboard

validate() None
Raises:

DashboardConfigError – when widget requires a single form but multiple are provided.

get_config_value(key: str, default=None) Any

gets a single config value. If value is not set (or is null), returns the default value.

property user: User | None

Current user instance if request was passed and user is authenticated

Returns:

user instance, or None is user is not authenticated

property institution: Institution | None

Gets institution instance if only one.

Returns:

Institution instance if the widget shows data from exactly one institution., None otherwise

property widget_editable: bool

Whether current user can edit this widget. Checks that user has the permission and current dashboard can be edited by user

class dashboard_widgets.widgets.mixin.ConfigSchemaWidgetMixin

Allows widget to define schema for its config json If schema is defined, a form will be rendered using django-jsonform inside the model form to configure the widget options instead of the raw json field.

Warning

When overriding config_defaults ensure that values in it are valid values for the respective config field type. For instance, for a list field, a valid empty value is [], but not None.

Example:

>>> class ExampleWidget(ConfigSchemaWidgetMixin, BaseDashboard):
>>>    config_schema = SchemaDict(type='dict', keys={
>>>        'label': SchemaDict(type='string', title=_('Label')),
>>>        'items': SchemaDict(type='list', items=ListItemSchema(type='string'), title=_('List of strings')),
>>>        'number': SchemaDict(type='integer', title=_('Number')),
>>>        'checkbox': SchemaDict(type='boolean', title=_("Checkbox")),
>>>        'dropdown': SchemaDict(type='string', title=_("Dropdown"), choices=[
>>>            ChoiceSchema(value='1', label=_("Option 1")),
>>>            ChoiceSchema(value='2', label=_("Option 2")),
>>>        ]),
>>>     config_defaults = {
>>>         'items': [],
>>>         'checkbox': True,
>>>     }
>>> })
config_defaults: dict[str, str | int | list | bool] = {}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

class dashboard_widgets.widgets.mixin.WidgetPermissionsMixin

Allows widget to define permissions required to view it. Analogous to django PermissionRequiredMixin view mixin.

Example:

>>> class ExampleWidget(WidgetPermissionsMixin, BaseDashboard):
>>>    permissions = 'megforms.view_auditform', 'qip.view_issue',
permissions: tuple[str]

Tuple of django permissions required by this widget

class dashboard_widgets.widgets.mixin.SingleStatMixin

Mixin that provides common single stat widget variables & answers to custom fields

get_answers_for_field(field_name: str) QuerySet

Queryset of observations or subobservations annotated with value, containing value of the field passed in field_name`

Parameters:

field_name – name of a custom field

Returns:

queryset of the (sub)observation with added annotation of the given field’s value

class dashboard_widgets.widgets.mixins.TimeGranularityWidgetMixin

Mixin for widget classes that provides granularity configuration and property for accessing it.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

get_date_range_for_dataframe(column_name: str, dataframe: DataFrame) DatetimeIndex

Given the currently selected granularity and a dataframe, return the date range as an index.

Parameters:
  • column_name – The column name of the date field in the dataframe.

  • dataframe – Optionally provide the dataframe as an argument. If not specified self.dataframe will be used.

class dashboard_widgets.widgets.mixins.DocumentWidgetMixin

Define documents and versions querysets

property megdocs_config: MegDocsConfig

Get MegDocsConfig for the widget’s institutions

class dashboard_widgets.widgets.mixins.TimeTrackingWidgetConfigMixin

Schema config shared by time tracking widgets.

Chart Widgets

All widgets rendering some sort of a graph or chart, primarily using ChartJS library. These widgets all inherit from dashboard_widgets.widgets.chart.BaseChartWidget.

class dashboard_widgets.widgets.chart.JchartDataset

Represents dict returned by DataSet() jchart func

class dashboard_widgets.widgets.chart.ChartType(*values)

Chart types supported by ChartJS More available: https://www.chartjs.org/docs/latest/charts/

property js_value

Value for rendering chartjs

class dashboard_widgets.widgets.chart.BaseChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

property value_format

str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.

limit_values(chart_data: Iterable[Tuple[int | float | None, str | date]]) list[Tuple[int | float | None, str | date]]

limits values to ‘limit_number_of_entries_to’ Sums all other entries if show_all_others is set to True

:param chart_data :param show_all_others

Returns:

chart_data limited to ‘limit_number_of_entries_to’

property values: list[Tuple[int | float | None, str | date]]

Override in sub-classes if widget self-calculates data

property name: str

unique widget name, used as id for js

generate_color() Iterator[Tuple[int, int, int]]

Generates new colour from the palette Yields: tuples of (R, G, B) values Rules: - If exactly one institution is selected, use that institution’s effective palette. - If multiple institutions are selected and they all belong to the same group, use that group’s palette - Otherwise (different groups or ungrouped mix), fall back to the widget’s default palette.

generate_colors(number: int) List[Tuple[int, int, int]]

generates number of colours (RGB tuples) and returns them as a tuple

get_dataset_label() str

Override this method to change the default label

get_contrasted_dataset_styles() dict
  • Increase line thickness (borderWidth)

  • Ensure a strong borderColor is applied

  • Increase point radius and hover radius for better visibility

  • Harmonise point colours with the line colour

Returns:

get_datasets(*args, **kwargs) list[JchartDataset]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

property flattened_values: Tuple[int | float | None]

Override this method in multi series charts. Used in min_value, max_value & is_empty methods.

get_dataset_colors() List[Tuple[int, int, int]]

Generates colour (R, G, B) tuple for each item in the dataset

get_labels(*args, **kwargs) Sequence[str]

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

get_js_data() dict

Data available as javascript dict js_data in the widget

get_context_data() Dict[str, Any]

Context data for rendered widget template

class dashboard_widgets.widgets.chart.TargetValue(label: 'str', actual: 'int', target: 'int')
class dashboard_widgets.widgets.pie_chart.PieChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

dynamic_field_target: str | None = 'field_name'

Key of the field in the widgets config which is to be used for dynamic fields

config_defaults: dict[str, str | int | list | bool] = {'counter_logic': 'observations', 'dynamic_filtering': False, 'field_name': None, 'limit_number_of_entries_to': None, 'show_all_others': False, 'value_color_map': None}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

get_table_rows() Iterator[TableRow]

Yields values as table rows to be rendered underneath the pie chart in full-size view

get_dataset_colors() List[Tuple[int, int, int]]

Generates colour (R, G, B) tuple for each item in the dataset

property table_percentage_label

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

get_labels(*args, **kwargs)

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

class dashboard_widgets.widgets.pie_chart.RingChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'counter_logic': 'observations', 'field_name': None, 'limit_number_of_entries_to': None, 'show_all_others': None, 'value_color_map': None}

default values to be set in widget model’s config when initially created.

class dashboard_widgets.widgets.pie_chart.DetailPieChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

gives different values depending on full_size

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

property values: list[Tuple[int | float | None, str | date]]

Override in sub-classes if widget self-calculates data

class dashboard_widgets.widgets.pie_chart.QipStatusWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

dynamic_field_target: str | None = None

Key of the field in the widgets config which is to be used for dynamic fields

config_defaults: dict[str, str | int | list | bool] = {'issue_level': 'all'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property values: list[Tuple[int | float | None, str | date]]

Override in sub-classes if widget self-calculates data

get_dataset_colors() list[Tuple[int, int, int]]

Generates colour (R, G, B) tuple for each item in the dataset

class dashboard_widgets.widgets.pie_chart.QipClosureStatisticsWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Pie chart widget showing QIP issues closed on time vs closed late. - Closed on time: completed date <= due date - Closed late: completed date > due date

Only shows closed issues (qipstatus.closed = True)

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

dynamic_field_target: str | None = None

Key of the field in the widgets config which is to be used for dynamic fields

config_defaults: dict[str, str | int | list | bool] = {'issue_level': 'all'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

generate_timeliness_data() Iterator[Tuple[int | float | None, str | date]]

Generates data showing count of issues closed on time vs closed late. Only includes closed issues (qipstatus.closed = True) with both completed date and due date set.

property colors: list

Define colors for the chart segments

property values: list[Tuple[int | float | None, str | date]]

Override in sub-classes if widget self-calculates data

get_dataset_colors() list[Tuple[int, int, int]]

Generates colour (R, G, B) tuple for each item in the dataset

class dashboard_widgets.widgets.bar_chart.BarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A horizontal bar chart widget that displays count of different values of the specified “field_name”. field_name can be a hardcoded audit form field, or a custom field or a related form custom field.

Examples:

Hardcoded field:

{
  "field_name": "ward"
}

Custom field:

{
  "field_name": "test_select"
}

Related form Custom field:

{
  "field_name": "27_patient_name"
}
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'compliance': False, 'compliance_threshold': None, 'custom_compliance_levels': [], 'max_label_length': 70, 'show_sample_size': False}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

property table_percentage_label: str

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

get_dataset_colors()

Generates colour (R, G, B) tuple for each item in the dataset

values
get_labels(*args, **kwargs) Sequence[str]

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

class dashboard_widgets.widgets.bar_chart.VerticalBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A vertical bar chart widget that displays count of different values of the specified “field_name”. Has the same functionality as the horizontal bar chart.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

class dashboard_widgets.widgets.bar_chart.DetailBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

gives different values depending on full_size

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

values
class dashboard_widgets.widgets.bar_chart.SubformBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Bar chart showing compliance/number of entries in each subform

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'compliance': True}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

values
is_empty()

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

table_percentage_label
class dashboard_widgets.widgets.bar_chart.FieldBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Bar-chart showing compliance numbers for each field in the audit

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'compliance': True, 'counter_logic': 'observations', 'sort_order': None}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

calculate_field_count(field: CustomField | Field) int | None

Calculates field compliance or count for the field across observations

property table_percentage_label

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

values
class dashboard_widgets.widgets.bar_chart.OverTimeBarChartMixin

A mixin for line chart to convert it into a bar chart. - changes chart type to bar - fixes scaling so the bars are uniform size - handles bar colors - formats dates

class dashboard_widgets.widgets.bar_chart.ValueOverTimeBarWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

class dashboard_widgets.widgets.bar_chart.ComplianceOverTimeBarWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A Bar chart widget inheriting characteristics of a line chart to allow showing over-time trend.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

dynamic_field_target: str | None = 'field_names'

Key of the field in the widgets config which is to be used for dynamic fields

config_defaults: dict[str, str | int | list | bool] = {'counter_logic': 'observations', 'dynamic_filtering': False}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

property table_percentage_label

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

get_datasets(*args, **kwargs) list[dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_dataset_colors() Iterator[Tuple[int, int, int]]

Generates colour (R, G, B) tuple for each item in the dataset

class dashboard_widgets.widgets.bar_chart.MultiTeamBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'data_label_compliance_none': '0', 'data_label_compliance_none_desc': 'N/A', 'data_label_compliance_zero': '0', 'data_label_compliance_zero_desc': 'zero compliance', 'data_label_entries_none': '0', 'data_label_entries_none_desc': 'no data', 'filter_teams_with_no_compliance': True, 'zero_none_compliance': False}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property data_label_compliance_none: str

Label for categories that have data but no compliance

property data_label_compliance_none_desc: str

Description for label for categories that have data but no compliance

property data_label_compliance_none_colour: str

Colour for label for categories that have data but no compliance

property data_label_entries_none: str

Data label for categories where data is missing

property data_label_entries_none_desc: str

Description for label for categories where data is missing

property data_label_entries_none_colour: str

Colour for label for categories where data is missing

property data_label_compliance_zero: str

Data label for categories where compliance score is zero

property data_label_compliance_zero_desc: str

Description for label for categories where compliance score is zero

property data_label_compliance_zero_colour: str

Colour for label for categories where compliance score is zero

zero_none_compliance

Show all wards, even if they have no data

wards

Wards mapped by their id

populate_wards(chart_values: Iterable[Tuple[int | float | None, str | date, int]]) Iterable[Tuple[int | float | None, str | date, int]]

Replaces ward ids with ward names in the chart data, and fills in missing wards if necessary

create_chart_data_with_for_teams() Iterable[tuple[str, list[Tuple[int | float | None, str | date, int]]]]

Builds chart data for teams

Returns:

tuples containing team and tuples with compliance/count

get_datasets(*args, **kwargs) list[dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_labels() List[str]

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

values

Iterates over each serie in the dataset instead of returning the first available

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

get_series_meta(series: Iterable[Tuple[int | float | None, str | date, int]]) dict[str, list[str]]

Meta data to be used by frontend JS to configure chart appearance.

get_series_data(series: Iterable[Tuple[int | float | None, str | date, int]]) tuple[int | float | None]

Maps value for each series to the correct label, and initialising as zero if no value

get_custom_legend() dict

Get dictionary details to populate a custom additional legend in the jchart html template.

class dashboard_widgets.widgets.bar_chart.StackedBarWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'granularity': 'month'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

build_dataframe(field_name1: str, field_name2: str) DataFrame

Builds an aggregated DataFrame with columns [field_name1, field_name2, ‘count’] using SQL-level aggregation instead of fetching all observation rows.

create_stacked_chart_data(field_name1: str, field_name2: str, granularity: Granularity) dict | None

Creates data for a stacked bar chart Args: field_name1: name of the field that represent x axis field_name2: name of the field that should be represented as stacked along with y axis (count) granularity: date granularity for x-axis when field_name1 has date value qip_flag: a flag to specify if calculations for issues or not

get_field(name: str) CustomField | Field

Gets field by name

Returns:

Hardcoded or custom field

Raises:

KeyError – if the field does not exist

render_content() str

Render contents inside the widget :return: html safe string

get_table_rows(key: str) Iterator[TableRow]

Yields values as table rows to be rendered underneath the pie chart in full-size view

render_table()

Render table with values used in the pie chart

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

get_datasets(*args, **kwargs) List[Dict[str, Any]]

Returns list of datasets dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

class dashboard_widgets.widgets.bar_chart.CalculatedFieldBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Bar-chart showing calculated custom field data over time.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: Dict[str, Any] = {'operator': 'sum'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

property table_percentage_label: str | None

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

class dashboard_widgets.widgets.bar_chart.TargetsBarChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict = {'counter_logic': 'observations', 'target_breakdown': 'ward'}

default values to be set in widget model’s config when initially created.

dynamic_field_target: str | None = None

Key of the field in the widgets config which is to be used for dynamic fields

classmethod get_target_breakdowns(institution: Institution) Dict[str, str]

Get target breakdown options with dynamic labels.

classmethod get_target_breakdown_choices(institution: Institution) list[ChoiceSchema]

Generate choices for target breakdown with ward first.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

property table_value_label: str

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

property table_percentage_label: str

Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called.

values
get_datasets(*args, **kwargs) list[JchartDataset]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_dataset_colors() Iterator[Tuple[int, int, int]]

Colors the bars based on % of target met. >= 100% = green >= 50% = orange < 50% = red

get_dataset_label() str

Override this method to change the default label

class dashboard_widgets.widgets.line_chart.LineChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'compliance': False, 'extra_dataset': None, 'show_events': False}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

field_weights

maps field names to their compliance weights

property subform_weights: dict[str, float]

Maps subform names to their compliance weights. Returns empty dict by default. Override in subclasses that support subforms.

property calculation_type: Literal['compliance', 'count', 'session-count']

Determine what to calculate from the dataset: number of sessions, observations, or compliance; depending on wiget config

values
get_annotation_options() dict

Options for the annotation plugin https://github.com/chartjs/chartjs-plugin-annotation#configuration

get_labels(*args, **kwargs) list[str | date]

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

get_dataset_colors()

Generates colour (R, G, B) tuple for each item in the dataset

get_datasets(*args, **kwargs) list[dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

class dashboard_widgets.widgets.line_chart.MultiSeriesLineChart(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

property subform_weights: dict[str, float]

Maps subform names to their compliance weights. Returns empty dict by default. Override in subclasses that support subforms.

subforms

Queryset of subforms, if subform names were designated in widget config

values
get_datasets(*args, **kwargs) List[Dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_labels(*args, **kwargs) List[str | date]

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

get_multi_series_labels() Sequence[datetime]

Gets labels for multi series chart: sorts and removes duplicates. This method shouldn’t overridden by any method which changes the label format.

flattened_values
get_table_rows() Iterator[TableRow]

Yields values as table rows to be rendered underneath the chart in full-size view

class dashboard_widgets.widgets.line_chart.ComplianceOverTimeWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Alias to line chart to allow custom dashboards distinguish it from the regular line chart

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

dynamic_field_target: str | None = 'field_names'

Key of the field in the widgets config which is to be used for dynamic fields

config_defaults: dict[str, str | int | list | bool] = {'compliance': False, 'counter_logic': 'observations', 'dynamic_filtering': False, 'extra_dataset': None, 'show_events': False}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

class dashboard_widgets.widgets.line_chart.ValueOverTimeLineChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Alias to line chart to allow custom dashboards distinguish it from the regular line chart. Shows integer value changes over time

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: Dict[str, Any] = {'average': False, 'extra_dataset': None, 'granularity': 'month', 'group_data_by': None, 'show_events': False, 'static_lines': []}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

values
labels

Extracts and deduplicates time period labels across all grouped series.

When data is grouped (e.g., by ward), each group may have different time periods. This method collects all unique labels from all groups, sorts them, and deduplicates them while preserving sort order. This ensures the chart displays all time periods consistently across all series, with None values filling gaps where a series has no data for a particular time period.

Returns:

Sorted list of unique time period labels

flattened_values
get_labels(*args, **kwargs) List[str | date]

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

get_datasets(*args, **kwargs) List[Dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_table_rows() Iterator[TableRow]

Yields values as table rows to be rendered underneath the chart in full-size view

class dashboard_widgets.widgets.line_chart.ControlChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Widget for rendering control charts. a control chart is a statistical tool used to monitor a process and detect any unusual variations or trends. It helps ensure that a process remains within the specified limits of variation and maintains its stability.

This Chart will display the provided data along with the mean, upper and lower limits as fixed lines calculated based on the current data.

Accepts compliance, granularity and field names for specific form and renders the chart

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: Dict[str, Any] = {'compliance': True, 'field_name': None, 'granularity': 'month'}

default values to be set in widget model’s config when initially created.

dynamic_field_target: str | None = None

Key of the field in the widgets config which is to be used for dynamic fields

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

get_datasets(*args, **kwargs) List[Dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

class dashboard_widgets.widgets.line_chart.TrendMetricVisualizerWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Used to either show a real trend metric & alert in action or to create / optimize a trend metric & alert. Default is not to use a real trend metric & alert & to instead use the many options below. If using a real alert ‘value’ & ‘alert_operator’ are ignored. If using a real trend metric ‘field’ to ‘smoothing’ will be ignored.

Very useful to find good values to use for trend metric & alert given your specific data.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: Dict[str, Any] = {'alert': None, 'alert_operator': '>', 'fast_window': None, 'field': '_count', 'granularity': 'day', 'series_start_date': None, 'slow_window': None, 'smoothing': 3, 'trend_metric': None, 'trigger_as_switch': True, 'use_dashboard_wards': False, 'value': 0.1, 'wards': None}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

get_alert_data() tuple[Callable[[float, float], bool], float]

Gets real alert if one is specified or creates a temp one for the chart

timeseries_and_alert

Creates timeseries which is used for plotting. Uses a real metric or metric options if None. Uses a real alert or alert options to find where threshold is crossed.

events

Adds triggers as lines indicating approximately where the alert would first be triggered. Used if trigger isn’t a switch (shaded area).

values

Gets timeseries dataframe as GroupedChartValues

get_datasets(*args, **kwargs) List[Dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_table_rows() Iterator[TableRow]

Yields values as table rows to be rendered underneath the chart in full-size view

class dashboard_widgets.widgets.pareto_chart.ParetoChartWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'field_name': None, 'non_compliance': False}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property values: list[Tuple[int | float | None, str | date]]

Override in sub-classes if widget self-calculates data

get_datasets(*args, **kwargs) List[Dict[str, Any]]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

class dashboard_widgets.widgets.takeda.PatientTrackerBarChart(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Shows number of active / discontinued patients based on number of enrolled (added) minus discontinued entries.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

dynamic_field_target: str | None = None

Key of the field in the widgets config which is to be used for dynamic fields

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

num_discontinued

Number of discontinued patients

property num_active_patients: int

Number of active patients (= added - discontinued)

values
class dashboard_widgets.widgets.benchmark.BenchmarkOverTimeLineChart(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'average': True, 'field_names': []}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

get_datasets(*args, **kwargs) list[JchartDataset]

Returns list of dataset dictionaries https://github.com/matthisk/django-jchart#docs-get-datasets

get_labels(*args, **kwargs)

Returns categorical axis labels https://github.com/matthisk/django-jchart#docs-get-labels

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

Table Widgets

class dashboard_widgets.widgets.table.TableCellValue(value, value_txt)

Create new instance of TableCellValue(value, value_txt)

value

Alias for field number 0

value_txt

Alias for field number 1

class dashboard_widgets.widgets.table.TableField(name: str, label: str)

Represents a table column for a field that is neither a model field, nor a custom field

Create new instance of TableField(name, label)

name: str

Alias for field number 0

label: str

Alias for field number 1

class dashboard_widgets.widgets.table.Ordering(field, reversed)

Create new instance of Ordering(field, reversed)

field: CustomField | Field

Alias for field number 0

reversed: bool

Alias for field number 1

class dashboard_widgets.widgets.table.TableMixin

Adds table to widget

get_table_rows() Iterator[TableRow]

Yields values as table rows to be rendered underneath the pie chart in full-size view

render_table() str

Render table with values used in the pie chart

class dashboard_widgets.widgets.table.TableItem(*, classes: Sequence[str] = ())

Base class representing an element rendered in table widget

class dashboard_widgets.widgets.table.TableRow(cells: Iterable[TableCell] = None, **kwargs)

Represents table widget row, contains cells stored in a tuple

static from_dataframe(df: DataFrame) Iterator[TableRow]

Converts rows in the dataframe to table rows index is ignored.

class dashboard_widgets.widgets.table.TableCell(plain_text: str = None, html_text: str | None = None, value: Any = None, style: str = '', **kwargs)

Represents table cell. Contains its value and methods of rendering the value to html or plain text

Parameters:
  • plain_text – value of the cell expressed as plain text

  • html_text – rich text representation of the value (in safe html string format)

  • value – raw python value stored in this cell

  • style – arbitrary css style of the cell

  • kwargs – any additional args for the base class

property html: str

Html contents of this cell or fall back to plain text if it has not html representation. Use this to render cell contents into html documents.

property text: str

Plain text contents of this cell. If cell has no text, return string representation of its value. Use this to render cell contents as plain text into csv or other non-html documents.

class dashboard_widgets.widgets.table.TableWidgetHighlightMixin

Adds text search highlighting widgets. Only works with table widget. Highlghting is done in javascript using mark.js. This mixin adds the needed js and adds logic to determine which columns to highlight. Text should be highlighted if the answers for a searchable custom field are shown (use_for_filtering set to exact / partial search)

should_highlight: bool = True

set if widget should highlight text on search

single_field_widget: bool = False

set if the widgets data is derived from one field / question

highlight_column_names: bool = False

set if search result filters columns

class dashboard_widgets.widgets.table.TableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

property show_numbering: bool

Whether columns in this table should be numbered (starting at 1)

property urlize_text: bool

Whether links in the text should be clickable

should_truncate_text(text: str) bool

Determine if cell content should be truncated. Returns True for text content longer than truncation_char_limit.

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

property column_styles: dict[str, list[str]]

Extra styles added to cells in columns, mapped by column name to list of specific column styles

render_cell(col: int, cell: TableCell, column_name: str | None = None) SafeString

Renders HTML for table cell

Parameters:
  • col – table column index

  • cell – cell to be rendered

  • column_name – name of column cell is rendered in

is_confidential

Check if the form type has confidential label configured

generate_filename(base_name: str, extension: str) str

Generate filename with confidential suffix if needed

Parameters:
  • base_name – base name for the file

  • extension – file extension (e.g., ‘csv’, ‘xls’)

Returns:

filename with confidential suffix if applicable

get_limit_message() str

Message to be appended to the table if number of rows exceeds the hard limit

render_content() str

Render contents inside the widget :return: html safe string

add_confidential_header(sheet: Worksheet) int

Add confidential header to the first row of Excel sheet.

Parameters:

sheet – xlwt worksheet object

Returns:

Row offset (1 if header was added, 0 if not) for subsequent content positioning

class dashboard_widgets.widgets.table.AnswerTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A table widget which displays answers to questions in this Form in a table.

Supported types of columns (definde in field_names): * form fields:

  • hardcoded fields (ward, auditor, etc)

  • hardcoded questions (e.g. hand hygiene form fields)

  • custom fields

Additional columns can be added by enabling various options: * show_numbering - prepends first column with sequence number * edit_link - appends a column with Edit button for each row

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'clickable_links': False, 'compliance_rate': '', 'edit_link': False, 'field_names': None, 'highlight_compliance': False, 'javascript_searchbar': False, 'latest_review_date': False, 'max_entries': 500, 'next_review_date': False, 'ordering_field': None, 'show_change_not_viewed_alert': False, 'show_empty_rows': False, 'show_issues': False, 'show_issues_state': False, 'show_numbering': False, 'show_review_count': False, 'sub_observation': None, 'web_request_lookup_value': ''}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

classmethod has_web_request_tracking(audit_form: AuditForm) bool

Check if web request tracking exists for this form by looking for workflows with web request operations.

Parameters:

audit_form – Audit Form

Whether the javascript searchbar should be enabled based on the widget config. Automatically False if there is no request object, or the page is not a Public Dashboard.

web_request_lookup

Returns web_request_lookup value from the widget configuration.

property urlize_text: bool

Whether links in the table should be clickable

is_empty() bool

Pre-fetches rows to populate total_row_count when not yet set, avoiding separate COUNT / EXISTS queries.

render_content() str

Override to render body first, populating total_row_count via the COUNT(*) OVER() window function before is_empty or num_items are evaluated. This eliminates separate COUNT and EXISTS queries.

get_context_data() Dict[str, Any]

Pre-render content so total_row_count is populated before get_subtitle accesses num_items.

answer_issues

Maps field name and observation id to the number of QIP issues raised. count will also be 0 if show_issues config is False

ordering

If ordering field is specified, returns a tuple of: * ordering field instance * boolean determining whether order is reversed (descending)

property show_numbering: bool

Whether columns in this table should be numbered (starting at 1)

property use_empty_row_filter: bool

Boolean property to denote whether or not to apply empty row filter

apply_empty_row_filter() Q

Build Q object to exclude rows with empty field values

property annotate_compliance: bool

Whether queryset should be annotated with compliance data

compliance_range

Selected compliance range to filter observations by

Returns:

a tuple representing lower bound and upped bound (float values between 0.0 and 1.0) if compliance_rate option is set on the widget.

num_items

Total row count from total_row_count.

Raises:

RuntimeError – if rows have not been evaluated yet.

get_limit_message() str

Message to be appended to the table if number of rows exceeds the hard limit

get_fields() Sequence[CustomField | Field | TableField]

List of mixed models.Field and custom Field instances

field_names_set

Cached set of field names for O(1) lookup in hot paths.

has_field(field_name: str) bool

Check if field is enabled (O(1) lookup).

headings

Extract heading texts from heading_field_map

heading_field_map

Maps each heading text to its corresponding field (or None for extra headings)

render_head() SafeString

Override to add MEG AI feature indicators to column headers for CustomFields

observations_qs
filter_by_latest_web_request(qs: ObservationQuerySet, lookup_value: str, date_range: DateTimeRange | None = None) ObservationQuerySet

Filters latest web requests for answer table using the configured lookup value.

Parameters:
  • qs – QuerySet of observations to filter

  • lookup_value – String value to match against web request payloads

  • date_range – Optional date range to pre-filter logs (improves performance when available)

build_review_data_lookup(item_pks: list[int], content_type: ContentType) tuple[dict[int, int], dict[int, datetime]]

Build lookup dicts for review counts and latest dates.

Returns:

Tuple of (review_counts, latest_review_dates) where review_counts maps observation PK to review count, and latest_review_dates maps PK to most recent review datetime.

build_issue_counts_lookup(item_pks: list[int], content_type: ContentType, items_model: type) Counter[int]

Build lookup of pending issue counts per observation. Includes issues on sub-observations when the form has subforms. Uses IssueQueryset.for_observations to reuse existing filtering logic.

build_change_alert_lookup(item_pks: list[int], content_type: ContentType) set[int]

Build a set of item PKs that have unseen changes. Replaces correlated subqueries for show_change_not_viewed_alert with bulk queries.

Returns:

Set of item PKs where the latest change is newer than the user’s last view/change action.

attach_bulk_data_to_items(items: list[BaseAuditModel | BaseSubObservation], content_type: ContentType, items_model: type) None

Fetch auxiliary data and attach to items in a single iteration. Only fetches data for columns that are actually enabled.

items

Items (rows) in this table

Returns:

a queryset of observations or sub-observations depending on config

ward_id_institution_slug_map

Maps ward ID to institution slug using single query

generate_rows(page: int | None = None, page_size: int | None = None) Iterator[TableRow]

Returns generator of row items

get_rows() Iterable[TableRow]

Returns table rows. Returns pre-fetched rows when available (populated by is_empty() for non-render paths), otherwise generates rows using infinite scrolling (first page only) unless printing, exporting, infinite scroll is explicitly disabled, or javascript_searchbar is enabled.

action_load_more_rows(request: HttpRequest, **kwargs) HttpResponse

AJAX endpoint for infinite scrolling - returns HTML rows for the next page.

compliance_calculator

Provides fall-back compliance calculator for when subform compliance cannot be extracted from observation’s compliance dict.

class dashboard_widgets.widgets.table.GroupedAnswerTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Table widget showing any the count for any answer grouped by ward, department or institution

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

single_field_widget: bool = True

set if the widgets data is derived from one field / question

highlight_column_names: bool = True

set if search result filters columns

config_defaults: dict[str, str | int | list | bool] = {'group_by': 'ward'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

get_group_value_statements() tuple[F, F]

returns an ORM “F” statement pair for value and label for any selected “group by” option

Returns:

ORM F for group by value, ORM F for group by label

class dashboard_widgets.widgets.table.IssueTypeMixin

For use in widgets that have the issue_type config option. Filters available issues by the selected issue_type.

class dashboard_widgets.widgets.table.QipCommonIssuesWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Table widget showing most common issues by looking at qip.models.Issue objects with most common ‘comment’ value.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

class dashboard_widgets.widgets.table.QipConfigurableTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Table widget allowing issues to be grouped in columns and rows by any field on the issue model, hardcoded or custom.

Implemented in Task #26468

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

render_pandas_table: bool = False

Whether to render dataframe into html. This overrides TableWidget’s rendering logic.

config_defaults: dict[str, str | int | list | bool] = {'column_field': 'qipstatus', 'issue_type': 'all', 'row_field': 'ward'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

row_label

Gets the label for a hardcoded field on the Issue model, or custom issue field.

headings

Get headings with custom labels for both row and column headers.

build_dataframe() DataFrame

Users of this mixin should override this method to build the dataframe. The method will be called only once.

Do not call this method directly, use the dataframe property.

class dashboard_widgets.widgets.table.TimeTrackingTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

An answer table which only shows observations which have had specific actions performed on them. A column is prepended to the table data to show the time taken for the action to be performed.

Widget implemented in Task #24426

Example configuration:

{
    "start_time": {"time_viewed": ""},
    "end_time": {"int_field": 20},
    "metric_verb": "Updated"
}
start_time

The time that a particular action was performed on an observation. This can be when it was created, updated, viewed, reviewed, or updated with a particular field/answer combination.

end_time

The time that a particular action was performed on an observation. This can be when it was created, updated, viewed, reviewed, or updated with a particular field/answer combination.

metric_verb

Used in the widget frontend to describe the activity that’s being tracked. The default is “Viewed” (the time taken to view an observation after creation).

Possible values for start_time and end_time:

  • {"time_created": ""} The time that the observation was created.

  • {"time_viewed": ""} The first time the observation was viewed by anybody.

  • {"time_changed": ""} The first time that the observation was changed by anybody.

  • {"time_reviewed": ""} The first time that the observation was reviewed by anybody.

  • {"field_name": "answer"} The first time that the observation was updated with a particular field/answer combination.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'edit_link': False, 'end_time': {'time_viewed': ''}, 'metric_verb': 'Viewed', 'start_time': {'time_created': ''}}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

metric_verb: str
get_fields() Sequence[CustomField | Field | TableField]

List of mixed models.Field and custom Field instances

actioned_observations() Iterable[tuple[int, timedelta]]

Iterator yielding tuples representing observation id and time difference as tuples for each observation

observation_times: Dict[int, str] | None

dictionary mapping observation ids to their respective time deltas

class dashboard_widgets.widgets.table.TargetsTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'actual_label': 'Actual', 'counter_logic': 'observations', 'target_breakdown': 'ward', 'target_label': 'Target'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

headings_and_column_map

Creates headings for chart and adds a map for each heading from data name to display name

dataframe
render_table() str

Render the dataframe as <table>. This is a custom logic invoked when render_pandas_table is True.

class dashboard_widgets.widgets.table.LFPSEAnswerTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

An implementation of AnswerTableWidget that displays LFPSE specific columns:

  • lfpse_errors

  • lfpse_reference

Either or none of the LFPSE specific field_names can be included.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

class dashboard_widgets.widgets.table.FieldComplianceTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Table widget showing number of observations and compliance for each answer to a given field

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

class dashboard_widgets.widgets.table.ObservationEmailTrackingTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'max_entries': 500}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property headings: Sequence[str]

Extract heading texts from heading_field_map

generate_rows(page: int | None = None, page_size: int | None = None) Iterator[TableRow]

Returns generator of row items

class dashboard_widgets.widgets.table.MetricOvertimeSummary(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Compares the answer values for a specific field of observations for a specific form over time. Groups observations by a text field. Only supports a single form, but will work in an overview dashboard if filtered by form_id. Does not support subforms.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'granularity': 'month', 'grouper_field_name': '', 'static_color_map': [], 'value_field_name': ''}

default values to be set in widget model’s config when initially created.

render_pandas_table: bool = False

Whether to render dataframe into html. This overrides TableWidget’s rendering logic.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

if widget extends ConfigSchemaWidgetMixin, add granularity dropdown. To work, this mixin must be added before ConfigSchemaWidgetMixin.

fields

Takes the selected value and grouper field names and pulls the fields they belong too. Required for highlighting, real fields need to be checked for searchablility.

build_dataframe() DataFrame

Users of this mixin should override this method to build the dataframe. The method will be called only once.

Do not call this method directly, use the dataframe property.

class dashboard_widgets.widgets.ward_compliance.WardCell(ward: Ward | Department, is_best: bool, **kwargs)
Parameters:
  • plain_text – value of the cell expressed as plain text

  • html_text – rich text representation of the value (in safe html string format)

  • value – raw python value stored in this cell

  • style – arbitrary css style of the cell

  • kwargs – any additional args for the base class

class dashboard_widgets.widgets.ward_compliance.WardComplianceWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

model

alias of Ward

config_defaults: dict[str, str | int | list | bool] = {'counter_logic': 'observations'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

trend_img(trend: float | None) str

Renders arrow image for the trend value

property compliance_range: tuple[float, float] | None

Selected compliance range to filter observations by

ward_dataframe

Dataframe holding ward data required for the table

class dashboard_widgets.widgets.ward_compliance.DepartmentComplianceWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

model

alias of Department

class dashboard_widgets.widgets.question_compliance_table.QuestionRow(field, observation, result)

Create new instance of QuestionRow(field, observation, result)

field

Alias for field number 0

observation

Alias for field number 1

result

Alias for field number 2

class dashboard_widgets.widgets.question_compliance_table.ComplianceData(compliant, total, compliance)

Create new instance of ComplianceData(compliant, total, compliance)

compliance

Alias for field number 2

compliant

Alias for field number 0

total

Alias for field number 1

class dashboard_widgets.widgets.question_compliance_table.QuestionComplianceTableWidget(*args, **kwargs)
support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'issue_level': 'all'}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

set_question_ignored(field: CustomField, observation: BaseAuditModel | BaseSubObservation) None

Controls if a field has been ignored for compliance calculation for every observation. Ensures that if an observation has not ignored a field, it won’t later be marked as ignored by subsequent observations.

Parameters:
  • field – The CustomField.

  • observation – The observation or sub observation to check.

render_cell(col: int, value: TableCell, column_name: str | None = None) SafeString

Renders HTML for table cell

Parameters:
  • col – table column index

  • cell – cell to be rendered

  • column_name – name of column cell is rendered in

get_context_data()

Context data for rendered widget template

class dashboard_widgets.widgets.kpi.KpiTableWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Key Performance Indicators widget. Allows configuration to define target compliance for each field and render table showing field by quarter heatmap representing compliance (or average value of the field) for given quarter and whether target was met.

Implemented in Task #23977

Example config:

{
  "granularity": "quarter",
  "targets": {
    "desks": 0.8,
    "phones": 0.5,
    "ethernet_wiring": 0.2
  },
  "target_margin": 0.1,
  "less_than_targets": [
    "ethernet_wiring"
  ]
}
targets

dictionary; maps field name to a target compliance (0.0 - 1.0 range). field name can be any field with compliance data, or a numeric field (int or float).

target_margin

float; Target will be marked as “met” (in orange) if within this margin. This value should be in 0.0-1.0 range.

target_margin_per_field

dictionary; maps field name to a target margin.

less_than_targets

list of field names where the target is to be below specified value.

use_compliance

If False, then field answer data is used for KPI calculation for all fields. If True and the field has compliance, the compliance data is used. This is the default behavior.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

config_defaults: dict[str, str | int | list | bool] = {'target_margin': 0.1, 'target_margin_per_field': {}, 'targets': {}, 'use_compliance': True}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property target_margin: dict[str, float]

Value for “close to achieving target”, marked as orange

class dashboard_widgets.widgets.accreditation.AccreditationLevelCell(level: AccreditationLevel, classes=('accreditation',))
Parameters:
  • plain_text – value of the cell expressed as plain text

  • html_text – rich text representation of the value (in safe html string format)

  • value – raw python value stored in this cell

  • style – arbitrary css style of the cell

  • kwargs – any additional args for the base class

class dashboard_widgets.widgets.accreditation.WardAccreditationWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Displays ward accreditation table based off Accreditation config specified in the config. Implemented in Task #26592, only supports customn observations.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

generate_form_compliances() Iterator[tuple[int, float]]

Generates tuples containing ward id and its compliance value

generate_subform_compliances() Iterator[tuple[int, float]]

Generates ward id and subform compliance for each ward and each subform.

Yields:

tuple containing (ward_id, compliance) where compliance is a non-null float, and ward_id is repeated for each subform.

property compliances: dict[int, list[float]]

Dictionary mapping ward ids to a list of form (or subform, depending on accreditation) compliances

class dashboard_widgets.widgets.audit_scores.AuditScoresWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Implemented in Task #25190, A table widget rendering compliance over time, grouped by a specific field (e.g., Department, Institution, or a Custom Field).

This widget aggregates observation compliance percentages and counts for specified time granularities (Day, Month, Year). It supports optional visualizations such as heatmaps for compliance levels and trend indicators.

Key Features:

  • Time-Series Aggregation: Groups data by the configured time granularity.

  • Custom Scoring: Can score by hardcoded fields (Department), custom form fields or subform.

  • Visual Styling: Optional heatmap coloring and trend arrows (up/down).

Example Output:

July 2025

August 2025

September 2025

October 2025

Department

Compliance

Count

Compliance

Count

Compliance

Count

Compliance

Count

Hand Hygiene Dept

0

82.5%

2

78.7% ↓

2

0

Test Department

77.6%

4

83.1% ↑

4

84.2% ↑

4

78.2% ↓

1

Average Score

77.6%

4

82.8% ↑

6

81.5% ↓

6

78.2% ↓

1

Supported Configuration Options:

granularity (str)

The time period to group columns by. Options: 'day', 'month', 'year'. Defaults to 'month'.

score_by (str)

The database field name used to group the rows and calculate compliance scores (e.g., department, institution, or custom field). If form has custom subforms, it’s also possible to score by subform.

Note

when scoring by “subform”, the behaviour changes from grouping observation compliance data, to grouping sub-observation compliance (Subform Compliance)

score_by_label (str)

A custom user-facing label for the first column header (e.g., “Ward” or “Department”).

show_count (bool)

If True, displays a “Count” column next to the “Compliance” column for each time period. Defaults to False.

heatmap (bool)

If True, applies color-coding (CSS classes) to compliance cells based on defined compliance levels.

trend_indicator (bool)

: If True, displays an arrow (↑ or ↓) indicating if the score has increased or decreased compared to the previous period.

show_total (bool)

If True, adds a summary row. Defaults to True.

Note

Totals are recalculated from source data, not column averages.
This ensures accuracy when observations overlap (e.g., multi-choice fields). See Retrieving Compliance Data.
counter_logic (str: “observations” | “audits”)

Defines the logic for the count metric (e.g., counting individual observations vs. audit sessions).

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'counter_logic': 'observations', 'granularity': 'month', 'show_total': True}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property show_count: bool

Whether to show the ‘Count’ column

property show_total: bool

Whether to show the final summary row

property heatmap: bool

Whether to color-code compliance cells

property score_by: Literal['audit_form', 'department', 'institution', 'subform'] | str

Field to score by. It’ll be one of the pre-determined options or a field name

property score_by_label: str

Label to display in the first column, describes what field we’re grouping by

Returns:

string or a lazy string

property score_by_subform: bool

Tells whether we’re scoring by subform.

property trend_indicator: bool

Whether to show trending arrow beside compliance figure

get_custom_field() CustomField | None

Instance of the custom field being scored by

property multi_institution: bool

Whether showing data from multiple institutions

property grouper_expr: F | Expression

Query expression used to determine grouper

property summary_label: Literal['Total Compliance', 'Average Score'] | str

Label used for the summary row - the last row.

Returns:

Label for summary score; either ‘Total Compliance’, or ‘Average Score’ (or localized translation of it)

Total Compliance

Means that resulting summary is computed from source. It’s the more accurate count. Task #30860

Average Score

Means that summary is computed by averaging data in the column, without accounting for number of observations (count column)

property time_bucket_expr: Expression

Query expression used to assign time bucket. Returned value aligns with the selected granularity

property subforms: QuerySet

Set of custom subforms to use when score_by=SUBFORM. Returns all subforms in the current audit form(s), but also respects filter form selection and narrows down forms if user has selected subforms.

Returns:

Queryset of subforms to score by

property item_order: list[str]

Defines order of rows, including the summary row If the grouper has a predefined order that needs to be respected, this property defines that order.

Returned values must match grouper values exactly. Any items in the dataframe not included in this order, will be placed after the ordered items and before the summary row.

calculate_average_score(scores: Iterable[tuple[date, str | int | bool | list | float | None, float | None, int]]) Iterator[tuple[date, str, float | None, int]]

Calculate average for each time bucket Returned dataset combines all data for each time bucket in a single data under a new grouper “Average “Score”.

The average score is calculated by averaging all compliance data, and counts are added up (sum).

Parameters:

scores – an iterable of tuples containing: time bucket (date), grouper, compliance, count. Must be ordered by date.

Yields:

Summary data (Compliance, count) of all groups for each time bucket

get_score_data() Iterator[tuple[date, str | int | bool | list | float | None, float | None, int]]

Builds Queryset returning score data and yields the results as tuples. An additional group “Average Score” is yielded at the end if show_total is enabled

Yields:

tuples: (date, grouper, compliance, count)

build_dataframe() DataFrame

Builds dataframe to be rendered as the table.

The resulting dataframe has multiindex columns (date / compliance) + (date / count) The index is the name of the grouper.

Example:

Department department

2023-01-01 compliance

2023-01-01 count

Test Department

1.0

5

Average Score

1.0

5

property index_mapping: dict[str, str]

Maps a value to its display name

format_index(value: Any, axis: Literal['columns', 'rows']) str

Format a DataFrame index/column label for display.

Parameters:
  • value – the value to be formatted

  • axis – whether this is a column heading (colums), or index value (rows).

render_content() str

Render contents inside the widget :return: html safe string

class dashboard_widgets.widgets.audit_scores.SubformFieldScoresWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A widget designed to display compliance trends for individual questions (fields) within a specific subform over time. Originally implemented in Task #30496.

Shows average compliance of question answers within each subform, and count of answers with compliance.

  • Average is not weighted, each answer is considered equally across observations

  • The count shows how many times the question was answered with an answer that has compliance data

The summary row shows “Total Compliance” - it’s a complete re-calculation of the subform’s compliance for the given time period, taking each sub-observation separately and averaging the result. The Count in the summary is the number of observations.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'granularity': 'month', 'subform': ''}

default values to be set in widget model’s config when initially created.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property score_by_label: str

Label to display in the first column, describes what field we’re grouping by

Returns:

string or a lazy string

property field_label_mapping: dict[str, str]

Dictionary mapping field names to their display labels.

Used by format_index to resolve unique field_name keys to human-readable labels at render time.

property item_order: list[str]

Return field names as the category order instead of labels.

format_index(value: Any, axis: Literal['columns', 'rows']) str

Override to show field labels when available, handling duplicate labels safely.

Parameters:
  • value – the index value to be formatted

  • axis – whether this is a column heading (columns), or index value (rows).

get_score_data() Iterator[tuple[date, str | int | bool | list | float | None, float | None, int]]

Yield score data tuples for each field in the subform.

Uses field_name as the grouper value to avoid duplicate category errors when multiple fields share the same label. Labels are resolved at render time via format_index.

class dashboard_widgets.widgets.audit_scores.FormFieldScoresWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Widget that renders the audit scores of a flat-form in a tabular format Inherits all the regular funtionality from the AuditScoresWidget like granularity, show_count, heatmap & trend_indicatior Also allows the user to specifiy which field names from they form, they would like to include in the scoring A form does not have to be specified in the config, instead the widget will render that form from the widget’s dashboard itself.

The final row shows “Average Score”: average compliance and total count. The average compliance is an average value of all above figures (without accounting for the count). And count is a sum of all the values above.

The Counts represent how many observations have an answer for given question with a value that has compliance data.

Config options:

field_names

Fields to show in the table

granularity

Date granularity for data aggregation

show_count

Whether to show how many times a field was answered with a value that has compliance data (i.e. not null)

heatmap

Whether to colour-code compliance cells based on its value

trend_indicator

Whether to show trend arrow comparing each compliance value with one from previous time bucket

Example appearance:

July 2025

August 2025

September 2025

October 2025

Compliance

Count

Compliance

Count

Compliance

Count

Compliance

Count

Compliance Field 1

0

82.5%

2

78.7% ↓

2

0

Compliance Field 2

77.6%

4

83.1% ↑

4

84.2% ↑

4

78.2% ↓

1

Average Score

77.6%

4

82.8% ↑

6

81.5% ↓

6

78.2% ↓

1

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'field_names': None, 'form': '', 'granularity': 'month'}

default values to be set in widget model’s config when initially created.

support_multi_form = False

Subclasses can override this to disable support for more than 1 audit form or no audit forms. Set this to false if the widget supports only exactly one form at a time.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property field_label_mapping: dict[str, str]

Dictionary mapping field names to their display name

property item_order: list[str]

Defines order of rows, including the summary row If the grouper has a predefined order that needs to be respected, this property defines that order.

Returned values must match grouper values exactly. Any items in the dataframe not included in this order, will be placed after the ordered items and before the summary row.

property summary_label: str

Label used for the summary row - the last row.

Returns:

Label for summary score; either ‘Total Compliance’, or ‘Average Score’ (or localized translation of it)

Total Compliance

Means that resulting summary is computed from source. It’s the more accurate count. Task #30860

Average Score

Means that summary is computed by averaging data in the column, without accounting for number of observations (count column)

property score_by_label: str

Label to display in the first column, describes what field we’re grouping by

Returns:

string or a lazy string

format_index(value: Any, axis: Literal['columns', 'rows']) str

Override to show custom field labels when available (handles duplicate indices safely) as per task: 32155

get_score_data() Iterator[tuple[date, str | int | bool | list | float | None, float | None, int]]

Builds Queryset returning score data and yields the results as tuples. An additional group “Average Score” is yielded at the end if show_total is enabled

Yields:

tuples: (date, grouper, compliance, count)

Heatmap widgets

dashboard_widgets.widgets.heatmap.HeatmapHeading

Represents a valid value for a heading (vertical or horizontal) value of a heading. It can be any valid answer type, but also a model instance, or a lazy string __proxy__ (Promise)

alias of str | int | bool | list | float | date | time | datetime | None | Model | ObservationFile | HeatmapField

dashboard_widgets.widgets.heatmap.HeatmapKey

a tuple of headings, identifying a position within the heatmap. Heading order within the tuple: (top, left)

alias of tuple[str | int | bool | list | float | date | time | datetime | None | Model | ObservationFile | HeatmapField, str | int | bool | list | float | date | time | datetime | None | Model | ObservationFile | HeatmapField]

class dashboard_widgets.widgets.heatmap.HeatmapCell(key: HeatmapKey = None, label: str | None = '', compliance: Compliance = None, count: int | None = 0, hint: str | None = '', compliance_levels: ComplianceLevels = (0.9, 0.8), classes: str = '')

Holds data for a cell within heatmap

static get_overall(cells: Iterable[HeatmapCell], **kwargs) HeatmapCell

Builds a summary cell for a number of cells by calculating average compliance (weighted by count)

property label_display_value: str

The label field formatted correctly for display.

For use with numeric cells formed from a summation or average of values. If the count for the number of values in the cell is 0 / None then we return the null placeholder ‘-‘. If the label is one of the NULL types (e.g. empty string / None) we return ‘-‘. Otherwise it returns the label.

property field_has_multiple_values: bool

Return true if there is a field in key that has multiple values input

property field_has_partial_compliance: bool

Return true if there is a field in key that has multiple values input

dashboard_widgets.widgets.heatmap.HeatmapValues

Maps heatmap position (top, left) to a value

alias of dict[tuple[str | int | bool | list | float | date | time | datetime | None | Model | ObservationFile | HeatmapField, str | int | bool | list | float | date | time | datetime | None | Model | ObservationFile | HeatmapField], HeatmapCell]

class dashboard_widgets.widgets.heatmap.HeatmapField(name: str, heatmap: HeatmapWidget)

Represents single field used for either heatmap’s X (left) or Y (top) axis and provides utility methods for handling its values.

make_key(value: SerializedAnswer) Answer

Convert raw key (serialized value) to a value that will be recognized by heatmap as matching axis values

get_annotation() F | Func

Provides SQL expression to annotate an observation queryset with a value of this field.

For field that have multiple values (multichoice, m2m relations), this will result in the queryset containing multiple rows for some observations, one for each choice - this is desired behaviour for the heatmap so that the observation can represent all relevant cells, one for each value. This also means that some obsevations will be excluded from the queryset if they don’t have any choices in the m2m relation or no answers in the multichoice field.

Returns:

SQL Expression

get_value(observation: BaseAuditModel) str | int | bool | list | float | None

Gets raw field value from observation

get_answer(observation: Observation) Answer

Gets answer from observation; converts choices to human-readable representation, and for foreign keys gets the referenced object instance

property choices: dict[SerializedAnswer, Answer]

Mapping of value to display value in this field

property choices_qs: QuerySet | None

if the field is a foreign key, this property provides a base queryset for the linked model.

property values: Iterable[Answer]

returns unique values for this field from observations. For choice fields it simply returns all possible choices. For fk fields, it returns all referenced objects as a queryset

property values_map: dict[SerializedAnswer, Answer]

Dictionary mapping (serialized) values present in this field to their (object) answer / human-readable answer. Note that some answers may not be present in this dict, and the dict will be empty for some types of fields, so you should fall back to the value (key) if corresponding answer is not present.

class dashboard_widgets.widgets.heatmap.HeatmapFields(left, top)

Create new instance of HeatmapFields(left, top)

left: HeatmapField

Alias for field number 0

top: HeatmapField

Alias for field number 1

make_key(top: SerializedAnswer, left: SerializedAnswer) HeatmapKey

Makes a pair of keys from a pair of raw values

class dashboard_widgets.widgets.heatmap.HeatmapWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A heatmap widget that given two fields renders a grid where each cell represents average compliance with the corresponding values.

The base implementation takes two fields as the configuration (configured as field_name and field_name_top) And represents compliance for each combination of values in those fields. The selected fields are wrapped in HeatmapField that represents the field, but subclasses of the heatmap can override the behaviour to represent other non-field concepts or models linked by m2m fields.

Configuration options

field_name

name of the field to use as the left heading (rows)

field_name_top

name of the field to use as the top heading (columns)

overall_compliance

whether to add an extra row and column at the ends, summarizing the overall compliance in each serie

pivoted

whether to swap columns/rows (pivot 90°)

value_field

use value from a numeric field instead of compliance

Example configuration json
{
  "pivoted": false,
  "field_name": "audit_form",
  "value_field": "",
  "field_name_top": "ward",
  "show_empty_rows": false,
  "overall_compliance": false,
  "headings_left_label": ""
}
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'field_name': 'auditor', 'field_name_top': 'ward', 'headings_left_label': '', 'overall_compliance': False, 'show_count': False, 'show_empty_rows': False, 'value_field': ''}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property value_field_aggregation: Literal['sum', 'avg']

Aggregation to use for one cell over its values, either a sum or an average

property overall_compliance: bool

Whether to show “Overall Compliance” row at the start

property show_count: bool

Whether to show the entry on row intersections

property show_empty_rows: bool

whether heatmap rows with no compliance should still render

property headings_left_label: str

Text displayed in the corner cell between top and left headings

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

get_headings_top(start: int | None = None, max_cols: int | None = None) Iterable[HeatmapHeading]

Gets the column headers. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

render_head(start: int | None = None, max_cols: int | None = None) str

Renders the thead html. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

generate_row_cells(heading_left: HeatmapHeading, start: int | None = None, max_cols: int | None = None) Iterator[HeatmapCell]

Generates cells for given left heading. Yields tuple of top heading and cell value

Parameters:
  • heading_left – The row header to generate the cells for.

  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

render_body(start: int | None = None, max_cols: int | None = None) SafeString

Renders the tbody html. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

get_context_data() Dict[str, Any]

When printing the heatmap is broken into separate tables to allow all the columns to fit into the printable window.

property compliance_levels_map: dict[AuditForm, ComplianceLevels]

Maps audit form instance to its compliance levels. Only forms that have a config object are mapped Returns empty dict if this heatmap does not use audit form field.

NOTE: It is assumed that Audit forms are located on the left axis.

calculate_overall(values: HeatmapValues) HeatmapValues

Computes average value and total count for each column and returns a new values dict representing the overall data. Resulting values dict will be keyed with each heading and “Average” string so that it can be merged into the heatmap dict.

Parameters:

values – heatmap values dict

Returns:

Overall values dict

class dashboard_widgets.widgets.heatmap.FieldComparisonCountWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A field comparison widget that given two fields renders a grid where each cell represents count of each intersecting values from both fields, the background color of each cell can also be customized using static color map attribute

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'field_name': 'auditor', 'field_name_top': 'ward', 'headings_left_label': '', 'overall_count': False, 'show_empty_rows': False, 'static_color_map': [], 'value_field': '', 'value_field_aggregator': 'sum'}

default values to be set in widget model’s config when initially created.

property overall_compliance: bool

Whether to show “Overall Count” row at the start

property value_field_aggregation: Literal['sum', 'avg']

Determines whether to Sum or Average the data. Required because summing is correct for counts (e.g. “Total Incidents”), but incorrect for rates (e.g. “Compliance %”), which should be averaged.

render_body(start: int | None = None, max_cols: int | None = None) SafeString

Renders the tbody html. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

class dashboard_widgets.widgets.ward_heatmap.RenderLabelMixin

Adds a method for rendering cell label based on the show_numerator, show_numerator_for_partial_compliance, and show_count_on_audit_form properties.

These properties can be overriden through config options of the same name, or as properties.

property show_numerator: bool

Whether the label will show the numerator (x/y) besides the percentage otherwise only the percentage will appear

property show_numerator_for_partial_compliance: bool

Whether the label will show the numerator (x/y) besides the percentage for fields with partial compliance

property show_count_on_audit_form: bool

Whether numerator will show as count (y) when field_name is “audit_form” otherwise it will always show as (x/y)

render_label(cell: HeatmapCell, numerator: int | None = None, denominator: int | None = None) str

Render label as percentage or percentage (denominator) or percentage (numerator/denominator) based on mixin attributes

Don’t show numerator even when enabled when field has multiple values or field has partial compliance setup #31263

class dashboard_widgets.widgets.ward_heatmap.WardHeatmapWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A heatmap widget where top headings are hardcoded for wards. The ward heading provides collapsible department/ward hierarchy.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'field_name': 'auditor', 'headings_left_label': '', 'overall_compliance': False, 'pivoted': False, 'show_empty_rows': False, 'value_field': ''}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

get_split_dept_colspans(start: int, max_cols: int) Counter

Return the Counter object that records the ‘colspan’ attribute for a Department <td> element when a large table is split into smaller tables (eg. for the Print View). Create and store the Counter object on first query. Update the counter if ‘max_cols’ changes.

render_heading(value, colspan: int = 1, rowspan: int = 1, classes='', attrs: Sequence[str] = ()) str

Renders the heading as a <th> tag

Parameters:
  • value – the heading value or label to render

  • colspan – add colspan to the tag

  • rowspan – add rowspan to the tag

  • classes – classes to add to the HTML tag

  • attrs – any additiona attributes. Will be inserted directly into the tag, separated by spaces

Returns:

rendered heading

generate_row_cells(heading_left: HeatmapHeading, start: int | None = None, max_cols: int | None = None) Iterator[HeatmapCell]

Generates cells for given left heading. Yields tuple of top heading and cell value

Parameters:
  • heading_left – The row header to generate the cells for.

  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

render_head(start: int | None = None, max_cols: int | None = None) str

Renders the thead html. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

render_row(heading_left: HeatmapHeading, cells: str, classes: str) SafeString

Renders the <tr> tag with given content

Parameters:
  • heading_left – heading representing this row (the first cell)

  • cells – HTML content of the row

  • classes – any css classes to be applied to the row

Returns:

rendered table row

render_body(start: int | None = None, max_cols: int | None = None) SafeString

Renders the tbody html. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

get_context_data() Dict[str, Any]

When printing the heatmap is broken into separate tables to allow all the columns to fit into the printable window.

class dashboard_widgets.widgets.ward_heatmap.HeatmapTeamField(name: 'str', heatmap: 'HeatmapWidget', team_names: 'list[str]')
get_value(observation: Observation) SerializedAnswer

Gets raw field value from observation

get_answer(observation: Observation) Answer

Gets answer from observation; converts choices to human-readable representation, and for foreign keys gets the referenced object instance

values
teams_by_userid

maps user ids to their respective teams

get_annotation() F

Provides SQL expression to annotate an observation queryset with a value of this field.

For field that have multiple values (multichoice, m2m relations), this will result in the queryset containing multiple rows for some observations, one for each choice - this is desired behaviour for the heatmap so that the observation can represent all relevant cells, one for each value. This also means that some obsevations will be excluded from the queryset if they don’t have any choices in the m2m relation or no answers in the multichoice field.

Returns:

SQL Expression

class dashboard_widgets.widgets.ward_heatmap.WardTeamHeatmapWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'overall_compliance': False, 'pivoted': False, 'show_empty_rows': False, 'team_names': []}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

class dashboard_widgets.widgets.ward_heatmap.HeatmapMultiField(name: 'str', heatmap: 'HeatmapWidget', fields: 'tuple[HeatmapField]')
property values: Iterable[Answer]

returns unique values for this field from observations. For choice fields it simply returns all possible choices. For fk fields, it returns all referenced objects as a queryset

class dashboard_widgets.widgets.ward_heatmap.WardQuestionHeatmapWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A heatmap mapping each question against each ward. Each cell shows compliance for the ward in context of just that one question. This widget supports only custom fields since they are the only fields that provide compliance information

Example appearance:

💡 Click on Departments to collapse / expand Wards data [Collapse All] [Expand All]

Overall Compliance

Test Department - meg-staff ^

Test Ward

Overall Compliance

80.3% (404/504)

80.3% (404/504)

Desks

89.9% (107/119)

89.9% (107/119)

Phones

50.9%

50.9%

Ethernet wiring

82.0% (100/122)

82.0% (100/122)

Door locked

93.3% (140/150)

93.3% (140/150)

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'field_names': [], 'headings_left_label': '', 'overall_compliance': False, 'pivoted': False, 'select_dynamic_fields': False, 'show_numerator': True, 'sub_observation': ''}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property dynamic_field_selection: bool

Whether to allow the user to click on fields in the heatmap to apply them as dynamic filter on this dahboard

Returns:

True if the widget setting is enabled, and widget is not currently opened in full-view

selected_field_name

Name of field selected by clicking a cell (if any)

render_row(heading_left: HeatmapHeading | HeatmapField, cells: str, classes: str) SafeString

Renders the <tr> tag with given content

Parameters:
  • heading_left – heading representing this row (the first cell)

  • cells – HTML content of the row

  • classes – any css classes to be applied to the row

Returns:

rendered table row

prefetch_subobservations_answer_counts() Dict[int, Counter]

Prefetch sub-observations and calculate field answer counts efficiently.

get_headings_top(start: int | None = None, max_cols: int | None = None) Iterable[HeatmapHeading]

Gets the column headers. Can limit the columns displayed when printing by specifying both start and max_cols.

Parameters:
  • start – Optionally specify the index of the first header to limit the headers returned.

  • max_cols – Optionally specify the max number of columns to limit the headers returned.

class dashboard_widgets.widgets.ward_heatmap.HeatmapSubformField(name: 'str', heatmap: 'HeatmapWidget', subforms: 'Sequence[Subform]')
get_value(observation: Observation) SerializedAnswer

Gets raw field value from observation

get_answer(observation: Observation) Sequence[Subform]

Gets answer from observation; converts choices to human-readable representation, and for foreign keys gets the referenced object instance

property subform_field_pairs: Iterator[tuple[Subform, Sequence[str]]]

Iterates subforms paired with a set of field names in that subform

subforms_by_observation

Dictionary mapping observation ids to set of sub-subforms in that observation

values
class dashboard_widgets.widgets.ward_heatmap.WardSubformHeatmapWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A heatmap mapping each subform against each ward Each cell shows compliance for the ward in context of single subform

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'overall_compliance': False, 'pivoted': False, 'show_count_on_audit_form': True}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

single_custom_form_with_weighted_compliance

Returns true if the widget is form specific and that form uses observation or subobservation weighted compliance.

Summary widgets

class dashboard_widgets.widgets.report_summary.PublicReportSummaryWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

is_empty() bool

Tells whether this widget has data to display To be overridden by subclasses to decide whether No Data placeholder should show

property session_multi_ward_map: set[int]

Set of session ids whose observations span multiple wards.

property ward_multi_map: dict[int, bool]

Map of ward id -> whether any of its sessions has observations in multiple wards.

property ward_labels: list[str]

List of ward display labels with a suffix for those that span multiple wards. Example: [“Ward 1 (has multiple wards)”, “Ward 2”]

get_context_data()

Build the template context for the public report summary widget.

Prepares per-ward multi-ward indicators, ward labels with suffixes where sessions span multiple wards, and aggregate figures for display.

Note:

Ward IDs are used internally to avoid name collisions across institutions. Ward labels are sorted by name for stable output.

Returns:

Context dictionary with ward names, auditors, observation/form counts, institution, and time span

class dashboard_widgets.widgets.report_summary.SingleStatWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Renders a single value aggregated from observations in the form.

Config options:

field_name

Custom field to use

message

Message to show under the value

operator

Operator used to aggregate data, for example: sum, mean. This config option is only used if answer_to_count is not set.

stat_format

Formatter function to format the value for display. Possible values are: seconds_to_time_string to use seconds_to_time_string(). If not provided, uses humanize_number().

stat_formatter_kwargs: dict

Keyword arguments to pass to stat_format

prefix: str

Text to be prepended to the value

suffix: str

Suffix to be appended to the value

answer_to_count: str

Count how many observations have this answer. When provided, instead of using the field’s answer itself as a value, the widget will show how many observations have this value.

Example appearance:

+-------------------------------------------+
|  Single stat                            : |
|                                           |
|        10 months, 4 weeks                 |
|                                           |
|      Total Time Difference                |
|                                           |
+-------------------------------------------+
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

property answer_to_count: str | None

Answer the value to count how many observations have this answer.

property operator: Aggregate | None

Aggregation operator

property stat_formatter_kwargs: dict

Any keyword arguments that are supported by the chosen stat_formatter method.

resolve_field_values(field: str | dict | int | float) str | int | bool | list | float | None

Resolves value of a field if input is a string, or using the passed value if its numeric

Parameters:

field – field name, a value, or calculation expression

Returns:

calculated value

get_context_data() Dict[str, Any]

Context data for rendered widget template

showing_time_string() bool

Provide context that the widget is displaying time. Time displays have a different css class compared to integer values

class dashboard_widgets.widgets.report_summary.NetPromoterScoreWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Widget for displaying Net Promoter Score (NPS) in dashboards NPS = Total % of promoters – total % of detractors

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

get_net_promoter_score() tuple[int, bool]

Calculates the net promoter score based on the number of promoter values and detractor values. Also determines if the widget has no data to compute the NPS score (empty).

Returns:

tuple[int, bool] - NPS, and True if there’s no data

get_context_data() Dict[str, Any]

Context data for rendered widget template

class dashboard_widgets.widgets.report_summary.TimeTrackingSummaryWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

Reports on the average time taken to perform specific actions on observations. Widget implemented in Task #24426

For example, show the time taken to view an observation after it has been created. Also allows for tracking time taken to give an observation a particular answer value. This would be useful in the case of an incident where you want to track the time taken to close an incident.

Example configuration:

{
    "start_time": {"time_viewed": ""},
    "end_time": {"int_field": 20},
    "metric_verb": "Updated"
}
start_time

The time that a particular action was performed on an observation. This can be when it was created, updated, viewed, reviewed, or updated with a particular field/answer combination.

end_time

The time that a particular action was performed on an observation. This can be when it was created, updated, viewed, reviewed, or updated with a particular field/answer combination.

metric_verb

Used in the widget frontend to describe the activity that’s being tracked. The default is “Viewed” (the time taken to view an observation after creation).

Possible values for start_time and end_time:

  • {"time_created": ""} The time that the observation was created.

  • {"time_viewed": ""} The first time the observation was viewed by anybody.

  • {"time_changed": ""} The first time that the observation was changed by anybody.

  • {"time_reviewed": ""} The first time that the observation was reviewed by anybody.

  • {"field_name": "answer"} The first time that the observation was updated with a particular field/answer combination.

Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

showing_time_string() bool

Provide context that the widget is displaying time. Time displays have a different css class compared to integer values

class dashboard_widgets.widgets.report_summary.ObservationCountWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

allow_download = False

whether this widget should have “download as image” option

is_compact()

Returns wheter this widget will be compact or standard. Compact widgets have a minimum height which is half of a standard widget

QIP widgets

class dashboard_widgets.widgets.qip_stackedbar_chart.ValueColorMapMixin

Map specific values to use specific colors in chart

property value_color_map: dict[Any, str]
Returns:

a dictionary mapping of Value -> Color

class dashboard_widgets.widgets.qip_stackedbar_chart.QipStackedBarWidget(*, widget_config: 'WidgetConfig' | None = None, request: HttpRequest | None = None, forms: AuditForm | Iterable[AuditForm] | None = None, observations: ObservationQuerySet | None = None, filters: OverviewFilterForm | None = None, user_config: dict[str, Any] | None = None, **kwargs)

A Stacked bar chart widget that renders two dimensions of QIP issues. The two fields are configured using field_names config, and the bar chart reflects how many issues ain each category.

For example, to render a bar chart showing a count of issues per ward and per status with customized colours:

{
  "colors": ["green", "orange", "red"],
  "field_names": [
    "ward",
    "status"
  ]
}
Parameters:
  • widget_configWidgetConfig instance if this widget is being instantiated from a custom widget.

  • request – current HTTP request. This is usually passed in if widget is being rendered for dashboard, but can be None when under test.

  • forms – current form, or multiple forms. If left blank, forms can still be inferred from filters if supplied.

  • observations – optional qs of observations pre-filter. If not specified, AuditForm.entries is used. If specified, it is expected that all observations belong to the selected audit form(s)

  • filters – when present, observations will be filtered. This is present for dashboards that have filter. In places without filter (submission report), this will be Null.

  • user_config – config dictionary, will override options in widget_config.config.

config_defaults: dict[str, str | int | list | bool] = {'value_color_map': {}}

default values to be set in widget model’s config when initially created.

classmethod get_config_schema(widget_config: WidgetConfig) SchemaDict

Gets config schema for the specified custom dashboard widget. Override this method to further configure the schema per widget config if needed.

property value_color_map: dict[Any, str]

Update the value_colour_map from the widget’s config to be locale agnostic. For each QIP Status, look at each translation of the label and see if there is an entry in the existing value_color_map. If an entry is found for any translation of a QIP Status’ label, copy that entry to the current translation of that QIP Status’ label.

Returns:

a dictionary mapping of Value -> Color

build_dataframe(field_name1: str, field_name2: str) DataFrame

Builds an aggregated DataFrame with columns [field_name1, field_name2, ‘count’] using SQL-level aggregation instead of fetching all observation rows.

get_field(name: str) CustomField | Field

Gets field by name

Returns:

Hardcoded or custom field

Raises:

KeyError – if the field does not exist