diff --git a/test/api/v3/integration/user/auth/POST-register_local.test.js b/test/api/v3/integration/user/auth/POST-register_local.test.js index 0a86f10daf..e41cf27a69 100644 --- a/test/api/v3/integration/user/auth/POST-register_local.test.js +++ b/test/api/v3/integration/user/auth/POST-register_local.test.js @@ -66,6 +66,7 @@ describe('POST /user/auth/local/register', () => { }); await expect(getProperty('users', user._id, '_ABtest')).to.eventually.be.a('string'); + await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object'); }); it('requires password and confirmPassword to match', async () => { diff --git a/website/server/libs/analyticsService.js b/website/server/libs/analyticsService.js index 1e29eaea80..5e3b16d38e 100644 --- a/website/server/libs/analyticsService.js +++ b/website/server/libs/analyticsService.js @@ -7,6 +7,7 @@ import useragent from 'useragent'; import { each, omit, + toArray, } from 'lodash'; import { content as Content } from '../../common'; @@ -90,6 +91,9 @@ let _formatUserData = (user) => { if (user._ABtest) { properties.ABtest = user._ABtest; } + if (user._ABtests) { + properties.ABtests = toArray(user._ABtests); + } if (user.registeredThrough) { properties.registeredPlatform = user.registeredThrough; diff --git a/website/server/libs/cron.js b/website/server/libs/cron.js index fb99b88290..a7f03c096a 100644 --- a/website/server/libs/cron.js +++ b/website/server/libs/cron.js @@ -130,6 +130,8 @@ function trackCronAnalytics (analytics, user, _progress, options) { function awardLoginIncentives (user) { if (user.loginIncentives > 50) return; + // A/B test 2016-12-21: Should we deliver notifications for upcoming incentives on days when users don't receive rewards? + if (!loginIncentives[user.loginIncentives].rewardKey && user._ABtests && user._ABtests.checkInModals === '20161221_noCheckInPreviews') return; // Remove old notifications if they exists user.notifications diff --git a/website/server/models/user/hooks.js b/website/server/models/user/hooks.js index 5e0f04d80e..a651c55ad4 100644 --- a/website/server/models/user/hooks.js +++ b/website/server/models/user/hooks.js @@ -10,7 +10,7 @@ import schema from './schema'; schema.plugin(baseModel, { // noSet is not used as updating uses a whitelist and creating only accepts specific params (password, email, username, ...) noSet: [], - private: ['auth.local.hashed_password', 'auth.local.salt', '_cronSignature', '_ABtest'], + private: ['auth.local.hashed_password', 'auth.local.salt', '_cronSignature', '_ABtest', '_ABtests'], toJSONTransform: function userToJSON (plainObj, originalDoc) { plainObj._tmp = originalDoc._tmp; // be sure to send down drop notifs delete plainObj.filters; @@ -78,6 +78,12 @@ function _setUpNewUser (user) { let iterableFlags = user.flags.toObject(); user._ABtest = ''; + // A/B test 2016-12-21: Should we deliver notifications for upcoming incentives on days when users don't receive rewards? + if (Math.random() < 0.5) { + user._ABtests.checkInModals = '20161221_noCheckInPreviews'; // no 'preview' check-in modals + } else { + user._ABtests.checkInModals = '20161221_showCheckInPreviews'; // show 'preview' check-in modals + } user.items.quests.dustbunnies = 1; if (user.registeredThrough === 'habitica-web' || user.registeredThrough === 'habitica-android') { diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index 2d33d822f6..dd1b3f9a1a 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -527,7 +527,10 @@ let schema = new Schema({ return {}; }}, pushDevices: [PushDeviceSchema], - _ABtest: {type: String}, + _ABtest: {type: String}, // deprecated. Superseded by _ABtests + _ABtests: {type: Schema.Types.Mixed, default: () => { + return {}; + }}, webhooks: [WebhookSchema], loginIncentives: {type: Number, default: 0}, }, {