Skip to content

BGL Assessment

Overview

The BGL Assessment feature provides a structured, guided workflow for recording and managing blood glucose level (BGL) readings for care recipients. Rather than a simple single-value entry, the assessment represents a full clinical protocol: an initial sensor or blood-test reading, then a branching state-machine for hypoglycemia (low BGL) or hyperglycemia (high BGL), each with timed treatment cycles, ketone measurements, and recheck loops. All assessments are persisted with full reading timelines and cycle details, and a history page presents them in an expandable table. The feature supports offline recording via IndexedDB and pushes timer notifications through the service worker.


Role Access

Role Create View Delete
SuperAdmin ✓ (all CRs) All
Administrator ✓ (all CRs) All
Carer ✓ (linked CRs) Linked CRs
SupportWorker ✓ (linked CRs) Linked CRs
CareRecipient ✓ (self) Own
HealthCareProvider Linked CRs

Backend

Controller: AssessmentController/api/assessment

Method Route Auth Description
POST /api/assessment Authorized Record a new BGL assessment
GET /api/assessment Authorized List assessments scoped by role
DELETE /api/assessment/{id} AdminOrHigher Hard-delete an assessment

Assessment Endpoint Details

POST /api/assessment - Resolves CareRecipientId by role: CareRecipient → self; Carer/SupportWorker → must be a linked CR; Admin/SuperAdmin → any valid user. - Reads termId from JWT claim and verifies the Cycle exists. - Persists the Assessment entity including all JSON blobs. - Sends push notification for critical outcomes: if outcome is CarerNotified or EmergencyRequiredCarerNotified, dispatches a AssessmentSeverity push to all linked carers.

GET /api/assessment - Admin/SuperAdmin: returns all assessments. - CareRecipient: returns own assessments. - Carer/SupportWorker/HealthCareProvider: returns assessments for all linked CRs. - Optional ?termId=N filter.

Key DTOs

CreateAssessmentDto

careRecipientId      int?       Omit for self-recording CareRecipient
initialReading       string     Numeric string, "LOW", or "HIGH"
type                 int        1=Normal, 2=Hypoglycemia, 3=Hyperglycemia
bloodTestReading     decimal?   Finger-prick confirmation value
recheckReading       decimal?   Post-treatment recheck value
treatmentCycles      int        Number of treatment cycles performed
ketoneLevel          decimal?   mmol/L ketone reading (required if BGL ≥ 15)
outcome              int        1=Normal, 2=Resolved, 3=CarerNotified, 4=EmergencyRequiredCarerNotified
notes                string?    Free-text notes
durationSeconds      int        Total assessment duration
allReadingsJson      string?    JSON array of every reading taken
cycleDetailsJson     string?    JSON array of each treatment cycle detail
readingDate          DateTime?  Back-dated timestamp (defaults to server now if omitted)
clientTimestamp      string?    ISO string of client-side capture time

AssessmentDto (response)

id                   int
userId               int
userName             string
careRecipientId      int?
careRecipientName    string?
timestamp            DateTime
initialReading       string
type                 string     "Hypoglycemia" | "Hyperglycemia" | "Normal"
bloodTestReading     decimal?
recheckReading       decimal?
treatmentCycles      int
ketoneLevel          decimal?
outcome              string
notes                string?
durationSeconds      int
allReadingsJson      string?
cycleDetailsJson     string?
termName             string?

Model: Assessment

Field Type Notes
Id int PK
UserId int FK → User (recorder)
CareRecipientId int? FK → User (subject)
Timestamp DateTime Server-side record time
InitialReading string Sensor/strip value or "LOW"/"HIGH"
Type AssessmentType Normal / Hypoglycemia / Hyperglycemia
BloodTestReading decimal? Finger-prick
RecheckReading decimal? Post-treatment
TreatmentCycles int 0–4
KetoneLevel decimal? mmol/L
Outcome AssessmentOutcome Normal → EmergencyRequiredCarerNotified
Notes string?
TermId int? FK → Cycle
DurationSeconds int
AllReadingsJson string? JSON array
CycleDetailsJson string? JSON array

