polscm32 2 лет назад
Родитель
Сommit
86510894be

+ 8 - 32
FreeAPS.xcodeproj/project.pbxproj

@@ -59,12 +59,10 @@
 		1BBB001DAD60F3B8CEA4B1C7 /* ISFEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */; };
 		1D845DF2E3324130E1D95E67 /* DataTableProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60744C3E9BB3652895C908CC /* DataTableProvider.swift */; };
 		23888883D4EA091C88480FF2 /* BolusProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19984D62EFC0035A9E9644D /* BolusProvider.swift */; };
-		28089E07169488CF6DCC2A31 /* AddCarbsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86FC1CFD647CF34508AF9A3B /* AddCarbsRootView.swift */; };
 		2BE9A6FA20875F6F4F9CD461 /* PumpSettingsEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97F14812C1AFED3621165A5 /* PumpSettingsEditorProvider.swift */; };
 		3083261C4B268E353F36CD0B /* AutotuneConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DCCCCE633F5E98E41B0CD3C /* AutotuneConfigDataFlow.swift */; };
 		3171D2818C7C72CD1584BB5E /* NotificationsConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC2C6489D29ECCCAD78E0721 /* NotificationsConfigStateModel.swift */; };
 		320D030F724170A637F06D50 /* CalibrationsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212E8BFE6D66EE65AA26A114 /* CalibrationsProvider.swift */; };
-		33E198D3039045D98C3DC5D4 /* AddCarbsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39E7C997E56DAF8D28D09014 /* AddCarbsStateModel.swift */; };
 		3811DE0B25C9D32F00A708ED /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE0725C9D32E00A708ED /* BaseView.swift */; };
 		3811DE0C25C9D32F00A708ED /* BaseProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3811DE0825C9D32F00A708ED /* BaseProvider.swift */; };
 		3811DE1025C9D37700A708ED /* Swinject in Frameworks */ = {isa = PBXBuildFile; productRef = 3811DE0F25C9D37700A708ED /* Swinject */; };
@@ -271,6 +269,8 @@
 		58237D9E2BCF0A6B00A47A79 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58237D9D2BCF0A6B00A47A79 /* PopupView.swift */; };
 		583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684052BD178DB00070A60 /* GlucoseStored+helper.swift */; };
 		583684082BD195A700070A60 /* Determination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 583684072BD195A700070A60 /* Determination.swift */; };
+		5837A5302BD2E3C700A5DC04 /* MealsStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A52F2BD2E3C700A5DC04 /* MealsStored+helper.swift */; };
+		5837A5322BD2E81100A5DC04 /* InsulinStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */; };
 		587DA1F62B77F3DD00B28F8A /* SettingsRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */; };
 		58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F107732BD1A4D000B1A680 /* Determination+helper.swift */; };
 		5BFA1C2208114643B77F8CEB /* AddTempTargetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE53A13D26F101B332EFFC8 /* AddTempTargetProvider.swift */; };
@@ -313,7 +313,6 @@
 		A0B8EC8CC5CD1DD237D1BCD2 /* PumpSettingsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C7F882606FF83A21BE00D8 /* PumpSettingsEditorRootView.swift */; };
 		A228DF96647338139F152B15 /* PreferencesEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12204445D7632AF09264A979 /* PreferencesEditorDataFlow.swift */; };
 		A33352ED40476125EBAC6EE0 /* CREditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E22146D3DF4853786C78132 /* CREditorDataFlow.swift */; };
-		A6F097A14CAAE0CE0D11BE1B /* AddCarbsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618E62C9757B2F95431B5DC0 /* AddCarbsProvider.swift */; };
 		AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */; };
 		B7C465E9472624D8A2BE2A6A /* CalibrationsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA241FB1663EC96FDBE64C8A /* CalibrationsDataFlow.swift */; };
 		B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */ = {isa = PBXBuildFile; fileRef = B9CAAEFB2AE70836000F68BC /* branch.txt */; };
@@ -416,7 +415,6 @@
 		E39E418C56A5A46B61D960EE /* ConfigEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */; };
 		E3A08AAE59538BC8A8ABE477 /* NotificationsConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3260468377DA9DB4DEE9AF6D /* NotificationsConfigDataFlow.swift */; };
 		E4984C5262A90469788754BB /* PreferencesEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F8BA8533F56BC55748CA877 /* PreferencesEditorProvider.swift */; };
-		E97285ED9B814CD5253C6658 /* AddCarbsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F48C3AC770D4CCD0EA2B0C2 /* AddCarbsDataFlow.swift */; };
 		E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD22C985B79A2F0D2EA3D9D /* PumpConfigRootView.swift */; };
 		F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */; };
 		F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */; };
@@ -822,7 +820,6 @@
 		38FEF3FD2738083E00574A46 /* CGMProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMProvider.swift; sourceTree = "<group>"; };
 		38FEF407273B011A00574A46 /* LibreTransmitterSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibreTransmitterSource.swift; sourceTree = "<group>"; };
 		38FEF412273B317A00574A46 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = "<group>"; };
-		39E7C997E56DAF8D28D09014 /* AddCarbsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddCarbsStateModel.swift; sourceTree = "<group>"; };
 		3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorProvider.swift; sourceTree = "<group>"; };
 		3BF768BD6264FF7D71D66767 /* NightscoutConfigProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigProvider.swift; sourceTree = "<group>"; };
 		3F60E97100041040446F44E7 /* PumpConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpConfigStateModel.swift; sourceTree = "<group>"; };
@@ -838,14 +835,14 @@
 		58237D9D2BCF0A6B00A47A79 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = "<group>"; };
 		583684052BD178DB00070A60 /* GlucoseStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+helper.swift"; sourceTree = "<group>"; };
 		583684072BD195A700070A60 /* Determination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Determination.swift; sourceTree = "<group>"; };
+		5837A52F2BD2E3C700A5DC04 /* MealsStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealsStored+helper.swift"; sourceTree = "<group>"; };
+		5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinStored+helper.swift"; sourceTree = "<group>"; };
 		587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowView.swift; sourceTree = "<group>"; };
 		58F107732BD1A4D000B1A680 /* Determination+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Determination+helper.swift"; sourceTree = "<group>"; };
 		5B8A42073A2D03A278914448 /* AddTempTargetDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddTempTargetDataFlow.swift; sourceTree = "<group>"; };
 		5C018D1680307A31C9ED7120 /* CGMStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMStateModel.swift; sourceTree = "<group>"; };
 		5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorStateModel.swift; sourceTree = "<group>"; };
-		5F48C3AC770D4CCD0EA2B0C2 /* AddCarbsDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddCarbsDataFlow.swift; sourceTree = "<group>"; };
 		60744C3E9BB3652895C908CC /* DataTableProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableProvider.swift; sourceTree = "<group>"; };
-		618E62C9757B2F95431B5DC0 /* AddCarbsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddCarbsProvider.swift; sourceTree = "<group>"; };
 		64AA5E04A2761F6EEA6568E1 /* CREditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CREditorStateModel.swift; sourceTree = "<group>"; };
 		66A5B83E7967C38F7CBD883C /* LibreConfigDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LibreConfigDataFlow.swift; sourceTree = "<group>"; };
 		67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorDataFlow.swift; sourceTree = "<group>"; };
@@ -864,7 +861,6 @@
 		72778B68C3004F71F6E79BDC /* PumpSettingsEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpSettingsEditorStateModel.swift; sourceTree = "<group>"; };
 		79BDA519C9B890FD9A5DFCF3 /* ISFEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorDataFlow.swift; sourceTree = "<group>"; };
 		7E22146D3DF4853786C78132 /* CREditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CREditorDataFlow.swift; sourceTree = "<group>"; };
-		86FC1CFD647CF34508AF9A3B /* AddCarbsRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddCarbsRootView.swift; sourceTree = "<group>"; };
 		8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigRootView.swift; sourceTree = "<group>"; };
 		881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableRootView.swift; sourceTree = "<group>"; };
 		8A965332F237348B119FB858 /* PreferencesEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PreferencesEditorRootView.swift; sourceTree = "<group>"; };
@@ -1272,7 +1268,6 @@
 				19E1F7E629D0828B005C8D20 /* IconConfig */,
 				19D466A129AA2B0A004D5F33 /* FPUConfig */,
 				F90692CD274B99850037068D /* HealthKit */,
