From f999ec4d88c92e9e281b17b958092e10d841c1fd Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 12 Feb 2020 14:31:15 -0800 Subject: [PATCH 01/13] Add duration to select local/remote kernel --- src/client/datascience/constants.ts | 17 +----- .../datascience/jupyter/jupyterExecution.ts | 1 + .../jupyter/kernels/kernelSelector.ts | 25 ++++++++- .../jupyter/kernels/kernelSwitcher.ts | 11 ++-- src/client/telemetry/index.ts | 2 +- .../kernels/kernelSelector.unit.test.ts | 20 ++++--- .../kernels/kernelSwitcher.unit.test.ts | 55 +++++++++++-------- 7 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 763548be676c..7bc031aab24e 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -100,8 +100,7 @@ export namespace RegExpValues { export const KernelSpecOutputRegEx = /^\s*(\S+)\s+(\S+)$/; // This next one has to be a string because uglifyJS isn't handling the groups. We use named-js-regexp to parse it // instead. - export const UrlPatternRegEx = - '(?https?:\\/\\/)((\\(.+\\s+or\\s+(?.+)\\))|(?[^\\s]+))(?:.+)'; + export const UrlPatternRegEx = '(?https?:\\/\\/)((\\(.+\\s+or\\s+(?.+)\\))|(?[^\\s]+))(?:.+)'; export interface IUrlPatternGroupType { LOCAL: string | undefined; PREFIX: string | undefined; @@ -152,7 +151,7 @@ export enum Telemetry { CollapseAll = 'DATASCIENCE.COLLAPSE_ALL', SelectJupyterURI = 'DATASCIENCE.SELECT_JUPYTER_URI', SelectLocalJupyterKernel = 'DATASCIENCE.SELECT_LOCAL_JUPYTER_KERNEL', - SelectRemoteJupyuterKernel = 'DATASCIENCE.SELECT_REMOTE_JUPYTER_KERNEL', + SelectRemoteJupyterKernel = 'DATASCIENCE.SELECT_REMOTE_JUPYTER_KERNEL', SetJupyterURIToLocal = 'DATASCIENCE.SET_JUPYTER_URI_LOCAL', SetJupyterURIToUserSpecified = 'DATASCIENCE.SET_JUPYTER_URI_USER_SPECIFIED', Interrupt = 'DATASCIENCE.INTERRUPT', @@ -350,17 +349,7 @@ export namespace Identifiers { } export namespace CodeSnippits { - export const ChangeDirectory = [ - '{0}', - '{1}', - 'import os', - 'try:', - "\tos.chdir(os.path.join(os.getcwd(), '{2}'))", - '\tprint(os.getcwd())', - 'except:', - '\tpass', - '' - ]; + export const ChangeDirectory = ['{0}', '{1}', 'import os', 'try:', "\tos.chdir(os.path.join(os.getcwd(), '{2}'))", '\tprint(os.getcwd())', 'except:', '\tpass', '']; export const ChangeDirectoryCommentIdentifier = '# ms-python.python added'; // Not translated so can compare. export const ImportIPython = '{0}\nfrom IPython import get_ipython\n\n{1}'; export const MatplotLibInitSvg = `import matplotlib\n%matplotlib inline\n${Identifiers.MatplotLibDefaultParams} = dict(matplotlib.rcParams)\n%config InlineBackend.figure_formats = {'svg', 'png'}`; diff --git a/src/client/datascience/jupyter/jupyterExecution.ts b/src/client/datascience/jupyter/jupyterExecution.ts index b739afec7d68..a5a5f5983fb7 100644 --- a/src/client/datascience/jupyter/jupyterExecution.ts +++ b/src/client/datascience/jupyter/jupyterExecution.ts @@ -221,6 +221,7 @@ export class JupyterExecutionBase implements IJupyterExecution { >(IJupyterSessionManagerFactory); const sessionManager = await sessionManagerFactory.create(connection); const kernelInterpreter = await this.kernelSelector.selectLocalKernel( + new StopWatch(), sessionManager, cancelToken, launchInfo.kernelSpec diff --git a/src/client/datascience/jupyter/kernels/kernelSelector.ts b/src/client/datascience/jupyter/kernels/kernelSelector.ts index f55f0bdecc9b..047475310058 100644 --- a/src/client/datascience/jupyter/kernels/kernelSelector.ts +++ b/src/client/datascience/jupyter/kernels/kernelSelector.ts @@ -90,13 +90,21 @@ export class KernelSelector { * @memberof KernelSelector */ public async selectRemoteKernel( + stopWatch: StopWatch, session: IJupyterSessionManager, cancelToken?: CancellationToken, currentKernel?: IJupyterKernelSpec | LiveKernelModel ): Promise { let suggestions = await this.selectionProvider.getKernelSelectionsForRemoteSession(session, cancelToken); suggestions = suggestions.filter(item => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || '')); - return this.selectKernel(suggestions, session, cancelToken, currentKernel); + return this.selectKernel( + stopWatch, + Telemetry.SelectRemoteJupyterKernel, + suggestions, + session, + cancelToken, + currentKernel + ); } /** * Select a kernel from a local session. @@ -107,13 +115,21 @@ export class KernelSelector { * @memberof KernelSelector */ public async selectLocalKernel( + stopWatch: StopWatch, session?: IJupyterSessionManager, cancelToken?: CancellationToken, currentKernel?: IJupyterKernelSpec | LiveKernelModel ): Promise { let suggestions = await this.selectionProvider.getKernelSelectionsForLocalSession(session, cancelToken); suggestions = suggestions.filter(item => !this.kernelIdsToHide.has(item.selection.kernelModel?.id || '')); - return this.selectKernel(suggestions, session, cancelToken, currentKernel); + return this.selectKernel( + stopWatch, + Telemetry.SelectLocalJupyterKernel, + suggestions, + session, + cancelToken, + currentKernel + ); } /** * Gets a kernel that needs to be used with a local session. @@ -168,7 +184,7 @@ export class KernelSelector { ); } else { telemetryProps.promptedToSelect = true; - selection = await this.selectLocalKernel(sessionManager, cancelToken); + selection = await this.selectLocalKernel(stopWatch, sessionManager, cancelToken); } } } else { @@ -263,6 +279,8 @@ export class KernelSelector { }; } private async selectKernel( + stopWatch: StopWatch, + telemetryEvent: Telemetry, suggestions: IKernelSpecQuickPickItem[], session?: IJupyterSessionManager, cancelToken?: CancellationToken, @@ -271,6 +289,7 @@ export class KernelSelector { const placeHolder = localize.DataScience.selectKernel() + (currentKernel ? ` (current: ${currentKernel.display_name || currentKernel.name})` : ''); + sendTelemetryEvent(telemetryEvent, stopWatch.elapsedTime); const selection = await this.applicationShell.showQuickPick(suggestions, { placeHolder }, cancelToken); if (!selection?.selection) { return {}; diff --git a/src/client/datascience/jupyter/kernels/kernelSwitcher.ts b/src/client/datascience/jupyter/kernels/kernelSwitcher.ts index 4826b2ac14a2..586442e61c17 100644 --- a/src/client/datascience/jupyter/kernels/kernelSwitcher.ts +++ b/src/client/datascience/jupyter/kernels/kernelSwitcher.ts @@ -8,8 +8,8 @@ import { ProgressLocation, ProgressOptions } from 'vscode'; import { IApplicationShell } from '../../../common/application/types'; import { IConfigurationService } from '../../../common/types'; import { Common, DataScience } from '../../../common/utils/localize'; -import { captureTelemetry } from '../../../telemetry'; -import { Commands, Settings, Telemetry } from '../../constants'; +import { StopWatch } from '../../../common/utils/stopWatch'; +import { Commands, Settings } from '../../constants'; import { IConnection, IJupyterKernelSpec, IJupyterSessionManagerFactory, INotebook } from '../../types'; import { JupyterSessionStartError } from '../jupyterSession'; import { KernelSelector, KernelSpecInterpreter } from './kernelSelector'; @@ -51,20 +51,19 @@ export class KernelSwitcher { return kernel; } - @captureTelemetry(Telemetry.SelectLocalJupyterKernel) private async selectLocalJupyterKernel( currentKernel?: IJupyterKernelSpec | LiveKernelModel ): Promise { - return this.kernelSelector.selectLocalKernel(undefined, undefined, currentKernel); + return this.kernelSelector.selectLocalKernel(new StopWatch(), undefined, undefined, currentKernel); } - @captureTelemetry(Telemetry.SelectRemoteJupyuterKernel) private async selectRemoteJupyterKernel( connInfo: IConnection, currentKernel?: IJupyterKernelSpec | LiveKernelModel ): Promise { + const stopWatch = new StopWatch(); const session = await this.jupyterSessionManagerFactory.create(connInfo); - return this.kernelSelector.selectRemoteKernel(session, undefined, currentKernel); + return this.kernelSelector.selectRemoteKernel(stopWatch, session, undefined, currentKernel); } private async switchKernelWithRetry(notebook: INotebook, kernel: KernelSpecInterpreter): Promise { const settings = this.configService.getSettings(); diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index ccd9d5219677..6196198b9519 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1534,7 +1534,7 @@ export interface IEventNamePropertyMapping { [Telemetry.SelfCertsMessageEnabled]: never | undefined; [Telemetry.SelectJupyterURI]: never | undefined; [Telemetry.SelectLocalJupyterKernel]: never | undefined; - [Telemetry.SelectRemoteJupyuterKernel]: never | undefined; + [Telemetry.SelectRemoteJupyterKernel]: never | undefined; [Telemetry.SessionIdleTimeout]: never | undefined; [Telemetry.JupyterNotInstalledErrorShown]: never | undefined; [Telemetry.JupyterCommandSearch]: { diff --git a/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts b/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts index 2962b6b88085..6ec0510af9e2 100644 --- a/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts +++ b/src/test/datascience/jupyter/kernels/kernelSelector.unit.test.ts @@ -14,6 +14,7 @@ import { IInstaller, Product } from '../../../../client/common/types'; import * as localize from '../../../../client/common/utils/localize'; import { noop } from '../../../../client/common/utils/misc'; import { Architecture } from '../../../../client/common/utils/platform'; +import { StopWatch } from '../../../../client/common/utils/stopWatch'; import { JupyterSessionManager } from '../../../../client/datascience/jupyter/jupyterSessionManager'; import { KernelSelectionProvider } from '../../../../client/datascience/jupyter/kernels/kernelSelections'; import { KernelSelector } from '../../../../client/datascience/jupyter/kernels/kernelSelector'; @@ -73,7 +74,7 @@ suite('Data Science - KernelSelector', () => { ).thenResolve([]); when(appShell.showQuickPick(anything(), anything(), anything())).thenResolve(); - const kernel = await kernelSelector.selectRemoteKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectRemoteKernel(new StopWatch(), instance(sessionManager)); assert.isEmpty(kernel); verify( @@ -87,7 +88,7 @@ suite('Data Science - KernelSelector', () => { ).thenResolve([]); when(appShell.showQuickPick(anything(), anything(), anything())).thenResolve(); - const kernel = await kernelSelector.selectLocalKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectLocalKernel(new StopWatch(), instance(sessionManager)); assert.isEmpty(kernel); verify( @@ -105,7 +106,7 @@ suite('Data Science - KernelSelector', () => { // tslint:disable-next-line: no-any } as any); - const kernel = await kernelSelector.selectRemoteKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectRemoteKernel(new StopWatch(), instance(sessionManager)); assert.isOk(kernel.kernelSpec === kernelSpec); assert.isOk(kernel.interpreter === interpreter); @@ -172,7 +173,7 @@ suite('Data Science - KernelSelector', () => { kernelSelector.addKernelToIgnoreList({ id: 'id2' } as any); // tslint:disable-next-line: no-any kernelSelector.addKernelToIgnoreList({ clientId: 'id4' } as any); - const kernel = await kernelSelector.selectRemoteKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectRemoteKernel(new StopWatch(), instance(sessionManager)); assert.isEmpty(kernel); verify( @@ -240,7 +241,7 @@ suite('Data Science - KernelSelector', () => { kernelSelector.addKernelToIgnoreList({ id: 'id2' } as any); // tslint:disable-next-line: no-any kernelSelector.addKernelToIgnoreList({ clientId: 'id4' } as any); - const kernel = await kernelSelector.selectLocalKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectLocalKernel(new StopWatch(), instance(sessionManager)); assert.isEmpty(kernel); verify( @@ -265,7 +266,7 @@ suite('Data Science - KernelSelector', () => { // tslint:disable-next-line: no-any } as any); - const kernel = await kernelSelector.selectLocalKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectLocalKernel(new StopWatch(), instance(sessionManager)); assert.isOk(kernel.kernelSpec === kernelSpec); assert.isOk(kernel.interpreter === interpreter); @@ -291,7 +292,7 @@ suite('Data Science - KernelSelector', () => { // tslint:disable-next-line: no-any } as any); - const kernel = await kernelSelector.selectLocalKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectLocalKernel(new StopWatch(), instance(sessionManager)); assert.isOk(kernel.kernelSpec === kernelSpec); verify(installer.isInstalled(Product.ipykernel, interpreter)).once(); @@ -323,7 +324,7 @@ suite('Data Science - KernelSelector', () => { // tslint:disable-next-line: no-any } as any); - const kernel = await kernelSelector.selectLocalKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectLocalKernel(new StopWatch(), instance(sessionManager)); assert.isOk(kernel.kernelSpec === kernelSpec); assert.isOk(kernel.interpreter === interpreter); @@ -354,7 +355,7 @@ suite('Data Science - KernelSelector', () => { // tslint:disable-next-line: no-any } as any); - const kernel = await kernelSelector.selectLocalKernel(instance(sessionManager)); + const kernel = await kernelSelector.selectLocalKernel(new StopWatch(), instance(sessionManager)); assert.isOk(kernel.kernelSpec === kernelSpec); verify(installer.isInstalled(Product.ipykernel, interpreter)).once(); @@ -381,6 +382,7 @@ suite('Data Science - KernelSelector', () => { let nbMetadata: nbformat.INotebookMetadata = {} as any; let selectLocalKernelStub: sinon.SinonStub< [ + StopWatch, (IJupyterSessionManager | undefined)?, (CancellationToken | undefined)?, (IJupyterKernelSpec | LiveKernelModel)? diff --git a/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts b/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts index 2af6a3697872..a8c58a4d9b42 100644 --- a/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts +++ b/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts @@ -129,7 +129,12 @@ suite('Data Science - Kernel Switcher', () => { if (isLocalConnection) { verify( - kernelSelector.selectLocalKernel(undefined, undefined, currentKernelInfo.currentKernel) + kernelSelector.selectLocalKernel( + anything(), + undefined, + undefined, + currentKernelInfo.currentKernel + ) ).once(); } else { verify(kernelSelector.selectRemoteKernel(anything(), anything(), anything())).once(); @@ -138,7 +143,12 @@ suite('Data Science - Kernel Switcher', () => { test('Prompt to select local kernel', async () => { when( - kernelSelector.selectLocalKernel(undefined, undefined, currentKernelInfo.currentKernel) + kernelSelector.selectLocalKernel( + anything(), + undefined, + undefined, + currentKernelInfo.currentKernel + ) ).thenResolve({}); const selection = await kernelSwitcher.switchKernel(instance(notebook)); @@ -151,6 +161,7 @@ suite('Data Science - Kernel Switcher', () => { if (isLocalConnection) { when( kernelSelector.selectLocalKernel( + anything(), undefined, undefined, currentKernelInfo.currentKernel @@ -161,13 +172,13 @@ suite('Data Science - Kernel Switcher', () => { interpreter: selectedInterpreter }); } else { - when(kernelSelector.selectRemoteKernel(anything(), anything(), anything())).thenResolve( - { - kernelModel: selectedKernel, - kernelSpec: undefined, - interpreter: selectedInterpreter - } - ); + when( + kernelSelector.selectRemoteKernel(anything(), anything(), anything(), anything()) + ).thenResolve({ + kernelModel: selectedKernel, + kernelSpec: undefined, + interpreter: selectedInterpreter + }); } }); teardown(() => { @@ -284,19 +295,19 @@ suite('Data Science - Kernel Switcher', () => { return; } }); - when(kernelSelector.selectLocalKernel(undefined, anything(), anything())).thenCall( - () => { - // When selecting a kernel the second time, then return a different selection. - firstTimeSelectingAKernel = false; - return { - kernelModel: firstTimeSelectingAKernel - ? selectedKernel - : selectedKernelSecondTime, - kernelSpec: undefined, - interpreter: selectedInterpreter - }; - } - ); + when( + kernelSelector.selectLocalKernel(anything(), undefined, anything(), anything()) + ).thenCall(() => { + // When selecting a kernel the second time, then return a different selection. + firstTimeSelectingAKernel = false; + return { + kernelModel: firstTimeSelectingAKernel + ? selectedKernel + : selectedKernelSecondTime, + kernelSpec: undefined, + interpreter: selectedInterpreter + }; + }); when(appShell.showErrorMessage(anything(), anything(), anything())).thenResolve( // tslint:disable-next-line: no-any DataScience.selectDifferentKernel() as any From 8545aee6fdea780cc9fbeaf2a0d5c00f8bed0636 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 12 Feb 2020 16:36:09 -0800 Subject: [PATCH 02/13] Add notebook language telemetry --- src/client/datascience/constants.ts | 32 +++++++++++++++++-- .../interactive-ipynb/nativeEditor.ts | 25 +++++++++++++++ src/client/telemetry/index.ts | 12 +++++++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 7bc031aab24e..1f5469a23c4e 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -14,6 +14,20 @@ export const JUPYTER_OUTPUT_CHANNEL = 'JUPYTER_OUTPUT_CHANNEL'; // Python Module to be used when instantiating the Python Daemon. export const PythonDaemonModule = 'datascience.jupyter_daemon'; +export const KnownNotebookLanguages: string[] = [ + 'python', + 'R', + 'Julia', + 'C++', + 'C#', + 'F#', + 'Scala', + 'Haskell', + 'bash', + 'cling', + 'SAS' +]; + export namespace Commands { export const RunAllCells = 'python.datascience.runallcells'; export const RunAllCellsAbove = 'python.datascience.runallcellsabove'; @@ -100,7 +114,8 @@ export namespace RegExpValues { export const KernelSpecOutputRegEx = /^\s*(\S+)\s+(\S+)$/; // This next one has to be a string because uglifyJS isn't handling the groups. We use named-js-regexp to parse it // instead. - export const UrlPatternRegEx = '(?https?:\\/\\/)((\\(.+\\s+or\\s+(?.+)\\))|(?[^\\s]+))(?:.+)'; + export const UrlPatternRegEx = + '(?https?:\\/\\/)((\\(.+\\s+or\\s+(?.+)\\))|(?[^\\s]+))(?:.+)'; export interface IUrlPatternGroupType { LOCAL: string | undefined; PREFIX: string | undefined; @@ -243,7 +258,8 @@ export enum Telemetry { OpenedInteractiveWindow = 'DATASCIENCE.OPENED_INTERACTIVE', FindKernelForLocalConnection = 'DS_INTERNAL.FIND_KERNEL_FOR_LOCAL_CONNECTION', CompletionTimeFromLS = 'DS_INTERNAL.COMPLETION_TIME_FROM_LS', - CompletionTimeFromJupyter = 'DS_INTERNAL.COMPLETION_TIME_FROM_JUPYTER' + CompletionTimeFromJupyter = 'DS_INTERNAL.COMPLETION_TIME_FROM_JUPYTER', + NotebookLanguage = 'DATASCIENCE.NOTEBOOK_LANGUAGE' } export enum NativeKeyboardCommandTelemetry { @@ -349,7 +365,17 @@ export namespace Identifiers { } export namespace CodeSnippits { - export const ChangeDirectory = ['{0}', '{1}', 'import os', 'try:', "\tos.chdir(os.path.join(os.getcwd(), '{2}'))", '\tprint(os.getcwd())', 'except:', '\tpass', '']; + export const ChangeDirectory = [ + '{0}', + '{1}', + 'import os', + 'try:', + "\tos.chdir(os.path.join(os.getcwd(), '{2}'))", + '\tprint(os.getcwd())', + 'except:', + '\tpass', + '' + ]; export const ChangeDirectoryCommentIdentifier = '# ms-python.python added'; // Not translated so can compare. export const ImportIPython = '{0}\nfrom IPython import get_ipython\n\n{1}'; export const MatplotLibInitSvg = `import matplotlib\n%matplotlib inline\n${Identifiers.MatplotLibDefaultParams} = dict(matplotlib.rcParams)\n%config InlineBackend.figure_formats = {'svg', 'png'}`; diff --git a/src/client/datascience/interactive-ipynb/nativeEditor.ts b/src/client/datascience/interactive-ipynb/nativeEditor.ts index a879a576d42f..93b4d4b63317 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditor.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditor.ts @@ -43,6 +43,7 @@ import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; import { EditorContexts, Identifiers, + KnownNotebookLanguages, NativeKeyboardCommandTelemetryLookup, NativeMouseCommandTelemetryLookup, Telemetry @@ -627,6 +628,9 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { // Then save the contents. We'll stick our cells back into this format when we save if (json) { this.notebookJson = json; + + // Log language or kernel telemetry + this.sendLanguageTelemetry(this.notebookJson); } this.contentsLoadedPromise.resolve(); @@ -650,6 +654,27 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { ); } + private sendLanguageTelemetry(notebookJson: Partial) { + try { + // See if we have a language + let language = ''; + if (notebookJson.metadata?.language_info?.name) { + language = notebookJson.metadata?.language_info?.name; + } else if (notebookJson.metadata?.kernelspec?.language) { + language = notebookJson.metadata?.kernelspec?.language.toString(); + } + if (language && !KnownNotebookLanguages.includes(language)) { + language = 'unknown'; + } + if (language) { + sendTelemetryEvent(Telemetry.NotebookLanguage, undefined, { language }); + } + } catch { + // If this fails, doesn't really matter + noop(); + } + } + private async loadCells(cells: ICell[], forceDirty: boolean): Promise { // Make sure cells have at least 1 if (cells.length === 0) { diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 6196198b9519..e5adefe94062 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1749,4 +1749,16 @@ export interface IEventNamePropertyMapping { * @memberof IEventNamePropertyMapping */ [Telemetry.CompletionTimeFromJupyter]: undefined | never; + /** + * Telemetry event sent to indicate the language used in a notebook + * + * @type {(undefined | never)} + * @memberof IEventNamePropertyMapping + */ + [Telemetry.NotebookLanguage]: { + /** + * Language found in the notebook if a known language. Otherwise 'unknown' + */ + language: string; + }; } From 8c04a7abe219b83351323ad086f06cf0de443100 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 12 Feb 2020 16:38:23 -0800 Subject: [PATCH 03/13] Add news entries --- news/3 Code Health/10049.md | 1 + news/3 Code Health/9819.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 news/3 Code Health/10049.md create mode 100644 news/3 Code Health/9819.md diff --git a/news/3 Code Health/10049.md b/news/3 Code Health/10049.md new file mode 100644 index 000000000000..8db350b7860d --- /dev/null +++ b/news/3 Code Health/10049.md @@ -0,0 +1 @@ +Change select kernel telemetry to track duration till quick pick appears. \ No newline at end of file diff --git a/news/3 Code Health/9819.md b/news/3 Code Health/9819.md new file mode 100644 index 000000000000..76445586b312 --- /dev/null +++ b/news/3 Code Health/9819.md @@ -0,0 +1 @@ +Add telemetry to track notebook languages \ No newline at end of file From ec0e889b8fdd23ebc6afe18e45222e9b7a5c2766 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 12 Feb 2020 17:15:58 -0800 Subject: [PATCH 04/13] #9883 telemetry --- src/client/datascience/constants.ts | 7 ++- .../interactive-ipynb/nativeEditor.ts | 2 +- ...yterCommandInterpreterDependencyService.ts | 23 +++++--- .../jupyterInterpreterDependencyService.ts | 5 +- .../jupyter/kernels/kernelSelector.ts | 24 ++++++-- .../jupyter/kernels/kernelService.ts | 12 +++- src/client/telemetry/index.ts | 57 ++++++++++++++++++- 7 files changed, 113 insertions(+), 17 deletions(-) diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 1f5469a23c4e..a3cc2b63c627 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -259,7 +259,12 @@ export enum Telemetry { FindKernelForLocalConnection = 'DS_INTERNAL.FIND_KERNEL_FOR_LOCAL_CONNECTION', CompletionTimeFromLS = 'DS_INTERNAL.COMPLETION_TIME_FROM_LS', CompletionTimeFromJupyter = 'DS_INTERNAL.COMPLETION_TIME_FROM_JUPYTER', - NotebookLanguage = 'DATASCIENCE.NOTEBOOK_LANGUAGE' + NotebookLanguage = 'DATASCIENCE.NOTEBOOK_LANGUAGE', + KernelSpecNotFound = 'DS_INTERNAL.KERNEL_SPEC_NOT_FOUND', + KernelRegisterFailed = 'DS_INTERNAL.KERNEL_REGISTER_FAILED', + KernelEnumeration = 'DS_INTERNAL.KERNEL_ENUMERATION', + JupyterInstallFailed = 'DS_INTERNAL.JUPYTER_INSTALL_FAILED', + UserInstalledModule = 'DATASCIENCE.USER_INSTALLED_MODULE' } export enum NativeKeyboardCommandTelemetry { diff --git a/src/client/datascience/interactive-ipynb/nativeEditor.ts b/src/client/datascience/interactive-ipynb/nativeEditor.ts index 93b4d4b63317..937531e47762 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditor.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditor.ts @@ -433,7 +433,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { } } - @captureTelemetry(Telemetry.ExecuteNativeCell, undefined, false) + @captureTelemetry(Telemetry.ExecuteNativeCell, undefined, true) // tslint:disable-next-line:no-any protected async reexecuteCell(info: ISubmitNewCell): Promise { try { diff --git a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts index cf7267414313..26fbf3279d91 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts @@ -9,6 +9,7 @@ import { ProductNames } from '../../../common/installer/productNames'; import { IInstallationChannelManager } from '../../../common/installer/types'; import { Product } from '../../../common/types'; import { DataScience } from '../../../common/utils/localize'; +import { StopWatch } from '../../../common/utils/stopWatch'; import { sendTelemetryEvent } from '../../../telemetry'; import { Telemetry } from '../../constants'; import { IJupyterInterpreterDependencyManager } from '../../types'; @@ -37,20 +38,28 @@ export class JupyterCommandInterpreterDependencyService implements IJupyterInter // If Conda is available, always pick it as the user must have a Conda Environment const installer = installers.find(ins => ins.name === 'Conda'); const product = ProductNames.get(Product.jupyter); + const stopWatch = new StopWatch(); if (installer && product) { - sendTelemetryEvent(Telemetry.UserInstalledJupyter); installer .installModule(product) - .catch(e => - this.applicationShell.showErrorMessage(e.message, DataScience.pythonInteractiveHelpLink()) - ); + .then(() => { + sendTelemetryEvent(Telemetry.UserInstalledJupyter, stopWatch.elapsedTime); + }) + .catch(e => { + sendTelemetryEvent(Telemetry.JupyterInstallFailed, undefined, { product }); + this.applicationShell.showErrorMessage(e.message, DataScience.pythonInteractiveHelpLink()); + }); } else if (installers[0] && product) { installers[0] .installModule(product) - .catch(e => - this.applicationShell.showErrorMessage(e.message, DataScience.pythonInteractiveHelpLink()) - ); + .then(() => { + sendTelemetryEvent(Telemetry.UserInstalledModule, stopWatch.elapsedTime, { product }); + }) + .catch(e => { + sendTelemetryEvent(Telemetry.JupyterInstallFailed, undefined, { product }); + this.applicationShell.showErrorMessage(e.message, DataScience.pythonInteractiveHelpLink()); + }); } } } else if (response === DataScience.notebookCheckForImportNo()) { diff --git a/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts b/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts index 43555442abe8..d06fb1f0669e 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterInterpreterDependencyService.ts @@ -303,7 +303,10 @@ export class JupyterInterpreterDependencyService { return execService .execModule('jupyter', ['kernelspec', '--version'], { throwOnStdErr: true }) .then(() => true) - .catch(() => false); + .catch(() => { + sendTelemetryEvent(Telemetry.KernelSpecNotFound); + return false; + }); } /** diff --git a/src/client/datascience/jupyter/kernels/kernelSelector.ts b/src/client/datascience/jupyter/kernels/kernelSelector.ts index 047475310058..36193b584dd0 100644 --- a/src/client/datascience/jupyter/kernels/kernelSelector.ts +++ b/src/client/datascience/jupyter/kernels/kernelSelector.ts @@ -15,7 +15,7 @@ import { noop } from '../../../common/utils/misc'; import { StopWatch } from '../../../common/utils/stopWatch'; import { IInterpreterService, PythonInterpreter } from '../../../interpreter/contracts'; import { IEventNamePropertyMapping, sendTelemetryEvent } from '../../../telemetry'; -import { Telemetry } from '../../constants'; +import { KnownNotebookLanguages, Telemetry } from '../../constants'; import { reportAction } from '../../progress/decorator'; import { ReportableAction } from '../../progress/types'; import { IJupyterKernelSpec, IJupyterSessionManager } from '../../types'; @@ -299,7 +299,9 @@ export class KernelSelector { sendTelemetryEvent(Telemetry.SwitchToInterpreterAsKernel); return this.useInterpreterAsKernel(selection.selection.interpreter, undefined, session, false, cancelToken); } else if (selection.selection.kernelModel) { - sendTelemetryEvent(Telemetry.SwitchToExistingKernel); + sendTelemetryEvent(Telemetry.SwitchToExistingKernel, undefined, { + language: this.computeLanguage(selection.selection.kernelModel.language) + }); // tslint:disable-next-line: no-any const interpreter = selection.selection.kernelModel ? await this.kernelService.findMatchingInterpreter(selection.selection.kernelModel, cancelToken) @@ -310,7 +312,9 @@ export class KernelSelector { kernelModel: selection.selection.kernelModel }; } else if (selection.selection.kernelSpec) { - sendTelemetryEvent(Telemetry.SwitchToExistingKernel); + sendTelemetryEvent(Telemetry.SwitchToExistingKernel, undefined, { + language: this.computeLanguage(selection.selection.kernelSpec.language) + }); const interpreter = selection.selection.kernelSpec ? await this.kernelService.findMatchingInterpreter(selection.selection.kernelSpec, cancelToken) : undefined; @@ -367,7 +371,12 @@ export class KernelSelector { } // Try an install this interpreter as a kernel. - kernelSpec = await this.kernelService.registerKernel(interpreter, disableUI, cancelToken); + try { + kernelSpec = await this.kernelService.registerKernel(interpreter, disableUI, cancelToken); + } catch (e) { + sendTelemetryEvent(Telemetry.KernelRegisterFailed); + throw e; + } // If we have a display name of a kernel that could not be found, // then notify user that we're using current interpreter instead. @@ -387,4 +396,11 @@ export class KernelSelector { return { kernelSpec, interpreter }; } + + private computeLanguage(language: string | undefined): string { + if (language && KnownNotebookLanguages.includes(language)) { + return language; + } + return 'unknown'; + } } diff --git a/src/client/datascience/jupyter/kernels/kernelService.ts b/src/client/datascience/jupyter/kernels/kernelService.ts index 66fbce7ac8c7..dd376a420f11 100644 --- a/src/client/datascience/jupyter/kernels/kernelService.ts +++ b/src/client/datascience/jupyter/kernels/kernelService.ts @@ -424,7 +424,17 @@ export class KernelService { return []; } const specs: IJupyterKernelSpec[] = await enumerator; - return specs.filter(item => !!item); + const result = specs.filter(item => !!item); + + // Send telemetry on this enumeration. + const anyPython = result.find(k => k.language === 'python') !== undefined; + sendTelemetryEvent(Telemetry.KernelEnumeration, undefined, { + count: result.length, + isPython: anyPython, + source: sessionManager ? 'connection' : 'cli' + }); + + return result; } /** * Not all characters are allowed in a kernel name. diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index e5adefe94062..d0e7d9b8c8d1 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1465,7 +1465,7 @@ export interface IEventNamePropertyMapping { [Telemetry.RegisterAndUseInterpreterAsKernel]: never | undefined; [Telemetry.UseInterpreterAsKernel]: never | undefined; [Telemetry.UseExistingKernel]: never | undefined; - [Telemetry.SwitchToExistingKernel]: never | undefined; + [Telemetry.SwitchToExistingKernel]: { language: string }; [Telemetry.SwitchToInterpreterAsKernel]: never | undefined; [Telemetry.ConvertToPythonFile]: never | undefined; [Telemetry.CopySourceCode]: never | undefined; @@ -1752,7 +1752,7 @@ export interface IEventNamePropertyMapping { /** * Telemetry event sent to indicate the language used in a notebook * - * @type {(undefined | never)} + * @type { language: string } * @memberof IEventNamePropertyMapping */ [Telemetry.NotebookLanguage]: { @@ -1761,4 +1761,57 @@ export interface IEventNamePropertyMapping { */ language: string; }; + /** + * Telemetry event sent to indicate 'jupyter kernelspec' is not possible. + * + * @type {(undefined | never)} + * @memberof IEventNamePropertyMapping + */ + [Telemetry.KernelSpecNotFound]: undefined | never; + /** + * Telemetry event sent to indicate registering a kernel with jupyter failed. + * + * @type {(undefined | never)} + * @memberof IEventNamePropertyMapping + */ + [Telemetry.KernelRegisterFailed]: undefined | never; + /** + * Telemetry event sent to every time a kernel enumeration is done + * + * @type {...} + * @memberof IEventNamePropertyMapping + */ + [Telemetry.KernelEnumeration]: { + /** + * Count of the number of kernels found + */ + count: number; + /** + * Boolean indicating if any are python or not + */ + isPython: boolean; + /** + * Indicates how the enumeration was acquired. + */ + source: 'cli' | 'connection'; + }; + /** + * Telemetry event sent if there's an error installing a jupyter required dependency + * + * @type { product: string } + * @memberof IEventNamePropertyMapping + */ + [Telemetry.JupyterInstallFailed]: { + /** + * Product being installed (jupyter or ipykernel or other) + */ + product: string; + }; + /** + * Telemetry event sent when installing a jupyter dependency + * + * @type {product: string} + * @memberof IEventNamePropertyMapping + */ + [Telemetry.UserInstalledModule]: { product: string }; } From e411a4e1907b20af62e221b7b98af0c76a492fe9 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 12 Feb 2020 17:16:07 -0800 Subject: [PATCH 05/13] News entry --- news/3 Code Health/9883.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/3 Code Health/9883.md diff --git a/news/3 Code Health/9883.md b/news/3 Code Health/9883.md new file mode 100644 index 000000000000..bf4fc2168a21 --- /dev/null +++ b/news/3 Code Health/9883.md @@ -0,0 +1 @@ +Telemetry around kernels not working and installs not working. \ No newline at end of file From 15f2e50cdececa1ef08d608dcdb7d10f2bdebe7e Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 10:17:42 -0800 Subject: [PATCH 06/13] Another spot for kernel spec failure --- .../jupyterInterpreterSubCommandExecutionService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts b/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts index a51fb4af1814..dfd2c29c86e2 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterInterpreterSubCommandExecutionService.ts @@ -15,7 +15,8 @@ import { DataScience } from '../../../common/utils/localize'; import { noop } from '../../../common/utils/misc'; import { EXTENSION_ROOT_DIR } from '../../../constants'; import { IInterpreterService, PythonInterpreter } from '../../../interpreter/contracts'; -import { JUPYTER_OUTPUT_CHANNEL, PythonDaemonModule } from '../../constants'; +import { sendTelemetryEvent } from '../../../telemetry'; +import { JUPYTER_OUTPUT_CHANNEL, PythonDaemonModule, Telemetry } from '../../constants'; import { IJupyterInterpreterDependencyManager, IJupyterSubCommandExecutionService } from '../../types'; import { JupyterServerInfo } from '../jupyterConnection'; import { JupyterInstallError } from '../jupyterInstallError'; @@ -189,6 +190,7 @@ export class JupyterInterpreterSubCommandExecutionService .execModule('jupyter', ['kernelspec', 'list', '--json'], spawnOptions) .then(output => output.stdout) .catch(daemonEx => { + sendTelemetryEvent(Telemetry.KernelSpecNotFound); traceError('Failed to list kernels from daemon', daemonEx); return ''; }); From 55b75ddf318e299b37499de54ba7c89ca225796d Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 10:48:19 -0800 Subject: [PATCH 07/13] Add telemetry on product install --- .../common/installer/productInstaller.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/client/common/installer/productInstaller.ts b/src/client/common/installer/productInstaller.ts index 851a411913c2..87c4d3e1e8e1 100644 --- a/src/client/common/installer/productInstaller.ts +++ b/src/client/common/installer/productInstaller.ts @@ -5,6 +5,7 @@ import * as os from 'os'; import { CancellationToken, OutputChannel, Uri } from 'vscode'; import '../../common/extensions'; import * as localize from '../../common/utils/localize'; +import { Telemetry } from '../../datascience/constants'; import { IInterpreterService } from '../../interpreter/contracts'; import { IServiceContainer } from '../../ioc/types'; import { LinterId } from '../../linters/types'; @@ -27,6 +28,7 @@ import { ProductType } from '../types'; import { isResource } from '../utils/misc'; +import { StopWatch } from '../utils/stopWatch'; import { ProductNames } from './productNames'; import { IInstallationChannelManager, InterpreterUri, IProductPathService, IProductService } from './types'; @@ -355,7 +357,22 @@ export class DataScienceInstaller extends BaseInstaller { 'Yes', 'No' ); - return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; + if (item === 'Yes') { + const stopWatch = new StopWatch(); + try { + const response = await this.install(product, resource, cancel); + const event = + product === Product.jupyter ? Telemetry.UserInstalledJupyter : Telemetry.UserInstalledModule; + sendTelemetryEvent(event, stopWatch.elapsedTime, { product: product.toString() }); + return response; + } catch (e) { + if (product === Product.jupyter) { + sendTelemetryEvent(Telemetry.JupyterInstallFailed); + } + throw e; + } + } + return InstallerResponse.Ignore; } } From 55cd1587301b3e27bb3835736b1ad06efe7fd150 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 10:54:56 -0800 Subject: [PATCH 08/13] Fix install telemetry --- src/client/common/installer/productInstaller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/common/installer/productInstaller.ts b/src/client/common/installer/productInstaller.ts index 87c4d3e1e8e1..639ad4a96951 100644 --- a/src/client/common/installer/productInstaller.ts +++ b/src/client/common/installer/productInstaller.ts @@ -363,7 +363,7 @@ export class DataScienceInstaller extends BaseInstaller { const response = await this.install(product, resource, cancel); const event = product === Product.jupyter ? Telemetry.UserInstalledJupyter : Telemetry.UserInstalledModule; - sendTelemetryEvent(event, stopWatch.elapsedTime, { product: product.toString() }); + sendTelemetryEvent(event, stopWatch.elapsedTime, { product: productName }); return response; } catch (e) { if (product === Product.jupyter) { From a8cae87dd887fe9e66e7338daad7292af2c5ecb8 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 13:06:39 -0800 Subject: [PATCH 09/13] Undo launch.json change --- .vscode/launch.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9564250b2ef5..451f2f9d3c67 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,10 +19,7 @@ "preLaunchTask": "Compile", "skipFiles": [ "/**" - ], - "env": { - "VSC_PYTHON_LOG_TELEMETRY": "true" - } + ] }, { "name": "Extension inside container", From 89f30c59024b7a5678b47042b6300c86efcc0b7f Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 13:18:36 -0800 Subject: [PATCH 10/13] Handle other cases --- src/client/datascience/constants.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index f695a339bc52..489e77342eee 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -14,6 +14,7 @@ export const JUPYTER_OUTPUT_CHANNEL = 'JUPYTER_OUTPUT_CHANNEL'; // Python Module to be used when instantiating the Python Daemon. export const PythonDaemonModule = 'datascience.jupyter_daemon'; +// List of 'language' names that we know about. Some are dupes with lower case export const KnownNotebookLanguages: string[] = [ 'python', 'R', @@ -25,7 +26,16 @@ export const KnownNotebookLanguages: string[] = [ 'Haskell', 'bash', 'cling', - 'SAS' + 'SAS', + 'Python', + 'r', + 'julia', + 'c++', + 'c#', + 'f#', + 'scala', + 'haskell', + 'sas' ]; export namespace Commands { From b53b6a739ecb276a870487029601397bb4ec0456 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 13:24:05 -0800 Subject: [PATCH 11/13] Better way to handle case --- src/client/datascience/constants.ts | 15 +++------------ .../datascience/interactive-ipynb/nativeEditor.ts | 2 +- .../datascience/jupyter/kernels/kernelSelector.ts | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 489e77342eee..4ae92a96395e 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -14,20 +14,9 @@ export const JUPYTER_OUTPUT_CHANNEL = 'JUPYTER_OUTPUT_CHANNEL'; // Python Module to be used when instantiating the Python Daemon. export const PythonDaemonModule = 'datascience.jupyter_daemon'; -// List of 'language' names that we know about. Some are dupes with lower case +// List of 'language' names that we know about. All should be lower case as that's how we compare. export const KnownNotebookLanguages: string[] = [ 'python', - 'R', - 'Julia', - 'C++', - 'C#', - 'F#', - 'Scala', - 'Haskell', - 'bash', - 'cling', - 'SAS', - 'Python', 'r', 'julia', 'c++', @@ -35,6 +24,8 @@ export const KnownNotebookLanguages: string[] = [ 'f#', 'scala', 'haskell', + 'bash', + 'cling', 'sas' ]; diff --git a/src/client/datascience/interactive-ipynb/nativeEditor.ts b/src/client/datascience/interactive-ipynb/nativeEditor.ts index 937531e47762..9e9d4ef4d7d0 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditor.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditor.ts @@ -663,7 +663,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { } else if (notebookJson.metadata?.kernelspec?.language) { language = notebookJson.metadata?.kernelspec?.language.toString(); } - if (language && !KnownNotebookLanguages.includes(language)) { + if (language && !KnownNotebookLanguages.includes(language.toLowerCase())) { language = 'unknown'; } if (language) { diff --git a/src/client/datascience/jupyter/kernels/kernelSelector.ts b/src/client/datascience/jupyter/kernels/kernelSelector.ts index 36193b584dd0..33cbaa3bf258 100644 --- a/src/client/datascience/jupyter/kernels/kernelSelector.ts +++ b/src/client/datascience/jupyter/kernels/kernelSelector.ts @@ -398,7 +398,7 @@ export class KernelSelector { } private computeLanguage(language: string | undefined): string { - if (language && KnownNotebookLanguages.includes(language)) { + if (language && KnownNotebookLanguages.includes(language.toLowerCase())) { return language; } return 'unknown'; From 2f2462d891865810b6b6a5ec5858dad173180985 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 13:28:51 -0800 Subject: [PATCH 12/13] Wrong event for jupyter install --- .../interpreter/jupyterCommandInterpreterDependencyService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts index 26fbf3279d91..d7b0cb24beaf 100644 --- a/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts +++ b/src/client/datascience/jupyter/interpreter/jupyterCommandInterpreterDependencyService.ts @@ -54,7 +54,7 @@ export class JupyterCommandInterpreterDependencyService implements IJupyterInter installers[0] .installModule(product) .then(() => { - sendTelemetryEvent(Telemetry.UserInstalledModule, stopWatch.elapsedTime, { product }); + sendTelemetryEvent(Telemetry.UserInstalledJupyter, stopWatch.elapsedTime); }) .catch(e => { sendTelemetryEvent(Telemetry.JupyterInstallFailed, undefined, { product }); From 73c206c8ca52e2f3c94808eade30f3dd163f4490 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Thu, 13 Feb 2020 14:01:24 -0800 Subject: [PATCH 13/13] Fix unit tests --- .../jupyter/kernels/kernelSwitcher.unit.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts b/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts index a8c58a4d9b42..c446b5100e0a 100644 --- a/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts +++ b/src/test/datascience/jupyter/kernels/kernelSwitcher.unit.test.ts @@ -137,7 +137,9 @@ suite('Data Science - Kernel Switcher', () => { ) ).once(); } else { - verify(kernelSelector.selectRemoteKernel(anything(), anything(), anything())).once(); + verify( + kernelSelector.selectRemoteKernel(anything(), anything(), anything(), anything()) + ).once(); } }); @@ -329,7 +331,9 @@ suite('Data Science - Kernel Switcher', () => { ) ).once(); // first time when user select a kernel, second time is when user selects after failing to switch to the first kernel. - verify(kernelSelector.selectLocalKernel(anything(), anything(), anything())).twice(); + verify( + kernelSelector.selectLocalKernel(anything(), anything(), anything(), anything()) + ).twice(); }); }); });