chore(analytics): various updates to analytics tracking

This commit is contained in:
Sabe Jones
2021-08-30 16:21:05 -05:00
parent 722770354d
commit 40bf664f20
44 changed files with 76 additions and 775 deletions

View File

@@ -293,90 +293,4 @@ describe('cron middleware', () => {
}); });
}); });
}); });
context('Drop Cap A/B Test', async () => {
it('enrolls web users', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
req.headers['x-client'] = 'habitica-web';
await new Promise((resolve, reject) => {
cronMiddleware(req, res, async err => {
if (err) return reject(err);
user = await User.findById(user._id).exec();
expect(user._ABtests.dropCapNotif).to.be.a.string;
return resolve();
});
});
});
it('enables the new notification for 50% of users', async () => {
sandbox.stub(Math, 'random').returns(0.5);
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
req.headers['x-client'] = 'habitica-web';
await new Promise((resolve, reject) => {
cronMiddleware(req, res, async err => {
if (err) return reject(err);
user = await User.findById(user._id).exec();
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-enabled');
return resolve();
});
});
});
it('disables the new notification for 50% of users', async () => {
sandbox.stub(Math, 'random').returns(0.51);
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
req.headers['x-client'] = 'habitica-web';
await new Promise((resolve, reject) => {
cronMiddleware(req, res, async err => {
if (err) return reject(err);
user = await User.findById(user._id).exec();
expect(user._ABtests.dropCapNotif).to.be.equal('drop-cap-notif-disabled');
return resolve();
});
});
});
it('does not affect subscribers', async () => {
sandbox.stub(Math, 'random').returns(0.2);
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
req.headers['x-client'] = 'habitica-web';
sandbox.stub(User.prototype, 'isSubscribed').returns(true);
await new Promise((resolve, reject) => {
cronMiddleware(req, res, async err => {
if (err) return reject(err);
user = await User.findById(user._id).exec();
expect(user._ABtests.dropCapNotif).to.not.exist;
return resolve();
});
});
});
it('does not affect mobile users', async () => {
user.lastCron = moment(new Date()).subtract({ days: 2 });
await user.save();
req.headers['x-client'] = 'habitica-ios';
await new Promise((resolve, reject) => {
cronMiddleware(req, res, async err => {
if (err) return reject(err);
user = await User.findById(user._id).exec();
expect(user._ABtests.dropCapNotif).to.not.exist;
return resolve();
});
});
});
});
}); });

View File

@@ -1,5 +1,4 @@
import randomDrop from '../../../website/common/script/fns/randomDrop'; import randomDrop from '../../../website/common/script/fns/randomDrop';
import i18n from '../../../website/common/script/i18n';
import { import {
generateUser, generateUser,
generateTodo, generateTodo,
@@ -145,148 +144,5 @@ describe('common.fns.randomDrop', () => {
expect(acceptableDrops).to.contain(user._tmp.drop.key); // always Desert expect(acceptableDrops).to.contain(user._tmp.drop.key); // always Desert
}); });
}); });
context('drop cap notification', () => {
let analytics;
const req = {};
let isSubscribedStub;
beforeEach(() => {
user.addNotification = () => {};
sandbox.stub(user, 'addNotification');
user.isSubscribed = () => {};
isSubscribedStub = sandbox.stub(user, 'isSubscribed');
isSubscribedStub.returns(false);
analytics = { track () {} };
sandbox.stub(analytics, 'track');
});
it('sends a notification if A/B test is enabled when drop cap is reached', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
predictableRandom.returns(0.1);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(user.addNotification).to.be.calledOnce;
expect(user.addNotification).to.be.calledWith('DROP_CAP_REACHED', {
message: i18n.t('dropCapReached'),
items: 5,
});
});
it('does not send a notification if user is enrolled in disabled A/B test group', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-disabled';
predictableRandom.returns(0.1);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(user.addNotification).to.not.be.called;
});
it('does not send a notification if user is enrolled in disabled A/B test group', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-not-enrolled';
predictableRandom.returns(0.1);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(user.addNotification).to.not.be.called;
});
it('does not send a notification if drop cap is not reached', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
predictableRandom.returns(0.1);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(4);
expect(user.addNotification).to.not.be.called;
});
it('does not send a notification if user is subscribed', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
predictableRandom.returns(0.1);
isSubscribedStub.returns(true);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(user.addNotification).to.not.be.called;
});
it('tracks drop cap reached event for enrolled users (notification enabled)', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-enabled';
predictableRandom.returns(0.1);
isSubscribedStub.returns(true);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(analytics.track).to.be.calledWith('drop cap reached');
});
it('tracks drop cap reached event for enrolled users (notification disabled)', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-disabled';
predictableRandom.returns(0.1);
isSubscribedStub.returns(true);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(analytics.track).to.be.calledWith('drop cap reached');
});
it('does not track drop cap reached event for users not enrolled in A/B test', () => {
user._ABtests.dropCapNotif = 'drop-cap-notif-not-enrolled';
predictableRandom.returns(0.1);
isSubscribedStub.returns(true);
// Max Drop Count is 5
expect(user.items.lastDrop.count).to.equal(0);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
randomDrop(user, { task, predictableRandom }, req, analytics);
expect(user.items.lastDrop.count).to.equal(5);
expect(analytics.track).to.not.be.calledWith('drop cap reached');
});
});
}); });
}); });

View File

@@ -88,14 +88,6 @@ describe('shared.ops.buyMysterySet', () => {
expect(user.items.gear.owned).to.have.property('armor_mystery_301404', true); expect(user.items.gear.owned).to.have.property('armor_mystery_301404', true);
expect(user.items.gear.owned).to.have.property('head_mystery_301404', true); expect(user.items.gear.owned).to.have.property('head_mystery_301404', true);
expect(user.items.gear.owned).to.have.property('eyewear_mystery_301404', true); expect(user.items.gear.owned).to.have.property('eyewear_mystery_301404', true);
expect(analytics.track).to.be.calledOnce;
expect(analytics.track).to.be.calledWithMatch('acquire item', {
uuid: user._id,
itemKey: '301404',
itemType: 'Subscriber Gear',
acquireMethod: 'Hourglass',
category: 'behavior',
});
}); });
}); });
}); });

