PY-77813: Report new project type broken in NPW.

FUS statistics consists of two parts:
1. Interpreter (i.e "venv" or "conda")
2. Project generator type ("Django" or "Flask")

`com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector.GENERATOR_FIELD` was a class without any limitation and `DirectoryProjectGenerator` instance was reported (i.e one for Django).

When migrated to NPW, we:
1. Dropped most old generator classes
2. Called this function providing `this::class` by accident, and it was `CoroutineScope`, so we finished with lots of `CoroutineScope` as generator type in FUS.

We must:
1. Provide old names for project types to preserve statistics.
2. Make it type-safe this time.

We also found that interpreter statistics is nullable for `PySdkCreator` which isn't true: SDK creation statistics is always not null.

So we:
* Introduce interface for project generators that reports "name for the statistics"
* Implement it both for DS and PyCharm by returning class name by default
* Overwrite it for several well-known generators to preserve statistics (use old named of now-deleted classes)
* Make interpreter statistics not null.


(cherry picked from commit bdfa73ba043d3584c6ba1871bca7a464a550bc21)

KT-CR-19191

GitOrigin-RevId: 53f874c18d67d33083cf8508a58be257b5e89ab7
This commit is contained in:
Ilya.Kazakevich
2024-12-03 01:05:02 +01:00
committed by intellij-monorepo-bot
parent fa7590530b
commit 5666495862
12 changed files with 127 additions and 41 deletions

View File

@@ -1,15 +1,22 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.pycharm.community.ide.impl.newProjectWizard.impl.emptyProject package com.intellij.pycharm.community.ide.impl.newProjectWizard.impl.emptyProject
import com.intellij.openapi.util.NlsSafe
import com.jetbrains.python.newProjectWizard.PyV3ProjectBaseGenerator import com.jetbrains.python.newProjectWizard.PyV3ProjectBaseGenerator
import com.jetbrains.python.PyBundle import com.jetbrains.python.PyBundle
import com.jetbrains.python.newProjectWizard.collector.PyProjectTypeValidationRule
import com.jetbrains.python.psi.icons.PythonPsiApiIcons import com.jetbrains.python.psi.icons.PythonPsiApiIcons
import org.jetbrains.annotations.ApiStatus.Internal
import org.jetbrains.annotations.Nls import org.jetbrains.annotations.Nls
import javax.swing.Icon import javax.swing.Icon
internal class PyV3EmptyProjectGenerator : PyV3ProjectBaseGenerator<PyV3EmptyProjectSettings>( @Internal
class PyV3EmptyProjectGenerator : PyV3ProjectBaseGenerator<PyV3EmptyProjectSettings>(
PyV3EmptyProjectSettings(generateWelcomeScript = false), PyV3EmptyProjectUI, _newProjectName = "PythonProject") { PyV3EmptyProjectSettings(generateWelcomeScript = false), PyV3EmptyProjectUI, _newProjectName = "PythonProject") {
override fun getName(): @Nls String = PyBundle.message("pure.python.project") override fun getName(): @Nls String = PyBundle.message("pure.python.project")
override fun getLogo(): Icon = PythonPsiApiIcons.Python override fun getLogo(): Icon = PythonPsiApiIcons.Python
override val projectTypeForStatistics: @NlsSafe String = PyProjectTypeValidationRule.EMPTY_PROJECT_TYPE_ID
} }

View File

@@ -158,5 +158,7 @@
<orderEntry type="library" name="kotlinx-datetime-jvm" level="project" /> <orderEntry type="library" name="kotlinx-datetime-jvm" level="project" />
<orderEntry type="module" module-name="intellij.platform.ide.remote" /> <orderEntry type="module" module-name="intellij.platform.ide.remote" />
<orderEntry type="module" module-name="intellij.platform.ide.ui" /> <orderEntry type="module" module-name="intellij.platform.ide.ui" />
<orderEntry type="library" name="jackson-module-kotlin" level="project" />
<orderEntry type="library" name="ap-validation" level="project" />
</component> </component>
</module> </module>

View File

