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

Rework glucose stats and bare statistics sheet WIP

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

+ 12 - 1
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -13173,6 +13173,9 @@
         }
       }
     },
+    "< 54" : {
+
+    },
     "< 59" : {
       "extractionState" : "stale",
       "localizations" : {
@@ -13601,6 +13604,9 @@
         }
       }
     },
+    "> 180" : {
+
+    },
     "> 198" : {
       "extractionState" : "stale",
       "localizations" : {
@@ -16427,7 +16433,6 @@
       }
     },
     "70-140" : {
-      "extractionState" : "stale",
       "localizations" : {
         "ar" : {
           "stringUnit" : {
@@ -16533,6 +16538,9 @@
         }
       }
     },
+    "70-180" : {
+
+    },
     "75%:" : {
 
     },
@@ -82729,6 +82737,9 @@
         }
       }
     },
+    "GMI" : {
+
+    },
     "Got it!" : {
       "localizations" : {
         "ar" : {

+ 20 - 11
Trio/Sources/Modules/Stat/View/StatRootView.swift

@@ -117,14 +117,14 @@ extension Stat {
                         )
                     }
 
-                    Divider()
-
-                    SectorChart(
-                        highLimit: state.highLimit,
-                        lowLimit: state.lowLimit,
-                        units: state.units,
-                        glucose: state.glucoseFromPersistence
-                    )
+//                    Divider()
+//
+//                    SectorChart(
+//                        highLimit: state.highLimit,
+//                        lowLimit: state.lowLimit,
+//                        units: state.units,
+//                        glucose: state.glucoseFromPersistence
+//                    )
                 }
             }
         }
@@ -132,23 +132,32 @@ extension Stat {
         private var glucoseStatsCard: some View {
             StatCard {
                 VStack(spacing: Constants.spacing) {
-                    BareStatisticsView.HbA1cView(
+                    SectorChart(
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        hbA1cDisplayUnit: state.hbA1cDisplayUnit,
                         glucose: state.glucoseFromPersistence
                     )
 
                     Divider()
 
-                    BareStatisticsView.BloodGlucoseView(
+                    BareStatisticsView.HbA1cView(
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
                         hbA1cDisplayUnit: state.hbA1cDisplayUnit,
                         glucose: state.glucoseFromPersistence
                     )
+
+//                    Divider()
+//
+//                    BareStatisticsView.BloodGlucoseView(
+//                        highLimit: state.highLimit,
+//                        lowLimit: state.lowLimit,
+//                        units: state.units,
+//                        hbA1cDisplayUnit: state.hbA1cDisplayUnit,
+//                        glucose: state.glucoseFromPersistence
+//                    )
                 }
             }
         }

+ 64 - 43
Trio/Sources/Modules/Stat/View/ViewElements/BareStatisticsView.swift

@@ -5,7 +5,7 @@ import SwiftUI
 struct BareStatisticsView {
     // MARK: - Helper Functions
 
-    private static func medianCalculation(array: [Int]) -> Double {
+    static func medianCalculation(array: [Int]) -> Double {
         guard !array.isEmpty else { return 0 }
         let sorted = array.sorted()
         let length = array.count
@@ -39,53 +39,68 @@ struct BareStatisticsView {
         }
 
         private var hba1c: some View {
-            HStack(spacing: 50) {
-                let useUnit: GlucoseUnits = {
-                    if hbA1cDisplayUnit == .mmolMol { return .mmolL }
-                    else { return .mgdL }
-                }()
-
-                let hba1cs = glucoseStats()
-                // First date
-                let previous = glucose.last?.date ?? Date()
-                // Last date (recent)
-                let current = glucose.first?.date ?? Date()
-                // Total time in days
-                let numberOfDays = (current - previous).timeInterval / 8.64E4
-
-                let hba1cString = (
-                    useUnit == .mmolL ? hba1cs.ifcc
-                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) : hba1cs.ngsp
-                        .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))
-                        + " %"
-                )
-                VStack(spacing: 5) {
-                    Text("HbA1c").font(.subheadline).foregroundColor(.secondary)
-                    Text(hba1cString)
-                }
-                VStack(spacing: 5) {
-                    Text("SD").font(.subheadline).foregroundColor(.secondary)
-                    Text(
-                        hba1cs.sd
-                            .formatted(
-                                .number.grouping(.never).rounded()
-                                    .precision(.fractionLength(units == .mmolL ? 1 : 0))
-                            )
+            VStack(alignment: .leading) {
+                HStack(spacing: 40) {
+                    let useUnit: GlucoseUnits = {
+                        if hbA1cDisplayUnit == .mmolMol { return .mmolL }
+                        else { return .mgdL }
+                    }()
+
+                    let glucoseStats = calculateGlucoseStatistics()
+                    // First date
+                    let previous = glucose.last?.date ?? Date()
+                    // Last date (recent)
+                    let current = glucose.first?.date ?? Date()
+                    // Total time in days
+                    let numberOfDays = (current - previous).timeInterval / 8.64E4
+
+                    let hba1cString = (
+                        useUnit == .mmolL ? glucoseStats.ifcc
+                            .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) : glucoseStats.ngsp
+                            .formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))
+                            + " %"
                     )
-                }
-                VStack(spacing: 5) {
-                    Text("CV").font(.subheadline).foregroundColor(.secondary)
-                    Text(hba1cs.cv.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))))
-                }
-                VStack(spacing: 5) {
-                    Text("Days").font(.subheadline).foregroundColor(.secondary)
-                    Text(numberOfDays.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))))
+                    VStack(spacing: 5) {
+                        Text("HbA1c").font(.subheadline).foregroundColor(.secondary)
+                        Text(hba1cString)
+                    }
+                    VStack(spacing: 5) {
+                        Text("GMI").font(.subheadline).foregroundColor(.secondary)
+                        Text(glucoseStats.gmi.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))) + " %")
+                    }
+                    VStack(spacing: 5) {
+                        Text("SD").font(.subheadline).foregroundColor(.secondary)
+                        Text(
+                            glucoseStats.sd
+                                .formatted(
+                                    .number.grouping(.never).rounded()
+                                        .precision(.fractionLength(units == .mmolL ? 1 : 0))
+                                )
+                        )
+                    }
+                    VStack(spacing: 5) {
+                        Text("CV").font(.subheadline).foregroundColor(.secondary)
+                        Text(glucoseStats.cv.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))))
+                    }
+                    VStack(spacing: 5) {
+                        Text("Days").font(.subheadline).foregroundColor(.secondary)
+                        Text(numberOfDays.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1))))
+                    }
                 }
             }
         }
 