BGL Classification Service

File: server/src/Vitara.Api/Services/BglClassificationService.cs

  • Holds CRUD for BglClassificationRange and KetoneThreshold entities.
  • Per-recipient ranges (where CareRecipientId is set) take priority over global defaults (where CareRecipientId is null).
  • Range matching: MinValue <= value < MaxValue; null bounds are treated as unbounded.
  • Fallback: if no range matches (e.g., unexpected reading), defaults to Hyperglycemia / Critical.
  • GetEffectiveRangesAsync(careRecipientId?): returns the resolved active set of classification ranges + ketone thresholds for a given CR (or global).
  • GetMaxBglValue(sensorValue, bloodTestValue): static helper — returns the higher of the two values for classification purposes.
  • Export endpoint GET /api/bgl-classification/effective?careRecipientId=N — used by BglReadingComponent to load dynamic thresholds before starting an assessment.

Frontend

Routes

Path Component Guard
/assessment AssessmentHistoryComponent AuthGuard
/assessment/reading BglReadingComponent AuthGuard, role check in component

AssessmentHistoryComponent/assessment

File: client/src/app/features/assessment/components/assessment-history.component.ts

On init: - Calls AssessmentService.getAssessments(termId) (termId from AuthService.getTermId()). - For Admin/SuperAdmin, also loads all users via GET /users to populate the user filter.

Table columns: Expand toggle | Timestamp | User | Care Recipient | Initial Reading | Assessment Type | Outcome | Treatment Cycles | Duration | Actions (delete for admins)

Expandable row: Shows the full allReadingsJson timeline (reading type, numeric value, timestamp) and each cycleDetailsJson entry (cycle number, start time, treatment type, recheck value).

Color coding: - reading-normal: 4.0–7.89 mmol/L → green - reading-warning: 7.9–14.99 mmol/L → amber - reading-danger: ≥15 or <4 mmol/L → red

Duration formatting: Converts raw durationSeconds to "Xm Ys" display string.

Search: Client-side text filter across user name, care recipient name, type, outcome, reading values, and term name.

Excel export: GET /export/assessment-report?termId=N → downloads .xlsx blob.

BglReadingComponent/assessment/reading

File: client/src/app/features/assessment/components/bgl-reading.component.ts

This is the core guided assessment workflow implemented as a state machine.

State Machine

initial
  ├── (BGL is LOW / < low threshold) → hypo-fingerprick
  │     └── hypo-treatment (15-min countdown)
  │           └── hypo-recheck
  │                 ├── (resolved, < 2 cycles) → complete
  │                 ├── (BGL=3.9 after cycle 2) → check-only cycle (up to 2 extra)
  │                 └── (not resolved, > 2 cycles) → carer-notified outcome
  └── (BGL is HIGH / ≥ high threshold) → hyper-fingerprick
        └── hyper-ketone
              ├── (ketone < 0.6) → monitoring-wait (2-hour countdown)
              │     └── monitoring-recheck → complete
              └── (ketone ≥ 0.6) → hyper-ketone-monitoring
                    └── hyper-ketone-recheck → complete / emergency

Care Recipient Selection

  • Auto-selects if only one CR is linked.
  • Shows a dropdown for carers/admins with multiple CRs.
  • Admin uses CareRecipientService.getAllActiveCareRecipients(); others use getCareRecipients().
  • On CR selection, loads effective BGL classification ranges via BglClassificationService.getEffectiveRanges(careRecipientId).

Initial Reading

  • Accepts numeric input (1.0–30.0 mmol/L) or text LOW / HIGH from CGM.
  • Validates against loaded thresholds to determine the path (hypo vs hyper vs normal).
  • isKetoneOptional(): returns true for T2D care recipients — the ketone step shows a "Skip" button.

Timer Implementation

  • Hypo treatment timer: 15 minutes (BglRecheckWaitMinutes from AppConfig via AppConfigService).
  • Hyper monitoring timer: 120 minutes.
  • Page Visibility API correction: When the tab is hidden and restored (visibilitychange event), the component recalculates elapsed time from a stored start timestamp to correct for timer drift.
  • Service Worker notification: At timer mid-points and expiry, ServiceWorkerNotificationService schedules a local notification to alert even when the browser tab is not active.

