DeterminationStorage.swift 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import CoreData
  2. import Foundation
  3. import Swinject
  4. protocol DeterminationStorage {
  5. func fetchLastDeterminationObjectID(predicate: NSPredicate) async -> [NSManagedObjectID]
  6. func getForecastIDs(for determinationID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID]
  7. func getForecastValueIDs(for forecastID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID]
  8. func getOrefDeterminationNotYetUploadedToNightscout(_ determinationIds: [NSManagedObjectID]) async -> Determination?
  9. }
  10. final class BaseDeterminationStorage: DeterminationStorage, Injectable {
  11. private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
  12. private let backgroundContext = CoreDataStack.shared.newTaskContext()
  13. init(resolver: Resolver) {
  14. injectServices(resolver)
  15. }
  16. func fetchLastDeterminationObjectID(predicate: NSPredicate) async -> [NSManagedObjectID] {
  17. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  18. ofType: OrefDetermination.self,
  19. onContext: backgroundContext,
  20. predicate: predicate,
  21. key: "deliverAt",
  22. ascending: false,
  23. fetchLimit: 1
  24. )
  25. return await backgroundContext.perform {
  26. results.map(\.objectID)
  27. }
  28. }
  29. func getForecastIDs(for determinationID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID] {
  30. await context.perform {
  31. do {
  32. guard let determination = try context.existingObject(with: determinationID) as? OrefDetermination,
  33. let forecastSet = determination.forecasts as? Set<NSManagedObject>
  34. else {
  35. return []
  36. }
  37. let forecasts = Array(forecastSet)
  38. return forecasts.map(\.objectID) as [NSManagedObjectID]
  39. } catch {
  40. debugPrint(
  41. "Failed \(DebuggingIdentifiers.failed) to fetch Forecast IDs for OrefDetermination with ID \(determinationID): \(error.localizedDescription)"
  42. )
  43. return []
  44. }
  45. }
  46. }
  47. func getForecastValueIDs(for forecastID: NSManagedObjectID, in context: NSManagedObjectContext) async -> [NSManagedObjectID] {
  48. await context.perform {
  49. do {
  50. guard let forecast = try context.existingObject(with: forecastID) as? Forecast,
  51. let forecastValueSet = forecast.forecastValues
  52. else {
  53. return []
  54. }
  55. let forecastValues = forecastValueSet.sorted(by: { $0.index < $1.index })
  56. return forecastValues.map(\.objectID)
  57. } catch {
  58. debugPrint(
  59. "Failed \(DebuggingIdentifiers.failed) to fetch Forecast Value IDs with ID \(forecastID): \(error.localizedDescription)"
  60. )
  61. return []
  62. }
  63. }
  64. }
  65. // Convert NSDecimalNumber to Decimal
  66. func decimal(from nsDecimalNumber: NSDecimalNumber?) -> Decimal {
  67. nsDecimalNumber?.decimalValue ?? 0.0
  68. }
  69. // Convert NSSet to array of Ints for Predictions
  70. func parseForecastValues(ofType _: String, from determinationID: NSManagedObjectID) async -> [Int]? {
  71. let forecastIDs = await getForecastIDs(for: determinationID, in: backgroundContext)
  72. var forecastValuesList: [Int] = []
  73. for forecastID in forecastIDs {
  74. let forecastValueIDs = await getForecastValueIDs(for: forecastID, in: backgroundContext)
  75. await backgroundContext.perform {
  76. for forecastValueID in forecastValueIDs {
  77. if let forecastValue = try? self.backgroundContext.existingObject(with: forecastValueID) as? ForecastValue {
  78. let forecastValueInt = Int(forecastValue.value)
  79. forecastValuesList.append(forecastValueInt)
  80. }
  81. }
  82. }
  83. }
  84. return forecastValuesList
  85. }
  86. func getOrefDeterminationNotYetUploadedToNightscout(_ determinationIds: [NSManagedObjectID]) async -> Determination? {
  87. var result: Determination?
  88. guard let determinationId = determinationIds.first else {
  89. print("No determination ID found.")
  90. return nil
  91. }
  92. print("Using context: \(backgroundContext)")
  93. print("Determination ID: \(determinationId)")
  94. let predictions = Predictions(
  95. iob: await parseForecastValues(ofType: "iob", from: determinationId),
  96. zt: await parseForecastValues(ofType: "zt", from: determinationId),
  97. cob: await parseForecastValues(ofType: "cob", from: determinationId),
  98. uam: await parseForecastValues(ofType: "uam", from: determinationId)
  99. )
  100. return await backgroundContext.perform {
  101. do {
  102. let orefDetermination = try self.backgroundContext.existingObject(with: determinationId) as? OrefDetermination
  103. // Log the type of the fetched object
  104. print("Fetched object type: \(type(of: orefDetermination))")
  105. print("Fetched object description: \(orefDetermination)")
  106. // Check if the fetched object is of the expected type
  107. if let orefDetermination = orefDetermination {
  108. print("Successfully cast to OrefDetermination")
  109. let forecastSet = orefDetermination.forecasts
  110. print("Fetched forecast set: \(forecastSet)")
  111. result = Determination(
  112. id: orefDetermination.id ?? UUID(),
  113. reason: orefDetermination.reason ?? "",
  114. units: orefDetermination.smbToDeliver as Decimal?,
  115. insulinReq: self.decimal(from: orefDetermination.insulinReq),
  116. eventualBG: orefDetermination.eventualBG as? Int,
  117. sensitivityRatio: self.decimal(from: orefDetermination.sensitivityRatio),
  118. rate: self.decimal(from: orefDetermination.rate),
  119. duration: self.decimal(from: orefDetermination.duration),
  120. iob: self.decimal(from: orefDetermination.iob),
  121. cob: orefDetermination.cob != 0 ? Decimal(orefDetermination.cob) : nil,
  122. predictions: predictions,
  123. deliverAt: orefDetermination.deliverAt,
  124. carbsReq: orefDetermination.carbsRequired != 0 ? Decimal(orefDetermination.carbsRequired) : nil,
  125. temp: TempType(rawValue: orefDetermination.temp ?? "absolute"),
  126. bg: self.decimal(from: orefDetermination.glucose),
  127. reservoir: self.decimal(from: orefDetermination.reservoir),
  128. isf: self.decimal(from: orefDetermination.insulinSensitivity),
  129. timestamp: orefDetermination.timestamp,
  130. tdd: self.decimal(from: orefDetermination.totalDailyDose),
  131. insulin: nil,
  132. current_target: self.decimal(from: orefDetermination.currentTarget),
  133. insulinForManualBolus: self.decimal(from: orefDetermination.insulinForManualBolus),
  134. manualBolusErrorString: self.decimal(from: orefDetermination.manualBolusErrorString),
  135. minDelta: self.decimal(from: orefDetermination.minDelta),
  136. expectedDelta: self.decimal(from: orefDetermination.expectedDelta),
  137. minGuardBG: nil,
  138. minPredBG: nil,
  139. threshold: self.decimal(from: orefDetermination.threshold),
  140. carbRatio: self.decimal(from: orefDetermination.carbRatio),
  141. received: orefDetermination.enacted // this is actually part of NS...
  142. )
  143. } else {
  144. print("Fetched object is not of type OrefDetermination")
  145. }
  146. } catch {
  147. print("Failed to fetch managed object: \(error)")
  148. }
  149. return result
  150. }
  151. }
  152. }