From 3e117ba69811d6f5fea29f4c837e575266f73885 Mon Sep 17 00:00:00 2001 From: David Kutugata Date: Wed, 22 Jul 2020 14:54:42 -0700 Subject: [PATCH 1/3] Fix the gather survey (#13086) * fix the gather survey added 'gather stats' telemetry mention the gather comments to update the python ext * oops * fix tests and address comments * update gather stats when resetting the kernel * set globalstate vars to 0 when we open vs code * fix gather stats telemetry --- package.json | 11 +++++++++++ package.nls.json | 12 +++++++++--- src/client/common/application/commands.ts | 1 + src/client/common/utils/localize.ts | 4 ++-- .../datascience/commands/commandRegistry.ts | 9 +++++++-- src/client/datascience/constants.ts | 2 ++ src/client/datascience/gather/gatherListener.ts | 17 +++++++++++++++++ .../interactive-common/linkProvider.ts | 4 ++-- src/client/telemetry/index.ts | 6 ++++++ 9 files changed, 57 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 2197d57833a6..e60bfb589563 100644 --- a/package.json +++ b/package.json @@ -765,6 +765,11 @@ "command": "python.datascience.gatherquality", "title": "DataScience.gatherQuality", "category": "Python" + }, + { + "command": "python.datascience.latestExtension", + "title": "DataScience.latestExtension", + "category": "Python" } ], "menus": { @@ -1276,6 +1281,12 @@ "category": "Python", "when": "false" }, + { + "command": "python.datascience.latestExtension", + "title": "%DataScience.latestExtension%", + "category": "Python", + "when": "false" + }, { "command": "python.datascience.export", "title": "%DataScience.notebookExportAs%", diff --git a/package.nls.json b/package.nls.json index 8103c7b7e653..2bef35ccb426 100644 --- a/package.nls.json +++ b/package.nls.json @@ -475,8 +475,8 @@ "DataScience.findJupyterCommandProgressCheckInterpreter": "Checking {0}.", "DataScience.findJupyterCommandProgressSearchCurrentPath": "Searching current path.", "DataScience.gatherError": "Gather internal error", - "DataScience.gatheredScriptDescription": "# This file was generated by the Gather Extension.\n#\n# The intent is that it contains only the code required to produce\n# the same results as the cell originally selected for gathering.\n# Please note that the Python analysis is quite conservative, so if\n# it is unsure whether a line of code is necessary for execution, it\n# will err on the side of including it.\n#\n# Please let us know if you are satisfied with what was gathered here:\n# https://aka.ms/gathersurvey\n\n", - "DataScience.gatheredNotebookDescriptionInMarkdown": "## Gathered Notebook\nGathered from ```{0}```\n\n| | |\n|---|---|\n|   |This notebook was generated by the Gather Extension. The intent is that it contains only the code and cells required to produce the same results as the cell originally selected for gathering. Please note that the Python analysis is quite conservative, so if it is unsure whether a line of code is necessary for execution, it will err on the side of including it.|\n\n**Are you satisfied with the code that was gathered?**\n\n[Yes](https://command:python.datascience.gatherquality?yes) [No](https://command:python.datascience.gatherquality?no)", + "DataScience.gatheredScriptDescription": "# This file was generated by the Gather Extension.\n# It requires version 2020.7.94776 (or newer) of the Python Extension.\n#\n# The intent is that it contains only the code required to produce\n# the same results as the cell originally selected for gathering.\n# Please note that the Python analysis is quite conservative, so if\n# it is unsure whether a line of code is necessary for execution, it\n# will err on the side of including it.\n#\n# Please let us know if you are satisfied with what was gathered here:\n# https://aka.ms/gathersurvey\n\n", + "DataScience.gatheredNotebookDescriptionInMarkdown": "## Gathered Notebook\nGathered from ```{0}```\n\n| | |\n|---|---|\n|   |This notebook was generated by the Gather Extension. It requires version 2020.7.94776 (or newer) of the Python Extension, please update [here](https://command:python.datascience.latestExtension). The intent is that it contains only the code and cells required to produce the same results as the cell originally selected for gathering. Please note that the Python analysis is quite conservative, so if it is unsure whether a line of code is necessary for execution, it will err on the side of including it.|\n\n**Are you satisfied with the code that was gathered?**\n\n[Yes](https://command:python.datascience.gatherquality?yes) [No](https://command:python.datascience.gatherquality?no)", "DataScience.savePngTitle": "Save Image", "DataScience.jupyterSelectURIQuickPickTitle": "Pick how to connect to Jupyter", "DataScience.jupyterSelectURIQuickPickPlaceholder": "Choose an option", @@ -515,6 +515,7 @@ "DataScience.jupyterSelectURIQuickPickTitleRemoteOnly": "Pick an already running jupyter server", "DataScience.jupyterSelectURIRemoteDetail": "Specify the URI of an existing server", "DataScience.gatherQuality": "Did gather work as desired?", + "DataScience.latestExtension": "Download the latest version of the Python Extension", "DataScience.loadClassFailedWithNoInternet": "Error loading {0}:{1}. Internet connection required for loading 3rd party widgets.", "DataScience.useCDNForWidgets": "Widgets require us to download supporting files from a 3rd party website. Click [here](https://aka.ms/PVSCIPyWidgets) for more information.", "DataScience.loadThirdPartyWidgetScriptsPostEnabled": "Please restart the Kernel when changing the setting 'python.dataScience.widgetScriptSources'.", @@ -557,5 +558,10 @@ "DataScienceRendererExtension.downloadingMessage": "Downloading Notebook Renderers Extension...", "DataScienceRendererExtension.downloadCompletedOutputMessage": "Notebook Renderers extension download complete.", "DataScience.uriProviderDescriptionFormat": "{0} (From {1} extension)", - "DataScience.unknownPackage": "unknown" + "DataScience.unknownPackage": "unknown", + "DataScience.interactiveWindowTitleFormat": "Python Interactive - {0}", + "DataScience.interactiveWindowModeBannerTitle": "Do you want to open a new Python Interactive window for this file? [More Information](command:workbench.action.openSettings?%5B%22python.dataScience.interactiveWindowMode%22%5D).", + "DataScience.interactiveWindowModeBannerSwitchYes": "Yes", + "DataScience.interactiveWindowModeBannerSwitchAlways": "Always", + "DataScience.interactiveWindowModeBannerSwitchNo": "No" } diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index 9e35df989c56..3a5985e972aa 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -182,6 +182,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu [DSCommands.SaveAsNotebookNonCustomEditor]: [Uri, Uri]; [DSCommands.OpenNotebookNonCustomEditor]: [Uri]; [DSCommands.GatherQuality]: [string]; + [DSCommands.LatestExtension]: [string]; [DSCommands.EnableLoadingWidgetsFrom3rdPartySource]: [undefined | never]; [DSCommands.TrustNotebook]: [undefined | never | Uri]; } diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index 324fc2e2f914..ba07eddce146 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -870,11 +870,11 @@ export namespace DataScience { export const gatherError = localize('DataScience.gatherError', 'Gather internal error'); export const gatheredScriptDescription = localize( 'DataScience.gatheredScriptDescription', - '# This file was generated by the Gather Extension.\n#\n# The intent is that it contains only the code required to produce\n# the same results as the cell originally selected for gathering.\n# Please note that the Python analysis is quite conservative, so if\n# it is unsure whether a line of code is necessary for execution, it\n# will err on the side of including it.\n#\n# Please let us know if you are satisfied with what was gathered here:\n# https://aka.ms/gathersurvey\n\n' + '# This file was generated by the Gather Extension.\n# It requires version 2020.7.94776 (or newer) of the Python Extension.\n#\n# The intent is that it contains only the code required to produce\n# the same results as the cell originally selected for gathering.\n# Please note that the Python analysis is quite conservative, so if\n# it is unsure whether a line of code is necessary for execution, it\n# will err on the side of including it.\n#\n# Please let us know if you are satisfied with what was gathered here:\n# https://aka.ms/gathersurvey\n\n' ); export const gatheredNotebookDescriptionInMarkdown = localize( 'DataScience.gatheredNotebookDescriptionInMarkdown', - '# Gathered Notebook\nGathered from ```{0}```\n\n| | |\n|---|---|\n|   |This notebook was generated by the Gather Extension. The intent is that it contains only the code and cells required to produce the same results as the cell originally selected for gathering. Please note that the Python analysis is quite conservative, so if it is unsure whether a line of code is necessary for execution, it will err on the side of including it.|\n\n**Are you satisfied with the code that was gathered?**\n\n[Yes](https://command:python.datascience.gatherquality?yes) [No](https://command:python.datascience.gatherquality?no)' + '# Gathered Notebook\nGathered from ```{0}```\n\n| | |\n|---|---|\n|   |This notebook was generated by the Gather Extension. It requires version 2020.7.94776 (or newer) of the Python Extension, please update [here](https://command:python.datascience.latestExtension). The intent is that it contains only the code and cells required to produce the same results as the cell originally selected for gathering. Please note that the Python analysis is quite conservative, so if it is unsure whether a line of code is necessary for execution, it will err on the side of including it.|\n\n**Are you satisfied with the code that was gathered?**\n\n[Yes](https://command:python.datascience.gatherquality?yes) [No](https://command:python.datascience.gatherquality?no)' ); export const savePngTitle = localize('DataScience.savePngTitle', 'Save Image'); export const fallbackToUseActiveInterpeterAsKernel = localize( diff --git a/src/client/datascience/commands/commandRegistry.ts b/src/client/datascience/commands/commandRegistry.ts index d474d5175ec4..5bf2ded5f2f9 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -78,6 +78,7 @@ export class CommandRegistry implements IDisposable { this.registerCommand(Commands.CreateNewNotebook, this.createNewNotebook); this.registerCommand(Commands.ViewJupyterOutput, this.viewJupyterOutput); this.registerCommand(Commands.GatherQuality, this.reportGatherQuality); + this.registerCommand(Commands.LatestExtension, this.openPythonExtensionPage); this.registerCommand( Commands.EnableLoadingWidgetsFrom3rdPartySource, this.enableLoadingWidgetScriptsFromThirdParty @@ -388,7 +389,11 @@ export class CommandRegistry implements IDisposable { } private reportGatherQuality(val: string) { - sendTelemetryEvent(Telemetry.GatherQualityReport, undefined, { result: val === 'no' ? 'no' : 'yes' }); - env.openExternal(Uri.parse(`https://aka.ms/gathersurvey?succeed=${val}`)); + sendTelemetryEvent(Telemetry.GatherQualityReport, undefined, { result: val[0] === 'no' ? 'no' : 'yes' }); + env.openExternal(Uri.parse(`https://aka.ms/gathersurvey?succeed=${val[0]}`)); + } + + private openPythonExtensionPage() { + env.openExternal(Uri.parse(`https://marketplace.visualstudio.com/items?itemName=ms-python.python`)); } } diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 03b38aaec1bf..43b550f1db4c 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -90,6 +90,7 @@ export namespace Commands { export const SaveAsNotebookNonCustomEditor = 'python.datascience.notebookeditor.saveAs'; export const OpenNotebookNonCustomEditor = 'python.datascience.notebookeditor.open'; export const GatherQuality = 'python.datascience.gatherquality'; + export const LatestExtension = 'python.datascience.latestExtension'; export const TrustNotebook = 'python.datascience.notebookeditor.trust'; export const EnableLoadingWidgetsFrom3rdPartySource = 'python.datascience.enableLoadingWidgetScriptsFromThirdPartySource'; @@ -320,6 +321,7 @@ export enum Telemetry { KernelInvalid = 'DS_INTERNAL.INVALID_KERNEL_USED', GatherIsInstalled = 'DS_INTERNAL.GATHER_IS_INSTALLED', GatherCompleted = 'DATASCIENCE.GATHER_COMPLETED', + GatherStats = 'DS_INTERNAL.GATHER_STATS', GatheredNotebookSaved = 'DATASCIENCE.GATHERED_NOTEBOOK_SAVED', GatherQualityReport = 'DS_INTERNAL.GATHER_QUALITY_REPORT', ZMQSupported = 'DS_INTERNAL.ZMQ_NATIVE_BINARIES_LOADING', diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index 470e7ef477a3..fc97d0f6097f 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -42,6 +42,8 @@ export class GatherListener implements IInteractiveWindowListener { private notebookUri: Uri | undefined; private gatherProvider: IGatherProvider | undefined; private gatherTimer: StopWatch | undefined; + private linesSubmitted: number = 0; + private cellsSubmitted: number = 0; constructor( @inject(IApplicationShell) private applicationShell: IApplicationShell, @@ -78,11 +80,19 @@ export class GatherListener implements IInteractiveWindowListener { break; case InteractiveWindowMessages.RestartKernel: + this.linesSubmitted = 0; + this.cellsSubmitted = 0; if (this.gatherProvider) { this.gatherProvider.resetLog(); } break; + case InteractiveWindowMessages.FinishCell: + const lineCount: number = payload.cell.data.source.length as number; + this.linesSubmitted += lineCount; + this.cellsSubmitted += 1; + break; + default: break; } @@ -155,6 +165,13 @@ export class GatherListener implements IInteractiveWindowListener { await this.showNotebook(slicedProgram, cell); sendTelemetryEvent(Telemetry.GatherCompleted, this.gatherTimer?.elapsedTime, { result: 'notebook' }); } + + sendTelemetryEvent(Telemetry.GatherStats, undefined, { + linesSubmitted: this.linesSubmitted, + cellsSubmitted: this.cellsSubmitted, + linesGathered: slicedProgram.splitLines().length, + cellsGathered: generateCellsFromString(slicedProgram).length + }); } }; diff --git a/src/client/datascience/interactive-common/linkProvider.ts b/src/client/datascience/interactive-common/linkProvider.ts index ea6461b66114..b8096ad616a6 100644 --- a/src/client/datascience/interactive-common/linkProvider.ts +++ b/src/client/datascience/interactive-common/linkProvider.ts @@ -19,6 +19,7 @@ const LineQueryRegex = /line=(\d+)/; // in a markdown cell using the syntax: https://command:[my.vscode.command]. const linkCommandWhitelist = [ 'python.datascience.gatherquality', + 'python.datascience.latestExtension', 'python.datascience.enableLoadingWidgetScriptsFromThirdPartySource' ]; @@ -52,8 +53,7 @@ export class LinkProvider implements IInteractiveWindowListener { this.openFile(href); } else if (href.startsWith('https://command:')) { const temp: string = href.split(':')[2]; - const params: string[] = - temp.includes('/?') && temp.includes(',') ? temp.split('/?')[1].split(',') : []; + const params: string[] = temp.includes('/?') ? temp.split('/?')[1].split(',') : []; let command = temp.split('/?')[0]; if (command.endsWith('/')) { command = command.substring(0, command.length - 1); diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index ff26bcf385b2..6be34d0f427e 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -2041,6 +2041,12 @@ export interface IEventNamePropertyMapping { */ result: 'err' | 'script' | 'notebook' | 'unavailable'; }; + [Telemetry.GatherStats]: { + linesSubmitted: number; + cellsSubmitted: number; + linesGathered: number; + cellsGathered: number; + }; /** * Telemetry event sent when a gathered notebook has been saved by the user. */ From 6f643ce057a5622a38330fc6ac93ede175574a69 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Wed, 22 Jul 2020 15:43:02 -0700 Subject: [PATCH 2/3] fix tests --- src/client/datascience/gather/gatherListener.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index fc97d0f6097f..227902950606 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -88,9 +88,11 @@ export class GatherListener implements IInteractiveWindowListener { break; case InteractiveWindowMessages.FinishCell: - const lineCount: number = payload.cell.data.source.length as number; - this.linesSubmitted += lineCount; - this.cellsSubmitted += 1; + if (payload.cell) { + const lineCount: number = payload.cell.data.source.length as number; + this.linesSubmitted += lineCount; + this.cellsSubmitted += 1; + } break; default: From 0210bd800693aa42ccb40b81a66667543a894790 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Wed, 22 Jul 2020 16:04:30 -0700 Subject: [PATCH 3/3] fix tests for real --- src/client/datascience/gather/gatherListener.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index 227902950606..95b6215b5e31 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -88,8 +88,9 @@ export class GatherListener implements IInteractiveWindowListener { break; case InteractiveWindowMessages.FinishCell: - if (payload.cell) { - const lineCount: number = payload.cell.data.source.length as number; + const cell = payload as ICell; + if (cell && cell.data && cell.data.source) { + const lineCount: number = cell.data.source.length as number; this.linesSubmitted += lineCount; this.cellsSubmitted += 1; }