mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
[kotlin] Send months since first Kotlin and IDEA usage in BuildProcessSatisfaction survey
^KTIJ-31263 fixed GitOrigin-RevId: e10ad3fe2648b3982db8c0fa87ec07b61e342352
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ef89768305
commit
71e4767fed
@@ -22,5 +22,7 @@ build.process.info.gradle.version=Gradle version:
|
||||
build.process.info.kotlin.version=Kotlin version:
|
||||
build.process.info.groovy.build.file.count=Number of Gradle Groovy build files:
|
||||
build.process.info.kts.build.file.count=Number of Gradle Kts build files:
|
||||
build.process.info.months.of.kotlin.usage=Months of Kotlin usage:
|
||||
build.process.info.months.of.idea.usage=Months of IntelliJ IDEA usage:
|
||||
|
||||
action.org.jetbrains.kotlin.onboarding.gradle.ShowBuildProcessSatisfactionDialogAction.text=Show Kotlin Gradle Build Process Satisfaction Survey
|
||||
@@ -19,9 +19,12 @@ import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.idea.configuration.getGradleKotlinVersion
|
||||
import org.jetbrains.kotlin.onboarding.KotlinNewUserTracker
|
||||
import org.jetbrains.plugins.gradle.service.GradleInstallationManager
|
||||
import org.jetbrains.plugins.gradle.settings.GradleSettings
|
||||
import org.jetbrains.plugins.gradle.util.GradleUtil
|
||||
import java.time.LocalDate
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.io.path.div
|
||||
|
||||
@Serializable
|
||||
@@ -30,6 +33,8 @@ internal data class BuildProcessSatisfactionDialogData(
|
||||
@NlsSafe val kotlinVersion: String,
|
||||
val groovyBuildFileCount: Int,
|
||||
val ktsBuildFileCount: Int,
|
||||
val monthsOfIdeaUsage: Int,
|
||||
val monthsOfKotlinUsage: Int,
|
||||
val commonData: CommonFeedbackSystemData
|
||||
) : SystemDataJsonSerializable {
|
||||
override fun serializeToJson(json: Json): JsonElement = json.encodeToJsonElement(this)
|
||||
@@ -63,6 +68,10 @@ internal class BuildProcessSatisfactionDialog(
|
||||
return project.modules.mapNotNull { it.getGradleKotlinVersion() }.distinct()
|
||||
}
|
||||
|
||||
private fun LocalDate.monthsSinceDate(): Int {
|
||||
return ChronoUnit.MONTHS.between(this, LocalDate.now()).toInt()
|
||||
}
|
||||
|
||||
private fun collectData(): BuildProcessSatisfactionDialogData {
|
||||
val allExternalModulePaths = project.modules.mapNotNullTo(mutableSetOf()) {
|
||||
ExternalSystemApiUtil.getExternalProjectPath(it)?.toNioPathOrNull()
|
||||
@@ -77,12 +86,17 @@ internal class BuildProcessSatisfactionDialog(
|
||||
val gradleVersion = getGradleVersion()?.version ?: "UNKNOWN"
|
||||
val kotlinVersion = getKotlinVersions().maxOrNull() ?: "UNKNOWN"
|
||||
|
||||
val monthsOfIdeaUsage = KotlinNewUserTracker.getInstance().getInstallationDate()?.monthsSinceDate() ?: 0
|
||||
val monthsOfKotlinUsage = KotlinNewUserTracker.getInstance().getFirstKotlinUsageDate()?.monthsSinceDate() ?: 0
|
||||
|
||||
return BuildProcessSatisfactionDialogData(
|
||||
gradleVersion,
|
||||
kotlinVersion,
|
||||
groovyCount,
|
||||
ktsCount,
|
||||
CommonFeedbackSystemData.getCurrentData()
|
||||
monthsOfIdeaUsage,
|
||||
monthsOfKotlinUsage,
|
||||
CommonFeedbackSystemData.getCurrentData(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,6 +123,12 @@ internal class BuildProcessSatisfactionDialog(
|
||||
row(GradleFeedbackBundle.message("build.process.info.kts.build.file.count")) {
|
||||
label(mySystemInfoData.ktsBuildFileCount.toString())
|
||||
}
|
||||
row(GradleFeedbackBundle.message("build.process.info.months.of.kotlin.usage")) {
|
||||
label(mySystemInfoData.monthsOfKotlinUsage.toString())
|
||||
}
|
||||
row(GradleFeedbackBundle.message("build.process.info.months.of.idea.usage")) {
|
||||
label(mySystemInfoData.monthsOfIdeaUsage.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
override val myTitle: String = GradleFeedbackBundle.message("dialog.build.process.gradle.satisfaction.top.title")
|
||||
|
||||
@@ -8,11 +8,9 @@ import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
import java.time.*
|
||||
|
||||
class KotlinNewUserTrackerState : BaseState() {
|
||||
// Unix time seconds
|
||||
@@ -54,7 +52,11 @@ class KotlinNewUserTracker : PersistentStateComponent<KotlinNewUserTrackerState>
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
internal fun getInstallationDate(): LocalDate? {
|
||||
/**
|
||||
* Returns the date on which the user installed IDEA, or null if it could not be determined.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun getInstallationDate(): LocalDate? {
|
||||
val installationId = getInstallationId() ?: return null
|
||||
val dateSubstring = installationId.take(6).takeIf { it.length == 6 } ?: return null
|
||||
val day = dateSubstring.substring(0..1).toIntOrNull() ?: return null
|
||||
@@ -84,6 +86,18 @@ class KotlinNewUserTracker : PersistentStateComponent<KotlinNewUserTrackerState>
|
||||
currentState = state
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date on which the user was first detected to have used Kotlin.
|
||||
* Returns null if an error occurred, or the user has not used Kotlin before.
|
||||
*
|
||||
* Note: This data only started being tracked in 2023, so that is the minimum date that exists and can be returned from here.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun getFirstKotlinUsageDate(): LocalDate? {
|
||||
if (state.newKtUserSince == 0L) return null
|
||||
return Instant.ofEpochSecond(state.newKtUserSince).atOffset(ZoneOffset.UTC).toLocalDate()
|
||||
}
|
||||
|
||||
internal fun isNewKtUser(): Boolean {
|
||||
if (state.newKtUserSince == 0L) return false
|
||||
val newUserStart = Instant.ofEpochSecond(state.newKtUserSince)
|
||||
|
||||
@@ -11,18 +11,23 @@ import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.*
|
||||
|
||||
class KotlinNewUserTrackerTest {
|
||||
private val now = Instant.now()
|
||||
private val today = LocalDate.now()
|
||||
|
||||
private fun Long.isRecentEpochTimestamp(): Boolean {
|
||||
return Duration.between(Instant.ofEpochSecond(this), Instant.now()) < Duration.ofSeconds(30)
|
||||
}
|
||||
|
||||
private fun LocalDate.daysSinceDate(): Int {
|
||||
return ChronoUnit.DAYS.between(this, today).toInt()
|
||||
}
|
||||
|
||||
|
||||
private fun createInstance(
|
||||
installationDate: LocalDate? = LocalDate.now().minusDays(NEW_IDEA_USER_DURATION.toDays() + 1)
|
||||
): KotlinNewUserTracker {
|
||||
@@ -43,6 +48,7 @@ class KotlinNewUserTrackerTest {
|
||||
assertTrue(instance.state.firstKtFileOpened == 0L)
|
||||
assertTrue(instance.state.newKtUserSince == 0L)
|
||||
assertTrue(instance.state.firstKtFileOpened == 0L)
|
||||
assertNull(instance.getFirstKotlinUsageDate())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,6 +59,9 @@ class KotlinNewUserTrackerTest {
|
||||
assertTrue(instance.state.lastKtFileOpened.isRecentEpochTimestamp())
|
||||
assertTrue(instance.isNewKtUser())
|
||||
assertFalse(instance.shouldShowNewUserDialog())
|
||||
assertNotNull(instance.getFirstKotlinUsageDate())
|
||||
// We are very careful and check <= 1 here in case the test changes over at that time, which is almost never going to happen
|
||||
assertTrue(instance.getFirstKotlinUsageDate()!!.daysSinceDate() <= 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,6 +72,7 @@ class KotlinNewUserTrackerTest {
|
||||
assertTrue(instance.state.lastKtFileOpened.isRecentEpochTimestamp())
|
||||
assertTrue(instance.isNewKtUser())
|
||||
assertFalse(instance.shouldShowNewUserDialog())
|
||||
assertTrue(instance.getFirstKotlinUsageDate()!!.daysSinceDate() <= 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,6 +94,7 @@ class KotlinNewUserTrackerTest {
|
||||
assertTrue(instance.isNewKtUser())
|
||||
// User has not passed the new user period
|
||||
assertFalse(instance.shouldShowNewUserDialog())
|
||||
assertTrue(instance.getFirstKotlinUsageDate()!!.daysSinceDate() <= 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,6 +102,8 @@ class KotlinNewUserTrackerTest {
|
||||
val instance = createInstance()
|
||||
instance.state.newKtUserSince = (now - NEW_USER_SURVEY_DELAY + Duration.ofHours(1)).epochSecond
|
||||
assertFalse(instance.shouldShowNewUserDialog())
|
||||
val newUserDays = NEW_USER_SURVEY_DELAY.toDays()
|
||||
assertTrue(instance.getFirstKotlinUsageDate()!!.daysSinceDate() in (newUserDays - 1..newUserDays + 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user