BolusStatsSetup.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import CoreData
  2. import Foundation
  3. /// Represents statistical data about bolus insulin delivery for a specific day
  4. struct BolusStats: Identifiable {
  5. let id = UUID()
  6. /// The date representing this time period
  7. let date: Date
  8. /// Total amount of manual boluses (excluding SMB and external)
  9. let manualBolus: Double
  10. /// Total amount of Super Micro Boluses (SMB)
  11. let smb: Double
  12. /// Total amount of external boluses (e.g., from pump directly)
  13. let external: Double
  14. }
  15. extension Stat.StateModel {
  16. func setupBolusStats() {
  17. Task {
  18. let stats = await fetchBolusStats()
  19. await MainActor.run {
  20. self.bolusStats = stats
  21. }
  22. }
  23. }
  24. /// Fetches and processes bolus statistics for a specific date range
  25. /// - Returns: Array of BolusStats containing daily bolus statistics
  26. private func fetchBolusStats() async -> [BolusStats] {
  27. let calendar = Calendar.current
  28. // Fetch bolus records from Core Data
  29. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  30. ofType: BolusStored.self,
  31. onContext: bolusTaskContext,
  32. predicate: NSPredicate.pumpHistoryForStats,
  33. key: "pumpEvent.timestamp",
  34. ascending: true,
  35. batchSize: 100
  36. )
  37. return await bolusTaskContext.perform {
  38. guard let fetchedResults = results as? [BolusStored] else { return [] }
  39. // Group boluses by day or hour depending on selected duration
  40. let groupedByTime = Dictionary(grouping: fetchedResults) { bolus -> Date in
  41. guard let timestamp = bolus.pumpEvent?.timestamp else { return Date() }
  42. if self.selectedDurationForInsulinStats == .Day {
  43. // For Day view, group by hour
  44. let components = calendar.dateComponents([.year, .month, .day, .hour], from: timestamp)
  45. return calendar.date(from: components) ?? Date()
  46. } else {
  47. // For other views, group by day
  48. return calendar.startOfDay(for: timestamp)
  49. }
  50. }
  51. // Get all unique time points
  52. let timePoints = groupedByTime.keys.sorted()
  53. // Calculate totals for each time point
  54. return timePoints.map { timePoint in
  55. let boluses = groupedByTime[timePoint, default: []]
  56. // Calculate total manual boluses (excluding SMB and external)
  57. let manualBolus = boluses
  58. .filter { !($0.isExternal || $0.isSMB) }
  59. .reduce(0.0) { $0 + (($1.amount as? Decimal) ?? 0).doubleValue }
  60. // Calculate total SMB
  61. let smb = boluses
  62. .filter { $0.isSMB }
  63. .reduce(0.0) { $0 + (($1.amount as? Decimal) ?? 0).doubleValue }
  64. // Calculate total external boluses
  65. let external = boluses
  66. .filter { $0.isExternal }
  67. .reduce(0.0) { $0 + (($1.amount as? Decimal) ?? 0).doubleValue }
  68. return BolusStats(
  69. date: timePoint,
  70. manualBolus: manualBolus,
  71. smb: smb,
  72. external: external
  73. )
  74. }
  75. }
  76. }
  77. func calculateAverageBolus(from startDate: Date, to endDate: Date) -> (manual: Double, smb: Double, external: Double) {
  78. let visibleStats = bolusStats.filter { stat in
  79. stat.date >= startDate && stat.date <= endDate
  80. }
  81. guard !visibleStats.isEmpty else { return (0, 0, 0) }
  82. let count = Double(visibleStats.count)
  83. let manualSum = visibleStats.reduce(0.0) { $0 + $1.manualBolus }
  84. let smbSum = visibleStats.reduce(0.0) { $0 + $1.smb }
  85. let externalSum = visibleStats.reduce(0.0) { $0 + $1.external }
  86. return (
  87. manualSum / count,
  88. smbSum / count,
  89. externalSum / count
  90. )
  91. }
  92. }
  93. /// Extension to convert Decimal to Double
  94. private extension Decimal {
  95. var doubleValue: Double {
  96. NSDecimalNumber(decimal: self).doubleValue
  97. }
  98. }