@appannie/ab-testing
Advanced tools
Comparing version 1.2.2 to 1.3.0
@@ -6,2 +6,10 @@ # Change Log | ||
## 1.3.0 (2022-03-08) | ||
**Note:** Version bump only for package @appannie/ab-testing | ||
### 1.2.2 (2021-08-23) | ||
@@ -8,0 +16,0 @@ |
@@ -0,8 +1,12 @@ | ||
declare type UserProfile = { | ||
[s: string]: string; | ||
}; | ||
declare type ForceInclude = { | ||
[key: string]: string[]; | ||
}; | ||
declare type Cohort = { | ||
export declare type Cohort = { | ||
name: string; | ||
allocation?: [number, number][]; | ||
force_include?: ForceInclude; | ||
allocation_criteria?: ForceInclude; | ||
}; | ||
@@ -18,2 +22,3 @@ declare type Experiment = { | ||
}; | ||
export declare function validateAllocation(cohort: Cohort, userProfile: UserProfile, userSegmentNum: number): boolean; | ||
export declare class Experiments { | ||
@@ -20,0 +25,0 @@ config: { |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Experiments = void 0; | ||
exports.Experiments = exports.validateAllocation = void 0; | ||
const js_crc32c_1 = __importDefault(require("fast-crc32c/impls/js_crc32c")); | ||
@@ -12,18 +12,45 @@ function getModuloValue(experiment, userId) { | ||
} | ||
function validateCriteria(criteria, userProfile) { | ||
for (const key in criteria) { | ||
if (!criteria[key].includes(userProfile[key])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function validateAllocation(cohort, userProfile, userSegmentNum) { | ||
let withinRange = false; | ||
let fulfillsCriteria = true; | ||
for (const allocation of cohort.allocation || []) { | ||
withinRange = allocation[0] <= userSegmentNum && userSegmentNum < allocation[1]; | ||
if (withinRange) { | ||
break; | ||
} | ||
} | ||
if (withinRange && cohort.allocation_criteria) { | ||
fulfillsCriteria = validateCriteria(cohort.allocation_criteria, userProfile); | ||
} | ||
return withinRange && fulfillsCriteria; | ||
} | ||
exports.validateAllocation = validateAllocation; | ||
function validateForceInclude(cohort, userProfile) { | ||
if (cohort.force_include) { | ||
for (const key in cohort.force_include) { | ||
if (cohort.force_include[key].includes(userProfile[key])) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
function matchUserCohort(experimentConfig, userId, userProfile) { | ||
let allocatedCohort = 'control'; | ||
const userSegmentNum = getModuloValue(experimentConfig.name, userId); | ||
let allocatedCohort = 'control'; | ||
for (const cohort of experimentConfig.cohorts) { | ||
if (cohort.force_include) { | ||
for (const key in cohort.force_include) { | ||
if (cohort.force_include[key].includes(userProfile[key])) { | ||
return cohort.name; | ||
} | ||
} | ||
if (validateForceInclude(cohort, userProfile)) { | ||
return cohort.name; | ||
} | ||
if (allocatedCohort === 'control') { | ||
for (const allocation of cohort.allocation || []) { | ||
if (allocation[0] <= userSegmentNum && userSegmentNum < allocation[1]) { | ||
allocatedCohort = cohort.name; | ||
} | ||
if (allocatedCohort === 'control' && cohort.allocation) { | ||
if (validateAllocation(cohort, userProfile, userSegmentNum)) { | ||
allocatedCohort = cohort.name; | ||
} | ||
@@ -30,0 +57,0 @@ } |
@@ -7,3 +7,3 @@ { | ||
}, | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"description": "", | ||
@@ -23,3 +23,3 @@ "files": [ | ||
}, | ||
"gitHead": "4660c199fb4ea6b7f21ad84d401b0d33e067a818" | ||
"gitHead": "491a631d53a8f8c1f38dd62c2ae145d057a477dc" | ||
} |
@@ -6,22 +6,22 @@ <!-- TOC --> | ||
- [Package API References](#package-api-references) | ||
- [For React: `@appannie/react-ab-testing`](#for-react-appanniereact-ab-testing) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [For Vanilla Javascript: `@appannie/ab-testing`](#for-vanilla-javascript-appannieab-testing) | ||
- [Installation](#installation-1) | ||
- [Usage](#usage-1) | ||
- [Javascript Hashing Utils `@appannie/ab-testing-hash-object`](#javascript-hashing-utils-appannieab-testing-hash-object) | ||
- [Installation](#installation-2) | ||
- [Usage](#usage-2) | ||
- [For Python: `py-ab-testing`](#for-python-py-ab-testing) | ||
- [Installation](#installation-3) | ||
- [Usage](#usage-3) | ||
- [Configration Hashing](#configration-hashing) | ||
- [Prepare config file BEFORE make it public](#prepare-config-file-before-make-it-public) | ||
- [In runtime](#in-runtime) | ||
- [For React: `@appannie/react-ab-testing`](#for-react-appanniereact-ab-testing) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
- [For Vanilla Javascript: `@appannie/ab-testing`](#for-vanilla-javascript-appannieab-testing) | ||
- [Installation](#installation-1) | ||
- [Usage](#usage-1) | ||
- [Javascript Hashing Utils `@appannie/ab-testing-hash-object`](#javascript-hashing-utils-appannieab-testing-hash-object) | ||
- [Installation](#installation-2) | ||
- [Usage](#usage-2) | ||
- [For Python: `py-ab-testing`](#for-python-py-ab-testing) | ||
- [Installation](#installation-3) | ||
- [Usage](#usage-3) | ||
- [Configration Hashing](#configration-hashing) | ||
- [Prepare config file BEFORE make it public](#prepare-config-file-before-make-it-public) | ||
- [In runtime](#in-runtime) | ||
- [Configuration File Reference](#configuration-file-reference) | ||
- [Configuration File Hashing and Protecting private information](#configuration-file-hashing-and-protecting-private-information) | ||
- [Example configuration and common use case](#example-configuration-and-common-use-case) | ||
- [Excluding a user from an experiment](#excluding-a-user-from-an-experiment) | ||
- [Internal whitelist](#internal-whitelist) | ||
- [Configuration File Hashing and Protecting private information](#configuration-file-hashing-and-protecting-private-information) | ||
- [Example configuration and common use case](#example-configuration-and-common-use-case) | ||
- [Excluding a user from an experiment](#excluding-a-user-from-an-experiment) | ||
- [Internal whitelist](#internal-whitelist) | ||
- [Credits](#credits) | ||
@@ -43,3 +43,3 @@ | ||
1. Percentage based segmentation: Randomly allocate certain percent of user to a "[cohort](#terms)". The allocation algorithm guarantees to produce stable results within a single [experiment](#terms), and produce a different set of users for another [experiment](#terms) in a random way. Take a look at this example: `first 25% users`, it will always result into the same group of users in [experiment](#terms) `A`, but results in another completely different group of users for [experiment](#terms) `B`. | ||
1. Percentage based segmentation: Randomly allocate certain percent of user to a "[cohort](#terms)". The allocation algorithm guarantees to produce stable results within a single [experiment](#terms), and produce a different set of users for another [experiment](#terms) in a random way. You can also further filter the user set using the key `allocation_criteria`. Take a look at this example: `first 25% users`, it will always result into the same group of users in [experiment](#terms) `A`, but results in another completely different group of users for [experiment](#terms) `B`. | ||
2. User profile based segmentation: Manually assign a specific group of users to a cohort. This can be used to achieve features like "expose a new feature to the company emplyees for internal testing". Note the profile based segmentation works independently from the percentage based method. They doesn't interfere each other. | ||
@@ -318,2 +318,8 @@ | ||
], | ||
// The allocation_criteria key allows us to further filter the set of users by enforcing | ||
// a criteria that must be valid before the cohort is approved. In this case, | ||
// the user must have an email domain of data.ai or appannie.com. | ||
allocation_criteria: { | ||
email_domain: ['appannie.com', 'data.ai'] | ||
} | ||
}, | ||
@@ -331,3 +337,3 @@ { | ||
At App Annie, we're maintaining this configuration in yaml format to reduce the syntax noise. We have an automated CI task to encode/encrypt and push the final configuration to a public S3 bucket from where our SDK retrieve the configuration. | ||
At Data AI, we're maintaining this configuration in yaml format to reduce the syntax noise. We have an automated CI task to encode/encrypt and push the final configuration to a public S3 bucket from where our SDK retrieve the configuration. | ||
@@ -385,3 +391,3 @@ ## Configuration File Hashing and Protecting private information | ||
force_include: | ||
# Force all App Annie user under the given cohort | ||
# Force all Data AI user under the given cohort | ||
email_domain: | ||
@@ -393,4 +399,4 @@ - appannie.com | ||
Made with ❤️ by [Zhang Tao](https://github.com/ZigZagT) and [Simon Boudrias](https://github.com/SBoudrias) from the App Annie Beijing office. | ||
Made with ❤️ by [Zhang Tao](https://github.com/ZigZagT) and [Simon Boudrias](https://github.com/SBoudrias) from the Data AI Beijing office. | ||
Available for public use under the MIT license. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23076
126
397