-				6DC5D590658EF8B8DF94F9F5 /* AddCarbs */,
 				A9A4C88374496B3C89058A89 /* AddTempTarget */,
 				672F63EEAE27400625E14BAD /* AutotuneConfig */,
 				A42F1FEDFFD0DDE00AAD54D3 /* BasalProfileEditor */,
@@ -2016,14 +2011,6 @@
 			path = ManualTempBasal;
 			sourceTree = "<group>";
 		};
-		50E85421406582CF9D321A20 /* View */ = {
-			isa = PBXGroup;
-			children = (
-				86FC1CFD647CF34508AF9A3B /* AddCarbsRootView.swift */,
-			);
-			path = View;
-			sourceTree = "<group>";
-		};
 		510CCF29FD3216C5BBC49A15 /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -2059,6 +2046,8 @@
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
+				5837A52F2BD2E3C700A5DC04 /* MealsStored+helper.swift */,
+				5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */,
 			);
 			path = Model;
 			sourceTree = "<group>";
@@ -2114,17 +2103,6 @@
 			path = LiveActivity;
 			sourceTree = "<group>";
 		};
-		6DC5D590658EF8B8DF94F9F5 /* AddCarbs */ = {
-			isa = PBXGroup;
-			children = (
-				5F48C3AC770D4CCD0EA2B0C2 /* AddCarbsDataFlow.swift */,
-				618E62C9757B2F95431B5DC0 /* AddCarbsProvider.swift */,
-				39E7C997E56DAF8D28D09014 /* AddCarbsStateModel.swift */,
-				50E85421406582CF9D321A20 /* View */,
-			);
-			path = AddCarbs;
-			sourceTree = "<group>";
-		};
 		833DA2F9E47E64E305F92F9D /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -2797,6 +2775,7 @@
 				388E5A5C25B6F0770019842D /* JSON.swift in Sources */,
 				3811DF0225CA9FEA00A708ED /* Credentials.swift in Sources */,
 				19DC678529CA67A400FD9EC4 /* OverrideProfilesRootView.swift in Sources */,
+				5837A5302BD2E3C700A5DC04 /* MealsStored+helper.swift in Sources */,
 				389A572026079BAA00BC102F /* Interpolation.swift in Sources */,
 				19A910382A24EF3200C8951B /* ChartsView.swift in Sources */,
 				38B4F3C625E5017E00E76A18 /* NotificationCenter.swift in Sources */,
@@ -2980,14 +2959,10 @@
 				DD399FB31EACB9343C944C4C /* PreferencesEditorStateModel.swift in Sources */,
 				19E1F7EA29D082ED005C8D20 /* IconConfigProvider.swift in Sources */,
 				44190F0BBA464D74B857D1FB /* PreferencesEditorRootView.swift in Sources */,
-				E97285ED9B814CD5253C6658 /* AddCarbsDataFlow.swift in Sources */,
 				CE48C86428CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift in Sources */,
 				38E8755427561E9800975559 /* DataFlow.swift in Sources */,
 				38E44522274E3DDC00EC9A94 /* NetworkReachabilityManager.swift in Sources */,
 				CE7CA34F2A064973004BE681 /* BaseIntentsRequest.swift in Sources */,
-				A6F097A14CAAE0CE0D11BE1B /* AddCarbsProvider.swift in Sources */,
-				33E198D3039045D98C3DC5D4 /* AddCarbsStateModel.swift in Sources */,
-				28089E07169488CF6DCC2A31 /* AddCarbsRootView.swift in Sources */,
 				D2165E9D78EFF692C1DED1C6 /* AddTempTargetDataFlow.swift in Sources */,
 				CE82E02728E869DF00473A9C /* AlertEntry.swift in Sources */,
 				38E4451E274DB04600EC9A94 /* AppDelegate.swift in Sources */,
@@ -3009,6 +2984,7 @@
 				F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */,
 				3862CC1F273FDC9200BF832C /* CalibrationsChart.swift in Sources */,
 				19E1F7EC29D082FE005C8D20 /* IconConfigStateModel.swift in Sources */,
+				5837A5322BD2E81100A5DC04 /* InsulinStored+helper.swift in Sources */,
 				711C0CB42CAABE788916BC9D /* ManualTempBasalDataFlow.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
 				583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */,

+ 20 - 39
FreeAPS/Sources/APS/APSManager.swift

