PumpEvent+helper.swift 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. }
  15. public extension PumpEventStored {
  16. enum EventType: String, JSON {
  17. case bolus = "Bolus"
  18. case smb = "SMB"
  19. case isExternal = "External Insulin"
  20. case mealBolus = "Meal Bolus"
  21. case correctionBolus = "Correction Bolus"
  22. case snackBolus = "Snack Bolus"
  23. case bolusWizard = "BolusWizard"
  24. case tempBasal = "TempBasal"
  25. case tempBasalDuration = "TempBasalDuration"
  26. case pumpSuspend = "PumpSuspend"
  27. case pumpResume = "PumpResume"
  28. case pumpAlarm = "PumpAlarm"
  29. case pumpBattery = "PumpBattery"
  30. case rewind = "Rewind"
  31. case prime = "Prime"
  32. case journalCarbs = "JournalEntryMealMarker"
  33. case nsTempBasal = "Temp Basal"
  34. case nsCarbCorrection = "Carb Correction"
  35. case nsTempTarget = "Temporary Target"
  36. case nsInsulinChange = "Insulin Change"
  37. case nsSiteChange = "Site Change"
  38. case nsBatteryChange = "Pump Battery Change"
  39. case nsAnnouncement = "Announcement"
  40. case nsSensorChange = "Sensor Start"
  41. case capillaryGlucose = "BG Check"
  42. }
  43. enum TempType: String, JSON {
  44. case absolute
  45. case percent
  46. }
  47. }
  48. extension NSPredicate {
  49. static var pumpHistoryLast24h: NSPredicate {
  50. let date = Date.oneDayAgo
  51. return NSPredicate(format: "timestamp >= %@", date as NSDate)
  52. }
  53. }
  54. // extension PumpEventStored: Encodable {
  55. // enum CodingKeys: String, CodingKey {
  56. // // pump event CD entitiy
  57. // case id
  58. // case timestamp
  59. // case type
  60. // // bolus CD entitity
  61. // case amount
  62. // case isSMB
  63. // case isExternal
  64. // // temp basal CD entity
  65. // case duration
  66. // case rate
  67. // case temp
  68. // }
  69. //
  70. // public func encode(to encoder: Encoder) throws {
  71. // var containers = encoder.unkeyedContainer()
  72. //
  73. // let dateFormatter = ISO8601DateFormatter()
  74. // let formattedDate = dateFormatter.string(from: timestamp ?? Date())
  75. //
  76. // if let tempBasal = self.tempBasal {
  77. // // TempBasalDuration
  78. // var tempBasalDurationContainer = containers.nestedContainer(keyedBy: CodingKeys.self)
  79. // try tempBasalDurationContainer.encode("TempBasalDuration", forKey: .type)
  80. // try tempBasalDurationContainer.encode(tempBasal.duration, forKey: .duration)
  81. // try tempBasalDurationContainer.encode(formattedDate, forKey: .timestamp)
  82. // try tempBasalDurationContainer.encode(id ?? UUID().uuidString, forKey: .id)
  83. //
  84. // // TempBasal
  85. // var tempBasalContainer = containers.nestedContainer(keyedBy: CodingKeys.self)
  86. // try tempBasalContainer.encode("TempBasal", forKey: .type)
  87. // if let rate = tempBasal.rate as Decimal? {
  88. // try tempBasalContainer.encode(rate, forKey: .rate)
  89. // } else {
  90. // try tempBasalContainer.encode(0, forKey: .rate)
  91. // }
  92. // // its called "temp" in the json thats passed into determineBasal hence the undescriptive name of this coding key
  93. // if let tempType = tempBasal.tempType {
  94. // try tempBasalContainer.encode(tempType, forKey: .temp)
  95. // } else {
  96. // try tempBasalContainer.encode("absolute", forKey: .temp)
  97. // }
  98. // try tempBasalContainer.encode(formattedDate, forKey: .timestamp)
  99. // // TempBasal and TempBasalDuration need to "relate" in the JSON, use same ID and prepemd with "_" here
  100. // try tempBasalContainer.encode("_\(id ?? UUID().uuidString)", forKey: .id)
  101. // }
  102. //
  103. // // Encode specific to Bolus
  104. // if let bolus = self.bolus {
  105. // var bolusContainer = containers.nestedContainer(keyedBy: CodingKeys.self)
  106. // try bolusContainer.encode("Bolus", forKey: .type)
  107. // if let bolusAmount = bolus.amount as Decimal? {
  108. // try bolusContainer.encode(bolusAmount, forKey: .amount)
  109. // } else {
  110. // // Default value
  111. // try bolusContainer.encode(Decimal(0), forKey: .amount)
  112. // }
  113. // try bolusContainer.encode(bolus.isSMB, forKey: .isSMB)
  114. // try bolusContainer.encode(bolus.isExternal, forKey: .isExternal)
  115. // try bolusContainer.encode(formattedDate, forKey: .timestamp)
  116. // try bolusContainer.encode(id ?? UUID().uuidString, forKey: .id)
  117. // }
  118. // }
  119. // }
  120. // Declare helper structs ("data transfer objects" = DTO) to utilize parsing a flattened pump history
  121. struct BolusDTO: Codable {
  122. var id: String
  123. var timestamp: String
  124. var amount: Double
  125. var isExternal: Bool
  126. var isSMB: Bool
  127. var duration: Int
  128. var _type: String = "Bolus"
  129. }
  130. struct TempBasalDTO: Codable {
  131. var id: String
  132. var timestamp: String
  133. var temp: String
  134. var rate: Double
  135. var _type: String = "TempBasal"
  136. }
  137. struct TempBasalDurationDTO: Codable {
  138. var id: String
  139. var timestamp: String
  140. var duration: Int
  141. var _type: String = "TempBasalDuration"
  142. private enum CodingKeys: String, CodingKey {
  143. case id
  144. case timestamp
  145. case duration = "duration (min)"
  146. case _type
  147. }
  148. }
  149. // Mask distinct DTO subtypes with a common enum that conforms to Encodable
  150. enum PumpEventDTO: Encodable {
  151. case bolus(BolusDTO)
  152. case tempBasal(TempBasalDTO)
  153. case tempBasalDuration(TempBasalDurationDTO)
  154. func encode(to encoder: Encoder) throws {
  155. switch self {
  156. case let .bolus(bolus):
  157. try bolus.encode(to: encoder)
  158. case let .tempBasal(tempBasal):
  159. try tempBasal.encode(to: encoder)
  160. case let .tempBasalDuration(tempBasalDuration):
  161. try tempBasalDuration.encode(to: encoder)
  162. }
  163. }
  164. }
  165. // Extension with helper functions to map pump events to DTO objects via uniform masking enum
  166. extension PumpEventStored {
  167. func toBolusDTOEnum() -> PumpEventDTO? {
  168. guard let id = id, let timestamp = timestamp, let bolus = bolus, let amount = bolus.amount else {
  169. return nil
  170. }
  171. let dateFormatter = ISO8601DateFormatter()
  172. let bolusDTO = BolusDTO(
  173. id: id,
  174. timestamp: dateFormatter.string(from: timestamp),
  175. amount: amount.doubleValue,
  176. isExternal: bolus.isExternal,
  177. isSMB: bolus.isSMB,
  178. duration: 0
  179. )
  180. return .bolus(bolusDTO)
  181. }
  182. func toTempBasalDTOEnum() -> PumpEventDTO? {
  183. guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal, let rate = tempBasal.rate else {
  184. return nil
  185. }
  186. let dateFormatter = ISO8601DateFormatter()
  187. let tempBasalDTO = TempBasalDTO(
  188. id: "_\(id)",
  189. timestamp: dateFormatter.string(from: timestamp),
  190. temp: tempBasal.tempType ?? "unknown",
  191. rate: rate.doubleValue
  192. )
  193. return .tempBasal(tempBasalDTO)
  194. }
  195. func toTempBasalDurationDTOEnum() -> PumpEventDTO? {
  196. guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal else {
  197. return nil
  198. }
  199. let dateFormatter = ISO8601DateFormatter()
  200. let tempBasalDurationDTO = TempBasalDurationDTO(
  201. id: id,
  202. timestamp: dateFormatter.string(from: timestamp),
  203. duration: Int(tempBasal.duration)
  204. )
  205. return .tempBasalDuration(tempBasalDurationDTO)
  206. }
  207. }