ソースを参照

Merge branch 'Test_new_json' into DailyStats

Jon B.M 3 年 前
コミット
51d971e1b6

+ 2 - 1
Config.xcconfig

@@ -1,6 +1,7 @@
 APP_DISPLAY_NAME = FreeAPS X
-APP_VERSION = 0.2.8
+APP_VERSION = 0.2.9
 APP_BUILD_NUMBER = 1
+BRANCH = Test_new_json
 DEVELOPER_TEAM = ##TEAM_ID##
 BUNDLE_IDENTIFIER = ru.artpancreas.$(DEVELOPMENT_TEAM).FreeAPS
 APP_GROUP_ID = group.com.$(DEVELOPMENT_TEAM).loopkit.LoopGroup

+ 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 */,

+ 2 - 0
FreeAPS/Resources/Info.plist

@@ -67,6 +67,8 @@
 	<string>Health App is used to store blood glucose data</string>
 	<key>NSHealthUpdateUsageDescription</key>
 	<string>Health App is used to store blood glucose data</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>$(BRANCH)</string>
 	<key>UIApplicationSceneManifest</key>
 	<dict>
 		<key>UIApplicationSupportsMultipleScenes</key>

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

@@ -641,81 +641,260 @@ 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
+            }
+        }
 
-                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)
+        var carbTotal: Decimal = 0
+
+        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 branch = Bundle.main.infoDictionary?["NSHumanReadableCopyright"] 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_,
+            FAX_Build_Version: version,
+            FAX_Build_Number: build ?? "1",
+            FAX_Branch: branch ?? "N/A",
+            FAX_Build_Date: buildDate,
+            Algorithm: algo_,
+            AdjustmentFactor: af,
+            Pump: pump_,
+            CGM: cgm.rawValue,
+            insulinType: insulin_type.rawValue,
+            peakActivityTime: iPa,
+            TDD: currentTDD ?? 0,
+            Carbs_24h: carbTotal,
+            Hypoglucemias_Percentage: tir().hypos,
+            TIR_Percentage: tir().TIR,
+            Hyperglucemias_Percentage: tir().hypers,
+            BG_daily_Average_mg_dl: bgAvg,
+            id: UUID().uuidString
+        )
+
+        var newEntries: [DailyStats] = []
+
+        // If file is empty
+        let file_3 = loadFileFromStorage(name: OpenAPS.Monitor.dailyStats)
+        var isJSONempty = false
+        if file_3.rawJSON.isEmpty {
+            print("Empty")
+            isJSONempty = true
+        }
+
+        let now = Date()
+        let calender = Calendar.current
+
+        // If current local time is 23:41 or later
+        if calender.component(.hour, from: now) > 22,
+           calender.component(.minute, from: now) > 40
+        {
+            if isJSONempty {
+                storage.save(dailystat, as: file)
+            } else {
+                storage.transaction { storage in
+                    storage.append(dailystat, to: file, uniqBy: \.id)
+                    newEntries = storage.retrieve(file, as: [DailyStats].self)?
+                        .sorted { $0.date > $1.date } ?? []
+                        .filter { $0.date.addingTimeInterval(1.days.timeInterval) < now }
+                    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 {

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

@@ -0,0 +1,85 @@
+import Foundation
+
+struct DailyStats: JSON, Equatable {
+    var date: Date
+    var FAX_Build_Version: String
+    var FAX_Build_Number: String
+    var FAX_Branch: String
+    var FAX_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 Hypoglucemias_Percentage: Decimal
+    var TIR_Percentage: Decimal
+    var Hyperglucemias_Percentage: Decimal
+    var BG_daily_Average_mg_dl: Decimal
+    var id: String
+
+    init(
+        date: Date,
+        FAX_Build_Version: String,
+        FAX_Build_Number: String,
+        FAX_Branch: String,
+        FAX_Build_Date: Date,
+        Algorithm: String,
+        AdjustmentFactor: Decimal,
+        Pump: String,
+        CGM: String,
+        insulinType: String,
+        peakActivityTime: Decimal,
+        TDD: Decimal,
+        Carbs_24h: Decimal,
+        Hypoglucemias_Percentage: Decimal,
+        TIR_Percentage: Decimal,
+        Hyperglucemias_Percentage: Decimal,
+        BG_daily_Average_mg_dl: Decimal,
+        id: String
+    ) {
+        self.date = date
+        self.FAX_Build_Version = FAX_Build_Version
+        self.FAX_Build_Number = FAX_Build_Number
+        self.FAX_Branch = FAX_Branch
+        self.FAX_Build_Date = FAX_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.Hypoglucemias_Percentage = Hypoglucemias_Percentage
+        self.TIR_Percentage = TIR_Percentage
+        self.Hyperglucemias_Percentage = Hyperglucemias_Percentage
+        self.BG_daily_Average_mg_dl = BG_daily_Average_mg_dl
+        self.id = id
+    }
+}
+
+extension DailyStats {
+    private enum CodingKeys: String, CodingKey {
+        case date
+        case FAX_Build_Version
+        case FAX_Build_Number
+        case FAX_Branch
+        case FAX_Build_Date
+        case Algorithm
+        case AdjustmentFactor
+        case Pump
+        case CGM
+        case insulinType
+        case peakActivityTime
+        case TDD
+        case Carbs_24h
+        case Hypoglucemias_Percentage
+        case TIR_Percentage
+        case Hyperglucemias_Percentage
+        case BG_daily_Average_mg_dl
+        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)
                         }