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

Add time in range type to stats views and charts

Deniz Cengiz 1 год назад
Родитель
Сommit
5cf3ad55bb

+ 10 - 10
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -5695,6 +5695,16 @@
         }
       }
     },
+    "%@ (%@-%@)" : {
+      "localizations" : {
+        "en" : {
+          "stringUnit" : {
+            "state" : "new",
+            "value" : "%1$@ (%2$@-%3$@)"
+          }
+        }
+      }
+    },
     "%@ %@" : {
       "localizations" : {
         "bg" : {
@@ -176780,16 +176790,6 @@
         }
       }
     },
-    "Tight (%@-%@)" : {
-      "localizations" : {
-        "en" : {
-          "stringUnit" : {
-            "state" : "new",
-            "value" : "Tight (%1$@-%2$@)"
-          }
-        }
-      }
-    },
     "Time" : {
       "comment" : "Time basal profile",
       "localizations" : {

+ 1 - 0
Trio/Sources/Models/TimeInRangeType.swift

@@ -4,6 +4,7 @@ enum TimeInRangeType: String, JSON, CaseIterable, Identifiable, Codable, Hashabl
     var id: String { rawValue }
     case timeInTightRange
     case timeInNormoglycemia
+
     var displayName: String {
         switch self {
         case .timeInTightRange:

+ 20 - 3
Trio/Sources/Modules/Stat/StatStateModel+Setup/StackedChartSetup.swift

@@ -64,6 +64,23 @@ extension Stat.StateModel {
     func calculateGlucoseRangeStatsForStackedChart(from ids: [NSManagedObjectID]) async {
         let taskContext = CoreDataStack.shared.newTaskContext()
 
+        var bottomThreshold: Int {
+            switch timeInRangeType {
+            case .timeInTightRange:
+                return 70
+            case .timeInNormoglycemia:
+                return 63
+            }
+        }
+
+        var topThreshold: Int {
+            switch timeInRangeType {
+            case .timeInNormoglycemia,
+                 .timeInTightRange:
+                return 140
+            }
+        }
+
         let calendar = Calendar.current
 
         let stats = await taskContext.perform {
@@ -93,9 +110,9 @@ extension Stat.StateModel {
             // Ranges are processed from bottom to top in the stacked chart
             let ranges: [(name: String, condition: (Int) -> Bool)] = [
                 ("<54", { g in g <= 54 }),
-                ("54-70", { g in g > 54 && g < 70 }),
-                ("70-140", { g in g >= 70 && g <= 140 }),
-                ("140-180", { g in g > 140 && g <= 180 }),
+                ("54-\(bottomThreshold)", { g in g > 54 && g < bottomThreshold }),
+                ("\(bottomThreshold)-\(topThreshold)", { g in g >= bottomThreshold && g <= topThreshold }),
+                ("\(topThreshold)-180", { g in g > topThreshold && g <= 180 }),
                 ("180-200", { g in g > 180 && g <= 200 }),
                 ("200-220", { g in g > 200 && g <= 220 }),
                 (">220", { g in g > 220 })

+ 2 - 0
Trio/Sources/Modules/Stat/StatStateModel.swift

@@ -11,6 +11,7 @@ extension Stat {
         var lowLimit: Decimal = 70
         var eA1cDisplayUnit: EstimatedA1cDisplayUnit = .percent
         var units: GlucoseUnits = .mgdL
+        var timeInRangeType: TimeInRangeType = .timeInTightRange
         var useFPUconversion: Bool = false
         var glucoseFromPersistence: [GlucoseStored] = []
         var loopStatRecords: [LoopStatRecord] = []
@@ -85,6 +86,7 @@ extension Stat {
             units = settingsManager.settings.units
             eA1cDisplayUnit = settingsManager.settings.eA1cDisplayUnit
             useFPUconversion = settingsManager.settings.useFPUconversion
+            timeInRangeType = settingsManager.settings.timeInRangeType
         }
 
         func setupGlucoseArray(for interval: StatsTimeIntervalWithToday) {

+ 4 - 2
Trio/Sources/Modules/Stat/View/StatRootView.swift

@@ -130,7 +130,8 @@ extension Stat {
                             highLimit: state.highLimit,
                             lowLimit: state.lowLimit,
                             units: state.units,
-                            glucoseRangeStats: state.glucoseRangeStats
+                            glucoseRangeStats: state.glucoseRangeStats,
+                            timeInRangeType: state.timeInRangeType
                         )
                     }
                 }
@@ -144,7 +145,8 @@ extension Stat {
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        glucose: state.glucoseFromPersistence
+                        glucose: state.glucoseFromPersistence,
+                        timeInRangeType: state.timeInRangeType
                     )
 
                     Divider()

+ 27 - 6
Trio/Sources/Modules/Stat/View/ViewElements/Glucose/GlucoseDistributionChart.swift

@@ -7,6 +7,24 @@ struct GlucoseDistributionChart: View {
     let lowLimit: Decimal
     let units: GlucoseUnits
     let glucoseRangeStats: [GlucoseRangeStats]
+    let timeInRangeType: TimeInRangeType
+
+    var bottomThreshold: Int {
+        switch timeInRangeType {
+        case .timeInTightRange:
+            return 70
+        case .timeInNormoglycemia:
+            return 63
+        }
+    }
+
+    var topThreshold: Int {
+        switch timeInRangeType {
+        case .timeInNormoglycemia,
+             .timeInTightRange:
+            return 140
+        }
+    }
 
     var body: some View {
         VStack(alignment: .leading, spacing: 8) {
@@ -25,9 +43,9 @@ struct GlucoseDistributionChart: View {
             }
             .chartForegroundStyleScale([
                 "<54": .purple.opacity(0.7),
-                "54-70": .red.opacity(0.7),
-                "70-140": .green,
-                "140-180": .green.opacity(0.7),
+                "54-\(bottomThreshold)": .red.opacity(0.7),
+                "\(bottomThreshold)-\(topThreshold)": .green,
+                "\(topThreshold)-180": .green.opacity(0.7),
                 "180-200": .yellow.opacity(0.7),
                 "200-220": .orange.opacity(0.7),
                 ">220": .orange.opacity(0.8)
@@ -36,12 +54,15 @@ struct GlucoseDistributionChart: View {
                 let legendItems: [(String, Color)] = [
                     ("<\(units == .mgdL ? Decimal(54) : 54.asMmolL)", .purple.opacity(0.7)),
                     (
-                        "\(units == .mgdL ? Decimal(54) : 54.asMmolL)-\(units == .mgdL ? Decimal(70) : 70.asMmolL)",
+                        "\(units == .mgdL ? Decimal(54) : 54.asMmolL)-\(units == .mgdL ? Decimal(bottomThreshold) : bottomThreshold.asMmolL)",
                         .red.opacity(0.7)
                     ),
-                    ("\(units == .mgdL ? Decimal(70) : 70.asMmolL)-\(units == .mgdL ? Decimal(140) : 140.asMmolL)", .green),
                     (
-                        "\(units == .mgdL ? Decimal(140) : 140.asMmolL)-\(units == .mgdL ? Decimal(180) : 180.asMmolL)",
+                        "\(units == .mgdL ? Decimal(bottomThreshold) : bottomThreshold.asMmolL)-\(units == .mgdL ? Decimal(topThreshold) : topThreshold.asMmolL)",
+                        .green
+                    ),
+                    (
+                        "\(units == .mgdL ? Decimal(topThreshold) : topThreshold.asMmolL)-\(units == .mgdL ? Decimal(180) : 180.asMmolL)",
                         .green.opacity(0.7)
                     ),
                     (

+ 26 - 5
Trio/Sources/Modules/Stat/View/ViewElements/Glucose/GlucoseSectorChart.swift

@@ -8,6 +8,7 @@ struct GlucoseSectorChart: View {
     let lowLimit: Decimal
     let units: GlucoseUnits
     let glucose: [GlucoseStored]
+    let timeInRangeType: TimeInRangeType
 
     @State private var selectedCount: Int?
     @State private var selectedRange: GlucoseRange?
@@ -22,14 +23,31 @@ struct GlucoseSectorChart: View {
         case low = "Low"
     }
 
+    var tightBottomThreshold: Int {
+        switch timeInRangeType {
+        case .timeInTightRange:
+            return 70
+        case .timeInNormoglycemia:
+            return 63
+        }
+    }
+
+    var topThreshold: Int {
+        switch timeInRangeType {
+        case .timeInNormoglycemia,
+             .timeInTightRange:
+            return 140
+        }
+    }
+
     var body: some View {
         HStack(alignment: .center, spacing: 20) {
             // Calculate total number of glucose readings
             let total = Decimal(glucose.count)
             // Count readings between high limit and 250 mg/dL (high)
             let high = glucose.filter { $0.glucose > Int(highLimit) }.count
-            // Count readings between low limit and 140 mg/dL (tight control)
-            let tight = glucose.filter { $0.glucose >= Int(lowLimit) && $0.glucose <= 140 }.count
+            // Count readings between low limit (TITR: 70 mg/dL, TING 63 mg/dL) and 140 mg/dL (tight control)
+            let tight = glucose.filter { $0.glucose >= tightBottomThreshold && $0.glucose <= topThreshold }.count
             // Count readings between 140 and high limit (normal range)
             let normal = glucose.filter { $0.glucose >= Int(lowLimit) && $0.glucose <= Int(highLimit) }.count
             // Count readings between 54 and low limit (low)
@@ -54,7 +72,8 @@ struct GlucoseSectorChart: View {
                 }
 
                 VStack(alignment: .leading, spacing: 5) {
-                    Text("\(formatValue(lowLimit))-\(formatValue(140))").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text("\(formatValue(Decimal(tightBottomThreshold)))-\(formatValue(Decimal(topThreshold)))").font(.subheadline)
+                        .foregroundStyle(Color.secondary)
                     Text(tightPercentage.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) + "%")
                         .foregroundStyle(Color.green)
                 }
@@ -219,7 +238,7 @@ struct GlucoseSectorChart: View {
             )
 
         case .inRange:
-            let tight = glucose.filter { $0.glucose >= Int(lowLimit) && $0.glucose <= 140 }.count
+            let tight = glucose.filter { $0.glucose >= Int(tightBottomThreshold) && $0.glucose <= topThreshold }.count
             let glucoseValues = glucose.filter { $0.glucose >= Int(lowLimit) && $0.glucose <= Int(highLimit) }
             let glucoseValuesAsInt = glucoseValues.map { Int($0.glucose) }
             let (average, median, standardDeviation) = calculateDetailedStatistics(for: glucoseValuesAsInt)
@@ -233,7 +252,9 @@ struct GlucoseSectorChart: View {
                         formatPercentage(Decimal(glucoseValues.count) / total * 100)
                     ),
                     (
-                        String(localized: "Tight (\(formatValue(lowLimit))-\(formatValue(140)))"),
+                        String(
+                            localized: "\(timeInRangeType == .timeInTightRange ? "TITR" : "TING") (\(formatValue(Decimal(tightBottomThreshold)))-\(formatValue(Decimal(topThreshold))))"
+                        ),
                         formatPercentage(Decimal(tight) / total * 100)
                     ),
                     (String(localized: "Average"), formatValue(average)),