Skip to content
Draft
98 changes: 89 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,12 @@
"description": "Disable SSL certificate verification (for development only)",
"scope": "application"
},
"deepnote.telemetry.enabled": {
"type": "boolean",
"default": true,
"description": "Enable anonymous usage telemetry to help improve Deepnote for VS Code.",
"scope": "application"
},
Comment thread
tkislan marked this conversation as resolved.
"deepnote.snapshots.enabled": {
"type": "boolean",
"default": true,
Expand Down Expand Up @@ -2725,6 +2731,7 @@
"pidtree": "^0.6.0",
"plotly.js-dist": "^3.0.1",
"portfinder": "^1.0.25",
"posthog-node": "^4.18.0",
"re-resizable": "^6.5.5",
"react": "^16.5.2",
"react-data-grid": "^6.0.2-0",
Expand Down
8 changes: 8 additions & 0 deletions src/extension.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import './platform/logging';
import { commands, env, ExtensionMode, UIKind, workspace, type OutputChannel } from 'vscode';
import { buildApi, IExtensionApi } from './standalone/api';
import { logger, setHomeDirectory } from './platform/logging';
import { IPostHogAnalyticsService } from './platform/analytics/types';
import { IAsyncDisposableRegistry, IExtensionContext, IsDevMode } from './platform/common/types';
import { IServiceContainer, IServiceManager } from './platform/ioc/types';
import { sendStartupTelemetry } from './platform/telemetry/startupTelemetry';
Expand Down Expand Up @@ -133,7 +134,14 @@ export function deactivate(): Thenable<void> {
Exiting.isExiting = true;
// Make sure to shutdown anybody who needs it.
if (activatedServiceContainer) {
const analytics = activatedServiceContainer.tryGet<IPostHogAnalyticsService>(IPostHogAnalyticsService);

if (analytics) {
void analytics.shutdown();
}
Comment thread
tkislan marked this conversation as resolved.
Outdated

const registry = activatedServiceContainer.get<IAsyncDisposableRegistry>(IAsyncDisposableRegistry);

if (registry) {
return registry.dispose();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { inject, injectable, named } from 'inversify';
import { inject, injectable, named, optional } from 'inversify';
import {
commands,
Disposable,
Expand All @@ -13,6 +13,7 @@ import {
import { IPythonApiProvider } from '../../../platform/api/types';
import { STANDARD_OUTPUT_CHANNEL } from '../../../platform/common/constants';
import { getDisplayPath } from '../../../platform/common/platform/fs-paths.node';
import { IPostHogAnalyticsService } from '../../../platform/analytics/types';
import { IDisposableRegistry, IOutputChannel } from '../../../platform/common/types';
import { createDeepnoteServerConfigHandle } from '../../../platform/deepnote/deepnoteServerUtils.node';
import { DeepnoteToolkitMissingError } from '../../../platform/errors/deepnoteKernelErrors';
Expand Down Expand Up @@ -52,7 +53,8 @@ export class DeepnoteEnvironmentsView implements Disposable {
@inject(IDeepnoteNotebookEnvironmentMapper)
private readonly notebookEnvironmentMapper: IDeepnoteNotebookEnvironmentMapper,
@inject(IKernelProvider) private readonly kernelProvider: IKernelProvider,
@inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private readonly outputChannel: IOutputChannel
@inject(IOutputChannel) @named(STANDARD_OUTPUT_CHANNEL) private readonly outputChannel: IOutputChannel,
@inject(IPostHogAnalyticsService) @optional() private readonly analytics: IPostHogAnalyticsService | undefined
) {
// Create tree data provider

Expand Down Expand Up @@ -193,6 +195,11 @@ export class DeepnoteEnvironmentsView implements Disposable {
const config = await this.environmentManager.createEnvironment(options, token);
logger.info(`Created environment: ${config.id} (${config.name})`);

this.analytics?.trackEvent('create_environment', {
hasDescription: !!options.description,
hasPackages: !!options.packages?.length
});

void window.showInformationMessage(
l10n.t('Environment "{0}" created successfully!', config.name)
);
Expand Down Expand Up @@ -314,6 +321,7 @@ export class DeepnoteEnvironmentsView implements Disposable {
}
);

this.analytics?.trackEvent('delete_environment');
void window.showInformationMessage(l10n.t('Environment "{0}" deleted', config.name));
} catch (error) {
logger.error('Failed to delete environment', error);
Expand Down Expand Up @@ -483,6 +491,7 @@ export class DeepnoteEnvironmentsView implements Disposable {
}
);

this.analytics?.trackEvent('select_environment');
void window.showInformationMessage(l10n.t('Environment switched successfully'));
} catch (error) {
if (error instanceof DeepnoteToolkitMissingError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ suite('DeepnoteEnvironmentsView', () => {
instance(mockKernelAutoSelector),
instance(mockNotebookEnvironmentMapper),
instance(mockKernelProvider),
instance(mockOutputChannel)
instance(mockOutputChannel),
undefined
);
Comment thread
tkislan marked this conversation as resolved.
});

Expand Down
11 changes: 9 additions & 2 deletions src/notebooks/deepnote/deepnoteActivationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { inject, injectable, optional } from 'inversify';
import { commands, l10n, workspace, window, type Disposable, type NotebookDocumentContentOptions } from 'vscode';

import { IExtensionSyncActivationService } from '../../platform/activation/types';
import { IPostHogAnalyticsService } from '../../platform/analytics/types';
import { IExtensionContext } from '../../platform/common/types';
import { ILogger } from '../../platform/logging/types';
import { IDeepnoteNotebookManager } from '../types';
Expand Down Expand Up @@ -34,7 +35,8 @@ export class DeepnoteActivationService implements IExtensionSyncActivationServic
@inject(IDeepnoteNotebookManager) private readonly notebookManager: IDeepnoteNotebookManager,
@inject(IIntegrationManager) integrationManager: IIntegrationManager,
@inject(ILogger) private readonly logger: ILogger,
@inject(SnapshotService) @optional() private readonly snapshotService?: SnapshotService
@inject(SnapshotService) @optional() private readonly snapshotService?: SnapshotService,
@inject(IPostHogAnalyticsService) @optional() private readonly analytics?: IPostHogAnalyticsService
) {
this.integrationManager = integrationManager;
}
Expand All @@ -45,7 +47,12 @@ export class DeepnoteActivationService implements IExtensionSyncActivationServic
*/
public activate() {
this.serializer = new DeepnoteNotebookSerializer(this.notebookManager, this.snapshotService);
this.explorerView = new DeepnoteExplorerView(this.extensionContext, this.notebookManager, this.logger);
this.explorerView = new DeepnoteExplorerView(
this.extensionContext,
this.notebookManager,
this.logger,
this.analytics
);
this.editProtection = new DeepnoteInputBlockEditProtection(this.logger);
this.snapshotsEnabled = this.isSnapshotsEnabled();

Expand Down
63 changes: 63 additions & 0 deletions src/notebooks/deepnote/deepnoteCellExecutionAnalytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { inject, injectable, optional } from 'inversify';
import { Disposable } from 'vscode';

import { IExtensionSyncActivationService } from '../../platform/activation/types';
import { IPostHogAnalyticsService } from '../../platform/analytics/types';
import { IDisposableRegistry } from '../../platform/common/types';
import { NotebookCellExecutionState, notebookCellExecutions } from '../../platform/notebooks/cellExecutionStateService';
import { IDeepnoteNotebookManager } from '../types';

/**
* Tracks cell execution events for PostHog analytics.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
*/
@injectable()
export class DeepnoteCellExecutionAnalytics implements IExtensionSyncActivationService {
constructor(
@inject(IPostHogAnalyticsService) @optional() private readonly analytics: IPostHogAnalyticsService | undefined,
@inject(IDeepnoteNotebookManager) private readonly notebookManager: IDeepnoteNotebookManager,
@inject(IDisposableRegistry) private readonly disposables: Disposable[]
) {}
Comment thread
tkislan marked this conversation as resolved.

public activate(): void {
if (!this.analytics) {
return;
}

this.disposables.push(
notebookCellExecutions.onDidChangeNotebookCellExecutionState((e) => {
if (e.state !== NotebookCellExecutionState.Executing) {
return;
}

if (e.cell.notebook.notebookType !== 'deepnote') {
return;
}

const languageId = e.cell.document.languageId;
const cellType = languageId === 'sql' ? 'sql' : languageId === 'markdown' ? 'markdown' : 'code';

const properties: Record<string, string> = { cellType };

if (cellType === 'sql') {
const integrationId =
e.cell.metadata?.__deepnotePocket?.sql_integration_id ?? e.cell.metadata?.sql_integration_id;

if (integrationId) {
const projectId = e.cell.notebook.metadata?.deepnoteProjectId;

if (projectId) {
const project = this.notebookManager.getOriginalProject(projectId);
const integration = project?.project.integrations?.find((i) => i.id === integrationId);

if (integration?.type) {
properties.integrationType = integration.type;
}
}
}
}

this.analytics?.trackEvent('execute_cell', properties);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
})
);
}
}
Loading
Loading