View File

@@ -1,250 +0,0 @@
<template>
<b-modal
id="drop-cap-reached"
size="md"
:hide-header="true"
:hide-footer="hasSubscription"
>
<div class="text-center">
<div
class="modal-close"
@click="close()"
>
<div
v-once
class="svg-icon"
v-html="icons.close"
></div>
</div>
<h1
v-once
class="header purple"
>
{{ $t('dropCapReached') }}
</h1>
<div class="max-items-wrapper d-flex align-items-center justify-content-center">
<div
class="svg-icon sparkles sparkles-rotate"
v-html="icons.sparkles"
></div>
<div class="max-items-module d-flex align-items-center justify-content-center flex-column">
<h1 class="max-items">
{{ maxItems }}
</h1>
<span
v-once
class="items-text"
>{{ $t('items') }}</span>
</div>
<div
class="svg-icon sparkles"
v-html="icons.sparkles"
></div>
</div>
<p
v-once
class="mb-4"
>
{{ $t('dropCapExplanation') }}
</p>
<a
v-once
class="standard-link d-block mb-3"
@click="toWiki()"
>
{{ $t('dropCapLearnMore') }}
</a>
</div>
<div
slot="modal-footer"
class="footer"
>
<span
v-once
class="purple d-block font-weight-bold mb-3 mt-3"
>
{{ $t('lookingForMoreItems') }}
</span>
<img
class="swords mb-3"
srcset="
~@/assets/images/swords.png,
~@/assets/images/swords@2x.png 2x,
~@/assets/images/swords@3x.png 3x"
src="~@/assets/images/swords.png"
>
<p
v-once
class="subs-benefits mb-3"
>
{{ $t('dropCapSubs') }}
</p>
<button
v-once
class="btn btn-primary"
@click="toLearnMore()"
>
{{ $t('learnMore') }}
</button>
</div>
</b-modal>
</template>
<style lang="scss">
@import '~@/assets/scss/colors.scss';
#drop-cap-reached {
.modal-body {
padding: 0 1.5rem;
}
.modal-footer {
background: $gray-700;
border-top: none;
padding: 0 1.5rem 2rem 1.5rem;
}
.modal-dialog {
width: 20.625rem;
font-size: 0.875rem;
line-height: 1.71;
text-align: center;
}
}
</style>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
.modal-close {
position: absolute;
width: 18px;
height: 18px;
padding: 4px;
right: 16px;
top: 16px;
cursor: pointer;
.svg-icon {
width: 12px;
height: 12px;
}
}
.subs-benefits {
font-size: 0.75rem;
line-height: 1.33;
font-style: normal;
}
.purple {
color: $purple-300;
}
.header {
font-size: 1.25rem;
line-height: 1.4;
text-align: center;
margin-top: 2rem;
}
.sparkles {
width: 2.5rem;
height: 4rem;
&.sparkles-rotate {
transform: rotate(180deg);
}
}
.max-items-wrapper {
margin: 17px auto;
}
.max-items-module {
background: white;
border-radius: 92px;
border: 8px solid $purple-400;
width: 92px;
height: 92px;
margin-left: 17px;
margin-right: 17px;
.items-text {
font-size: 0.75rem;
line-height: 1.33;
color: $gray-100;
}
}
.max-items {
font-size: 2rem;
line-height: 1.25;
color: $purple-300;
margin: 0;
}
.swords {
width: 7rem;
height: 3rem;
}
</style>
<script>
import closeIcon from '@/assets/svg/close.svg';
import sparkles from '@/assets/svg/star-group.svg';
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store';
export default {
data () {
return {
icons: Object.freeze({
close: closeIcon,
sparkles,
}),
maxItems: null,
};
},
computed: {
...mapState({ user: 'user.data' }),
hasSubscription () {
return Boolean(this.user.purchased.plan.customerId);
},
},
mounted () {
this.$root.$on('habitica:drop-cap-reached', notification => {
this.maxItems = notification.data.items;
this.$root.$emit('bv::show::modal', 'drop-cap-reached');
});
},
beforeDestroy () {
this.$root.$off('habitica:drop-cap-reached');
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'drop-cap-reached');
},
toWiki () {
window.open('https://habitica.fandom.com/wiki/Drops', '_blank');
Analytics.track({
hitType: 'event',
eventCategory: 'drop-cap-reached',
eventAction: 'click',
eventLabel: 'Drop Cap Reached > Modal > Wiki',
});
},
toLearnMore () {
Analytics.track({
hitType: 'event',
eventCategory: 'drop-cap-reached',
eventAction: 'click',
eventLabel: 'Drop Cap Reached > Modal > Subscriptions',
});
this.close();
this.$router.push('/user/settings/subscription');
},
},
};
</script>

View File

@@ -466,7 +466,6 @@ footer {
import axios from 'axios'; import axios from 'axios';
import moment from 'moment'; import moment from 'moment';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import gryphon from '@/assets/svg/gryphon.svg'; import gryphon from '@/assets/svg/gryphon.svg';
import twitter from '@/assets/svg/twitter.svg'; import twitter from '@/assets/svg/twitter.svg';
import facebook from '@/assets/svg/facebook.svg'; import facebook from '@/assets/svg/facebook.svg';
@@ -580,12 +579,6 @@ export default {
this.$root.$emit('bv::show::modal', 'modify-inventory'); this.$root.$emit('bv::show::modal', 'modify-inventory');
}, },
donate () { donate () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > Donate',
});
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true }); this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
}, },
}, },

