From 72fca5821757bcd7479c401f5e27cc01a70eef38 Mon Sep 17 00:00:00 2001 From: Nathaniel Hamming Date: Thu, 21 Mar 2024 16:00:58 -0300 Subject: [PATCH] [PAL-458] Adding the check for rapidly rising glucose (#622) * Adding the check for rapidly rising glucose * matching the previous implementation * clean up --- Loop/Managers/LoopDataManager.swift | 5 ++- Loop/View Models/CarbEntryViewModel.swift | 47 ++++++++++++++++++++++- Loop/Views/CarbEntryView.swift | 4 ++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index 77e6da91c7..8af722fc81 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -1134,7 +1134,10 @@ extension LoopDataManager: CarbEntryViewModelDelegate { var defaultAbsorptionTimes: DefaultAbsorptionTimes { LoopCoreConstants.defaultCarbAbsorptionTimes } - + + func getGlucoseSamples(start: Date?, end: Date?) async throws -> [StoredGlucoseSample] { + try await glucoseStore.getGlucoseSamples(start: start, end: end) + } } extension LoopDataManager: ManualDoseViewModelDelegate { diff --git a/Loop/View Models/CarbEntryViewModel.swift b/Loop/View Models/CarbEntryViewModel.swift index 01abe61905..49b596f97e 100644 --- a/Loop/View Models/CarbEntryViewModel.swift +++ b/Loop/View Models/CarbEntryViewModel.swift @@ -15,6 +15,7 @@ import LoopCore protocol CarbEntryViewModelDelegate: AnyObject, BolusEntryViewModelDelegate { var defaultAbsorptionTimes: DefaultAbsorptionTimes { get } func scheduleOverrideEnabled(at date: Date) -> Bool + func getGlucoseSamples(start: Date?, end: Date?) async throws -> [StoredGlucoseSample] } final class CarbEntryViewModel: ObservableObject { @@ -38,11 +39,14 @@ final class CarbEntryViewModel: ObservableObject { return 1 case .overrideInProgress: return 2 + case .glucoseRisingRapidly: + return 3 } } case entryIsMissedMeal case overrideInProgress + case glucoseRisingRapidly } @Published var alert: CarbEntryViewModel.Alert? @@ -284,12 +288,14 @@ final class CarbEntryViewModel: ObservableObject { } private func observeLoopUpdates() { - self.checkIfOverrideEnabled() + checkIfOverrideEnabled() + checkGlucoseRisingRapidly() NotificationCenter.default .publisher(for: .LoopDataUpdated) .receive(on: DispatchQueue.main) .sink { [weak self] _ in self?.checkIfOverrideEnabled() + self?.checkGlucoseRisingRapidly() } .store(in: &cancellables) } @@ -309,6 +315,45 @@ final class CarbEntryViewModel: ObservableObject { } } + private func checkGlucoseRisingRapidly() { + guard let delegate else { + warnings.remove(.glucoseRisingRapidly) + return + } + + let now = Date() + let startDate = now.addingTimeInterval(-LoopConstants.missedMealWarningGlucoseRecencyWindow) + + Task { @MainActor in + let glucoseSamples = try? await delegate.getGlucoseSamples(start: startDate, end: nil) + guard let glucoseSamples else { + warnings.remove(.glucoseRisingRapidly) + return + } + + let filteredGlucoseSamples = glucoseSamples.filterDateRange(startDate, now) + guard let startSample = filteredGlucoseSamples.first, let endSample = filteredGlucoseSamples.last else { + warnings.remove(.glucoseRisingRapidly) + return + } + + let duration = endSample.startDate.timeIntervalSince(startSample.startDate) + guard duration >= LoopConstants.missedMealWarningVelocitySampleMinDuration else { + warnings.remove(.glucoseRisingRapidly) + return + } + + let delta = endSample.quantity.doubleValue(for: .milligramsPerDeciliter) - startSample.quantity.doubleValue(for: .milligramsPerDeciliter) + let velocity = delta / duration.minutes // Unit = mg/dL/m + + if velocity > LoopConstants.missedMealWarningGlucoseRiseThreshold { + warnings.insert(.glucoseRisingRapidly) + } else { + warnings.remove(.glucoseRisingRapidly) + } + } + } + private func observeAbsorptionTimeChange() { $absorptionTime .receive(on: RunLoop.main) diff --git a/Loop/Views/CarbEntryView.swift b/Loop/Views/CarbEntryView.swift index 14c6b2c460..97082a9b59 100644 --- a/Loop/Views/CarbEntryView.swift +++ b/Loop/Views/CarbEntryView.swift @@ -167,6 +167,8 @@ extension CarbEntryView { return .critical case .overrideInProgress: return .warning + case .glucoseRisingRapidly: + return .critical } } @@ -176,6 +178,8 @@ extension CarbEntryView { return NSLocalizedString("Loop has detected an missed meal and estimated its size. Edit the carb amount to match the amount of any carbs you may have eaten.", comment: "Warning displayed when user is adding a meal from an missed meal notification") case .overrideInProgress: return NSLocalizedString("An active override is modifying your carb ratio and insulin sensitivity. If you don't want this to affect your bolus calculation and projected glucose, consider turning off the override.", comment: "Warning to ensure the carb entry is accurate during an override") + case .glucoseRisingRapidly: + return NSLocalizedString("Your glucose is rapidly rising. Check that any carbs you've eaten were logged. If you logged carbs, check that the time you entered lines up with when you started eating.", comment: "Warning to ensure the carb entry is accurate") } }