Explorar el Código

Store predBGs as forecasts in core data WIP

dnzxy hace 2 años
padre
commit
44b234dc9d

+ 4 - 0
Forecast+CoreDataClass.swift

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

+ 32 - 0
Forecast+CoreDataProperties.swift

@@ -0,0 +1,32 @@
+import CoreData
+import Foundation
+
+public extension Forecast {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<Forecast> {
+        NSFetchRequest<Forecast>(entityName: "Forecast")
+    }
+
+    @NSManaged var date: Date?
+    @NSManaged var id: UUID?
+    @NSManaged var type: String?
+    @NSManaged var forecastValues: Set<ForecastValue>?
+    @NSManaged var orefDetermination: OrefDetermination?
+}
+
+// MARK: Generated accessors for forecastValues
+
+public extension Forecast {
+    @objc(addForecastValuesObject:)
+    @NSManaged func addToForecastValues(_ value: ForecastValue)
+
+    @objc(removeForecastValuesObject:)
+    @NSManaged func removeFromForecastValues(_ value: ForecastValue)
+
+    @objc(addForecastValues:)
+    @NSManaged func addToForecastValues(_ values: NSSet)
+
+    @objc(removeForecastValues:)
+    @NSManaged func removeFromForecastValues(_ values: NSSet)
+}
+
+extension Forecast: Identifiable {}

+ 4 - 0
ForecastValue+CoreDataClass.swift

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

+ 14 - 0
ForecastValue+CoreDataProperties.swift

@@ -0,0 +1,14 @@
+import CoreData
+import Foundation
+
+public extension ForecastValue {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<ForecastValue> {
+        NSFetchRequest<ForecastValue>(entityName: "ForecastValue")
+    }
+
+    @NSManaged var index: Int32
+    @NSManaged var value: Int32
+    @NSManaged var forecast: Forecast?
+}
+
+extension ForecastValue: Identifiable {}

+ 20 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -381,6 +381,11 @@
 		CA370FC152BC98B3D1832968 /* BasalProfileEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8BCB0C37DEB5EC377B9612 /* BasalProfileEditorRootView.swift */; };
 		CC41E29A2B1E1F460070974F /* HistoryLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC41E2992B1E1F460070974F /* HistoryLayout.swift */; };
 		CC6C406E2ACDD69E009B8058 /* RawFetchedProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */; };
+		CC76E94C2BD471BA008BEB61 /* Forecast+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC76E9482BD471BA008BEB61 /* Forecast+CoreDataClass.swift */; };
+		CC76E94D2BD471BA008BEB61 /* Forecast+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC76E9492BD471BA008BEB61 /* Forecast+CoreDataProperties.swift */; };
+		CC76E94E2BD471BA008BEB61 /* ForecastValue+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC76E94A2BD471BA008BEB61 /* ForecastValue+CoreDataClass.swift */; };
+		CC76E94F2BD471BA008BEB61 /* ForecastValue+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC76E94B2BD471BA008BEB61 /* ForecastValue+CoreDataProperties.swift */; };
+		CC76E9512BD4812E008BEB61 /* Forecast+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC76E9502BD4812E008BEB61 /* Forecast+helper.swift */; };
 		CD78BB94E43B249D60CC1A1B /* NotificationsConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22963BD06A9C83959D4914E4 /* NotificationsConfigRootView.swift */; };
 		CE1856F52ADC4858007E39C7 /* AddCarbPresetIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1856F42ADC4858007E39C7 /* AddCarbPresetIntent.swift */; };
 		CE1856F72ADC4869007E39C7 /* CarbPresetIntentRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1856F62ADC4869007E39C7 /* CarbPresetIntentRequest.swift */; };