Back-Dating

  • readingDateStr input (datetime-local) defaults to the current time.
  • Sent to the API as readingDate in the DTO; API uses this as the assessment Timestamp.

Offline Fallback

  • If the network request to POST /assessment fails with a network error or a 5xx status, AssessmentService.createAssessment() catches the error and enqueues the payload to OfflineQueueService (IndexedDB, 24-hour TTL, type 'assessment').
  • A toast notification informs the user that recording was saved locally.

Submission Payload

AssessmentService.createAssessment() sends the full CreateAssessmentDto including: - allReadingsJson: every reading taken with type labels and timestamps. - cycleDetailsJson: per-cycle detail objects with cycle number, treatment given, start time, and outcome reading. - durationSeconds: Date.now() - startTime in seconds. - clientTimestamp: ISO timestamp captured when the user tapped the first reading button.

Feature Service: AssessmentService

File: client/src/app/features/assessment/services/assessment.service.ts

Method HTTP Endpoint Notes
createAssessment(dto) POST /assessment Offline fallback via OfflineQueueService
getAssessments(termId?) GET /assessment?termId=N Role-scoped by server
deleteAssessment(id) DELETE /assessment/{id} Admin only
getExportReport(termId?) GET /export/assessment-report Returns Blob

Shared Service: BglClassificationService

File: client/src/app/shared/services/bgl-classification.service.ts

Method HTTP Endpoint
getEffectiveRanges(careRecipientId?) GET /bgl-classification/effective
getRanges(careRecipientId?) GET /bgl-classification/ranges
createRange(dto) POST /bgl-classification/ranges
updateRange(id, dto) PUT /bgl-classification/ranges/{id}
deleteRange(id) DELETE /bgl-classification/ranges/{id}
getKetoneThresholds(careRecipientId?) GET /bgl-classification/ketone-thresholds
createKetoneThreshold(dto) POST /bgl-classification/ketone-thresholds
updateKetoneThreshold(id, dto) PUT /bgl-classification/ketone-thresholds/{id}
deleteKetoneThreshold(id) DELETE /bgl-classification/ketone-thresholds/{id}

AppConfig Keys Relevant to This Feature

Key Default Description
BglRecheckWaitMinutes 15 Hypo treatment cycle wait
BglAdditionalWaitMinutes 15 Additional check-only cycle wait
HyperGlucoseReminderIntervalMinutes 120 Hyper monitoring timer
HypoMaxTreatmentCycles 2 Max full treatment cycles
HypoMaxCheckOnlyCycles 2 Max extra check-only cycles after cycle 2

Push Notifications Sent

Trigger Notification Type Recipients
Outcome = CarerNotified AssessmentSeverity (3) All linked carers
Outcome = EmergencyRequiredCarerNotified AssessmentSeverity (3) All linked carers
Timer expires (in-browser) BgTimerReminder (6) Local service-worker notification only

End-to-End Data Flow

Carer/CareRecipient                Angular                     API                   DB
  |                                |                            |                     |
  | Select care recipient          |                            |                     |
  |                                |-- GET /bgl-classification/effective? -->          |
  |                                |<-- classification ranges --|                     |
  |                                |                            |                     |
  | Enter initial reading (e.g. 2.8)|                           |                     |
  |                                | state → hypo-fingerprick   |                     |
  | Wait 15-min timer              |                            |                     |
  | Enter treatment (8g carbs)     | state → hypo-treatment     |                     |
  | Timer expires                  | state → hypo-recheck       |                     |
  | Enter recheck reading (5.2)    | state → complete           |                     |
  |                                |                            |                     |
  | Confirm & submit               |                            |                     |
  |                                |-- POST /assessment ------->|                     |
  |                                |                            |-- INSERT Assessment->|
  |                                |                            | if carers notified:  |
  |                                |                            |-- push to carers     |
  |                                |<-- AssessmentDto ----------|                     |
  |<-- navigate /assessment        |                            |                     |