148 lines
4.3 KiB
Swift
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()
|
|
}
|
|
}
|