| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- 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
- }
- // Preview
- @discardableResult static func makePreviewEvents(count: Int, provider: CoreDataStack) -> [PumpEventStored] {
- let context = provider.persistentContainer.viewContext
- let events = (0 ..< count).map { index -> PumpEventStored in
- let event = PumpEventStored(context: context)
- event.id = UUID().uuidString
- event.timestamp = Date.now.addingTimeInterval(Double(index) * -300) // Every 5 minutes
- event.type = EventType.bolus.rawValue
- event.isUploadedToNS = false
- event.isUploadedToHealth = false
- event.isUploadedToTidepool = false
- // Add a bolus
- let bolus = BolusStored(context: context)
- bolus.amount = 2.5 as NSDecimalNumber
- bolus.isExternal = false
- bolus.isSMB = false
- bolus.pumpEvent = event
- return event
- }
- try? context.save()
- return events
- }
- }
- 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 siteChange = "SiteChange"
- case nsNote = "Note"
- 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 nsExercise = "Exercise"
- case capillaryGlucose = "BG Check"
- }
- enum TempType: String, JSON {
- case absolute
- case percent
- }
- }
- extension NSPredicate {
- static var pumpHistoryLast1440Minutes: NSPredicate {
- let date = Date.oneDayAgoInMinutes
- return NSPredicate(format: "timestamp >= %@", date as NSDate)
- }
- static var pumpHistoryLast48h: NSPredicate {
- let date = Date() - TimeInterval(hours: 48)
- return NSPredicate(format: "timestamp >= %@", date as NSDate)
- }
- static var pumpHistoryLast24h: NSPredicate {
- let date = Date.oneDayAgo
- return NSPredicate(format: "timestamp >= %@", date as NSDate)
- }
- static var pumpHistoryForStats: NSPredicate {
- let date = Date.threeMonthsAgo
- return NSPredicate(format: "pumpEvent.timestamp >= %@", date as NSDate)
- }
- static var recentPumpHistory: NSPredicate {
- let date = Date.twentyMinutesAgo
- return NSPredicate(
- format: "type == %@ AND timestamp >= %@",
- PumpEventStored.EventType.tempBasal.rawValue,
- date as NSDate
- )
- }
- static var lastPumpBolus: NSPredicate {
- let date = Date.twentyMinutesAgo
- return NSPredicate(format: "timestamp >= %@ AND bolus.isExternal == %@", date as NSDate, false as NSNumber)
- }
- static func duplicates(_ date: Date) -> NSPredicate {
- NSPredicate(format: "timestamp == %@", date as NSDate)
- }
- static var pumpEventsNotYetUploadedToNightscout: NSPredicate {
- let date = Date.oneDayAgo
- return NSPredicate(format: "timestamp >= %@ AND isUploadedToNS == %@", date as NSDate, false as NSNumber)
- }
- static var pumpEventsNotYetUploadedToHealth: NSPredicate {
- let date = Date.oneDayAgo
- return NSPredicate(format: "timestamp >= %@ AND isUploadedToHealth == %@", date as NSDate, false as NSNumber)
- }
- static var pumpEventsNotYetUploadedToTidepool: NSPredicate {
- let date = Date.oneDayAgo
- return NSPredicate(format: "timestamp >= %@ AND isUploadedToTidepool == %@", date as NSDate, false as NSNumber)
- }
- }
- // MARK: - Native mapping
- extension PumpEventStored {
- /// Converts a stored pump event into the `PumpHistoryEvent`s the oref algorithm can use.
- /// A temp basal yields a duration entry followed by a rate entry.
- func toPumpHistoryEvents() -> [PumpHistoryEvent] {
- var events: [PumpHistoryEvent] = []
- if let bolus = toBolusPumpHistoryEvent() { events.append(bolus) }
- if let duration = toTempBasalDurationPumpHistoryEvent() { events.append(duration) }
- if let tempBasal = toTempBasalPumpHistoryEvent() { events.append(tempBasal) }
- if let suspend = toSuspendPumpHistoryEvent() { events.append(suspend) }
- if let resume = toResumePumpHistoryEvent() { events.append(resume) }
- if let rewind = toRewindPumpHistoryEvent() { events.append(rewind) }
- if let prime = toPrimePumpHistoryEvent() { events.append(prime) }
- return events
- }
- private func toBolusPumpHistoryEvent() -> PumpHistoryEvent? {
- guard let timestamp = timestamp, let bolus = bolus, let amount = bolus.amount else {
- return nil
- }
- return PumpHistoryEvent(
- id: id ?? UUID().uuidString,
- type: .bolus,
- timestamp: timestamp,
- amount: Decimal(algorithmValue: amount.doubleValue),
- duration: 0,
- isSMB: bolus.isSMB,
- isExternal: bolus.isExternal
- )
- }
- // The temp basal duration populates `durationMin`, not `duration`.
- private func toTempBasalDurationPumpHistoryEvent() -> PumpHistoryEvent? {
- guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal else {
- return nil
- }
- return PumpHistoryEvent(
- id: id,
- type: .tempBasalDuration,
- timestamp: timestamp,
- durationMin: Int(tempBasal.duration)
- )
- }
- // The temp basal rate entry id is prefixed with "_". An unrecognized `tempType` maps to nil.
- private func toTempBasalPumpHistoryEvent() -> PumpHistoryEvent? {
- guard let id = id, let timestamp = timestamp, let tempBasal = tempBasal, let rate = tempBasal.rate else {
- return nil
- }
- return PumpHistoryEvent(
- id: "_\(id)",
- type: .tempBasal,
- timestamp: timestamp,
- rate: Decimal(algorithmValue: rate.doubleValue),
- temp: tempBasal.tempType.flatMap { Trio.TempType(rawValue: $0) }
- )
- }
- private func toSuspendPumpHistoryEvent() -> PumpHistoryEvent? {
- guard let id = id, let timestamp = timestamp, type == EventType.pumpSuspend.rawValue else {
- return nil
- }
- return PumpHistoryEvent(id: id, type: .pumpSuspend, timestamp: timestamp)
- }
- private func toResumePumpHistoryEvent() -> PumpHistoryEvent? {
- guard let id = id, let timestamp = timestamp, type == EventType.pumpResume.rawValue else {
- return nil
- }
- return PumpHistoryEvent(id: id, type: .pumpResume, timestamp: timestamp)
- }
- private func toRewindPumpHistoryEvent() -> PumpHistoryEvent? {
- guard let id = id, let timestamp = timestamp, type == EventType.rewind.rawValue else {
- return nil
- }
- return PumpHistoryEvent(id: id, type: .rewind, timestamp: timestamp)
- }
- private func toPrimePumpHistoryEvent() -> PumpHistoryEvent? {
- guard let id = id, let timestamp = timestamp, type == EventType.prime.rawValue else {
- return nil
- }
- return PumpHistoryEvent(id: id, type: .prime, timestamp: timestamp)
- }
- }
|