Skip to content

Commit 4314a71

Browse files
committed
Track specific sync skip reasons
1 parent 5d49005 commit 4314a71

File tree

3 files changed

+86
-18
lines changed

3 files changed

+86
-18
lines changed

Modules/Sources/WooFoundationCore/Analytics/WooAnalyticsEvent.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,10 @@ extension WooAnalyticsEvent {
139139
error: error)
140140
}
141141

142-
public static func syncSkipped(reason: String) -> WooAnalyticsEvent {
142+
public static func syncSkipped(reason: String, syncType: String) -> WooAnalyticsEvent {
143143
WooAnalyticsEvent(statName: .pointOfSaleLocalCatalogSyncSkipped,
144-
properties: [Key.reason: reason])
144+
properties: [Key.reason: reason,
145+
Key.syncType: syncType])
145146
}
146147
}
147148
}

Modules/Sources/Yosemite/Protocols/POSLocalCatalogEligibilityServiceProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public enum POSLocalCatalogIneligibleReason: Equatable {
2222
public var skipReason: String {
2323
switch self {
2424
case .posTabNotEligible:
25-
return "pos_inactive"
25+
return "pos_not_eligible"
2626
case .featureFlagDisabled:
2727
return "feature_flag_disabled"
2828
case .unsupportedWooCommerceVersion:

Modules/Sources/Yosemite/Tools/POS/POSCatalogSyncCoordinator.swift

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ public actor POSCatalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol {
149149

150150
guard try await shouldPerformFullSync(for: siteID, maxAge: maxAge) else {
151151
let reason = await getSyncSkipReason(for: siteID, maxAge: maxAge)
152-
trackAnalytics(WooAnalyticsEvent.LocalCatalog.syncSkipped(reason: reason))
152+
trackAnalytics(WooAnalyticsEvent.LocalCatalog.syncSkipped(reason: reason,
153+
syncType: POSCatalogSyncType.full.rawValue))
153154
throw POSCatalogSyncError.shouldNotSync
154155
}
155156

@@ -315,7 +316,8 @@ public actor POSCatalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol {
315316

316317
guard try await shouldPerformIncrementalSync(for: siteID, maxAge: maxAge) else {
317318
let reason = await getIncrementalSyncSkipReason(for: siteID, maxAge: maxAge)
318-
trackAnalytics(WooAnalyticsEvent.LocalCatalog.syncSkipped(reason: reason))
319+
trackAnalytics(WooAnalyticsEvent.LocalCatalog.syncSkipped(reason: reason,
320+
syncType: POSCatalogSyncType.incremental.rawValue))
319321
return
320322
}
321323

@@ -596,6 +598,16 @@ public actor POSCatalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol {
596598
return "eligibility_check_failed"
597599
}
598600

601+
// Check sync eligibility
602+
do {
603+
let eligibility = try await syncEligibility(for: siteID)
604+
if case .ineligible(let reason) = eligibility {
605+
return reason.skipReason
606+
}
607+
} catch {
608+
return "sync_eligibility_check_failed"
609+
}
610+
599611
// Check if sync is needed based on age
600612
guard let lastSyncDate = await lastFullSyncDate(for: siteID) else {
601613
return "no_previous_sync" // This shouldn't happen if shouldPerformFullSync returned false
@@ -610,7 +622,7 @@ public actor POSCatalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol {
610622
}
611623

612624
private func getIncrementalSyncSkipReason(for siteID: Int64, maxAge: TimeInterval) async -> String {
613-
// Check eligibility first
625+
// Check overall local catalog eligibility first
614626
do {
615627
let eligibility = try await catalogEligibilityChecker.catalogEligibility(for: siteID)
616628
if case .ineligible(let reason) = eligibility {
@@ -620,6 +632,16 @@ public actor POSCatalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol {
620632
return "eligibility_check_failed"
621633
}
622634

635+
// Check sync eligibility
636+
do {
637+
let eligibility = try await syncEligibility(for: siteID)
638+
if case .ineligible(let reason) = eligibility {
639+
return reason.skipReason
640+
}
641+
} catch {
642+
return "sync_eligibility_check_failed"
643+
}
644+
623645
// Check if full sync exists
624646
guard await lastFullSyncDate(for: siteID) != nil else {
625647
return "no_full_sync"
@@ -703,19 +725,43 @@ private extension POSCatalogSyncCoordinator {
703725

704726
/// Checks if sync is eligible for the given site based on catalog eligibility and temporal criteria
705727
func checkSyncEligibility(for siteID: Int64) async throws -> Bool {
706-
guard try await catalogEligibilityChecker.catalogEligibility(for: siteID) == .eligible else {
707-
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) - Catalog ineligible")
728+
switch try await syncEligibility(for: siteID) {
729+
case .eligible(reason: let reason):
730+
switch reason {
731+
case .neverSynced:
732+
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) eligible (no first sync date recorded)")
733+
case .openedRecently(daysSinceOpened: let daysSinceOpened):
734+
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) eligible (last opened \(daysSinceOpened) days ago)")
735+
case .recentFirstSync(daysSinceFirstSync: let daysSinceFirstSync):
736+
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) eligible (within grace period: \(daysSinceFirstSync) days since first sync)")
737+
}
738+
return true
739+
case .ineligible(reason: let reason):
740+
switch reason {
741+
case .notEligibleForLocalCatalog:
742+
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) - Catalog ineligible")
743+
case .notOpenedRecently(daysSinceOpened: let daysSinceOpened):
744+
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) ineligible (POS last opened \(daysSinceOpened) days ago)")
745+
case .neverOpenedAndPastFirstSyncGracePeriod(daysSinceFirstSync: let daysSinceFirstSync):
746+
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) ineligible (past 30-day grace period, " +
747+
"no recent POS open, \(daysSinceFirstSync) days since first sync)")
748+
}
708749
return false
709750
}
751+
}
752+
753+
func syncEligibility(for siteID: Int64) async throws -> SyncEligibility {
754+
guard try await catalogEligibilityChecker.catalogEligibility(for: siteID) == .eligible else {
755+
return .ineligible(reason: .notEligibleForLocalCatalog)
756+
}
710757

711758
// Then check temporal eligibility (30-day criteria)
712759
let firstSyncDate = siteSettings.getFirstPOSCatalogSyncDate(siteID: siteID)
713760
let lastOpenedDate = siteSettings.getPOSLastOpenedDate(siteID: siteID)
714761

715762
// Case 1: No first sync date yet - eligible (will be set on first sync)
716763
guard let firstSync = firstSyncDate else {
717-
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) eligible (no first sync date recorded)")
718-
return true
764+
return .eligible(reason: .neverSynced)
719765
}
720766

721767
// Case 2: Has synced before. Check if within 30-day window from first sync
@@ -724,23 +770,44 @@ private extension POSCatalogSyncCoordinator {
724770
if daysSinceFirstSync > Constants.maxDaysSinceLastOpened {
725771
// More than 30 days since first sync - must have opened POS recently to remain eligible
726772
guard let lastOpened = lastOpenedDate else {
727-
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) ineligible (past 30-day grace period, no recent POS open)")
728-
return false
773+
return .ineligible(reason: .neverOpenedAndPastFirstSyncGracePeriod(daysSinceFirstSync: daysSinceFirstSync))
729774
}
730775

731776
let daysSinceLastOpened = Calendar.current.dateComponents([.day], from: lastOpened, to: Date()).day ?? 0
732777

733778
if daysSinceLastOpened <= Constants.maxDaysSinceLastOpened {
734-
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) eligible (last opened \(daysSinceLastOpened) days ago)")
735-
return true
779+
return .eligible(reason: .openedRecently(daysSinceOpened: daysSinceLastOpened))
736780
} else {
737-
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) ineligible (POS last opened \(daysSinceLastOpened) days ago)")
738-
return false
781+
return .ineligible(reason: .notOpenedRecently(daysSinceOpened: daysSinceLastOpened))
739782
}
740783
} else {
741784
// Within 30 days of first sync - always eligible (new user grace period)
742-
DDLogInfo("📋 POSCatalogSyncCoordinator: Site \(siteID) eligible (within grace period: \(daysSinceFirstSync) days since first sync)")
743-
return true
785+
return .eligible(reason: .recentFirstSync(daysSinceFirstSync: daysSinceFirstSync))
786+
}
787+
}
788+
789+
enum SyncEligibility {
790+
case eligible(reason: EligibleReason)
791+
case ineligible(reason: IneligibleReason)
792+
793+
enum EligibleReason {
794+
case neverSynced
795+
case openedRecently(daysSinceOpened: Int)
796+
case recentFirstSync(daysSinceFirstSync: Int)
797+
}
798+
799+
enum IneligibleReason {
800+
case notEligibleForLocalCatalog
801+
case notOpenedRecently(daysSinceOpened: Int)
802+
case neverOpenedAndPastFirstSyncGracePeriod(daysSinceFirstSync: Int)
803+
804+
var skipReason: String {
805+
switch self {
806+
case .notEligibleForLocalCatalog: "not_eligible"
807+
case .notOpenedRecently: "pos_not_opened_30_days"
808+
case .neverOpenedAndPastFirstSyncGracePeriod: "pos_not_opened_30_days"
809+
}
810+
}
744811
}
745812
}
746813

0 commit comments

Comments
 (0)