-        func glucoseStats()
-            -> (ifcc: Double, ngsp: Double, average: Double, median: Double, sd: Double, cv: Double, readings: Double)
+        func calculateGlucoseStatistics()
+            -> (
+                ifcc: Double,
+                ngsp: Double,
+                gmi: Double,
+                average: Double,
+                median: Double,
+                sd: Double,
+                cv: Double,
+                readings: Double
+            )
         {
             // First date
             let previous = glucose.last?.date ?? Date()
@@ -105,10 +120,15 @@ struct BareStatisticsView {
 
             var NGSPa1CStatisticValue = 0.0
             var IFCCa1CStatisticValue = 0.0
+            var GMIValue = 0.0
 
             if numberOfDays > 0 {
                 NGSPa1CStatisticValue = (glucoseAverage + 46.7) / 28.7
                 IFCCa1CStatisticValue = 10.929 * (NGSPa1CStatisticValue - 2.152)
+
+                // Calculate GMI using the standard formula
+                // GMI = 3.31 + 0.02392 * averageGlucose (mg/dL)
+                GMIValue = 3.31 + 0.02392 * glucoseAverage
             }
 
             var sumOfSquares = 0.0
@@ -127,6 +147,7 @@ struct BareStatisticsView {
             return (
                 ifcc: IFCCa1CStatisticValue,
                 ngsp: NGSPa1CStatisticValue,
+                gmi: GMIValue,
                 average: glucoseAverage * (units == .mmolL ? 0.0555 : 1),
                 median: medianGlucose * (units == .mmolL ? 0.0555 : 1),
                 sd: sd * (units == .mmolL ? 0.0555 : 1),

+ 82 - 20
Trio/Sources/Modules/Stat/View/ViewElements/SectorChart.swift

@@ -24,6 +24,68 @@ struct SectorChart: View {
 
     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 140 and high limit (normal range)
+            let normal = glucose.filter { $0.glucose > 140 && $0.glucose <= Int(highLimit) }.count
+            // Count readings between 54 and low limit (low)
+            let low = glucose.filter { $0.glucose < Int(lowLimit) }.count
+
+            let justGlucoseArray = glucose.compactMap({ each in Int(each.glucose as Int16) })
+            let sumReadings = justGlucoseArray.reduce(0, +)
+
+            let glucoseAverage = Decimal(sumReadings) / total
+            let medianGlucose = BareStatisticsView.medianCalculation(array: justGlucoseArray)
+
+            let lowPercentage = Decimal(low) / total * 100
+            let tightPercentage = Decimal(tight) / total * 100
+            let inRangePercentage = Decimal(normal) / total * 100
+            let highPercentage = Decimal(high) / total * 100
+
+            VStack(alignment: .leading, spacing: 10) {
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("70-180").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text(inRangePercentage.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) + " %")
+                        .foregroundStyle(Color.loopGreen)
+                }
+
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("70-140").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text(tightPercentage.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) + " %")
+                        .foregroundStyle(Color.green)
+                }
+            }.padding(.leading, 5)
+
+            VStack(alignment: .leading, spacing: 10) {
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("> 180").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text(highPercentage.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) + " %")
+                        .foregroundStyle(Color.orange)
+                }
+
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("< 54").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text(lowPercentage.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))) + " %")
+                        .foregroundStyle(Color.loopRed)
+                }
+            }
+
+            VStack(alignment: .leading, spacing: 10) {
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("Average").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text(glucoseAverage.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))))
+                }
+
+                VStack(alignment: .leading, spacing: 5) {
+                    Text("Median").font(.subheadline).foregroundStyle(Color.secondary)
+                    Text(medianGlucose.formatted(.number.grouping(.never).rounded().precision(.fractionLength(0))))
+                }
+            }
+
             Chart {
                 ForEach(rangeData, id: \.range) { data in
                     SectorMark(
@@ -32,27 +94,27 @@ struct SectorChart: View {
                         outerRadius: selectedRange == data.range ? 100 : 80,
                         angularInset: 1.5
                     )
-                    .cornerRadius(3)
+//                    .cornerRadius(3)
                     .foregroundStyle(data.color)
-                    .annotation(position: .overlay, alignment: .center, spacing: 0) {
-                        if data.percentage > 0 {
-                            Text("\(Int(data.percentage))%")
-                                .font(.callout)
-                                .foregroundStyle(.white)
-                                .fontWeight(.bold)
-                        }
-                    }
+//                    .annotation(position: .automatic, alignment: .leading, spacing: 0) {
+//                        if data.percentage > 0 {
+//                            Text("\(Int(data.percentage))%")
+//                                .font(.callout)
+//                                .foregroundStyle(.white)
+//                                .fontWeight(.bold)
+//                        }
+//                    }
                 }
             }
-            .chartLegend(position: .bottom, spacing: 20)
+//            .chartLegend(position: .bottom, spacing: 20)
             .chartAngleSelection(value: $selectedCount)
-            .chartForegroundStyleScale([
-                "High": Color.orange,
-                "In Range": Color.green,
-                "Low": Color.red
-            ])
-            .padding(.vertical)
-            .frame(height: 250)
+//            .chartForegroundStyleScale([
+//                "High": Color.orange,
+//                "In Range": Color.green,
+//                "Low": Color.red
+//            ])
+//            .padding(.vertical)
+            .frame(height: 100)
         }
         .onChange(of: selectedCount) { _, newValue in
             if let newValue {
@@ -193,8 +255,8 @@ struct SectorChart: View {
                 title: "Low Glucose",
                 color: .red,
                 items: [
-                    ("Very Low (<\(veryLowThresholdTreshold) \(units.rawValue))", Decimal(veryLow) / total * 100),
-                    ("Low (\(veryLowThresholdTreshold)-\(lowLimitTreshold) \(units.rawValue))", Decimal(low) / total * 100)
+                    ("Low (\(veryLowThresholdTreshold)-\(lowLimitTreshold) \(units.rawValue))", Decimal(low) / total * 100),
+                    ("Very Low (<\(veryLowThresholdTreshold) \(units.rawValue))", Decimal(veryLow) / total * 100)
                 ]
             )
         }
@@ -234,7 +296,7 @@ private struct RangeDetailPopover: View {
         .padding(20)
         .background {
             RoundedRectangle(cornerRadius: 10)
-                .fill(data.color)
+                .fill(Color.blue)
         }
     }