Parcourir la source

remove InsulinStored, replace it with Entities for storing PumpHistory, use it in main chart and determine basal ...wip

polscm32 il y a 2 ans
Parent
commit
e410ac9e35

+ 4 - 0
BolusStored+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(BolusStored) public class BolusStored: NSManagedObject {}

+ 15 - 0
BolusStored+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension BolusStored {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<BolusStored> {
+        NSFetchRequest<BolusStored>(entityName: "BolusStored")
+    }
+
+    @NSManaged var amount: NSDecimalNumber?
+    @NSManaged var isExternal: Bool
+    @NSManaged var isSMB: Bool
+    @NSManaged var pumpEvent: PumpEventStored?
+}
+
+extension BolusStored: Identifiable {}

+ 28 - 12
FreeAPS.xcodeproj/project.pbxproj

@@ -267,13 +267,13 @@
 		581516A42BCED84A00BF67D7 /* DebuggingIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */; };
 		581516A92BCEEDF800BF67D7 /* NSPredicates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A82BCEEDF800BF67D7 /* NSPredicates.swift */; };
 		581AC4392BE22ED10038760C /* JSONConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581AC4382BE22ED10038760C /* JSONConverter.swift */; };
+		581F7FB32BE7B2A0005A5F89 /* BolusStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581F7FB12BE7B2A0005A5F89 /* BolusStored+CoreDataClass.swift */; };
+		581F7FB42BE7B2A0005A5F89 /* BolusStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581F7FB22BE7B2A0005A5F89 /* BolusStored+CoreDataProperties.swift */; };
 		58237D9E2BCF0A6B00A47A79 /* PopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58237D9D2BCF0A6B00A47A79 /* PopupView.swift */; };
 		5825D1342BD4058F00F36E9B /* BGaverages+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1062BD4058F00F36E9B /* BGaverages+CoreDataClass.swift */; };
 		5825D1352BD4058F00F36E9B /* BGaverages+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1072BD4058F00F36E9B /* BGaverages+CoreDataProperties.swift */; };
 		5825D1362BD4058F00F36E9B /* BGmedian+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1082BD4058F00F36E9B /* BGmedian+CoreDataClass.swift */; };
 		5825D1372BD4058F00F36E9B /* BGmedian+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D1092BD4058F00F36E9B /* BGmedian+CoreDataProperties.swift */; };
-		5825D1382BD4058F00F36E9B /* InsulinStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D10A2BD4058F00F36E9B /* InsulinStored+CoreDataClass.swift */; };
-		5825D1392BD4058F00F36E9B /* InsulinStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D10B2BD4058F00F36E9B /* InsulinStored+CoreDataProperties.swift */; };
 		5825D13A2BD4058F00F36E9B /* LoopStatRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D10C2BD4058F00F36E9B /* LoopStatRecord+CoreDataClass.swift */; };
 		5825D13B2BD4058F00F36E9B /* LoopStatRecord+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D10D2BD4058F00F36E9B /* LoopStatRecord+CoreDataProperties.swift */; };
 		5825D13C2BD4058F00F36E9B /* ImportError+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5825D10E2BD4058F00F36E9B /* ImportError+CoreDataClass.swift */; };
@@ -315,9 +315,13 @@
 		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 /* CarbEntryStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A52F2BD2E3C700A5DC04 /* CarbEntryStored+helper.swift */; };
-		5837A5322BD2E81100A5DC04 /* InsulinStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */; };
 		5856174D2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5856174B2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift */; };
 		5856174E2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5856174C2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift */; };
+		585E2CAE2BE7BF46006ECF1A /* PumpEvent+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E2CAD2BE7BF46006ECF1A /* PumpEvent+helper.swift */; };
+		585E2CB32BE7DA03006ECF1A /* TempBasalStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E2CAF2BE7DA03006ECF1A /* TempBasalStored+CoreDataClass.swift */; };
+		585E2CB42BE7DA03006ECF1A /* TempBasalStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E2CB02BE7DA03006ECF1A /* TempBasalStored+CoreDataProperties.swift */; };
+		585E2CB52BE7DA03006ECF1A /* PumpEventStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E2CB12BE7DA03006ECF1A /* PumpEventStored+CoreDataClass.swift */; };
+		585E2CB62BE7DA03006ECF1A /* PumpEventStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E2CB22BE7DA03006ECF1A /* PumpEventStored+CoreDataProperties.swift */; };
 		587DA1F62B77F3DD00B28F8A /* SettingsRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */; };
 		5887527C2BD986E1008B081D /* OpenAPSBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5887527B2BD986E1008B081D /* OpenAPSBattery.swift */; };
 		588752842BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588752822BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift */; };
