mirror of https://github.com/tasks/tasks
Started cleaning up and refactoring the AB testing classes to fit with the new analytics framework
parent
6e37a22bf8
commit
02049930c9
@ -1,194 +0,0 @@
|
|||||||
package com.todoroo.astrid.service.abtesting;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.todoroo.astrid.service.StatisticsConstants;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to define options with their probabilities and descriptions
|
|
||||||
* @author Sam Bosley <sam@astrid.com>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ABOptions {
|
|
||||||
|
|
||||||
public ABOptions() {
|
|
||||||
bundles = new HashMap<String, ABOptionBundle>();
|
|
||||||
events = new HashMap<String, List<String>>();
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the integer array of weighted probabilities for an option key
|
|
||||||
* @param key
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public synchronized int[] getProbsForKey(String key) {
|
|
||||||
if (bundles.containsKey(key)) {
|
|
||||||
ABOptionBundle bundle = bundles.get(key);
|
|
||||||
return bundle.weightedProbs;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the weighted probability array for a given key. Returns true
|
|
||||||
* on success, false if they key doesn't exist or if the array is the wrong
|
|
||||||
* length.
|
|
||||||
* @param key
|
|
||||||
* @param newProbs
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public synchronized boolean setProbsForKey(String key, int[] newProbs) {
|
|
||||||
if (bundles.containsKey(newProbs)) {
|
|
||||||
ABOptionBundle bundle = bundles.get(key);
|
|
||||||
if (bundle.descriptions == null || newProbs.length == bundle.descriptions.length) {
|
|
||||||
bundle.weightedProbs = newProbs;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the string array of option descriptions for an option key
|
|
||||||
* @param key
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String[] getDescriptionsForKey(String key) {
|
|
||||||
if (bundles.containsKey(key)) {
|
|
||||||
ABOptionBundle bundle = bundles.get(key);
|
|
||||||
return bundle.descriptions;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the description for a particular choice of the given option
|
|
||||||
* @param key
|
|
||||||
* @param optionIndex
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getDescriptionForOption(String key, int optionIndex) {
|
|
||||||
if (bundles.containsKey(key)) {
|
|
||||||
ABOptionBundle bundle = bundles.get(key);
|
|
||||||
if (bundle.descriptions != null && optionIndex < bundle.descriptions.length) {
|
|
||||||
return bundle.descriptions[optionIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps keys (i.e. preference key identifiers) to feature weights and descriptions
|
|
||||||
*/
|
|
||||||
private final HashMap<String, ABOptionBundle> bundles;
|
|
||||||
private final HashMap<String, List<String>> events; // maps events to lists of interested keys
|
|
||||||
|
|
||||||
private static class ABOptionBundle {
|
|
||||||
public int[] weightedProbs;
|
|
||||||
public String[] descriptions;
|
|
||||||
|
|
||||||
public ABOptionBundle(int[] weightedProbs, String[] descriptions) {
|
|
||||||
this.weightedProbs = weightedProbs;
|
|
||||||
this.descriptions = descriptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isValidKey(String key) {
|
|
||||||
return bundles.containsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a localytics attribute array for the specified event.
|
|
||||||
* @param event
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String[] getLocalyticsAttributeArrayForEvent(String event) {
|
|
||||||
ArrayList<String> attributes = new ArrayList<String>();
|
|
||||||
List<String> interestedKeys = events.get(event);
|
|
||||||
if (interestedKeys != null)
|
|
||||||
for (String key : interestedKeys) {
|
|
||||||
// Get choice if exists and add to array
|
|
||||||
if (isValidKey(key)) {
|
|
||||||
ABOptionBundle bundle = bundles.get(key);
|
|
||||||
int choice = ABChooser.readChoiceForOption(key);
|
|
||||||
if (choice != ABChooser.NO_OPTION &&
|
|
||||||
bundle.descriptions != null && choice < bundle.descriptions.length) {
|
|
||||||
attributes.add(key);
|
|
||||||
attributes.add(getDescriptionForOption(key, choice));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attributes.toArray(new String[attributes.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A/B testing options are defined below according to the following spec:
|
|
||||||
*
|
|
||||||
* @param optionKey = "<key>"
|
|
||||||
* --This key is used to identify the option in the application and in the preferences
|
|
||||||
*
|
|
||||||
* @param probs = { int, int, ... }
|
|
||||||
* --The different choices in an option correspond to an index in the probability array.
|
|
||||||
* Probabilities are expressed as integers to easily define relative weights. For example,
|
|
||||||
* the array { 1, 2 } would mean option 0 would happen one time for every two occurrences of option 1
|
|
||||||
*
|
|
||||||
* (optional)
|
|
||||||
* @param descriptions = { "...", "...", ... }
|
|
||||||
* --A string description of each option. Useful for tagging events. The index of
|
|
||||||
* each description should correspond to the events location in the probability array
|
|
||||||
* (i.e. the arrays should be the same length if this one exists)
|
|
||||||
*
|
|
||||||
* (optional)
|
|
||||||
* @param relevantEvents = { "...", "...", ... }
|
|
||||||
* --An arbitrary length list of relevant localytics events. When events are
|
|
||||||
* tagged from StatisticsService, they will be appended with attributes
|
|
||||||
* that have that event in this array
|
|
||||||
*/
|
|
||||||
public void addOption(String optionKey, int[] probs, String[] descriptions, String[] relevantEvents) {
|
|
||||||
ABOptionBundle bundle = new ABOptionBundle(probs, descriptions);
|
|
||||||
bundles.put(optionKey, bundle);
|
|
||||||
|
|
||||||
if (relevantEvents != null) {
|
|
||||||
for (String event : relevantEvents) {
|
|
||||||
List<String> interestedKeys = events.get(event);
|
|
||||||
if (interestedKeys == null) {
|
|
||||||
interestedKeys = new ArrayList<String>();
|
|
||||||
events.put(event, interestedKeys);
|
|
||||||
}
|
|
||||||
interestedKeys.add(optionKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize() { // Set up
|
|
||||||
//Calls to addOption go here
|
|
||||||
addOption(AB_OPTION_SWIPE_ENABLED_KEY, AB_OPTION_SWIPE_ENABLED_PROBS, AB_OPTION_SWIPE_ENABLED_DESC, AB_OPTION_SWIPE_ENABLED_EVENTS);
|
|
||||||
addOption(AB_OPTION_CONTACTS_PICKER_ENABLED, AB_OPTION_CONTACTS_ENABLED_PROBS, AB_OPTION_CONTACTS_ENABLED_DESC, AB_OPTION_CONTACTS_ENABLED_EVENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String AB_OPTION_SWIPE_ENABLED_KEY = "swipeEnabled"; //$NON-NLS-1$
|
|
||||||
private static final int[] AB_OPTION_SWIPE_ENABLED_PROBS = { 1, 1 };
|
|
||||||
private static final String[] AB_OPTION_SWIPE_ENABLED_DESC = { "swipe-lists-disabled", "swipe-lists-enabled" }; //$NON-NLS-1$//$NON-NLS-2$
|
|
||||||
private static final String[] AB_OPTION_SWIPE_ENABLED_EVENTS = { StatisticsConstants.APP_OPEN_THREE_DAYS,
|
|
||||||
StatisticsConstants.APP_OPEN_ONE_WEEK,
|
|
||||||
StatisticsConstants.APP_OPEN_TWO_WEEKS,
|
|
||||||
StatisticsConstants.APP_OPEN_THREE_WEEKS };
|
|
||||||
|
|
||||||
public static final String AB_OPTION_CONTACTS_PICKER_ENABLED = "contactsEnabled"; //$NON-NLS-1$
|
|
||||||
private static final int[] AB_OPTION_CONTACTS_ENABLED_PROBS = { 1, 1 };
|
|
||||||
private static final String[] AB_OPTION_CONTACTS_ENABLED_DESC = { "contacts-disabled", "contacts-enabled" }; //$NON-NLS-1$//$NON-NLS-2$
|
|
||||||
private static final String[] AB_OPTION_CONTACTS_ENABLED_EVENTS = { StatisticsConstants.APP_OPEN_THREE_DAYS,
|
|
||||||
StatisticsConstants.APP_OPEN_ONE_WEEK,
|
|
||||||
StatisticsConstants.APP_OPEN_TWO_WEEKS,
|
|
||||||
StatisticsConstants.APP_OPEN_THREE_WEEKS,
|
|
||||||
StatisticsConstants.TASK_ASSIGNED_EMAIL,
|
|
||||||
StatisticsConstants.TASK_ASSIGNED_PICKER };
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,147 @@
|
|||||||
|
package com.todoroo.astrid.service.abtesting;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to define options with their probabilities and descriptions
|
||||||
|
* @author Sam Bosley <sam@astrid.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ABTests {
|
||||||
|
|
||||||
|
public ABTests() {
|
||||||
|
bundles = new HashMap<String, ABTestBundle>();
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the integer array of weighted probabilities for an option key
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public synchronized int[] getProbsForTestKey(String key) {
|
||||||
|
if (bundles.containsKey(key)) {
|
||||||
|
ABTestBundle bundle = bundles.get(key);
|
||||||
|
return bundle.weightedProbs;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the weighted probability array for a given key. Returns true
|
||||||
|
* on success, false if they key doesn't exist or if the array is the wrong
|
||||||
|
* length.
|
||||||
|
* @param key
|
||||||
|
* @param newProbs
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public synchronized boolean setProbsForTestKey(String key, int[] newProbs) {
|
||||||
|
if (bundles.containsKey(newProbs)) {
|
||||||
|
ABTestBundle bundle = bundles.get(key);
|
||||||
|
if (bundle.descriptions == null || newProbs.length == bundle.descriptions.length) {
|
||||||
|
bundle.weightedProbs = newProbs;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string array of option descriptions for an option key
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String[] getDescriptionsForTestKey(String key) {
|
||||||
|
if (bundles.containsKey(key)) {
|
||||||
|
ABTestBundle bundle = bundles.get(key);
|
||||||
|
return bundle.descriptions;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the description for a particular choice of the given option
|
||||||
|
* @param testKey
|
||||||
|
* @param optionIndex
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getDescriptionForTestOption(String testKey, int optionIndex) {
|
||||||
|
if (bundles.containsKey(testKey)) {
|
||||||
|
ABTestBundle bundle = bundles.get(testKey);
|
||||||
|
if (bundle.descriptions != null && optionIndex < bundle.descriptions.length) {
|
||||||
|
return bundle.descriptions[optionIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAllTestKeys() {
|
||||||
|
return bundles.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps keys (i.e. preference key identifiers) to feature weights and descriptions
|
||||||
|
*/
|
||||||
|
private final HashMap<String, ABTestBundle> bundles;
|
||||||
|
|
||||||
|
private static class ABTestBundle {
|
||||||
|
public int[] weightedProbs;
|
||||||
|
public String[] descriptions;
|
||||||
|
|
||||||
|
public ABTestBundle(int[] weightedProbs, String[] descriptions) {
|
||||||
|
this.weightedProbs = weightedProbs;
|
||||||
|
this.descriptions = descriptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidTestKey(String key) {
|
||||||
|
return bundles.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A/B testing options are defined below according to the following spec:
|
||||||
|
*
|
||||||
|
* @param testKey = "<key>"
|
||||||
|
* --This key is used to identify the option in the application and in the preferences
|
||||||
|
*
|
||||||
|
* @param probs = { int, int, ... }
|
||||||
|
* --The different choices in an option correspond to an index in the probability array.
|
||||||
|
* Probabilities are expressed as integers to easily define relative weights. For example,
|
||||||
|
* the array { 1, 2 } would mean option 0 would happen one time for every two occurrences of option 1
|
||||||
|
*
|
||||||
|
* (optional)
|
||||||
|
* @param descriptions = { "...", "...", ... }
|
||||||
|
* --A string description of each option. Useful for tagging events. The index of
|
||||||
|
* each description should correspond to the events location in the probability array
|
||||||
|
* (i.e. the arrays should be the same length if this one exists)
|
||||||
|
*
|
||||||
|
* (optional)
|
||||||
|
* @param relevantEvents = { "...", "...", ... }
|
||||||
|
* --An arbitrary length list of relevant localytics events. When events are
|
||||||
|
* tagged from StatisticsService, they will be appended with attributes
|
||||||
|
* that have that event in this array
|
||||||
|
*/
|
||||||
|
public void addTest(String testKey, int[] probs, String[] descriptions) {
|
||||||
|
ABTestBundle bundle = new ABTestBundle(probs, descriptions);
|
||||||
|
bundles.put(testKey, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() { // Set up
|
||||||
|
//Calls to addTest go here
|
||||||
|
addTest(AB_TEST_SWIPE_ENABLED_KEY, AB_TEST_SWIPE_ENABLED_PROBS, AB_TEST_SWIPE_ENABLED_DESC);
|
||||||
|
addTest(AB_TEST_CONTACTS_PICKER_ENABLED, AB_TEST_CONTACTS_ENABLED_PROBS, AB_TEST_CONTACTS_ENABLED_DESC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String AB_TEST_SWIPE_ENABLED_KEY = "swipeEnabled"; //$NON-NLS-1$
|
||||||
|
private static final int[] AB_TEST_SWIPE_ENABLED_PROBS = { 1, 1 };
|
||||||
|
private static final String[] AB_TEST_SWIPE_ENABLED_DESC = { "swipe-lists-disabled", "swipe-lists-enabled" }; //$NON-NLS-1$//$NON-NLS-2$
|
||||||
|
|
||||||
|
public static final String AB_TEST_CONTACTS_PICKER_ENABLED = "contactsEnabled"; //$NON-NLS-1$
|
||||||
|
private static final int[] AB_TEST_CONTACTS_ENABLED_PROBS = { 1, 1 };
|
||||||
|
private static final String[] AB_TEST_CONTACTS_ENABLED_DESC = { "contacts-disabled", "contacts-enabled" }; //$NON-NLS-1$//$NON-NLS-2$
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue