Skip to content

Alerts

The alert system flags rota issues that require admin attention -- staffing shortfalls, overworked clinicians, and scheduling constraint violations. Alerts are generated automatically during rota generation and refreshed whenever shifts change.

Alert Model

Defined in config/models.py.

Field Type Description
type CharField (choices) Category of alert (see below)
severity CharField (choices) WARNING or CRITICAL
date DateField (indexed) The affected date
message CharField(500) Human-readable description
details JSONField Machine-readable context (counts, clinician IDs, etc.)
status CharField (indexed) ACTIVE, RESOLVED, or DISMISSED
created_at DateTimeField When the alert was created
resolved_at DateTimeField (nullable) When the alert was resolved or dismissed

The model has a composite index on (date, status) for efficient active-alert lookups.

Alert Types

Enum Value Label Severity Trigger
BELOW_MINIMUM Short-staffed CRITICAL Scheduled doctors below day's minimum
AT_WARNING_THRESHOLD At Minimum Staffing WARNING Scheduled doctors exactly at minimum
OVERWORKED_DOCTOR Overworked Doctor WARNING Clinician exceeds 110% of target shifts
CONSTRAINT_VIOLATION Scheduling Concern CRITICAL Hard constraint broken during generation
INSUFFICIENT_DUTY_DOCTORS Not Enough Duty Doctors CRITICAL Too few duty doctors assigned (esp. post-bank-holiday)

Status Lifecycle

ACTIVE --> RESOLVED   (condition fixed, auto or manual)
ACTIVE --> DISMISSED  (admin dismissed manually)

Alerts are created with ACTIVE status. They can transition to RESOLVED (the underlying condition was fixed) or DISMISSED (an admin acknowledged and dismissed the alert). Both transitions set resolved_at to the current time.

The refresh_alerts_for_date method takes a different approach: it deletes stale ACTIVE alerts that no longer apply, rather than resolving them. This keeps the alert table clean.

Alert Generation

During Rota Generation

After every generation phase completes, _generate_alerts_for_phase() in rota_generation/tasks.py calls AlertService.generate_alerts_for_date_range() for the rota date range. This scans all weekdays and creates alerts for:

  • Below-minimum staffing
  • Warning-threshold staffing (at exactly minimum)
  • Insufficient duty doctors
  • Overworked clinicians (checked against target shifts for the reporting period)

Weekends and bank holidays are skipped -- no alerts are generated for these dates.

Phase 6 (duty doctor assignment in rota_generation/phases/phase6_duty_doctors.py) also creates INSUFFICIENT_DUTY_DOCTORS alerts directly when it cannot assign enough duty doctors to a day.

During Leave Processing

When leave is approved, LeaveRequestProcessor._refresh_alerts_for_leave_dates() iterates through all dates covered by the leave request and calls refresh_alerts_for_date() for each one. The same happens in the admin leave approval view (frontend/views/admin/leave.py).

During Manual Shift Changes

The admin shift management views in frontend/views/admin/shifts.py call refresh_alerts_for_date() after:

  • Manually adding a shift
  • Editing a shift
  • Deleting a shift
  • Bulk-deleting shifts
  • Processing shift swaps

The shift swap service (shift_swap/services.py) also refreshes alerts for both affected dates after an admin approves a swap.

Manual Generation from Dashboard

Admins can trigger manual alert generation via the dashboard view at frontend/views/admin/dashboard.py, which provides a form to select a date range and calls generate_alerts_for_date_range().

Alert Resolution

Automatic Resolution

AlertService.auto_resolve_alerts_for_date() checks active alerts for a date and resolves them if conditions have improved:

  • BELOW_MINIMUM alerts are resolved when the doctor count meets or exceeds the minimum.
  • AT_WARNING_THRESHOLD alerts are resolved when the doctor count exceeds minimum + 1.

The refresh_alerts_for_date() method takes a more aggressive approach: it deletes active alerts that no longer match the current state, then recreates any that are still valid. This avoids accumulating stale alert records.

Manual Resolution

Admins can resolve or dismiss alerts through two interfaces:

  • Django admin (/django-admin/): Standard model admin with list filters for type, severity, status, and date.
  • REST API: POST /api/alerts/{id}/resolve/ and POST /api/alerts/{id}/dismiss/

Both actions delegate to AlertService.resolve_alert() and AlertService.dismiss_alert() respectively.

AlertService

Defined in alerts/alert_service.py. This is the single entry point for all alert operations.

Key Methods

Method Purpose
create_below_minimum_alert(date, doctors_count, minimum_required) Creates a below-minimum staffing alert. Skips if one already exists for that date.
create_warning_threshold_alert(date, doctors_count, minimum_required) Creates a warning-threshold alert. Skips if one already exists.
create_overwork_alert(clinician, date, shifts_worked, target_shifts) Creates an overwork alert for a specific clinician. Skips if one already exists for that clinician on that date.
create_constraint_violation_alert(date, violation_message) Creates a constraint violation alert. Skips if one already exists for that date.
create_insufficient_duty_doctors_alert(date, required, actual, is_post_bank_holiday) Creates a duty doctor shortfall alert. Includes post-bank-holiday context when applicable.
generate_alerts_for_date_range(start_date, end_date, clinicians, reporting_period_start) Scans a date range and creates all applicable alerts. Returns counts by type.
refresh_alerts_for_date(date) Re-evaluates all alerts for a single date. Deletes stale active alerts, creates new ones.
auto_resolve_alerts_for_date(date) Resolves alerts whose conditions are now met.
resolve_alert(alert) Sets an alert to RESOLVED status with a timestamp.
dismiss_alert(alert) Sets an alert to DISMISSED status with a timestamp.
get_active_alerts(type, severity, start_date, end_date) Queries active alerts with optional filters.

All creation methods are idempotent -- they check for existing active alerts of the same type and date before creating a new one.

Admin Interface

Django Admin

AlertAdmin in config/admin.py provides:

  • List display: type, severity, date, status, message
  • Filters: type, severity, status, date
  • Search: message text
  • Date hierarchy: by date

REST API

The AlertViewSet in api/viewsets.py exposes:

Endpoint Method Description
/api/alerts/ GET List alerts (active by default; pass ?active=false for all)
/api/alerts/{id}/ GET Retrieve a single alert
/api/alerts/{id}/resolve/ POST Resolve an alert
/api/alerts/{id}/dismiss/ POST Dismiss an alert

The list endpoint defaults to showing only ACTIVE alerts. Alerts are ordered by date descending, then creation time descending.

Database Indexes

The Alert model defines one explicit index:

  • idx_alert_date_status on (date, status) -- optimises the common query pattern of finding active alerts for a specific date or date range.