MealStatsSetup.swift 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import CoreData
  2. import Foundation
  3. /// Represents statistical data about meal macronutrients for a specific day
  4. struct MealStats: Identifiable {
  5. let id = UUID()
  6. /// The date representing this time period
  7. let date: Date
  8. /// Total carbohydrates in grams
  9. let carbs: Double
  10. /// Total fat in grams
  11. let fat: Double
  12. /// Total protein in grams
  13. let protein: Double
  14. }
  15. extension Stat.StateModel {
  16. /// Initiates the process of fetching and processing meal statistics
  17. /// - Parameter duration: The time period to fetch records for
  18. func setupMealStats() {
  19. Task {
  20. let stats = await fetchMealStats()
  21. await MainActor.run {
  22. self.mealStats = stats
  23. }
  24. }
  25. }
  26. /// Fetches and processes meal statistics for a specific duration
  27. /// - Parameter duration: The time period to fetch records for (Today, 24h, 7 Days, 30 Days, or All)
  28. /// - Returns: Array of MealStats containing daily meal statistics, sorted by date
  29. private func fetchMealStats() async -> [MealStats] {
  30. // Fetch CarbEntryStored entries from Core Data
  31. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  32. ofType: CarbEntryStored.self,
  33. onContext: mealTaskContext,
  34. predicate: NSPredicate.carbsForStats,
  35. key: "date",
  36. ascending: true,
  37. batchSize: 100
  38. )
  39. return await mealTaskContext.perform {
  40. // Safely unwrap the fetched results, return empty array if nil
  41. guard let fetchedResults = results as? [CarbEntryStored] else { return [] }
  42. let calendar = Calendar.current
  43. // Group entries by day using calendar's startOfDay
  44. let groupedEntries = Dictionary(grouping: fetchedResults) { entry in
  45. calendar.startOfDay(for: entry.date ?? Date())
  46. }
  47. // Get all unique dates from the entries - they'll already be sorted
  48. let dates = groupedEntries.keys.sorted()
  49. // Calculate statistics for each day
  50. return dates.map { date in
  51. let entries = groupedEntries[date, default: []]
  52. // Sum up macronutrients for the day
  53. let carbsTotal = entries.reduce(0.0) { $0 + $1.carbs }
  54. let fatTotal = entries.reduce(0.0) { $0 + $1.fat }
  55. let proteinTotal = entries.reduce(0.0) { $0 + $1.protein }
  56. return MealStats(
  57. date: date,
  58. carbs: carbsTotal,
  59. fat: fatTotal,
  60. protein: proteinTotal
  61. )
  62. }
  63. }
  64. }
  65. func calculateAverageMealStats(
  66. from startDate: Date,
  67. to endDate: Date
  68. ) async -> (carbs: Double, fat: Double, protein: Double) {
  69. let filteredStats = self.mealStats.filter { stat in
  70. stat.date >= startDate && stat.date <= endDate
  71. }
  72. guard !filteredStats.isEmpty else { return (0, 0, 0) }
  73. let totalCarbs = filteredStats.reduce(0.0) { $0 + $1.carbs }
  74. let totalFat = filteredStats.reduce(0.0) { $0 + $1.fat }
  75. let totalProtein = filteredStats.reduce(0.0) { $0 + $1.protein }
  76. let count = Double(filteredStats.count)
  77. return (
  78. carbs: totalCarbs / count,
  79. fat: totalFat / count,
  80. protein: totalProtein / count
  81. )
  82. }
  83. }