StateIntentRequest.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import AppIntents
  2. import CoreData
  3. import Foundation
  4. enum StateIntentError: Error {
  5. case StateIntentUnknownError
  6. case NoBG
  7. case NoIOBCOB
  8. }
  9. @available(iOS 16, *) struct StateResults: AppEntity {
  10. static var defaultQuery = StateBGQuery()
  11. static var typeDisplayRepresentation: TypeDisplayRepresentation = "Trio State Result"
  12. var displayRepresentation: DisplayRepresentation {
  13. DisplayRepresentation(title: "\(glucose)")
  14. }
  15. var id: UUID
  16. @Property(title: "Glucose") var glucose: String
  17. @Property(title: "Trend") var trend: String
  18. @Property(title: "Delta") var delta: String
  19. @Property(title: "Date") var date: Date
  20. @Property(title: "IOB") var iob: Double?
  21. @Property(title: "COB") var cob: Double?
  22. @Property(title: "unit") var unit: String?
  23. init(glucose: String, trend: String, delta: String, date: Date, iob: Double, cob: Double, unit: GlucoseUnits) {
  24. id = UUID()
  25. self.glucose = glucose
  26. self.trend = trend
  27. self.delta = delta
  28. self.date = date
  29. self.iob = iob
  30. self.cob = cob
  31. self.unit = unit.rawValue
  32. }
  33. }
  34. @available(iOS 16.0, *) struct StateBGQuery: EntityQuery {
  35. func entities(for _: [StateResults.ID]) async throws -> [StateResults] {
  36. []
  37. }
  38. func suggestedEntities() async throws -> [StateResults] {
  39. []
  40. }
  41. }
  42. @available(iOS 16.0, *) final class StateIntentRequest: BaseIntentsRequest {
  43. let moc = CoreDataStack.shared.newTaskContext()
  44. func getLastGlucose(onContext: NSManagedObjectContext) throws
  45. -> (dateGlucose: Date, glucose: String, trend: String, delta: String)
  46. {
  47. do {
  48. let results = CoreDataStack.shared.fetchEntities(
  49. ofType: GlucoseStored.self,
  50. onContext: onContext,
  51. predicate: NSPredicate.predicateFor30MinAgo,
  52. key: "date",
  53. ascending: false,
  54. fetchLimit: 2
  55. )
  56. guard let lastValue = results.first else { throw StateIntentError.NoBG }
  57. /// calculate delta
  58. let lastGlucose = lastValue.glucose
  59. let secondLastGlucose = results.dropFirst().first?.glucose
  60. let delta = results.count > 1 ? (lastGlucose - (secondLastGlucose ?? 0)) : nil
  61. /// formatting
  62. let units = settingsManager.settings.units
  63. let glucoseAsString = glucoseFormatter.string(from: Double(
  64. units == .mmolL ? Decimal(lastGlucose)
  65. .asMmolL : Decimal(lastGlucose)
  66. ) as NSNumber)!
  67. let directionAsString = lastValue.direction ?? "none"
  68. let deltaAsString = delta
  69. .map {
  70. self.deltaFormatter
  71. .string(from: Double(
  72. units == .mmolL ? Decimal($0)
  73. .asMmolL : Decimal($0)
  74. ) as NSNumber)!
  75. } ?? "--"
  76. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.succeeded) fetched latest 2 glucose values")
  77. return (lastValue.date ?? Date(), glucoseAsString, directionAsString, deltaAsString)
  78. } catch {
  79. debugPrint("StateIntentRequest: \(#function) \(DebuggingIdentifiers.failed) failed to fetch latest 2 glucose values")
  80. return (Date(), "", "", "")
  81. }
  82. }
  83. func getIobAndCob(onContext: NSManagedObjectContext) throws -> (iob: Double, cob: Double) {
  84. let results = CoreDataStack.shared.fetchEntities(
  85. ofType: OrefDetermination.self,
  86. onContext: onContext,
  87. predicate: NSPredicate.enactedDetermination,
  88. key: "deliverAt",
  89. ascending: false,
  90. fetchLimit: 1
  91. )
  92. let iobAsDouble = Double(truncating: (results.first?.iob ?? 0.0) as NSNumber)
  93. let cobAsDouble = Double(truncating: (results.first?.cob ?? 0) as NSNumber)
  94. return (iobAsDouble, cobAsDouble)
  95. }
  96. private var glucoseFormatter: NumberFormatter {
  97. let formatter = NumberFormatter()
  98. formatter.numberStyle = .decimal
  99. formatter.maximumFractionDigits = 0
  100. if settingsManager.settings.units == .mmolL {
  101. formatter.minimumFractionDigits = 1
  102. formatter.maximumFractionDigits = 1
  103. }
  104. formatter.roundingMode = .halfUp
  105. return formatter
  106. }
  107. private var deltaFormatter: NumberFormatter {
  108. let formatter = NumberFormatter()
  109. formatter.numberStyle = .decimal
  110. formatter.maximumFractionDigits = 1
  111. formatter.positivePrefix = "+"
  112. return formatter
  113. }
  114. }