@@ -510,6 +510,7 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.namespacePackages.PyNamespacePackagesStatisticsCollector"/> <statistics.counterUsagesCollector implementationClass="com.jetbrains.python.namespacePackages.PyNamespacePackagesStatisticsCollector"/>
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.codeInsight.codeVision.PyCodeVisionUsageCollector"/> <statistics.counterUsagesCollector implementationClass="com.jetbrains.python.codeInsight.codeVision.PyCodeVisionUsageCollector"/>
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector"/> <statistics.counterUsagesCollector implementationClass="com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector"/>
<statistics.validation.customValidationRule implementation="com.jetbrains.python.newProjectWizard.collector.PyProjectTypeValidationRule"/>
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.sdk.add.collector.PythonNewInterpreterAddedCollector"/> <statistics.counterUsagesCollector implementationClass="com.jetbrains.python.sdk.add.collector.PythonNewInterpreterAddedCollector"/>
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.run.runAnything.PyRunAnythingCollector"/> <statistics.counterUsagesCollector implementationClass="com.jetbrains.python.run.runAnything.PyRunAnythingCollector"/>
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.debugger.statistics.PyDataViewerCollector"/> <statistics.counterUsagesCollector implementationClass="com.jetbrains.python.debugger.statistics.PyDataViewerCollector"/>

View File

