Explorar o código

Create dailyStat.JSON.

Version. DaílyStats

New names. Daily Stats. TIR works.

New variable names etc

Use type Integer for rounding of TIR etc.

Add Build Number and fix one new entry in DailyStats array every day.

1 day between entries

Refactor

Rounding
Jon Mårtensson %!s(int64=3) %!d(string=hai) anos
pai
achega
fc8c273cd9

+ 1 - 1
Config.xcconfig

@@ -1,5 +1,5 @@
 APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 0.2.8
+APP_VERSION = 0.2.9
 APP_BUILD_NUMBER = 1
 DEVELOPER_TEAM = ##TEAM_ID##
 BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS

+ 4 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -18,6 +18,7 @@
 		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 */; };
 		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 */; };
@@ -456,6 +457,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>"; };
 		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>"; };
@@ -1327,6 +1329,7 @@
 				19F79FA8283AE7E000646323 /* TDD.swift */,
 				1935363F28496F7D001E0B16 /* TDD_averages.swift */,
 				CE82E02628E869DF00473A9C /* AlertEntry.swift */,
+				19B0EF2028F6D66200069496 /* DailyStats.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
@@ -2168,6 +2171,7 @@
 				3811DE3025C9D49500A708ED /* HomeStateModel.swift in Sources */,
 				38BF021725E7CBBC00579895 /* PumpManagerExtensions.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
+				19B0EF2128F6D66200069496 /* DailyStats.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */,
 				38A0364225ED069400FCBB52 /* TempBasal.swift in Sources */,

+ 233 - 60
FreeAPS/Sources/APS/APSManager.swift