@@ -993,6 +998,11 @@
 		C8D1A7CA8C10C4403D4BBFA7 /* BolusDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusDataFlow.swift; sourceTree = "<group>"; };
 		CC41E2992B1E1F460070974F /* HistoryLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryLayout.swift; sourceTree = "<group>"; };
 		CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawFetchedProfile.swift; sourceTree = "<group>"; };
+		CC76E9482BD471BA008BEB61 /* Forecast+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		CC76E9492BD471BA008BEB61 /* Forecast+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
+		CC76E94A2BD471BA008BEB61 /* ForecastValue+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForecastValue+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
+		CC76E94B2BD471BA008BEB61 /* ForecastValue+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ForecastValue+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
+		CC76E9502BD4812E008BEB61 /* Forecast+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Forecast+helper.swift"; sourceTree = "<group>"; };
 		CE1856F42ADC4858007E39C7 /* AddCarbPresetIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCarbPresetIntent.swift; sourceTree = "<group>"; };
 		CE1856F62ADC4869007E39C7 /* CarbPresetIntentRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbPresetIntentRequest.swift; sourceTree = "<group>"; };
 		CE2FAD39297D93F0001A872C /* BloodGlucoseExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloodGlucoseExtensions.swift; sourceTree = "<group>"; };
@@ -2132,6 +2142,10 @@
 		5825D1052BD4056700F36E9B /* Classes+Properties */ = {
 			isa = PBXGroup;
 			children = (
+				CC76E9482BD471BA008BEB61 /* Forecast+CoreDataClass.swift */,
+				CC76E9492BD471BA008BEB61 /* Forecast+CoreDataProperties.swift */,
+				CC76E94A2BD471BA008BEB61 /* ForecastValue+CoreDataClass.swift */,
+				CC76E94B2BD471BA008BEB61 /* ForecastValue+CoreDataProperties.swift */,
 				5825D1062BD4058F00F36E9B /* BGaverages+CoreDataClass.swift */,
 				5825D1072BD4058F00F36E9B /* BGaverages+CoreDataProperties.swift */,
 				5825D1082BD4058F00F36E9B /* BGmedian+CoreDataClass.swift */,
@@ -2191,6 +2205,7 @@
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
 				5837A52F2BD2E3C700A5DC04 /* MealsStored+helper.swift */,
 				5837A5312BD2E81100A5DC04 /* InsulinStored+helper.swift */,
+				CC76E9502BD4812E008BEB61 /* Forecast+helper.swift */,
 			);
 			path = Helper;
 			sourceTree = "<group>";
@@ -2944,6 +2959,10 @@
 				5825D1452BD4058F00F36E9B /* Autosens_+CoreDataProperties.swift in Sources */,
 				19DC678529CA67A400FD9EC4 /* OverrideProfilesRootView.swift in Sources */,
 				5837A5302BD2E3C700A5DC04 /* MealsStored+helper.swift in Sources */,
+				CC76E94C2BD471BA008BEB61 /* Forecast+CoreDataClass.swift in Sources */,
+				CC76E94D2BD471BA008BEB61 /* Forecast+CoreDataProperties.swift in Sources */,
+				CC76E94E2BD471BA008BEB61 /* ForecastValue+CoreDataClass.swift in Sources */,
+				CC76E94F2BD471BA008BEB61 /* ForecastValue+CoreDataProperties.swift in Sources */,
 				389A572026079BAA00BC102F /* Interpolation.swift in Sources */,
 				19A910382A24EF3200C8951B /* ChartsView.swift in Sources */,
 				38B4F3C625E5017E00E76A18 /* NotificationCenter.swift in Sources */,
@@ -3090,6 +3109,7 @@
 				38E98A3725F5509500C0CED0 /* String+Extensions.swift in Sources */,
 				5825D13C2BD4058F00F36E9B /* ImportError+CoreDataClass.swift in Sources */,
 				5825D13F2BD4058F00F36E9B /* TDD+CoreDataProperties.swift in Sources */,
+				CC76E9512BD4812E008BEB61 /* Forecast+helper.swift in Sources */,
 				F90692D1274B99B60037068D /* HealthKitProvider.swift in Sources */,
 				19F95FF729F10FEE00314DDC /* StatStateModel.swift in Sources */,
 				5825D1432BD4058F00F36E9B /* Presets+CoreDataProperties.swift in Sources */,

+ 84 - 36
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift

@@ -85,42 +85,60 @@ final class OpenAPS {
 
                     // save to core data asynchronously
                     self.viewContext.perform {
-                        let new = OrefDetermination(context: self.viewContext)
-                        new.id = UUID()
-                        new.totalDailyDose = determination.tdd as? NSDecimalNumber
-                        new.insulinSensitivity = determination.isf as? NSDecimalNumber
-                        new.currentTarget = determination.current_target as? NSDecimalNumber
-                        new.eventualBG = determination.eventualBG as? NSDecimalNumber
-                        new.deliverAt = determination.deliverAt
-                        new.enacted = false
-                        new.insulinForManualBolus = determination.insulinForManualBolus as? NSDecimalNumber
-                        new.carbRatio = determination.carbRatio as? NSDecimalNumber
-                        new.glucose = determination.bg as? NSDecimalNumber
-                        new.reservoir = determination.reservoir as? NSDecimalNumber
-                        new.insulinReq = determination.insulinReq as? NSDecimalNumber
-                        new.temp = determination.temp?.rawValue ?? "absolute"
-                        new.rate = determination.rate as? NSDecimalNumber
-                        new.reason = determination.reason
-                        new.duration = Int16(determination.duration ?? 0)
-                        new.iob = determination.iob as? NSDecimalNumber
-                        new.treshold = determination.treshold as? NSDecimalNumber
-//                                                new.predBGs: [
-//                                                "zt": immutableDetermination.predictions?.zt ?? [],
-//                                                "cob": immutableDetermination.predictions?.cob ?? [],
-//                                                "iob": immutableDetermination.predictions?.iob ?? [],
-//                                                "uam": immutableDetermination.predictions?.uam ?? []
-//                                            ]
-                        new.minDelta = determination.minDelta as? NSDecimalNumber
-                        new.sensitivityRatio = determination.sensitivityRatio as? NSDecimalNumber
-                        new.expectedDelta = determination.expectedDelta as? NSDecimalNumber
-                        new.cob = Int16(Int(determination.cob ?? 0))
-                        new.manualBolusErrorString = determination.manualBolusErrorString as? NSDecimalNumber
-                        new.timestampEnacted = nil
-                        new.tempBasal = determination.insulin?.temp_basal as? NSDecimalNumber
-                        new.scheduledBasal = determination.insulin?.scheduled_basal as? NSDecimalNumber
-                        new.bolus = determination.insulin?.bolus as? NSDecimalNumber
-                        new.smbToDeliver = determination.units as? NSDecimalNumber
-                        new.carbsRequired = Int16(Int(determination.carbsReq ?? 0))
+                        let newOrefDetermination = OrefDetermination(context: self.viewContext)
+                        newOrefDetermination.id = UUID()
+                        newOrefDetermination.totalDailyDose = determination.tdd as? NSDecimalNumber
+                        newOrefDetermination.insulinSensitivity = determination.isf as? NSDecimalNumber
+                        newOrefDetermination.currentTarget = determination.current_target as? NSDecimalNumber
+                        newOrefDetermination.eventualBG = determination.eventualBG as? NSDecimalNumber
+                        newOrefDetermination.deliverAt = determination.deliverAt
+                        newOrefDetermination.enacted = false
+                        newOrefDetermination.insulinForManualBolus = determination.insulinForManualBolus as? NSDecimalNumber
+                        newOrefDetermination.carbRatio = determination.carbRatio as? NSDecimalNumber
+                        newOrefDetermination.glucose = determination.bg as? NSDecimalNumber
+                        newOrefDetermination.reservoir = determination.reservoir as? NSDecimalNumber
+                        newOrefDetermination.insulinReq = determination.insulinReq as? NSDecimalNumber
+                        newOrefDetermination.temp = determination.temp?.rawValue ?? "absolute"
+                        newOrefDetermination.rate = determination.rate as? NSDecimalNumber
+                        newOrefDetermination.reason = determination.reason
+                        newOrefDetermination.duration = Int16(determination.duration ?? 0)
+                        newOrefDetermination.iob = determination.iob as? NSDecimalNumber
+                        newOrefDetermination.treshold = determination.treshold as? NSDecimalNumber
+
+                        if let predictions = determination.predictions {
+                            // Create forecasts for each type
+                            ["iob": predictions.iob, "zt": predictions.zt, "cob": predictions.cob, "uam": predictions.uam]
+                                .forEach { type, values in
+                                    if let values = values {
+                                        let forecast = Forecast(context: self.viewContext)
+                                        forecast.type = type
+                                        forecast.date = Date() // or use a specific timestamp if available
+                                        forecast
+                                            .orefDetermination =
+                                            newOrefDetermination // Assuming a relationship from Forecast to OrefDetermination
+
+                                        for (index, value) in values.enumerated() {
+                                            let forecastValue = ForecastValue(context: self.viewContext)
+                                            forecastValue.index = Int32(index)
+                                            forecastValue.value = Int32(value)
+                                            forecast.addToForecastValues(forecastValue)
+                                        }
+                                        newOrefDetermination.addToForecasts(forecast)
+                                    }
+                                }
+                        }
+
+                        newOrefDetermination.minDelta = determination.minDelta as? NSDecimalNumber
+                        newOrefDetermination.sensitivityRatio = determination.sensitivityRatio as? NSDecimalNumber
+                        newOrefDetermination.expectedDelta = determination.expectedDelta as? NSDecimalNumber
+                        newOrefDetermination.cob = Int16(Int(determination.cob ?? 0))
+                        newOrefDetermination.manualBolusErrorString = determination.manualBolusErrorString as? NSDecimalNumber
+                        newOrefDetermination.timestampEnacted = nil
+                        newOrefDetermination.tempBasal = determination.insulin?.temp_basal as? NSDecimalNumber
+                        newOrefDetermination.scheduledBasal = determination.insulin?.scheduled_basal as? NSDecimalNumber
+                        newOrefDetermination.bolus = determination.insulin?.bolus as? NSDecimalNumber
+                        newOrefDetermination.smbToDeliver = determination.units as? NSDecimalNumber
+                        newOrefDetermination.carbsRequired = Int16(Int(determination.carbsReq ?? 0))
                         do {
                             try self.viewContext.save()
                             debugPrint(
@@ -702,4 +720,34 @@ final class OpenAPS {
         }
         return (try? String(contentsOf: url)) ?? ""
     }
+
+    func processAndSave(forecastData: [String: [Int]]) {
+        let context = viewContext
+
+        let currentDate = Date()
+
+        for (type, values) in forecastData {
+            createForecast(type: type, values: values, date: currentDate, context: context)
+        }
+    }
+
+    func createForecast(type: String, values: [Int], date: Date, context: NSManagedObjectContext) {
+        let forecast = Forecast(context: context)
+        forecast.id = UUID()
+        forecast.date = date
+        forecast.type = type
+
+        for (index, value) in values.enumerated() {
+            let forecastValue = ForecastValue(context: context)
+            forecastValue.value = Int32(value)
+            forecastValue.index = Int32(index)
+            forecastValue.forecast = forecast
+        }
+
+        do {
+            try context.save()
+        } catch {
+            print("Failed to save forecast: \(error)")
+        }
+    }
 }

+ 48 - 66
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -115,6 +115,11 @@ struct MainChartView: View {
         animation: Animation.bouncy
     ) var determinations: FetchedResults<OrefDetermination>
 
+    @FetchRequest(
+        fetchRequest: Forecast.fetch(NSPredicate.forecastsForChart, sortedBy: "date", ascending: true),
+        animation: .default
+    ) var forecasts: FetchedResults<Forecast>
+
     private var bolusFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
@@ -227,7 +232,7 @@ extension MainChartView {
                 drawFpus()
                 drawBoluses()
                 drawTempTargets()
-                drawPredictions()
+//                drawPredictions()
                 drawGlucose()
                 drawManualGlucose()
 
@@ -252,7 +257,7 @@ extension MainChartView {
             }
             .id("MainChart")
             .onChange(of: glucose) { _ in
-                calculatePredictions()
+//                calculatePredictions()
             }
             .onChange(of: boluses) { _ in
                 state.roundedTotalBolus = state.calculateTINS()
@@ -261,17 +266,17 @@ extension MainChartView {
                 calculateTTs()
             }
             .onChange(of: didAppearTrigger) { _ in
-                calculatePredictions()
+//                calculatePredictions()
                 calculateTTs()
             }
             .onChange(of: determinations.map(\.id)) { _ in
-                calculatePredictions()
+//                calculatePredictions()
             }
             .onReceive(
                 Foundation.NotificationCenter.default
                     .publisher(for: UIApplication.willEnterForegroundNotification)
             ) { _ in
-                calculatePredictions()
+//                calculatePredictions()
             }
             .frame(minHeight: UIScreen.main.bounds.height * 0.2)
             .frame(width: fullWidth(viewWidth: screenSize.width))
@@ -464,42 +469,34 @@ extension MainChartView {
         }
     }
 
-    private func drawPredictions() -> some ChartContent {
-        /// predictions
-        ForEach(Predictions, id: \.self) { info in
-            let y = max(info.amount, 0)
-
-            if info.type == .uam {
-                LineMark(
-                    x: .value("Time", info.timestamp, unit: .second),
-                    y: .value("Value", Decimal(y) * conversionFactor),
-                    series: .value("uam", "uam")
-                ).foregroundStyle(Color.uam).symbolSize(16)
-            }
-            if info.type == .cob {
-                LineMark(
-                    x: .value("Time", info.timestamp, unit: .second),
-                    y: .value("Value", Decimal(y) * conversionFactor),
-                    series: .value("cob", "cob")
-                ).foregroundStyle(Color.orange).symbolSize(16)
-            }
-            if info.type == .iob {
-                LineMark(
-                    x: .value("Time", info.timestamp, unit: .second),
-                    y: .value("Value", Decimal(y) * conversionFactor),
-                    series: .value("iob", "iob")
-                ).foregroundStyle(Color.insulin).symbolSize(16)
-            }
-            if info.type == .zt {
+    private func drawPredictions() -> some View {
+        ForEach(forecasts, id: \.self) { forecast in
+            ForEach(forecast.forecastValuesArray) { value in
                 LineMark(
-                    x: .value("Time", info.timestamp, unit: .second),
-                    y: .value("Value", Decimal(y) * conversionFactor),
-                    series: .value("zt", "zt")
-                ).foregroundStyle(Color.zt).symbolSize(16)
+                    x: .value("Time", value.timestamp),
+                    y: .value("Value", Decimal(value.value) * conversionFactor)
+                    //                    series: .value("Series", forecast.type)
+                )
+                //                .foregroundStyle(colorForType(PredictionType(rawValue: forecast.type ?? "") ?? .unknown))
             }
         }
     }
 
+    private func colorForType(_ type: PredictionType) -> Color {
+        switch type {
+        case .uam:
+            return .uam
+        case .cob:
+            return .orange
+        case .iob:
+            return .insulin
+        case .zt:
+            return .zt
+        default:
+            return .gray // Default color for unknown types
+        }
+    }
+
     private func drawCurrentTimeMarker() -> some ChartContent {
         RuleMark(
             x: .value(
@@ -744,36 +741,6 @@ extension MainChartView {
         ChartTempTargets = calculatedTTs
     }
 
-    private func addPredictions(_ predictions: [Int], type: PredictionType, deliveredAt: Date, endMarker: Date) -> [Prediction] {
-        var calculatedPredictions: [Prediction] = []
-        predictions.indices.forEach { index in
-            let predTime = Date(
-                timeIntervalSince1970: deliveredAt.timeIntervalSince1970 + TimeInterval(index) * 5.minutes.timeInterval
-            )
-            if predTime.timeIntervalSince1970 < endMarker.timeIntervalSince1970 {
-                calculatedPredictions.append(
-                    Prediction(amount: predictions[index], timestamp: predTime, type: type)
-                )
-            }
-        }
-        return calculatedPredictions
-    }
-
-    private func calculatePredictions() {
-//        guard let suggestion = suggestion, let deliveredAt = suggestion.deliverAt else { return }
-//        let uamPredictions = suggestion.predictions?.uam ?? []
-//        let iobPredictions = suggestion.predictions?.iob ?? []
-//        let cobPredictions = suggestion.predictions?.cob ?? []
-//        let ztPredictions = suggestion.predictions?.zt ?? []
-//
-//        let uam = addPredictions(uamPredictions, type: .uam, deliveredAt: deliveredAt, endMarker: endMarker)
-//        let iob = addPredictions(iobPredictions, type: .iob, deliveredAt: deliveredAt, endMarker: endMarker)
-//        let cob = addPredictions(cobPredictions, type: .cob, deliveredAt: deliveredAt, endMarker: endMarker)
-//        let zt = addPredictions(ztPredictions, type: .zt, deliveredAt: deliveredAt, endMarker: endMarker)
-//
-//        Predictions = uam + iob + cob + zt
-    }
-
     private func calculateTempBasals() {
         let basals = tempBasals
         var returnTempBasalRates: [PumpHistoryEvent] = []
@@ -800,6 +767,21 @@ extension MainChartView {
         TempBasals = returnTempBasalRates
     }
 
+    private func addPredictions(_ predictions: [Int], type: PredictionType, deliveredAt: Date, endMarker: Date) -> [Prediction] {
+        var calculatedPredictions: [Prediction] = []
+        predictions.indices.forEach { index in
+            let predTime = Date(
+                timeIntervalSince1970: deliveredAt.timeIntervalSince1970 + TimeInterval(index) * 5.minutes.timeInterval
+            )
+            if predTime.timeIntervalSince1970 < endMarker.timeIntervalSince1970 {
+                calculatedPredictions.append(
+                    Prediction(amount: predictions[index], timestamp: predTime, type: type)
+                )
+            }
+        }
+        return calculatedPredictions
+    }
+
     private func findRegularBasalPoints(
         timeBegin: TimeInterval,
         timeEnd: TimeInterval,

+ 14 - 1
Model/Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22222" systemVersion="22G120" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22757" systemVersion="23E224" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
     <entity name="Autosens_" representedClassName="Autosens_" syncable="YES">
         <attribute name="newisf" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="ratio" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
@@ -25,6 +25,18 @@
         <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="Forecast" representedClassName="Forecast" syncable="YES">
+        <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
+        <attribute name="type" optional="YES" attributeType="String"/>
+        <relationship name="forecastValues" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="ForecastValue" inverseName="forecast" inverseEntity="ForecastValue"/>
+        <relationship name="orefDetermination" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="OrefDetermination" inverseName="forecasts" inverseEntity="OrefDetermination"/>
+    </entity>
+    <entity name="ForecastValue" representedClassName="ForecastValue" syncable="YES">
+        <attribute name="index" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="value" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
+        <relationship name="forecast" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Forecast" inverseName="forecastValues" inverseEntity="Forecast"/>
+    </entity>
     <entity name="GlucoseStored" representedClassName="GlucoseStored" syncable="YES">
         <attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="direction" optional="YES" attributeType="String"/>
@@ -113,6 +125,7 @@
         <attribute name="timestampEnacted" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
         <attribute name="totalDailyDose" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
         <attribute name="treshold" optional="YES" attributeType="Decimal" defaultValueString="0.0"/>
+        <relationship name="forecasts" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Forecast" inverseName="orefDetermination" inverseEntity="Forecast"/>
     </entity>
     <entity name="Override" representedClassName="Override" syncable="YES">
         <attribute name="advancedSettings" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>

+ 16 - 0
Model/Helper/Forecast+helper.swift

@@ -0,0 +1,16 @@
+import CoreData
+import Foundation
+
+public extension Forecast {
+    static func fetch(_ predicate: NSPredicate, sortedBy keyPath: String, ascending: Bool) -> NSFetchRequest<Forecast> {
+        let request = NSFetchRequest<Forecast>(entityName: "Forecast")
+        request.sortDescriptors = [NSSortDescriptor(key: keyPath, ascending: ascending)]
+        request.predicate = predicate
+        return request
+    }
+
+    var forecastValuesArray: [ForecastValue] {
+        let set = forecastValues as? Set<ForecastValue> ?? []
+        return set.sorted { $0.index < $1.index }
+    }
+}

+ 5 - 0
Model/Helper/NSPredicates.swift

@@ -75,4 +75,9 @@ extension NSPredicate {
         let date = Date.threeMonthsAgo
         return NSPredicate(format: "date >= %@", date as NSDate)
     }
+
+    static var forecastsForChart: NSPredicate {
+        let oneDayAgo = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
+        return NSPredicate(format: "date >= %@", oneDayAgo as NSDate)
+    }
 }

+ 17 - 0
OrefDetermination+CoreDataProperties.swift

@@ -37,6 +37,23 @@ public extension OrefDetermination {
     @NSManaged var timestampEnacted: Date?
     @NSManaged var totalDailyDose: NSDecimalNumber?
     @NSManaged var treshold: NSDecimalNumber?
+    @NSManaged var forecasts: Set<Forecast>?
+}
+
+// MARK: Generated accessors for forecasts
+
+public extension OrefDetermination {
+    @objc(addForecastsObject:)
+    @NSManaged func addToForecasts(_ value: Forecast)
+
+    @objc(removeForecastsObject:)
+    @NSManaged func removeFromForecasts(_ value: Forecast)
+
+    @objc(addForecasts:)
+    @NSManaged func addToForecasts(_ values: NSSet)
+
+    @objc(removeForecasts:)
+    @NSManaged func removeFromForecasts(_ values: NSSet)
 }
 
 extension OrefDetermination: Identifiable {}