Просмотр исходного кода

Don't recommend insulin when `minPredBG` < 54 mg/dL

* New function `minPredBGFromReason` to pull `minPredBG` from the `reason` of an OrefDetermination
* Add header in red above Treatment button when `currentBG` or `minPredBG` are < 54 and a non-external bolus is entered
  * Revert coloring Treatment button red when these conditions are met
* Add bolus amount to confirmation dialog
* l18n automations
Mike Plante 1 год назад
Родитель
Сommit
cd273e8891

+ 14 - 0
Model/Helper/Determination+helper.swift

@@ -19,6 +19,20 @@ extension OrefDetermination {
     var reasonConclusion: String {
         reason?.components(separatedBy: "; ").last ?? ""
     }
+
+    var minPredBGFromReason: Decimal? {
+        // Find the part that contains "minPredBG"
+        if let minPredBGPart = reasonParts.first(where: { $0.contains("minPredBG") }) {
+            // Extract the number after "minPredBG"
+            let components = minPredBGPart.components(separatedBy: "minPredBG ")
+            if components.count > 1 {
+                // Get everything after "minPredBG " and convert to Decimal
+                let valueString = components[1].trimmingCharacters(in: CharacterSet(charactersIn: "0123456789.-").inverted)
+                return Decimal(string: valueString)
+            }
+        }
+        return nil
+    }
 }
 
 extension NSPredicate {

+ 17 - 9
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -4077,7 +4077,6 @@
       }
     },
     "(Current - Target) / ISF" : {
-      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {
@@ -4183,9 +4182,6 @@
         }
       }
     },
-    "(Current - Target) ÷ ISF" : {
-
-    },
     "(Eventual Glucose - Target) / ISF" : {
       "comment" : "Formula displayed in Bolus info pop-up. Make translation short!",
       "extractionState" : "manual",
@@ -4941,7 +4937,11 @@
         }
       }
     },
+    "(𝒳 / 60) × current basal rate" : {
+
+    },
     "(𝒳 ÷ 60) × current basal rate" : {
+      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {
@@ -15773,7 +15773,6 @@
       }
     },
     "15 min Delta / ISF" : {
-      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {
@@ -15879,9 +15878,6 @@
         }
       }
     },
