ForecastSetup.swift 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import CoreData
  2. import Foundation
  3. extension Home.StateModel {
  4. // Asynchronously preprocess Forecast data in a background thread
  5. func preprocessForecastData() async -> [(
  6. id: UUID, forecastID: NSManagedObjectID, forecastValueIDs: [NSManagedObjectID]
  7. )] {
  8. do {
  9. // Get the Determination ID on the main context
  10. guard let determination = await viewContext.perform({
  11. self.enactedAndNonEnactedDeterminations.first
  12. }) else {
  13. debug(.default, "No determination found for forecast preprocessing")
  14. return []
  15. }
  16. // Fetch complete forecast hierarchy with prefetched values
  17. return try await determinationStorage.fetchForecastHierarchy(
  18. for: determination.objectID,
  19. in: taskContext
  20. )
  21. } catch {
  22. debug(
  23. .default,
  24. "\(DebuggingIdentifiers.failed) Failed to preprocess forecast data: \(error.localizedDescription)"
  25. )
  26. return []
  27. }
  28. }
  29. // Update forecast data and UI on the main thread
  30. @MainActor func updateForecastData() async {
  31. let forecastDataIDs = await preprocessForecastData()
  32. var allForecastValues = [[Int]]()
  33. var preprocessedData = [(id: UUID, forecast: Forecast, forecastValue: ForecastValue)]()
  34. // Process prefetched data directly
  35. for data in forecastDataIDs {
  36. if let forecast = try? viewContext.existingObject(with: data.forecastID) as? Forecast {
  37. let values = data.forecastValueIDs.compactMap {
  38. try? viewContext.existingObject(with: $0) as? ForecastValue
  39. }
  40. // Extract values for graph
  41. let forecastValueInts = values.map { Int($0.value) }
  42. allForecastValues.append(forecastValueInts)
  43. // Add data for further processing
  44. preprocessedData.append(contentsOf: values.map {
  45. (id: data.id, forecast: forecast, forecastValue: $0)
  46. })
  47. }
  48. }
  49. // Update UI-relevant data
  50. self.preprocessedData = preprocessedData
  51. guard !allForecastValues.isEmpty else {
  52. minForecast = []
  53. maxForecast = []
  54. return
  55. }
  56. minCount = max(12, allForecastValues.map(\.count).min() ?? 0)
  57. let localMinCount = minCount
  58. guard localMinCount > 0 else { return }
  59. // Calculate min/max values for graph
  60. let (minResult, maxResult) = await Task.detached {
  61. let minForecast = (0 ..< localMinCount).map { index in
  62. allForecastValues.compactMap { $0.indices.contains(index) ? $0[index] : nil }
  63. .min() ?? 0
  64. }
  65. let maxForecast = (0 ..< localMinCount).map { index in
  66. allForecastValues.compactMap { $0.indices.contains(index) ? $0[index] : nil }
  67. .max() ?? 0
  68. }
  69. return (minForecast, maxForecast)
  70. }.value
  71. minForecast = minResult
  72. maxForecast = maxResult
  73. }
  74. }