TDDSetup.swift 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import CoreData
  2. import Foundation
  3. extension Stat.StateModel {
  4. func setupTDDs() {
  5. Task {
  6. let tddStats = await fetchAndMapDeterminations()
  7. await MainActor.run {
  8. self.tddStats = tddStats
  9. }
  10. }
  11. }
  12. func fetchAndMapDeterminations() async -> [TDD] {
  13. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  14. ofType: OrefDetermination.self,
  15. onContext: determinationFetchContext,
  16. predicate: NSPredicate.determinationsForStats,
  17. key: "deliverAt",
  18. ascending: true,
  19. propertiesToFetch: ["objectID", "timestamp", "deliverAt", "totalDailyDose"]
  20. )
  21. return await determinationFetchContext.perform {
  22. guard let fetchedResults = results as? [[String: Any]] else { return [] }
  23. let calendar = Calendar.current
  24. // Group determinations by day or hour
  25. let groupedByTime = Dictionary(grouping: fetchedResults) { result -> Date in
  26. guard let deliverAt = result["deliverAt"] as? Date else { return Date() }
  27. if self.selectedDurationForInsulinStats == .Day {
  28. // For Day view, group by hour
  29. let components = calendar.dateComponents([.year, .month, .day, .hour], from: deliverAt)
  30. return calendar.date(from: components) ?? Date()
  31. } else {
  32. // For other views, group by day
  33. return calendar.startOfDay(for: deliverAt)
  34. }
  35. }
  36. // Get all unique time points
  37. let timePoints = groupedByTime.keys.sorted()
  38. // Calculate totals for each time point
  39. return timePoints.map { timePoint in
  40. let determinations = groupedByTime[timePoint, default: []]
  41. let totalDose = determinations.reduce(Decimal.zero) { sum, determination in
  42. sum + (determination["totalDailyDose"] as? Decimal ?? 0)
  43. }
  44. // Calculate average dose for the time period
  45. let count = Decimal(determinations.count)
  46. let averageDose = count > 0 ? totalDose / count : 0
  47. return TDD(
  48. totalDailyDose: averageDose,
  49. timestamp: timePoint
  50. )
  51. }
  52. }
  53. }
  54. var averageTDD: Decimal {
  55. let calendar = Calendar.current
  56. let now = Date()
  57. // Filter TDDs based on selected time frame
  58. let filteredTDDs: [TDD] = tddStats.filter { tdd in
  59. guard let timestamp = tdd.timestamp else { return false }
  60. switch selectedDurationForInsulinStats {
  61. case .Day:
  62. // Last 3 days
  63. let threeDaysAgo = calendar.date(byAdding: .day, value: -3, to: now)!
  64. return timestamp >= threeDaysAgo
  65. case .Week:
  66. // Last week
  67. let weekAgo = calendar.date(byAdding: .weekOfYear, value: -1, to: now)!
  68. return timestamp >= weekAgo
  69. case .Month:
  70. // Last month
  71. let monthAgo = calendar.date(byAdding: .month, value: -1, to: now)!
  72. return timestamp >= monthAgo
  73. case .Total:
  74. // Last 3 months
  75. let threeMonthsAgo = calendar.date(byAdding: .month, value: -3, to: now)!
  76. return timestamp >= threeMonthsAgo
  77. }
  78. }
  79. let sum = filteredTDDs.reduce(Decimal.zero) { $0 + ($1.totalDailyDose ?? 0) }
  80. return filteredTDDs.isEmpty ? 0 : sum / Decimal(filteredTDDs.count)
  81. }
  82. func calculateAverageTDD(from startDate: Date, to endDate: Date) async -> Decimal {
  83. let filteredTDDs = tddStats.filter { tdd in
  84. guard let timestamp = tdd.timestamp else { return false }
  85. return timestamp >= startDate && timestamp <= endDate
  86. }
  87. let sum = filteredTDDs.reduce(Decimal.zero) { $0 + ($1.totalDailyDose ?? 0) }
  88. return filteredTDDs.isEmpty ? 0 : sum / Decimal(filteredTDDs.count)
  89. }
  90. func calculateMedianTDD(from startDate: Date, to endDate: Date) async -> Decimal {
  91. let filteredTDDs = tddStats.filter { tdd in
  92. guard let timestamp = tdd.timestamp else { return false }
  93. return timestamp >= startDate && timestamp <= endDate
  94. }
  95. let sortedDoses = filteredTDDs.compactMap(\.totalDailyDose).sorted()
  96. guard !sortedDoses.isEmpty else { return 0 }
  97. let middle = sortedDoses.count / 2
  98. if sortedDoses.count % 2 == 0 {
  99. return (sortedDoses[middle - 1] + sortedDoses[middle]) / 2
  100. } else {
  101. return sortedDoses[middle]
  102. }
  103. }
  104. }