PumpEvent+helper.swift 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import CoreData
  2. import Foundation
  3. extension PumpEventStored {
  4. static func fetch(_ predicate: NSPredicate, ascending: Bool, fetchLimit: Int? = nil) -> NSFetchRequest<PumpEventStored> {
  5. let request = PumpEventStored.fetchRequest()
  6. request.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: ascending)]
  7. request.resultType = .managedObjectResultType
  8. request.predicate = predicate
  9. if let fetchLimit = fetchLimit {
  10. request.fetchLimit = fetchLimit
  11. }
  12. return request
  13. }
  14. // Preview
  15. @discardableResult static func makePreviewEvents(count: Int, provider: CoreDataStack) -> [PumpEventStored] {
  16. let context = provider.persistentContainer.viewContext
  17. let events = (0 ..< count).map { index -> PumpEventStored in
  18. let event = PumpEventStored(context: context)
  19. event.id = UUID().uuidString
  20. event.timestamp = Date.now.addingTimeInterval(Double(index) * -300) // Every 5 minutes
  21. event.type = EventType.bolus.rawValue
  22. event.isUploadedToNS = false
  23. event.isUploadedToHealth = false
  24. event.isUploadedToTidepool = false
  25. // Add a bolus
  26. let bolus = BolusStored(context: context)
  27. bolus.amount = 2.5 as NSDecimalNumber
  28. bolus.isExternal = false
  29. bolus.isSMB = false
  30. bolus.pumpEvent = event
  31. return event
  32. }
  33. try? context.save()
  34. return events
  35. }
  36. }
  37. public extension PumpEventStored {
  38. enum EventType: String, JSON {
  39. case bolus = "Bolus"
  40. case smb = "SMB"
  41. case isExternal = "External Insulin"
  42. case mealBolus = "Meal Bolus"
  43. case correctionBolus = "Correction Bolus"
  44. case snackBolus = "Snack Bolus"
  45. case bolusWizard = "BolusWizard"
  46. case tempBasal = "TempBasal"
  47. case tempBasalDuration = "TempBasalDuration"
  48. case pumpSuspend = "PumpSuspend"
  49. case pumpResume = "PumpResume"
  50. case pumpAlarm = "PumpAlarm"
  51. case pumpBattery = "PumpBattery"
  52. case rewind = "Rewind"
  53. case prime = "Prime"
  54. case journalCarbs = "JournalEntryMealMarker"
  55. case siteChange = "SiteChange"
  56. case nsNote = "Note"
  57. case nsTempBasal = "Temp Basal"
  58. case nsCarbCorrection = "Carb Correction"
  59. case nsTempTarget = "Temporary Target"
  60. case nsInsulinChange = "Insulin Change"
  61. case nsSiteChange = "Site Change"
  62. case nsBatteryChange = "Pump Battery Change"
  63. case nsAnnouncement = "Announcement"
  64. case nsSensorChange = "Sensor Start"
  65. case nsExercise = "Exercise"
  66. case capillaryGlucose = "BG Check"
  67. }
  68. enum TempType: String, JSON {
  69. case absolute
  70. case percent
  71. }
  72. }
  73. extension NSPredicate {
  74. static var pumpHistoryLast1440Minutes: NSPredicate {
  75. let date = Date.oneDayAgoInMinutes
  76. return NSPredicate(format: "timestamp >= %@", date as NSDate)
  77. }
  78. static var pumpHistoryLast48h: NSPredicate {
  79. let date = Date() - TimeInterval(hours: 48)
  80. return NSPredicate(format: "timestamp >= %@", date as NSDate)
  81. }
  82. static var pumpHistoryLast24h: NSPredicate {
  83. let date = Date.oneDayAgo
  84. return NSPredicate(format: "timestamp >= %@", date as NSDate)
  85. }
  86. static var pumpHistoryForStats: NSPredicate {
  87. let date = Date.threeMonthsAgo
  88. return NSPredicate(format: "pumpEvent.timestamp >= %@", date as NSDate)
  89. }
  90. static var recentPumpHistory: NSPredicate {
  91. let date = Date.twentyMinutesAgo
  92. return NSPredicate(
  93. format: "type == %@ AND timestamp >= %@",
  94. PumpEventStored.EventType.tempBasal.rawValue,
  95. date as NSDate
  96. )
  97. }
  98. static var lastPumpBolus: NSPredicate {
  99. let date = Date.twentyMinutesAgo
  100. return NSPredicate(format: "timestamp >= %@ AND bolus.isExternal == %@", date as NSDate, false as NSNumber)
  101. }
  102. static func duplicates(_ date: Date) -> NSPredicate {
  103. NSPredicate(format: "timestamp == %@", date as NSDate)
  104. }
  105. static var pumpEventsNotYetUploadedToNightscout: NSPredicate {
  106. let date = Date.oneDayAgo
  107. return NSPredicate(format: "timestamp >= %@ AND isUploadedToNS == %@", date as NSDate, false as NSNumber)
  108. }
  109. static var pumpEventsNotYetUploadedToHealth: NSPredicate {
  110. let date = Date.oneDayAgo
  111. return NSPredicate(format: "timestamp >= %@ AND isUploadedToHealth == %@", date as NSDate, false as NSNumber)
  112. }
  113. static var pumpEventsNotYetUploadedToTidepool: NSPredicate {
  114. let date = Date.oneDayAgo
  115. return NSPredicate(format: "timestamp >= %@ AND isUploadedToTidepool == %@", date as NSDate, false as NSNumber)
  116. }
  117. }
  118. // MARK: - Native mapping
  119. extension PumpEventStored {
  120. /// Converts a stored pump event into the `PumpHistoryEvent`s the oref algorithm can use.
  121. /// A temp basal yields a duration entry followed by a rate entry.
  122. func toPumpHistoryEvents() -> [PumpHistoryEvent] {
  123. var events: [PumpHistoryEvent] = []
  124. if let bolus = toBolusPumpHistoryEvent() { events.append(bolus) }
  125. if let duration = toTempBasalDurationPumpHistoryEvent() { events.append(duration) }
  126. if let tempBasal = toTempBasalPumpHistoryEvent() { events.append(tempBasal) }
  127. if let suspend = toSuspendPumpHistoryEvent() { events.append(suspend) }
  128. if let resume = toResumePumpHistoryEvent() { events.append(resume) }
  129. if let rewind = toRewindPumpHistoryEvent() { events.append(rewind) }
  130. if let prime = toPrimePumpHistoryEvent() { events.append(prime) }
  131. return events
  132. }
  133. private func toBolusPumpHistoryEvent() -> PumpHistoryEvent? {
  134. guard let timestamp = timestamp, let bolus = bolus, let amount = bolus.amount else {
  135. return nil
  136. }
  137. return PumpHistoryEvent(
  138. id: id ?? UUID().uuidString,
  139. type: .bolus,
  140. timestamp: timestamp,
  141. amount: Decimal(algorithmValue: amount.doubleValue),
  142. duration: 0,
  143. isSMB: bolus.isSMB,
  144. isExternal: bolus.isExternal
  145. )
  146. }
  147. // The temp basal duration populates `durationMin`, not `duration`.
  148. private func toTempBasalDurationPumpHistoryEvent() -> PumpHistoryEvent? {
  149. guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal else {
  150. return nil
  151. }
  152. return PumpHistoryEvent(
  153. id: id,
  154. type: .tempBasalDuration,
  155. timestamp: timestamp,
  156. durationMin: Int(tempBasal.duration)
  157. )
  158. }
  159. // The temp basal rate entry id is prefixed with "_". An unrecognized `tempType` maps to nil.
  160. private func toTempBasalPumpHistoryEvent() -> PumpHistoryEvent? {
  161. guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal, let rate = tempBasal.rate else {
  162. return nil
  163. }
  164. return PumpHistoryEvent(
  165. id: "_\(id)",
  166. type: .tempBasal,
  167. timestamp: timestamp,
  168. rate: Decimal(algorithmValue: rate.doubleValue),
  169. temp: tempBasal.tempType.flatMap { Trio.TempType(rawValue: $0) }
  170. )
  171. }
  172. private func toSuspendPumpHistoryEvent() -> PumpHistoryEvent? {
  173. guard let id = id, let timestamp = timestamp, type == EventType.pumpSuspend.rawValue else {
  174. return nil
  175. }
  176. return PumpHistoryEvent(id: id, type: .pumpSuspend, timestamp: timestamp)
  177. }
  178. private func toResumePumpHistoryEvent() -> PumpHistoryEvent? {
  179. guard let id = id, let timestamp = timestamp, type == EventType.pumpResume.rawValue else {
  180. return nil
  181. }
  182. return PumpHistoryEvent(id: id, type: .pumpResume, timestamp: timestamp)
  183. }
  184. private func toRewindPumpHistoryEvent() -> PumpHistoryEvent? {
  185. guard let id = id, let timestamp = timestamp, type == EventType.rewind.rawValue else {
  186. return nil
  187. }
  188. return PumpHistoryEvent(id: id, type: .rewind, timestamp: timestamp)
  189. }
  190. private func toPrimePumpHistoryEvent() -> PumpHistoryEvent? {
  191. guard let id = id, let timestamp = timestamp, type == EventType.prime.rawValue else {
  192. return nil
  193. }
  194. return PumpHistoryEvent(id: id, type: .prime, timestamp: timestamp)
  195. }
  196. }