Extensions.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import Foundation
  2. @testable import Trio
  3. // Helper extension for Date from ISO string
  4. extension Date {
  5. static func from(isoString: String) -> Date {
  6. let formatter = ISO8601DateFormatter()
  7. formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
  8. return formatter.date(from: isoString)!
  9. }
  10. var iso8601String: String {
  11. let formatter = ISO8601DateFormatter()
  12. formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate, .withColonSeparatorInTime, .withTimeZone]
  13. return formatter.string(from: self)
  14. }
  15. }
  16. extension CarbsEntry {
  17. static func forTest(createdAt: Date, carbs: Decimal) -> CarbsEntry {
  18. CarbsEntry(
  19. id: nil,
  20. createdAt: createdAt,
  21. actualDate: nil,
  22. carbs: carbs,
  23. fat: nil,
  24. protein: nil,
  25. note: nil,
  26. enteredBy: nil,
  27. isFPU: nil,
  28. fpuID: nil
  29. )
  30. }
  31. }
  32. extension TimeInterval {
  33. static func hours(_ hours: Double) -> TimeInterval {
  34. hours * 60 * 60
  35. }
  36. }
  37. extension [ComputedPumpHistoryEvent] {
  38. func netInsulin() -> Decimal { compactMap(\.insulin).reduce(0, +) }
  39. }
  40. extension Decimal {
  41. func isWithin(_ error: Decimal, of value: Decimal) -> Bool {
  42. (self - value).magnitude <= error
  43. }
  44. }
  45. extension Encodable {
  46. var prettyPrintedJSON: String? {
  47. let encoder = JSONCoding.encoder
  48. encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes, .sortedKeys]
  49. do {
  50. let data = try encoder.encode(self)
  51. return String(data: data, encoding: .utf8)
  52. } catch {
  53. return nil
  54. }
  55. }
  56. }
  57. extension IobResult {
  58. func approximatelyEquals(_ rhs: IobResult) -> Bool {
  59. // Compare all properties
  60. guard iob.isWithin(0.001, of: rhs.iob),
  61. activity.isWithin(0.0001, of: rhs.activity),
  62. basaliob.isWithin(0.001, of: rhs.basaliob),
  63. bolusiob.isWithin(0.001, of: rhs.bolusiob),
  64. netbasalinsulin.isWithin(0.05, of: rhs.netbasalinsulin),
  65. bolusinsulin.isWithin(0.001, of: rhs.bolusinsulin),
  66. time == rhs.time,
  67. lastBolusTime == rhs.lastBolusTime
  68. else {
  69. return false
  70. }
  71. // Compare nested IobWithZeroTemp
  72. guard iobWithZeroTemp.iob.isWithin(0.001, of: rhs.iobWithZeroTemp.iob),
  73. iobWithZeroTemp.activity.isWithin(0.0001, of: rhs.iobWithZeroTemp.activity),
  74. iobWithZeroTemp.basaliob.isWithin(0.001, of: rhs.iobWithZeroTemp.basaliob),
  75. iobWithZeroTemp.bolusiob.isWithin(0.001, of: rhs.iobWithZeroTemp.bolusiob),
  76. iobWithZeroTemp.netbasalinsulin.isWithin(0.05, of: rhs.iobWithZeroTemp.netbasalinsulin),
  77. iobWithZeroTemp.bolusinsulin.isWithin(0.001, of: rhs.iobWithZeroTemp.bolusinsulin),
  78. iobWithZeroTemp.time == rhs.iobWithZeroTemp.time
  79. else {
  80. return false
  81. }
  82. // Compare optional LastTemp
  83. if let selfTemp = lastTemp, let rhsTemp = rhs.lastTemp {
  84. guard let selfDuration = selfTemp.duration, let rhsDuration = rhsTemp.duration, selfDuration.isWithin(
  85. 0.01,
  86. of: rhsDuration
  87. ) else {
  88. return false
  89. }
  90. // Both are non-nil, compare their properties
  91. return selfTemp.rate == rhsTemp.rate &&
  92. selfTemp.timestamp == rhsTemp.timestamp &&
  93. selfTemp.started_at == rhsTemp.started_at &&
  94. selfTemp.date == rhsTemp.date
  95. } else {
  96. // Both should be nil for equality
  97. return lastTemp == nil && rhs.lastTemp == nil
  98. }
  99. }
  100. }
  101. extension ComputedPumpHistoryEvent {
  102. func contains(tempBolus: ComputedPumpHistoryEvent) -> Bool {
  103. guard type == .tempBasal, tempBolus.isTempBolus else {
  104. fatalError("invalid type for computed pump history event")
  105. }
  106. let start = timestamp
  107. let end = start + duration!.minutesToSeconds
  108. return start <= tempBolus.timestamp && end > tempBolus.timestamp
  109. }
  110. }