From c3393fb8bf07f8e68ea2469c45b928d9734c44b9 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Tue, 21 Jul 2020 16:47:46 -0700 Subject: [PATCH 1/6] fix the gather survey added 'gather stats' telemetry mention the gather comments to update the python ext --- package.json | 11 +++++++++++ package.nls.json | 15 ++++++++------- 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 | 14 ++++++++++++-- src/client/datascience/gather/gatherLogger.ts | 17 +++++++++++++++-- .../interactive-common/linkProvider.ts | 4 ++-- src/client/telemetry/index.ts | 5 +++++ 10 files changed, 65 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index d02aab2c8da7..0ddbfde0187a 100644 --- a/package.json +++ b/package.json @@ -770,6 +770,11 @@ "command": "python.datascience.gatherquality", "title": "DataScience.gatherQuality", "category": "Python" + }, + { + "command": "python.datascience.latestExtension", + "title": "DataScience.latestExtension", + "category": "Python" } ], "menus": { @@ -1281,6 +1286,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 3de3b7a252e3..fa82bde27db9 100644 --- a/package.nls.json +++ b/package.nls.json @@ -474,8 +474,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 will not work without the lastest version 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 will not work without the lastest version 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", @@ -514,6 +514,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,9 +558,9 @@ "DataScienceRendererExtension.downloadCompletedOutputMessage": "Notebook Renderers extension download complete.", "DataScience.uriProviderDescriptionFormat": "{0} (From {1} extension)", "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" + "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 103e28a59fce..b4d127760c2c 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 7d94f06707ab..605aa5912d94 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -886,11 +886,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 will not work without the lastest version 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 will not work without the lastest version 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 aa279097fb4b..2d1eb1a79650 100644 --- a/src/client/datascience/commands/commandRegistry.ts +++ b/src/client/datascience/commands/commandRegistry.ts @@ -80,6 +80,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 @@ -394,7 +395,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 76b014f2a6d8..fb94d0eac9ee 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..e98d777b7ef0 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -7,7 +7,7 @@ import { IApplicationShell, IDocumentManager } from '../../common/application/ty import { PYTHON_LANGUAGE } from '../../common/constants'; import { traceError } from '../../common/logger'; import { IFileSystem } from '../../common/platform/types'; -import { IConfigurationService, Resource } from '../../common/types'; +import { IConfigurationService, IExtensionContext, Resource } from '../../common/types'; import * as localize from '../../common/utils/localize'; import { noop } from '../../common/utils/misc'; import { StopWatch } from '../../common/utils/stopWatch'; @@ -50,7 +50,8 @@ export class GatherListener implements IInteractiveWindowListener { @inject(INotebookProvider) private notebookProvider: INotebookProvider, @inject(IConfigurationService) private configService: IConfigurationService, @inject(IDocumentManager) private documentManager: IDocumentManager, - @inject(IFileSystem) private fileSystem: IFileSystem + @inject(IFileSystem) private fileSystem: IFileSystem, + @inject(IExtensionContext) private context: IExtensionContext ) {} public dispose() { @@ -80,6 +81,7 @@ export class GatherListener implements IInteractiveWindowListener { case InteractiveWindowMessages.RestartKernel: if (this.gatherProvider) { this.gatherProvider.resetLog(); + this.context.globalState.update('gatherCount', 0); } break; @@ -143,6 +145,8 @@ export class GatherListener implements IInteractiveWindowListener { ? this.gatherProvider.gatherCode(cell) : localize.DataScience.gatherError(); + const linesAvailable: number | undefined = this.context.globalState.get('gatherCount'); + if (!slicedProgram) { sendTelemetryEvent(Telemetry.GatherCompleted, this.gatherTimer?.elapsedTime, { result: 'err' }); } else { @@ -155,6 +159,12 @@ export class GatherListener implements IInteractiveWindowListener { await this.showNotebook(slicedProgram, cell); sendTelemetryEvent(Telemetry.GatherCompleted, this.gatherTimer?.elapsedTime, { result: 'notebook' }); } + + sendTelemetryEvent(Telemetry.GatherStats, undefined, { + linesAvailable: linesAvailable ? linesAvailable : -1, + linesInGatheredCell: cell.data.source.length, + gatheredLines: slicedProgram.splitLines().length + }); } }; diff --git a/src/client/datascience/gather/gatherLogger.ts b/src/client/datascience/gather/gatherLogger.ts index 85260dab9cd1..757e22450bc5 100644 --- a/src/client/datascience/gather/gatherLogger.ts +++ b/src/client/datascience/gather/gatherLogger.ts @@ -3,7 +3,7 @@ import { inject, injectable } from 'inversify'; import cloneDeep = require('lodash/cloneDeep'); import { extensions } from 'vscode'; import { concatMultilineStringInput } from '../../../datascience-ui/common'; -import { IConfigurationService } from '../../common/types'; +import { IConfigurationService, IExtensionContext } from '../../common/types'; import { noop } from '../../common/utils/misc'; import { sendTelemetryEvent } from '../../telemetry'; import { CellMatcher } from '../cellMatcher'; @@ -13,7 +13,10 @@ import { ICell as IVscCell, IGatherLogger, IGatherProvider } from '../types'; @injectable() export class GatherLogger implements IGatherLogger { private gather: IGatherProvider | undefined; - constructor(@inject(IConfigurationService) private configService: IConfigurationService) { + constructor( + @inject(IConfigurationService) private configService: IConfigurationService, + @inject(IExtensionContext) private context: IExtensionContext + ) { this.initGatherExtension().ignoreErrors(); } @@ -42,6 +45,16 @@ export class GatherLogger implements IGatherLogger { cloneCell.data.source = cellMatcher.stripFirstMarker(concatMultilineStringInput(vscCell.data.source)); this.gather.logExecution(cloneCell); + + // We save the amount lines the code had before gathering for telemetry purposes. + let gatherCount: number | undefined = this.context.globalState.get('gatherCount'); + + if (gatherCount) { + gatherCount += vscCell.data.source.length; + this.context.globalState.update('gatherCount', gatherCount); + } else { + this.context.globalState.update('gatherCount', 0); + } } } } 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 9b0071c21894..83b4d4838616 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -2041,6 +2041,11 @@ export interface IEventNamePropertyMapping { */ result: 'err' | 'script' | 'notebook' | 'unavailable'; }; + [Telemetry.GatherStats]: { + linesAvailable: number; // Code lines before executing gather, includes the gathered cell. + linesInGatheredCell: number; // Code lines of the gathered cell. + gatheredLines: number; // Code lines after executing gather, includes the gathered cell. + }; /** * Telemetry event sent when a gathered notebook has been saved by the user. */ From 8aed5826a453c1bea294c466b590e9a0f1a96fc8 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Tue, 21 Jul 2020 16:54:18 -0700 Subject: [PATCH 2/6] oops --- src/client/datascience/gather/gatherListener.ts | 4 ++-- src/client/telemetry/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index e98d777b7ef0..fea6709ff0b8 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -161,9 +161,9 @@ export class GatherListener implements IInteractiveWindowListener { } sendTelemetryEvent(Telemetry.GatherStats, undefined, { - linesAvailable: linesAvailable ? linesAvailable : -1, + linesAvailable: linesAvailable ? linesAvailable - cell.data.source.length : -1, linesInGatheredCell: cell.data.source.length, - gatheredLines: slicedProgram.splitLines().length + gatheredLines: slicedProgram.splitLines().length - cell.data.source.length }); } }; diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 83b4d4838616..2651969fc4dd 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -2042,9 +2042,9 @@ export interface IEventNamePropertyMapping { result: 'err' | 'script' | 'notebook' | 'unavailable'; }; [Telemetry.GatherStats]: { - linesAvailable: number; // Code lines before executing gather, includes the gathered cell. + linesAvailable: number; // Code lines before executing gather, does not include the gathered cell. linesInGatheredCell: number; // Code lines of the gathered cell. - gatheredLines: number; // Code lines after executing gather, includes the gathered cell. + gatheredLines: number; // Code lines after executing gather, does not include the gathered cell. }; /** * Telemetry event sent when a gathered notebook has been saved by the user. From 744ee6876eb21c650b4c0992f33b36c3bcbf32bb Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Wed, 22 Jul 2020 01:34:30 -0700 Subject: [PATCH 3/6] fix tests and address comments --- package.nls.json | 4 +- src/client/common/utils/localize.ts | 4 +- .../datascience/gather/gatherListener.ts | 39 +++++++++++++++---- src/client/datascience/gather/gatherLogger.ts | 27 +++++++++---- src/client/telemetry/index.ts | 7 ++-- 5 files changed, 59 insertions(+), 22 deletions(-) diff --git a/package.nls.json b/package.nls.json index fa82bde27db9..e5b4f3afe8e3 100644 --- a/package.nls.json +++ b/package.nls.json @@ -474,8 +474,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# It will not work without the lastest version 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 will not work without the lastest version 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.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", diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index 605aa5912d94..6ddf5112b3b3 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -886,11 +886,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# It will not work without the lastest version 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' + '# 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. It will not work without the lastest version 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)' + '# 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/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index fea6709ff0b8..1852d0d37e89 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -81,7 +81,11 @@ export class GatherListener implements IInteractiveWindowListener { case InteractiveWindowMessages.RestartKernel: if (this.gatherProvider) { this.gatherProvider.resetLog(); - this.context.globalState.update('gatherCount', 0); + try { + this.context.globalState.update('gatherCount', 0); + } catch (e) { + traceError(e); + } } break; @@ -145,8 +149,6 @@ export class GatherListener implements IInteractiveWindowListener { ? this.gatherProvider.gatherCode(cell) : localize.DataScience.gatherError(); - const linesAvailable: number | undefined = this.context.globalState.get('gatherCount'); - if (!slicedProgram) { sendTelemetryEvent(Telemetry.GatherCompleted, this.gatherTimer?.elapsedTime, { result: 'err' }); } else { @@ -160,11 +162,19 @@ export class GatherListener implements IInteractiveWindowListener { sendTelemetryEvent(Telemetry.GatherCompleted, this.gatherTimer?.elapsedTime, { result: 'notebook' }); } - sendTelemetryEvent(Telemetry.GatherStats, undefined, { - linesAvailable: linesAvailable ? linesAvailable - cell.data.source.length : -1, - linesInGatheredCell: cell.data.source.length, - gatheredLines: slicedProgram.splitLines().length - cell.data.source.length - }); + try { + const linesSubmitted: number | undefined = this.context.globalState.get('gatherLinesCount'); + const cellsSubmitted: number | undefined = this.context.globalState.get('gatherCellsCount'); + + sendTelemetryEvent(Telemetry.GatherStats, undefined, { + linesSubmitted: linesSubmitted ? linesSubmitted : -1, + cellsSubmitted: cellsSubmitted ? cellsSubmitted : -1, + linesGathered: slicedProgram.splitLines().length, + cellsGathered: this.getNumberOfCells(slicedProgram) + }); + } catch (e) { + traceError(e); + } } }; @@ -265,4 +275,17 @@ export class GatherListener implements IInteractiveWindowListener { editBuilder.insert(new Position(editor.document.lineCount, 0), '\n'); }); } + + private getNumberOfCells(program: string): number { + let cellCount = 0; + const settings = this.configService.getSettings(); + const cellMarker = settings.datascience.defaultCellMarker || '# %%'; + const regex = new RegExp(cellMarker, 'gi'); + + while (regex.exec(program)) { + cellCount += 1; + } + + return cellCount; + } } diff --git a/src/client/datascience/gather/gatherLogger.ts b/src/client/datascience/gather/gatherLogger.ts index 757e22450bc5..ef54d111bf86 100644 --- a/src/client/datascience/gather/gatherLogger.ts +++ b/src/client/datascience/gather/gatherLogger.ts @@ -3,6 +3,7 @@ import { inject, injectable } from 'inversify'; import cloneDeep = require('lodash/cloneDeep'); import { extensions } from 'vscode'; import { concatMultilineStringInput } from '../../../datascience-ui/common'; +import { traceError } from '../../common/logger'; import { IConfigurationService, IExtensionContext } from '../../common/types'; import { noop } from '../../common/utils/misc'; import { sendTelemetryEvent } from '../../telemetry'; @@ -46,14 +47,26 @@ export class GatherLogger implements IGatherLogger { this.gather.logExecution(cloneCell); - // We save the amount lines the code had before gathering for telemetry purposes. - let gatherCount: number | undefined = this.context.globalState.get('gatherCount'); + try { + // We save the amount lines and cells the code had before gathering for telemetry purposes. + let gatherLinesCount: number | undefined = this.context.globalState.get('gatherLinesCount'); + let gatherCellsCount: number | undefined = this.context.globalState.get('gatherCellsCount'); - if (gatherCount) { - gatherCount += vscCell.data.source.length; - this.context.globalState.update('gatherCount', gatherCount); - } else { - this.context.globalState.update('gatherCount', 0); + if (gatherLinesCount) { + gatherLinesCount += vscCell.data.source.length; + this.context.globalState.update('gatherLinesCount', gatherLinesCount); + } else { + this.context.globalState.update('gatherLinesCount', 0); + } + + if (gatherCellsCount) { + gatherCellsCount += 1; + this.context.globalState.update('gatherCellsCount', gatherCellsCount); + } else { + this.context.globalState.update('gatherCellsCount', 0); + } + } catch (e) { + traceError(e); } } } diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 2651969fc4dd..516146d6eeb9 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -2042,9 +2042,10 @@ export interface IEventNamePropertyMapping { result: 'err' | 'script' | 'notebook' | 'unavailable'; }; [Telemetry.GatherStats]: { - linesAvailable: number; // Code lines before executing gather, does not include the gathered cell. - linesInGatheredCell: number; // Code lines of the gathered cell. - gatheredLines: number; // Code lines after executing gather, does not include the gathered cell. + linesSubmitted: number; + cellsSubmitted: number; + linesGathered: number; + cellsGathered: number; }; /** * Telemetry event sent when a gathered notebook has been saved by the user. From b9c22cc92c0cd5d39fa178e89bd8dfb40a05ae02 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Wed, 22 Jul 2020 08:56:27 -0700 Subject: [PATCH 4/6] update gather stats when resetting the kernel --- src/client/datascience/gather/gatherListener.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index 1852d0d37e89..a204552bd53a 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -82,7 +82,8 @@ export class GatherListener implements IInteractiveWindowListener { if (this.gatherProvider) { this.gatherProvider.resetLog(); try { - this.context.globalState.update('gatherCount', 0); + this.context.globalState.update('gatherLinesCount', 0); + this.context.globalState.update('gatherCellsCount', 0); } catch (e) { traceError(e); } From f486d67d8307717967f1321939697a1b7195f674 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Wed, 22 Jul 2020 11:02:04 -0700 Subject: [PATCH 5/6] set globalstate vars to 0 when we open vs code --- src/client/datascience/gather/gatherLogger.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/client/datascience/gather/gatherLogger.ts b/src/client/datascience/gather/gatherLogger.ts index ef54d111bf86..48d32c56cc0c 100644 --- a/src/client/datascience/gather/gatherLogger.ts +++ b/src/client/datascience/gather/gatherLogger.ts @@ -19,6 +19,12 @@ export class GatherLogger implements IGatherLogger { @inject(IExtensionContext) private context: IExtensionContext ) { this.initGatherExtension().ignoreErrors(); + try { + this.context.globalState.update('gatherLinesCount', 0); + this.context.globalState.update('gatherCellsCount', 0); + } catch (e) { + traceError(e); + } } public dispose() { From e8a0a9a2744d7aa4fb0f911d3a1208299a9f1409 Mon Sep 17 00:00:00 2001 From: DavidKutu Date: Wed, 22 Jul 2020 13:30:45 -0700 Subject: [PATCH 6/6] fix gather stats telemetry --- .../datascience/gather/gatherListener.ts | 53 +++++++------------ src/client/datascience/gather/gatherLogger.ts | 36 +------------ 2 files changed, 20 insertions(+), 69 deletions(-) diff --git a/src/client/datascience/gather/gatherListener.ts b/src/client/datascience/gather/gatherListener.ts index a204552bd53a..fc97d0f6097f 100644 --- a/src/client/datascience/gather/gatherListener.ts +++ b/src/client/datascience/gather/gatherListener.ts @@ -7,7 +7,7 @@ import { IApplicationShell, IDocumentManager } from '../../common/application/ty import { PYTHON_LANGUAGE } from '../../common/constants'; import { traceError } from '../../common/logger'; import { IFileSystem } from '../../common/platform/types'; -import { IConfigurationService, IExtensionContext, Resource } from '../../common/types'; +import { IConfigurationService, Resource } from '../../common/types'; import * as localize from '../../common/utils/localize'; import { noop } from '../../common/utils/misc'; import { StopWatch } from '../../common/utils/stopWatch'; @@ -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, @@ -50,8 +52,7 @@ export class GatherListener implements IInteractiveWindowListener { @inject(INotebookProvider) private notebookProvider: INotebookProvider, @inject(IConfigurationService) private configService: IConfigurationService, @inject(IDocumentManager) private documentManager: IDocumentManager, - @inject(IFileSystem) private fileSystem: IFileSystem, - @inject(IExtensionContext) private context: IExtensionContext + @inject(IFileSystem) private fileSystem: IFileSystem ) {} public dispose() { @@ -79,17 +80,19 @@ export class GatherListener implements IInteractiveWindowListener { break; case InteractiveWindowMessages.RestartKernel: + this.linesSubmitted = 0; + this.cellsSubmitted = 0; if (this.gatherProvider) { this.gatherProvider.resetLog(); - try { - this.context.globalState.update('gatherLinesCount', 0); - this.context.globalState.update('gatherCellsCount', 0); - } catch (e) { - traceError(e); - } } break; + case InteractiveWindowMessages.FinishCell: + const lineCount: number = payload.cell.data.source.length as number; + this.linesSubmitted += lineCount; + this.cellsSubmitted += 1; + break; + default: break; } @@ -163,19 +166,12 @@ export class GatherListener implements IInteractiveWindowListener { sendTelemetryEvent(Telemetry.GatherCompleted, this.gatherTimer?.elapsedTime, { result: 'notebook' }); } - try { - const linesSubmitted: number | undefined = this.context.globalState.get('gatherLinesCount'); - const cellsSubmitted: number | undefined = this.context.globalState.get('gatherCellsCount'); - - sendTelemetryEvent(Telemetry.GatherStats, undefined, { - linesSubmitted: linesSubmitted ? linesSubmitted : -1, - cellsSubmitted: cellsSubmitted ? cellsSubmitted : -1, - linesGathered: slicedProgram.splitLines().length, - cellsGathered: this.getNumberOfCells(slicedProgram) - }); - } catch (e) { - traceError(e); - } + sendTelemetryEvent(Telemetry.GatherStats, undefined, { + linesSubmitted: this.linesSubmitted, + cellsSubmitted: this.cellsSubmitted, + linesGathered: slicedProgram.splitLines().length, + cellsGathered: generateCellsFromString(slicedProgram).length + }); } }; @@ -276,17 +272,4 @@ export class GatherListener implements IInteractiveWindowListener { editBuilder.insert(new Position(editor.document.lineCount, 0), '\n'); }); } - - private getNumberOfCells(program: string): number { - let cellCount = 0; - const settings = this.configService.getSettings(); - const cellMarker = settings.datascience.defaultCellMarker || '# %%'; - const regex = new RegExp(cellMarker, 'gi'); - - while (regex.exec(program)) { - cellCount += 1; - } - - return cellCount; - } } diff --git a/src/client/datascience/gather/gatherLogger.ts b/src/client/datascience/gather/gatherLogger.ts index 48d32c56cc0c..85260dab9cd1 100644 --- a/src/client/datascience/gather/gatherLogger.ts +++ b/src/client/datascience/gather/gatherLogger.ts @@ -3,8 +3,7 @@ import { inject, injectable } from 'inversify'; import cloneDeep = require('lodash/cloneDeep'); import { extensions } from 'vscode'; import { concatMultilineStringInput } from '../../../datascience-ui/common'; -import { traceError } from '../../common/logger'; -import { IConfigurationService, IExtensionContext } from '../../common/types'; +import { IConfigurationService } from '../../common/types'; import { noop } from '../../common/utils/misc'; import { sendTelemetryEvent } from '../../telemetry'; import { CellMatcher } from '../cellMatcher'; @@ -14,17 +13,8 @@ import { ICell as IVscCell, IGatherLogger, IGatherProvider } from '../types'; @injectable() export class GatherLogger implements IGatherLogger { private gather: IGatherProvider | undefined; - constructor( - @inject(IConfigurationService) private configService: IConfigurationService, - @inject(IExtensionContext) private context: IExtensionContext - ) { + constructor(@inject(IConfigurationService) private configService: IConfigurationService) { this.initGatherExtension().ignoreErrors(); - try { - this.context.globalState.update('gatherLinesCount', 0); - this.context.globalState.update('gatherCellsCount', 0); - } catch (e) { - traceError(e); - } } public dispose() { @@ -52,28 +42,6 @@ export class GatherLogger implements IGatherLogger { cloneCell.data.source = cellMatcher.stripFirstMarker(concatMultilineStringInput(vscCell.data.source)); this.gather.logExecution(cloneCell); - - try { - // We save the amount lines and cells the code had before gathering for telemetry purposes. - let gatherLinesCount: number | undefined = this.context.globalState.get('gatherLinesCount'); - let gatherCellsCount: number | undefined = this.context.globalState.get('gatherCellsCount'); - - if (gatherLinesCount) { - gatherLinesCount += vscCell.data.source.length; - this.context.globalState.update('gatherLinesCount', gatherLinesCount); - } else { - this.context.globalState.update('gatherLinesCount', 0); - } - - if (gatherCellsCount) { - gatherCellsCount += 1; - this.context.globalState.update('gatherCellsCount', gatherCellsCount); - } else { - this.context.globalState.update('gatherCellsCount', 0); - } - } catch (e) { - traceError(e); - } } } }