GlucoseStored+helper.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import CoreData
  2. import Foundation
  3. extension GlucoseStored {
  4. static func fetch(
  5. _ predicate: NSPredicate = .all,
  6. ascending: Bool,
  7. fetchLimit: Int? = nil,
  8. batchSize: Int? = nil
  9. ) -> NSFetchRequest<GlucoseStored> {
  10. let request = GlucoseStored.fetchRequest()
  11. request.sortDescriptors = [NSSortDescriptor(keyPath: \GlucoseStored.date, ascending: ascending)]
  12. request.predicate = predicate
  13. if let limit = fetchLimit {
  14. request.fetchLimit = limit
  15. }
  16. if let batchSize = batchSize {
  17. request.fetchBatchSize = batchSize
  18. }
  19. return request
  20. }
  21. static func glucoseIsFlat(_ glucose: [GlucoseStored]) -> Bool {
  22. guard glucose.count >= 6 else { return false }
  23. let firstValue = glucose.first?.glucose
  24. return glucose.allSatisfy { $0.glucose == firstValue }
  25. }
  26. // Preview
  27. @discardableResult static func makePreviewGlucose(count: Int, provider: CoreDataStack) -> [GlucoseStored] {
  28. let context = provider.persistentContainer.viewContext
  29. let baseGlucose = 120
  30. let glucoseValues = (0 ..< count).map { index -> GlucoseStored in
  31. let glucose = GlucoseStored(context: context)
  32. glucose.id = UUID()
  33. glucose.date = Date.now.addingTimeInterval(Double(index) * -300) // Every 5 minutes
  34. glucose.glucose = Int16(baseGlucose + (index % 3) * 10) // Varying between 120-140
  35. glucose.direction = BloodGlucose.Direction.flat.rawValue
  36. glucose.isManual = false
  37. glucose.isUploadedToNS = false
  38. glucose.isUploadedToHealth = false
  39. glucose.isUploadedToTidepool = false
  40. return glucose
  41. }
  42. try? context.save()
  43. return glucoseValues
  44. }
  45. }
  46. extension NSPredicate {
  47. static var glucose: NSPredicate {
  48. let date = Date.oneDayAgo
  49. return NSPredicate(format: "date >= %@", date as NSDate)
  50. }
  51. static var manualGlucose: NSPredicate {
  52. let date = Date.oneDayAgo
  53. return NSPredicate(format: "isManual == %@ AND date >= %@", true as NSNumber, date as NSDate)
  54. }
  55. static var glucoseForStatsDay: NSPredicate {
  56. let date = Date.oneDayAgo
  57. return NSPredicate(format: "date >= %@", date as NSDate)
  58. }
  59. static var glucoseForStatsToday: NSPredicate {
  60. let date = Date.startOfToday
  61. return NSPredicate(format: "date >= %@", date as NSDate)
  62. }
  63. static var glucoseForStatsMonth: NSPredicate {
  64. let date = Date.oneMonthAgo
  65. return NSPredicate(format: "date >= %@", date as NSDate)
  66. }
  67. static var glucoseForStatsTotal: NSPredicate {
  68. let date = Date.threeMonthsAgo
  69. return NSPredicate(format: "date >= %@", date as NSDate)
  70. }
  71. static var glucoseForStatsWeek: NSPredicate {
  72. let date = Date.oneWeekAgo
  73. return NSPredicate(format: "date >= %@", date as NSDate)
  74. }
  75. static var glucoseNotYetUploadedToNightscout: NSPredicate {
  76. let date = Date.oneDayAgo
  77. return NSPredicate(format: "date >= %@ AND isUploadedToNS == %@", date as NSDate, false as NSNumber)
  78. }
  79. static var glucoseNotYetUploadedToHealth: NSPredicate {
  80. let date = Date.oneDayAgo
  81. return NSPredicate(format: "date >= %@ AND isUploadedToHealth == %@", date as NSDate, false as NSNumber)
  82. }
  83. static var glucoseNotYetUploadedToTidepool: NSPredicate {
  84. let date = Date.oneDayAgo
  85. return NSPredicate(format: "date >= %@ AND isUploadedToTidepool == %@", date as NSDate, false as NSNumber)
  86. }
  87. static var manualGlucoseNotYetUploadedToNightscout: NSPredicate {
  88. let date = Date.oneDayAgo
  89. return NSPredicate(
  90. format: "date >= %@ AND isUploadedToNS == %@ AND isManual == %@",
  91. date as NSDate,
  92. false as NSNumber,
  93. true as NSNumber
  94. )
  95. }
  96. static var manualGlucoseNotYetUploadedToHealth: NSPredicate {
  97. let date = Date.oneDayAgo
  98. return NSPredicate(
  99. format: "date >= %@ AND isUploadedToHealth == %@ AND isManual == %@",
  100. date as NSDate,
  101. false as NSNumber,
  102. true as NSNumber
  103. )
  104. }
  105. static var manualGlucoseNotYetUploadedToTidepool: NSPredicate {
  106. let date = Date.oneDayAgo
  107. return NSPredicate(
  108. format: "date >= %@ AND isUploadedToTidepool == %@ AND isManual == %@",
  109. date as NSDate,
  110. false as NSNumber,
  111. true as NSNumber
  112. )
  113. }
  114. }
  115. extension GlucoseStored: Encodable {
  116. enum CodingKeys: String, CodingKey {
  117. case date
  118. case dateString
  119. case sgv
  120. case glucose
  121. case direction
  122. case id
  123. case type
  124. }
  125. public func encode(to encoder: Encoder) throws {
  126. var container = encoder.container(keyedBy: CodingKeys.self)
  127. let dateFormatter = ISO8601DateFormatter()
  128. dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
  129. try container.encode(dateFormatter.string(from: date ?? Date()), forKey: .dateString)
  130. let dateAsUnixTimestamp = String(format: "%.0f", (date?.timeIntervalSince1970 ?? Date().timeIntervalSince1970) * 1000)
  131. try container.encode(dateAsUnixTimestamp, forKey: .date)
  132. try container.encode(direction, forKey: .direction)
  133. try container.encode(id, forKey: .id)
  134. // TODO: Handle the type of the glucose entry conditionally not hardcoded
  135. try container.encode("sgv", forKey: .type)
  136. if isManual {
  137. try container.encode(glucose, forKey: .glucose)
  138. } else {
  139. try container.encode(glucose, forKey: .sgv)
  140. }
  141. }
  142. }
  143. // In order to show the correct direction in the bobble we convert the direction property of the NSManagedObject GlucoseStored back to the Direction type
  144. extension GlucoseStored {
  145. var directionEnum: BloodGlucose.Direction? {
  146. BloodGlucose.Direction(rawValue: direction ?? "")
  147. }
  148. }