BolusStatsSetup.swift 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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. /// Updates the bolus statistics for the currently selected time period
  17. func updateBolusStats() {
  18. Task {
  19. // let stats = await fetchBolusStats(days: requestedDaysTDD, endDate: requestedEndDayTDD)
  20. // await MainActor.run {
  21. // self.bolusStats = stats
  22. // }
  23. }
  24. }
  25. /// Fetches and processes bolus statistics for a specific date range
  26. /// - Parameters:
  27. /// - days: Number of days to fetch
  28. /// - endDate: The end date of the range
  29. /// - Returns: Array of BolusStats containing daily bolus statistics
  30. func fetchBolusStats(days: Int, endDate: Date) async -> [BolusStats] {
  31. let calendar = Calendar.current
  32. let endDate = calendar.startOfDay(for: endDate)
  33. let startDate = calendar.date(byAdding: .day, value: -(days - 1), to: endDate)!
  34. // Fetch bolus records from Core Data
  35. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  36. ofType: BolusStored.self,
  37. onContext: bolusTaskContext,
  38. predicate: NSPredicate(
  39. format: "pumpEvent.timestamp >= %@ AND pumpEvent.timestamp < %@",
  40. startDate as NSDate,
  41. calendar.date(byAdding: .day, value: 1, to: endDate)! as NSDate
  42. ),
  43. key: "pumpEvent.timestamp",
  44. ascending: false,
  45. batchSize: 100
  46. )
  47. return await bolusTaskContext.perform {
  48. guard let fetchedResults = results as? [BolusStored] else { return [] }
  49. // Group entries by day
  50. let groupedEntries = Dictionary(grouping: fetchedResults) { entry in
  51. calendar.startOfDay(for: entry.pumpEvent?.timestamp ?? Date())
  52. }
  53. // Create array of all dates in the range
  54. var dates: [Date] = []
  55. var currentDate = startDate
  56. while currentDate <= endDate {
  57. dates.append(calendar.startOfDay(for: currentDate))
  58. currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)!
  59. }
  60. // Calculate statistics for each day
  61. return dates.map { date in
  62. let dayEntries = groupedEntries[date, default: []]
  63. // Calculate total manual boluses (excluding SMB and external)
  64. let manualBolus = dayEntries
  65. .filter { !($0.isExternal || $0.isSMB) }
  66. .reduce(0.0) { $0 + (($1.amount as? Decimal) ?? 0).doubleValue }
  67. // Calculate total SMB
  68. let smb = dayEntries
  69. .filter { $0.isSMB }
  70. .reduce(0.0) { $0 + (($1.amount as? Decimal) ?? 0).doubleValue }
  71. // Calculate total external boluses
  72. let external = dayEntries
  73. .filter { $0.isExternal }
  74. .reduce(0.0) { $0 + (($1.amount as? Decimal) ?? 0).doubleValue }
  75. return BolusStats(
  76. date: date,
  77. manualBolus: manualBolus,
  78. smb: smb,
  79. external: external
  80. )
  81. }.sorted { $0.date < $1.date }
  82. }
  83. }
  84. }
  85. /// Extension to convert Decimal to Double
  86. private extension Decimal {
  87. var doubleValue: Double {
  88. NSDecimalNumber(decimal: self).doubleValue
  89. }
  90. }