GlucoseStored+helper.swift 5.7 KB

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