@@ -888,13 +892,13 @@
 		581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggingIdentifiers.swift; sourceTree = "<group>"; };
 		581516A82BCEEDF800BF67D7 /* NSPredicates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPredicates.swift; sourceTree = "<group>"; };
 		581AC4382BE22ED10038760C /* JSONConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONConverter.swift; sourceTree = "<group>"; };
+		581F7FB12BE7B2A0005A5F89 /* BolusStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BolusStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		581F7FB22BE7B2A0005A5F89 /* BolusStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BolusStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		58237D9D2BCF0A6B00A47A79 /* PopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupView.swift; sourceTree = "<group>"; };
 		5825D1062BD4058F00F36E9B /* BGaverages+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BGaverages+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1072BD4058F00F36E9B /* BGaverages+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BGaverages+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1082BD4058F00F36E9B /* BGmedian+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BGmedian+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D1092BD4058F00F36E9B /* BGmedian+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BGmedian+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
-		5825D10A2BD4058F00F36E9B /* InsulinStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
-		5825D10B2BD4058F00F36E9B /* InsulinStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D10C2BD4058F00F36E9B /* LoopStatRecord+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5825D10D2BD4058F00F36E9B /* LoopStatRecord+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		5825D10E2BD4058F00F36E9B /* ImportError+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImportError+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
@@ -936,9 +940,13 @@
 		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 /* CarbEntryStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CarbEntryStored+helper.swift"; sourceTree = "<group>"; };
-		5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InsulinStored+helper.swift"; sourceTree = "<group>"; };
 		5856174B2BDADA3F009B23D7 /* GlucoseStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		5856174C2BDADA3F009B23D7 /* GlucoseStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GlucoseStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
+		585E2CAD2BE7BF46006ECF1A /* PumpEvent+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEvent+helper.swift"; sourceTree = "<group>"; };
+		585E2CAF2BE7DA03006ECF1A /* TempBasalStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		585E2CB02BE7DA03006ECF1A /* TempBasalStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempBasalStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
+		585E2CB12BE7DA03006ECF1A /* PumpEventStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEventStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		585E2CB22BE7DA03006ECF1A /* PumpEventStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpEventStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		587DA1F52B77F3DD00B28F8A /* SettingsRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRowView.swift; sourceTree = "<group>"; };
 		5887527B2BD986E1008B081D /* OpenAPSBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAPSBattery.swift; sourceTree = "<group>"; };
 		588752822BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OpenAPS_Battery+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
@@ -2150,6 +2158,12 @@
 		5825D1052BD4056700F36E9B /* Classes+Properties */ = {
 			isa = PBXGroup;
 			children = (
+				585E2CAF2BE7DA03006ECF1A /* TempBasalStored+CoreDataClass.swift */,
+				585E2CB02BE7DA03006ECF1A /* TempBasalStored+CoreDataProperties.swift */,
+				585E2CB12BE7DA03006ECF1A /* PumpEventStored+CoreDataClass.swift */,
+				585E2CB22BE7DA03006ECF1A /* PumpEventStored+CoreDataProperties.swift */,
+				581F7FB12BE7B2A0005A5F89 /* BolusStored+CoreDataClass.swift */,
+				581F7FB22BE7B2A0005A5F89 /* BolusStored+CoreDataProperties.swift */,
 				CC76E9482BD471BA008BEB61 /* Forecast+CoreDataClass.swift */,
 				CC76E9492BD471BA008BEB61 /* Forecast+CoreDataProperties.swift */,
 				CC76E94A2BD471BA008BEB61 /* ForecastValue+CoreDataClass.swift */,
@@ -2162,8 +2176,6 @@
 				5825D1072BD4058F00F36E9B /* BGaverages+CoreDataProperties.swift */,
 				5825D1082BD4058F00F36E9B /* BGmedian+CoreDataClass.swift */,
 				5825D1092BD4058F00F36E9B /* BGmedian+CoreDataProperties.swift */,
-				5825D10A2BD4058F00F36E9B /* InsulinStored+CoreDataClass.swift */,
-				5825D10B2BD4058F00F36E9B /* InsulinStored+CoreDataProperties.swift */,
 				5825D10C2BD4058F00F36E9B /* LoopStatRecord+CoreDataClass.swift */,
 				5825D10D2BD4058F00F36E9B /* LoopStatRecord+CoreDataProperties.swift */,
 				5825D10E2BD4058F00F36E9B /* ImportError+CoreDataClass.swift */,
@@ -2214,7 +2226,7 @@
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
 				5837A52F2BD2E3C700A5DC04 /* CarbEntryStored+helper.swift */,
-				5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */,
+				585E2CAD2BE7BF46006ECF1A /* PumpEvent+helper.swift */,
 				CC76E9502BD4812E008BEB61 /* Forecast+helper.swift */,
 				5887527B2BD986E1008B081D /* OpenAPSBattery.swift */,
 				581AC4382BE22ED10038760C /* JSONConverter.swift */,
@@ -2899,6 +2911,7 @@
 				38C4D33725E9A1A300D30B77 /* DispatchQueue+Extensions.swift in Sources */,
 				F90692CF274B999A0037068D /* HealthKitDataFlow.swift in Sources */,
 				CE7CA3552A064973004BE681 /* ListStateIntent.swift in Sources */,
+				581F7FB42BE7B2A0005A5F89 /* BolusStored+CoreDataProperties.swift in Sources */,
 				BDF530D82B40F8AC002CAF43 /* LockScreenView.swift in Sources */,
 				5825D15F2BD4058F00F36E9B /* Protein+CoreDataProperties.swift in Sources */,
 				5825D15D2BD4058F00F36E9B /* Target+CoreDataProperties.swift in Sources */,
@@ -2909,6 +2922,7 @@
 				CEA4F62329BE10F70011ADF7 /* SavitzkyGolayFilter.swift in Sources */,
 				38B4F3C325E2A20B00E76A18 /* PumpSetupView.swift in Sources */,
 				38E4453C274E411700EC9A94 /* Disk+Codable.swift in Sources */,
+				585E2CB52BE7DA03006ECF1A /* PumpEventStored+CoreDataClass.swift in Sources */,
 				19E1F7EF29D08EBA005C8D20 /* IconConfigRootWiew.swift in Sources */,
 				1967DFC229D053D300759F30 /* IconImage.swift in Sources */,
 				382C134B25F14E3700715CE1 /* BGTargets.swift in Sources */,
@@ -2918,6 +2932,7 @@
 				38FEF408273B011A00574A46 /* LibreTransmitterSource.swift in Sources */,
 				3894873A2614928B004DF424 /* DispatchTimer.swift in Sources */,
 				5825D14E2BD4058F00F36E9B /* HbA1c+CoreDataClass.swift in Sources */,
+				585E2CB32BE7DA03006ECF1A /* TempBasalStored+CoreDataClass.swift in Sources */,
 				3895E4C625B9E00D00214B37 /* Preferences.swift in Sources */,
 				386A124F271707F000DDC61C /* DexcomSourceG6.swift in Sources */,
 				CE94598429E9E3E60047C9C6 /* WatchConfigStateModel.swift in Sources */,
@@ -2971,6 +2986,7 @@
 				388E5A5C25B6F0770019842D /* JSON.swift in Sources */,
 				3811DF0225CA9FEA00A708ED /* Credentials.swift in Sources */,
 				5825D1452BD4058F00F36E9B /* Autosens_+CoreDataProperties.swift in Sources */,
+				585E2CB42BE7DA03006ECF1A /* TempBasalStored+CoreDataProperties.swift in Sources */,
 				19DC678529CA67A400FD9EC4 /* OverrideProfilesRootView.swift in Sources */,
 				5837A5302BD2E3C700A5DC04 /* CarbEntryStored+helper.swift in Sources */,
 				CC76E94C2BD471BA008BEB61 /* Forecast+CoreDataClass.swift in Sources */,
@@ -2989,7 +3005,6 @@
 				E00EEC0627368630002FF094 /* UIAssembly.swift in Sources */,
 				3811DE1825C9D40400A708ED /* Router.swift in Sources */,
 				5825D1462BD4058F00F36E9B /* Oref0Suggestion+CoreDataClass.swift in Sources */,
-				5825D1392BD4058F00F36E9B /* InsulinStored+CoreDataProperties.swift in Sources */,
 				CE7950262998056D00FA576E /* CGMSetupView.swift in Sources */,
 				38A0363B25ECF07E00FCBB52 /* GlucoseStorage.swift in Sources */,
 				190EBCC629FF138000BA767D /* StatConfigProvider.swift in Sources */,
@@ -2997,6 +3012,7 @@
 				E00EEC0427368630002FF094 /* SecurityAssembly.swift in Sources */,
 				3811DEE825CA063400A708ED /* Injected.swift in Sources */,
 				5825D14A2BD4058F00F36E9B /* OrefDetermination+CoreDataClass.swift in Sources */,
+				585E2CAE2BE7BF46006ECF1A /* PumpEvent+helper.swift in Sources */,
 				3811DEAF25C9D88300A708ED /* KeyValueStorage.swift in Sources */,
 				38FE826D25CC8461001FF17A /* NightscoutAPI.swift in Sources */,
 				388358C825EEF6D200E024B2 /* BasalProfileEntry.swift in Sources */,
@@ -3065,7 +3081,6 @@
 				38DAB28A260D349500F74C1A /* FetchGlucoseManager.swift in Sources */,
 				38F37828261260DC009DB701 /* Color+Extensions.swift in Sources */,
 				3811DE3F25C9D4A100A708ED /* SettingsStateModel.swift in Sources */,
-				5825D1382BD4058F00F36E9B /* InsulinStored+CoreDataClass.swift in Sources */,
 				CE7CA3582A064E2F004BE681 /* ListStateView.swift in Sources */,
 				193F6CDD2A512C8F001240FD /* Loops.swift in Sources */,
 				38B4F3CB25E502E200E76A18 /* WeakObjectSet.swift in Sources */,
@@ -3171,6 +3186,7 @@
 				38E4453D274E411700EC9A94 /* Disk+Errors.swift in Sources */,
 				38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */,
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
+				581F7FB32BE7B2A0005A5F89 /* BolusStored+CoreDataClass.swift in Sources */,
 				F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */,
 				5075C1608E6249A51495C422 /* TargetsEditorProvider.swift in Sources */,
 				E13B7DAB2A435F57066AF02E /* TargetsEditorStateModel.swift in Sources */,
@@ -3212,7 +3228,6 @@
 				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 */,
 				588752842BD9986A008B081D /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
@@ -3231,6 +3246,7 @@
 				891DECF7BC20968D7F566161 /* AutotuneConfigProvider.swift in Sources */,
 				5825D15C2BD4058F00F36E9B /* Target+CoreDataClass.swift in Sources */,
 				D76333C9256787610B3B4875 /* AutotuneConfigStateModel.swift in Sources */,
+				585E2CB62BE7DA03006ECF1A /* PumpEventStored+CoreDataProperties.swift in Sources */,
 				A05235B9112E677ED03B6E8E /* AutotuneConfigRootView.swift in Sources */,
 				5825D14F2BD4058F00F36E9B /* HbA1c+CoreDataProperties.swift in Sources */,
 				7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */,

+ 20 - 4
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -120,6 +120,20 @@ final class OpenAPS {
         }
     }
 
+    private func fetchPumpHistory() -> [PumpEventStored]? {
+        do {
+            debugPrint("OpenAPS: \(#function) \(DebuggingIdentifiers.succeeded) fetched pump history")
+
+            return try context
+                .fetch(PumpEventStored.fetch(NSPredicate.pumpHistoryLast24h, ascending: true))
+        } catch {
+            debugPrint(
+                "OpenAPS: \(#function) \(DebuggingIdentifiers.failed) error while fetching pumphistory for determine basal with error: \(error)"
+            )
+            return []
+        }
+    }
+
     func determineBasal(currentTemp: TempBasal, clock: Date = Date()) -> Future<Determination?, Never> {
         Future { promise in
             self.processQueue.async {
@@ -131,7 +145,9 @@ final class OpenAPS {
                 let tempBasal = currentTemp.rawJSON
                 self.storage.save(tempBasal, as: Monitor.tempBasal)
 
-                let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
+//                let pumpHistory = self.loadFileFromStorage(name: OpenAPS.Monitor.pumpHistory)
+                let pumpHistory = self.fetchPumpHistory()
+                let pumpHistoryJSON = self.jsonConverter.convertToJSON(pumpHistory)
 
                 // carbs
                 let carbs = self.fetchCarbs()
@@ -147,7 +163,7 @@ final class OpenAPS {
 
                 /// meal
                 let meal = self.meal(
-                    pumphistory: pumpHistory,
+                    pumphistory: pumpHistoryJSON,
                     profile: profile,
                     basalProfile: basalProfile,
                     clock: clock,
@@ -160,7 +176,7 @@ final class OpenAPS {
                 // iob
                 let autosens = self.loadFileFromStorage(name: Settings.autosense)
                 let iob = self.iob(
-                    pumphistory: pumpHistory,
+                    pumphistory: pumpHistoryJSON,
                     profile: profile,
                     clock: clock,
                     autosens: autosens.isEmpty ? .null : autosens
@@ -185,7 +201,7 @@ final class OpenAPS {
                     meal: meal,
                     microBolusAllowed: true,
                     reservoir: reservoir,
-                    pumpHistory: pumpHistory,
+                    pumpHistory: pumpHistoryJSON,
                     preferences: preferences,
                     basalProfile: basalProfile,
                     oref2_variables: oref2_variables

+ 64 - 8
FreeAPS/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -26,6 +26,9 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
         injectServices(resolver)
     }
 
+    typealias PumpEvent = PumpEventStored.EventType
+    typealias TempType = PumpEventStored.TempType
+
     private let context = CoreDataStack.shared.backgroundContext
 
     func storePumpEvents(_ events: [NewPumpEvent]) {
@@ -38,15 +41,20 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                     let amount = Decimal(string: dose.unitsInDeliverableIncrements.description)
                     let minutes = Int((dose.endDate - dose.startDate).timeInterval / 60)
 
-                    // MARK: - save to Core Data
-
                     self.context.perform {
-                        let new = InsulinStored(context: self.context)
-                        new.amount = amount as? NSDecimalNumber
-                        new.date = Date()
-                        new.external = false
-                        new.id = UUID()
-                        new.isSMB = true
+                        // create pump event
+                        let newPumpEvent = PumpEventStored(context: self.context)
+                        newPumpEvent.id = id
+                        newPumpEvent.timestamp = event.date
+                        newPumpEvent.type = PumpEvent.bolus.rawValue
+
+                        // create bolus entry and specify relationship to pump event
+                        let newBolusEntry = BolusStored(context: self.context)
+                        newBolusEntry.pumpEvent = newPumpEvent
+                        newBolusEntry.amount = amount as? NSDecimalNumber
+                        newBolusEntry.isExternal = dose.manuallyEntered
+                        newBolusEntry.isSMB = dose.automatic ?? true
+                        // TODO: - do we need duration here?
 
                         if self.context.hasChanges {
                             do {
@@ -86,6 +94,34 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                     let isCancel = delivered != nil //! event.isMutable && delivered != nil
                     guard !isCancel else { return [] }
 
+                    self.context.perform {
+                        // create pump event
+                        let newPumpEvent = PumpEventStored(context: self.context)
+                        newPumpEvent.id = id
+                        newPumpEvent.timestamp = date
+                        newPumpEvent.type = PumpEvent.tempBasal.rawValue
+
+                        // create temp basal and specify relationship
+                        let newTempBasal = TempBasalStored(context: self.context)
+                        newTempBasal.pumpEvent = newPumpEvent
+                        newTempBasal.duration = Int16(round(minutes))
+                        newTempBasal.rate = rate as NSDecimalNumber
+                        newTempBasal.tempType = TempType.absolute.rawValue
+
+                        if self.context.hasChanges {
+                            do {
+                                try self.context.save()
+                                debugPrint(
+                                    "Pump History storage: \(#function) \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved temp basal to core data"
+                                )
+                            } catch {
+                                debugPrint(
+                                    "Pump History storage: \(#function) \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save temp basal to core data"
+                                )
+                            }
+                        }
+                    }
+
                     return [
                         PumpHistoryEvent(
                             id: id,
@@ -111,6 +147,26 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                         )
                     ]
                 case .suspend:
+                    self.context.perform {
+                        // create pump event
+                        let newPumpEvent = PumpEventStored(context: self.context)
+                        newPumpEvent.id = id
+                        newPumpEvent.timestamp = event.date
+                        newPumpEvent.type = PumpEvent.pumpSuspend.rawValue
+
+                        if self.context.hasChanges {
+                            do {
+                                try self.context.save()
+                                debugPrint(
+                                    "Pump History storage: \(#function) \(CoreDataStack.identifier) \(DebuggingIdentifiers.succeeded) saved suspension to core data"
+                                )
+                            } catch {
+                                debugPrint(
+                                    "Pump History storage: \(#function) \(CoreDataStack.identifier) \(DebuggingIdentifiers.failed) failed to save suspension to core data"
+                                )
+                            }
+                        }
+                    }
                     return [
                         PumpHistoryEvent(
                             id: id,

+ 29 - 13
FreeAPS/Sources/Modules/Bolus/BolusStateModel.swift

@@ -99,6 +99,8 @@ extension Bolus {
 
         let context = CoreDataStack.shared.viewContext
 
+        typealias PumpEvent = PumpEventStored.EventType
+
         override func subscribe() {
             fetchGlucose()
             fetchDetermination()
@@ -355,13 +357,20 @@ extension Bolus {
             }
         }
 
-        private func savePumpInsulin(amount: Decimal) {
-            let newItem = InsulinStored(context: context)
-            newItem.id = UUID()
-            newItem.amount = amount as NSDecimalNumber
-            newItem.date = Date()
-            newItem.external = false
-            newItem.isSMB = false
+        private func savePumpInsulin(amount _: Decimal) {
+            // create pump event
+            let newPumpEvent = PumpEventStored(context: context)
+            newPumpEvent.id = UUID().uuidString
+            newPumpEvent.timestamp = Date()
+            newPumpEvent.type = PumpEvent.bolus.rawValue
+
+            // create bolus entry and specify relationship to pump event
+            let newBolusEntry = BolusStored(context: context)
+            newBolusEntry.pumpEvent = newPumpEvent
+            newBolusEntry.amount = amount as NSDecimalNumber
+            newBolusEntry.isExternal = false
+            newBolusEntry.isSMB = false
+
             context.perform {
                 if self.context.hasChanges {
                     do {
@@ -427,12 +436,19 @@ extension Bolus {
 
             // save to core data asynchronously
             context.perform {
-                let newItem = InsulinStored(context: self.context)
-                newItem.id = UUID()
-                newItem.amount = self.amount as NSDecimalNumber
-                newItem.date = Date()
-                newItem.external = true
-                newItem.isSMB = false
+                // create pump event
+                let newPumpEvent = PumpEventStored(context: self.context)
+                newPumpEvent.id = UUID().uuidString
+                newPumpEvent.timestamp = self.date
+                newPumpEvent.type = PumpEvent.bolus.rawValue
+
+                // create bolus entry and specify relationship to pump event
+                let newBolusEntry = BolusStored(context: self.context)
+                newBolusEntry.pumpEvent = newPumpEvent
+                newBolusEntry.amount = self.amount as NSDecimalNumber
+                newBolusEntry.isExternal = true
+                newBolusEntry.isSMB = false
+
                 if self.context.hasChanges {
                     do {
                         try self.context.save()

+ 44 - 34
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -98,9 +98,9 @@ struct MainChartView: View {
     ) var fpusFromPersistence: FetchedResults<CarbEntryStored>
 
     @FetchRequest(
-        fetchRequest: InsulinStored.fetch(NSPredicate.insulinForChart),
+        fetchRequest: PumpEventStored.fetch(NSPredicate.pumpHistoryLast24h, ascending: true),
         animation: Animation.bouncy
-    ) var insulinFromPersistence: FetchedResults<InsulinStored>
+    ) var insulinFromPersistence: FetchedResults<PumpEventStored>
 
     @FetchRequest(
         fetchRequest: GlucoseStored.fetch(NSPredicate.glucose, ascending: true),
@@ -364,24 +364,27 @@ extension MainChartView {
 extension MainChartView {
     private func drawBoluses() -> some ChartContent {
         /// smbs in triangle form
-        ForEach(insulinFromPersistence) { bolus in
-            let bolusAmount = bolus.amount ?? 0 as NSDecimalNumber
-            let bolusDate = bolus.date ?? Date()
+        ForEach(insulinFromPersistence) { insulin in
+            let amount = insulin.bolus?.amount ?? 0 as NSDecimalNumber
+            let bolusDate = insulin.timestamp ?? Date()
             let glucose = timeToNearestGlucose(time: bolusDate.timeIntervalSince1970)
             let yPosition = (Decimal(glucose.glucose) * conversionFactor) + bolusOffset
-            let size = (Config.bolusSize + CGFloat(truncating: bolusAmount) * Config.bolusScale) * 1.8
+            let size = (Config.bolusSize + CGFloat(truncating: amount) * Config.bolusScale) * 1.8
 
-            PointMark(
-                x: .value("Time", bolus.date ?? Date(), unit: .second),
-                y: .value("Value", yPosition)
-            )
-            .symbol {
-                Image(systemName: "arrowtriangle.down.fill").font(.system(size: size)).foregroundStyle(Color.insulin)
-            }
-            .annotation(position: .top) {
-                Text(bolusFormatter.string(from: bolusAmount) ?? "")
-                    .font(.caption2)
-                    .foregroundStyle(Color.insulin)
+            // don't display triangles if it is no smb
+            if amount != 0 {
+                PointMark(
+                    x: .value("Time", bolusDate, unit: .second),
+                    y: .value("Value", yPosition)
+                )
+                .symbol {
+                    Image(systemName: "arrowtriangle.down.fill").font(.system(size: size)).foregroundStyle(Color.insulin)
+                }
+                .annotation(position: .top) {
+                    Text(bolusFormatter.string(from: amount) ?? "")
+                        .font(.caption2)
+                        .foregroundStyle(Color.insulin)
+                }
             }
         }
     }
@@ -592,24 +595,25 @@ extension MainChartView {
         }
     }
 
-    private func filteredTempBasals() -> [(start: Date, end: Date, rate: Double)] {
+    private func prepareTempBasals() -> [(start: Date, end: Date, rate: Double)] {
         let now = Date()
-        return TempBasals.compactMap { temp -> (start: Date, end: Date, rate: Double)? in
-            let end = min(temp.timestamp + (temp.durationMin ?? 0).minutes.timeInterval, now)
-            let isInsulinSuspended = suspensions.contains { $0.timestamp >= temp.timestamp && $0.timestamp <= end }
-
-            let rate = Double(temp.rate ?? Decimal.zero) * (isInsulinSuspended ? 0 : 1)
+        return insulinFromPersistence.compactMap { temp -> (start: Date, end: Date, rate: Double)? in
+            let duration = temp.tempBasal?.duration ?? 0
+            let timestamp = temp.timestamp ?? Date()
+            let end = min(timestamp + duration.minutes, now)
+            let isInsulinSuspended = suspensions.contains { $0.timestamp >= timestamp && $0.timestamp <= end }
+            let rate = Double(truncating: temp.tempBasal?.rate ?? Decimal.zero as NSDecimalNumber) * (isInsulinSuspended ? 0 : 1)
 
             // Check if there's a subsequent temp basal to determine the end time
-            guard let nextTemp = TempBasals.first(where: { $0.timestamp > temp.timestamp }) else {
-                return (temp.timestamp, end, rate)
+            guard let nextTemp = insulinFromPersistence.first(where: { $0.timestamp ?? .distantPast > timestamp }) else {
+                return (timestamp, end, rate)
             }
-            return (temp.timestamp, nextTemp.timestamp, rate)
+            return (timestamp, nextTemp.timestamp ?? Date(), rate) // end defaults to current time
         }
     }
 
     private func drawTempBasals() -> some ChartContent {
-        ForEach(filteredTempBasals(), id: \.rate) { basal in
+        ForEach(prepareTempBasals(), id: \.rate) { basal in
             RectangleMark(
                 xStart: .value("start", basal.start),
                 xEnd: .value("end", basal.end),
@@ -843,13 +847,13 @@ extension MainChartView {
                 startDate: totalBasal[index].startDate,
                 endDate: totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker
             ))
-            print(
-                "Basal",
-                totalBasal[index].startDate,
-                totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker,
-                totalBasal[index].amount,
-                totalBasal[index].isOverwritten
-            )
+//            print(
+//                "Basal",
+//                totalBasal[index].startDate,
+//                totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker,
+//                totalBasal[index].amount,
+//                totalBasal[index].isOverwritten
+//            )
         }
         BasalProfiles = basals
     }
@@ -941,3 +945,9 @@ struct LegendItem: View {
         }
     }
 }
+
+extension Int16 {
+    var minutes: TimeInterval {
+        TimeInterval(self) * 60
+    }
+}

+ 0 - 4
InsulinStored+CoreDataClass.swift

@@ -1,4 +0,0 @@
-import CoreData
-import Foundation
-
-@objc(InsulinStored) public class InsulinStored: NSManagedObject {}

+ 0 - 16
InsulinStored+CoreDataProperties.swift

@@ -1,16 +0,0 @@
-import CoreData
-import Foundation
-
-public extension InsulinStored {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<InsulinStored> {
-        NSFetchRequest<InsulinStored>(entityName: "InsulinStored")
-    }
-
-    @NSManaged var amount: NSDecimalNumber?
-    @NSManaged var date: Date?
-    @NSManaged var external: Bool
-    @NSManaged var id: UUID?
-    @NSManaged var isSMB: Bool
-}
-
-extension InsulinStored: Identifiable {}

+ 3 - 0
Model/CoreDataStack.swift

@@ -22,6 +22,9 @@ class CoreDataStack: ObservableObject {
     lazy var backgroundContext: NSManagedObjectContext = {
         let newbackgroundContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
         newbackgroundContext.automaticallyMergesChangesFromParent = true
+        newbackgroundContext
+            .mergePolicy =
+            NSMergeByPropertyStoreTrumpMergePolicy // if two objects with the same unique constraint are found, overwrite with the object in the external storage
         return newbackgroundContext
     }()
 

+ 27 - 10
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -25,6 +25,12 @@
         <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="BolusStored" representedClassName="BolusStored" syncable="YES">
+        <attribute name="amount" optional="YES" attributeType="Decimal" defaultValueString="0"/>
+        <attribute name="isExternal" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
+        <attribute name="isSMB" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
+        <relationship name="pumpEvent" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PumpEventStored" inverseName="bolus" inverseEntity="PumpEventStored"/>
+    </entity>
     <entity name="CarbEntryStored" representedClassName="CarbEntryStored" syncable="YES">
         <attribute name="carbs" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@@ -83,16 +89,6 @@
             <fetchIndexElement property="date" type="Binary" order="ascending"/>
         </fetchIndex>
     </entity>
-    <entity name="InsulinStored" representedClassName="InsulinStored" syncable="YES">
-        <attribute name="amount" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
-        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
-        <attribute name="external" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
-        <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
-        <attribute name="isSMB" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
-        <fetchIndex name="byDate">
-            <fetchIndexElement property="date" type="Binary" order="ascending"/>
-        </fetchIndex>
-    </entity>
     <entity name="LastLoop" representedClassName="LastLoop" syncable="YES">
         <attribute name="cob" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="iob" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -213,6 +209,21 @@
         <attribute name="enteredBy" optional="YES" attributeType="String"/>
         <attribute name="protein" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
     </entity>
+    <entity name="PumpEventStored" representedClassName="PumpEventStored" syncable="YES">
+        <attribute name="id" optional="YES" attributeType="String"/>
+        <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="type" optional="YES" attributeType="String"/>
+        <relationship name="bolus" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="BolusStored" inverseName="pumpEvent" inverseEntity="BolusStored"/>
+        <relationship name="tempBasal" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TempBasalStored" inverseName="pumpEvent" inverseEntity="TempBasalStored"/>
+        <fetchIndex name="byTimestamp">
+            <fetchIndexElement property="timestamp" type="Binary" order="ascending"/>
+        </fetchIndex>
+        <uniquenessConstraints>
+            <uniquenessConstraint>
+                <constraint value="id"/>
+            </uniquenessConstraint>
+        </uniquenessConstraints>
+    </entity>
     <entity name="StatsData" representedClassName="StatsData" syncable="YES">
         <attribute name="lastrun" attributeType="Date" defaultDateTimeInterval="704497620" usesScalarValueType="NO"/>
     </entity>
@@ -227,6 +238,12 @@
             <fetchIndexElement property="timestamp" type="Binary" order="ascending"/>
         </fetchIndex>
     </entity>
+    <entity name="TempBasalStored" representedClassName="TempBasalStored" syncable="YES">
+        <attribute name="duration" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="rate" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <attribute name="tempType" optional="YES" attributeType="String"/>
+        <relationship name="pumpEvent" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PumpEventStored" inverseName="tempBasal" inverseEntity="PumpEventStored"/>
+    </entity>
     <entity name="TempTargets" representedClassName="TempTargets" syncable="YES">
         <attribute name="active" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>

+ 0 - 20
Model/Helper/InsulinStored+helper.swift

@@ -1,20 +0,0 @@
-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: true)]
-        request.propertiesToFetch = ["amount", "date"]
-        request.resultType = .managedObjectResultType
-        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)
-    }
-}

+ 130 - 0
Model/Helper/PumpEvent+helper.swift

@@ -0,0 +1,130 @@
+import CoreData
+import Foundation
+
+extension PumpEventStored {
+    static func fetch(_ predicate: NSPredicate, ascending: Bool, fetchLimit: Int? = nil) -> NSFetchRequest<PumpEventStored> {
+        let request = PumpEventStored.fetchRequest()
+        request.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: ascending)]
+        request.resultType = .managedObjectResultType
+        request.predicate = predicate
+        if let fetchLimit = fetchLimit {
+            request.fetchLimit = fetchLimit
+        }
+        return request
+    }
+}
+
+public extension PumpEventStored {
+    enum EventType: String, JSON {
+        case bolus = "Bolus"
+        case smb = "SMB"
+        case isExternal = "External Insulin"
+        case mealBolus = "Meal Bolus"
+        case correctionBolus = "Correction Bolus"
+        case snackBolus = "Snack Bolus"
+        case bolusWizard = "BolusWizard"
+        case tempBasal = "TempBasal"
+        case tempBasalDuration = "TempBasalDuration"
+        case pumpSuspend = "PumpSuspend"
+        case pumpResume = "PumpResume"
+        case pumpAlarm = "PumpAlarm"
+        case pumpBattery = "PumpBattery"
+        case rewind = "Rewind"
+        case prime = "Prime"
+        case journalCarbs = "JournalEntryMealMarker"
+
+        case nsTempBasal = "Temp Basal"
+        case nsCarbCorrection = "Carb Correction"
+        case nsTempTarget = "Temporary Target"
+        case nsInsulinChange = "Insulin Change"
+        case nsSiteChange = "Site Change"
+        case nsBatteryChange = "Pump Battery Change"
+        case nsAnnouncement = "Announcement"
+        case nsSensorChange = "Sensor Start"
+        case capillaryGlucose = "BG Check"
+    }
+
+    enum TempType: String, JSON {
+        case absolute
+        case percent
+    }
+}
+
+extension NSPredicate {
+    static var pumpHistoryLast24h: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(format: "timestamp >= %@", date as NSDate)
+    }
+}
+
+extension PumpEventStored: Encodable {
+    enum CodingKeys: String, CodingKey {
+        // pump event CD entitiy
+        case id
+        case timestamp
+        case type
+        // bolus CD entitity
+        case amount
+        case isSMB
+        case isExternal
+        // temp basal CD entity
+        case duration
+        case rate
+        case tempType
+    }
+
+    public func encode(to encoder: Encoder) throws {
+        var container = encoder.container(keyedBy: CodingKeys.self)
+
+        let dateFormatter = ISO8601DateFormatter()
+        let formattedDate = dateFormatter.string(from: timestamp ?? Date())
+
+        // PumpEventStored
+        try container.encode(id, forKey: .id)
+        try container.encode(formattedDate, forKey: .timestamp)
+        try container.encode(type, forKey: .type)
+
+        // access to BolusStored entity
+        //
+        // amount
+        if let bolusAmount = bolus?.amount as Decimal? {
+            try container.encode(bolusAmount, forKey: .amount)
+        } else {
+            // Default value
+            try container.encode(Decimal(0), forKey: .amount)
+        }
+        // isSMB
+        if let isSMB = bolus?.isSMB {
+            try container.encode(isSMB, forKey: .isSMB)
+        } else {
+            try container.encode(true, forKey: .amount)
+        }
+        // isExternal
+        if let isExternal = bolus?.isExternal {
+            try container.encode(isExternal, forKey: .isExternal)
+        } else {
+            try container.encode(false, forKey: .isExternal)
+        }
+
+        // access to TempBasalStored entity
+        //
+        // duration
+        if let duration = tempBasal?.duration {
+            try container.encode(duration, forKey: .duration)
+        } else {
+            try container.encode(0, forKey: .duration)
+        }
+        // rate
+        if let rate = tempBasal?.rate as Decimal? {
+            try container.encode(rate, forKey: .rate)
+        } else {
+            try container.encode(0, forKey: .rate)
+        }
+        // temp type
+        if let tempType = tempBasal?.tempType {
+            try container.encode(tempType, forKey: .tempType)
+        } else {
+            try container.encode("absolute", forKey: .tempType)
+        }
+    }
+}

+ 4 - 0
PumpEventStored+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(PumpEventStored) public class PumpEventStored: NSManagedObject {}

+ 16 - 0
PumpEventStored+CoreDataProperties.swift

@@ -0,0 +1,16 @@
+import CoreData
+import Foundation
+
+public extension PumpEventStored {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<PumpEventStored> {
+        NSFetchRequest<PumpEventStored>(entityName: "PumpEventStored")
+    }
+
+    @NSManaged var id: String?
+    @NSManaged var timestamp: Date?
+    @NSManaged var type: String?
+    @NSManaged var bolus: BolusStored?
+    @NSManaged var tempBasal: TempBasalStored?
+}
+
+extension PumpEventStored: Identifiable {}

+ 4 - 0
TempBasalStored+CoreDataClass.swift

@@ -0,0 +1,4 @@
+import CoreData
+import Foundation
+
+@objc(TempBasalStored) public class TempBasalStored: NSManagedObject {}

+ 15 - 0
TempBasalStored+CoreDataProperties.swift

@@ -0,0 +1,15 @@
+import CoreData
+import Foundation
+
+public extension TempBasalStored {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<TempBasalStored> {
+        NSFetchRequest<TempBasalStored>(entityName: "TempBasalStored")
+    }
+
+    @NSManaged var duration: Int16
+    @NSManaged var rate: NSDecimalNumber?
+    @NSManaged var tempType: String?
+    @NSManaged var pumpEvent: PumpEventStored?
+}
+
+extension TempBasalStored: Identifiable {}