From 29c7ccad39b9bbaa36ae6a311cabb7f53fa83ea8 Mon Sep 17 00:00:00 2001 From: Aditya Bist Date: Thu, 11 Apr 2019 12:07:20 -0700 Subject: [PATCH] Added some usage details (#4711) * added some details * remove unused import * added new metrics, removed churn * merged master and code review comments * code review comments * normalized days to calendar days/weeks/months * cleaned up code * changed comment to start required check for PR * fix failing test * fix test * removed null assignment * fix null test script --- .../telemetry/telemetry.contribution.ts | 142 ++++++++++++++++-- 1 file changed, 128 insertions(+), 14 deletions(-) diff --git a/src/sql/platform/telemetry/telemetry.contribution.ts b/src/sql/platform/telemetry/telemetry.contribution.ts index 0587a755e1..af58beeff6 100644 --- a/src/sql/platform/telemetry/telemetry.contribution.ts +++ b/src/sql/platform/telemetry/telemetry.contribution.ts @@ -13,42 +13,156 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag export class SqlTelemetryContribution extends Disposable implements IWorkbenchContribution { constructor( - @ITelemetryService telemetryService: ITelemetryService, + @ITelemetryService private telemetryService: ITelemetryService, @IStorageService storageService: IStorageService ) { super(); - const dailyLastUseDate = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0')); - const weeklyLastUseDate = Date.parse(storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0')); - const monthlyLastUseDate = Date.parse(storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0')); + const dailyLastUseDate: number = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0')); + const weeklyLastUseDate: number = Date.parse(storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0')); + const monthlyLastUseDate: number = Date.parse(storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0')); + const firstTimeUser: boolean = dailyLastUseDate === Date.parse('0'); - let today = new Date().toUTCString(); + let todayString: string = new Date().toUTCString(); // daily user event - if (this.diffInDays(Date.parse(today), dailyLastUseDate) >= 1) { + if (this.didDayChange(dailyLastUseDate)) { // daily first use telemetryService.publicLog('telemetry.dailyFirstUse', { dailyFirstUse: true }); - storageService.store('telemetry.dailyLastUseDate', today, StorageScope.GLOBAL); + storageService.store('telemetry.dailyLastUseDate', todayString, StorageScope.GLOBAL); } // weekly user event - if (this.diffInDays(Date.parse(today), weeklyLastUseDate) >= 7) { + if (this.didWeekChange(weeklyLastUseDate)) { // weekly first use telemetryService.publicLog('telemetry.weeklyFirstUse', { weeklyFirstUse: true }); - storageService.store('telemetry.weeklyLastUseDate', today, StorageScope.GLOBAL); + storageService.store('telemetry.weeklyLastUseDate', todayString, StorageScope.GLOBAL); } - // monthly user events - if (this.diffInDays(Date.parse(today), monthlyLastUseDate) >= 30) { + + /* send monthly uses once the user launches on a day that's in a month + after the last time we sent a monthly usage count */ + const monthlyUseCount: number = storageService.getNumber('telemetry.monthlyUseCount', StorageScope.GLOBAL, 0); + if (this.didMonthChange(monthlyLastUseDate)) { telemetryService.publicLog('telemetry.monthlyUse', { monthlyFirstUse: true }); - storageService.store('telemetry.monthlyLastUseDate', today, StorageScope.GLOBAL); - } + // the month changed, so send the user usage type event based on monthly count for last month + // and reset the count for this month + let lastMonthDate = new Date(monthlyLastUseDate); + this.sendUsageEvent(monthlyUseCount, lastMonthDate); + const wasActiveLastMonth: boolean = storageService.getBoolean('telemetry.wasActiveLastMonth', StorageScope.GLOBAL, false); + + if (firstTimeUser) { + // new user + this.sendGrowthTypeEvent(UserGrowthType.NewUser, lastMonthDate); + } + + // continuing or returning user + this.sendGrowthTypeEvent(wasActiveLastMonth ? UserGrowthType.ContinuingUser : UserGrowthType.ReturningUser, lastMonthDate); + + // set wasActiveUserLastMonth + storageService.store('telemetry.wasActiveLastMonth', true, StorageScope.GLOBAL); + + // reset the monthly count for the new month + storageService.store('telemetry.monthlyUseCount', 1, StorageScope.GLOBAL); + storageService.store('telemetry.monthlyLastUseDate', todayString, StorageScope.GLOBAL); + } else { + // if it's the same month, increment the monthly use count + storageService.store('telemetry.monthlyUseCount', monthlyUseCount + 1, StorageScope.GLOBAL); + } + } + + private didDayChange(lastUseDateNumber: number): boolean { + let nowDateNumber: number = Date.parse(new Date().toUTCString()); + if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 1) { + return true; + } else { + let nowDate = new Date(nowDateNumber); + let lastUseDate = new Date(lastUseDateNumber); + return nowDate.getUTCDay() !== lastUseDate.getUTCDay(); + } + } + + private didWeekChange(lastUseDateNumber: number): boolean { + let nowDateNumber: number = Date.parse(new Date().toUTCString()); + if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 7) { + return true; + } else { + let nowDate = new Date(nowDateNumber); + let lastUseDate = new Date(lastUseDateNumber); + return nowDate.getUTCDay() < lastUseDate.getUTCDay(); + } + } + + private didMonthChange(lastUseDateNumber: number): boolean { + let nowDateNumber: number = Date.parse(new Date().toUTCString()); + if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 30) { + return true; + } else { + let nowDate = new Date(nowDateNumber); + let lastUseDate = new Date(lastUseDateNumber); + return nowDate.getUTCMonth() !== lastUseDate.getUTCMonth(); + } } private diffInDays(nowDate: number, lastUseDate: number): number { - return (nowDate - lastUseDate) / (24 * 3600 * 1000); + return (nowDate - lastUseDate) / (3600 * 1000 * 24); + } + + // Usage Metrics + private sendUsageEvent(monthlyUseCount: number, lastMonthDate: Date): void { + let userUsageType: UserUsageType | undefined; + if (monthlyUseCount === 1) { + userUsageType = UserUsageType.TireKicker; + } else if (monthlyUseCount >= 2 && monthlyUseCount <= 11) { + userUsageType = UserUsageType.Occasional; + } else if (monthlyUseCount >= 12 && monthlyUseCount <= 20) { + userUsageType = UserUsageType.Engaged; + } else if (monthlyUseCount > 20) { + userUsageType = UserUsageType.Dedicated; + } + if (userUsageType) { + this.telemetryService.publicLog('telemetry.userUsage', + { userType: userUsageType, monthlyUseCount: monthlyUseCount, month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() }); + } + } + + // Growth Metrics + private sendGrowthTypeEvent(growthType: UserGrowthType, lastMonthDate: Date): void { + this.telemetryService.publicLog('telemetry.userGrowthType', { + userGrowthType: growthType, month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() + }); } } +/** + Growth Metrics + Active here means opened app atleast 1 time in a month + */ +export enum UserGrowthType { + // first time opening app + NewUser = 1, + // was active before, wasn't active last month, but is active this month + ReturningUser = 2, + // was active last month and this month + ContinuingUser = 3 +} + +/** + * Usage Metrics + * TireKicker = 1 day/month + * Occasional = 2-11 days/month + * Engaged = 12-20 days/month + * Dedicated = 20+ days/month + */ +export enum UserUsageType { + /* 1 day per month */ + TireKicker = 1, + /* 2-11 days per month */ + Occasional = 2, + /* 12-20 days per month */ + Engaged = 3, + /* 20+ days per month */ + Dedicated = 4 +} Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SqlTelemetryContribution, LifecyclePhase.Starting);