Explorar o código

Initial migration setup WIP

Deniz Cengiz hai 1 ano
pai
achega
66040b8aa4

+ 111 - 4
Model/JSONImporter.swift

@@ -741,8 +741,115 @@ extension Determination: Codable {
 }
 
 extension JSONImporter {
-    func importGlucoseHistoryIfNeeded() async {}
-    func importPumpHistoryIfNeeded() async {}
-    func importCarbHistoryIfNeeded() async {}
-    func importDeterminationIfNeeded() async {}
+    private func openAPSFileURL(_ relativePath: String) -> URL {
+        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+            .appendingPathComponent(relativePath)
+    }
+
+    func importGlucoseHistoryIfNeeded() async throws {
+        debug(.coreData, "Checking for glucose history JSON file...")
+        
+        let url = openAPSFileURL(OpenAPS.Monitor.glucose)
+        let suffix = "migrated.json"
+
+        guard FileManager.default.fileExists(atPath: url.path) else {
+            debug(.coreData, "❌ No JSON file to import at \(url.path)")
+            return
+        }
+        
+        debug(.coreData, "Glucose history JSON file found, proceeding with import of glucose history...")
+        
+        try await importGlucoseHistory(url: url, now: Date())
+
+        debug(.coreData, "Glucose history JSON file imported successfully, moving to \(suffix)")
+        
+        try FileManager.default.moveItem(
+            at: url,
+            to: url.deletingPathExtension().appendingPathExtension(suffix)
+        )
+        
+        debug(.coreData, "Import of glucose history completed successfully.")
+    }
+
+    func importPumpHistoryIfNeeded() async throws {
+        debug(.coreData, "Checking for pump history JSON file...")
+        
+        let url = openAPSFileURL(OpenAPS.Monitor.pumpHistory)
+        let suffix = "migrated.json"
+
+        guard FileManager.default.fileExists(atPath: url.path) else {
+            debug(.coreData, "❌ No JSON file to import at \(url.path)")
+            return
+        }
+        
+        debug(.coreData, "Pump history JSON file found, proceeding with import of glucose history...")
+        
+        try await importPumpHistory(url: url, now: Date())
+
+        debug(.coreData, "Pump history JSON file imported successfully, moving to \(suffix)")
+        
+        try FileManager.default.moveItem(
+            at: url,
+            to: url.deletingPathExtension().appendingPathExtension(suffix)
+        )
+        
+        debug(.coreData, "Import of pump history completed successfully.")
+    }
+
+    func importCarbHistoryIfNeeded() async throws {
+        debug(.coreData, "Checking for carb history JSON file...")
+        
+        let url = openAPSFileURL(OpenAPS.Monitor.pumpHistory)
+        let suffix = "migrated.json"
+
+        guard FileManager.default.fileExists(atPath: url.path) else {
+            debug(.coreData, "❌ No JSON file to import at \(url.path)")
+            return
+        }
+        
+        debug(.coreData, "Carb history JSON file found, proceeding with import of glucose history...")
+        
+        try await importCarbHistory(url: url, now: Date())
+
+        debug(.coreData, "Carb history JSON file imported successfully, moving to \(suffix)")
+        
+        try FileManager.default.moveItem(
+            at: url,
+            to: url.deletingPathExtension().appendingPathExtension(suffix)
+        )
+        
+        debug(.coreData, "Import of carb history completed successfully.")
+    }
+
+    func importDeterminationIfNeeded() async throws {
+        debug(.coreData, "Checking for determination JSON files...")
+        
+        let enactedPath = OpenAPS.Enact.enacted // "enact/enacted.json"
+        let suggestedPath = OpenAPS.Enact.suggested // "enact/suggested.json"
+        let suffix = "migrated.json"
+
+        let enactedURL = openAPSFileURL(enactedPath)
+        let suggestedURL = openAPSFileURL(suggestedPath)
+
+        guard FileManager.default.fileExists(atPath: enactedURL.path),
+              FileManager.default.fileExists(atPath: suggestedURL.path)
+        else {
+            debug(.coreData, "❌ No JSON file to import at \(enactedURL.path) and/or \(suggestedURL.path)")
+            return
+        }
+        
+        debug(.coreData, "Determination JSON files found, proceeding with import...")
+
+        try await importOrefDetermination(enactedUrl: enactedURL, suggestedUrl: suggestedURL, now: Date())
+        
+        debug(.coreData, "Determination JSON file(s) imported successfully, moving to \(suffix)")
+
+        try FileManager.default.moveItem(at: enactedURL, to: enactedURL.deletingPathExtension().appendingPathExtension(suffix))
+        try FileManager.default.moveItem(
+            at: suggestedURL,
+            to: suggestedURL.deletingPathExtension().appendingPathExtension(suffix)
+        )
+        
+        debug(.coreData, "Import of determination data completed successfully.")
+    }
 }

