From 4468ddb8ab9c636b114695d72257f90865672780 Mon Sep 17 00:00:00 2001 From: Divyanshu Agrawal Date: Fri, 24 Nov 2023 15:10:55 +0530 Subject: [PATCH] Add @vscode/extension-telemetry --- README.md | 14 +++- package-lock.json | 131 +++++++++++++++++++++++++++++++- package.json | 5 +- src/companion.ts | 22 ++++-- src/compiler.ts | 2 + src/config.ts | 1 + src/executions.ts | 2 + src/extension.ts | 10 ++- src/runTestCases.ts | 3 + src/submit.ts | 2 + src/telmetry.ts | 13 ++++ src/types.ts | 8 ++ src/utils.ts | 2 + src/webview/JudgeView.ts | 2 + src/webview/processRunSingle.ts | 2 + tsconfig.json | 3 + 16 files changed, 204 insertions(+), 18 deletions(-) create mode 100644 src/telmetry.ts diff --git a/README.md b/README.md index 6a3d5ab..99e0c08 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![Downloads](https://img.shields.io/visual-studio-marketplace/d/DivyanshuAgrawal.competitive-programming-helper)](https://marketplace.visualstudio.com/items?itemName=DivyanshuAgrawal.competitive-programming-helper) Quickly compile, run and judge competitive programming problems in VS Code. -Automatically download testcases , or write & test your own problems. -Once you are done, easily your solutions directly with the click of a button! +Automatically download testcases , or write & test your own problems. Once you +are done, easily your solutions directly with the click of a button! -Cph supports a large number of popular platforms like Codeforces, Codechef, TopCoder etc. -with the help of competitive companion browser extension +Cph supports a large number of popular platforms like Codeforces, Codechef, +TopCoder etc. with the help of competitive companion browser extension ![Screenshot](screenshots/screenshot-main.png) @@ -66,6 +66,12 @@ You can contribute to this extension in many ways: **Before creating a Pull Request, please create an issue to discuss the approach. It makes reviewing and accepting the PR much easier.** +## Telemetry + +The extension collects basic events defined in `src/telmetry.ts`. To disable, +modify the setting `telemetry.telemetryLevel` (applies to all VSCode +extensions). + ## License Copyright (C) 2023 Divyanshu Agrawal diff --git a/package-lock.json b/package-lock.json index b315dff..f40f922 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "5.16.0", "license": "GPL-3.0-or-later", "dependencies": { + "@vscode/extension-telemetry": "^0.9.0", "python-shell": "^5.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -1264,6 +1265,120 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@microsoft/1ds-core-js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.4.tgz", + "integrity": "sha512-QOCE0fTDOMNptB791chnVlfnRvb7faDQTaUIO3DfPBkvjF3PUAJJCsqJKWitw7nwVn8L82TFx+K22UifIr0zkg==", + "dependencies": { + "@microsoft/applicationinsights-core-js": "3.0.5", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.2", + "@nevware21/ts-async": ">= 0.3.0 < 2.x", + "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + } + }, + "node_modules/@microsoft/1ds-post-js": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.4.tgz", + "integrity": "sha512-jlPNL16iRXzmXfriGXv0INzrAl3AeDx+eCORjq8ZjRhIvohB6Q88m5E28nL6Drf5hJWE2ehoW4q8Vh612VoEHw==", + "dependencies": { + "@microsoft/1ds-core-js": "4.0.4", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.2", + "@nevware21/ts-async": ">= 0.3.0 < 2.x", + "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + } + }, + "node_modules/@microsoft/applicationinsights-channel-js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.5.tgz", + "integrity": "sha512-KfTYY0uZmrQgrz8ErBh1q08eiYfzjUIVzJZHETgEkqv3l2RTndQgpmywDbVNf9wVTB7Mp89ZrFeCciVJFf5geg==", + "dependencies": { + "@microsoft/applicationinsights-common": "3.0.5", + "@microsoft/applicationinsights-core-js": "3.0.5", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.2", + "@nevware21/ts-async": ">= 0.3.0 < 2.x", + "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + }, + "peerDependencies": { + "tslib": "*" + } + }, + "node_modules/@microsoft/applicationinsights-common": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.5.tgz", + "integrity": "sha512-ahph1fMqyLcZ1twzDKMzpHRgR9zEIyqNhMQxDgQ45ieVD641bZiYVwSlbntSXhGCtr5G5HE02zlEzwSxbx95ng==", + "dependencies": { + "@microsoft/applicationinsights-core-js": "3.0.5", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.2", + "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + }, + "peerDependencies": { + "tslib": "*" + } + }, + "node_modules/@microsoft/applicationinsights-core-js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.5.tgz", + "integrity": "sha512-/x+tkxsVALNWSvwGMyaLwFPdD3p156Pef9WHftXrzrKkJ+685nhrwm9MqHIyEHHpSW09ElOdpJ3rfFVqpKRQyQ==", + "dependencies": { + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.2", + "@nevware21/ts-async": ">= 0.3.0 < 2.x", + "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + }, + "peerDependencies": { + "tslib": "*" + } + }, + "node_modules/@microsoft/applicationinsights-shims": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", + "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "dependencies": { + "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + } + }, + "node_modules/@microsoft/applicationinsights-web-basic": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.5.tgz", + "integrity": "sha512-ps4wjmF9X80hakYxywlzBdSlDjfToZrz/cHKA/9yarrW3mbZGqjjksNoaFxtyU5BK4lhOvrgu+2+QcDHeEEnOA==", + "dependencies": { + "@microsoft/applicationinsights-channel-js": "3.0.5", + "@microsoft/applicationinsights-common": "3.0.5", + "@microsoft/applicationinsights-core-js": "3.0.5", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.2", + "@nevware21/ts-async": ">= 0.3.0 < 2.x", + "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + }, + "peerDependencies": { + "tslib": "*" + } + }, + "node_modules/@microsoft/dynamicproto-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", + "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "dependencies": { + "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + } + }, + "node_modules/@nevware21/ts-async": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", + "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "dependencies": { + "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + } + }, + "node_modules/@nevware21/ts-utils": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", + "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1734,6 +1849,19 @@ "integrity": "sha512-7iiKdA5wHVYSbO7/Mm0hiHD3i4h+9hKUe1O4hISAe/nHhagMwb2ZbFC8jU6d7Cw+JNT2dWXN2j+WHbkhT5/l2w==", "dev": true }, + "node_modules/@vscode/extension-telemetry": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", + "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "dependencies": { + "@microsoft/1ds-core-js": "^4.0.3", + "@microsoft/1ds-post-js": "^4.0.3", + "@microsoft/applicationinsights-web-basic": "^3.0.4" + }, + "engines": { + "vscode": "^1.75.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -7013,8 +7141,7 @@ "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/type-check": { "version": "0.4.0", diff --git a/package.json b/package.json index d3db45f..23e2ed6 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "GPL-3.0-or-later", "icon": "icon.png", "publisher": "DivyanshuAgrawal", - "version": "5.16.0", + "version": "6.0.0", "engines": { "vscode": "^1.52.0" }, @@ -318,7 +318,8 @@ "python-shell": "^5.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-textarea-autosize": "^8.5.3" + "react-textarea-autosize": "^8.5.3", + "@vscode/extension-telemetry": "^0.9.0" }, "repository": { "type": "git", diff --git a/src/companion.ts b/src/companion.ts index 5c39b25..ebf4f76 100644 --- a/src/companion.ts +++ b/src/companion.ts @@ -17,12 +17,14 @@ import { getProblemName } from './submit'; import { spawn } from 'child_process'; import { getJudgeViewProvider } from './extension'; import { words_in_text } from './utilsPure'; +import telmetry from './telmetry'; const emptyResponse: CphEmptyResponse = { empty: true }; let savedResponse: CphEmptyResponse | CphSubmitResponse = emptyResponse; const COMPANION_LOGGING = false; export const submitKattisProblem = (problem: Problem) => { + globalThis.reporter.sendTelemetryEvent(telmetry.SUBMIT_TO_KATTIS); const srcPath = problem.srcPath; const homedir = require('os').homedir(); let submitPath = `${homedir}/.kattis/submit.py`; @@ -85,7 +87,7 @@ export const storeSubmitProblem = (problem: Problem) => { sourceCode, languageId, }; - + globalThis.reporter.sendTelemetryEvent(telmetry.SUBMIT_TO_CODEFORCES); console.log('Stored savedResponse', savedResponse); }; @@ -103,10 +105,14 @@ export const setupCompanionServer = () => { } }); req.on('close', function () { - const problem: Problem = JSON.parse(rawProblem); - handleNewProblem(problem); - COMPANION_LOGGING && - console.log('Companion server closed connection.'); + try { + const problem: Problem = JSON.parse(rawProblem); + handleNewProblem(problem); + COMPANION_LOGGING && + console.log('Companion server closed connection.'); + } catch (e) { + // Ignore + } }); res.write(JSON.stringify(savedResponse)); if (headers['cph-submit'] == 'true') { @@ -158,6 +164,7 @@ export const getProblemFileName = (problem: Problem, ext: string) => { /** Handle the `problem` sent by Competitive Companion, such as showing the webview, opening an editor, managing layout etc. */ const handleNewProblem = async (problem: Problem) => { + globalThis.reporter.sendTelemetryEvent(telmetry.GET_PROBLEM_FROM_COMPANION); // If webview may be focused, close it, to prevent layout bug. if (vscode.window.activeTextEditor == undefined) { getJudgeViewProvider().extensionToJudgeViewMessage({ @@ -225,9 +232,8 @@ const handleNewProblem = async (problem: Problem) => { `Template file does not exist: ${templateLocation}`, ); } else { - const templateContents = readFileSync( - templateLocation, - ).toString(); + const templateContents = + readFileSync(templateLocation).toString(); writeFileSync(srcPath, templateContents); } } diff --git a/src/compiler.ts b/src/compiler.ts index 22c4336..b7c20ba 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -8,10 +8,12 @@ import { } from './preferences'; import * as vscode from 'vscode'; import { getJudgeViewProvider } from './extension'; +import telmetry from './telmetry'; export let onlineJudgeEnv = false; export const setOnlineJudgeEnv = (value: boolean) => { onlineJudgeEnv = value; + globalThis.reporter.sendTelemetryEvent(telmetry.ONLINE_JUDGE_ENV); console.log('online judge env:', onlineJudgeEnv); }; diff --git a/src/config.ts b/src/config.ts index bf82478..8cd8df4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,6 +4,7 @@ */ export default { + telemetryKey: 'e57deb32-2bd3-4c02-89a5-d1f1ed0bc0d6', port: 27121, // companion listener server timeout: 10000, // for a testcase run extensions: { diff --git a/src/executions.ts b/src/executions.ts index 7e3da96..6068ca2 100644 --- a/src/executions.ts +++ b/src/executions.ts @@ -6,6 +6,7 @@ import { getTimeOutPref } from './preferences'; import * as vscode from 'vscode'; import path from 'path'; import { onlineJudgeEnv } from './compiler'; +import telmetry from './telmetry'; const runningBinaries: ChildProcessWithoutNullStreams[] = []; @@ -160,6 +161,7 @@ export const deleteBinary = (language: Language, binPath: string) => { /** Kill all running binaries. Usually, only one should be running at a time. */ export const killRunning = () => { + globalThis.reporter.sendTelemetryEvent(telmetry.KILL_RUNNING); console.log('Killling binaries'); runningBinaries.forEach((process) => process.kill()); }; diff --git a/src/extension.ts b/src/extension.ts index 7752d1f..aa8fbb4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,6 +9,9 @@ import { import { submitToCodeForces, submitToKattis } from './submit'; import JudgeViewProvider from './webview/JudgeView'; import { getRetainWebviewContextPref } from './preferences'; +import TelemetryReporter from '@vscode/extension-telemetry'; +import config from './config'; +import telmetry from './telmetry'; let judgeViewProvider: JudgeViewProvider; @@ -62,13 +65,16 @@ const registerCommands = (context: vscode.ExtensionContext) => { context.subscriptions.push(disposable2); context.subscriptions.push(disposable3); context.subscriptions.push(disposable4); + globalThis.reporter = new TelemetryReporter(config.telemetryKey); + context.subscriptions.push(globalThis.reporter); + + globalThis.reporter.sendTelemetryEvent(telmetry.EXTENSION_ACTIVATED); }; // This method is called when the extension is activated export function activate(context: vscode.ExtensionContext) { console.log('cph: activate() execution started'); - - (globalThis as any).context = context; + globalThis.context = context; const statusBarItem = vscode.window.createStatusBarItem( vscode.StatusBarAlignment.Left, diff --git a/src/runTestCases.ts b/src/runTestCases.ts index b853b0b..3efc716 100644 --- a/src/runTestCases.ts +++ b/src/runTestCases.ts @@ -6,6 +6,7 @@ import { compileFile } from './compiler'; import runAllAndSave from './webview/processRunAll'; import path from 'path'; import { getJudgeViewProvider } from './extension'; +import telmetry from './telmetry'; /** * Execution for the run testcases command. Runs all testcases for the active @@ -14,6 +15,7 @@ import { getJudgeViewProvider } from './extension'; * create an empty testcases file and show it in the results section. */ export default async () => { + globalThis.reporter.sendTelemetryEvent(telmetry.RUN_ALL_TESTCASES); console.log('Running command "runTestCases"'); const editor = vscode.window.activeTextEditor; if (editor === undefined) { @@ -50,6 +52,7 @@ export default async () => { }; const createLocalProblem = async (editor: vscode.TextEditor) => { + globalThis.reporter.sendTelemetryEvent(telmetry.NEW_LOCAL_PROBLEM); console.log('Creating local problem'); const srcPath = editor.document.fileName; if (checkUnsupported(srcPath)) { diff --git a/src/submit.ts b/src/submit.ts index c93c6c3..5527dc6 100644 --- a/src/submit.ts +++ b/src/submit.ts @@ -2,8 +2,10 @@ import { getProblem } from './parser'; import * as vscode from 'vscode'; import { storeSubmitProblem, submitKattisProblem } from './companion'; import { getJudgeViewProvider } from './extension'; +import telmetry from './telmetry'; export const submitToKattis = async () => { + globalThis.reporter.sendTelemetryEvent(telmetry.SUBMIT_TO_KATTIS); const srcPath = vscode.window.activeTextEditor?.document.fileName; if (!srcPath) { vscode.window.showErrorMessage( diff --git a/src/telmetry.ts b/src/telmetry.ts new file mode 100644 index 0000000..8d17767 --- /dev/null +++ b/src/telmetry.ts @@ -0,0 +1,13 @@ +export default { + EXTENSION_ACTIVATED: 'extension-activated', + RUN_TESTCASE: 'run-testcase', + RUN_ALL_TESTCASES: 'run-all-testcases', + SUBMIT_TO_CODEFORCES: 'submit-to-codeforces', + SUBMIT_TO_KATTIS: 'submit-to-kattis', + GET_PROBLEM_FROM_COMPANION: 'get-problem-from-companion', + NEW_LOCAL_PROBLEM: 'new-local-problem', + KILL_RUNNING: 'kill-running', + DELETE_ALL_TESTCASES: 'delete-all-testcases', + ONLINE_JUDGE_ENV: 'online-judge-env', + USE_EXTENSION: 'use-extension', +}; diff --git a/src/types.ts b/src/types.ts index 5552935..77f30a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,6 @@ +import TelemetryReporter from '@vscode/extension-telemetry'; +import * as vscode from 'vscode'; + /** Valid name for a VS Code preference section for the extension */ export type prefSection = | 'general.saveLocation' @@ -203,3 +206,8 @@ export type CphSubmitResponse = { export type WebViewpersistenceState = { ignoreSpaceWarning: boolean; }; + +declare global { + var reporter: TelemetryReporter; + var context: vscode.ExtensionContext; +} diff --git a/src/utils.ts b/src/utils.ts index 15d2738..e5fd021 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -23,6 +23,7 @@ import { getGoCommand, } from './preferences'; import { Language, Problem } from './types'; +import telmetry from './telmetry'; const oc = vscode.window.createOutputChannel('cph'); @@ -149,6 +150,7 @@ export const checkUnsupported = (srcPath: string): boolean => { /** Deletes the .prob problem file for a given source code path. */ export const deleteProblemFile = (srcPath: string) => { + globalThis.reporter.sendTelemetryEvent(telmetry.DELETE_ALL_TESTCASES); const probPath = getProbSaveLocation(srcPath); try { if (platform() === 'win32') { diff --git a/src/webview/JudgeView.ts b/src/webview/JudgeView.ts index 7f56e5f..cd05555 100644 --- a/src/webview/JudgeView.ts +++ b/src/webview/JudgeView.ts @@ -12,6 +12,7 @@ import { getRetainWebviewContextPref, } from '../preferences'; import { setOnlineJudgeEnv } from '../compiler'; +import telmetry from '../telmetry'; class JudgeViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = 'cph.judgeView'; @@ -107,6 +108,7 @@ class JudgeViewProvider implements vscode.WebviewViewProvider { private getInitialProblem() { const doc = vscode.window.activeTextEditor?.document; + globalThis.reporter.sendTelemetryEvent(telmetry.USE_EXTENSION); this.extensionToJudgeViewMessage({ command: 'new-problem', problem: getProblemForDocument(doc), diff --git a/src/webview/processRunSingle.ts b/src/webview/processRunSingle.ts index 017167b..48c1602 100644 --- a/src/webview/processRunSingle.ts +++ b/src/webview/processRunSingle.ts @@ -7,12 +7,14 @@ import { isResultCorrect } from '../judge'; import * as vscode from 'vscode'; import { getJudgeViewProvider } from '../extension'; import { getIgnoreSTDERRORPref } from '../preferences'; +import telmetry from '../telmetry'; export const runSingleAndSave = async ( problem: Problem, id: number, skipCompile = false, ) => { + globalThis.reporter.sendTelemetryEvent(telmetry.RUN_TESTCASE); console.log('Run and save started', problem, id); const srcPath = problem.srcPath; const language = getLanguage(srcPath); diff --git a/tsconfig.json b/tsconfig.json index 64557c2..27182ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,7 @@ { + "files":[ + "src/types.ts", + ], "compilerOptions": { "module": "commonjs", "target": "es2017",