View File

@@ -133,7 +133,6 @@
</style> </style>
<script> <script>
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import paymentsMixin from '../../mixins/payments'; import paymentsMixin from '../../mixins/payments';
import paymentsButtons from '@/components/payments/buttons/list'; import paymentsButtons from '@/components/payments/buttons/list';
@@ -174,12 +173,6 @@ export default {
}, },
methods: { methods: {
changePage (page) { changePage (page) {
Analytics.track({
hitType: 'event',
eventCategory: 'group-plans-static',
eventAction: 'view',
eventLabel: page,
});
this.activePage = page; this.activePage = page;
window.scrollTo(0, 0); window.scrollTo(0, 0);
}, },

View File

@@ -4,7 +4,6 @@
title="Empty" title="Empty"
size="lg" size="lg"
hide-footer="hide-footer" hide-footer="hide-footer"
@shown="shown()"
> >
<div <div
slot="modal-header" slot="modal-header"
@@ -342,7 +341,6 @@
</style> </style>
<script> <script>
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import upIcon from '@/assets/svg/up.svg'; import upIcon from '@/assets/svg/up.svg';
@@ -368,14 +366,6 @@ export default {
...mapState({ user: 'user.data' }), ...mapState({ user: 'user.data' }),
}, },
methods: { methods: {
shown () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'viewed-group-plan-overview',
});
},
toggle (question) { toggle (question) {
this.expandedQuestions[question] = !this.expandedQuestions[question]; this.expandedQuestions[question] = !this.expandedQuestions[question];
}, },

View File