+ 56 - 0
Trio/Sources/Application/TrioApp.swift

@@ -36,6 +36,8 @@ extension Notification.Name {
     @State private var showLoadingView = true
     @State private var showLoadingError = false
     @State private var showOnboardingCompletedSplash = false
+    @State private var migrationErrors: [String] = []
+    @State private var showMigrationErrorAlert: Bool = false
 
     // Dependencies Assembler
     // contain all dependencies Assemblies
@@ -129,6 +131,9 @@ extension Notification.Name {
             do {
                 try await coreDataStack.initializeStack()
 
+                // TODO: possibly wrap this in a UserDefault / TinyStorage flag check, so we do not even attempt to fetch files unnecessary, but early exit the import
+                try await performJsonToCoreDataMigrationIfNeeded()
+
                 await Task { @MainActor in
                     // Only load services after successful Core Data initialization
                     loadServices()
@@ -165,6 +170,47 @@ extension Notification.Name {
         }
     }
 
+    private func performJsonToCoreDataMigrationIfNeeded() async throws {
+        let importer = JSONImporter(context: coreDataStack.newTaskContext(), coreDataStack: coreDataStack)
+        var importErrors: [String] = []
+
+        do {
+            try await importer.importGlucoseHistoryIfNeeded()
+        } catch {
+            importErrors
+                .append(String(localized: "Migration of JSON-based Glucose History failed: \(error.localizedDescription)"))
+            debug(.coreData, "❌ Failed to import JSON-based Glucose History: \(error)")
+        }
+
+        do {
+            try await importer.importPumpHistoryIfNeeded()
+        } catch {
+            importErrors.append(String(localized: "Migration of JSON-based Pump History failed: \(error.localizedDescription)"))
+            debug(.coreData, "❌ Failed to import JSON-based Pump History: \(error)")
+        }
+
+        do {
+            try await importer.importCarbHistoryIfNeeded()
+        } catch {
+            importErrors.append(String(localized: "Migration of JSON-based Carb History failed: \(error.localizedDescription)"))
+            debug(.coreData, "❌ Failed to import JSON-based Carb History: \(error)")
+        }
+
+        do {
+            try await importer.importDeterminationIfNeeded()
+        } catch {
+            importErrors
+                .append(
+                    String(localized: "Migration of JSON-based OpenAPS Determination Data failed: \(error.localizedDescription)")
+                )
+            debug(.coreData, "❌ Failed to import JSON-based OpenAPS Determination Data: \(error)")
+        }
+
+        await MainActor.run {
+            self.migrationErrors = importErrors
+        }
+    }
+
     /// Clears any legacy (Trio 0.2.x) delivered and pending notifications related to non-looping alerts.
     /// It targets the following notifications:
     /// - `noLoopFirstNotification`: The first notification for non-looping alerts.
@@ -254,6 +300,16 @@ extension Notification.Name {
                         .onOpenURL(perform: handleURL)
                 }
             }
+            // TODO: this is just for testing...
+            .sheet(isPresented: $showMigrationErrorAlert) {
+                if migrationErrors.isNotEmpty {
+                    ForEach(migrationErrors, id: \.self) { message in
+                        Text(message)
+                            .foregroundColor(.red)
+                            .font(.footnote)
+                    }
+                }
+            }
             .onReceive(Foundation.NotificationCenter.default.publisher(for: .onboardingCompleted)) { _ in
                 Task { @MainActor in
                     self.showOnboardingCompletedSplash = true

+ 12 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -142817,6 +142817,18 @@
         }
       }
     },
+    "Migration of JSON-based Carb History failed: %@" : {
+
+    },
+    "Migration of JSON-based Glucose History failed: %@" : {
+
+    },
+    "Migration of JSON-based OpenAPS Determination Data failed: %@" : {
+
+    },
+    "Migration of JSON-based Pump History failed: %@" : {
+
+    },
     "min" : {
       "comment" : "Minutes abbreviation\nShort form for minutes",
       "localizations" : {