iOS Build 23
This commit is contained in:
@@ -852,4 +852,73 @@ class ImageManager {
|
||||
print("ERROR: Failed to migrate from previous Application Support: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func migrateImageNamesToDeterministic(dataManager: ClimbingDataManager) {
|
||||
print("Starting migration of image names to deterministic format...")
|
||||
|
||||
var migrationCount = 0
|
||||
var updatedProblems: [Problem] = []
|
||||
|
||||
for problem in dataManager.problems {
|
||||
guard !problem.imagePaths.isEmpty else { continue }
|
||||
|
||||
var newImagePaths: [String] = []
|
||||
var problemNeedsUpdate = false
|
||||
|
||||
for (index, imagePath) in problem.imagePaths.enumerated() {
|
||||
let currentFilename = URL(fileURLWithPath: imagePath).lastPathComponent
|
||||
|
||||
if ImageNamingUtils.isValidImageFilename(currentFilename) {
|
||||
newImagePaths.append(imagePath)
|
||||
continue
|
||||
}
|
||||
|
||||
let deterministicName = ImageNamingUtils.generateImageFilename(
|
||||
problemId: problem.id.uuidString, imageIndex: index)
|
||||
|
||||
let oldPath = imagesDirectory.appendingPathComponent(currentFilename)
|
||||
let newPath = imagesDirectory.appendingPathComponent(deterministicName)
|
||||
|
||||
if fileManager.fileExists(atPath: oldPath.path) {
|
||||
do {
|
||||
try fileManager.moveItem(at: oldPath, to: newPath)
|
||||
|
||||
let oldBackupPath = backupDirectory.appendingPathComponent(currentFilename)
|
||||
let newBackupPath = backupDirectory.appendingPathComponent(
|
||||
deterministicName)
|
||||
|
||||
if fileManager.fileExists(atPath: oldBackupPath.path) {
|
||||
try? fileManager.moveItem(at: oldBackupPath, to: newBackupPath)
|
||||
}
|
||||
|
||||
newImagePaths.append(deterministicName)
|
||||
problemNeedsUpdate = true
|
||||
migrationCount += 1
|
||||
|
||||
print("Migrated: \(currentFilename) → \(deterministicName)")
|
||||
|
||||
} catch {
|
||||
print("Failed to migrate \(currentFilename): \(error)")
|
||||
newImagePaths.append(imagePath)
|
||||
}
|
||||
} else {
|
||||
print("Warning: Image file not found: \(currentFilename)")
|
||||
newImagePaths.append(imagePath)
|
||||
}
|
||||
}
|
||||
|
||||
if problemNeedsUpdate {
|
||||
let updatedProblem = problem.updated(imagePaths: newImagePaths)
|
||||
updatedProblems.append(updatedProblem)
|
||||
}
|
||||
}
|
||||
|
||||
for updatedProblem in updatedProblems {
|
||||
dataManager.updateProblem(updatedProblem)
|
||||
}
|
||||
|
||||
print(
|
||||
"Migration completed: \(migrationCount) images renamed, \(updatedProblems.count) problems updated"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,18 @@ class ImageNamingUtils {
|
||||
private static let hashLength = 12
|
||||
|
||||
/// Generates a deterministic filename for a problem image
|
||||
static func generateImageFilename(problemId: String, timestamp: String, imageIndex: Int)
|
||||
-> String
|
||||
{
|
||||
|
||||
let input = "\(problemId)_\(timestamp)_\(imageIndex)"
|
||||
static func generateImageFilename(problemId: String, imageIndex: Int) -> String {
|
||||
let input = "\(problemId)_\(imageIndex)"
|
||||
let hash = createHash(from: input)
|
||||
|
||||
return "problem_\(hash)_\(imageIndex)\(imageExtension)"
|
||||
}
|
||||
|
||||
/// Generates a deterministic filename using current timestamp
|
||||
static func generateImageFilename(problemId: String, imageIndex: Int) -> String {
|
||||
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||
return generateImageFilename(
|
||||
problemId: problemId, timestamp: timestamp, imageIndex: imageIndex)
|
||||
/// Legacy method for backward compatibility
|
||||
static func generateImageFilename(problemId: String, timestamp: String, imageIndex: Int)
|
||||
-> String
|
||||
{
|
||||
return generateImageFilename(problemId: problemId, imageIndex: imageIndex)
|
||||
}
|
||||
|
||||
/// Extracts problem ID from an image filename
|
||||
@@ -64,9 +61,7 @@ class ImageNamingUtils {
|
||||
return oldFilename
|
||||
}
|
||||
|
||||
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||
return generateImageFilename(
|
||||
problemId: problemId, timestamp: timestamp, imageIndex: imageIndex)
|
||||
return generateImageFilename(problemId: problemId, imageIndex: imageIndex)
|
||||
}
|
||||
|
||||
/// Creates a deterministic hash from input string
|
||||
@@ -84,8 +79,7 @@ class ImageNamingUtils {
|
||||
var renameMap: [String: String] = [:]
|
||||
|
||||
for (index, oldFilename) in existingFilenames.enumerated() {
|
||||
let newFilename = migrateFilename(
|
||||
oldFilename: oldFilename, problemId: problemId, imageIndex: index)
|
||||
let newFilename = generateImageFilename(problemId: problemId, imageIndex: index)
|
||||
if newFilename != oldFilename {
|
||||
renameMap[oldFilename] = newFilename
|
||||
}
|
||||
@@ -113,6 +107,40 @@ class ImageNamingUtils {
|
||||
invalidImages: invalidImages
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the canonical filename that should be used for a problem image
|
||||
static func getCanonicalImageFilename(problemId: String, imageIndex: Int) -> String {
|
||||
return generateImageFilename(problemId: problemId, imageIndex: imageIndex)
|
||||
}
|
||||
|
||||
/// Creates a mapping of existing server filenames to canonical filenames
|
||||
static func createServerMigrationMap(
|
||||
problemId: String,
|
||||
serverImageFilenames: [String],
|
||||
localImageCount: Int
|
||||
) -> [String: String] {
|
||||
var migrationMap: [String: String] = [:]
|
||||
|
||||
for imageIndex in 0..<localImageCount {
|
||||
let canonicalName = getCanonicalImageFilename(
|
||||
problemId: problemId, imageIndex: imageIndex)
|
||||
|
||||
if serverImageFilenames.contains(canonicalName) {
|
||||
continue
|
||||
}
|
||||
|
||||
for serverFilename in serverImageFilenames {
|
||||
if isValidImageFilename(serverFilename)
|
||||
&& !migrationMap.values.contains(serverFilename)
|
||||
{
|
||||
migrationMap[serverFilename] = canonicalName
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return migrationMap
|
||||
}
|
||||
}
|
||||
|
||||
// Result of image filename validation
|
||||
|
||||
Reference in New Issue
Block a user