-    "15 min Delta ÷ ISF" : {
-
-    },
     "19:05" : {
       "localizations" : {
         "ar" : {
@@ -32757,7 +32753,11 @@
     "Autosens Ratio" : {
 
     },
+    "Autosens Ratio =\n(Weighted Average of TDD) / (10-day Average of TDD)" : {
+
+    },
     "Autosens Ratio =\n(Weighted Average of TDD) ÷ (10-day Average of TDD)" : {
+      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {
@@ -81157,7 +81157,7 @@
     "Glucose Deviations Algorithm" : {
 
     },
-    "Glucose is very low! Give insulin?" : {
+    "Glucose forecast is very low." : {
 
     },
     "Glucose is very low." : {
@@ -89089,7 +89089,11 @@
         }
       }
     },
+    "If you have a \"Recommended Bolus Percentage\" of 80%, and a \"Fatty Meal Bolus Percentage\" of 70%, your recommended bolus will be multiplied by: (80 × 70) / 100 = 56%." : {
+
+    },
     "If you have a \"Recommended Bolus Percentage\" of 80%, and a \"Fatty Meal Bolus Percentage\" of 70%, your recommended bolus will be multiplied by: (80 × 70) ÷ 100 = 56%." : {
+      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {
@@ -89301,7 +89305,11 @@
         }
       }
     },
+    "If your current basal rate is 0.8 U/hr and \"Super Bolus Percentage\" is set to 200%: 0.8 × (200 / 100) = 1.6 units will be added to your bolus recommendation." : {
+
+    },
     "If your current basal rate is 0.8 U/hr and \"Super Bolus Percentage\" is set to 200%: 0.8 × (200 ÷ 100) = 1.6 units will be added to your bolus recommendation." : {
+      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {

+ 1 - 0
Trio/Sources/Modules/Treatments/TreatmentsStateModel.swift

@@ -773,6 +773,7 @@ extension Treatments.StateModel {
             // setup vars for bolus calculation
             insulinRequired = (mostRecentDetermination.insulinReq ?? 0) as Decimal
             evBG = (mostRecentDetermination.eventualBG ?? 0) as Decimal
+            minPredBG = (mostRecentDetermination.minPredBGFromReason ?? 0) as Decimal
             insulin = (mostRecentDetermination.insulinForManualBolus ?? 0) as Decimal
             target = (mostRecentDetermination.currentTarget ?? currentBGTarget as NSDecimalNumber) as Decimal
             isf = (mostRecentDetermination.insulinSensitivity ?? currentISF as NSDecimalNumber) as Decimal

+ 3 - 1
Trio/Sources/Modules/Treatments/View/PopupView.swift

@@ -95,7 +95,7 @@ struct PopupView: View {
 
                     DividerDouble()
 
-                    if (state.factoredInsulin > 0) {
+                    if state.factoredInsulin > 0 {
                         calcResultRow
                         calcResultFormulaRow
                         DividerCustom()
@@ -453,6 +453,8 @@ struct PopupView: View {
                     Text("No insulin recommended.")
                 } else if state.currentBG < 54 {
                     Text("Glucose is very low.")
+                } else if state.minPredBG < 54 {
+                    Text("Glucose forecast is very low.")
                 } else if state.maxBolus <= iobAvailable && state.factoredInsulin > state.maxBolus {
                     Text("Max Bolus = \(insulinFormatter(state.maxBolus)) U")
                 } else if state.factoredInsulin > iobAvailable {

+ 60 - 31
Trio/Sources/Modules/Treatments/View/TreatmentsRootView.swift

@@ -379,48 +379,77 @@ extension Treatments {
             }
         }
 
-        @State private var showDangerousLowAlert = false
+        @State private var showConfirmDialogForBolusing = false
+
+        private var bolusWarning: String {
+            let isGlucoseVeryLow = state.currentBG < 54
+            let isMinPredBGVeryLow = state.minPredBG < 54
+
+            guard !state.externalInsulin,
+                  state.amount > 0,
+                  isGlucoseVeryLow || isMinPredBGVeryLow
+            else {
+                return ""
+            }
+
+            let warning = isGlucoseVeryLow
+                ? "Glucose is very low."
+                : "Glucose forecast is very low."
+
+            return warning
+        }
 
         var treatmentButton: some View {
             var treatmentButtonBackground = Color(.systemBlue)
-            if limitExceeded || (state.amount > 0 && state.currentBG <= 54) {
+            if limitExceeded {
                 treatmentButtonBackground = Color(.systemRed)
             } else if disableTaskButton {
                 treatmentButtonBackground = Color(.systemGray)
             }
 
-            return Button {
-                if state.currentBG <= 54 {
-                    showDangerousLowAlert = true
-                } else {
-                    state.invokeTreatmentsTask()
+            return Section {
+                Button {
+                    if bolusWarning != "" {
+                        showConfirmDialogForBolusing = true
+                    } else {
+                        state.invokeTreatmentsTask()
+                    }
+                } label: {
+                    HStack {
+                        if state.isBolusInProgress && state.amount > 0 &&
+                            !state.externalInsulin && (state.carbs == 0 || state.fat == 0 || state.protein == 0)
+                        {
+                            ProgressView()
+                        }
+                        taskButtonLabel
+                    }
+                    .font(.headline)
+                    .foregroundStyle(Color.white)
+                    .frame(maxWidth: .infinity, alignment: .center)
+                    .frame(height: 35)
                 }
-            } label: {
-                HStack {
-                    if state.isBolusInProgress && state.amount > 0 &&
-                        !state.externalInsulin && (state.carbs == 0 || state.fat == 0 || state.protein == 0)
-                    {
-                        ProgressView()
+                .disabled(disableTaskButton)
+                .listRowBackground(treatmentButtonBackground)
+                .shadow(radius: 3)
+                .clipShape(RoundedRectangle(cornerRadius: 8))
+                .confirmationDialog(
+                    bolusWarning + " Bolus \(state.amount.description) U?",
+                    isPresented: $showConfirmDialogForBolusing,
+                    titleVisibility: .visible
+                ) {
+                    Button("Cancel", role: .cancel) {}
+                    Button("Ignore Warning and Enact Bolus", role: .destructive) {
+                        state.invokeTreatmentsTask()
                     }
-                    taskButtonLabel
                 }
-                .font(.headline)
-                .foregroundStyle(Color.white)
-                .frame(maxWidth: .infinity, alignment: .center)
-                .frame(height: 35)
-            }
-            .disabled(disableTaskButton)
-            .listRowBackground(treatmentButtonBackground)
-            .shadow(radius: 3)
-            .clipShape(RoundedRectangle(cornerRadius: 8))
-            .confirmationDialog(
-                "Glucose is very low! Give insulin?",
-                isPresented: $showDangerousLowAlert,
-                titleVisibility: .visible
-            ) {
-                Button("Cancel", role: .cancel) {}
-                Button("Ignore Warning and Enact Bolus", role: .destructive) {
-                    state.invokeTreatmentsTask()
+            } header: {
+                if bolusWarning != "" {
+                    Text(bolusWarning)
+                        .textCase(nil)
+                        .font(.subheadline)
+                        .foregroundColor(Color.loopRed)
+                        .frame(maxWidth: .infinity, alignment: .center)
+                        .padding(.top, -22)
                 }
             }
         }

+ 8 - 3
Trio/Sources/Services/BolusCalculator/BolusCalculationManager.swift

@@ -30,6 +30,7 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
     private struct BolusCalculatorVariables {
         var insulinRequired: Decimal
         var evBG: Decimal
+        var minPredBG: Decimal
         var insulin: Decimal
         var target: Decimal
         var isf: Decimal
@@ -245,6 +246,7 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
             return BolusCalculatorVariables(
                 insulinRequired: 0,
                 evBG: 0,
+                minPredBG: 0,
                 insulin: 0,
                 target: currentBGTarget,
                 isf: currentISF,
@@ -259,6 +261,7 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
         return BolusCalculatorVariables(
             insulinRequired: (mostRecentDetermination.insulinReq ?? 0) as Decimal,
             evBG: (mostRecentDetermination.eventualBG ?? 0) as Decimal,
+            minPredBG: (mostRecentDetermination.minPredBGFromReason ?? 0) as Decimal,
             insulin: (mostRecentDetermination.insulinForManualBolus ?? 0) as Decimal,
             target: (mostRecentDetermination.currentTarget ?? currentBGTarget as NSDecimalNumber) as Decimal,
             isf: (mostRecentDetermination.insulinSensitivity ?? NSDecimalNumber(decimal: currentISF)) as Decimal,
@@ -338,7 +341,8 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
             fraction: settings.fraction,
             maxBolus: maxBolus,
             maxIOB: maxIOB,
-            maxCOB: maxCOB
+            maxCOB: maxCOB,
+            minPredBG: bolusVars.minPredBG
         )
     }
 
@@ -389,8 +393,8 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
 
         // the final result for recommended insulin amount
         var insulinCalculated: Decimal
-        // don't recommend insulin when glucose is < 54
-        if input.currentBG < 54 {
+        // don't recommend insulin when current glucose or minPredBG is < 54
+        if input.currentBG < 54 || input.minPredBG < 54 {
             insulinCalculated = 0
         } else {
             // no negative insulinCalculated
@@ -454,6 +458,7 @@ struct CalculationInput: Sendable {
     let maxBolus: Decimal // Maximum allowed bolus
     let maxIOB: Decimal // Maximum allowed IOB to be used for rec. bolus calculation
     let maxCOB: Decimal // Maximum allowed COB to be used for rec. bolus calculation
+    let minPredBG: Decimal // Minimum Predicted Glucose determined by Oref
 }
 
 /// Results of the bolus calculation