-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
change api #1133
change api #1133
Changes from 4 commits
590f416
511f3a8
f303cfa
e5c0ba3
9aa81e8
6b67ffb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -72,7 +72,7 @@ extension PixelKit { | |||||||||||||||||||||||||||||||
ExperimentConfig.fireFunction(event, .uniqueByNameAndParameters, false) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/// Fires a pixel for a specific action in an experiment, based on conversion window and value thresholds (if value is a number). | ||||||||||||||||||||||||||||||||
/// Fires a pixel for a specific action in an experiment, based on conversion window and value. | ||||||||||||||||||||||||||||||||
/// - Parameters: | ||||||||||||||||||||||||||||||||
/// - subfeatureID: Identifier for the subfeature associated with the experiment. | ||||||||||||||||||||||||||||||||
/// - metric: The name of the metric being tracked (e.g., "searches"). | ||||||||||||||||||||||||||||||||
|
@@ -82,7 +82,7 @@ extension PixelKit { | |||||||||||||||||||||||||||||||
/// This function: | ||||||||||||||||||||||||||||||||
/// 1. Validates if the experiment is active. | ||||||||||||||||||||||||||||||||
/// 2. Ensures the user is within the specified conversion window. | ||||||||||||||||||||||||||||||||
/// 3. Tracks actions performed and sends the pixel once the target value is reached (if applicable). | ||||||||||||||||||||||||||||||||
/// 3. Sends the pixel if not sent before (unique by name and parameter) | ||||||||||||||||||||||||||||||||
public static func fireExperimentPixel(for subfeatureID: SubfeatureID, | ||||||||||||||||||||||||||||||||
metric: String, | ||||||||||||||||||||||||||||||||
conversionWindowDays: ConversionWindow, | ||||||||||||||||||||||||||||||||
|
@@ -94,11 +94,43 @@ extension PixelKit { | |||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
guard let experimentData = featureFlagger.getAllActiveExperiments()[subfeatureID] else { return } | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Define event | ||||||||||||||||||||||||||||||||
let event = event(for: subfeatureID, experimentData: experimentData, conversionWindowDays: conversionWindowDays, metric: metric, value: value) | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Send unique by name and parameter pixel if within conversion window | ||||||||||||||||||||||||||||||||
let isInWindow = isUserInConversionWindow(conversionWindowDays, enrollmentDate: experimentData.enrollmentDate) | ||||||||||||||||||||||||||||||||
if isInWindow { | ||||||||||||||||||||||||||||||||
ExperimentConfig.fireFunction(event, .uniqueByNameAndParameters, false) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: We could avoid the creation of the event and return early by moving the conversion window check up:
Suggested change
|
||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/// Fires a pixel for a specific action in an experiment, based on conversion window and value thresholds. | ||||||||||||||||||||||||||||||||
/// - Parameters: | ||||||||||||||||||||||||||||||||
/// - subfeatureID: Identifier for the subfeature associated with the experiment. | ||||||||||||||||||||||||||||||||
/// - metric: The name of the metric being tracked (e.g., "searches"). | ||||||||||||||||||||||||||||||||
/// - conversionWindowDays: The range of days after enrollment during which the action is valid. | ||||||||||||||||||||||||||||||||
/// - numberOfCalls: target number of actions required to fire the pixel. | ||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||
/// This function: | ||||||||||||||||||||||||||||||||
/// 1. Validates if the experiment is active. | ||||||||||||||||||||||||||||||||
/// 2. Ensures the user is within the specified conversion window. | ||||||||||||||||||||||||||||||||
/// 3. Tracks actions performed and sends the pixel once the target value is reached (if applicable). | ||||||||||||||||||||||||||||||||
public static func fireExperimentPixelWhenReachingNumberOfCalls(for subfeatureID: SubfeatureID, | ||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you have any better name suggestion do let me know! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could include "threshold" in the name, e.g:
|
||||||||||||||||||||||||||||||||
metric: String, | ||||||||||||||||||||||||||||||||
conversionWindowDays: ConversionWindow, | ||||||||||||||||||||||||||||||||
numberOfCalls: Int) { | ||||||||||||||||||||||||||||||||
// Check is active experiment for user | ||||||||||||||||||||||||||||||||
guard let featureFlagger = ExperimentConfig.featureFlagger else { | ||||||||||||||||||||||||||||||||
assertionFailure("PixelKit is not configured for experiments") | ||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
guard let experimentData = featureFlagger.getAllActiveExperiments()[subfeatureID] else { return } | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
fireExperimentPixelForActiveExperiment(subfeatureID, | ||||||||||||||||||||||||||||||||
experimentData: experimentData, | ||||||||||||||||||||||||||||||||
metric: metric, | ||||||||||||||||||||||||||||||||
conversionWindowDays: conversionWindowDays, | ||||||||||||||||||||||||||||||||
value: value) | ||||||||||||||||||||||||||||||||
numberOfCalls: numberOfCalls) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
/// Fires search-related experiment pixels for all active experiments. | ||||||||||||||||||||||||||||||||
|
@@ -172,7 +204,7 @@ extension PixelKit { | |||||||||||||||||||||||||||||||
experimentData: experimentData, | ||||||||||||||||||||||||||||||||
metric: metric, | ||||||||||||||||||||||||||||||||
conversionWindowDays: range, | ||||||||||||||||||||||||||||||||
value: "\(value)" | ||||||||||||||||||||||||||||||||
numberOfCalls: value | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
@@ -182,39 +214,24 @@ extension PixelKit { | |||||||||||||||||||||||||||||||
experimentData: ExperimentData, | ||||||||||||||||||||||||||||||||
metric: String, | ||||||||||||||||||||||||||||||||
conversionWindowDays: ConversionWindow, | ||||||||||||||||||||||||||||||||
value: String) { | ||||||||||||||||||||||||||||||||
numberOfCalls: Int) { | ||||||||||||||||||||||||||||||||
// Set parameters, event name, store key | ||||||||||||||||||||||||||||||||
let eventName = "\(Constants.metricsEventPrefix)_\(subfeatureID)_\(experimentData.cohortID)" | ||||||||||||||||||||||||||||||||
let conversionWindowValue = (conversionWindowDays.lowerBound != conversionWindowDays.upperBound) ? | ||||||||||||||||||||||||||||||||
"\(conversionWindowDays.lowerBound)-\(conversionWindowDays.upperBound)" : | ||||||||||||||||||||||||||||||||
"\(conversionWindowDays.lowerBound)" | ||||||||||||||||||||||||||||||||
let parameters: [String: String] = [ | ||||||||||||||||||||||||||||||||
Constants.metricKey: metric, | ||||||||||||||||||||||||||||||||
Constants.conversionWindowDaysKey: conversionWindowValue, | ||||||||||||||||||||||||||||||||
Constants.valueKey: value, | ||||||||||||||||||||||||||||||||
Constants.enrollmentDateKey: experimentData.enrollmentDate.toYYYYMMDDInET() | ||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||
let event = ExperimentEvent(name: eventName, parameters: parameters) | ||||||||||||||||||||||||||||||||
let eventStoreKey = "\(eventName)_\(parameters.toString())" | ||||||||||||||||||||||||||||||||
let event = event(for: subfeatureID, experimentData: experimentData, conversionWindowDays: conversionWindowDays, metric: metric, value: String(numberOfCalls)) | ||||||||||||||||||||||||||||||||
let parameters = parameters(metric: metric, conversionWindowDays: conversionWindowDays, value: String(numberOfCalls), experimentData: experimentData) | ||||||||||||||||||||||||||||||||
let eventStoreKey = "\(event.name)_\(parameters.toString())" | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Determine if the user is within the conversion window | ||||||||||||||||||||||||||||||||
let isInWindow = isUserInConversionWindow(conversionWindowDays, enrollmentDate: experimentData.enrollmentDate) | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Check if value is a number | ||||||||||||||||||||||||||||||||
if let numberOfAction = NumberOfActions(value), numberOfAction > 1 { | ||||||||||||||||||||||||||||||||
// Increment or remove based on conversion window status | ||||||||||||||||||||||||||||||||
let shouldSendPixel = ExperimentConfig.eventTracker.incrementAndCheckThreshold( | ||||||||||||||||||||||||||||||||
forKey: eventStoreKey, | ||||||||||||||||||||||||||||||||
threshold: numberOfAction, | ||||||||||||||||||||||||||||||||
isInWindow: isInWindow | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
// Increment or remove based on conversion window status | ||||||||||||||||||||||||||||||||
let shouldSendPixel = ExperimentConfig.eventTracker.incrementAndCheckThreshold( | ||||||||||||||||||||||||||||||||
forKey: eventStoreKey, | ||||||||||||||||||||||||||||||||
threshold: numberOfCalls, | ||||||||||||||||||||||||||||||||
isInWindow: isInWindow | ||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
// Send the pixel only if conditions are met | ||||||||||||||||||||||||||||||||
if shouldSendPixel { | ||||||||||||||||||||||||||||||||
ExperimentConfig.fireFunction(event, .uniqueByNameAndParameters, false) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} else if isInWindow { | ||||||||||||||||||||||||||||||||
// If value is not a number, send the pixel only if within the window | ||||||||||||||||||||||||||||||||
// Send the pixel only if conditions are met | ||||||||||||||||||||||||||||||||
if shouldSendPixel { | ||||||||||||||||||||||||||||||||
ExperimentConfig.fireFunction(event, .uniqueByNameAndParameters, false) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
@@ -233,6 +250,24 @@ extension PixelKit { | |||||||||||||||||||||||||||||||
return currentDate >= calendar.startOfDay(for: startOfWindow) && | ||||||||||||||||||||||||||||||||
currentDate <= calendar.startOfDay(for: endOfWindow) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
private static func event(for subfeatureID: SubfeatureID, experimentData: ExperimentData, conversionWindowDays: ConversionWindow, metric: String, value: String) -> ExperimentEvent{ | ||||||||||||||||||||||||||||||||
let eventName = "\(Constants.metricsEventPrefix)_\(subfeatureID)_\(experimentData.cohortID)" | ||||||||||||||||||||||||||||||||
let parameters = parameters(metric: metric, conversionWindowDays: conversionWindowDays, value: value, experimentData: experimentData) | ||||||||||||||||||||||||||||||||
return ExperimentEvent(name: eventName, parameters: parameters) | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
private static func parameters(metric: String, conversionWindowDays: ConversionWindow, value: String, experimentData: ExperimentData) -> [String: String] { | ||||||||||||||||||||||||||||||||
let conversionWindowValue = (conversionWindowDays.lowerBound != conversionWindowDays.upperBound) ? | ||||||||||||||||||||||||||||||||
"\(conversionWindowDays.lowerBound)-\(conversionWindowDays.upperBound)" : | ||||||||||||||||||||||||||||||||
"\(conversionWindowDays.lowerBound)" | ||||||||||||||||||||||||||||||||
return [ | ||||||||||||||||||||||||||||||||
Constants.metricKey: metric, | ||||||||||||||||||||||||||||||||
Constants.conversionWindowDaysKey: conversionWindowValue, | ||||||||||||||||||||||||||||||||
Constants.valueKey: value, | ||||||||||||||||||||||||||||||||
Constants.enrollmentDateKey: experimentData.enrollmentDate.toYYYYMMDDInET() | ||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
extension Date { | ||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we not be storing the pixel that was sent in this method so that we can ensure pixels are unique? (i.e not sent again if they were sent before). When the value being sent is unchanged, this method fires the same pixel on subsequent calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind the above, missed the uniqueness being handled in
pixelkit