IobJsonTypes.swift 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import Foundation
  2. @testable import Trio
  3. protocol BaseHistoryRecord {
  4. var date: UInt64 { get }
  5. }
  6. // For insulin bolus records
  7. struct InsulinRecord: BaseHistoryRecord, Codable {
  8. let insulin: Decimal
  9. let date: UInt64
  10. let created_at: Date?
  11. let started_at: Date?
  12. let timestamp: Date?
  13. enum CodingKeys: String, CodingKey {
  14. case insulin
  15. case date
  16. case created_at
  17. case started_at
  18. case timestamp
  19. }
  20. func mismatch(field: String, record: ComputedPumpHistoryEvent) -> Bool {
  21. print("Insulin mismatch \(field) json date: \(date) swift: \(record.date)")
  22. return false
  23. }
  24. func matches(record: ComputedPumpHistoryEvent) -> Bool {
  25. if insulin != record.insulin {
  26. return mismatch(field: "insulin", record: record)
  27. }
  28. if date != record.date, (date - 1) != record.date {
  29. return mismatch(field: "date", record: record)
  30. }
  31. if let timestamp = timestamp, timestamp != record.timestamp {
  32. return mismatch(field: "timestamp", record: record)
  33. }
  34. if let started_at = started_at, started_at != record.started_at {
  35. return mismatch(field: "started_at", record: record)
  36. }
  37. return true
  38. }
  39. }
  40. // For temporary basal rate records
  41. struct BasalRateRecord: BaseHistoryRecord, Codable {
  42. let rate: Decimal
  43. let timestamp: Date?
  44. let started_at: Date?
  45. let date: UInt64
  46. let duration: Decimal
  47. enum CodingKeys: String, CodingKey {
  48. case rate
  49. case timestamp
  50. case started_at
  51. case date
  52. case duration
  53. }
  54. func mismatch(field: String, record: ComputedPumpHistoryEvent) -> Bool {
  55. print("Basal mismatch \(field) json date: \(date) swift: \(record.date)")
  56. return false
  57. }
  58. func matches(record: ComputedPumpHistoryEvent) -> Bool {
  59. if rate != record.rate! {
  60. return mismatch(field: "rate", record: record)
  61. }
  62. if date != record.date {
  63. return mismatch(field: "date", record: record)
  64. }
  65. if !duration.isWithin(0.00001, of: record.duration!) {
  66. return mismatch(field: "duration", record: record)
  67. }
  68. if let timestamp = timestamp, timestamp != record.timestamp {
  69. return mismatch(field: "timestamp", record: record)
  70. }
  71. if let started_at = started_at, started_at != record.started_at {
  72. return mismatch(field: "started_at", record: record)
  73. }
  74. return true
  75. }
  76. }
  77. // Helper enum to handle either type of record
  78. enum HistoryRecord: Decodable {
  79. case insulin(InsulinRecord)
  80. case basal(BasalRateRecord)
  81. init(from decoder: Decoder) throws {
  82. let container = try decoder.container(keyedBy: CodingKeys.self)
  83. let date: UInt64
  84. if let doubleDate = try? container.decode(Double.self, forKey: .date) {
  85. date = UInt64(doubleDate)
  86. } else {
  87. date = try container.decode(UInt64.self, forKey: .date)
  88. }
  89. // If not basal, it must be insulin - check for insulin value
  90. if let insulin = try? container.decode(Decimal.self, forKey: .insulin) {
  91. // Handle both formats of insulin records
  92. let created_at = try? container.decode(Date.self, forKey: .created_at)
  93. let timestamp = try? container.decode(Date.self, forKey: .timestamp)
  94. let started_at = try? container.decode(Date.self, forKey: .started_at)
  95. self = .insulin(InsulinRecord(
  96. insulin: insulin,
  97. date: date,
  98. created_at: created_at,
  99. started_at: started_at,
  100. timestamp: timestamp
  101. ))
  102. return
  103. }
  104. // Otherwise, try to decode as basal record
  105. let rate = try container.decode(Decimal.self, forKey: .rate)
  106. let timestamp = try? container.decode(Date.self, forKey: .timestamp)
  107. let started_at = try? container.decode(Date.self, forKey: .started_at)
  108. let duration = try container.decode(Decimal.self, forKey: .duration)
  109. self = .basal(BasalRateRecord(
  110. rate: rate,
  111. timestamp: timestamp,
  112. started_at: started_at,
  113. date: date,
  114. duration: duration
  115. ))
  116. }
  117. func matches(_ event: ComputedPumpHistoryEvent) -> Bool {
  118. switch self {
  119. case let .insulin(record):
  120. return record.matches(record: event)
  121. case let .basal(record):
  122. return record.matches(record: event)
  123. }
  124. }
  125. private enum CodingKeys: String, CodingKey {
  126. case insulin
  127. case date
  128. case created_at
  129. case rate
  130. case timestamp
  131. case started_at
  132. case duration
  133. }
  134. }