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

Update stats,
create separate glucose storage for stats,
normal oref0 glucose.json back to 24 hours
Machine readable stats.

Jon Mårtensson 3 лет назад
Родитель
Сommit
3f2c310e04

+ 8 - 8
FreeAPS.xcodeproj/project.pbxproj

@@ -16,11 +16,11 @@
 		19012CDC291D2CB900FB8210 /* LoopStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19012CDB291D2CB900FB8210 /* LoopStats.swift */; };
 		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* TDD_averages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* TDD_averages.swift */; };
-		193B6FAA291AB9AC0087410F /* TwoDaysStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193B6FA9291AB9AC0087410F /* TwoDaysStats.swift */; };
+		19788CAF293CE0F0002FC264 /* GlucoseDataForStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */; };
 		19795118275953E50044850D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		199561C1275E61A50077B976 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 199561C0275E61A50077B976 /* HealthKit.framework */; };
-		19B0EF2128F6D66200069496 /* DailyStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* DailyStats.swift */; };
+		19B0EF2128F6D66200069496 /* Statistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B0EF2028F6D66200069496 /* Statistics.swift */; };
 		19F79FA9283AE7E000646323 /* TDD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19F79FA8283AE7E000646323 /* TDD.swift */; };
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
@@ -443,7 +443,7 @@
 		1927C8FB2744612600347C69 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* TDD_averages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TDD_averages.swift; sourceTree = "<group>"; };
-		193B6FA9291AB9AC0087410F /* TwoDaysStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoDaysStats.swift; sourceTree = "<group>"; };
+		19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseDataForStats.swift; sourceTree = "<group>"; };
 		198377D3266BFFF6004DE65E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377D5266C0A05004DE65E /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
 		198377D6266C0A0A004DE65E /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -464,7 +464,7 @@
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
 		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
-		19B0EF2028F6D66200069496 /* DailyStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyStats.swift; sourceTree = "<group>"; };
+		19B0EF2028F6D66200069496 /* Statistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics.swift; sourceTree = "<group>"; };
 		19C166682756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		19C166692756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		19F79FA8283AE7E000646323 /* TDD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TDD.swift; sourceTree = "<group>"; };
@@ -1341,9 +1341,9 @@
 				19F79FA8283AE7E000646323 /* TDD.swift */,
 				1935363F28496F7D001E0B16 /* TDD_averages.swift */,
 				CE82E02628E869DF00473A9C /* AlertEntry.swift */,
-				19B0EF2028F6D66200069496 /* DailyStats.swift */,
-				193B6FA9291AB9AC0087410F /* TwoDaysStats.swift */,
+				19B0EF2028F6D66200069496 /* Statistics.swift */,
 				19012CDB291D2CB900FB8210 /* LoopStats.swift */,
+				19788CAE293CE0F0002FC264 /* GlucoseDataForStats.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
@@ -2187,7 +2187,7 @@
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
-				19B0EF2128F6D66200069496 /* DailyStats.swift in Sources */,
+				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */,
 				38A0364225ED069400FCBB52 /* TempBasal.swift in Sources */,
@@ -2309,7 +2309,6 @@
 				389ECDFE2601061500D86C4F /* View+Snapshot.swift in Sources */,
 				38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */,
 				38E98A3725F5509500C0CED0 /* String+Extensions.swift in Sources */,
-				193B6FAA291AB9AC0087410F /* TwoDaysStats.swift in Sources */,
 				F90692D1274B99B60037068D /* HealthKitProvider.swift in Sources */,
 				385CEAC125F2EA52002D6D5B /* Announcement.swift in Sources */,
 				8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */,
@@ -2372,6 +2371,7 @@
 				38E98A2D25F52DC400C0CED0 /* NSLocking+Extensions.swift in Sources */,
 				38569353270B5E350002C50D /* CGMRootView.swift in Sources */,
 				69A31254F2451C20361D172F /* BolusStateModel.swift in Sources */,
+				19788CAF293CE0F0002FC264 /* GlucoseDataForStats.swift in Sources */,
 				0CEA2EA070AB041AF3E3745B /* BolusRootView.swift in Sources */,
 				FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */,
 				F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */,

+ 0 - 1
FreeAPS/Resources/json/defaults/monitor/dailyStats.json

@@ -1 +0,0 @@
-[]

+ 112 - 0
FreeAPS/Resources/json/defaults/monitor/statistics.json

@@ -0,0 +1,112 @@
+[
+  {
+    "createdAt" : "1978-02-22T11:43:54.659Z",
+    "iPhone" : "Default",
+    "iOS" : "Default",
+    "Build_Version" : "Default",
+    "Build_Number2" : "Default",
+    "Branch" : "Default",
+    "Build_Date" : "1978-02-22T11:59:59.659Z",
+    "Algorithm" : "Default",
+    "AdjustmentFactor" : 1,
+    "Pump" : "Default",
+    "CGM" : "Default",
+    "insulinType" : "Default",
+    "peakActivityTime" : 65,
+    "TDD" : 0,
+    "Carbs_24h":  0,
+    "GlucoseStorage_Days" : 0,
+    "TIR" : [
+      {
+        "TIR" : [
+          {
+            "oneDay" : 0,
+            "sevenDays" : 0,
+            "thirtyDays" : 0,
+            "ninetyDays" : 0,
+            "totalDays" : 0
+          }
+        ],
+        "Hypos" : [
+          {
+            "oneDay" : 0,
+            "sevenDays" : 0,
+            "thirtyDays" : 0,
+            "ninetyDays" : 0,
+            "totalDays" : 0
+          }
+        ],
+        "Hypers" : [
+          {
+            "oneDay" : 0,
+            "sevenDays" : 0,
+            "thirtyDays" : 0,
+            "ninetyDays" : 0,
+            "totalDays" : 0
+          }
+        ]
+      }
+    ],
+    "Glucose" : [
+      {
+        "Average" : [
+          {
+            "oneDay_mmol" : 0,
+            "oneDay" : 0,
+            "sevenDays_mmol" : 0,
+            "sevenDays" : 0,
+            "thirtyDays_mmol" : 0,
+            "thirtyDays" : 0,
+            "ninetyDays_mmol" : 0,
+            "ninetyDays" : 0,
+            "totalDays_mmol" : 0,
+            "totalDays" : 0
+          }
+        ],
+        "Median" : [
+          {
+            "oneDay_mmol" : 0,
+            "oneDay" : 0,
+            "sevenDays_mmol" : 0,
+            "sevenDays" : 0,
+            "thirtyDays_mmol":  0,
+            "thirtyDays" : 0,
+            "ninetyDays_mmol" : 0,
+            "ninetyDays" : 0,
+            "totalDays_mmol" : 0,
+            "totalDays" : 0
+          }
+        ]
+      }
+    ],
+    "HbA1c" : [
+      {
+        "oneDay_mmolMol" : 0,
+        "oneDay" : 0,
+        "sevenDays_mmolMol" : 0,
+        "sevenDays" : 0,
+        "thirtyDays_mmolMol" : 0,
+        "thirtyDays" : 0,
+        "ninetyDays_mmolMol" : 0,
+        "ninetyDays" : 0,
+        "totalDays_mmolMol" : 0,
+        "totalDays" : 0
+      }
+    ],
+    "LoopStats" : [
+      {
+        "loops" : 0,
+        "errors" : 0,
+        "success_rate" : 0,
+        "avg_interval" : 0,
+        "median_interval" : 0,
+        "min_interval" : 0,
+        "max_interval" : 0,
+        "avg_duration" : 0,
+        "median_duration" : 0,
+        "min_duration" : 0,
+        "max_duration" : 0
+      }
+    ]
+  }
+]