@@ -82,6 +82,7 @@ final class BaseAPSManager: APSManager, Injectable {
 
 //    let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
     let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
+    let privateContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
 
     private var openAPS: OpenAPS!
 
@@ -908,7 +909,7 @@ final class BaseAPSManager: APSManager, Injectable {
     // fetch glucose for time interval
     private func fetchGlucoseData(forPeriod period: String, withPredicate predicate: NSPredicate) -> [GlucoseStored] {
         do {
-            let fetchedData = try coredataContext.fetch(GlucoseStored.fetch(predicate))
+            let fetchedData = try coredataContext.fetch(GlucoseStored.fetch(predicate, ascending: false))
             debugPrint(
                 "APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose for \(period) period"
             )
@@ -921,37 +922,6 @@ final class BaseAPSManager: APSManager, Injectable {
         }
     }
 
-//    private func fetchGlucose() {
-//        ///fetch 24h
-//        do {
-//            let glucose24h = try coredataContext.fetch(GlucoseStored.fetch(NSPredicate.predicateForOneDayAgo))
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose for 24h period")
-//        } catch  {
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while fetching glucose for 24h period")
-//        }
-//        ///fetch one week
-//        do {
-//            let glucoseOneWeek = try coredataContext.fetch(GlucoseStored.fetch(NSPredicate.predicateForOneWeek))
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose for 7d period")
-//        } catch {
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while fetching glucose for 7d")
-//        }
-//        ///fetch one month
-//        do {
-//            let glucoseOneMonth = try coredataContext.fetch(GlucoseStored.fetch(NSPredicate.predicateForOneMonth))
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose for 30d period")
-//        } catch {
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while fetching glucose for 30d period")
-//        }
-//        ///total
-//        do {
-//            let glucoseTotal = try coredataContext.fetch(GlucoseStored.fetch(NSPredicate.predicateForThreeMonths))
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose for 90d period")
-//        } catch {
-//            debugPrint("APSManager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while fetching glucose for 90d period")
-//        }
-//    }
-
     // Add to statistics.JSON for upload to NS.
     private func statistics() {
         let now = Date()
@@ -960,7 +930,7 @@ final class BaseAPSManager: APSManager, Injectable {
             guard hour > 20 else {
                 return
             }
-            coredataContext.perform { [self] in
+            privateContext.perform { [self] in
                 var stats = [StatsData]()
                 let requestStats = StatsData.fetchRequest() as NSFetchRequest<StatsData>
                 let sortStats = NSSortDescriptor(key: "lastrun", ascending: false)
@@ -974,15 +944,26 @@ final class BaseAPSManager: APSManager, Injectable {
                 let preferences = settingsManager.preferences
 
                 // Carbs
-                var carbs = [Carbohydrates]()
                 var carbTotal: Decimal = 0
-                let requestCarbs = Carbohydrates.fetchRequest() as NSFetchRequest<Carbohydrates>
+                let requestCarbs = MealsStored.fetchRequest() as NSFetchRequest<MealsStored>
                 let daysAgo = Date().addingTimeInterval(-1.days.timeInterval)
                 requestCarbs.predicate = NSPredicate(format: "carbs > 0 AND date > %@", daysAgo as NSDate)
-                let sortCarbs = NSSortDescriptor(key: "date", ascending: true)
-                requestCarbs.sortDescriptors = [sortCarbs]
-                try? carbs = coredataContext.fetch(requestCarbs)
-                carbTotal = carbs.map({ carbs in carbs.carbs as? Decimal ?? 0 }).reduce(0, +)
+                requestCarbs.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
+
+                do {
+                    let carbs = try coredataContext.fetch(requestCarbs)
+                    carbTotal = carbs.reduce(0) { sum, meal in
+                        let mealCarbs = Decimal(string: "\(meal.carbs)") ?? Decimal.zero
+                        return sum + mealCarbs
+                    }
+                    debugPrint(
+                        "APSManager: statistics() -> \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched carbs"
+                    )
+                } catch {
+                    debugPrint(
+                        "APSManager: statistics() -> \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while fetching carbs"
+                    )
+                }
 
                 // TDD
                 var tdds = [TDD]()

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

@@ -87,6 +87,7 @@ final class OpenAPS {
                     // save to core data asynchronously
                     self.coredataContext.perform {
                         let new = OrefDetermination(context: self.coredataContext)
+                        new.id = UUID()
                         new.totalDailyDose = determination.tdd as? NSDecimalNumber
                         new.insulinSensitivity = determination.isf as? NSDecimalNumber
                         new.currentTarget = determination.current_target as? NSDecimalNumber

+ 270 - 103
FreeAPS/Sources/APS/Storage/CarbsStorage.swift

@@ -29,126 +29,293 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
 
     func storeCarbs(_ entries: [CarbsEntry]) {
         processQueue.sync {
-            let file = OpenAPS.Monitor.carbHistory
-            var uniqEvents: [CarbsEntry] = []
-
-            let fat = entries.last?.fat ?? 0
-            let protein = entries.last?.protein ?? 0
-
-            if fat > 0 || protein > 0 {
-                // -------------------------- FPU--------------------------------------
-                let interval = settings.settings.minuteInterval // Interval betwwen carbs
-                let timeCap = settings.settings.timeCap // Max Duration
-                let adjustment = settings.settings.individualAdjustmentFactor
-                let delay = settings.settings.delay // Tme before first future carb entry
-                let kcal = protein * 4 + fat * 9
-                let carbEquivalents = (kcal / 10) * adjustment
-                let fpus = carbEquivalents / 10
-                // Duration in hours used for extended boluses with Warsaw Method. Here used for total duration of the computed carbquivalents instead, excluding the configurable delay.
-                var computedDuration = 0
-                switch fpus {
-                case ..<2:
-                    computedDuration = 3
-                case 2 ..< 3:
-                    computedDuration = 4
-                case 3 ..< 4:
-                    computedDuration = 5
-                default:
-                    computedDuration = timeCap
-                }
-                // Size of each created carb equivalent if 60 minutes interval
-                var equivalent: Decimal = carbEquivalents / Decimal(computedDuration)
-                // Adjust for interval setting other than 60 minutes
-                equivalent /= Decimal(60 / interval)
-                // Round to 1 fraction digit
-                // equivalent = Decimal(round(Double(equivalent * 10) / 10))
-                let roundedEquivalent: Double = round(Double(equivalent * 10)) / 10
-                equivalent = Decimal(roundedEquivalent)
-                // Number of equivalents
-                var numberOfEquivalents = carbEquivalents / equivalent
-                // Only use delay in first loop
-                var firstIndex = true
-                // New date for each carb equivalent
-                var useDate = entries.last?.actualDate ?? Date()
-                // Group and Identify all FPUs together
-                let fpuID = entries.last?.fpuID ?? ""
-                // Create an array of all future carb equivalents.
-                var futureCarbArray = [CarbsEntry]()
-                while carbEquivalents > 0, numberOfEquivalents > 0 {
-                    if firstIndex {
-                        useDate = useDate.addingTimeInterval(delay.minutes.timeInterval)
-                        firstIndex = false
-                    } else { useDate = useDate.addingTimeInterval(interval.minutes.timeInterval) }
-
-                    let eachCarbEntry = CarbsEntry(
-                        id: UUID().uuidString, createdAt: entries.last?.createdAt ?? Date(), actualDate: useDate,
-                        carbs: equivalent, fat: 0, protein: 0, note: nil,
-                        enteredBy: CarbsEntry.manual, isFPU: true,
-                        fpuID: fpuID
-                    )
-                    futureCarbArray.append(eachCarbEntry)
-                    numberOfEquivalents -= 1
-                }
-                // Save the array
-                if carbEquivalents > 0 {
-                    self.storage.transaction { storage in
-                        storage.append(futureCarbArray, to: file, uniqBy: \.id)
-                        uniqEvents = storage.retrieve(file, as: [CarbsEntry].self)?
-                            .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
-                            .sorted { $0.createdAt > $1.createdAt } ?? []
-                        storage.save(Array(uniqEvents), as: file)
-                    }
-                }
-            } // ------------------------- END OF TPU ----------------------------------------
-            // Store the actual (normal) carbs
-            if let entry = entries.last, entry.carbs > 0 {
-                // uniqEvents = []
-                let onlyCarbs = CarbsEntry(
-                    id: entry.id ?? "",
-                    createdAt: entry.createdAt,
-                    actualDate: entry.actualDate ?? entry.createdAt,
-                    carbs: entry.carbs,
-                    fat: nil,
-                    protein: nil,
-                    note: entry.note ?? "",
-                    enteredBy: entry.enteredBy ?? "",
-                    isFPU: false,
-                    fpuID: ""
-                )
+            self.handleFPUCalculations(entries: entries)
+            self.storeNormalCarbs(entries: entries)
+            self.saveCarbsToCoreData(entries: entries)
+            self.saveFPUToCoreData(entries: entries)
+            self.notifyObservers(entries: entries)
+        }
+    }
+
+    private func handleFPUCalculations(entries: [CarbsEntry]) {
+        let file = OpenAPS.Monitor.carbHistory
+        var uniqEvents: [CarbsEntry] = []
 
-                self.storage.transaction { storage in
-                    storage.append(onlyCarbs, to: file, uniqBy: \.id)
+        let fat = entries.last?.fat ?? 0
+        let protein = entries.last?.protein ?? 0
+
+        if fat > 0 || protein > 0 {
+            // -------------------------- FPU--------------------------------------
+            let interval = settings.settings.minuteInterval // Interval betwwen carbs
+            let timeCap = settings.settings.timeCap // Max Duration
+            let adjustment = settings.settings.individualAdjustmentFactor
+            let delay = settings.settings.delay // Tme before first future carb entry
+            let kcal = protein * 4 + fat * 9
+            let carbEquivalents = (kcal / 10) * adjustment
+            let fpus = carbEquivalents / 10
+            // Duration in hours used for extended boluses with Warsaw Method. Here used for total duration of the computed carbquivalents instead, excluding the configurable delay.
+            var computedDuration = 0
+            switch fpus {
+            case ..<2:
+                computedDuration = 3
+            case 2 ..< 3:
+                computedDuration = 4
+            case 3 ..< 4:
+                computedDuration = 5
+            default:
+                computedDuration = timeCap
+            }
+            // Size of each created carb equivalent if 60 minutes interval
+            var equivalent: Decimal = carbEquivalents / Decimal(computedDuration)
+            // Adjust for interval setting other than 60 minutes
+            equivalent /= Decimal(60 / interval)
+            // Round to 1 fraction digit
+            // equivalent = Decimal(round(Double(equivalent * 10) / 10))
+            let roundedEquivalent: Double = round(Double(equivalent * 10)) / 10
+            equivalent = Decimal(roundedEquivalent)
+            // Number of equivalents
+            var numberOfEquivalents = carbEquivalents / equivalent
+            // Only use delay in first loop
+            var firstIndex = true
+            // New date for each carb equivalent
+            var useDate = entries.last?.actualDate ?? Date()
+            // Group and Identify all FPUs together
+            let fpuID = entries.last?.fpuID ?? ""
+            // Create an array of all future carb equivalents.
+            var futureCarbArray = [CarbsEntry]()
+            while carbEquivalents > 0, numberOfEquivalents > 0 {
+                if firstIndex {
+                    useDate = useDate.addingTimeInterval(delay.minutes.timeInterval)
+                    firstIndex = false
+                } else { useDate = useDate.addingTimeInterval(interval.minutes.timeInterval) }
+
+                let eachCarbEntry = CarbsEntry(
+                    id: UUID().uuidString, createdAt: entries.last?.createdAt ?? Date(), actualDate: useDate,
+                    carbs: equivalent, fat: 0, protein: 0, note: nil,
+                    enteredBy: CarbsEntry.manual, isFPU: true,
+                    fpuID: fpuID
+                )
+                futureCarbArray.append(eachCarbEntry)
+                numberOfEquivalents -= 1
+            }
+            // Save the array
+            if carbEquivalents > 0 {
+                storage.transaction { storage in
+                    storage.append(futureCarbArray, to: file, uniqBy: \.id)
                     uniqEvents = storage.retrieve(file, as: [CarbsEntry].self)?
                         .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
                         .sorted { $0.createdAt > $1.createdAt } ?? []
                     storage.save(Array(uniqEvents), as: file)
                 }
+
+                // MARK: - save also to core data
+
+                saveFPUToCoreData(entries: futureCarbArray)
             }
+        }
+    }
 
-            // MARK: Save to CoreData. TEST
+    private func storeNormalCarbs(entries: [CarbsEntry]) {
+        let file = OpenAPS.Monitor.carbHistory
+        var uniqEvents: [CarbsEntry] = []
 
-            var cbs: Decimal = 0
-            var carbDate = Date()
-            if entries.isNotEmpty {
-                cbs = entries[0].carbs
-                carbDate = entries[0].actualDate ?? entries[0].createdAt
+        if let entry = entries.last, entry.carbs > 0 {
+            // uniqEvents = []
+            let onlyCarbs = CarbsEntry(
+                id: entry.id ?? "",
+                createdAt: entry.createdAt,
+                actualDate: entry.actualDate ?? entry.createdAt,
+                carbs: entry.carbs,
+                fat: nil,
+                protein: nil,
+                note: entry.note ?? "",
+                enteredBy: entry.enteredBy ?? "",
+                isFPU: false,
+                fpuID: ""
+            )
+
+            storage.transaction { storage in
+                storage.append(onlyCarbs, to: file, uniqBy: \.id)
+                uniqEvents = storage.retrieve(file, as: [CarbsEntry].self)?
+                    .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
+                    .sorted { $0.createdAt > $1.createdAt } ?? []
+                storage.save(Array(uniqEvents), as: file)
             }
-            if cbs != 0 {
-                self.coredataContext.perform {
-                    let carbDataForStats = Carbohydrates(context: self.coredataContext)
+        }
+    }
 
-                    carbDataForStats.date = carbDate
-                    carbDataForStats.carbs = cbs as NSDecimalNumber
+    private func saveCarbsToCoreData(entries: [CarbsEntry]) {
+        guard let entry = entries.last, entry.carbs != 0 else { return }
 
-                    try? self.coredataContext.save()
-                }
+        coredataContext.perform {
+            let newItem = MealsStored(context: self.coredataContext)
+            newItem.date = entry.actualDate ?? entry.createdAt
+            newItem.carbs = Double(truncating: NSDecimalNumber(decimal: entry.carbs))
+            newItem.id = UUID()
+            newItem.isFPU = false
+            do {
+                try self.coredataContext.save()
+                debugPrint(
+                    "Carbs Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved carbs to core data"
+                )
+            } catch {
+                debugPrint(
+                    "Carbs Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) error while saving carbs to core data"
+                )
             }
-            broadcaster.notify(CarbsObserver.self, on: processQueue) {
-                $0.carbsDidUpdate(uniqEvents)
+        }
+    }
+
+    private func saveFPUToCoreData(entries: [CarbsEntry]) {
+        guard let entry = entries.last, entry.fat != 0 || entry.protein != 0 else { return }
+
+        coredataContext.perform {
+            let newItem = MealsStored(context: self.coredataContext)
+            newItem.date = entry.actualDate ?? entry.createdAt
+            newItem.fat = Double(truncating: NSDecimalNumber(decimal: entry.fat ?? 0))
+            newItem.protein = Double(truncating: NSDecimalNumber(decimal: entry.protein ?? 0))
+            newItem.carbs = Double(truncating: NSDecimalNumber(decimal: entry.carbs))
+            newItem.id = UUID()
+            newItem.isFPU = true
+            do {
+                try self.coredataContext.save()
+                debugPrint("Carbs Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved fpus to core data")
+            } catch {
+                debugPrint(
+                    "Carbs Storage: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) error while saving fpus to core data"
+                )
             }
         }
     }
 
+    private func notifyObservers(entries: [CarbsEntry]) {
+        broadcaster.notify(CarbsObserver.self, on: processQueue) {
+            $0.carbsDidUpdate(entries)
+        }
+    }
+
+//    func storeCarbs(_ entries: [CarbsEntry]) {
+//        processQueue.sync {
+//            let file = OpenAPS.Monitor.carbHistory
+//            var uniqEvents: [CarbsEntry] = []
+//
+//            let fat = entries.last?.fat ?? 0
+//            let protein = entries.last?.protein ?? 0
+//
+//            if fat > 0 || protein > 0 {
+//                // -------------------------- FPU--------------------------------------
+//                let interval = settings.settings.minuteInterval // Interval betwwen carbs
+//                let timeCap = settings.settings.timeCap // Max Duration
+//                let adjustment = settings.settings.individualAdjustmentFactor
+//                let delay = settings.settings.delay // Tme before first future carb entry
+//                let kcal = protein * 4 + fat * 9
+//                let carbEquivalents = (kcal / 10) * adjustment
+//                let fpus = carbEquivalents / 10
+//                // Duration in hours used for extended boluses with Warsaw Method. Here used for total duration of the computed carbquivalents instead, excluding the configurable delay.
+//                var computedDuration = 0
+//                switch fpus {
+//                case ..<2:
+//                    computedDuration = 3
+//                case 2 ..< 3:
+//                    computedDuration = 4
+//                case 3 ..< 4:
+//                    computedDuration = 5
+//                default:
+//                    computedDuration = timeCap
+//                }
+//                // Size of each created carb equivalent if 60 minutes interval
+//                var equivalent: Decimal = carbEquivalents / Decimal(computedDuration)
+//                // Adjust for interval setting other than 60 minutes
+//                equivalent /= Decimal(60 / interval)
+//                // Round to 1 fraction digit
+//                // equivalent = Decimal(round(Double(equivalent * 10) / 10))
+//                let roundedEquivalent: Double = round(Double(equivalent * 10)) / 10
+//                equivalent = Decimal(roundedEquivalent)
+//                // Number of equivalents
+//                var numberOfEquivalents = carbEquivalents / equivalent
+//                // Only use delay in first loop
+//                var firstIndex = true
+//                // New date for each carb equivalent
+//                var useDate = entries.last?.actualDate ?? Date()
+//                // Group and Identify all FPUs together
+//                let fpuID = entries.last?.fpuID ?? ""
+//                // Create an array of all future carb equivalents.
+//                var futureCarbArray = [CarbsEntry]()
+//                while carbEquivalents > 0, numberOfEquivalents > 0 {
+//                    if firstIndex {
+//                        useDate = useDate.addingTimeInterval(delay.minutes.timeInterval)
+//                        firstIndex = false
+//                    } else { useDate = useDate.addingTimeInterval(interval.minutes.timeInterval) }
+//
+//                    let eachCarbEntry = CarbsEntry(
+//                        id: UUID().uuidString, createdAt: entries.last?.createdAt ?? Date(), actualDate: useDate,
+//                        carbs: equivalent, fat: 0, protein: 0, note: nil,
+//                        enteredBy: CarbsEntry.manual, isFPU: true,
+//                        fpuID: fpuID
+//                    )
+//                    futureCarbArray.append(eachCarbEntry)
+//                    numberOfEquivalents -= 1
+//                }
+//                // Save the array
+//                if carbEquivalents > 0 {
+//                    self.storage.transaction { storage in
+//                        storage.append(futureCarbArray, to: file, uniqBy: \.id)
+//                        uniqEvents = storage.retrieve(file, as: [CarbsEntry].self)?
+//                            .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
+//                            .sorted { $0.createdAt > $1.createdAt } ?? []
+//                        storage.save(Array(uniqEvents), as: file)
+//                    }
+//                }
+//            } // ------------------------- END OF TPU ----------------------------------------
+//            // Store the actual (normal) carbs
+//            if let entry = entries.last, entry.carbs > 0 {
+//                // uniqEvents = []
+//                let onlyCarbs = CarbsEntry(
+//                    id: entry.id ?? "",
+//                    createdAt: entry.createdAt,
+//                    actualDate: entry.actualDate ?? entry.createdAt,
+//                    carbs: entry.carbs,
+//                    fat: nil,
+//                    protein: nil,
+//                    note: entry.note ?? "",
+//                    enteredBy: entry.enteredBy ?? "",
+//                    isFPU: false,
+//                    fpuID: ""
+//                )
+//
+//                self.storage.transaction { storage in
+//                    storage.append(onlyCarbs, to: file, uniqBy: \.id)
+//                    uniqEvents = storage.retrieve(file, as: [CarbsEntry].self)?
+//                        .filter { $0.createdAt.addingTimeInterval(1.days.timeInterval) > Date() }
+//                        .sorted { $0.createdAt > $1.createdAt } ?? []
+//                    storage.save(Array(uniqEvents), as: file)
+//                }
+//            }
+
+    // MARK: Save to CoreData
+
+//            var cbs: Decimal = 0
+//            var carbDate = Date()
+//            if entries.isNotEmpty {
+//                cbs = entries[0].carbs
+//                carbDate = entries[0].actualDate ?? entries[0].createdAt
+//            }
+//            if cbs != 0 {
+//                self.coredataContext.perform {
+//                    let carbDataForStats = Carbohydrates(context: self.coredataContext)
+//
+//                    carbDataForStats.date = carbDate
+//                    carbDataForStats.carbs = cbs as NSDecimalNumber
+//
+//                    try? self.coredataContext.save()
+//                }
+//            }
+//            broadcaster.notify(CarbsObserver.self, on: processQueue) {
+//                $0.carbsDidUpdate(uniqEvents)
+//            }
+//        }
+//    }
+
     func syncDate() -> Date {
         Date().addingTimeInterval(-1.days.timeInterval)
     }

+ 0 - 7
FreeAPS/Sources/Modules/AddCarbs/AddCarbsDataFlow.swift

@@ -1,7 +0,0 @@
-enum AddCarbs {
-    enum Config {}
-}
-
-protocol AddCarbsProvider: Provider {
-    var suggestion: Suggestion? { get }
-}

+ 0 - 7
FreeAPS/Sources/Modules/AddCarbs/AddCarbsProvider.swift

@@ -1,7 +0,0 @@
-extension AddCarbs {
-    final class Provider: BaseProvider, AddCarbsProvider {
-        var suggestion: Suggestion? {
-            storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
-        }
-    }
-}

+ 0 - 210
FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift

@@ -1,210 +0,0 @@
-import CoreData
-import Foundation
-import SwiftUI
-
-extension AddCarbs {
-    final class StateModel: BaseStateModel<Provider> {
-        @Injected() var carbsStorage: CarbsStorage!
-        @Injected() var apsManager: APSManager!
-        @Injected() var settings: SettingsManager!
-        @Published var carbs: Decimal = 0
-        @Published var date = Date()
-        @Published var protein: Decimal = 0
-        @Published var fat: Decimal = 0
-        @Published var carbsRequired: Decimal?
-        @Published var useFPUconversion: Bool = false
-        @Published var dish: String = ""
-        @Published var selection: Presets?
-        @Published var summation: [String] = []
-        @Published var maxCarbs: Decimal = 0
-        @Published var note: String = ""
-        @Published var id_: String = ""
-        @Published var summary: String = ""
-        @Published var skipBolus: Bool = false
-
-        let now = Date.now
-
-        let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
-
-        override func subscribe() {
-            carbsRequired = provider.suggestion?.carbsReq
-            maxCarbs = settings.settings.maxCarbs
-            skipBolus = settingsManager.settings.skipBolusScreenAfterCarbs
-            useFPUconversion = settingsManager.settings.useFPUconversion
-        }
-
-        func add(_ continue_: Bool, fetch: Bool) {
-            guard carbs > 0 || fat > 0 || protein > 0 else {
-                showModal(for: nil)
-                return
-            }
-            carbs = min(carbs, maxCarbs)
-            id_ = UUID().uuidString
-
-            let carbsToStore = [CarbsEntry(
-                id: id_,
-                createdAt: now,
-                actualDate: date,
-                carbs: carbs,
-                fat: fat,
-                protein: protein,
-                note: note,
-                enteredBy: CarbsEntry.manual,
-                isFPU: false, fpuID: UUID().uuidString
-            )]
-            carbsStorage.storeCarbs(carbsToStore)
-
-            if skipBolus, !continue_, !fetch {
-                apsManager.determineBasalSync()
-                showModal(for: nil)
-            } else if carbs > 0 {
-                saveToCoreData(carbsToStore)
-            } else {
-                hideModal()
-            }
-        }
-
-        func deletePreset() {
-            if selection != nil {
-                try? coredataContext.delete(selection!)
-                try? coredataContext.save()
-                carbs = 0
-                fat = 0
-                protein = 0
-            }
-            selection = nil
-        }
-
-        func removePresetFromNewMeal() {
-            let a = summation.firstIndex(where: { $0 == selection?.dish! })
-            if a != nil, summation[a ?? 0] != "" {
-                summation.remove(at: a!)
-            }
-        }
-
-        func addPresetToNewMeal() {
-            let test: String = selection?.dish ?? "dontAdd"
-            if test != "dontAdd" {
-                summation.append(test)
-            }
-        }
-
-        func addNewPresetToWaitersNotepad(_ dish: String) {
-            summation.append(dish)
-        }
-
-        func addToSummation() {
-            summation.append(selection?.dish ?? "")
-        }
-
-        func waitersNotepad() -> String {
-            var filteredArray = summation.filter { !$0.isEmpty }
-
-            if carbs == 0, protein == 0, fat == 0 {
-                filteredArray = []
-            }
-
-            guard filteredArray != [] else {
-                return ""
-            }
-            var carbs_: Decimal = 0.0
-            var fat_: Decimal = 0.0
-            var protein_: Decimal = 0.0
-            var presetArray = [Presets]()
-
-            coredataContext.performAndWait {
-                let requestPresets = Presets.fetchRequest() as NSFetchRequest<Presets>
-                try? presetArray = coredataContext.fetch(requestPresets)
-            }
-            var waitersNotepad = [String]()
-            var stringValue = ""
-
-            for each in filteredArray {
-                let countedSet = NSCountedSet(array: filteredArray)
-                let count = countedSet.count(for: each)
-                if each != stringValue {
-                    waitersNotepad.append("\(count) \(each)")
-                }
-                stringValue = each
-
-                for sel in presetArray {
-                    if sel.dish == each {
-                        carbs_ += (sel.carbs)! as Decimal
-                        fat_ += (sel.fat)! as Decimal
-                        protein_ += (sel.protein)! as Decimal
-                        break
-                    }
-                }
-            }
-            let extracarbs = carbs - carbs_
-            let extraFat = fat - fat_
-            let extraProtein = protein - protein_
-            var addedString = ""
-
-            if extracarbs > 0, filteredArray.isNotEmpty {
-                addedString += "Additional carbs: \(extracarbs) ,"
-            } else if extracarbs < 0 { addedString += "Removed carbs: \(extracarbs) " }
-
-            if extraFat > 0, filteredArray.isNotEmpty {
-                addedString += "Additional fat: \(extraFat) ,"
-            } else if extraFat < 0 { addedString += "Removed fat: \(extraFat) ," }
-
-            if extraProtein > 0, filteredArray.isNotEmpty {
-                addedString += "Additional protein: \(extraProtein) ,"
-            } else if extraProtein < 0 { addedString += "Removed protein: \(extraProtein) ," }
-
-            if addedString != "" {
-                waitersNotepad.append(addedString)
-            }
-            var waitersNotepadString = ""
-
-            if waitersNotepad.count == 1 {
-                waitersNotepadString = waitersNotepad[0]
-            } else if waitersNotepad.count > 1 {
-                for each in waitersNotepad {
-                    if each != waitersNotepad.last {
-                        waitersNotepadString += " " + each + ","
-                    } else { waitersNotepadString += " " + each }
-                }
-            }
-            return waitersNotepadString
-        }
-
-        func loadEntries(_ editMode: Bool) {
-            if editMode {
-                coredataContext.performAndWait {
-                    var mealToEdit = [Meals]()
-                    let requestMeal = Meals.fetchRequest() as NSFetchRequest<Meals>
-                    let sortMeal = NSSortDescriptor(key: "createdAt", ascending: false)
-                    requestMeal.sortDescriptors = [sortMeal]
-                    requestMeal.fetchLimit = 1
-                    try? mealToEdit = self.coredataContext.fetch(requestMeal)
-
-                    self.carbs = Decimal(mealToEdit.first?.carbs ?? 0)
-                    self.fat = Decimal(mealToEdit.first?.fat ?? 0)
-                    self.protein = Decimal(mealToEdit.first?.protein ?? 0)
-                    self.note = mealToEdit.first?.note ?? ""
-                    self.id_ = mealToEdit.first?.id ?? ""
-                }
-            }
-        }
-
-        func saveToCoreData(_ stored: [CarbsEntry]) {
-            coredataContext.performAndWait {
-                let save = Meals(context: coredataContext)
-                if let entry = stored.first {
-                    save.createdAt = now
-                    save.actualDate = entry.actualDate ?? Date.now
-                    save.id = entry.id ?? ""
-                    save.fpuID = entry.fpuID ?? ""
-                    save.carbs = Double(entry.carbs)
-                    save.fat = Double(entry.fat ?? 0)
-                    save.protein = Double(entry.protein ?? 0)
-                    save.note = entry.note
-                    try? coredataContext.save()
-                }
-                print("meals 1: ID: " + (save.id ?? "").description + " FPU ID: " + (save.fpuID ?? "").description)
-            }
-        }
-    }
-}

+ 0 - 353
FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift

@@ -1,353 +0,0 @@
-import CoreData
-import SwiftUI
-import Swinject
-
-extension AddCarbs {
-    struct RootView: BaseView {
-        let resolver: Resolver
-        let editMode: Bool
-        let override: Bool
-        @StateObject var state = StateModel()
-        @State var dish: String = ""
-        @State var isPromptPresented = false
-        @State var saved = false
-        @State var pushed = false
-        @State private var showAlert = false
-        @FocusState private var isFocused: Bool
-
-        @FetchRequest(
-            entity: Presets.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "dish", ascending: true)]
-        ) var carbPresets: FetchedResults<Presets>
-
-        @Environment(\.managedObjectContext) var moc
-        @Environment(\.colorScheme) var colorScheme
-
-        private var formatter: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 1
-            return formatter
-        }
-
-        private var color: LinearGradient {
-            colorScheme == .dark ? LinearGradient(
-                gradient: Gradient(colors: [
-                    Color.bgDarkBlue,
-                    Color.bgDarkerDarkBlue
-                ]),
-                startPoint: .top,
-                endPoint: .bottom
-            )
-                :
-                LinearGradient(
-                    gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
-                    startPoint: .top,
-                    endPoint: .bottom
-                )
-        }
-
-        var body: some View {
-            Form {
-                if let carbsReq = state.carbsRequired, state.carbs < carbsReq {
-                    Section {
-                        HStack {
-                            Text("Carbs required")
-                            Spacer()
-                            Text((formatter.string(from: carbsReq as NSNumber) ?? "") + " g")
-                        }
-                    }
-                }
-                Section {
-                    HStack {
-                        Text("Carbs").fontWeight(.semibold)
-                        Spacer()
-                        DecimalTextField(
-                            "0",
-                            value: $state.carbs,
-                            formatter: formatter,
-                            autofocus: true,
-                            cleanInput: true
-                        )
-                        Text("g").foregroundColor(.secondary)
-                    }
-
-                    if state.useFPUconversion {
-                        proteinAndFat()
-                    }
-
-                    // Summary when combining presets
-                    if state.waitersNotepad() != "" {
-                        HStack {
-                            Text("Total")
-                            let test = state.waitersNotepad().components(separatedBy: ", ").removeDublicates()
-                            HStack(spacing: 0) {
-                                ForEach(test, id: \.self) {
-                                    Text($0).foregroundStyle(Color.randomGreen()).font(.footnote)
-                                    Text($0 == test[test.count - 1] ? "" : ", ")
-                                }
-                            }.frame(maxWidth: .infinity, alignment: .trailing)
-                        }
-                    }
-
-                    // Time
-                    HStack {
-                        let now = Date.now
-                        Text("Time")
-                        Spacer()
-                        if !pushed {
-                            Button {
-                                pushed = true
-                            } label: { Text("Now") }.buttonStyle(.borderless).foregroundColor(.secondary).padding(.trailing, 5)
-                        } else {
-                            Button { state.date = state.date.addingTimeInterval(-15.minutes.timeInterval) }
-                            label: { Image(systemName: "minus.circle") }.tint(.blue).buttonStyle(.borderless)
-                            DatePicker(
-                                "Time",
-                                selection: $state.date,
-                                displayedComponents: [.hourAndMinute]
-                            ).controlSize(.mini)
-                                .labelsHidden()
-                            Button {
-                                state.date = state.date.addingTimeInterval(15.minutes.timeInterval)
-                            }
-                            label: { Image(systemName: "plus.circle") }.tint(.blue).buttonStyle(.borderless)
-                        }
-                    }
-
-                    // Optional meal note
-                    HStack {
-                        Text("Note").foregroundColor(.secondary)
-                        TextField("", text: $state.note).multilineTextAlignment(.trailing)
-                        if state.note != "", isFocused {
-                            Button { isFocused = false } label: { Image(systemName: "keyboard.chevron.compact.down") }
-                                .controlSize(.mini)
-                        }
-                    }
-                    .focused($isFocused)
-                    .popover(isPresented: $isPromptPresented) {
-                        presetPopover
-                    }
-                }.listRowBackground(Color.chart)
-
-                Section {
-                    Button { state.add(override, fetch: editMode) }
-                    label: { Text((state.skipBolus && !override && !editMode) ? "Save" : "Continue") }
-                        .disabled(empty)
-                        .frame(maxWidth: .infinity, alignment: .center)
-                }.listRowBackground(!empty ? Color(.systemBlue) : Color(.systemGray4))
-                    .tint(.white)
-
-                Section {
-                    mealPresets
-                }.listRowBackground(Color.chart)
-            }.scrollContentBackground(.hidden).background(color)
-                .onAppear {
-                    configureView {
-                        state.loadEntries(editMode)
-                    }
-                }
-                .navigationTitle("Add Meal")
-                .navigationBarTitleDisplayMode(.inline)
-                .toolbar {
-                    ToolbarItem(placement: .topBarLeading) {
-                        Button {
-                            state.hideModal()
-                        } label: {
-                            Text("Close")
-                        }
-                    }
-                }
-        }
-
-        private var presetPopover: some View {
-            Form {
-                Section {
-                    TextField("Name Of Dish", text: $dish)
-                    Button {
-                        saved = true
-                        if dish != "", saved {
-                            let preset = Presets(context: moc)
-                            preset.dish = dish
-                            preset.fat = state.fat as NSDecimalNumber
-                            preset.protein = state.protein as NSDecimalNumber
-                            preset.carbs = state.carbs as NSDecimalNumber
-                            try? moc.save()
-                            state.addNewPresetToWaitersNotepad(dish)
-                            saved = false
-                            isPromptPresented = false
-                        }
-                    }
-                    label: { Text("Save") }
-                    Button {
-                        dish = ""
-                        saved = false
-                        isPromptPresented = false }
-                    label: { Text("Cancel") }
-                } header: { Text("Enter Meal Preset Name") }
-            }
-        }
-
-        private var empty: Bool {
-            state.carbs <= 0 && state.fat <= 0 && state.protein <= 0
-        }
-
-        private var minusButton: some View {
-            Button {
-                if state.carbs != 0,
-                   (state.carbs - (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
-                {
-                    state.carbs -= (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal)
-                } else { state.carbs = 0 }
-
-                if state.fat != 0,
-                   (state.fat - (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
-                {
-                    state.fat -= (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal)
-                } else { state.fat = 0 }
-
-                if state.protein != 0,
-                   (state.protein - (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) as Decimal) >= 0
-                {
-                    state.protein -= (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal)
-                } else { state.protein = 0 }
-
-                state.removePresetFromNewMeal()
-                if state.carbs == 0, state.fat == 0, state.protein == 0 { state.summation = [] }
-            }
-            label: { Image(systemName: "minus.circle") }
-                .disabled(
-                    state
-                        .selection == nil ||
-                        (
-                            !state.summation
-                                .contains(state.selection?.dish ?? "") && (state.selection?.dish ?? "") != ""
-                        )
-                )
-                .buttonStyle(.borderless)
-                .tint(.blue)
-        }
-
-        private var plusButton: some View {
-            Button {
-                state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
-                state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
-                state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
-
-                state.addPresetToNewMeal()
-            }
-            label: { Image(systemName: "plus.circle") }
-                .disabled(state.selection == nil)
-                .buttonStyle(.borderless)
-                .tint(.blue)
-        }
-
-        private var mealPresets: some View {
-            Section {
-                HStack {
-                    if state.selection != nil {
-                        minusButton
-                    }
-                    Picker("Preset", selection: $state.selection) {
-                        Text("Saved Food").tag(nil as Presets?)
-                        ForEach(carbPresets, id: \.self) { (preset: Presets) in
-                            Text(preset.dish ?? "").tag(preset as Presets?)
-                        }
-                    }
-                    .labelsHidden()
-                    .frame(maxWidth: .infinity, alignment: .center)
-                    ._onBindingChange($state.selection) { _ in
-                        state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
-                        state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
-                        state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
-                        state.addToSummation()
-                    }
-                    if state.selection != nil {
-                        plusButton
-                    }
-                }
-
-                HStack {
-                    Button("Delete Preset") {
-                        showAlert.toggle()
-                    }
-                    .disabled(state.selection == nil)
-                    .tint(.orange)
-                    .buttonStyle(.borderless)
-                    .alert(
-                        "Delete preset '\(state.selection?.dish ?? "")'?",
-                        isPresented: $showAlert,
-                        actions: {
-                            Button("No", role: .cancel) {}
-                            Button("Yes", role: .destructive) {
-                                state.deletePreset()
-
-                                state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
-                                state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
-                                state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
-
-                                state.addPresetToNewMeal()
-                            }
-                        }
-                    )
-
-                    Spacer()
-
-                    Button {
-                        isPromptPresented = true
-                    }
-                    label: { Text("Save as Preset") }
-                        .buttonStyle(.borderless)
-                        .disabled(
-                            empty ||
-                                (
-                                    (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
-                                        .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
-                                        .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
-                                        .protein
-                                )
-                        )
-                }
-            }
-        }
-
-        @ViewBuilder private func proteinAndFat() -> some View {
-            HStack {
-                Text("Fat").foregroundColor(.orange)
-                Spacer()
-                DecimalTextField(
-                    "0",
-                    value: $state.fat,
-                    formatter: formatter,
-                    autofocus: false,
-                    cleanInput: true
-                )
-                Text("g").foregroundColor(.secondary)
-            }
-            HStack {
-                Text("Protein").foregroundColor(.red)
-                Spacer()
-                DecimalTextField(
-                    "0",
-                    value: $state.protein,
-                    formatter: formatter,
-                    autofocus: false,
-                    cleanInput: true
-                ).foregroundColor(.loopRed)
-
-                Text("g").foregroundColor(.secondary)
-            }
-        }
-    }
-}
-
-public extension Color {
-    static func randomGreen(randomOpacity: Bool = false) -> Color {
-        Color(
-            red: .random(in: 0 ... 1),
-            green: .random(in: 0.4 ... 0.7),
-            blue: .random(in: 0.2 ... 1),
-            opacity: randomOpacity ? .random(in: 0.8 ... 1) : 1
-        )
-    }
-}

+ 0 - 46
FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift

@@ -473,8 +473,6 @@ extension Bolus {
             carbsStorage.storeCarbs(carbsToStore)
 
             if carbs > 0 {
-                saveCarbsToCoreData(carbsToStore)
-
                 // only perform determine basal sync if the user doesn't use the pump bolus, otherwise the enact bolus func in the APSManger does a sync
                 if amount <= 0 {
                     apsManager.determineBasalSync()
@@ -482,31 +480,6 @@ extension Bolus {
             }
         }
 
-        func saveCarbsToCoreData(_: [CarbsEntry]) {
-            context.performAndWait {
-                // create new object in the view context
-                let newCarbEntry = MealsStored(context: self.context)
-                newCarbEntry.id = UUID()
-                newCarbEntry.note = ""
-                newCarbEntry.carbs = Double(carbs)
-                newCarbEntry.fat = Double(fat)
-                newCarbEntry.protein = Double(protein)
-                newCarbEntry.date = Date()
-                self.context.perform {
-                    do {
-                        try self.context.save()
-                        debugPrint(
-                            "Bolus State: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved carbs to core data"
-                        )
-                    } catch {
-                        debugPrint(
-                            "Bolus State: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save carbs to core data"
-                        )
-                    }
-                }
-            }
-        }
-
         // MARK: - Presets
 
         func deletePreset() {
@@ -614,25 +587,6 @@ extension Bolus {
             }
             return waitersNotepadString
         }
-
-        func loadEntries(_ editMode: Bool) {
-            if editMode {
-                context.performAndWait {
-                    var mealToEdit = [Meals]()
-                    let requestMeal = Meals.fetchRequest() as NSFetchRequest<Meals>
-                    let sortMeal = NSSortDescriptor(key: "createdAt", ascending: false)
-                    requestMeal.sortDescriptors = [sortMeal]
-                    requestMeal.fetchLimit = 1
-                    try? mealToEdit = self.context.fetch(requestMeal)
-
-                    self.carbs = Decimal(mealToEdit.first?.carbs ?? 0)
-                    self.fat = Decimal(mealToEdit.first?.fat ?? 0)
-                    self.protein = Decimal(mealToEdit.first?.protein ?? 0)
-                    self.note = mealToEdit.first?.note ?? ""
-                    self.id_ = mealToEdit.first?.id ?? ""
-                }
-            }
-        }
     }
 }
 

+ 1 - 1
FreeAPS/Sources/Modules/Bolus/View/BolusRootView.swift

@@ -306,7 +306,7 @@ extension Bolus {
                                     let test = state.waitersNotepad().components(separatedBy: ", ").removeDublicates()
                                     HStack(spacing: 0) {
                                         ForEach(test, id: \.self) {
-                                            Text($0).foregroundStyle(Color.randomGreen()).font(.footnote)
+                                            Text($0).foregroundStyle(Color.blue).font(.footnote)
                                             Text($0 == test[test.count - 1] ? "" : ", ")
                                         }
                                     }.frame(maxWidth: .infinity, alignment: .trailing)

+ 18 - 13
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -93,25 +93,30 @@ struct MainChartView: View {
     // MARK: - Core Data Fetch Requests
 
     @FetchRequest(
-        entity: MealsStored.entity(),
-        sortDescriptors: [NSSortDescriptor(keyPath: \MealsStored.date, ascending: true)],
-        predicate: NSPredicate.predicateForOneDayAgo
+        fetchRequest: MealsStored.fetch(NSPredicate.carbsForChart),
+        animation: Animation.bouncy
     ) var carbsFromPersistence: FetchedResults<MealsStored>
 
     @FetchRequest(
-        entity: InsulinStored.entity(),
-        sortDescriptors: [NSSortDescriptor(keyPath: \InsulinStored.date, ascending: true)],
-        predicate: NSPredicate.predicateForOneDayAgo,
+        fetchRequest: MealsStored.fetch(NSPredicate.fpusForChart),
+        animation: Animation.bouncy
+    ) var fpusFromPersistence: FetchedResults<MealsStored>
+
+    @FetchRequest(
+        fetchRequest: InsulinStored.fetch(NSPredicate.insulinForChart),
         animation: Animation.bouncy
     ) var insulinFromPersistence: FetchedResults<InsulinStored>
 
     @FetchRequest(
-        entity: GlucoseStored.entity(),
-        sortDescriptors: [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: true)],
-        predicate: NSPredicate.predicateForOneDayAgo,
+        fetchRequest: GlucoseStored.fetch(NSPredicate.predicateForOneDayAgo, ascending: false),
         animation: Animation.bouncy
     ) var glucoseFromPersistence: FetchedResults<GlucoseStored>
 
+    @FetchRequest(
+        fetchRequest: OrefDetermination.fetch(NSPredicate.enactedDetermination),
+        animation: Animation.bouncy
+    ) var determinations: FetchedResults<OrefDetermination>
+
     private var bolusFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
@@ -166,12 +171,12 @@ struct MainChartView: View {
                         updateStartEndMarkers()
                         yAxisChartData()
                         scroller.scrollTo("MainChart", anchor: .trailing)
-                    }.onChange(of: glucose) { _ in
+                    }.onChange(of: glucoseFromPersistence.map(\.id)) { _ in
                         updateStartEndMarkers()
                         yAxisChartData()
                         scroller.scrollTo("MainChart", anchor: .trailing)
                     }
-                    .onChange(of: suggestion) { _ in
+                    .onChange(of: determinations.map(\.id)) { _ in
                         updateStartEndMarkers()
                         scroller.scrollTo("MainChart", anchor: .trailing)
                     }
@@ -404,13 +409,13 @@ extension MainChartView {
 
     private func drawFpus() -> some ChartContent {
         /// fpus
-        ForEach(fpusForChart) { fpu in
+        ForEach(fpusFromPersistence) { fpu in
             let fpuAmount = fpu.carbs
             let size = (Config.fpuSize + CGFloat(fpuAmount) * Config.carbsScale) * 1.8
             let yPosition = units == .mgdL ? 60 : 3.33
 
             PointMark(
-                x: .value("Time", fpu.actualDate ?? Date(), unit: .second),
+                x: .value("Time", fpu.date ?? Date(), unit: .second),
                 y: .value("Value", yPosition)
             )
             .symbolSize(size)

+ 3 - 3
FreeAPS/Sources/Modules/Home/View/Header/LoopView.swift

@@ -12,7 +12,7 @@ struct LoopView: View {
 
     @FetchRequest(
         fetchRequest: OrefDetermination
-            .fetch(NSPredicate.predicateFor30MinAgoForDetermination)
+            .fetch(NSPredicate.enactedDetermination)
     ) var determination: FetchedResults<OrefDetermination>
 
     @Binding var closedLoop: Bool
@@ -42,7 +42,7 @@ struct LoopView: View {
                 Text("looping")
             } else if manualTempBasal {
                 Text("Manual")
-            } else if determination.first?.timestamp != nil {
+            } else if determination.first?.deliverAt != nil { //previously the .timestamp property was used here because this only gets updated when the reportenacted function in the aps manager gets called
                 Text(timeString)
             } else {
                 Text("--")
@@ -62,7 +62,7 @@ struct LoopView: View {
     }
 
     private var color: Color {
-        guard determination.first?.timestamp != nil else {
+        guard determination.first?.deliverAt != nil else { //previously the .timestamp property was used here because this only gets updated when the reportenacted function in the aps manager gets called
             return .secondary
         }
         guard manualTempBasal == false else {

+ 1 - 1
FreeAPS/Sources/Services/LiveActivity/LiveActivityBridge.swift

@@ -312,7 +312,7 @@ extension LiveActivityBridge: GlucoseObserver {
     private func fetchGlucose() -> [GlucoseStored] {
         let context = CoreDataStack.shared.persistentContainer.viewContext
         do {
-            let fetchedGlucose = try context.fetch(GlucoseStored.fetch(NSPredicate.predicateForSixHoursAgo))
+            let fetchedGlucose = try context.fetch(GlucoseStored.fetch(NSPredicate.predicateForSixHoursAgo, ascending: false))
             debugPrint("LA Bridge: \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to fetch glucose")
 
             return fetchedGlucose

+ 1 - 1
FreeAPS/Sources/Services/WatchManager/WatchManager.swift

@@ -56,7 +56,7 @@ final class BaseWatchManager: NSObject, WatchManager, Injectable {
 
     private func fetchGlucose() -> [GlucoseStored] {
         do {
-            let fetchedReadings = try context.fetch(GlucoseStored.fetch(NSPredicate.predicateFor120MinAgo))
+            let fetchedReadings = try context.fetch(GlucoseStored.fetch(NSPredicate.predicateFor120MinAgo, ascending: true))
             debugPrint("Watch Manager: \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) fetched glucose")
             return fetchedReadings
         } catch {

+ 2 - 25
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -25,16 +25,6 @@
         <attribute name="median_7" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="median_30" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
     </entity>
-    <entity name="Carbohydrates" representedClassName="Carbohydrates" syncable="YES" codeGenerationType="class">
-        <attribute name="carbs" optional="YES" attributeType="Decimal" defaultValueString="0"/>
-        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="enteredBy" optional="YES" attributeType="String"/>
-    </entity>
-    <entity name="Fat" representedClassName="Fat" syncable="YES" codeGenerationType="class">
-        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="enteredBy" optional="YES" attributeType="String"/>
-        <attribute name="fat" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-    </entity>
     <entity name="GlucoseStored" representedClassName="GlucoseStored" syncable="YES" codeGenerationType="class">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="direction" optional="YES" attributeType="String"/>
@@ -78,22 +68,12 @@
         <attribute name="loopStatus" optional="YES" attributeType="String"/>
         <attribute name="start" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
     </entity>
-    <entity name="Meals" representedClassName="Meals" syncable="YES" codeGenerationType="class">
-        <attribute name="actualDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="carbs" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
-        <attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="fat" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
-        <attribute name="fpuID" optional="YES" attributeType="String"/>
-        <attribute name="id" optional="YES" attributeType="String"/>
-        <attribute name="note" optional="YES" attributeType="String"/>
-        <attribute name="protein" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
-    </entity>
     <entity name="MealsStored" representedClassName="MealsStored" syncable="YES" codeGenerationType="class">
         <attribute name="carbs" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="fat" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
         <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
+        <attribute name="isFPU" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="note" optional="YES" attributeType="String"/>
         <attribute name="protein" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
     </entity>
@@ -113,6 +93,7 @@
         <attribute name="eventualBG" optional="YES" attributeType="Decimal" defaultValueString="0"/>
         <attribute name="expectedDelta" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="glucose" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
         <attribute name="insulinForManualBolus" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="insulinReq" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="insulinSensitivity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -211,8 +192,4 @@
         <attribute name="id" optional="YES" attributeType="String" defaultValueString="empy"/>
         <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
     </entity>
-    <entity name="UXSettings" representedClassName="UXSettings" syncable="YES" codeGenerationType="class">
-        <attribute name="date" optional="YES" attributeType="Date" defaultDateTimeInterval="722095800" usesScalarValueType="NO"/>
-        <attribute name="hours" optional="YES" attributeType="Integer 16" defaultValueString="6" usesScalarValueType="YES"/>
-    </entity>
 </model>

+ 8 - 1
Model/Determination+helper.swift

@@ -2,7 +2,7 @@ import CoreData
 import Foundation
 
 extension OrefDetermination {
-    static func fetch(_ predicate: NSPredicate = .all) -> NSFetchRequest<OrefDetermination> {
+    static func fetch(_ predicate: NSPredicate = .predicateForOneDayAgo) -> NSFetchRequest<OrefDetermination> {
         let request = OrefDetermination.fetchRequest()
         request.sortDescriptors = [NSSortDescriptor(keyPath: \OrefDetermination.deliverAt, ascending: false)]
         request.predicate = predicate
@@ -20,3 +20,10 @@ extension OrefDetermination {
         reason?.components(separatedBy: "; ").last ?? ""
     }
 }
+
+extension NSPredicate {
+    static var enactedDetermination: NSPredicate {
+        let date = Date.halfHourAgo
+        return NSPredicate(format: "enacted == true AND deliverAt >= %@", date as NSDate)
+    }
+}

+ 2 - 2
Model/GlucoseStored+helper.swift

@@ -2,9 +2,9 @@ import CoreData
 import Foundation
 
 extension GlucoseStored {
-    static func fetch(_ predicate: NSPredicate = .all) -> NSFetchRequest<GlucoseStored> {
+    static func fetch(_ predicate: NSPredicate = .all, ascending: Bool) -> NSFetchRequest<GlucoseStored> {
         let request = GlucoseStored.fetchRequest()
-        request.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: true)]
+        request.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: ascending)]
         request.predicate = predicate
         return request
     }

+ 19 - 0
Model/InsulinStored+helper.swift

@@ -0,0 +1,19 @@
+import CoreData
+import Foundation
+
+extension InsulinStored {
+    static func fetch(_ predicate: NSPredicate = .predicateForOneDayAgo) -> NSFetchRequest<InsulinStored> {
+        let request = InsulinStored.fetchRequest()
+        request.sortDescriptors = [NSSortDescriptor(keyPath: \InsulinStored.date, ascending: false)]
+        request.fetchLimit = 100
+        request.predicate = predicate
+        return request
+    }
+}
+
+extension NSPredicate {
+    static var insulinForChart: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(format: "amount > 0 AND date >= %@", date as NSDate)
+    }
+}

+ 26 - 0
Model/MealsStored+helper.swift

@@ -0,0 +1,26 @@
+import CoreData
+import Foundation
+
+extension NSPredicate {
+    static var fpusForChart: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(format: "fat > 0 AND protein > 0 AND isFPU == true AND date >= %@", date as NSDate)
+    }
+
+    static var carbsForChart: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(format: "carbs > 0 AND isFPU == false AND date >= %@", date as NSDate)
+    }
+}
+
+extension MealsStored {
+    static func fetch(_ predicate: NSPredicate = .predicateForOneDayAgo) -> NSFetchRequest<MealsStored> {
+        let request = MealsStored.fetchRequest()
+        request.sortDescriptors = [NSSortDescriptor(keyPath: \MealsStored.date, ascending: false)]
+        request.fetchLimit = 100
+        request.predicate = predicate
+//        request.propertiesToFetch = ["date", "carbs"]
+//        request.resultType = .dictionaryResultType
+        return request
+    }
+}