DataTableDataFlow.swift 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import CoreData
  2. import Foundation
  3. import HealthKit
  4. import SwiftUI
  5. enum DataTable {
  6. enum Config {}
  7. enum TreatmentType: String, CaseIterable {
  8. case bolus = "Bolus"
  9. case externalBolus = "External Bolus"
  10. case smb = "SMB"
  11. case tempBasal = "Temp Basal"
  12. case suspend = "Suspend"
  13. case other = "Other"
  14. var displayName: String {
  15. switch self {
  16. case .bolus:
  17. return String(localized: "Bolus")
  18. case .externalBolus:
  19. return String(localized: "External Bolus")
  20. case .smb:
  21. return String(localized: "SMB")
  22. case .tempBasal:
  23. return String(localized: "Temp Basal")
  24. case .suspend:
  25. return String(localized: "Suspend")
  26. case .other:
  27. return String(localized: "Other")
  28. }
  29. }
  30. }
  31. enum Mode: String, Hashable, Identifiable, CaseIterable {
  32. case treatments
  33. case meals
  34. case glucose
  35. case adjustments
  36. var id: String { rawValue }
  37. var name: String {
  38. switch self {
  39. case .treatments:
  40. return String(localized: "Treatments", comment: "History Mode")
  41. case .meals:
  42. return String(localized: "Meals", comment: "History Mode")
  43. case .glucose:
  44. return String(localized: "Glucose", comment: "History Mode")
  45. case .adjustments:
  46. return String(localized: "Adjustments", comment: "History Mode")
  47. }
  48. }
  49. }
  50. enum DataType: String, Equatable {
  51. case carbs
  52. case fpus
  53. case bolus
  54. case tempBasal
  55. case tempTarget
  56. case suspend
  57. case resume
  58. var name: String {
  59. switch self {
  60. case .carbs:
  61. return String(localized: "Carbs", comment: "Treatment type")
  62. case .fpus:
  63. return String(localized: "Protein / Fat", comment: "Treatment type")
  64. case .bolus:
  65. return String(localized: "Bolus", comment: "Treatment type")
  66. case .tempBasal:
  67. return String(localized: "Temp Basal", comment: "Treatment type")
  68. case .tempTarget:
  69. return String(localized: "Temp Target", comment: "Treatment type")
  70. case .suspend:
  71. return String(localized: "Suspend", comment: "Treatment type")
  72. case .resume:
  73. return String(localized: "Resume", comment: "Treatment type")
  74. }
  75. }
  76. }
  77. class Treatment: Identifiable, Hashable, Equatable {
  78. let id: String
  79. let idPumpEvent: String?
  80. let units: GlucoseUnits
  81. let type: DataType
  82. let date: Date
  83. let amount: Decimal?
  84. let secondAmount: Decimal?
  85. let duration: Decimal?
  86. let isFPU: Bool?
  87. let fpuID: String?
  88. let note: String?
  89. let isSMB: Bool?
  90. let isExternal: Bool?
  91. private var numberFormatter: NumberFormatter {
  92. let formatter = NumberFormatter()
  93. formatter.numberStyle = .decimal
  94. formatter.maximumFractionDigits = 2
  95. return formatter
  96. }
  97. private var tempTargetFormater: NumberFormatter {
  98. let formatter = NumberFormatter()
  99. formatter.numberStyle = .decimal
  100. formatter.maximumFractionDigits = 1
  101. return formatter
  102. }
  103. init(
  104. units: GlucoseUnits,
  105. type: DataType,
  106. date: Date,
  107. amount: Decimal? = nil,
  108. secondAmount: Decimal? = nil,
  109. duration: Decimal? = nil,
  110. id: String? = nil,
  111. idPumpEvent: String? = nil,
  112. isFPU: Bool? = nil,
  113. fpuID: String? = nil,
  114. note: String? = nil,
  115. isSMB: Bool? = nil,
  116. isExternal: Bool? = nil
  117. ) {
  118. self.units = units
  119. self.type = type
  120. self.date = date
  121. self.amount = amount
  122. self.secondAmount = secondAmount
  123. self.duration = duration
  124. self.id = id ?? UUID().uuidString
  125. self.idPumpEvent = idPumpEvent
  126. self.isFPU = isFPU
  127. self.fpuID = fpuID
  128. self.note = note
  129. self.isSMB = isSMB
  130. self.isExternal = isExternal
  131. }
  132. static func == (lhs: Treatment, rhs: Treatment) -> Bool {
  133. lhs.id == rhs.id
  134. }
  135. func hash(into hasher: inout Hasher) {
  136. hasher.combine(id)
  137. }
  138. var amountText: String {
  139. guard let amount = amount else {
  140. return ""
  141. }
  142. if amount == 0, duration == 0 {
  143. return "Cancel temp"
  144. }
  145. switch type {
  146. case .carbs:
  147. return numberFormatter
  148. .string(from: amount as NSNumber)! + String(localized: " g", comment: "gram of carbs")
  149. case .fpus:
  150. return numberFormatter
  151. .string(from: amount as NSNumber)! + String(localized: " g", comment: "gram of carb equilvalents")
  152. case .bolus:
  153. var bolusText = " "
  154. if isSMB ?? false {}
  155. else if isExternal ?? false {
  156. bolusText += String(localized: "External", comment: "External Insulin")
  157. } else {
  158. bolusText += String(localized: "Manual", comment: "Manual Bolus")
  159. }
  160. return numberFormatter
  161. .string(from: amount as NSNumber)! + String(localized: " U", comment: "Insulin unit") + bolusText
  162. case .tempBasal:
  163. return numberFormatter
  164. .string(from: amount as NSNumber)! + String(localized: " U/hr", comment: "Unit insulin per hour")
  165. case .tempTarget:
  166. var converted = amount
  167. if units == .mmolL {
  168. converted = converted.asMmolL
  169. }
  170. guard var secondAmount = secondAmount else {
  171. return numberFormatter.string(from: converted as NSNumber)! + " \(units.rawValue)"
  172. }
  173. if units == .mmolL {
  174. secondAmount = secondAmount.asMmolL
  175. }
  176. return tempTargetFormater.string(from: converted as NSNumber)! + " - " + tempTargetFormater
  177. .string(from: secondAmount as NSNumber)! + " \(units.rawValue)"
  178. case .resume,
  179. .suspend:
  180. return type.name
  181. }
  182. }
  183. var color: Color {
  184. switch type {
  185. case .carbs:
  186. return .loopYellow
  187. case .fpus:
  188. return .orange.opacity(0.5)
  189. case .bolus:
  190. return Color.insulin
  191. case .tempBasal:
  192. return Color.insulin.opacity(0.4)
  193. case .resume,
  194. .suspend,
  195. .tempTarget:
  196. return .loopGray
  197. }
  198. }
  199. var durationText: String? {
  200. guard let duration = duration, duration > 0 else {
  201. return nil
  202. }
  203. return numberFormatter.string(from: duration as NSNumber)! + " min"
  204. }
  205. }
  206. class Glucose: Identifiable, Hashable, Equatable {
  207. static func == (lhs: DataTable.Glucose, rhs: DataTable.Glucose) -> Bool {
  208. lhs.glucose == rhs.glucose
  209. }
  210. let glucose: BloodGlucose
  211. init(glucose: BloodGlucose) {
  212. self.glucose = glucose
  213. }
  214. var id: String { glucose.id }
  215. }
  216. }
  217. protocol DataTableProvider: Provider {
  218. func deleteCarbsFromNightscout(withID id: String)
  219. func deleteInsulinFromNightscout(withID id: String)
  220. func deleteManualGlucoseFromNightscout(withID id: String)
  221. func deleteGlucoseFromHealth(withSyncID id: String)
  222. func deleteMealDataFromHealth(byID id: String, sampleType: HKSampleType)
  223. func deleteInsulinFromHealth(withSyncID id: String)
  224. }