From f1b013a88c376bc4ac788049c9f6f9ed348dc276 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 9 Apr 2025 10:16:40 +0200 Subject: [PATCH] fix search view model --- PadelClub/Data/PlayerRegistration.swift | 20 +++-- PadelClub/Extensions/String+Extensions.swift | 46 +++++++++++- PadelClub/ViewModel/SearchViewModel.swift | 75 +++++++++++++------ .../Screen/InscriptionManagerView.swift | 6 ++ 4 files changed, 116 insertions(+), 31 deletions(-) diff --git a/PadelClub/Data/PlayerRegistration.swift b/PadelClub/Data/PlayerRegistration.swift index cfa2315..d190a82 100644 --- a/PadelClub/Data/PlayerRegistration.swift +++ b/PadelClub/Data/PlayerRegistration.swift @@ -254,15 +254,21 @@ final class PlayerRegistration: BasePlayerRegistration, SideStorable { #endif if let dataFound = try await history(from: sources) { - rank = dataFound.rankValue?.toInt() - points = dataFound.points - tournamentPlayed = dataFound.tournamentCountValue?.toInt() + await MainActor.run { + rank = dataFound.rankValue?.toInt() + points = dataFound.points + tournamentPlayed = dataFound.tournamentCountValue?.toInt() + } } else if let dataFound = try await historyFromName(from: sources) { - rank = dataFound.rankValue?.toInt() - points = dataFound.points - tournamentPlayed = dataFound.tournamentCountValue?.toInt() + await MainActor.run { + rank = dataFound.rankValue?.toInt() + points = dataFound.points + tournamentPlayed = dataFound.tournamentCountValue?.toInt() + } } else { - rank = lastRank + await MainActor.run { + rank = lastRank + } } } diff --git a/PadelClub/Extensions/String+Extensions.swift b/PadelClub/Extensions/String+Extensions.swift index 21231bb..eef13a6 100644 --- a/PadelClub/Extensions/String+Extensions.swift +++ b/PadelClub/Extensions/String+Extensions.swift @@ -162,8 +162,50 @@ extension String { } func licencesFound() -> [String] { - let matches = self.matches(of: /[1-9][0-9]{5,7}/) - return matches.map { String(self[$0.range]) } + // First try to find licenses with format: 5-8 digits followed by optional letter + let precisePattern = /[1-9][0-9]{5,7}[ ]?[A-Za-z]?/ + let preciseMatches = self.matches(of: precisePattern) + let preciseResults = preciseMatches.map { String(self[$0.range]).trimmingCharacters(in: .whitespaces) } + + // If we find potential licenses with the precise pattern + if !preciseResults.isEmpty { + // Filter to only include those with trailing letters + let licensesWithLetters = preciseResults.filter { + let lastChar = $0.last + return lastChar != nil && lastChar!.isLetter + } + + print("๐ŸŽซ Found \(preciseResults.count) potential licenses, filtering to \(licensesWithLetters.count) with trailing letters") + + // If we have licenses with letters, validate them + if !licensesWithLetters.isEmpty { + let validLicenses = licensesWithLetters.filter { $0.isLicenseNumber } + + // If we have valid licenses, return the numeric part of each + if !validLicenses.isEmpty { + let numericLicenses = validLicenses.map { license -> String in + // Extract just the numeric part (all characters except the last letter) + if let lastChar = license.last, lastChar.isLetter { + return String(license.dropLast()) + } + return license + } + + if numericLicenses.isEmpty == false { + print("๐ŸŽซ Found valid licenses: \(validLicenses), returning numeric parts: \(numericLicenses)") + return numericLicenses + } + } + } + } + + // Fallback to just number pattern if we didn't find good matches + let numberPattern = /[1-9][0-9]{5,7}/ + let numberMatches = self.matches(of: numberPattern) + let numberResults = numberMatches.map { String(self[$0.range]) } + + print("๐ŸŽซ Falling back to number-only pattern, found: \(numberResults)") + return numberResults } } diff --git a/PadelClub/ViewModel/SearchViewModel.swift b/PadelClub/ViewModel/SearchViewModel.swift index 9320e26..e3c43e5 100644 --- a/PadelClub/ViewModel/SearchViewModel.swift +++ b/PadelClub/ViewModel/SearchViewModel.swift @@ -472,32 +472,42 @@ class SearchViewModel: ObservableObject, Identifiable { static func pastePredicate( pasteField: String, mostRecentDate: Date?, filterOption: PlayerFilterOption ) -> NSPredicate? { + print("๐Ÿ” pastePredicate called with: \(pasteField)") + print("๐Ÿ“… mostRecentDate: \(String(describing: mostRecentDate))") + print("๐Ÿ” filterOption: \(filterOption)") + var andPredicates = [NSPredicate]() var orPredicates = [NSPredicate]() // Check for license numbers let matches = pasteField.licencesFound() + print("๐ŸŽซ Licenses found: \(matches)") let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) } orPredicates = licensesPredicates if matches.count == 2 { + print("โœ… Returning early with 2 license predicates") return NSCompoundPredicate(orPredicateWithSubpredicates: orPredicates) } // Add gender filter if specified if filterOption == .female { + print("๐Ÿ‘ฉ Adding female filter") andPredicates.append(NSPredicate(format: "male == NO")) } else if filterOption == .male { + print("๐Ÿ‘จ Adding male filter") andPredicates.append(NSPredicate(format: "male == YES")) } // Add date filter if specified if let mostRecentDate { + print("๐Ÿ“† Adding date filter for: \(mostRecentDate)") andPredicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg)) } // Check for slashes (representing alternatives) if let slashPredicate = getSpecialSlashPredicate(inputString: pasteField) { + print("๐Ÿ”€ Found slash predicate") orPredicates.append(slashPredicate) } @@ -506,66 +516,80 @@ class SearchViewModel: ObservableObject, Identifiable { pasteField .replacingOccurrences(of: "[\\(\\)\\[\\]\\{\\}]", with: "", options: .regularExpression) .replacingOccurrences(of: "/", with: " ") // Replace slashes with spaces + .replacingOccurrences(of: "[\\.,;:!?]", with: " ", options: .regularExpression) // Replace common punctuation with spaces .trimmingCharacters(in: .whitespacesAndNewlines) + + print("๐Ÿงน Cleaned text: \"\(text)\"") // Remove digits let digitPattern = /\b\w*\d\w*\b/ text = text.replacing(digitPattern, with: "").trimmingCharacters( in: .whitespacesAndNewlines) + print("๐Ÿ”ข After digit removal: \"\(text)\"") // Split text by whitespace to get potential name components let textComponents = text.components(separatedBy: .whitespacesAndNewlines) .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { - !$0.isEmpty && $0.count > 1 && !["de", "la", "le", "du"].contains($0.lowercased()) + !$0.isEmpty && $0.count > 1 && !["de", "la", "le", "du", "et", "si"].contains($0.lowercased()) } + + print("๐Ÿ“š Text components: \(textComponents)") if textComponents.count < 50 { + print("โœ“ Text components count is reasonable: \(textComponents.count)") + // Handle exact fullname match let fullName = textComponents.joined(separator: " ") if !fullName.isEmpty { + print("๐Ÿ“ Adding predicate for full name: \"\(fullName)\"") orPredicates.append( NSPredicate(format: "canonicalFullName CONTAINS[cd] %@", fullName)) } // Handle hyphenated last names let hyphenatedComponents = textComponents.filter { $0.contains("-") } + print("๐Ÿ”— Hyphenated components: \(hyphenatedComponents)") for component in hyphenatedComponents { orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", component)) + print("โž• Added hyphenated last name predicate: \"\(component)\"") // Also search for each part of the hyphenated name let parts = component.components(separatedBy: "-") for part in parts { if part.count > 1 { orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", part)) + print("โž• Added hyphenated part predicate: \"\(part)\"") } } } // Try different combinations for first/last name if textComponents.count > 1 { + print("๐Ÿ”„ Creating combinations with \(textComponents.count) components") // Try each pair of components as first+last and last+first - for i in 0..