1.2.2 - "Bug fixes and improvements"
This commit is contained in:
@@ -4,54 +4,36 @@
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
|
||||
/// Utility for creating consistent image filenames across iOS and Android platforms.
|
||||
/// Uses deterministic naming based on problem ID and timestamp to ensure sync compatibility.
|
||||
/// Utility for creating consistent image filenames across platforms
|
||||
class ImageNamingUtils {
|
||||
|
||||
private static let imageExtension = ".jpg"
|
||||
private static let hashLength = 12 // First 12 chars of SHA-256
|
||||
private static let hashLength = 12
|
||||
|
||||
/// Generates a deterministic filename for a problem image.
|
||||
/// Format: "problem_{hash}_{index}.jpg"
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - problemId: The ID of the problem this image belongs to
|
||||
/// - timestamp: ISO8601 timestamp when the image was created
|
||||
/// - imageIndex: The index of this image for the problem (0, 1, 2, etc.)
|
||||
/// - Returns: A consistent filename that will be the same across platforms
|
||||
/// Generates a deterministic filename for a problem image
|
||||
static func generateImageFilename(problemId: String, timestamp: String, imageIndex: Int)
|
||||
-> String
|
||||
{
|
||||
// Create a deterministic hash from problemId + timestamp + index
|
||||
|
||||
let input = "\(problemId)_\(timestamp)_\(imageIndex)"
|
||||
let hash = createHash(from: input)
|
||||
|
||||
return "problem_\(hash)_\(imageIndex)\(imageExtension)"
|
||||
}
|
||||
|
||||
/// Generates a deterministic filename for a problem image using current timestamp.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - problemId: The ID of the problem this image belongs to
|
||||
/// - imageIndex: The index of this image for the problem (0, 1, 2, etc.)
|
||||
/// - Returns: A consistent filename
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Extracts problem ID from an image filename created by this utility.
|
||||
/// Returns nil if the filename doesn't match our naming convention.
|
||||
///
|
||||
/// - Parameter filename: The image filename
|
||||
/// - Returns: The hash identifier or nil if not a valid filename
|
||||
/// Extracts problem ID from an image filename
|
||||
static func extractProblemIdFromFilename(_ filename: String) -> String? {
|
||||
guard filename.hasPrefix("problem_") && filename.hasSuffix(imageExtension) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format: problem_{hash}_{index}.jpg
|
||||
let nameWithoutExtension = String(filename.dropLast(imageExtension.count))
|
||||
let parts = nameWithoutExtension.components(separatedBy: "_")
|
||||
|
||||
@@ -59,14 +41,10 @@ class ImageNamingUtils {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the hash as identifier
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
/// Validates if a filename follows our naming convention.
|
||||
///
|
||||
/// - Parameter filename: The filename to validate
|
||||
/// - Returns: true if it matches our convention, false otherwise
|
||||
/// Validates if a filename follows our naming convention
|
||||
static func isValidImageFilename(_ filename: String) -> Bool {
|
||||
guard filename.hasPrefix("problem_") && filename.hasSuffix(imageExtension) else {
|
||||
return false
|
||||
@@ -79,32 +57,19 @@ class ImageNamingUtils {
|
||||
&& Int(parts[2]) != nil
|
||||
}
|
||||
|
||||
/// Migrates an existing UUID-based filename to our naming convention.
|
||||
/// This is used during sync to rename downloaded images.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - oldFilename: The existing filename (UUID-based)
|
||||
/// - problemId: The problem ID this image belongs to
|
||||
/// - imageIndex: The index of this image
|
||||
/// - Returns: The new filename following our convention
|
||||
/// Migrates an existing filename to our naming convention
|
||||
static func migrateFilename(oldFilename: String, problemId: String, imageIndex: Int) -> String {
|
||||
// If it's already using our convention, keep it
|
||||
|
||||
if isValidImageFilename(oldFilename) {
|
||||
return oldFilename
|
||||
}
|
||||
|
||||
// Generate new deterministic name
|
||||
// Use current timestamp to maintain some consistency
|
||||
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||
return generateImageFilename(
|
||||
problemId: problemId, timestamp: timestamp, imageIndex: imageIndex)
|
||||
}
|
||||
|
||||
/// Creates a deterministic hash from input string.
|
||||
/// Uses SHA-256 and takes first 12 characters for filename safety.
|
||||
///
|
||||
/// - Parameter input: The input string to hash
|
||||
/// - Returns: First 12 characters of SHA-256 hash in lowercase
|
||||
/// Creates a deterministic hash from input string
|
||||
private static func createHash(from input: String) -> String {
|
||||
let inputData = Data(input.utf8)
|
||||
let hashed = SHA256.hash(data: inputData)
|
||||
@@ -112,13 +77,7 @@ class ImageNamingUtils {
|
||||
return String(hashString.prefix(hashLength))
|
||||
}
|
||||
|
||||
/// Batch renames images for a problem to use our naming convention.
|
||||
/// Returns a mapping of old filename -> new filename.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - problemId: The problem ID
|
||||
/// - existingFilenames: List of current image filenames for this problem
|
||||
/// - Returns: Dictionary mapping old filename to new filename
|
||||
/// Batch renames images for a problem to use our naming convention
|
||||
static func batchRenameForProblem(problemId: String, existingFilenames: [String]) -> [String:
|
||||
String]
|
||||
{
|
||||
@@ -135,10 +94,7 @@ class ImageNamingUtils {
|
||||
return renameMap
|
||||
}
|
||||
|
||||
/// Validates that a collection of filenames follow our naming convention.
|
||||
///
|
||||
/// - Parameter filenames: Array of filenames to validate
|
||||
/// - Returns: Dictionary with validation results
|
||||
/// Validates that a collection of filenames follow our naming convention
|
||||
static func validateFilenames(_ filenames: [String]) -> ImageValidationResult {
|
||||
var validImages: [String] = []
|
||||
var invalidImages: [String] = []
|
||||
@@ -159,7 +115,7 @@ class ImageNamingUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of image filename validation
|
||||
// Result of image filename validation
|
||||
struct ImageValidationResult {
|
||||
let totalImages: Int
|
||||
let validImages: [String]
|
||||
|
||||
Reference in New Issue
Block a user