@@ -15,6 +15,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.NlsContexts.DialogMessage; import com.intellij.openapi.util.NlsContexts.DialogMessage;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGeneratorBase; import com.intellij.platform.DirectoryProjectGeneratorBase;
@@ -22,6 +23,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyPsiPackageUtil; import com.jetbrains.python.PyPsiPackageUtil;
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo; import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo;
import com.jetbrains.python.newProjectWizard.collector.PyProjectTypeGenerator;
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector; import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector;
import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageManager;
@@ -49,7 +51,8 @@ import java.util.function.Consumer;
* @deprecated Use {@link com.jetbrains.python.newProjectWizard} * @deprecated Use {@link com.jetbrains.python.newProjectWizard}
*/ */
@Deprecated @Deprecated
public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> extends DirectoryProjectGeneratorBase<T> { public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> extends DirectoryProjectGeneratorBase<T> implements
PyProjectTypeGenerator {
public static final PyNewProjectSettings NO_SETTINGS = new PyNewProjectSettings(); public static final PyNewProjectSettings NO_SETTINGS = new PyNewProjectSettings();
private static final Logger LOGGER = Logger.getInstance(PythonProjectGenerator.class); private static final Logger LOGGER = Logger.getInstance(PythonProjectGenerator.class);
@@ -72,6 +75,11 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
this(false, null); this(false, null);
} }
@Override
public final @NlsSafe @NotNull String getProjectTypeForStatistics() {
return getClass().getName();
}
/** /**
* @param allowRemoteProjectCreation if project of this type could be created remotely * @param allowRemoteProjectCreation if project of this type could be created remotely
* @param preferredInterpreter interpreter type to select by default * @param preferredInterpreter interpreter type to select by default
@@ -179,7 +187,7 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
if (statisticsInfo instanceof InterpreterStatisticsInfo interpreterStatisticsInfo && settings.getSdk() != null) { if (statisticsInfo instanceof InterpreterStatisticsInfo interpreterStatisticsInfo && settings.getSdk() != null) {
PythonNewProjectWizardCollector.logPythonNewProjectGenerated(interpreterStatisticsInfo, PythonNewProjectWizardCollector.logPythonNewProjectGenerated(interpreterStatisticsInfo,
PyStatisticToolsKt.getVersion(settings.getSdk()), PyStatisticToolsKt.getVersion(settings.getSdk()),
this.getClass(), this,
Collections.emptyList()); Collections.emptyList());
} }
} }

View File

@@ -2,20 +2,20 @@
package com.jetbrains.python.newProjectWizard package com.jetbrains.python.newProjectWizard
import com.intellij.openapi.GitRepositoryInitializer import com.intellij.openapi.GitRepositoryInitializer
import com.intellij.openapi.application.EDT
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.ide.progress.withBackgroundProgress import com.intellij.platform.ide.progress.withBackgroundProgress
import com.jetbrains.python.PyBundle import com.jetbrains.python.PyBundle
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector.logPythonNewProjectGenerated
import com.jetbrains.python.sdk.ModuleOrProject import com.jetbrains.python.sdk.ModuleOrProject
import com.jetbrains.python.sdk.add.v2.PySdkCreator import com.jetbrains.python.sdk.add.v2.PySdkCreator
import com.jetbrains.python.sdk.pythonSdk import com.jetbrains.python.sdk.pythonSdk
import com.jetbrains.python.sdk.setAssociationToModule import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.statistics.version import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
/** /**
* Settings each Python project has: [sdkCreator] and [createGitRepository] * Settings each Python project has: [sdkCreator] and [createGitRepository]
@@ -23,7 +23,7 @@ import kotlinx.coroutines.*
class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) { class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) {
lateinit var sdkCreator: PySdkCreator lateinit var sdkCreator: PySdkCreator
suspend fun generateAndGetSdk(module: Module, baseDir: VirtualFile): Result<Sdk> = coroutineScope { suspend fun generateAndGetSdk(module: Module, baseDir: VirtualFile): Result<Pair<Sdk, InterpreterStatisticsInfo>> = coroutineScope {
val project = module.project val project = module.project
if (createGitRepository) { if (createGitRepository) {
launch(CoroutineName("Generating git") + Dispatchers.IO) { launch(CoroutineName("Generating git") + Dispatchers.IO) {
@@ -32,22 +32,14 @@ class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) {
} }
} }
} }
val (sdk: Sdk, statistics: InterpreterStatisticsInfo?) = getSdkAndInterpreter(module).getOrElse { return@coroutineScope Result.failure(it) } val (sdk: Sdk, interpreterStatistics: InterpreterStatisticsInfo) = getSdkAndInterpreter(module).getOrElse { return@coroutineScope Result.failure(it) }
sdk.setAssociationToModule(module) sdk.setAssociationToModule(module)
module.pythonSdk = sdk module.pythonSdk = sdk
if (statistics != null) { return@coroutineScope Result.success(Pair(sdk, interpreterStatistics))
logPythonNewProjectGenerated(statistics,
sdk.version,
this::class.java,
emptyList())
}
return@coroutineScope Result.success(sdk)
} }
private suspend fun getSdkAndInterpreter(module: Module): Result<Pair<Sdk, InterpreterStatisticsInfo?>> = withContext(Dispatchers.EDT) { private suspend fun getSdkAndInterpreter(module: Module): Result<Pair<Sdk, InterpreterStatisticsInfo>> =
val sdk: Sdk = sdkCreator.getSdk(ModuleOrProject.ModuleAndProject(module)).getOrElse { return@withContext Result.failure(it) } sdkCreator.getSdk(ModuleOrProject.ModuleAndProject(module))
return@withContext Result.success(Pair<Sdk, InterpreterStatisticsInfo?>(sdk, sdkCreator.createStatisticsInfo()))
}
override fun toString(): String { override fun toString(): String {

View File

@@ -14,17 +14,19 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.DirectoryProjectGenerator import com.intellij.platform.DirectoryProjectGenerator
import com.intellij.platform.ProjectGeneratorPeer import com.intellij.platform.ProjectGeneratorPeer
import com.jetbrains.python.Result import com.jetbrains.python.Result
import com.jetbrains.python.newProjectWizard.collector.PyProjectTypeGenerator
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector.logPythonNewProjectGenerated
import com.jetbrains.python.newProjectWizard.impl.PyV3GeneratorPeer import com.jetbrains.python.newProjectWizard.impl.PyV3GeneratorPeer
import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows.Companion.validatePath import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows.Companion.validatePath
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMode import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMode
import com.jetbrains.python.statistics.version
import com.jetbrains.python.util.ErrorSink import com.jetbrains.python.util.ErrorSink
import com.jetbrains.python.util.ShowingMessageErrorSync import com.jetbrains.python.util.ShowingMessageErrorSync
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.annotations.Nls import kotlin.reflect.jvm.jvmName
import java.nio.file.Path
/** /**
* Extend this class to register a new project generator. * Extend this class to register a new project generator.
@@ -41,15 +43,16 @@ abstract class PyV3ProjectBaseGenerator<TYPE_SPECIFIC_SETTINGS : PyV3ProjectType
private val errorSink: ErrorSink = ShowingMessageErrorSync, private val errorSink: ErrorSink = ShowingMessageErrorSync,
private val _newProjectName: @NlsSafe String? = null, private val _newProjectName: @NlsSafe String? = null,
private val expandProjectAfterCreation: Boolean = !ApplicationManager.getApplication().isHeadlessEnvironment, private val expandProjectAfterCreation: Boolean = !ApplicationManager.getApplication().isHeadlessEnvironment,
) : DirectoryProjectGenerator<PyV3BaseProjectSettings> { ) : DirectoryProjectGenerator<PyV3BaseProjectSettings>, PyProjectTypeGenerator {
private val baseSettings = PyV3BaseProjectSettings() private val baseSettings = PyV3BaseProjectSettings()
val newProjectName: @NlsSafe String get() = _newProjectName ?: "${name.replace(" ", "")}Project" val newProjectName: @NlsSafe String get() = _newProjectName ?: "${name.replace(" ", "")}Project"
override val projectTypeForStatistics: @NlsSafe String = this::class.jvmName
override fun generateProject(project: Project, baseDir: VirtualFile, settings: PyV3BaseProjectSettings, module: Module) { override fun generateProject(project: Project, baseDir: VirtualFile, settings: PyV3BaseProjectSettings, module: Module) {
val coroutineScope = project.service<MyService>().coroutineScope val coroutineScope = project.service<MyService>().coroutineScope
coroutineScope.launch { coroutineScope.launch {
val sdk = settings.generateAndGetSdk(module, baseDir).getOrElse { val (sdk, interpreterStatistics) = settings.generateAndGetSdk(module, baseDir).getOrElse {
withContext(Dispatchers.EDT) { withContext(Dispatchers.EDT) {
errorSink.emit(it.localizedMessage) // Show error generation to user errorSink.emit(it.localizedMessage) // Show error generation to user
} }
@@ -58,6 +61,14 @@ abstract class PyV3ProjectBaseGenerator<TYPE_SPECIFIC_SETTINGS : PyV3ProjectType
// Project view must be expanded (PY-75909) but it can't be unless it contains some files. // Project view must be expanded (PY-75909) but it can't be unless it contains some files.
// Either base settings (which create venv) might generate some or type specific settings (like Django) may. // Either base settings (which create venv) might generate some or type specific settings (like Django) may.
// So we expand it right after SDK generation, but if there are no files yet, we do it again after project generation // So we expand it right after SDK generation, but if there are no files yet, we do it again after project generation
val pythonVersion = withContext(Dispatchers.IO) { sdk.version }
logPythonNewProjectGenerated(interpreterStatistics,
pythonVersion,
this@PyV3ProjectBaseGenerator,
emptyList())
ensureProjectViewExpanded(project) ensureProjectViewExpanded(project)
typeSpecificSettings.generateProject(module, baseDir, sdk).onFailure { errorSink.emit(it.localizedMessage) } typeSpecificSettings.generateProject(module, baseDir, sdk).onFailure { errorSink.emit(it.localizedMessage) }
ensureProjectViewExpanded(project) ensureProjectViewExpanded(project)

View File

@@ -0,0 +1,24 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.newProjectWizard.collector
import com.intellij.openapi.util.NlsSafe
import org.jetbrains.annotations.ApiStatus
/**
* [com.intellij.platform.DirectoryProjectGenerator] for something python-specific.
* You must implement both [com.intellij.platform.DirectoryProjectGenerator] and this interface.
*
* This interface is a part of internal JetBrains infrastructure.
* Extend [com.jetbrains.python.newProjectWizard.PyV3ProjectBaseGenerator] instead.
*/
@ApiStatus.Internal
interface PyProjectTypeGenerator {
/**
* Project type for FUS, see [PythonNewProjectWizardCollector.GENERATOR_FIELD].
* Try not to change it ofter not to break statistics.
*
* This property is for JetBrains plugins only and will be ignored for other plugins.
* Do not overwrite it if you aren't JetBrains employee.
*/
val projectTypeForStatistics: @NlsSafe String
}