+ 0 - 1
FreeAPS/Resources/json/defaults/monitor/twoDaysStats.json

@@ -1 +0,0 @@
-[]

+ 237 - 260
FreeAPS/Sources/APS/APSManager.swift

@@ -659,8 +659,8 @@ final class BaseAPSManager: APSManager, Injectable {
             // Create a tdd.json
             tdd(enacted_: enacted)
 
-            // Create a dailyStats.json
-            dailyStats()
+            // Create a statistics.json
+            statistics()
 
             debug(.apsManager, "Suggestion enacted. Received: \(received)")
             DispatchQueue.main.async {
@@ -747,17 +747,17 @@ final class BaseAPSManager: APSManager, Injectable {
         let length = array.count
 
         if length % 2 == 0 {
-            return (sorted[length / 2 - 1] + sorted[length / 2]) / 2.0
+            return (sorted[length / 2 - 1] + sorted[length / 2]) / 2
         }
         return sorted[length / 2]
     }
 
-    // Add to dailyStats.JSON
-    private func dailyStats() {
-        var testFile: [DailyStats] = []
+    // Add to statistics.JSON
+    private func statistics() {
+        var testFile: [Statistics] = []
         var testIfEmpty = 0
         storage.transaction { storage in
-            testFile = storage.retrieve(OpenAPS.Monitor.dailyStats, as: [DailyStats].self) ?? []
+            testFile = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self) ?? []
             testIfEmpty = testFile.count
         }
         // Only run every hour
@@ -771,14 +771,11 @@ final class BaseAPSManager: APSManager, Injectable {
         let carbs = storage.retrieve(OpenAPS.Monitor.carbHistory, as: [CarbsEntry].self)
         let tdds = storage.retrieve(OpenAPS.Monitor.tdd, as: [TDD].self)
         var currentTDD: Decimal = 0
-
         if tdds?.count ?? 0 > 0 {
             currentTDD = tdds?[0].TDD ?? 0
         }
-
         let carbs_length = carbs?.count ?? 0
         var carbTotal: Decimal = 0
-
         if carbs_length != 0 {
             for each in carbs! {
                 if each.carbs != 0 {
@@ -786,7 +783,6 @@ final class BaseAPSManager: APSManager, Injectable {
                 }
             }
         }
-
         var algo_ = "oref0" // Default
         if preferences.enableChris, preferences.useNewFormula {
             algo_ = "Dynamic ISF, Logarithmic Formula"
@@ -801,7 +797,7 @@ final class BaseAPSManager: APSManager, Injectable {
         let branch = Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String
         let pump_ = pumpManager?.localizedTitle ?? ""
         let cgm = settingsManager.settings.cgm
-        let file = OpenAPS.Monitor.dailyStats
+        let file = OpenAPS.Monitor.statistics
         var iPa: Decimal = 75
         if preferences.useCustomPeakTime {
             iPa = preferences.insulinPeakTime
@@ -810,11 +806,9 @@ final class BaseAPSManager: APSManager, Injectable {
         } else if preferences.curve.rawValue == "ultra-rapid" {
             iPa = 50
         }
-
         // Retrieve the loopStats data
         let lsData = storage.retrieve(OpenAPS.Monitor.loopStats, as: [LoopStats].self)?
             .sorted { $0.start > $1.start } ?? []
-
         var successRate: Double?
         var successNR = 0.0
         var errorNR = 0.0
@@ -878,310 +872,293 @@ final class BaseAPSManager: APSManager, Injectable {
             }
 
             successRate = (successNR / Double(i)) * 100
-
             averageIntervalLoops = ((lsData[0].end ?? lsData[lsData.count - 1].start) - lsData[lsData.count - 1].start)
                 .timeInterval / 60 / Double(i)
-
             averageLoopTime /= Double(i)
             // Median values
             medianLoopTime = medianCalculation(array: timeForOneLoopArray)
             medianInterval = medianCalculation(array: timeIntervalLoopArray)
         }
-
         if minimumInt == 999.0 {
             minimumInt = 0.0
         }
-
         if minimumLoopTime == 9999.0 {
             minimumLoopTime = 0.0
         }
-
-        // Time In Range (%) and Average Glucose (24 hours). This looks dumb and I will refactor it later.
-        let glucose = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)
-
+        // Time In Range (%) and Average Glucose (24 hours). This will be refactored later after some testing.
+        let glucose = storage.retrieve(OpenAPS.Monitor.glucose_data, as: [GlucoseDataForStats].self)
         let length_ = glucose?.count ?? 0
         let endIndex = length_ - 1
-        var oneDayGlucoseIndex = endIndex
-
         var bg: Decimal = 0
         var bgArray: [Double] = []
+        var bgArrayForTIR: [(bg_: Double, date_: Date)] = []
+        var bgArray_1: [(bg_: Double, date_: Date)] = []
+        var bgArray_7: [(bg_: Double, date_: Date)] = []
+        var bgArray_30: [(bg_: Double, date_: Date)] = []
+        var bgArray_90: [(bg_: Double, date_: Date)] = []
         var medianBG = 0.0
         var nr_bgs: Decimal = 0
-        let startDate = glucose![0].date
+
+        var startDate = Date("1978-02-22T11:43:54.659Z")
+        if endIndex >= 0 {
+            startDate = glucose?[0].date
+        }
         var end1 = false
-        var end2 = false
+        var end7 = false
+        var end30 = false
+        var end90 = false
         var bg_1: Decimal = 0
-        var bg_2: Decimal = 0
+        var bg_7: Decimal = 0
+        var bg_30: Decimal = 0
+        var bg_90: Decimal = 0
         var bg_total: Decimal = 0
         var j = -1
 
-        if length_ != 0 {
+        // Make arrays for median calculations and calculate averages
+        if endIndex >= 0 {
             for entry in glucose! {
                 j += 1
-                if entry.glucose! > 0 {
-                    bg += Decimal(entry.glucose!)
-                    bgArray.append(Double(entry.glucose!))
+                if entry.glucose > 0 {
+                    bg += Decimal(entry.glucose)
+                    bgArray.append(Double(entry.glucose))
+                    bgArrayForTIR.append((Double(entry.glucose), entry.date))
                     nr_bgs += 1
 
-                    if startDate - entry.date >= 8.64E7, !end1 {
+                    if (startDate! - entry.date).timeInterval >= 8.64E4, !end1 {
                         end1 = true
-                        oneDayGlucoseIndex = j
                         bg_1 = bg / nr_bgs
+                        bgArray_1 = bgArrayForTIR
+                        // time_1 = ((startDate ?? Date()) - entry.date).timeInterval
+                    }
+                    if (startDate! - entry.date).timeInterval >= 6.048E5, !end7 {
+                        end7 = true
+                        bg_7 = bg / nr_bgs
+                        bgArray_7 = bgArrayForTIR
+                        // time_7 = ((startDate ?? Date()) - entry.date).timeInterval
+                    }
+                    if (startDate! - entry.date).timeInterval >= 2.592E6, !end30 {
+                        end30 = true
+                        bg_30 = bg / nr_bgs
+                        bgArray_30 = bgArrayForTIR
+                        // time_30 = ((startDate ?? Date()) - entry.date).timeInterval
                     }
-                    if startDate - entry.date >= 1.72E8, !end2 {
-                        end2 = true
-                        oneDayGlucoseIndex = j
-                        bg_2 = bg / nr_bgs
+                    if (startDate! - entry.date).timeInterval >= 7.776E6, !end90 {
+                        end90 = true
+                        bg_90 = bg / nr_bgs
+                        bgArray_90 = bgArrayForTIR
+                        // time_90 = ((startDate ?? Date()) - entry.date).timeInterval
                     }
                 }
             }
         }
 
-        if nr_bgs != 0 {
-            // Up tp two days
+        if nr_bgs > 0 {
+            // Up to 91 days
             bg_total = bg / nr_bgs
         }
 
+        // Total median
         medianBG = medianCalculation(array: bgArray)
-
-        let fullTime = glucose![0].date - glucose![endIndex].date
-        let fullTime_1 = glucose![0].date - glucose![oneDayGlucoseIndex].date
-
-        var daysBG = fullTime / 8.64E7
-
-        var timeInHypo: Decimal = 0
-        var timeInHyper: Decimal = 0
-        var hypos: Decimal = 0
-        var hypers: Decimal = 0
-        var i = -1
-        var lastIndex = false
-
-        while i < endIndex {
-            i += 1
-
-            let currentTime = glucose![i].date
-            var previousTime = currentTime
-
-            if i + 1 <= endIndex {
-                previousTime = glucose![i + 1].date
-            } else {
-                lastIndex = true
-            }
-
-            if glucose![i].glucose! < 72, !lastIndex {
-                timeInHypo += currentTime - previousTime
-            } else if glucose![i].glucose! > 180, !lastIndex {
-                timeInHyper += currentTime - previousTime
-            }
-        }
-
-        if timeInHypo == 0 {
-            hypos = 0
-        } else { hypos = (timeInHypo / fullTime) * 100
-        }
-
-        if timeInHyper == 0 {
-            hypers = 0
-        } else { hypers = (timeInHyper / fullTime) * 100
-        }
-
-        let TIR = 100 - (hypos + hypers)
-
-        // Do the loop again but with for 1 day. I will change this later, because this looks really dumb:
-        var timeInHypo_1: Decimal = 0
-        var timeInHyper_1: Decimal = 0
-        var hypos_1: Decimal = 0
-        var hypers_1: Decimal = 0
-        i = -1
-        lastIndex = false
-
-        while i < oneDayGlucoseIndex {
-            i += 1
-
-            let currentTime = glucose![i].date
-            var previousTime = currentTime
-
-            if i + 1 <= oneDayGlucoseIndex {
-                previousTime = glucose![i + 1].date
-            } else {
-                lastIndex = true
-            }
-
-            if glucose![i].glucose! < 72, !lastIndex {
-                timeInHypo_1 += currentTime - previousTime
-            } else if glucose![i].glucose! > 180, !lastIndex {
-                timeInHyper_1 += currentTime - previousTime
-            }
-        }
-
-        if timeInHypo_1 == 0 {
-            hypos_1 = 0
-        } else { hypos_1 = (timeInHypo_1 / fullTime_1) * 100
-        }
-
-        if timeInHyper_1 == 0 {
-            hypers_1 = 0
-        } else { hypers_1 = (timeInHyper_1 / fullTime_1) * 100
-        }
-
-        let TIR_1 = 100 - (hypos_1 + hypers_1)
-
-        // Add a 2 day average to twoDaysStats.json
-        let file_10 = OpenAPS.Monitor.twoDaysStats
-
-        let twoStats = TwoDaysStats(
-            createdAt: Date(), past2daysAverage: roundDecimal(bg_2, 1)
-        )
-        var uniqEvents: [TwoDaysStats] = []
-        var test1 = uniqEvents
-        let test2: [TwoDaysStats] = [twoStats]
-        var countIndeces = 0
-
-        storage.transaction { storage in
-            test1 = storage.retrieve(file_10, as: [TwoDaysStats].self) ?? []
-            countIndeces = test1.count
-        }
-
-        if daysBG >= 2 {
-            if countIndeces == 0 {
-                storage.transaction { storage in
-                    storage.save(test2, as: file_10)
-                    daysBG = Decimal(countIndeces * 2)
+        var daysBG = 0.0
+        var fullTime = 0.0
+
+        if endIndex >= 0 {
+            fullTime = (startDate! - glucose![endIndex].date).timeInterval
+            daysBG = fullTime / 8.64E4
+        }
+
+        func tir(_ array: [(bg_: Double, date_: Date)]) -> (TIR: Double, hypos: Double, hypers: Double) {
+            var timeInHypo = 0.0
+            var timeInHyper = 0.0
+            var hypos = 0.0
+            var hypers = 0.0
+            var i = -1
+            var lastIndex = false
+            let endIndex = array.count - 1
+            while i < endIndex {
+                i += 1
+                let currentTime = array[i].date_
+                var previousTime = currentTime
+                if i + 1 <= endIndex {
+                    previousTime = array[i + 1].date_
+                } else {
+                    lastIndex = true
                 }
-
-                // Keep 2 days apart from each array
-            } else if test1[0].createdAt.addingTimeInterval(2.days.timeInterval) < Date() {
-                storage.transaction { storage in
-                    storage.append(twoStats, to: file_10, uniqBy: \.createdAt)
-                    uniqEvents = storage.retrieve(file_10, as: [TwoDaysStats].self)?
-                        .filter { $0.createdAt.addingTimeInterval(365.days.timeInterval) > Date() }
-                        .sorted { $0.createdAt > $1.createdAt } ?? []
-                    storage.save(Array(uniqEvents), as: file_10)
+                if array[i].bg_ < 72.0, !lastIndex {
+                    timeInHypo += (currentTime - previousTime).timeInterval
+                } else if array[i].bg_ > 180, !lastIndex {
+                    timeInHyper += (currentTime - previousTime).timeInterval
                 }
             }
-        }
-
-        // Retrieve the 2 days data array
-        let uniqEvents_1 = storage.retrieve(OpenAPS.Monitor.twoDaysStats, as: [TwoDaysStats].self)?
-            .filter { $0.createdAt.addingTimeInterval(365.days.timeInterval) > Date() }
-            .sorted { $0.createdAt > $1.createdAt } ?? []
-
-        var index = 0
-        var total: Decimal = 0
-        var thirtyDays: Decimal = 0
-        var ninetyDays: Decimal = 0
-        var tenDays: Decimal = 0
-
-        for uniqEvent in uniqEvents_1 {
-            if uniqEvent.past2daysAverage != 0 {
-                total += uniqEvent.past2daysAverage
-                index += 1
-            }
-            if index == 15 {
-                thirtyDays = total / 15
+            if timeInHypo == 0 {
+                hypos = 0
+            } else if fullTime != 0.0 { hypos = (timeInHypo / fullTime) * 100
             }
-            if index == 5 {
-                tenDays = total / 5
-            }
-            if index == 45 {
-                ninetyDays = total / 45
+            if timeInHyper == 0 {
+                hypers = 0
+            } else if fullTime != 0.0 { hypers = (timeInHyper / fullTime) * 100
             }
+            let TIR = 100 - (hypos + hypers)
+            return (roundDouble(TIR, 1), roundDouble(hypos, 1), roundDouble(hypers, 1))
+        }
+
+        // HbA1c estimation (%, mmol/mol) 1 day
+        var NGSPa1CStatisticValue: Decimal = 0.0
+        var IFCCa1CStatisticValue: Decimal = 0.0
+        if end1 {
+            NGSPa1CStatisticValue = (46.7 + bg_1) / 28.7 // NGSP (%)
+            IFCCa1CStatisticValue = 10.929 *
+                (NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
+        }
+        // 7 days
+        var NGSPa1CStatisticValue_7: Decimal = 0.0
+        var IFCCa1CStatisticValue_7: Decimal = 0.0
+        if end7 {
+            NGSPa1CStatisticValue_7 = (46.7 + bg_7) / 28.7 // NGSP (%)
+            IFCCa1CStatisticValue_7 = 10.929 *
+                (NGSPa1CStatisticValue_7 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
         }
-        if index != 0 {
-            total /= Decimal(index)
-        } else { total = bg_total }
-
-        // HbA1c estimation (%, mmol/mol)
-        let NGSPa1CStatisticValue = (46.7 + bg_1) / 28.7 // NGSP (%)
-        let IFCCa1CStatisticValue = 10.929 *
-            (NGSPa1CStatisticValue - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
-        // 10 days
-        let NGSPa1CStatisticValue_10 = (46.7 + tenDays) / 28.7
-        let IFCCa1CStatisticValue_10 = 10.929 * (NGSPa1CStatisticValue_10 - 2.152)
         // 30 days
-        let NGSPa1CStatisticValue_30 = (46.7 + thirtyDays) / 28.7
-        let IFCCa1CStatisticValue_30 = 10.929 * (NGSPa1CStatisticValue_30 - 2.152)
-        // Total days
-        let NGSPa1CStatisticValue_total = (46.7 + total) / 28.7
-        let IFCCa1CStatisticValue_total = 10.929 * (NGSPa1CStatisticValue_total - 2.152)
+        var NGSPa1CStatisticValue_30: Decimal = 0.0
+        var IFCCa1CStatisticValue_30: Decimal = 0.0
+        if end30 {
+            NGSPa1CStatisticValue_30 = (46.7 + bg_30) / 28.7 // NGSP (%)
+            IFCCa1CStatisticValue_30 = 10.929 *
+                (NGSPa1CStatisticValue_30 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
+        }
         // 90 Days
-        let NGSPa1CStatisticValue_90 = (46.7 + ninetyDays) / 28.7
-        let IFCCa1CStatisticValue_90 = 10.929 * (NGSPa1CStatisticValue_90 - 2.152)
-
-        // HbA1c string and BG string:
-        var HbA1c_string_1 = ""
-        var string10Days = ""
-        var string30Days = ""
-        var string90Days = ""
-        var stringTotal = ""
-        var bgString1day = ""
-        var bgString10Days = ""
-        var bgString30Days = ""
-        var bgString90Days = ""
-        var bgAverageTotalString = ""
+        var NGSPa1CStatisticValue_90: Decimal = 0.0
+        var IFCCa1CStatisticValue_90: Decimal = 0.0
+        if end90 {
+            NGSPa1CStatisticValue_90 = (46.7 + bg_90) / 28.7 // NGSP (%)
+            IFCCa1CStatisticValue_90 = 10.929 *
+                (NGSPa1CStatisticValue_90 - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
+        }
+        // Total days
+        var NGSPa1CStatisticValue_total: Decimal = 0.0
+        var IFCCa1CStatisticValue_total: Decimal = 0.0
+        if nr_bgs > 0 {
+            NGSPa1CStatisticValue_total = (46.7 + bg_total) / 28.7 // NGSP (%)
+            IFCCa1CStatisticValue_total = 10.929 *
+                (NGSPa1CStatisticValue_total - 2.152) // IFCC (mmol/mol)  A1C(mmol/mol) = 10.929 * (A1C(%) - 2.15)
+        }
 
         // round output values
-        daysBG = roundDecimal(daysBG, 1)
+        daysBG = roundDouble(daysBG, 1)
 
-        if bg_1 != 0 {
-            bgString1day =
-                " Average BG (mmol/l) 24 hours): \(roundDecimal(bg_1 * 0.0555, 1)). Average BG (mmg/dl) 24 hours: \(roundDecimal(bg_1, 0))."
-            HbA1c_string_1 =
-                "Estimated HbA1c (mmol/mol, 1 day): \(roundDecimal(IFCCa1CStatisticValue, 1)). Estimated HbA1c (%, 1 day): \(roundDecimal(NGSPa1CStatisticValue, 1)). "
+        let loopstat = LoopCycles(
+            loops: Int(successNR + errorNR),
+            errors: Int(errorNR),
+            success_rate: Decimal(round(successRate ?? 0)),
+            avg_interval: roundDecimal(Decimal(averageIntervalLoops), 1),
+            median_interval: roundDecimal(Decimal(medianInterval), 1),
+            min_interval: roundDecimal(Decimal(minimumInt), 1),
+            max_interval: roundDecimal(Decimal(maximumInt), 1),
+            avg_duration: Decimal(roundDouble(averageLoopTime, 2)),
+            median_duration: Decimal(roundDouble(medianLoopTime, 1)),
+            min_duration: roundDecimal(Decimal(minimumLoopTime), 2),
+            max_duration: Decimal(roundDouble(maximumLoopTime, 1))
+        )
+
+        // TIR calcs for every case
+        var oneDay_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
+        var sevenDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
+        var thirtyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
+        var ninetyDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
+        var totalDays_: (TIR: Double, hypos: Double, hypers: Double) = (0.0, 0.0, 0.0)
+
+        // Get all TIR calcs for every case
+        if end1 {
+            oneDay_ = tir(bgArray_1)
         }
-        if tenDays != 0 {
-            string10Days =
-                " HbA1c 10 days (mmol/mol): \(roundDecimal(IFCCa1CStatisticValue_10, 1)). HbA1c 7 days (%): \(roundDecimal(NGSPa1CStatisticValue_10, 1))."
-            bgString10Days =
-                " Average BG (mmol/l) 10 days: \(roundDecimal(tenDays * 0.0555, 1)). Average BG (mg/dl) 10 days: \(roundDecimal(tenDays, 0))."
+        if end7 {
+            sevenDays_ = tir(bgArray_7)
         }
-        if thirtyDays != 0 {
-            string30Days =
-                " HbA1c 30 days (mmol/mol): \(roundDecimal(IFCCa1CStatisticValue_30, 1)).  HbA1c 30 days (%): \(roundDecimal(NGSPa1CStatisticValue_30, 1))."
-            bgString30Days =
-                " Average BG 30 days (mmol/l): \(roundDecimal(thirtyDays * 0.0555, 1)). Average BG 30 days (mg/dl): \(roundDecimal(thirtyDays, 0)). "
+        if end30 {
+            thirtyDays_ = tir(bgArray_30)
         }
-        if ninetyDays != 0 {
-            string90Days =
-                " HbA1c 90 days (mmol/mol): \(roundDecimal(IFCCa1CStatisticValue_90, 1)).  HbA1c 90 days (%): \(roundDecimal(NGSPa1CStatisticValue_90, 1))."
-            bgString90Days =
-                " Average BG 90 days (mmol/l): \(roundDecimal(ninetyDays * 0.0555, 1)). Average BG 90 days (mg/dl): \(roundDecimal(ninetyDays, 0)). "
+        if end90 {
+            ninetyDays_ = tir(bgArray_90)
         }
 
-        if total != 0, daysBG >= 2 {
-            stringTotal =
-                " HbA1c \(daysBG) Days (mmol/mol): \(roundDecimal(IFCCa1CStatisticValue_total, 1)). HbA1c \(daysBG) Days (mg/dl): \(roundDecimal(NGSPa1CStatisticValue_total, 1)) %."
-            bgAverageTotalString =
-                " BG Median 2 Days (mmol/l): \(roundDouble(medianBG * 0.0555, 1)). BG Median 2 Days (mg/dl): \(roundDouble(medianBG, 0)). BG Average \(daysBG) Days (mmg/dl): \(roundDecimal(total, 0))."
+        if nr_bgs > 0 {
+            totalDays_ = tir(bgArrayForTIR)
         }
 
-        let HbA1c_string = HbA1c_string_1 + string10Days + string30Days + string90Days + stringTotal
+        let tir = TIR(
+            oneDay: Decimal(oneDay_.TIR),
+            sevenDays: Decimal(sevenDays_.TIR),
+            thirtyDays: Decimal(thirtyDays_.TIR),
+            ninetyDays: Decimal(ninetyDays_.TIR),
+            totalDays: Decimal(totalDays_.TIR)
+        )
 
-        var tirString =
-            "TIR (24 hours): \(roundDecimal(TIR_1, 0)) %. Time with Hypoglycaemia: \(roundDecimal(hypos_1, 0)) % (< 4 / 72). Time with Hyperglycaemia:  \(roundDecimal(hypers_1, 0)) % (> 10 / 180)."
+        /*
+         var TIR: [TIR]
+         var Hypos: [Hypos]
+         var Hypers: [Hypers]
+          */
+
+        let hypo = Hypos(
+            oneDay: Decimal(oneDay_.hypos),
+            sevenDays: Decimal(sevenDays_.hypos),
+            thirtyDays: Decimal(thirtyDays_.hypos),
+            ninetyDays: Decimal(ninetyDays_.hypos),
+            totalDays: Decimal(totalDays_.hypos)
+        )
 
-        if daysBG >= 2 {
-            tirString +=
-                " (2 days  TIR: \(roundDecimal(TIR, 1)) %. Time with Hypoglycaemia: \(roundDecimal(hypos, 1)) % (< 4 / 72). Time with Hyperglycaemia: \(roundDecimal(hypers, 1)) % (> 10 / 180)."
-        }
+        let hyper = Hypers(
+            oneDay: Decimal(oneDay_.hypers),
+            sevenDays: Decimal(sevenDays_.hypers),
+            thirtyDays: Decimal(thirtyDays_.hypers),
+            ninetyDays: Decimal(ninetyDays_.hypers),
+            totalDays: Decimal(totalDays_.hypers)
+        )
 
-        let bgAverageString = bgString1day + bgString10Days + bgString30Days + bgString90Days + bgAverageTotalString
+        let TimeInRange = TIRs(TIR: [tir], Hypos: [hypo], Hypers: [hyper])
+
+        let median = Median(
+            oneDay_mmol: medianCalculation(array: bgArray_1.map(\.bg_)).asMmolL,
+            oneDay: Decimal(medianCalculation(array: bgArray_1.map(\.bg_))),
+            sevenDays_mmol: medianCalculation(array: bgArray_7.map(\.bg_)).asMmolL,
+            sevenDays: Decimal(medianCalculation(array: bgArray_7.map(\.bg_))),
+            thirtyDays_mmol: medianCalculation(array: bgArray_30.map(\.bg_)).asMmolL,
+            thirtyDays: Decimal(medianCalculation(array: bgArray_30.map(\.bg_))),
+            ninetyDays_mmol: medianCalculation(array: bgArray_90.map(\.bg_)).asMmolL,
+            ninetyDays: Decimal(medianCalculation(array: bgArray_90.map(\.bg_))),
+            totalDays_mmol: roundDecimal(Decimal(medianBG).asMmolL, 2),
+            totalDays: Decimal(medianBG)
+        )
 
-        let loopstat = LoopCycles(
-            success_rate: Decimal(round(successRate ?? 0)),
-            loops: Int(successNR + errorNR),
-            errors: Int(errorNR),
-            median_interval: roundDecimal(Decimal(medianInterval), 1),
-            avg_interval: roundDecimal(Decimal(averageIntervalLoops), 1),
-            min_interval: roundDecimal(Decimal(minimumInt), 1),
-            max_interval: roundDecimal(Decimal(maximumInt), 1),
-            median_duration: Decimal(roundDouble(medianLoopTime, 1)),
-            avg_duration: Decimal(roundDouble(averageLoopTime, 2)),
-            min_duration: roundDecimal(Decimal(minimumLoopTime), 2),
-            max_duration: Decimal(roundDouble(maximumLoopTime, 1))
+        let avgs = Average(
+            oneDay_mmol: roundDecimal(bg_1.asMmolL, 2),
+            oneDay: roundDecimal(bg_1, 0),
+            sevenDays_mmol: roundDecimal(bg_7.asMmolL, 2),
+            sevenDays: roundDecimal(bg_7, 0),
+            thirtyDays_mmol: roundDecimal(bg_30.asMmolL, 2),
+            thirtyDays: roundDecimal(bg_30, 0),
+            ninetyDays_mmol: roundDecimal(bg_90.asMmolL, 2),
+            ninetyDays: roundDecimal(bg_90, 0),
+            totalDays_mmol: roundDecimal(bg_total.asMmolL, 2),
+            totalDays: roundDecimal(bg_total, 0)
         )
 
-        let dailystat = DailyStats(
+        let avg = Averages(Average: [avgs], Median: [median])
+
+        let hbs = Hbs(
+            oneDay_mmolMol: roundDecimal(IFCCa1CStatisticValue, 1),
+            oneDay: roundDecimal(NGSPa1CStatisticValue, 1),
+            sevenDays_mmolMol: roundDecimal(IFCCa1CStatisticValue_7, 1),
+            sevenDays: roundDecimal(NGSPa1CStatisticValue_7, 1),
+            thirtyDays_mmolMol: roundDecimal(IFCCa1CStatisticValue_30, 1),
+            thirtyDays: roundDecimal(NGSPa1CStatisticValue_30, 1),
+            ninetyDays_mmolMol: roundDecimal(IFCCa1CStatisticValue_90, 1),
+            ninetyDays: roundDecimal(NGSPa1CStatisticValue_90, 1),
+            totalDays_mmolMol: roundDecimal(IFCCa1CStatisticValue_total, 1),
+            totalDays: roundDecimal(NGSPa1CStatisticValue_total, 1)
+        )
+
+        let dailystat = Statistics(
             createdAt: Date(),
             iPhone: UIDevice.current.getDeviceId,
             iOS: UIDevice.current.getOSInfo,
@@ -1197,17 +1174,17 @@ final class BaseAPSManager: APSManager, Injectable {
             peakActivityTime: iPa,
             TDD: roundDecimal(currentTDD, 2),
             Carbs_24h: carbTotal,
-            TIR: tirString,
-            BG_Average: bgAverageString,
-            HbA1c: HbA1c_string,
+            GlucoseStorage_Days: Decimal(daysBG),
+            TIR: [TimeInRange],
+            Glucose: [avg],
+            HbA1c: [hbs],
             LoopStats: [loopstat]
         )
-
-        var uniqeEvents: [DailyStats] = []
+        // var uniqeEvents: [Statistics]
 
         storage.transaction { storage in
             storage.append(dailystat, to: file, uniqBy: \.createdAt)
-            uniqeEvents = storage.retrieve(file, as: [DailyStats].self)?
+            var uniqeEvents: [Statistics] = storage.retrieve(file, as: [Statistics].self)?
                 .filter { $0.createdAt.addingTimeInterval(24.hours.timeInterval) > Date() }
                 .sorted { $0.createdAt > $1.createdAt } ?? []
 

+ 2 - 2
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -57,9 +57,9 @@ extension OpenAPS {
         static let tdd = "monitor/tdd.json"
         static let tdd_averages = "monitor/tdd_averages.json"
         static let alertHistory = "monitor/alerthistory.json"
-        static let dailyStats = "monitor/statistics.json"
-        static let twoDaysStats = "monitor/twoDays.json"
+        static let statistics = "monitor/statistics.json"
         static let loopStats = "monitor/loopStats.json"
+        static let glucose_data = "monitor/glucoseForStats.json"
     }
 
     enum Enact {

+ 19 - 1
FreeAPS/Sources/APS/Storage/GlucoseStorage.swift

@@ -38,7 +38,7 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             self.storage.transaction { storage in
                 storage.append(glucose, to: file, uniqBy: \.dateString)
                 let uniqEvents = storage.retrieve(file, as: [BloodGlucose].self)?
-                    .filter { $0.dateString.addingTimeInterval(49.hours.timeInterval) > Date() }
+                    .filter { $0.dateString.addingTimeInterval(24.hours.timeInterval) > Date() }
                     .sorted { $0.dateString > $1.dateString } ?? []
                 let glucose = Array(uniqEvents)
                 storage.save(glucose, as: file)
@@ -48,6 +48,24 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
                         $0.glucoseDidUpdate(glucose.reversed())
                     }
                 }
+
+                // Save to glucoseForStats also.
+                var bg_ = 0
+                var bgDate = Date()
+
+                if glucose.isNotEmpty {
+                    bg_ = glucose[0].glucose ?? 0
+                    bgDate = glucose[0].dateString
+                }
+                if bg_ != 0 {
+                    let dataForStats = GlucoseDataForStats(date: bgDate, glucose: bg_)
+                    storage.append(dataForStats, to: OpenAPS.Monitor.glucose_data, uniqBy: \.date)
+                    let uniqEvents_1 = storage.retrieve(OpenAPS.Monitor.glucose_data, as: [GlucoseDataForStats].self)?
+                        .filter { $0.date.addingTimeInterval(90.days.timeInterval) > Date() }
+                        .sorted { $0.date > $1.date } ?? []
+                    let dataForStats_ = Array(uniqEvents_1)
+                    storage.save(dataForStats_, as: OpenAPS.Monitor.glucose_data)
+                }
             }
 
             self.storage.transaction { storage in

+ 0 - 127
FreeAPS/Sources/Models/DailyStats.swift

@@ -1,127 +0,0 @@
-import Foundation
-
-struct DailyStats: JSON, Equatable {
-    var createdAt: Date
-    var iPhone: String
-    var iOS: String
-    var Build_Version: String
-    var Build_Number: String
-    var Branch: String
-    var Build_Date: Date
-    var Algorithm: String
-    var AdjustmentFactor: Decimal
-    var Pump: String
-    var CGM: String
-    var insulinType: String
-    var peakActivityTime: Decimal
-    var TDD: Decimal
-    var Carbs_24h: Decimal
-    var TIR: String
-    var BG_Average: String
-    var HbA1c: String
-    var LoopStats: [LoopCycles]
-
-    init(
-        createdAt: Date,
-        iPhone: String,
-        iOS: String,
-        Build_Version: String,
-        Build_Number: String,
-        Branch: String,
-        Build_Date: Date,
-        Algorithm: String,
-        AdjustmentFactor: Decimal,
-        Pump: String,
-        CGM: String,
-        insulinType: String,
-        peakActivityTime: Decimal,
-        TDD: Decimal,
-        Carbs_24h: Decimal,
-        TIR: String,
-        BG_Average: String,
-        HbA1c: String,
-        LoopStats: [LoopCycles]
-    ) {
-        self.createdAt = createdAt
-        self.iPhone = iPhone
-        self.iOS = iOS
-        self.Build_Version = Build_Version
-        self.Build_Number = Build_Number
-        self.Branch = Branch
-        self.Build_Date = Build_Date
-        self.Algorithm = Algorithm
-        self.AdjustmentFactor = AdjustmentFactor
-        self.Pump = Pump
-        self.CGM = CGM
-        self.insulinType = insulinType
-        self.peakActivityTime = peakActivityTime
-        self.TDD = TDD
-        self.Carbs_24h = Carbs_24h
-        self.TIR = TIR
-        self.BG_Average = BG_Average
-        self.HbA1c = HbA1c
-        self.LoopStats = LoopStats
-    }
-
-    static func == (lhs: DailyStats, rhs: DailyStats) -> Bool {
-        lhs.createdAt == rhs.createdAt
-    }
-
-    func hash(into hasher: inout Hasher) {
-        hasher.combine(createdAt)
-    }
-}
-
-extension DailyStats {
-    private enum CodingKeys: String, CodingKey {
-        case createdAt
-        case iPhone
-        case iOS
-        case Build_Version
-        case Build_Number
-        case Branch
-        case Build_Date
-        case Algorithm
-        case AdjustmentFactor
-        case Pump
-        case CGM
-        case insulinType
-        case peakActivityTime
-        case TDD
-        case Carbs_24h
-        case TIR
-        case BG_Average
-        case HbA1c
-        case LoopStats
-    }
-}
-
-struct LoopCycles: JSON, Equatable {
-    var success_rate: Decimal
-    var loops: Int
-    var errors: Int
-    var median_interval: Decimal
-    var avg_interval: Decimal
-    var min_interval: Decimal
-    var max_interval: Decimal
-    var median_duration: Decimal
-    var avg_duration: Decimal
-    var min_duration: Decimal
-    var max_duration: Decimal
-}
-
-extension LoopCycles {
-    private enum CodingKeys: String, CodingKey {
-        case success_rate
-        case loops
-        case errors
-        case median_interval
-        case avg_interval
-        case min_interval
-        case max_interval
-        case median_duration
-        case avg_duration
-        case min_duration
-        case max_duration
-    }
-}

+ 6 - 0
FreeAPS/Sources/Models/GlucoseDataForStats.swift

@@ -0,0 +1,6 @@
+import Foundation
+
+struct GlucoseDataForStats: JSON {
+    let date: Date
+    var glucose: Int
+}

+ 1 - 1
FreeAPS/Sources/Models/NightscoutStatus.swift

@@ -6,7 +6,7 @@ struct NightscoutStatus: JSON {
     let pump: NSPumpStatus
     let preferences: Preferences
     let uploader: Uploader
-    let dailystats: DailyStats?
+    let dailystats: Statistics?
 }
 
 struct OpenAPSStatus: JSON {

+ 270 - 0
FreeAPS/Sources/Models/Statistics.swift

@@ -0,0 +1,270 @@
+import Foundation
+
+struct Statistics: JSON, Equatable {
+    var createdAt: Date
+    var iPhone: String
+    var iOS: String
+    var Build_Version: String
+    var Build_Number: String
+    var Branch: String
+    var Build_Date: Date
+    var Algorithm: String
+    var AdjustmentFactor: Decimal
+    var Pump: String
+    var CGM: String
+    var insulinType: String
+    var peakActivityTime: Decimal
+    var TDD: Decimal
+    var Carbs_24h: Decimal
+    var GlucoseStorage_Days: Decimal
+    var TIR: [TIRs]
+    var Glucose: [Averages]
+    var HbA1c: [Hbs]
+    var LoopStats: [LoopCycles]
+
+    init(
+        createdAt: Date,
+        iPhone: String,
+        iOS: String,
+        Build_Version: String,
+        Build_Number: String,
+        Branch: String,
+        Build_Date: Date,
+        Algorithm: String,
+        AdjustmentFactor: Decimal,
+        Pump: String,
+        CGM: String,
+        insulinType: String,
+        peakActivityTime: Decimal,
+        TDD: Decimal,
+        Carbs_24h: Decimal,
+        GlucoseStorage_Days: Decimal,
+        TIR: [TIRs],
+        Glucose: [Averages],
+        HbA1c: [Hbs],
+        LoopStats: [LoopCycles]
+    ) {
+        self.createdAt = createdAt
+        self.iPhone = iPhone
+        self.iOS = iOS
+        self.Build_Version = Build_Version
+        self.Build_Number = Build_Number
+        self.Branch = Branch
+        self.Build_Date = Build_Date
+        self.Algorithm = Algorithm
+        self.AdjustmentFactor = AdjustmentFactor
+        self.Pump = Pump
+        self.CGM = CGM
+        self.insulinType = insulinType
+        self.peakActivityTime = peakActivityTime
+        self.TDD = TDD
+        self.Carbs_24h = Carbs_24h
+        self.GlucoseStorage_Days = GlucoseStorage_Days
+        self.TIR = TIR
+        self.Glucose = Glucose
+        self.HbA1c = HbA1c
+        self.LoopStats = LoopStats
+    }
+
+    static func == (lhs: Statistics, rhs: Statistics) -> Bool {
+        lhs.createdAt == rhs.createdAt
+    }
+
+    func hash(into hasher: inout Hasher) {
+        hasher.combine(createdAt)
+    }
+}
+
+extension Statistics {
+    private enum CodingKeys: String, CodingKey {
+        case createdAt
+        case iPhone
+        case iOS
+        case Build_Version
+        case Build_Number
+        case Branch
+        case Build_Date
+        case Algorithm
+        case AdjustmentFactor
+        case Pump
+        case CGM
+        case insulinType
+        case peakActivityTime
+        case TDD
+        case Carbs_24h
+        case GlucoseStorage_Days
+        case TIR
+        case Glucose
+        case HbA1c
+        case LoopStats
+    }
+}
+
+struct LoopCycles: JSON, Equatable {
+    var loops: Int
+    var errors: Int
+    var success_rate: Decimal
+    var avg_interval: Decimal
+    var median_interval: Decimal
+    var min_interval: Decimal
+    var max_interval: Decimal
+    var avg_duration: Decimal
+    var median_duration: Decimal
+    var min_duration: Decimal
+    var max_duration: Decimal
+}
+
+struct Averages: JSON, Equatable {
+    var Average: [Average]
+    var Median: [Median]
+}
+
+struct Average: JSON, Equatable {
+    var oneDay_mmol: Decimal
+    var oneDay: Decimal
+    var sevenDays_mmol: Decimal
+    var sevenDays: Decimal
+    var thirtyDays_mmol: Decimal
+    var thirtyDays: Decimal
+    var ninetyDays_mmol: Decimal
+    var ninetyDays: Decimal
+    var totalDays_mmol: Decimal
+    var totalDays: Decimal
+}
+
+struct Median: JSON, Equatable {
+    var oneDay_mmol: Decimal
+    var oneDay: Decimal
+    var sevenDays_mmol: Decimal
+    var sevenDays: Decimal
+    var thirtyDays_mmol: Decimal
+    var thirtyDays: Decimal
+    var ninetyDays_mmol: Decimal
+    var ninetyDays: Decimal
+    var totalDays_mmol: Decimal
+    var totalDays: Decimal
+}
+
+struct Hbs: JSON, Equatable {
+    var oneDay_mmolMol: Decimal
+    var oneDay: Decimal
+    var sevenDays_mmolMol: Decimal
+    var sevenDays: Decimal
+    var thirtyDays_mmolMol: Decimal
+    var thirtyDays: Decimal
+    var ninetyDays_mmolMol: Decimal
+    var ninetyDays: Decimal
+    var totalDays_mmolMol: Decimal
+    var totalDays: Decimal
+}
+
+struct TIRs: JSON, Equatable {
+    var TIR: [TIR]
+    var Hypos: [Hypos]
+    var Hypers: [Hypers]
+}
+
+struct TIR: JSON, Equatable {
+    var oneDay: Decimal
+    var sevenDays: Decimal
+    var thirtyDays: Decimal
+    var ninetyDays: Decimal
+    var totalDays: Decimal
+}
+
+struct Hypos: JSON, Equatable {
+    var oneDay: Decimal
+    var sevenDays: Decimal
+    var thirtyDays: Decimal
+    var ninetyDays: Decimal
+    var totalDays: Decimal
+}
+
+struct Hypers: JSON, Equatable {
+    var oneDay: Decimal
+    var sevenDays: Decimal
+    var thirtyDays: Decimal
+    var ninetyDays: Decimal
+    var totalDays: Decimal
+}
+
+extension LoopCycles {
+    private enum CodingKeys: String, CodingKey {
+        case loops
+        case errors
+        case success_rate
+        case avg_interval
+        case median_interval
+        case min_interval
+        case max_interval
+        case avg_duration
+        case median_duration
+        case min_duration
+        case max_duration
+    }
+}
+
+extension Averages {
+    private enum CodingKeys: String, CodingKey {
+        case Average
+        case Median
+    }
+}
+
+extension Median {
+    private enum CodingKeys: String, CodingKey {
+        case oneDay_mmol
+        case oneDay
+        case sevenDays_mmol
+        case sevenDays
+        case thirtyDays_mmol
+        case thirtyDays
+        case ninetyDays_mmol
+        case ninetyDays
+        case totalDays_mmol
+        case totalDays
+    }
+}
+
+extension Hbs {
+    private enum CodingKeys: String, CodingKey {
+        case oneDay_mmolMol
+        case oneDay
+        case sevenDays_mmolMol
+        case sevenDays
+        case thirtyDays_mmolMol
+        case thirtyDays
+        case ninetyDays_mmolMol
+        case ninetyDays
+        case totalDays_mmolMol
+        case totalDays
+    }
+}
+
+extension TIRs {
+    private enum CodingKeys: String, CodingKey {
+        case TIR
+        case Hypos
+        case Hypers
+    }
+}
+
+extension Hypos {
+    private enum CodingKeys: String, CodingKey {
+        case oneDay
+        case sevenDays
+        case thirtyDays
+        case ninetyDays
+        case totalDays
+    }
+}
+
+extension Hypers {
+    private enum CodingKeys: String, CodingKey {
+        case oneDay
+        case sevenDays
+        case thirtyDays
+        case ninetyDays
+        case totalDays
+    }
+}

+ 0 - 21
FreeAPS/Sources/Models/TwoDaysStats.swift

@@ -1,21 +0,0 @@
-import Foundation
-
-struct TwoDaysStats: JSON, Equatable {
-    var createdAt: Date
-    var past2daysAverage: Decimal
-
-    init(
-        createdAt: Date,
-        past2daysAverage: Decimal
-    ) {
-        self.createdAt = createdAt
-        self.past2daysAverage = past2daysAverage
-    }
-}
-
-extension TwoDaysStats {
-    private enum CodingKeys: String, CodingKey {
-        case createdAt
-        case past2daysAverage
-    }
-}

+ 5 - 1
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -81,6 +81,8 @@ extension Settings {
                                 .navigationLink(to: .configEditor(file: OpenAPS.FreeAPS.announcementsEnacted), from: self)
                             Text("Autotune")
                                 .navigationLink(to: .configEditor(file: OpenAPS.Settings.autotune), from: self)
+                            Text("Glucose")
+                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.glucose), from: self)
                         }
 
                         Group {
@@ -91,9 +93,11 @@ extension Settings {
                             Text("Middleware")
                                 .navigationLink(to: .configEditor(file: OpenAPS.Middleware.determineBasal), from: self)
                             Text("Statistics")
-                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.dailyStats), from: self)
+                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.statistics), from: self)
                             Text("Loop Cycles")
                                 .navigationLink(to: .configEditor(file: OpenAPS.Monitor.loopStats), from: self)
+                            Text("Glucose Data used for statistics")
+                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.glucose_data), from: self)
                             Text("Edit settings json")
                                 .navigationLink(to: .configEditor(file: OpenAPS.FreeAPS.settings), from: self)
                         }

+ 1 - 1
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -212,7 +212,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
 
         let uploader = Uploader(batteryVoltage: nil, battery: Int(device.batteryLevel * 100))
 
-        let dailyStats = storage.retrieve(OpenAPS.Monitor.dailyStats, as: [DailyStats].self) ?? []
+        let dailyStats = storage.retrieve(OpenAPS.Monitor.statistics, as: [Statistics].self) ?? []
 
         let status: NightscoutStatus