InvestmentTrackerApp/InvestmentTracker/Models/CoreData/PredictionCache+CoreDataCla...

148 lines
4.3 KiB
Swift

import Foundation
import CoreData
@objc(PredictionCache)
public class PredictionCache: NSManagedObject, Identifiable {
@nonobjc public class func fetchRequest() -> NSFetchRequest<PredictionCache> {
return NSFetchRequest<PredictionCache>(entityName: "PredictionCache")
}
@NSManaged public var id: UUID
@NSManaged public var sourceId: UUID?
@NSManaged public var algorithm: String
@NSManaged public var predictionData: Data?
@NSManaged public var calculatedAt: Date
@NSManaged public var validUntil: Date
public override func awakeFromInsert() {
super.awakeFromInsert()
id = UUID()
calculatedAt = Date()
// Cache valid for 24 hours
validUntil = Calendar.current.date(byAdding: .hour, value: 24, to: Date()) ?? Date()
algorithm = PredictionAlgorithm.linear.rawValue
}
}
// MARK: - Prediction Algorithm
enum PredictionAlgorithm: String, CaseIterable, Identifiable {
case linear = "linear"
case exponentialSmoothing = "exponential_smoothing"
case movingAverage = "moving_average"
var id: String { rawValue }
var displayName: String {
switch self {
case .linear: return "Linear Regression"
case .exponentialSmoothing: return "Exponential Smoothing"
case .movingAverage: return "Moving Average"
}
}
var description: String {
switch self {
case .linear:
return "Projects future values based on historical trend line"
case .exponentialSmoothing:
return "Gives more weight to recent data points"
case .movingAverage:
return "Smooths out short-term fluctuations"
}
}
}
// MARK: - Computed Properties
extension PredictionCache {
var isValid: Bool {
Date() < validUntil
}
var algorithmType: PredictionAlgorithm {
PredictionAlgorithm(rawValue: algorithm) ?? .linear
}
var predictions: [Prediction]? {
guard let data = predictionData else { return nil }
return try? JSONDecoder().decode([Prediction].self, from: data)
}
}
// MARK: - Static Helpers
extension PredictionCache {
static func getCache(
for sourceId: UUID?,
algorithm: PredictionAlgorithm,
in context: NSManagedObjectContext
) -> PredictionCache? {
let request: NSFetchRequest<PredictionCache> = PredictionCache.fetchRequest()
if let sourceId = sourceId {
request.predicate = NSPredicate(
format: "sourceId == %@ AND algorithm == %@",
sourceId as CVarArg,
algorithm.rawValue
)
} else {
request.predicate = NSPredicate(
format: "sourceId == nil AND algorithm == %@",
algorithm.rawValue
)
}
request.fetchLimit = 1
guard let cache = try? context.fetch(request).first,
cache.isValid else {
return nil
}
return cache
}
static func saveCache(
for sourceId: UUID?,
algorithm: PredictionAlgorithm,
predictions: [Prediction],
in context: NSManagedObjectContext
) {
// Remove old cache
let request: NSFetchRequest<PredictionCache> = PredictionCache.fetchRequest()
if let sourceId = sourceId {
request.predicate = NSPredicate(
format: "sourceId == %@ AND algorithm == %@",
sourceId as CVarArg,
algorithm.rawValue
)
} else {
request.predicate = NSPredicate(
format: "sourceId == nil AND algorithm == %@",
algorithm.rawValue
)
}
if let oldCache = try? context.fetch(request) {
oldCache.forEach { context.delete($0) }
}
// Create new cache
let cache = PredictionCache(context: context)
cache.sourceId = sourceId
cache.algorithm = algorithm.rawValue
cache.predictionData = try? JSONEncoder().encode(predictions)
try? context.save()
}
static func invalidateAll(in context: NSManagedObjectContext) {
let request: NSFetchRequest<PredictionCache> = PredictionCache.fetchRequest()
if let caches = try? context.fetch(request) {
caches.forEach { context.delete($0) }
}
try? context.save()
}
}