View File

@@ -0,0 +1,36 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.newProjectWizard.collector
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep
import com.intellij.internal.statistic.eventLog.validator.ValidationResultType
import com.intellij.internal.statistic.eventLog.validator.rules.EventContext
import com.intellij.internal.statistic.eventLog.validator.rules.impl.CustomValidationRule
import com.intellij.internal.statistic.utils.getPluginInfo
import com.jetbrains.python.newProjectWizard.collector.PyProjectTypeValidationRule.Companion.EMPTY_PROJECT_TYPE_ID
import org.jetbrains.annotations.ApiStatus.Internal
/**
* Ensures project type provided to [doValidate] is [PyProjectTypeGenerator.projectTypeForStatistics]
*/
@Internal
class PyProjectTypeValidationRule : CustomValidationRule() {
companion object {
/**
* [com.intellij.platform.DirectoryProjectGenerator] for default (empty, base) project type isn't registered in EP, hence hardcoded
*/
const val EMPTY_PROJECT_TYPE_ID = "com.intellij.pycharm.community.ide.impl.newProject.steps.PythonBaseProjectGenerator"
}
override fun getRuleId(): String = "python_new_project_type"
override fun doValidate(data: String, context: EventContext): ValidationResultType = validate(data)
}
@Internal
fun validate(data: String): ValidationResultType {
val valid = data == EMPTY_PROJECT_TYPE_ID || AbstractNewProjectStep.EP_NAME
.extensionList
.filterIsInstance<PyProjectTypeGenerator>()
.any { getPluginInfo(it::class.java).isDevelopedByJetBrains() && it.projectTypeForStatistics == data }
return if (valid) ValidationResultType.ACCEPTED else ValidationResultType.REJECTED
}

