diff --git a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings index ed2e9071cc..5f06348bc5 100644 --- a/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings +++ b/FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings @@ -2476,6 +2476,21 @@ Enact a temp Basal or a temp target */ /* Threshold Table Columns Title */ "Threshold" = "Threshold"; +/* Dynamic settings View */ +"Averages" = "Averages"; + +/* Dynamic settings View */ +"Average ISF" = "Average ISF"; + +/* Dynamic settings View */ +"Average CR" = "Average CR"; + +/* Dynamic settings View */ +"Average CSF" = "Average CSF"; + +/* Dynamic settings View */ +"ISF: Insulin Sensitivity, CR: Carb Ratio,\nCSF: Carb Sensitivity = ISF/CR" = "ISF: Insulin Sensitivity, CR: Carb Ratio,\nCSF: Carb Sensitivity = ISF/CR"; + /* Header */ "Calculator settings" = "Calculator settings"; diff --git a/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift b/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift index 6a4f30e6fd..232b221a65 100644 --- a/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift +++ b/FreeAPS/Sources/Modules/Dynamic/DynamicStateModel.swift @@ -13,6 +13,7 @@ extension Dynamic { @Published var tddAdjBasal: Bool = false @Published var threshold_setting: Decimal = 65 @Published var unit: GlucoseUnits = .mmolL + @Published var averages: (isf: Double, cr: Double, days: Double)? var preferences: Preferences { settingsManager.preferences @@ -26,6 +27,7 @@ extension Dynamic { adjustmentFactor = settings.preferences.adjustmentFactor weightPercentage = settings.preferences.weightPercentage tddAdjBasal = settings.preferences.tddAdjBasal + averages = thirtyDaysAverages() if unit == .mmolL { threshold_setting = settings.preferences.threshold_setting.asMmolL @@ -65,5 +67,33 @@ extension Dynamic { storage.save(newSettings, as: OpenAPS.Settings.preferences) } } + + var reasons: [Reasons] { + CoreDataStorage().fetchReasons(interval: DateFilter().month) + } + + private var sameUnit: Bool { + unit == .mmolL + } + + private func thirtyDaysAverages() -> (isf: Double, cr: Double, days: Double)? { + let history = reasons.filter({ $0.mmol == sameUnit }).sorted(by: { $0.date ?? Date() > $1.date ?? Date() }) + let days = -1 * (history.last?.date ?? .now).timeIntervalSince(history.first?.date ?? .now) / 8.64E4 + // Avoid displaying "0 days" + guard !history.isEmpty, days >= 0.06 else { return nil } + + let isf = history.compactMap(\.isf) + let cr = history.compactMap(\.cr) + let totalISF = isf.reduce(0, { x, y in + x + (y as Decimal) + }) + let totalCR = cr.reduce(0, { x, y in + x + (y as Decimal) + }) + let averageCR = Double(totalCR) / Double(cr.count) + let averageISF = Double(totalISF) / Double(isf.count) + + return (averageISF, averageCR, days) + } } } diff --git a/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift b/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift index 2475645f4f..b4da1b9bf0 100644 --- a/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift +++ b/FreeAPS/Sources/Modules/Dynamic/View/DynamicRootView.swift @@ -39,6 +39,13 @@ extension Dynamic { return formatter } + private var daysFormatter: NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = 1 + return formatter + } + var body: some View { Form { Section { @@ -161,6 +168,49 @@ extension Dynamic { Text(state.unit.rawValue) } } header: { Text("Safety") } + + if let averages = state.averages { + Section { + HStack { + Text("Average ISF") + Spacer() + Text( + glucoseFormatter + .string(from: averages.isf as NSNumber) ?? "" + ) + Text(state.unit.rawValue + NSLocalizedString("/U", comment: "")).foregroundColor(.secondary) + } + + HStack { + Text("Average CR") + Spacer() + Text( + glucoseFormatter + .string(from: averages.cr as NSNumber) ?? "" + ) + Text("g/U").foregroundColor(.secondary) + } + + HStack { + Text("Average CSF") + Spacer() + Text( + glucoseFormatter + .string(from: (Double(averages.isf) / Double(averages.cr)) as NSNumber) ?? "" + ) + Text(state.unit.rawValue + "/g").foregroundColor(.secondary) + } + } header: { + HStack(spacing: 0) { + Text("Averages") + Text( + " (" + (daysFormatter.string(from: averages.days as NSNumber) ?? "") + " " + + NSLocalizedString("days", comment: " days of data") + ")" + ) + } + } + footer: { Text("ISF: Insulin Sensitivity, CR: Carb Ratio,\nCSF: Carb Sensitivity = ISF/CR") } + } } .blur(radius: isPresented ? 5 : 0) .description(isPresented: isPresented, alignment: .center) {