@@ -725,7 +725,6 @@ body.modal-open #habitica-menu {
<script> <script>
import { mapState, mapGetters } from '@/libs/store'; import { mapState, mapGetters } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import { goToModForm } from '@/libs/modform'; import { goToModForm } from '@/libs/modform';
import gemIcon from '@/assets/svg/gem.svg'; import gemIcon from '@/assets/svg/gem.svg';
@@ -804,13 +803,6 @@ export default {
this.$root.$emit('bv::show::modal', 'create-party-modal'); this.$root.$emit('bv::show::modal', 'create-party-modal');
}, },
showBuyGemsModal () { showBuyGemsModal () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > Toolbar',
});
this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true }); this.$root.$emit('bv::show::modal', 'buy-gems', { alreadyTracked: true });
}, },
dropdownDesktop (hover) { dropdownDesktop (hover) {

View File

@@ -1,43 +0,0 @@
<template>
<base-notification
:can-remove="canRemove"
:has-icon="false"
:notification="notification"
:read-after-click="true"
@click="action"
>
<div
slot="content"
class="notification-bold-blue"
>
{{ $t('dropCapReached') }}
</div>
</base-notification>
</template>
<script>
import BaseNotification from './base';
import * as Analytics from '@/libs/analytics';
export default {
components: {
BaseNotification,
},
props: {
notification: { type: Object, required: true },
canRemove: { type: Boolean, required: true },
},
methods: {
action () {
this.$root.$emit('habitica:drop-cap-reached', this.notification);
Analytics.track({
hitType: 'event',
eventCategory: 'drop-cap-reached',
eventAction: 'click',
eventLabel: 'Drop Cap Reached > Notification Click',
});
},
},
};
</script>

View File

@@ -146,7 +146,6 @@ import ACHIEVEMENT_MIND_OVER_MATTER from './notifications/mindOverMatter';
import ONBOARDING_COMPLETE from './notifications/onboardingComplete'; import ONBOARDING_COMPLETE from './notifications/onboardingComplete';
import GIFT_ONE_GET_ONE from './notifications/g1g1'; import GIFT_ONE_GET_ONE from './notifications/g1g1';
import OnboardingGuide from './onboardingGuide'; import OnboardingGuide from './onboardingGuide';
import DROP_CAP_REACHED from './notifications/dropCapReached';
export default { export default {
components: { components: {
@@ -176,7 +175,6 @@ export default {
OnboardingGuide, OnboardingGuide,
ONBOARDING_COMPLETE, ONBOARDING_COMPLETE,
GIFT_ONE_GET_ONE, GIFT_ONE_GET_ONE,
DROP_CAP_REACHED,
}, },
data () { data () {
return { return {
@@ -202,7 +200,7 @@ export default {
'GROUP_TASK_CLAIMED', 'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED', 'GROUP_TASK_CLAIMED', 'NEW_MYSTERY_ITEMS', 'CARD_RECEIVED',
'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS', 'NEW_INBOX_MESSAGE', 'NEW_CHAT_MESSAGE', 'UNALLOCATED_STATS_POINTS',
'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER', 'ACHIEVEMENT_JUST_ADD_WATER', 'ACHIEVEMENT_LOST_MASTERCLASSER', 'ACHIEVEMENT_MIND_OVER_MATTER',
'VERIFY_USERNAME', 'ONBOARDING_COMPLETE', 'DROP_CAP_REACHED', 'VERIFY_USERNAME', 'ONBOARDING_COMPLETE',
], ],
}; };
}, },

View File

@@ -139,7 +139,6 @@
<script> <script>
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import userIcon from '@/assets/svg/user.svg'; import userIcon from '@/assets/svg/user.svg';
import MenuDropdown from '../ui/customMenuDropdown'; import MenuDropdown from '../ui/customMenuDropdown';
import MessageCount from './messageCount'; import MessageCount from './messageCount';
@@ -178,13 +177,6 @@ export default {
this.$router.push({ name: startingPage }); this.$router.push({ name: startingPage });
}, },
toLearnMore () { toLearnMore () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'User Dropdown > Subscriptions',
});
this.$router.push({ name: 'subscription' }); this.$router.push({ name: 'subscription' });
}, },
logout () { logout () {

View File

@@ -35,7 +35,6 @@
<mind-over-matter /> <mind-over-matter />
<onboarding-complete /> <onboarding-complete />
<first-drops /> <first-drops />
<drop-cap-reached-modal />
</div> </div>
</template> </template>
@@ -118,6 +117,7 @@ import { mapState } from '@/libs/store';
import { MAX_LEVEL_HARD_CAP } from '@/../../common/script/constants'; import { MAX_LEVEL_HARD_CAP } from '@/../../common/script/constants';
import notifications from '@/mixins/notifications'; import notifications from '@/mixins/notifications';
import guide from '@/mixins/guide'; import guide from '@/mixins/guide';
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
import yesterdailyModal from './tasks/yesterdailyModal'; import yesterdailyModal from './tasks/yesterdailyModal';
import newStuff from './news/modal'; import newStuff from './news/modal';
@@ -147,7 +147,6 @@ import loginIncentives from './achievements/login-incentives';
import onboardingComplete from './achievements/onboardingComplete'; import onboardingComplete from './achievements/onboardingComplete';
import verifyUsername from './settings/verifyUsername'; import verifyUsername from './settings/verifyUsername';
import firstDrops from './achievements/firstDrops'; import firstDrops from './achievements/firstDrops';
import DropCapReachedModal from '@/components/achievements/dropCapReached';
const NOTIFICATIONS = { const NOTIFICATIONS = {
CHALLENGE_JOINED_ACHIEVEMENT: { CHALLENGE_JOINED_ACHIEVEMENT: {
@@ -459,7 +458,6 @@ export default {
justAddWater, justAddWater,
onboardingComplete, onboardingComplete,
firstDrops, firstDrops,
DropCapReachedModal,
}, },
mixins: [notifications, guide], mixins: [notifications, guide],
data () { data () {
@@ -811,6 +809,10 @@ export default {
// Run Cron // Run Cron
await axios.post('/api/v4/cron'); await axios.post('/api/v4/cron');
// Reset daily analytics actions
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 0);
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 0);
// Sync // Sync
await Promise.all([ await Promise.all([
this.$store.dispatch('user:fetch', { forceLoad: true }), this.$store.dispatch('user:fetch', { forceLoad: true }),

View File

@@ -56,7 +56,6 @@
<script> <script>
import axios from 'axios'; import axios from 'axios';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager'; import { CONSTANTS, setLocalSetting } from '@/libs/userlocalManager';
import paymentsMixin from '@/mixins/payments'; import paymentsMixin from '@/mixins/payments';
@@ -261,13 +260,6 @@ export default {
if (newGroup && newGroup._id) { if (newGroup && newGroup._id) {
// Handle new user signup // Handle new user signup
if (!this.$store.state.isUserLoggedIn) { if (!this.$store.state.isUserLoggedIn) {
Analytics.track({
hitType: 'event',
eventCategory: 'group-plans-static',
eventAction: 'view',
eventLabel: 'paid-with-amazon',
});
this.storePaymentStatusAndReload(`${habiticaUrl}/group-plans/${newGroup._id}/task-information?showGroupOverview=true`); this.storePaymentStatusAndReload(`${habiticaUrl}/group-plans/${newGroup._id}/task-information?showGroupOverview=true`);
return; return;
} }

View File

@@ -363,7 +363,6 @@
<script> <script>
import moment from 'moment'; import moment from 'moment';
import * as Analytics from '@/libs/analytics';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import markdown from '@/directives/markdown'; import markdown from '@/directives/markdown';
import paymentsMixin from '@/mixins/payments'; import paymentsMixin from '@/mixins/payments';
@@ -449,22 +448,11 @@ export default {
async mounted () { async mounted () {
await this.$store.dispatch('worldState:getWorldState'); await this.$store.dispatch('worldState:getWorldState');
this.$root.$on('bv::show::modal', (modalId, data = {}) => { this.$root.$on('bv::show::modal', modalId => {
if (modalId === 'buy-gems') { if (modalId === 'buy-gems') {
// We force reloading the world state every time the modal is reopened // We force reloading the world state every time the modal is reopened
// To make sure the promo status is always up to date // To make sure the promo status is always up to date
this.$store.dispatch('worldState:getWorldState', { forceLoad: true }); this.$store.dispatch('worldState:getWorldState', { forceLoad: true });
// Track opening of gems modal unless it's been already tracked
// For example the gems button in the menu already tracks the event by itself
if (data.alreadyTracked !== true) {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Gems > Wallet',
});
}
} }
}); });
}, },

View File

@@ -246,7 +246,6 @@
</style> </style>
<script> <script>
import * as Analytics from '@/libs/analytics';
import { setup as setupPayments } from '@/libs/payments'; import { setup as setupPayments } from '@/libs/payments';
import amazonPaymentsModal from '@/components/payments/amazonModal'; import amazonPaymentsModal from '@/components/payments/amazonModal';
import AuthForm from '../auth/authForm.vue'; import AuthForm from '../auth/authForm.vue';
@@ -280,24 +279,10 @@ export default {
}, },
methods: { methods: {
goToNewGroupPage () { goToNewGroupPage () {
Analytics.track({
hitType: 'event',
eventCategory: 'group-plans-static',
eventAction: 'view',
eventLabel: 'view-auth-form',
});
this.$root.$emit('bv::show::modal', 'group-plan'); this.$root.$emit('bv::show::modal', 'group-plan');
}, },
authenticate () { authenticate () {
this.modalPage = 'purchaseGroup'; this.modalPage = 'purchaseGroup';
Analytics.track({
hitType: 'event',
eventCategory: 'group-plans-static',
eventAction: 'view',
eventLabel: 'create-group',
});
}, },
}, },
}; };

View File

@@ -262,7 +262,6 @@
<script> <script>
import logo from '@/assets/svg/logo.svg'; import logo from '@/assets/svg/logo.svg';
import purpleLogo from '@/assets/svg/purple-logo.svg'; import purpleLogo from '@/assets/svg/purple-logo.svg';
import * as Analytics from '@/libs/analytics';
export default { export default {
data () { data () {
@@ -286,13 +285,6 @@ export default {
} }
// @TODO duplicate of code in home.vue // @TODO duplicate of code in home.vue
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Play',
});
this.$router.push('/register'); this.$router.push('/register');
}, },
scrollToMobileApp () { scrollToMobileApp () {

View File

@@ -816,7 +816,6 @@ import kickstarter from '@/assets/images/home/kickstarter.svg';
import lifehacker from '@/assets/images/home/lifehacker.svg'; import lifehacker from '@/assets/images/home/lifehacker.svg';
import makeuseof from '@/assets/images/home/make-use-of.svg'; import makeuseof from '@/assets/images/home/make-use-of.svg';
import thenewyorktimes from '@/assets/images/home/the-new-york-times.svg'; import thenewyorktimes from '@/assets/images/home/the-new-york-times.svg';
import * as Analytics from '@/libs/analytics';
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants'; import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
export default { export default {
@@ -944,12 +943,6 @@ export default {
window.location.href = this.$route.query.redirectTo || '/'; window.location.href = this.$route.query.redirectTo || '/';
}, },
playButtonClick () { playButtonClick () {
Analytics.track({
hitType: 'event',
eventCategory: 'button',
eventAction: 'click',
eventLabel: 'Play',
});
this.$router.push('/register'); this.$router.push('/register');
}, },
// @TODO: Abstract hello in to action or lib // @TODO: Abstract hello in to action or lib

View File

@@ -6,6 +6,8 @@ const CONSTANTS = {
CURRENT_EQUIPMENT_DRAWER_TAB: 'current-equipment-drawer-tab', CURRENT_EQUIPMENT_DRAWER_TAB: 'current-equipment-drawer-tab',
STABLE_SORT_STATE: 'stable-sort-state', STABLE_SORT_STATE: 'stable-sort-state',
ONBOARDING_PANEL_STATE: 'onboarding-panel-state', ONBOARDING_PANEL_STATE: 'onboarding-panel-state',
TASKS_CREATED_COUNT: 'tasks-created-count',
TASKS_SCORED_COUNT: 'tasks-scored-count',
}, },
drawerStateValues: { drawerStateValues: {
DRAWER_CLOSED: 'drawer-closed', DRAWER_CLOSED: 'drawer-closed',

View File

@@ -1,6 +1,5 @@
import times from 'lodash/times'; import times from 'lodash/times';
import introjs from 'intro.js'; import introjs from 'intro.js';
import * as Analytics from '@/libs/analytics';
let showingTour = false; let showingTour = false;
@@ -159,15 +158,6 @@ export default {
opts.steps = opts.steps.concat(this.chapters[chapter][p]); opts.steps = opts.steps.concat(this.chapters[chapter][p]);
}); });
Analytics.track({
hitType: 'event',
eventCategory: 'behavior',
eventAction: 'tutorial',
eventLabel: `${chapter}-web`,
eventValue: page + 1,
complete: true,
});
// @TODO: Do we always need to initialize here? // @TODO: Do we always need to initialize here?
const intro = introjs(); const intro = introjs();
intro.setOptions({ intro.setOptions({
@@ -194,15 +184,6 @@ export default {
// Mark tour complete // Mark tour complete
ups[`flags.tour.${chapter}`] = -2; // @TODO: Move magic numbers to enum ups[`flags.tour.${chapter}`] = -2; // @TODO: Move magic numbers to enum
Analytics.track({
hitType: 'event',
eventCategory: 'behavior',
eventAction: 'tutorial',
eventLabel: `${chapter}-web`,
eventValue: lastKnownStep,
complete: true,
});
this.$store.dispatch('user:set', ups); this.$store.dispatch('user:set', ups);
}, },
}, },

View File

@@ -3,6 +3,8 @@ import Vue from 'vue';
import notifications from './notifications'; import notifications from './notifications';
import scoreTask from '@/../../common/script/ops/scoreTask'; import scoreTask from '@/../../common/script/ops/scoreTask';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
export default { export default {
mixins: [notifications], mixins: [notifications],
@@ -69,6 +71,22 @@ export default {
}); });
this.handleTaskScoreNotifications(response.data.data._tmp || {}); this.handleTaskScoreNotifications(response.data.data._tmp || {});
const tasksScoredCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT);
if (!tasksScoredCount || tasksScoredCount < 2) {
Analytics.track('task scored', {
uuid: user._id,
hitType: 'event',
category: 'behavior',
taskType: task.type,
direction,
});
if (!tasksScoredCount) {
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, 1);
} else {
setLocalSetting(CONSTANTS.keyConstants.TASKS_SCORED_COUNT, tasksScoredCount + 1);
}
}
}, },
async handleTaskScoreNotifications (tmpObject = {}) { async handleTaskScoreNotifications (tmpObject = {}) {
const { user } = this; const { user } = this;

View File

@@ -1,5 +1,4 @@
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager'; import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
import * as Analytics from '@/libs/analytics';
export default function (to, from, next) { export default function (to, from, next) {
const { redirect } = to.params; const { redirect } = to.params;
@@ -40,13 +39,6 @@ export default function (to, from, next) {
if (newGroup && newGroup._id) { if (newGroup && newGroup._id) {
// Handle new user signup // Handle new user signup
if (newAppState.newSignup === true) { if (newAppState.newSignup === true) {
Analytics.track({
hitType: 'event',
eventCategory: 'group-plans-static',
eventAction: 'view',
eventLabel: 'paid-with-stripe',
});
return next({ return next({
name: 'groupPlanDetailTaskInformation', name: 'groupPlanDetailTaskInformation',
params: { groupId: newGroup._id }, params: { groupId: newGroup._id },

View File

@@ -3,6 +3,8 @@ import Vue from 'vue';
import compact from 'lodash/compact'; import compact from 'lodash/compact';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { loadAsyncResource } from '@/libs/asyncResource'; import { loadAsyncResource } from '@/libs/asyncResource';
import * as Analytics from '@/libs/analytics';
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
export function fetchUserTasks (store, options = {}) { export function fetchUserTasks (store, options = {}) {
return loadAsyncResource({ return loadAsyncResource({
@@ -108,6 +110,21 @@ export async function create (store, createdTask) {
if (taskDataIndex !== -1) { if (taskDataIndex !== -1) {
Vue.set(tasksArr, taskDataIndex, { ...tasksArr[taskDataIndex], ...taskRes }); Vue.set(tasksArr, taskDataIndex, { ...tasksArr[taskDataIndex], ...taskRes });
} }
const tasksCreatedCount = getLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT);
if (!tasksCreatedCount || tasksCreatedCount < 2) {
const uuid = store.state.user.data._id;
Analytics.track('task created', {
uuid,
hitType: 'event',
category: 'behavior',
taskType: taskRes.type,
});
if (!tasksCreatedCount) {
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, 1);
} else {
setLocalSetting(CONSTANTS.keyConstants.TASKS_CREATED_COUNT, tasksCreatedCount + 1);
}
}
}); });
} }

View File

@@ -5,7 +5,6 @@ import reduce from 'lodash/reduce';
import filter from 'lodash/filter'; import filter from 'lodash/filter';
import pickBy from 'lodash/pickBy'; import pickBy from 'lodash/pickBy';
import size from 'lodash/size'; import size from 'lodash/size';
import moment from 'moment';
import content from '../content/index'; import content from '../content/index';
import i18n from '../i18n'; import i18n from '../i18n';
import { daysSince } from '../cron'; import { daysSince } from '../cron';
@@ -156,43 +155,10 @@ export default function randomDrop (user, options, req = {}, analytics) {
user.items.lastDrop.date = Number(new Date()); user.items.lastDrop.date = Number(new Date());
user.items.lastDrop.count += 1; user.items.lastDrop.count += 1;
const dropN = user.items.lastDrop.count; if (analytics) {
const dropCapReached = dropN === maxDropCount;
const isEnrolledInDropCapTest = user._ABtests.dropCapNotif
&& user._ABtests.dropCapNotif !== 'drop-cap-notif-not-enrolled';
const hasActiveDropCapNotif = isEnrolledInDropCapTest
&& user._ABtests.dropCapNotif === 'drop-cap-notif-enabled';
// Unsubscribed users get a notification when they reach the drop cap
// One per day
if (
hasActiveDropCapNotif && dropCapReached
&& user.addNotification
&& user.isSubscribed && !user.isSubscribed()
) {
const prevNotifIndex = user.notifications.findIndex(n => n.type === 'DROP_CAP_REACHED');
if (prevNotifIndex !== -1) user.notifications.splice(prevNotifIndex, 1);
user.addNotification('DROP_CAP_REACHED', {
message: i18n.t('dropCapReached', req.language),
items: dropN,
});
}
if (isEnrolledInDropCapTest && dropCapReached) {
analytics.track('drop cap reached', {
uuid: user._id,
dropCap: maxDropCount,
category: 'behavior',
headers: req.headers,
});
}
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
analytics.track('dropped item', { analytics.track('dropped item', {
uuid: user._id, uuid: user._id,
itemKey: drop.key, itemKey: drop.key,
acquireMethod: 'Drop',
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,
}); });

View File

@@ -7,7 +7,7 @@ import {
import { toNextLevel } from '../statHelpers'; import { toNextLevel } from '../statHelpers';
import autoAllocate from './autoAllocate'; import autoAllocate from './autoAllocate';
export default function updateStats (user, stats, req = {}, analytics) { export default function updateStats (user, stats) {
let allocatedStatPoints; let allocatedStatPoints;
let totalStatPoints; let totalStatPoints;
let experienceToNextLevel; let experienceToNextLevel;
@@ -88,15 +88,6 @@ export default function updateStats (user, stats, req = {}, analytics) {
}; };
if (user.markModified) user.markModified('items.quests'); if (user.markModified) user.markModified('items.quests');
if (analytics) {
analytics.track('acquire item', {
uuid: user._id,
itemKey: k,
acquireMethod: 'Level Drop',
category: 'behavior',
headers: req.headers,
});
}
user._tmp.drop = { user._tmp.drop = {
type: 'Quest', type: 'Quest',
key: k, key: k,

View File

@@ -105,7 +105,7 @@ export class AbstractBuyOperation {
} }
analyticsLabel () { // eslint-disable-line class-methods-use-this analyticsLabel () { // eslint-disable-line class-methods-use-this
return 'acquire item'; return 'buy';
} }
sendToAnalytics (additionalData = {}) { sendToAnalytics (additionalData = {}) {
@@ -151,7 +151,7 @@ export class AbstractGoldItemOperation extends AbstractBuyOperation {
return { return {
itemKey: this.getItemKey(this.item), itemKey: this.getItemKey(this.item),
itemType: this.getItemType(this.item), itemType: this.getItemType(this.item),
acquireMethod: 'Gold', currency: 'Gold',
goldCost: this.getItemValue(this.item), goldCost: this.getItemValue(this.item),
}; };
} }
@@ -181,7 +181,7 @@ export class AbstractGemItemOperation extends AbstractBuyOperation {
return { return {
itemKey: this.getItemKey(this.item), itemKey: this.getItemKey(this.item),
itemType: this.getItemType(this.item), itemType: this.getItemType(this.item),
acquireMethod: 'Gems', currency: 'Gems',
gemCost: this.getItemValue(this.item) * 4, gemCost: this.getItemValue(this.item) * 4,
}; };
} }
@@ -203,7 +203,7 @@ export class AbstractHourglassItemOperation extends AbstractBuyOperation {
analyticsData () { analyticsData () {
return { return {
itemKey: this.item.key, itemKey: this.item.key,
acquireMethod: 'Hourglass', currency: 'Hourglass',
}; };
} }
} }

View File

@@ -72,11 +72,10 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-d
_trackDropAnalytics (userId, key) { _trackDropAnalytics (userId, key) {
this.analytics.track( this.analytics.track(
'dropped item', 'Enchanted Armoire',
{ {
uuid: userId, uuid: userId,
itemKey: key, itemKey: key,
acquireMethod: 'Armoire',
category: 'behavior', category: 'behavior',
headers: this.req.headers, headers: this.req.headers,
}, },

View File

@@ -29,11 +29,11 @@ export default function buyMysterySet (user, req = {}, analytics) {
}); });
if (analytics) { if (analytics) {
analytics.track('acquire item', { analytics.track('buy', {
uuid: user._id, uuid: user._id,
itemKey: mysterySet.key, itemKey: mysterySet.key,
itemType: 'Subscriber Gear', itemType: 'Subscriber Gear',
acquireMethod: 'Hourglass', currency: 'Hourglass',
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,
}); });

View File

@@ -83,11 +83,11 @@ export default function purchaseHourglass (user, req = {}, analytics, quantity =
} }
if (analytics) { if (analytics) {
analytics.track('acquire item', { analytics.track('buy', {
uuid: user._id, uuid: user._id,
itemKey: key, itemKey: key,
itemType: type, itemType: type,
acquireMethod: 'Hourglass', currency: 'Hourglass',
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,
}); });

View File

@@ -113,11 +113,11 @@ export default function purchase (user, req = {}, analytics) {
} }
if (analytics) { if (analytics) {
analytics.track('acquire item', { analytics.track('buy', {
uuid: user._id, uuid: user._id,
itemKey: key, itemKey: key,
itemType: type, itemType: type,
acquireMethod: 'Gems', currency: 'Gems',
gemCost: price * 4, gemCost: price * 4,
quantityPurchased: quantity, quantityPurchased: quantity,
category: 'behavior', category: 'behavior',

View File

@@ -71,8 +71,7 @@ export default function changeClass (user, req = {}, analytics) {
analytics.track('change class', { analytics.track('change class', {
uuid: user._id, uuid: user._id,
class: klass, class: klass,
acquireMethod: balanceRemoved === 0 ? 'Free' : 'Gems', currency: balanceRemoved === 0 ? 'Free' : 'Gems',
gemCost: balanceRemoved / 0.25,
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,
}); });

View File

@@ -11,7 +11,7 @@ function markNotificationAsRead (user) {
if (index !== -1) user.notifications.splice(index, 1); if (index !== -1) user.notifications.splice(index, 1);
} }
export default function openMysteryItem (user, req = {}, analytics) { export default function openMysteryItem (user, req = {}) {
const { mysteryItems } = user.purchased.plan; const { mysteryItems } = user.purchased.plan;
let item = mysteryItems.shift(); let item = mysteryItems.shift();
@@ -33,17 +33,6 @@ export default function openMysteryItem (user, req = {}, analytics) {
user.markModified('items.gear.owned'); user.markModified('items.gear.owned');
} }
if (analytics) {
analytics.track('open mystery item', {
uuid: user._id,
itemKey: item,
itemType: 'Subscriber Gear',
acquireMethod: 'Subscriber',
category: 'behavior',
headers: req.headers,
});
}
return [ return [
item, item,
i18n.t('mysteryItemOpened', req.language), i18n.t('mysteryItemOpened', req.language),

View File

@@ -26,11 +26,11 @@ export default function rebirth (user, tasks = [], req = {}, analytics) {
if (notFree) { if (notFree) {
user.balance -= 1.5; user.balance -= 1.5;
analyticsData.acquireMethod = 'Gems'; analyticsData.currency = 'Gems';
analyticsData.gemCost = 6; analyticsData.gemCost = 6;
} else { } else {
analyticsData.currency = 'Free';
analyticsData.gemCost = 0; analyticsData.gemCost = 0;
analyticsData.acquireMethod = '> 100';
} }
if (analytics) { if (analytics) {

View File

@@ -23,21 +23,6 @@ export default function releaseBoth (user, req = {}) {
let giveBeastMasterAchievement = true; let giveBeastMasterAchievement = true;
let giveMountMasterAchievement = true; let giveMountMasterAchievement = true;
// @TODO: We are only offering the free version now
// if (!user.achievements.triadBingo) {
// if (analytics) {
// analytics.track('release pets & mounts', {
// uuid: user._id,
// acquireMethod: 'Gems',
// gemCost: 6,
// category: 'behavior',
// headers: req.headers,
// });
// }
//
// user.balance -= 1.5;
// }
const mountInfo = content.mountInfo[user.items.currentMount]; const mountInfo = content.mountInfo[user.items.currentMount];
if (mountInfo && mountInfo.type === 'drop') { if (mountInfo && mountInfo.type === 'drop') {

View File

@@ -43,7 +43,7 @@ export default function releaseMounts (user, req = {}, analytics) {
if (analytics) { if (analytics) {
analytics.track('release mounts', { analytics.track('release mounts', {
uuid: user._id, uuid: user._id,
acquireMethod: 'Gems', currency: 'Gems',
gemCost: 4, gemCost: 4,
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,

View File

@@ -43,7 +43,7 @@ export default function releasePets (user, req = {}, analytics) {
if (analytics) { if (analytics) {
analytics.track('release pets', { analytics.track('release pets', {
uuid: user._id, uuid: user._id,
acquireMethod: 'Gems', currency: 'Gems',
gemCost: 4, gemCost: 4,
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,

View File

@@ -23,7 +23,7 @@ export default function reroll (user, tasks = [], req = {}, analytics) {
if (analytics) { if (analytics) {
analytics.track('Fortify Potion', { analytics.track('Fortify Potion', {
uuid: user._id, uuid: user._id,
acquireMethod: 'Gems', currency: 'Gems',
gemCost: 4, gemCost: 4,
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,

View File

@@ -305,11 +305,11 @@ export default function unlock (user, req = {}, analytics) {
user.balance -= cost; user.balance -= cost;
if (analytics) { if (analytics) {
analytics.track('acquire item', { analytics.track('buy', {
uuid: user._id, uuid: user._id,
itemKey: path, itemKey: path,
itemType: 'customization', itemType: 'customization',
acquireMethod: 'Gems', currency: 'Gems',
gemCost: cost / 0.25, gemCost: cost / 0.25,
category: 'behavior', category: 'behavior',
headers: req.headers, headers: req.headers,

View File

@@ -266,8 +266,6 @@ api.postChat = {
analyticsObject.groupName = group.name; analyticsObject.groupName = group.name;
} }
res.analytics.track('group chat', analyticsObject);
if (chatUpdated) { if (chatUpdated) {
res.respond(200, { chat: chatRes.chat }); res.respond(200, { chat: chatRes.chat });
} else { } else {

View File

@@ -158,6 +158,7 @@ api.createGroup = {
groupType: savedGroup.type, groupType: savedGroup.type,
privacy: savedGroup.privacy, privacy: savedGroup.privacy,
headers: req.headers, headers: req.headers,
invited: false,
}; };
if (savedGroup.privacy === 'public') { if (savedGroup.privacy === 'public') {
@@ -206,6 +207,7 @@ api.createGroupPlan = {
groupType: savedGroup.type, groupType: savedGroup.type,
privacy: savedGroup.privacy, privacy: savedGroup.privacy,
headers: req.headers, headers: req.headers,
invited: false,
}); });
// do not remove chat flags data as we've just created the group // do not remove chat flags data as we've just created the group
@@ -714,6 +716,7 @@ api.joinGroup = {
groupType: group.type, groupType: group.type,
privacy: group.privacy, privacy: group.privacy,
headers: req.headers, headers: req.headers,
invited: isUserInvited,
}; };
if (group.privacy === 'public') { if (group.privacy === 'public') {

View File

@@ -165,7 +165,7 @@ api.inviteToQuest = {
questName: questKey, questName: questKey,
uuid: user._id, uuid: user._id,
headers: req.headers, headers: req.headers,
}, true); });
}, },
}; };
@@ -226,7 +226,7 @@ api.acceptQuest = {
questName: group.quest.key, questName: group.quest.key,
uuid: user._id, uuid: user._id,
headers: req.headers, headers: req.headers,
}, true); });
}, },
}; };
@@ -287,7 +287,7 @@ api.rejectQuest = {
questName: group.quest.key, questName: group.quest.key,
uuid: user._id, uuid: user._id,
headers: req.headers, headers: req.headers,
}, true); });
}, },
}; };
@@ -347,7 +347,7 @@ api.forceStart = {
questName: group.quest.key, questName: group.quest.key,
uuid: user._id, uuid: user._id,
headers: req.headers, headers: req.headers,
}, true); });
}, },
}; };

View File

@@ -199,16 +199,6 @@ api.createUserTasks = {
res.respond(201, tasks.length === 1 ? tasks[0] : tasks); res.respond(201, tasks.length === 1 ? tasks[0] : tasks);
tasks.forEach(task => { tasks.forEach(task => {
if (user.flags.welcomed) { // Don't send Habitica default tasks to analytics
res.analytics.track('task create', {
uuid: user._id,
hitType: 'event',
category: 'behavior',
taskType: task.type,
headers: req.headers,
});
}
taskActivityWebhook.send(user, { taskActivityWebhook.send(user, {
type: 'created', type: 'created',
task, task,
@@ -326,7 +316,7 @@ api.createChallengeTasks = {
if (challenge) challenge.addTasks(tasks); if (challenge) challenge.addTasks(tasks);
tasks.forEach(task => { tasks.forEach(task => {
res.analytics.track('task create', { res.analytics.track('challenge task created', {
uuid: user._id, uuid: user._id,
hitType: 'event', hitType: 'event',
category: 'behavior', category: 'behavior',

View File

@@ -61,7 +61,7 @@ api.createGroupTasks = {
res.respond(201, tasks.length === 1 ? tasks[0] : tasks); res.respond(201, tasks.length === 1 ? tasks[0] : tasks);
tasks.forEach(task => { tasks.forEach(task => {
res.analytics.track('task create', { res.analytics.track('team task created', {
uuid: user._id, uuid: user._id,
hitType: 'event', hitType: 'event',
category: 'behavior', category: 'behavior',

View File

@@ -260,6 +260,7 @@ function _sendPurchaseDataToAmplitude (data) {
amplitudeData.event_type = 'purchase'; amplitudeData.event_type = 'purchase';
amplitudeData.revenue = data.purchaseValue; amplitudeData.revenue = data.purchaseValue;
amplitudeData.productId = data.itemPurchased;
if (LOG_AMPLITUDE_EVENTS) { if (LOG_AMPLITUDE_EVENTS) {
logger.info('Amplitude Purchase Event', amplitudeData); logger.info('Amplitude Purchase Event', amplitudeData);

View File

@@ -438,15 +438,6 @@ async function scoreTask (user, task, direction, req, res) {
user, user,
}); });
res.analytics.track('task score', {
uuid: user._id,
hitType: 'event',
category: 'behavior',
taskType: task.type,
direction,
headers: req.headers,
});
return { return {
task, task,
delta, delta,