@@ -641,81 +641,254 @@ final class BaseAPSManager: APSManager, Injectable {
 
             storage.save(enacted, as: OpenAPS.Enact.enacted)
 
-            // Add to tdd.json:
-            //
-            let preferences = settingsManager.preferences
-            let currentTDD = enacted.tdd ?? 0
-            let file = OpenAPS.Monitor.tdd
-            let tdd = TDD(
-                TDD: currentTDD,
-                timestamp: Date(),
-                id: UUID().uuidString
-            )
-            var uniqEvents: [TDD] = []
-            storage.transaction { storage in
-                storage.append(tdd, to: file, uniqBy: \.id)
-                uniqEvents = storage.retrieve(file, as: [TDD].self)?
-                    .filter { $0.timestamp.addingTimeInterval(14.days.timeInterval) > Date() }
-                    .sorted { $0.timestamp > $1.timestamp } ?? []
-
-                var total: Decimal = 0
-                var indeces: Decimal = 0
-
-                for uniqEvent in uniqEvents {
-                    if uniqEvent.TDD > 0 {
-                        total += uniqEvent.TDD
-                        indeces += 1
-                    }
-                }
+            // Create a tdd.json
+            tdd(enacted_: enacted)
 
-                let entriesPast2hours = storage.retrieve(file, as: [TDD].self)?
-                    .filter { $0.timestamp.addingTimeInterval(2.hours.timeInterval) > Date() }
-                    .sorted { $0.timestamp > $1.timestamp } ?? []
+            // Create a dailyStats.json
+            dailyStats()
 
-                var totalAmount: Decimal = 0
-                var nrOfIndeces: Decimal = 0
-
-                for entry in entriesPast2hours {
-                    if entry.TDD > 0 {
-                        totalAmount += entry.TDD
-                        nrOfIndeces += 1
-                    }
+            debug(.apsManager, "Suggestion enacted. Received: \(received)")
+            DispatchQueue.main.async {
+                self.broadcaster.notify(EnactedSuggestionObserver.self, on: .main) {
+                    $0.enactedSuggestionDidUpdate(enacted)
                 }
+            }
+            nightscout.uploadStatus()
+        }
+    }
 
-                if indeces == 0 {
-                    indeces = 1
+    func tdd(enacted_: Suggestion) {
+        // Add to tdd.json:
+        let preferences = settingsManager.preferences
+        let currentTDD = enacted_.tdd ?? 0
+        let file = OpenAPS.Monitor.tdd
+        let tdd = TDD(
+            TDD: currentTDD,
+            timestamp: Date(),
+            id: UUID().uuidString
+        )
+        var uniqEvents: [TDD] = []
+        storage.transaction { storage in
+            storage.append(tdd, to: file, uniqBy: \.id)
+            uniqEvents = storage.retrieve(file, as: [TDD].self)?
+                .filter { $0.timestamp.addingTimeInterval(14.days.timeInterval) > Date() }
+                .sorted { $0.timestamp > $1.timestamp } ?? []
+            var total: Decimal = 0
+            var indeces: Decimal = 0
+            for uniqEvent in uniqEvents {
+                if uniqEvent.TDD > 0 {
+                    total += uniqEvent.TDD
+                    indeces += 1
                 }
-
-                if nrOfIndeces == 0 {
-                    nrOfIndeces = 1
+            }
+            let entriesPast2hours = storage.retrieve(file, as: [TDD].self)?
+                .filter { $0.timestamp.addingTimeInterval(2.hours.timeInterval) > Date() }
+                .sorted { $0.timestamp > $1.timestamp } ?? []
+            var totalAmount: Decimal = 0
+            var nrOfIndeces: Decimal = 0
+            for entry in entriesPast2hours {
+                if entry.TDD > 0 {
+                    totalAmount += entry.TDD
+                    nrOfIndeces += 1
                 }
+            }
+            if indeces == 0 {
+                indeces = 1
+            }
+            if nrOfIndeces == 0 {
+                nrOfIndeces = 1
+            }
+            let average14 = total / indeces
+            let average2hours = totalAmount / nrOfIndeces
+            let weight = preferences.weightPercentage
+            let weighted_average = weight * average2hours + (1 - weight) * average14
+            let averages = TDD_averages(
+                average_total_data: average14,
+                weightedAverage: weighted_average,
+                past2hoursAverage: average2hours,
+                date: Date()
+            )
+            storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
+            storage.save(Array(uniqEvents), as: file)
+        }
+    }
 
-                let average14 = total / indeces
-                let average2hours = totalAmount / nrOfIndeces
-                let weight = preferences.weightPercentage
-                let weighted_average = weight * average2hours + (1 - weight) * average14
+    func dailyStats() {
+        // Add to dailyStats.JSON
+        let preferences = settingsManager.preferences
+        let glucose = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)
+        let carbs = storage.retrieve(OpenAPS.Monitor.carbHistory, as: [CarbsEntry].self)
+        let tdds = storage.retrieve(OpenAPS.Monitor.tdd, as: [TDD].self)
+        let currentTDD = tdds?[0].TDD
+        var bg: Decimal = 0
+        var nr_bgs: Decimal = 0
+
+        for entry in glucose! {
+            if entry.glucose! > 0 {
+                bg += Decimal(entry.glucose!)
+                nr_bgs += 1
+            }
+        }
+
+        var carbTotal: Decimal = 0
 
-                let averages = TDD_averages(
-                    average_total_data: average14,
-                    weightedAverage: weighted_average,
-                    past2hoursAverage: average2hours,
-                    date: Date()
-                )
-                storage.save(averages, as: OpenAPS.Monitor.tdd_averages)
-                storage.save(Array(uniqEvents), as: file)
+        for each in carbs! {
+            if each.carbs != 0 {
+                carbTotal += each.carbs
             }
-            // End of tdd.json
+        }
 
-            debug(.apsManager, "Suggestion enacted. Received: \(received)")
-            DispatchQueue.main.async {
-                self.broadcaster.notify(EnactedSuggestionObserver.self, on: .main) {
-                    $0.enactedSuggestionDidUpdate(enacted)
+        var bgAvg = bg / nr_bgs
+        // Round to two decimals
+        bgAvg = Decimal(round(Double(bgAvg)))
+
+        var algo_ = "oref0"
+        if preferences.enableChris, preferences.useNewFormula {
+            algo_ = "Dynamic ISF, Logarithmic Formula"
+        } else if !preferences.useNewFormula, preferences.enableChris {
+            algo_ = "Dynamic ISF, Original Formula"
+        }
+
+        let af = preferences.adjustmentFactor
+        let insulin_type = preferences.curve
+
+        var buildDate: Date {
+            if let infoPath = Bundle.main.path(forResource: "Info", ofType: "plist"),
+               let infoAttr = try? FileManager.default.attributesOfItem(atPath: infoPath),
+               let infoDate = infoAttr[.modificationDate] as? Date
+            {
+                return infoDate
+            }
+            return Date()
+        }
+
+        let nsObject: AnyObject? = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as AnyObject
+        let version = nsObject as! String
+
+        let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
+
+        let pump_ = pumpManager?.localizedTitle ?? ""
+        let cgm = settingsManager.settings.cgm
+        let file = OpenAPS.Monitor.dailyStats
+        let date_ = Date()
+        var iPa: Decimal = 75
+        if preferences.useCustomPeakTime {
+            iPa = preferences.insulinPeakTime
+        } else if preferences.curve.rawValue == "rapid-acting" {
+            iPa = 65
+        } else if preferences.curve.rawValue == "ultra-rapid" {
+            iPa = 50
+        }
+
+        let dailystat = DailyStats(
+            date: date_,
+            Pump: pump_,
+            CGM: cgm.rawValue,
+            TIR_Percentage: tir().TIR,
+            Hypoglucemias_Percentage: tir().hypos,
+            Hyperglucemias_Percentage: tir().hypers,
+            BG_daily_Average: bgAvg,
+            TDD: currentTDD ?? 0,
+            Carbs_24h: carbTotal,
+            Algorithm: algo_,
+            AdjustmentFactor: af,
+            insulinType: insulin_type.rawValue,
+            peakActivityTime: iPa,
+            FAX_Build_Version: version,
+            FAX_Build_Number: build ?? "1",
+            FAX_Build_Date: buildDate,
+            id: UUID().uuidString
+        )
+
+        let file_2 = storage.retrieve(OpenAPS.Monitor.dailyStats, as: [DailyStats].self)
+        let file_3 = loadFileFromStorage(name: OpenAPS.Monitor.dailyStats)
+
+        // If empty daily_stats.json, create a first entry
+        if file_3.isEmpty, Calendar.current.component(.hour, from: Date()) == 23,
+           Calendar.current.component(.minute, from: Date()) > 45
+        {
+            storage.save(dailystat, as: file)
+        } else {
+            var newEntries: [DailyStats] = []
+            let isDateOldEnough = file_2?[0].date ?? Date("2999-10-24T14:29:23.893Z")
+
+            if Date() > isDateOldEnough!.addingTimeInterval(1.days.timeInterval),
+               Calendar.current.component(.hour, from: Date()) == 23, Calendar.current.component(.minute, from: Date()) > 45
+            {
+                storage.transaction { storage in
+                    storage.append(dailystat, to: file, uniqBy: \.id)
+                    newEntries = storage.retrieve(file, as: [DailyStats].self)?
+                        .sorted { $0.date > $1.date } ?? []
+                    storage.save(Array(newEntries), as: file)
                 }
             }
-            nightscout.uploadStatus()
         }
     }
 
+    // Time In Range (%)
+    func tir() -> (hypos: Decimal, hypers: Decimal, TIR: Decimal) {
+        let glucose = storage.retrieve(OpenAPS.Monitor.glucose, as: [BloodGlucose].self)
+        let length_ = glucose!.count
+        let endIndex = length_ - 1
+
+        let fullTime = glucose![0].date - glucose![endIndex].date
+
+        // If empty json
+        guard fullTime != 0 else {
+            return (0, 0, 0)
+        }
+
+        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
+        }
+
+        // round to 1 decimal
+        let hypoRounded = round(Double(hypos) * 10) / 10
+        let hyperRounded = round(Double(hypers) * 10) / 10
+        let TIRrounded = round((100 - (hypoRounded + hyperRounded)) * 10) / 10
+
+        print("TIR: \(TIRrounded) %, Hypos: \(hypoRounded) %, Hypers: \(hyperRounded) %")
+
+        return (Decimal(hypoRounded), Decimal(hyperRounded), Decimal(TIRrounded))
+    }
+
+    private func loadFileFromStorage(name: String) -> RawJSON {
+        storage.retrieveRaw(name) ?? OpenAPS.defaults(for: name)
+    }
+
     private func processError(_ error: Error) {
         warning(.apsManager, "\(error.localizedDescription)")
         lastError.send(error)

+ 1 - 0
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -57,6 +57,7 @@ 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/daily_stats.json"
     }
 
     enum Enact {

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

@@ -0,0 +1,81 @@
+import Foundation
+
+struct DailyStats: JSON, Equatable {
+    var date: Date
+    var Pump: String
+    var CGM: String
+    var TIR_Percentage: Decimal
+    var Hypoglucemias_Percentage: Decimal
+    var Hyperglucemias_Percentage: Decimal
+    var BG_daily_Average: Decimal
+    var TDD: Decimal
+    var Carbs_24h: Decimal
+    var Algorithm: String
+    var AdjustmentFactor: Decimal
+    var insulinType: String
+    var peakActivityTime: Decimal
+    var FAX_Build_Version: String
+    var FAX_Build_Number: String
+    var FAX_Build_Date: Date
+    var id: String
+
+    init(
+        date: Date,
+        Pump: String,
+        CGM: String,
+        TIR_Percentage: Decimal,
+        Hypoglucemias_Percentage: Decimal,
+        Hyperglucemias_Percentage: Decimal,
+        BG_daily_Average: Decimal,
+        TDD: Decimal,
+        Carbs_24h: Decimal,
+        Algorithm: String,
+        AdjustmentFactor: Decimal,
+        insulinType: String,
+        peakActivityTime: Decimal,
+        FAX_Build_Version: String,
+        FAX_Build_Number: String,
+        FAX_Build_Date: Date,
+        id: String
+    ) {
+        self.date = date
+        self.Pump = Pump
+        self.CGM = CGM
+        self.TIR_Percentage = TIR_Percentage
+        self.Hypoglucemias_Percentage = Hypoglucemias_Percentage
+        self.Hyperglucemias_Percentage = Hyperglucemias_Percentage
+        self.BG_daily_Average = BG_daily_Average
+        self.TDD = TDD
+        self.Carbs_24h = Carbs_24h
+        self.Algorithm = Algorithm
+        self.AdjustmentFactor = AdjustmentFactor
+        self.insulinType = insulinType
+        self.peakActivityTime = peakActivityTime
+        self.FAX_Build_Version = FAX_Build_Version
+        self.FAX_Build_Number = FAX_Build_Number
+        self.FAX_Build_Date = FAX_Build_Date
+        self.id = id
+    }
+}
+
+extension DailyStats {
+    private enum CodingKeys: String, CodingKey {
+        case date
+        case Pump
+        case CGM
+        case TIR_Percentage
+        case Hypoglucemias_Percentage
+        case Hyperglucemias_Percentage
+        case BG_daily_Average
+        case TDD
+        case Carbs_24h
+        case Algorithm
+        case AdjustmentFactor
+        case insulinType
+        case peakActivityTime
+        case FAX_Build_Version
+        case FAX_Build_Number
+        case FAX_Build_Date
+        case id
+    }
+}

+ 2 - 0
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -114,6 +114,8 @@ extension Settings {
                                 .navigationLink(to: .configEditor(file: OpenAPS.Monitor.tdd), from: self)
                             Text("TDD Averages")
                                 .navigationLink(to: .configEditor(file: OpenAPS.Monitor.tdd_averages), from: self)
+                            Text("Daily Statistics")
+                                .navigationLink(to: .configEditor(file: OpenAPS.Monitor.dailyStats), from: self)
                             Text("Edit settings json")
                                 .navigationLink(to: .configEditor(file: OpenAPS.FreeAPS.settings), from: self)
                         }