Blood Pressure Monitoring¶
Overview¶
The Blood Pressure Monitoring feature enables recording of multi-reading blood pressure sessions and reviewing historical session data. A session consists of two or more individual readings taken with a rest timer between each, after which the application computes averages, classifies the result using AHA-aligned categories (Hypotension through Hypertensive Crisis), and captures the active medication status. Sessions are stored with full reading arrays as JSON and weekly aggregate averages are available for trend analysis. The feature supports offline recording and sends push notifications when a critical classification is detected.
Role Access¶
| Role | Create Session | View | Delete |
|---|---|---|---|
| SuperAdmin | ✓ (all CRs) | All | ✓ |
| Administrator | ✓ (all CRs) | All | ✓ |
| Carer | ✓ (linked CRs) | Linked CRs | ✓ |
| SupportWorker | ✓ (linked CRs) | Linked CRs | — |
| CareRecipient | ✓ (self) | Own | — |
| HealthCareProvider | — (read only) | Linked CRs | — |
Backend¶
Controller: BloodPressureController — /api/blood-pressure¶
| Method | Route | Auth | Description |
|---|---|---|---|
| POST | /api/blood-pressure |
Authorized | Create a multi-reading BP session |
| GET | /api/blood-pressure |
Authorized | List sessions (role-scoped) |
| GET | /api/blood-pressure/{id} |
Authorized | Get a single session |
| DELETE | /api/blood-pressure/{id} |
AdminOrHigher | Delete a session |
Additional routes under BloodPressureController:
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /api/blood-pressure/weekly-averages |
Authorized | 7-day rolling averages per care recipient |
| GET | /api/blood-pressure/pulse-rate-ranges |
Authorized | Pulse rate classification ranges |
| GET | /api/blood-pressure/bp-classification-ranges |
Authorized | BP classification ranges |
| POST | /api/blood-pressure/bp-classification-ranges |
AdminOrHigher | Create a classification range |
| PUT | /api/blood-pressure/bp-classification-ranges/{id} |
AdminOrHigher | Update range |
| DELETE | /api/blood-pressure/bp-classification-ranges/{id} |
AdminOrHigher | Delete range (seed rows protected) |
POST /api/blood-pressure — Session Creation Detail¶
- Resolves
CareRecipientIdby role (same pattern as Assessment). - Validates readings: each must have
SystolicandDiastolic;PulseRateis optional. - Computes averages across all readings:
AverageSystolic,AverageDiastolic,AveragePulseRate?. - Calls
BpClassificationService.ClassifyAsync(avgSys, avgDia)→Category+Severity. - Calls
PulseRateClassificationService.ClassifyAsync(avgPulse?)→PulseRateCategory+PulseRateSeverity. - Captures active medication snapshot: queries the CR's active
BpMedicationrecord and serialises it toMedicationSnapshotJSON (name + dose + frequency at time of session). - Serialises individual readings array to
ReadingsJson. - Sets
TermIdfrom the default cycle. - On
Severity == Critical: sendsBloodPressureAlert(type 9) push to all linked carers. - Returns full session DTO including computed averages and classifications.
Key DTOs¶
CreateBpSessionDto
readings[] object[] Required, min 1
systolic int 40–300 mmHg
diastolic int 20–200 mmHg
pulseRate int? Optional bpm
careRecipientId int? Omit for self-recording CareRecipient
readingDate DateTime? Back-dated session datetime
clientTimestamp string? ISO client capture time
notes string? Free-text
medicationTaken bool? Whether medication was taken
medicationStatus int? 0=Unknown, 1=Before, 2=After, 3=AboutTo, 4=NoMedicationConsumed
BpSessionDto (response — abbreviated)
id int
userId int
userName string
careRecipientName string?
timestamp DateTime
averageSystolic decimal
averageDiastolic decimal
averagePulseRate decimal?
category string "Hypotension" | "Normal" | "Elevated" | "HypertensionStage1" | "HypertensionStage2" | "HypertensiveCrisis"
severity string
pulseRateCategory string?
pulseRateSeverity string?
readingsJson string JSON array of individual readings
medicationSnapshot string? JSON object { name, dose, frequency }
medicationTaken bool?
medicationStatus string?
termName string?
Model: BloodPressureSession¶
| Field | Type | Notes |
|---|---|---|
Id |
int | PK |
UserId |
int | FK → User (recorder) |
CareRecipientId |
int? | FK → User (subject) |
TermId |
int? | FK → Cycle |
Timestamp |
DateTime | Server record time |
ClientTimestamp |
string? | ISO client capture time |
AverageSystolic |
decimal | |
AverageDiastolic |
decimal | |
AveragePulseRate |
decimal? | |
Category |
BpCategory enum | Hypotension=1 … HypertensiveCrisis=6 |
Severity |
Severity enum | Normal=1 … Critical=5 |
PulseRateCategory |
string? | |
PulseRateSeverity |
string? | |
ReadingsJson |
string | JSON array |
Notes |
string? | |
MedicationSnapshot |
string? | JSON object |
MedicationTaken |
bool? | |
MedicationStatus |
MedicationStatus enum |
BpClassificationService¶
File: server/src/Vitara.Api/Services/BpClassificationService.cs
- Per-recipient ranges take priority over global seeds.
- Ranges sorted by
SortOrder ASC; first match wins. - OR logic (
IsOrLogic = true): match if systolic OR diastolic meets the criterion. - AND logic (
IsOrLogic = false): match only if both systolic AND diastolic are in range. - Fallback:
HypertensiveCrisis / Critical. - 6 global default seed ranges cannot be deleted.
PulseRateClassificationService¶
Same per-recipient/global override pattern. Range matching by MinBpm <= value < MaxBpm. 4 global defaults cannot be deleted.
Frontend¶
Routes¶
| Path | Component | Notes |
|---|---|---|
/blood-pressure |
BpHistoryComponent |
All roles |
/blood-pressure/entry |
BpEntryComponent |
All except HealthCareProvider (route data guard) |
BpHistoryComponent — /blood-pressure¶
File: client/src/app/features/blood-pressure/components/bp-history.component.ts
On init:
- Calls BloodPressureService.getSessions(termId) and BloodPressureService.getWeeklyAverages().
Table columns: Expand | Timestamp | Care Recipient | Recorded By | Avg Systolic | Category | Severity | Actions
Expandable row: Renders individual readings from readingsJson (supports both PascalCase and camelCase keys for backward compatibility), shows medication snapshot (name + dose + frequency), and medication timing status (Before/After/About To/No Medication etc.)
Delete: Available for Administrator, SuperAdmin, and Carer roles. Uses DialogService.confirmDelete() before calling BloodPressureService.deleteSession(id).
HealthCareProvider: "Add Reading" button is hidden; read-only view only.
Colour-coded categories: Each category string maps to a CSS class for visual severity indication.
BpEntryComponent — /blood-pressure/entry¶
File: client/src/app/features/blood-pressure/components/bp-entry.component.ts
This component implements a multi-step wizard.
Configuration Loading¶
On init:
- Loads BpReadingCount from AppConfigService.getByKey('BpReadingCount') — determines how many readings to collect (default 2).
- Loads pulse rate ranges via BloodPressureService.getPulseRateRanges().
Step Sequence¶
Thecare-recipient step is skipped if the user has only one linked CR or is a CareRecipient self-recording.
Reading Step¶
FormArraywith one entry per configured reading count.- Each entry:
systolic(40–300, required),diastolic(20–200, required),pulseRate(optional, 20–300). - AHA category preview: Computed client-side after each keypress using the classification ranges loaded from
BloodPressureService.getBpClassificationRanges(). Displays e.g. "Stage 2 Hypertension" in real time below the inputs.
Timer Step (2-minute rest)¶
- 2-minute countdown (120 seconds) between consecutive readings.
- Page Visibility API correction: On
visibilitychange, recalculates elapsed seconds from a storedtimerStartTimeto correct for browser throttling. - User can click "Skip Timer" — the reading is flagged
timerSkipped: truein the readings array. sessionDateUserEditedflag: if the user has manually changed the back-date field, the timer completion callback does not overwrite it.
Summary Step¶
- Computes and displays averages:
avgSystolic,avgDiastolic,avgPulseRate?. - Resolves the overall AHA category + severity for the averaged values.
- Loads the CR's active BP medication via
UserConditionsService.getActiveBpMedication(careRecipientId?). - Medication status selector: Unknown / Before / After / AboutTo / NoMedicationConsumed.
- "Session date" datetime-local input for back-dating.
Offline Fallback¶
BloodPressureService.createSession() catches network/5xx errors and enqueues the payload to OfflineQueueService with type 'blood-pressure'.
Submission¶
Calls BloodPressureService.createSession(dto) → POST /blood-pressure with the full CreateBpSessionDto including the readings array, medication status, and optional back-dated timestamp.
Feature Service: BloodPressureService¶
File: client/src/app/features/blood-pressure/services/blood-pressure.service.ts
| Method | HTTP | Endpoint | Notes |
|---|---|---|---|
createSession(dto) |
POST | /blood-pressure |
Offline fallback |
getSessions(termId?) |
GET | /blood-pressure?termId=N |
|
deleteSession(id) |
DELETE | /blood-pressure/{id} |
|
getWeeklyAverages() |
GET | /blood-pressure/weekly-averages |
|
getPulseRateRanges(careRecipientId?) |
GET | /blood-pressure/pulse-rate-ranges |
|
getBpClassificationRanges(careRecipientId?) |
GET | /blood-pressure/bp-classification-ranges |
|
createBpClassificationRange(dto) |
POST | /blood-pressure/bp-classification-ranges |
|
updateBpClassificationRange(id, dto) |
PUT | /blood-pressure/bp-classification-ranges/{id} |
|
deleteBpClassificationRange(id) |
DELETE | /blood-pressure/bp-classification-ranges/{id} |
AppConfig Keys Relevant to This Feature¶
| Key | Default | Description |
|---|---|---|
BpReadingCount |
2 | Number of readings per session |
HighBpRecheckWaitMinutes |
2 | Rest timer duration between readings (minutes) |
HighBpRequiredChecks |
2 | Minimum readings before summary is shown |
Push Notifications Sent¶
| Trigger | Notification Type | Recipients |
|---|---|---|
| Session severity = Critical | BloodPressureAlert (9) |
All linked carers of the care recipient |
BP Categories (AHA-aligned)¶
| Category | Systolic | Diastolic |
|---|---|---|
| Hypotension | < 90 | OR < 60 |
| Normal | 90–119 | AND 60–79 |
| Elevated | 120–129 | AND < 80 |
| Hypertension Stage 1 | 130–139 | OR 80–89 |
| Hypertension Stage 2 | 140–179 | OR 90–119 |
| Hypertensive Crisis | ≥ 180 | OR ≥ 120 |
Classification ranges are configurable per care recipient or globally via Management → BP Classification.
End-to-End Data Flow¶
Carer/CareRecipient Angular API DB
| | | |
| Select care recipient | | |
| |-- GET /bp-classification-ranges? --> |
| |-- GET /pulse-rate-ranges? --| |
| |<-- ranges loaded -----------| |
| | | |
| Enter reading 1 (e.g. 138/90)| | |
| Preview: Stage 1 shown | | |
| Wait 2-min timer | (timer, may skip) | |
| Enter reading 2 (e.g. 142/92)| | |
| | | |
| Summary: avg=140/91, Stage 2| | |
| Load active medication |-- GET active BP med ------->| |
| Select: "Taken Before" | | |
| | | |
| Submit |-- POST /blood-pressure ---->| |
| | |-- classify averages |
| | |-- INSERT session --->|
| | | if Critical: push |
| |<-- BpSessionDto ------------| |
|<-- navigate /blood-pressure| | |