11import { getDefaultEnvManagerSetting , getDefaultPkgManagerSetting } from '../../features/settings/settingHelpers' ;
22import { EnvironmentManagers , PythonProjectManager } from '../../internal.api' ;
3+ import { getUvEnvironments } from '../../managers/builtin/uvEnvironments' ;
4+ import { ISSUES_URL } from '../constants' ;
5+ import { traceInfo , traceVerbose , traceWarn } from '../logging' ;
36import { getWorkspaceFolders } from '../workspace.apis' ;
47import { EventNames } from './constants' ;
58import { sendTelemetryEvent } from './sender' ;
69
10+ /**
11+ * Extracts the base tool name from a manager ID.
12+ * Example: 'ms-python.python:venv' -> 'venv'
13+ * Example: 'ms-python.python:conda' -> 'conda'
14+ */
15+ function extractToolName ( managerId : string ) : string {
16+ // Manager IDs follow the pattern 'extensionId:toolName'
17+ const parts = managerId . split ( ':' ) ;
18+ return parts . length > 1 ? parts [ 1 ] . toLowerCase ( ) : managerId . toLowerCase ( ) ;
19+ }
20+
721export function sendManagerSelectionTelemetry ( pm : PythonProjectManager ) {
822 const ems : Set < string > = new Set ( ) ;
923 const ps : Set < string > = new Set ( ) ;
@@ -58,7 +72,7 @@ export async function sendProjectStructureTelemetry(
5872 for ( const wsFolder of workspaceFolders ) {
5973 const workspacePath = wsFolder . uri . fsPath ;
6074 const projectPath = project . uri . fsPath ;
61-
75+
6276 // Check if project is a subdirectory of workspace folder:
6377 // - Path must start with workspace path
6478 // - Path must not be equal to workspace path
@@ -80,3 +94,98 @@ export async function sendProjectStructureTelemetry(
8094 projectUnderRoot,
8195 } ) ;
8296}
97+
98+ /**
99+ * Sends telemetry about which environment tools are actively used across all projects.
100+ * This tracks ACTUAL USAGE (which environments are set for projects), not just what's installed.
101+ *
102+ * Fires one event per tool that has at least one project using it.
103+ * This allows simple deduplication: dcount(machineId) by toolName gives unique users per tool.
104+ *
105+ * Called once at extension activation to understand user's environment tool usage patterns.
106+ */
107+ export async function sendEnvironmentToolUsageTelemetry (
108+ pm : PythonProjectManager ,
109+ envManagers : EnvironmentManagers ,
110+ ) : Promise < void > {
111+ try {
112+ const projects = pm . getProjects ( ) ;
113+
114+ // Track which tools are used (Set ensures uniqueness)
115+ const toolsUsed = new Set < string > ( ) ;
116+
117+ // Lazily loaded once when a venv environment is first encountered
118+ let uvEnvPaths : string [ ] | undefined ;
119+
120+ // Check which environment manager is used for each project
121+ for ( const project of projects ) {
122+ try {
123+ const env = await envManagers . getEnvironment ( project . uri ) ;
124+ if ( env ?. envId ?. managerId ) {
125+ let toolName = extractToolName ( env . envId . managerId ) ;
126+
127+ // UV environments share the venv manager. Check the persistent UV env list instead
128+ if ( toolName === 'venv' && env . environmentPath ) {
129+ uvEnvPaths ??= await getUvEnvironments ( ) ;
130+ if ( uvEnvPaths . includes ( env . environmentPath . fsPath ) ) {
131+ toolName = 'uv' ;
132+ }
133+ }
134+
135+ // Normalize 'global' to 'system' for consistency
136+ if ( toolName === 'global' ) {
137+ toolName = 'system' ;
138+ }
139+
140+ toolsUsed . add ( toolName ) ;
141+ }
142+ } catch {
143+ // Ignore errors when getting environment for a project
144+ }
145+ }
146+
147+ // Fire one event per tool used
148+ toolsUsed . forEach ( ( tool ) => {
149+ sendTelemetryEvent ( EventNames . ENVIRONMENT_TOOL_USAGE , undefined , {
150+ toolName : tool ,
151+ } ) ;
152+ } ) ;
153+ } catch ( error ) {
154+ // Telemetry failures must never disrupt extension activation
155+ traceVerbose ( 'Failed to send environment tool usage telemetry:' , error ) ;
156+ }
157+ }
158+
159+ /**
160+ * Logs a summary of environment discovery results after startup.
161+ * If no environments are found, logs guidance to help users troubleshoot.
162+ */
163+ export async function logDiscoverySummary ( envManagers : EnvironmentManagers ) : Promise < void > {
164+ const managers = envManagers . managers ;
165+ let totalEnvCount = 0 ;
166+ const managerSummaries : string [ ] = [ ] ;
167+
168+ for ( const manager of managers ) {
169+ try {
170+ const envs = await manager . getEnvironments ( 'all' ) ;
171+ totalEnvCount += envs . length ;
172+ if ( envs . length > 0 ) {
173+ managerSummaries . push ( `${ manager . displayName } : ${ envs . length } ` ) ;
174+ }
175+ } catch {
176+ // Discovery errors are already logged by InternalEnvironmentManager.refresh()
177+ }
178+ }
179+
180+ if ( totalEnvCount === 0 ) {
181+ traceWarn (
182+ `No Python environments were found. ` +
183+ `Try running "Python Environments: Run Python Environment Tool (PET) in Terminal..." from the Command Palette to diagnose. ` +
184+ `If environments should be detected, please report this: ${ ISSUES_URL } /new` ,
185+ ) ;
186+ } else {
187+ traceInfo (
188+ `Environment discovery complete: ${ totalEnvCount } environments found (${ managerSummaries . join ( ', ' ) } )` ,
189+ ) ;
190+ }
191+ }
0 commit comments