View File

@@ -7,6 +7,7 @@ import com.intellij.internal.statistic.eventLog.events.EventFields.createAdditio
import com.intellij.internal.statistic.eventLog.events.EventPair import com.intellij.internal.statistic.eventLog.events.EventPair
import com.intellij.internal.statistic.eventLog.events.ObjectEventData import com.intellij.internal.statistic.eventLog.events.ObjectEventData
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
import com.intellij.platform.DirectoryProjectGenerator
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.psi.LanguageLevel
import com.jetbrains.python.statistics.EXECUTION_TYPE import com.jetbrains.python.statistics.EXECUTION_TYPE
@@ -22,13 +23,13 @@ object PythonNewProjectWizardCollector : CounterUsagesCollector() {
return GROUP return GROUP
} }
private val GROUP = EventLogGroup("python.new.project.wizard", 9) private val GROUP = EventLogGroup("python.new.project.wizard", 10)
const val PROJECT_GENERATED_EVENT_ID = "project.generated" const val PROJECT_GENERATED_EVENT_ID = "project.generated"
private val INHERIT_GLOBAL_SITE_PACKAGE_FIELD = EventFields.Boolean("inherit_global_site_package") private val INHERIT_GLOBAL_SITE_PACKAGE_FIELD = EventFields.Boolean("inherit_global_site_package")
private val MAKE_AVAILABLE_TO_ALL_PROJECTS = EventFields.Boolean("make_available_to_all_projects") private val MAKE_AVAILABLE_TO_ALL_PROJECTS = EventFields.Boolean("make_available_to_all_projects")
private val PREVIOUSLY_CONFIGURED = EventFields.Boolean("previously_configured") private val PREVIOUSLY_CONFIGURED = EventFields.Boolean("previously_configured")
private val IS_WSL_CONTEXT = EventFields.Boolean("wsl_context") private val IS_WSL_CONTEXT = EventFields.Boolean("wsl_context")
private val GENERATOR_FIELD = EventFields.Class("generator") private val GENERATOR_FIELD = EventFields.StringValidatedByCustomRule("generator", PyProjectTypeValidationRule::class.java)
private val DJANGO_ADMIN_FIELD = EventFields.Boolean("django_admin") private val DJANGO_ADMIN_FIELD = EventFields.Boolean("django_admin")
private val ADDITIONAL = createAdditionalDataField(GROUP.id, PROJECT_GENERATED_EVENT_ID) private val ADDITIONAL = createAdditionalDataField(GROUP.id, PROJECT_GENERATED_EVENT_ID)
@@ -50,12 +51,12 @@ object PythonNewProjectWizardCollector : CounterUsagesCollector() {
private val USE_EXISTING_VENV_FIX = GROUP.registerEvent("existing.venv") private val USE_EXISTING_VENV_FIX = GROUP.registerEvent("existing.venv")
@JvmStatic @JvmStatic
fun logPythonNewProjectGenerated( fun <T> logPythonNewProjectGenerated(
info: InterpreterStatisticsInfo, info: InterpreterStatisticsInfo,
pythonVersion: LanguageLevel, pythonVersion: LanguageLevel,
generatorClass: Class<*>, generator: T,
additionalData: List<EventPair<*>>, additionalData: List<EventPair<*>>,
) { ) where T : PyProjectTypeGenerator, T : DirectoryProjectGenerator<*> {
PROJECT_GENERATED_EVENT.log( PROJECT_GENERATED_EVENT.log(
INTERPRETER_TYPE.with(info.type.value), INTERPRETER_TYPE.with(info.type.value),
EXECUTION_TYPE.with(info.target.value), EXECUTION_TYPE.with(info.target.value),
@@ -64,7 +65,7 @@ object PythonNewProjectWizardCollector : CounterUsagesCollector() {
INHERIT_GLOBAL_SITE_PACKAGE_FIELD.with(info.globalSitePackage), INHERIT_GLOBAL_SITE_PACKAGE_FIELD.with(info.globalSitePackage),
MAKE_AVAILABLE_TO_ALL_PROJECTS.with(info.makeAvailableToAllProjects), MAKE_AVAILABLE_TO_ALL_PROJECTS.with(info.makeAvailableToAllProjects),
PREVIOUSLY_CONFIGURED.with(info.previouslyConfigured), PREVIOUSLY_CONFIGURED.with(info.previouslyConfigured),
GENERATOR_FIELD.with(generatorClass), GENERATOR_FIELD.with(generator.projectTypeForStatistics),
IS_WSL_CONTEXT.with(info.isWSLContext), IS_WSL_CONTEXT.with(info.isWSLContext),
ADDITIONAL.with(ObjectEventData(additionalData)) ADDITIONAL.with(ObjectEventData(additionalData))
) )

View File

@@ -0,0 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@ApiStatus.Internal
package com.jetbrains.python.newProjectWizard.collector;
import org.jetbrains.annotations.ApiStatus;

View File

@@ -2,7 +2,6 @@
package com.jetbrains.python.sdk.add.v2 package com.jetbrains.python.sdk.add.v2
import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.Sdk
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
import com.jetbrains.python.sdk.ModuleOrProject import com.jetbrains.python.sdk.ModuleOrProject
@@ -10,8 +9,5 @@ interface PySdkCreator {
/** /**
* Error is shown to user. Do not catch all exceptions, only return exceptions valuable to user * Error is shown to user. Do not catch all exceptions, only return exceptions valuable to user
*/ */
suspend fun getSdk(moduleOrProject: ModuleOrProject): Result<Sdk> suspend fun getSdk(moduleOrProject: ModuleOrProject): Result<Pair<Sdk, InterpreterStatisticsInfo>>
@RequiresEdt
fun createStatisticsInfo(): InterpreterStatisticsInfo? = null
} }

View File

@@ -20,6 +20,7 @@ import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.bindText import com.intellij.ui.dsl.builder.bindText
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.ui.showingScope import com.intellij.util.ui.showingScope
import com.jetbrains.python.PyBundle.message import com.jetbrains.python.PyBundle.message
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
@@ -158,12 +159,12 @@ class PythonAddNewEnvironmentPanel(val projectPathFlows: ProjectPathFlows, onlyA
} }
else { else {
runBlockingCancellable { getSdk(moduleOrProject) } runBlockingCancellable { getSdk(moduleOrProject) }
}.getOrThrow() }.getOrThrow().first
} }
override suspend fun getSdk(moduleOrProject: ModuleOrProject): Result<Sdk> { override suspend fun getSdk(moduleOrProject: ModuleOrProject): Result<Pair<Sdk, InterpreterStatisticsInfo>> {
model.navigator.saveLastState() model.navigator.saveLastState()
return when (selectedMode.get()) { val sdk = when (selectedMode.get()) {
PROJECT_VENV -> { PROJECT_VENV -> {
val projectPath = projectPathFlows.projectPathWithDefault.first() val projectPath = projectPathFlows.projectPathWithDefault.first()
// todo just keep venv path, all the rest is in the model // todo just keep venv path, all the rest is in the model
@@ -171,11 +172,13 @@ class PythonAddNewEnvironmentPanel(val projectPathFlows: ProjectPathFlows, onlyA
} }
BASE_CONDA -> model.selectCondaEnvironment(base = true) BASE_CONDA -> model.selectCondaEnvironment(base = true)
CUSTOM -> custom.currentSdkManager.getOrCreateSdk(moduleOrProject) CUSTOM -> custom.currentSdkManager.getOrCreateSdk(moduleOrProject)
} }.getOrElse { return Result.failure(it) }
val statistics = withContext(Dispatchers.EDT) { createStatisticsInfo() }
return Result.success(Pair(sdk, statistics))
} }
@RequiresEdt
override fun createStatisticsInfo(): InterpreterStatisticsInfo = when (selectedMode.get()) { fun createStatisticsInfo(): InterpreterStatisticsInfo = when (selectedMode.get()) {
PROJECT_VENV -> InterpreterStatisticsInfo(InterpreterType.VIRTUALENV, PROJECT_VENV -> InterpreterStatisticsInfo(InterpreterType.VIRTUALENV,
InterpreterTarget.LOCAL, InterpreterTarget.LOCAL,
false, false,