mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
[evaluation-plugin] LME-56 Encapsulate everything project-specific to EvaluationEnvironment and create an example of standalone environment
- introduce DatasetContext for better control over data sources - extract actions and associated fields like language to an optional config - create sub-config for standalone datasets - adapt result report rendering more convenient for csv dataset - make language configuration optional where possible - make application lifecycle management more transparent and add sleep for quick runs (intellij can throw errors) - fix inability to use colons in chunk name Merge-request: IJ-MR-143249 Merged-by: Roman Vasiliev <Roman.Vasiliev@jetbrains.com> GitOrigin-RevId: 0b15518f22af27ee9cfb544e99010ec293f31eb5
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d17f70059a
commit
b177803d6f
@@ -10,7 +10,7 @@ object EvaluationFilterReader {
|
||||
for ((id, description) in map) {
|
||||
val configuration = EvaluationFilterManager.getConfigurationById(id)
|
||||
?: throw IllegalStateException("Unknown filter: $id")
|
||||
assert(configuration.isLanguageSupported(language)) { "filter $id is not supported for this language" }
|
||||
assert(configuration.isLanguageSupported(language)) { "filter $id is not supported for this language: ${language}" }
|
||||
evaluationFilters[id] = configuration.buildFromJson(description)
|
||||
}
|
||||
return evaluationFilters
|
||||
|
||||
@@ -40,7 +40,7 @@ class FusLogsSaver(private val finalStorageDir: Path, private val allowedGroups:
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPathApi::class)
|
||||
override fun save(languageName: String, trainingPercentage: Int) {
|
||||
override fun save(languageName: String?, trainingPercentage: Int) {
|
||||
try {
|
||||
for (sessionTemporaryRelativePath in temporaryFusLogsDirectory.walk()) {
|
||||
val sessionTemporaryPath = temporaryFusLogsDirectory / sessionTemporaryRelativePath
|
||||
@@ -54,8 +54,8 @@ class FusLogsSaver(private val finalStorageDir: Path, private val allowedGroups:
|
||||
}
|
||||
}
|
||||
|
||||
private fun obtainFinalLogsDirectory(languageName: String): Path {
|
||||
val fusFinalDirectory = finalStorageDir / languageName
|
||||
private fun obtainFinalLogsDirectory(languageName: String?): Path {
|
||||
val fusFinalDirectory = if (languageName != null) finalStorageDir / languageName else finalStorageDir
|
||||
if (!fusFinalDirectory.exists()) {
|
||||
fusFinalDirectory.createDirectory()
|
||||
}
|
||||
|
||||
@@ -5,24 +5,20 @@ import com.intellij.cce.actions.*
|
||||
import com.intellij.cce.core.Session
|
||||
import com.intellij.cce.util.FileTextUtil.computeChecksum
|
||||
import com.intellij.cce.util.FileTextUtil.getDiff
|
||||
import java.nio.file.Paths
|
||||
import kotlin.random.Random
|
||||
|
||||
class ActionInvokingInterpreter(private val invokersFactory: InvokersFactory,
|
||||
private val handler: InterpretationHandler,
|
||||
private val filter: InterpretFilter,
|
||||
private val order: InterpretationOrder,
|
||||
private val projectPath: String?) : Interpreter {
|
||||
private val order: InterpretationOrder) {
|
||||
|
||||
override fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
|
||||
fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
|
||||
val actionsInvoker = invokersFactory.createActionsInvoker()
|
||||
val featureInvoker = invokersFactory.createFeatureInvoker()
|
||||
val sessions = mutableListOf<Session>()
|
||||
val filePath = if (projectPath == null) fileActions.path else Paths.get(projectPath).resolve(fileActions.path).toString()
|
||||
val needToClose = !actionsInvoker.isOpen(filePath)
|
||||
val text = actionsInvoker.openFile(filePath)
|
||||
val needToClose = !actionsInvoker.isOpen(fileActions.path)
|
||||
val text = actionsInvoker.openFile(fileActions.path)
|
||||
if (fileActions.checksum != computeChecksum(text)) {
|
||||
handler.onErrorOccurred(IllegalStateException("File $filePath has been modified."), fileActions.sessionsCount)
|
||||
handler.onErrorOccurred(IllegalStateException("File ${fileActions.path} has been modified."), fileActions.sessionsCount)
|
||||
return emptyList()
|
||||
}
|
||||
var shouldCompleteToken = filter.shouldCompleteToken()
|
||||
@@ -57,22 +53,11 @@ class ActionInvokingInterpreter(private val invokersFactory: InvokersFactory,
|
||||
if (text != resultText) {
|
||||
actionsInvoker.deleteRange(0, resultText.length)
|
||||
actionsInvoker.printText(text)
|
||||
if (needToClose) actionsInvoker.closeFile(filePath)
|
||||
if (needToClose) actionsInvoker.closeFile(fileActions.path)
|
||||
throw IllegalStateException("Text before and after interpretation doesn't match. Diff:\n${getDiff(text, resultText)}")
|
||||
}
|
||||
if (needToClose) actionsInvoker.closeFile(filePath)
|
||||
if (needToClose) actionsInvoker.closeFile(fileActions.path)
|
||||
handler.onFileProcessed(fileActions.path)
|
||||
return sessions.sortedBy { it.offset }
|
||||
}
|
||||
|
||||
private fun List<Action>.reorder(order: InterpretationOrder): List<Action> {
|
||||
val groups = groupBy { it.sessionId }.values
|
||||
return when (order) {
|
||||
InterpretationOrder.LINEAR -> groups.flatten()
|
||||
InterpretationOrder.REVERSED -> groups.reversed().flatten()
|
||||
InterpretationOrder.RANDOM -> groups.shuffled(ORDER_RANDOM).flatten()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ORDER_RANDOM = Random(42)
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.cce.interpreter
|
||||
|
||||
import com.intellij.cce.actions.Action
|
||||
import com.intellij.cce.interpreter.InterpretationOrder.*
|
||||
import kotlin.random.Random
|
||||
|
||||
enum class InterpretationOrder {
|
||||
LINEAR,
|
||||
REVERSED,
|
||||
RANDOM
|
||||
}
|
||||
|
||||
private val ORDER_RANDOM = Random(42)
|
||||
|
||||
fun <T : Action> List<T>.reorder(order: InterpretationOrder): List<T> {
|
||||
val groups = groupBy { it.sessionId }.values
|
||||
return when (order) {
|
||||
LINEAR -> groups.flatten()
|
||||
REVERSED -> groups.reversed().flatten()
|
||||
RANDOM -> groups.shuffled(ORDER_RANDOM).flatten()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// 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.cce.interpreter
|
||||
|
||||
import com.intellij.cce.actions.*
|
||||
import com.intellij.cce.core.Session
|
||||
|
||||
interface Interpreter {
|
||||
fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session>
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.intellij.cce.interpreter
|
||||
|
||||
import com.intellij.cce.actions.FileActions
|
||||
import com.intellij.cce.core.Session
|
||||
import com.intellij.cce.workspace.storages.LogsSaver
|
||||
|
||||
class LoggingInterpreterWrapper(private val baseInterpreter: Interpreter, private val logsSaver: LogsSaver) : Interpreter {
|
||||
override fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
|
||||
return logsSaver.invokeRememberingLogs {
|
||||
baseInterpreter.interpret(fileActions, sessionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Interpreter.wrapLogging(logsSaver: LogsSaver): Interpreter = LoggingInterpreterWrapper(this, logsSaver)
|
||||
@@ -35,7 +35,7 @@ abstract class BaseCompletionGolfFileReportGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHtml(fileEvaluations: List<FileEvaluationInfo>, fileName: String, resourcePath: String, text: String): String {
|
||||
override fun getHtml(fileEvaluations: List<FileEvaluationInfo>, resourcePath: String, text: String): String {
|
||||
return createHTML().body {
|
||||
div("cg") {
|
||||
div {
|
||||
|
||||
@@ -15,7 +15,7 @@ open class BasicFileReportGenerator(
|
||||
dirs: GeneratorDirectories
|
||||
) : FileReportGenerator(featuresStorages, dirs, filterName, comparisonFilterName) {
|
||||
|
||||
override fun getHtml(fileEvaluations: List<FileEvaluationInfo>, fileName: String, resourcePath: String, text: String): String {
|
||||
override fun getHtml(fileEvaluations: List<FileEvaluationInfo>, resourcePath: String, text: String): String {
|
||||
val sessions = fileEvaluations.map { it.sessionsInfo.sessions }
|
||||
val maxLookupOrder = sessions.flatMap { session -> session.map { it.lookups.size - 1 } }.maxOrNull() ?: 0
|
||||
return createHTML().body {
|
||||
|
||||
@@ -25,23 +25,23 @@ abstract class FileReportGenerator(
|
||||
|
||||
val reportReferences: MutableMap<String, ReferenceInfo> = mutableMapOf()
|
||||
|
||||
abstract fun getHtml(fileEvaluations: List<FileEvaluationInfo>, fileName: String, resourcePath: String, text: String): String
|
||||
abstract fun getHtml(fileEvaluations: List<FileEvaluationInfo>, resourcePath: String, text: String): String
|
||||
|
||||
abstract val scripts: List<Resource>
|
||||
|
||||
override fun generateFileReport(sessions: List<FileEvaluationInfo>) {
|
||||
val fileInfo = sessions.first()
|
||||
val fileName = if (sessions.size > 1) {
|
||||
"${fileInfo.sessionsInfo.projectName} - ${File(fileInfo.sessionsInfo.filePath).name}"
|
||||
} else {
|
||||
File(fileInfo.sessionsInfo.filePath).name
|
||||
}
|
||||
val (resourcePath, reportPath) = dirs.getPaths(fileName)
|
||||
val fileName = File(fileInfo.sessionsInfo.filePath).name
|
||||
val fileNameAlreadyHasProject = fileName.startsWith(fileInfo.sessionsInfo.projectName)
|
||||
val internalFileName =
|
||||
if (sessions.size > 1 && !fileNameAlreadyHasProject) "${fileInfo.sessionsInfo.projectName} - $fileName" else fileName
|
||||
val (resourcePath, reportPath) = dirs.getPaths(internalFileName)
|
||||
val sessionsJson = sessionSerializer.serialize(sessions.map { it.sessionsInfo.sessions }.flatten())
|
||||
val resourceFile = File(resourcePath.toString())
|
||||
resourceFile.writeText("var sessions = {};\nvar features={};\nvar fullLineLog=[];\nsessions = ${parseJsonInJs(sessionsJson)};\n")
|
||||
processStorages(sessions, resourceFile)
|
||||
val reportTitle = "Evaluation Report for file $fileName (project: ${fileInfo.sessionsInfo.projectName})"
|
||||
val titleProject = if (fileNameAlreadyHasProject) "" else " (project: ${fileInfo.sessionsInfo.projectName})"
|
||||
val reportTitle = "Evaluation Report for $fileName$titleProject"
|
||||
createHTML().html {
|
||||
head {
|
||||
createHead(this, reportTitle, resourcePath)
|
||||
@@ -51,15 +51,15 @@ abstract class FileReportGenerator(
|
||||
unsafe {
|
||||
+getHtml(
|
||||
sessions.sortedBy { it.evaluationType },
|
||||
fileName,
|
||||
dirs.filesDir.relativize(resourcePath).toString(),
|
||||
fileInfo.sessionsInfo.text
|
||||
)
|
||||
}
|
||||
}
|
||||
}.also { html -> FileWriter(reportPath.toString()).use { it.write(html) } }
|
||||
val fileReference = "$fileName (${fileInfo.sessionsInfo.filePath})"
|
||||
reportReferences[fileReference] = ReferenceInfo(reportPath, sessions.map { it.metrics }.flatten())
|
||||
val fullPathDetails = if (fileInfo.sessionsInfo.filePath != fileName) " (${fileInfo.sessionsInfo.filePath})" else ""
|
||||
val tableReference = "$fileName$fullPathDetails"
|
||||
reportReferences[tableReference] = ReferenceInfo(reportPath, sessions.map { it.metrics }.flatten())
|
||||
}
|
||||
|
||||
open fun createHead(head: HEAD, reportTitle: String, resourcePath: Path) = with(head) {
|
||||
|
||||
@@ -110,7 +110,7 @@ class HtmlReportGenerator(
|
||||
}
|
||||
body {
|
||||
h1 { +reportTitle }
|
||||
h3 { +"${fileGenerator.reportReferences.size} file(s) successfully processed" }
|
||||
h3 { +"${fileGenerator.reportReferences.size} chunk(s) successfully processed" }
|
||||
h3 { +"${errorReferences.size} errors occurred" }
|
||||
unsafe { raw(getToolbar(globalMetrics)) }
|
||||
div { id = "metricsTable" }
|
||||
|
||||
@@ -8,36 +8,31 @@ import com.intellij.cce.workspace.filter.CompareSessionsFilter
|
||||
import com.intellij.cce.workspace.filter.NamedFilter
|
||||
import com.intellij.cce.workspace.filter.SessionsFilter
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
/**
|
||||
* Represents a configuration of the evaluation process.
|
||||
*
|
||||
* @property projectPath The path to the project that will be used for the evaluation.
|
||||
* @property projectName The name of the project. It may differ from the root directory name.
|
||||
* @property language The programming language whose files are used in the evaluation.
|
||||
* @property outputDir The output directory for the evaluation results.
|
||||
* @property strategy The evaluation strategy used.
|
||||
* @property actions The configuration for actions generation step.
|
||||
* @property fileDataset Dataset configuration for standalone setups
|
||||
* @property interpret The configuration for actions interpretation step.
|
||||
* @property reorder The configuration for element reordering step.
|
||||
* @property reports The configuration for report generation step.
|
||||
*/
|
||||
data class Config private constructor(
|
||||
val projectPath: String,
|
||||
val projectName: String,
|
||||
val language: String,
|
||||
val outputDir: String,
|
||||
val strategy: EvaluationStrategy,
|
||||
val actions: ActionsGeneration,
|
||||
val actions: ActionsGeneration?,
|
||||
val fileDataset: FileDataset?,
|
||||
val interpret: ActionsInterpretation,
|
||||
val reorder: ReorderElements,
|
||||
val reports: ReportGeneration
|
||||
) {
|
||||
companion object {
|
||||
fun build(projectPath: String, language: String, init: Builder.() -> Unit): Config {
|
||||
val builder = Builder(projectPath, language)
|
||||
fun build(init: Builder.() -> Unit): Config {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build()
|
||||
}
|
||||
@@ -49,17 +44,37 @@ data class Config private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : EvaluationStrategy> strategy(): T = strategy as T // TODO add type parameter to Config?
|
||||
|
||||
/**
|
||||
* Represents the configuration for generating actions.
|
||||
*
|
||||
* @property projectPath The path to the project that will be used for the evaluation.
|
||||
* @property projectName The name of the project. It may differ from the root directory name.
|
||||
* @property language The programming language whose files are used in the evaluation.
|
||||
* @property evaluationRoots The list of evaluation roots. Directories and files with relative and absolute paths are allowed.
|
||||
* @property ignoreFileNames The set of file names to ignore. Files and directories with these names inside [evaluationRoots] will be skipped.
|
||||
*/
|
||||
data class ActionsGeneration internal constructor(
|
||||
val projectPath: String,
|
||||
val projectName: String,
|
||||
val language: String,
|
||||
val evaluationRoots: List<String>,
|
||||
val ignoreFileNames: Set<String>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents a configuration for datasets stored on a file system (probably in one file).
|
||||
*
|
||||
* @property url The URL of the file. Check DatasetRef for available options.
|
||||
* @property chunkSize The size of each chunk when reading and rendering the file.
|
||||
*/
|
||||
data class FileDataset internal constructor(
|
||||
val url: String,
|
||||
val chunkSize: Int,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents the configuration for the interpretation of actions.
|
||||
*
|
||||
@@ -116,11 +131,11 @@ data class Config private constructor(
|
||||
val sessionsFilters: List<SessionsFilter>,
|
||||
val comparisonFilters: List<CompareSessionsFilter>)
|
||||
|
||||
class Builder internal constructor(private val projectPath: String, private val language: String) {
|
||||
var evaluationRoots = mutableListOf<String>()
|
||||
var ignoreFileNames = mutableSetOf<String>()
|
||||
var projectName = projectPath.split('/').last()
|
||||
var outputDir: String = Paths.get(projectPath, "completion-evaluation").toAbsolutePath().toString()
|
||||
class Builder internal constructor() {
|
||||
var actions: ActionsGeneration? = null
|
||||
var fileDataset: FileDataset? = null
|
||||
|
||||
var outputDir: String? = null
|
||||
var strategy: EvaluationStrategy = EvaluationStrategy.defaultStrategy
|
||||
var saveLogs = false
|
||||
var saveFusLogs = false
|
||||
@@ -143,11 +158,11 @@ data class Config private constructor(
|
||||
private val sessionsFilters: MutableList<SessionsFilter> = mutableListOf()
|
||||
private val comparisonFilters: MutableList<CompareSessionsFilter> = mutableListOf()
|
||||
|
||||
constructor(config: Config) : this(config.projectPath, config.language) {
|
||||
constructor(config: Config) : this() {
|
||||
actions = config.actions
|
||||
fileDataset = config.fileDataset
|
||||
outputDir = config.outputDir
|
||||
strategy = config.strategy
|
||||
evaluationRoots.addAll(config.actions.evaluationRoots)
|
||||
ignoreFileNames.addAll(config.actions.ignoreFileNames)
|
||||
saveLogs = config.interpret.saveLogs
|
||||
saveFusLogs = config.interpret.saveFusLogs
|
||||
saveFeatures = config.interpret.saveFeatures
|
||||
@@ -180,16 +195,16 @@ data class Config private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildOutputDir() =
|
||||
outputDir
|
||||
?: actions?.projectPath?.let { Paths.get(it, "completion-evaluation").toAbsolutePath().toString() }
|
||||
?: throw IllegalStateException("Output directory is not defined")
|
||||
|
||||
fun build(): Config = Config(
|
||||
Paths.get(projectPath).absolute().toString(),
|
||||
projectName,
|
||||
language,
|
||||
outputDir,
|
||||
buildOutputDir(),
|
||||
strategy,
|
||||
ActionsGeneration(
|
||||
evaluationRoots,
|
||||
ignoreFileNames,
|
||||
),
|
||||
actions,
|
||||
fileDataset,
|
||||
ActionsInterpretation(
|
||||
experimentGroup,
|
||||
sessionsLimit,
|
||||
|
||||
@@ -17,27 +17,22 @@ import java.nio.file.Path
|
||||
|
||||
object ConfigFactory {
|
||||
const val DEFAULT_CONFIG_NAME = "config.json"
|
||||
private const val NO_LANGUAGE = "NO LANGUAGE PROVIDED"
|
||||
|
||||
private lateinit var gson: Gson
|
||||
|
||||
private fun defaultConfig(projectPath: String = "", language: String = "Java"): Config =
|
||||
Config.build(projectPath, language) {}
|
||||
private fun defaultConfig(): Config =
|
||||
Config.build {}
|
||||
|
||||
fun <T : EvaluationStrategy> load(path: Path, strategySerializer: StrategySerializer<T>): Config {
|
||||
gson = GsonBuilder()
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SessionsFilter::class.java,
|
||||
SessionFiltersSerializer())
|
||||
.registerTypeAdapter(EvaluationStrategy::class.java, strategySerializer)
|
||||
.create()
|
||||
gson = createGson(strategySerializer)
|
||||
val configFile = path.toFile()
|
||||
if (!configFile.exists()) {
|
||||
save(defaultConfig(), path.parent, configFile.name)
|
||||
throw IllegalArgumentException("Config file missing. Config created by path: ${configFile.absolutePath}. Fill settings in config.")
|
||||
}
|
||||
|
||||
return deserialize(configFile.readText(), strategySerializer)
|
||||
return deserialize(gson, configFile.readText(), strategySerializer)
|
||||
}
|
||||
|
||||
fun save(config: Config, directory: Path, name: String = DEFAULT_CONFIG_NAME) {
|
||||
@@ -47,28 +42,74 @@ object ConfigFactory {
|
||||
|
||||
fun serialize(config: Config): String = gson.toJson(config)
|
||||
|
||||
fun <T : EvaluationStrategy> deserialize(json: String, strategySerializer: StrategySerializer<T>): Config {
|
||||
fun <T : EvaluationStrategy> deserialize(gson: Gson, json: String, strategySerializer: StrategySerializer<T>): Config {
|
||||
val map = gson.fromJson(json, HashMap<String, Any>().javaClass)
|
||||
val languageName = map.getAs<String>("language")
|
||||
return Config.build(map.handleEnv("projectPath"), languageName) {
|
||||
return Config.build {
|
||||
outputDir = map.handleEnv("outputDir")
|
||||
if (map.containsKey("projectName")) {
|
||||
projectName = map.handleEnv("projectName")
|
||||
}
|
||||
deserializeStrategy(map.getIfExists("strategy"), strategySerializer, languageName, this)
|
||||
deserializeActionsGeneration(map.getIfExists("actions"), languageName, this)
|
||||
deserializeActionsGeneration(
|
||||
map.getIfExists("actions"),
|
||||
map.getIfExists<String>("projectPath")?.handleEnv(),
|
||||
map.getIfExists<String>("projectName")?.handleEnv(),
|
||||
map.getIfExists<String>("language"),
|
||||
this
|
||||
)
|
||||
deserializeFileDataset(map.getIfExists("fileDataset"), this)
|
||||
|
||||
deserializeStrategy(map.getIfExists("strategy"), strategySerializer, actions?.language, this)
|
||||
deserializeActionsInterpretation(map.getIfExists("interpret"), this)
|
||||
deserializeReorderElements(map.getIfExists("reorder"), this)
|
||||
deserializeReportGeneration(map.getIfExists("reports"), languageName, this)
|
||||
deserializeReportGeneration(map.getIfExists("reports"), actions?.language, this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeActionsGeneration(map: Map<String, Any>?, language: String, builder: Config.Builder) {
|
||||
if (map == null) return
|
||||
builder.evaluationRoots = map.getAs("evaluationRoots")
|
||||
if (map.containsKey("ignoreFileNames")) {
|
||||
builder.ignoreFileNames = map.getAs<List<String>>("ignoreFileNames").toMutableSet()
|
||||
fun <T : EvaluationStrategy> createGson(strategySerializer: StrategySerializer<T>): Gson {
|
||||
return GsonBuilder()
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SessionsFilter::class.java,
|
||||
SessionFiltersSerializer())
|
||||
.registerTypeAdapter(EvaluationStrategy::class.java, strategySerializer)
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun deserializeActionsGeneration(
|
||||
map: Map<String, Any>?,
|
||||
projectPath: String?,
|
||||
projectName: String?,
|
||||
language: String?,
|
||||
builder: Config.Builder
|
||||
) {
|
||||
if (map == null && projectPath == null && language == null && projectName == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (map != null) {
|
||||
val resultProjectPath = projectPath ?: map.handleEnv("projectPath")
|
||||
builder.actions = Config.ActionsGeneration(
|
||||
resultProjectPath,
|
||||
projectName ?: map.getIfExists<String>("projectName")?.handleEnv() ?: resultProjectPath.split('/').last(),
|
||||
language ?: map.getAs("language"),
|
||||
map.getAs("evaluationRoots"),
|
||||
map.getIfExists<List<String>>("ignoreFileNames")?.toSet() ?: emptySet()
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
throw IllegalStateException("Missing 'actions' in config when 'language', 'projectPath' or 'projectName' was provided")
|
||||
}
|
||||
|
||||
private fun deserializeFileDataset(
|
||||
map: Map<String, Any>?,
|
||||
builder: Config.Builder
|
||||
) {
|
||||
if (map == null) {
|
||||
return
|
||||
}
|
||||
|
||||
builder.fileDataset = Config.FileDataset(
|
||||
map.getAs("url"),
|
||||
map.getAs<Double>("chunkSize").toInt(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun deserializeActionsInterpretation(map: Map<String, Any>?, builder: Config.Builder) {
|
||||
@@ -103,11 +144,11 @@ object ConfigFactory {
|
||||
|
||||
private fun <T : EvaluationStrategy> deserializeStrategy(map: Map<String, Any>?,
|
||||
strategySerializer: StrategySerializer<T>,
|
||||
language: String,
|
||||
language: String?,
|
||||
builder: Config.Builder) {
|
||||
if (map == null)
|
||||
throw IllegalArgumentException("No strategy found in config!")
|
||||
builder.strategy = strategySerializer.deserialize(map, language)
|
||||
builder.strategy = strategySerializer.deserialize(map, language ?: NO_LANGUAGE)
|
||||
}
|
||||
|
||||
private fun deserializeReorderElements(map: Map<String, Any>?, builder: Config.Builder) {
|
||||
@@ -122,7 +163,7 @@ object ConfigFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeReportGeneration(map: Map<String, Any>?, language: String, builder: Config.Builder) {
|
||||
private fun deserializeReportGeneration(map: Map<String, Any>?, language: String?, builder: Config.Builder) {
|
||||
if (map == null) return
|
||||
builder.evaluationTitle = map.handleEnv("evaluationTitle")
|
||||
if (map.containsKey("defaultMetrics")) {
|
||||
@@ -132,7 +173,7 @@ object ConfigFactory {
|
||||
val filters = mutableListOf<SessionsFilter>()
|
||||
filtersList.forEach {
|
||||
val name = it.getAs<String>("name")
|
||||
filters.add(SessionsFilter(name, EvaluationFilterReader.readFilters(it, language)))
|
||||
filters.add(SessionsFilter(name, EvaluationFilterReader.readFilters(it, language ?: NO_LANGUAGE)))
|
||||
}
|
||||
builder.mergeFilters(filters)
|
||||
val comparisonFiltersList = map.getAs<List<Map<String, Any>>>("comparisonFilters")
|
||||
@@ -144,6 +185,7 @@ object ConfigFactory {
|
||||
}
|
||||
|
||||
private fun Map<String, *>.handleEnv(key: String): String = StrSubstitutor.replaceSystemProperties(getAs(key))
|
||||
private fun String.handleEnv(): String = StrSubstitutor.replaceSystemProperties(this)
|
||||
|
||||
private class SessionFiltersSerializer : JsonSerializer<SessionsFilter> {
|
||||
override fun serialize(src: SessionsFilter, typeOfSrc: Type, context: JsonSerializationContext): JsonObject {
|
||||
|
||||
@@ -6,9 +6,8 @@ import com.intellij.cce.evaluable.EvaluationStrategy
|
||||
import com.intellij.cce.evaluable.StrategySerializer
|
||||
import com.intellij.cce.fus.FusLogsSaver
|
||||
import com.intellij.cce.workspace.storages.*
|
||||
import com.intellij.cce.workspace.storages.storage.ActionsStorage
|
||||
import com.intellij.cce.workspace.storages.storage.ActionsStorageFactory
|
||||
import com.intellij.cce.workspace.storages.storage.getActionsStorageTypeFromEnv
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.io.createDirectories
|
||||
import java.io.FileWriter
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@@ -27,8 +26,13 @@ class EvaluationWorkspace private constructor(private val basePath: Path,
|
||||
return EvaluationWorkspace(Paths.get(workspaceDir).toAbsolutePath(), statsLogsPath)
|
||||
}
|
||||
|
||||
fun create(config: Config, statsLogsPath: Path): EvaluationWorkspace {
|
||||
val workspace = EvaluationWorkspace(Paths.get(config.outputDir).toAbsolutePath().resolve(formatter.format(Date())), statsLogsPath)
|
||||
fun create(config: Config, statsLogsPath: Path, debug: Boolean = false): EvaluationWorkspace {
|
||||
val path = Paths.get(config.outputDir).toAbsolutePath()
|
||||
.resolve(if (debug) "debug" else formatter.format(Date()))
|
||||
if (debug) {
|
||||
FileUtil.deleteRecursively(path)
|
||||
}
|
||||
val workspace = EvaluationWorkspace(path, statsLogsPath)
|
||||
workspace.writeConfig(config)
|
||||
return workspace
|
||||
}
|
||||
@@ -37,7 +41,6 @@ class EvaluationWorkspace private constructor(private val basePath: Path,
|
||||
private val sessionsDir = subdir("data")
|
||||
private val fullLineLogsDir = subdir("full-line-logs")
|
||||
private val featuresDir = subdir("features")
|
||||
private val actionsDir = subdir("actions")
|
||||
private val errorsDir = subdir("errors")
|
||||
private val reportsDir = subdir("reports")
|
||||
private val pathToConfig = path().resolve(ConfigFactory.DEFAULT_CONFIG_NAME)
|
||||
@@ -45,8 +48,6 @@ class EvaluationWorkspace private constructor(private val basePath: Path,
|
||||
|
||||
val sessionsStorage: CompositeSessionsStorage = CompositeSessionsStorage(sessionsDir.toString())
|
||||
|
||||
val actionsStorage: ActionsStorage = ActionsStorageFactory.create(actionsDir.toString(), getActionsStorageTypeFromEnv())
|
||||
|
||||
val errorsStorage: FileErrorsStorage = FileErrorsStorage(errorsDir.toString())
|
||||
|
||||
val statLogsSaver: StatLogsSaver = StatLogsSaver(statsLogsPath, subdir("logs"))
|
||||
|
||||
@@ -34,7 +34,7 @@ class CompositeSessionsStorage(storageDir: String) : SessionsStorage(storageDir)
|
||||
}
|
||||
|
||||
override fun getSessions(path: String): FileSessionsInfo {
|
||||
val (projectName, filePath) = path.split(":")
|
||||
val (projectName, filePath) = path.split(":", limit = 2)
|
||||
return storages[projectName]!!.getSessions(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ package com.intellij.cce.workspace.storages
|
||||
interface LogsSaver {
|
||||
fun <T> invokeRememberingLogs(action: () -> T): T
|
||||
|
||||
fun save(languageName: String, trainingPercentage: Int)
|
||||
fun save(languageName: String?, trainingPercentage: Int)
|
||||
}
|
||||
|
||||
class NoLogsSaver : LogsSaver {
|
||||
override fun <T> invokeRememberingLogs(action: () -> T): T = action()
|
||||
|
||||
override fun save(languageName: String, trainingPercentage: Int) = Unit
|
||||
override fun save(languageName: String?, trainingPercentage: Int) = Unit
|
||||
}
|
||||
|
||||
fun logsSaverIf(condition: Boolean, createSaver: () -> LogsSaver): LogsSaver = if (condition) createSaver() else NoLogsSaver()
|
||||
@@ -23,7 +23,7 @@ private fun makeNestingCollector(saverA: LogsSaver,
|
||||
}
|
||||
}
|
||||
|
||||
override fun save(languageName: String, trainingPercentage: Int) {
|
||||
override fun save(languageName: String?, trainingPercentage: Int) {
|
||||
try {
|
||||
saverA.save(languageName, trainingPercentage)
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ class StatLogsSaver(private val logsTemporaryStoragePath: Path, private val fina
|
||||
|
||||
override fun <T> invokeRememberingLogs(action: () -> T): T = action()
|
||||
|
||||
override fun save(languageName: String, trainingPercentage: Int) {
|
||||
override fun save(languageName: String?, trainingPercentage: Int) {
|
||||
val logsDir = logsTemporaryStoragePath.toFile()
|
||||
if (!logsDir.exists()) return
|
||||
require(logsDir.isDirectory)
|
||||
val outputDir = finalStorageDir / languageName
|
||||
val outputDir = if (languageName != null) finalStorageDir / languageName else finalStorageDir
|
||||
Files.createDirectories(outputDir)
|
||||
FileWriter(Paths.get(outputDir.toString(), "full.log").toString()).use { writer ->
|
||||
for (logChunk in (logsDir.listFiles() ?: emptyArray())
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"outputDir": "ml-eval-standalone-example-output",
|
||||
"strategy": {},
|
||||
"fileDataset": {
|
||||
"url": "./latin_letters.csv",
|
||||
"chunkSize": 10
|
||||
},
|
||||
"interpret": {
|
||||
"saveLogs": false,
|
||||
"saveFeatures": false,
|
||||
"sessionProbability": 1.0,
|
||||
"sessionSeed": null,
|
||||
"sessionsLimit": 30,
|
||||
"filesLimit": 2,
|
||||
"order": "LINEAR",
|
||||
"trainTestSplit": 70
|
||||
},
|
||||
"reports": {
|
||||
"evaluationTitle": "Default",
|
||||
"defaultMetrics": null,
|
||||
"sessionsFilters": [],
|
||||
"comparisonFilters": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
Letter,Type
|
||||
A,Vowel
|
||||
B,Consonant
|
||||
C,Consonant
|
||||
D,Consonant
|
||||
E,Vowel
|
||||
F,Consonant
|
||||
G,Consonant
|
||||
H,Consonant
|
||||
I,Vowel
|
||||
J,Consonant
|
||||
K,Consonant
|
||||
L,Consonant
|
||||
M,Consonant
|
||||
N,Consonant
|
||||
O,Vowel
|
||||
P,Consonant
|
||||
Q,Consonant
|
||||
R,Consonant
|
||||
S,Consonant
|
||||
T,Consonant
|
||||
U,Vowel
|
||||
V,Consonant
|
||||
W,Consonant
|
||||
X,Consonant
|
||||
Y,Consonant
|
||||
Z,Consonant
|
||||
|
@@ -6,6 +6,8 @@ import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
interface EvaluationStep {
|
||||
val name: String
|
||||
val description: String
|
||||
}
|
||||
|
||||
interface ForegroundEvaluationStep : EvaluationStep {
|
||||
fun start(workspace: EvaluationWorkspace): EvaluationWorkspace?
|
||||
}
|
||||
@@ -5,4 +5,6 @@ import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
|
||||
interface FinishEvaluationStep {
|
||||
fun start(workspace: EvaluationWorkspace, withErrors: Boolean)
|
||||
|
||||
class EvaluationCompletedWithErrorsException : Exception("Evaluation completed with errors.")
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.intellij.cce.core.Language
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
abstract class SetupSdkStep : EvaluationStep {
|
||||
abstract class SetupSdkStep : ForegroundEvaluationStep {
|
||||
companion object {
|
||||
private val EP_NAME = ExtensionPointName.create<SetupSdkStep>("com.intellij.cce.setupSdkStep")
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.cce.evaluation
|
||||
|
||||
interface UndoableEvaluationStep : EvaluationStep {
|
||||
interface UndoableEvaluationStep : ForegroundEvaluationStep {
|
||||
fun undoStep(): UndoStep
|
||||
|
||||
interface UndoStep : EvaluationStep
|
||||
interface UndoStep : ForegroundEvaluationStep
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
<evaluableFeature implementation="com.intellij.cce.evaluable.testGeneration.TestGenerationFeature"/>
|
||||
<evaluableFeature implementation="com.intellij.cce.evaluable.completion.TokenCompletionFeature"/>
|
||||
<evaluableFeature implementation="com.intellij.cce.actions.ContextCollectionFeature"/>
|
||||
<evaluableFeature implementation="com.intellij.cce.evaluable.standaloneExample.StandaloneExampleFeature"/>
|
||||
<suggestionsProvider implementation="com.intellij.cce.evaluable.completion.DefaultCompletionProvider"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -13,12 +13,15 @@ fun <T : EvaluationStrategy> List<EvaluationWorkspace>.buildMultipleEvaluationsC
|
||||
): Config {
|
||||
val existingConfig = this.first().readConfig(strategySerializer)
|
||||
val projectPath = createTempProject()
|
||||
return Config.build(projectPath, existingConfig.language) {
|
||||
return Config.build {
|
||||
for (workspace in this@buildMultipleEvaluationsConfig) {
|
||||
val config = workspace.readConfig(strategySerializer)
|
||||
mergeFilters(config.reports.sessionsFilters)
|
||||
mergeComparisonFilters(config.reports.comparisonFilters)
|
||||
}
|
||||
actions = existingConfig.actions?.copy(
|
||||
projectPath = projectPath
|
||||
)
|
||||
strategy = existingConfig.strategy
|
||||
outputDir = existingConfig.outputDir
|
||||
title?.let { evaluationTitle = title }
|
||||
|
||||
@@ -14,17 +14,16 @@ import com.intellij.cce.evaluable.EvaluationStrategy
|
||||
import com.intellij.cce.evaluable.StrategySerializer
|
||||
import com.intellij.cce.evaluation.BackgroundStepFactory
|
||||
import com.intellij.cce.evaluation.EvaluationProcess
|
||||
import com.intellij.cce.evaluation.EvaluationRootInfo
|
||||
import com.intellij.cce.evaluation.FinishEvaluationStep
|
||||
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
|
||||
import com.intellij.cce.evaluation.step.runInIntellij
|
||||
import com.intellij.cce.util.ExceptionsUtil.stackTraceToString
|
||||
import com.intellij.cce.workspace.ConfigFactory
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.ide.impl.runUnderModalProgressIfIsEdt
|
||||
import com.intellij.openapi.application.ApplicationStarter
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.application.ex.ApplicationEx.FORCE_EXIT
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||
import com.intellij.platform.ide.bootstrap.commandNameFromExtension
|
||||
import com.intellij.warmup.util.importOrOpenProjectAsync
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.exists
|
||||
@@ -36,7 +35,8 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
get() = ApplicationStarter.NOT_IN_EDT
|
||||
|
||||
override fun main(args: List<String>) {
|
||||
MainEvaluationCommand()
|
||||
|
||||
fun run() = MainEvaluationCommand()
|
||||
.subcommands(
|
||||
FullCommand(),
|
||||
GenerateActionsCommand(),
|
||||
@@ -47,6 +47,23 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
ContextCollectionEvaluationCommand()
|
||||
)
|
||||
.main(args.toList().subList(1, args.size))
|
||||
|
||||
|
||||
val startTimestamp = System.currentTimeMillis()
|
||||
try {
|
||||
run()
|
||||
val delta = 5_000 - (System.currentTimeMillis() - startTimestamp)
|
||||
if (delta > 0) {
|
||||
Thread.sleep(delta) // for graceful shutdown
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
catch (e: FinishEvaluationStep.EvaluationCompletedWithErrorsException) {
|
||||
fatalError(e.message!!)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
fatalError("Evaluation failed $e. StackTrace: ${stackTraceToString(e)}")
|
||||
}
|
||||
}
|
||||
|
||||
abstract class EvaluationCommand(name: String, help: String) : CliktCommand(name = name, help = help) {
|
||||
@@ -66,37 +83,9 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
protected fun runPreliminarySteps(feature: EvaluableFeature<*>, workspace: EvaluationWorkspace) {
|
||||
for (step in feature.getPreliminaryEvaluationSteps()) {
|
||||
println("Starting preliminary step: ${step.name}")
|
||||
step.start(workspace)
|
||||
step.runInIntellij(null, workspace)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun loadAndApply(projectPath: String, action: (Project) -> Unit) {
|
||||
val project: Project?
|
||||
|
||||
try {
|
||||
println("Open and load project $projectPath. Operation may take a few minutes.")
|
||||
@Suppress("SSBasedInspection")
|
||||
project = runUnderModalProgressIfIsEdt {
|
||||
importOrOpenProjectAsync(OpenProjectArgsData(FileSystems.getDefault().getPath(projectPath)))
|
||||
}
|
||||
println("Project loaded!")
|
||||
|
||||
try {
|
||||
action(project)
|
||||
}
|
||||
catch (exception: Exception) {
|
||||
throw RuntimeException("Failed to run actions on the project: $exception", exception)
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
fatalError("Project could not be loaded or processed: $e. StackTrace: ${stackTraceToString(e)}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fatalError(msg: String): Nothing {
|
||||
System.err.println("Evaluation failed: $msg")
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
inner class MainEvaluationCommand : EvaluationCommand(commandNameFromExtension!!, "Evaluate code completion quality in headless mode") {
|
||||
@@ -111,13 +100,14 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the $featureName")
|
||||
val config = loadConfig(Paths.get(configPath), feature.getStrategySerializer())
|
||||
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
val datasetContext = DatasetContext(workspace, workspace, configPath)
|
||||
runPreliminarySteps(feature, workspace)
|
||||
loadAndApply(config.projectPath) { project ->
|
||||
val stepFactory = BackgroundStepFactory(feature, config, project, null, EvaluationRootInfo(true))
|
||||
EvaluationProcess.build({
|
||||
customize()
|
||||
shouldReorderElements = config.reorder.useReordering
|
||||
}, stepFactory).startAsync(workspace).get()
|
||||
feature.prepareEnvironment(config).use { environment ->
|
||||
val stepFactory = BackgroundStepFactory(feature, config, environment, null, datasetContext)
|
||||
EvaluationProcess.build(environment, stepFactory) {
|
||||
customize()
|
||||
shouldReorderElements = config.reorder.useReordering
|
||||
}.start(workspace)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,16 +140,18 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
override fun run() {
|
||||
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
|
||||
val workspace = EvaluationWorkspace.open(workspacePath, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
val datasetContext = DatasetContext(workspace, workspace, null)
|
||||
val config = workspace.readConfig(feature.getStrategySerializer())
|
||||
runPreliminarySteps(feature, workspace)
|
||||
loadAndApply(config.projectPath) { project ->
|
||||
val process = EvaluationProcess.build({
|
||||
shouldGenerateActions = false
|
||||
shouldInterpretActions = interpretActions
|
||||
shouldReorderElements = reorderElements
|
||||
shouldGenerateReports = generateReport
|
||||
}, BackgroundStepFactory(feature, config, project, null, EvaluationRootInfo(true)))
|
||||
process.startAsync(workspace)
|
||||
feature.prepareEnvironment(config).use { environment ->
|
||||
val stepFactory = BackgroundStepFactory(feature, config, environment, null, datasetContext)
|
||||
val process = EvaluationProcess.build(environment, stepFactory) {
|
||||
shouldGenerateActions = false
|
||||
shouldInterpretActions = interpretActions
|
||||
shouldReorderElements = reorderElements
|
||||
shouldGenerateReports = generateReport
|
||||
}
|
||||
process.start(workspace)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,13 +168,13 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
"COMPARING",
|
||||
)
|
||||
val outputWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
loadAndApply(config.projectPath) { project ->
|
||||
val process = EvaluationProcess.build({
|
||||
shouldGenerateReports = true
|
||||
},
|
||||
BackgroundStepFactory(feature, config, project, workspacesToCompare,
|
||||
EvaluationRootInfo(true)))
|
||||
process.startAsync(outputWorkspace)
|
||||
val datasetContext = DatasetContext(outputWorkspace, null, null)
|
||||
feature.prepareEnvironment(config).use { environment ->
|
||||
val stepFactory = BackgroundStepFactory(feature, config, environment, workspacesToCompare, datasetContext)
|
||||
val process = EvaluationProcess.build(environment, stepFactory) {
|
||||
shouldGenerateReports = true
|
||||
}
|
||||
process.start(outputWorkspace)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +204,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
feature.getStrategySerializer()
|
||||
)
|
||||
val outputWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
val datasetContext = DatasetContext(outputWorkspace, null, null)
|
||||
for (workspacePath in workspacesToMerge) {
|
||||
val workspace = EvaluationWorkspace.open(workspacePath, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
val sessionFiles = workspace.sessionsStorage.getSessionFiles()
|
||||
@@ -220,12 +213,12 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
|
||||
}
|
||||
}
|
||||
outputWorkspace.saveMetadata()
|
||||
loadAndApply(config.projectPath) { project ->
|
||||
val process = EvaluationProcess.build({
|
||||
shouldGenerateReports = true
|
||||
},
|
||||
BackgroundStepFactory(feature, config, project, null, EvaluationRootInfo(true)))
|
||||
process.startAsync(outputWorkspace)
|
||||
feature.prepareEnvironment(config).use { environment ->
|
||||
val stepFactory = BackgroundStepFactory(feature, config, environment, null, datasetContext)
|
||||
val process = EvaluationProcess.build(environment, stepFactory) {
|
||||
shouldGenerateReports = true
|
||||
}
|
||||
process.start(outputWorkspace)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,3 +239,15 @@ private fun readWorkspacesFromDirectory(directory: String): List<String> {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun exit(exitCode: Int): Nothing = try {
|
||||
ApplicationManagerEx.getApplicationEx().exit(FORCE_EXIT, exitCode)
|
||||
throw IllegalStateException("Process should be finished!!!")
|
||||
} catch (t: Throwable) {
|
||||
exitProcess(exitCode)
|
||||
}
|
||||
|
||||
private fun fatalError(msg: String): Nothing {
|
||||
System.err.println("Evaluation failed: $msg")
|
||||
exit(1)
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import com.intellij.cce.evaluable.StrategySerializer
|
||||
import com.intellij.cce.evaluable.common.getEditorSafe
|
||||
import com.intellij.cce.evaluable.completion.BaseCompletionActionsInvoker
|
||||
import com.intellij.cce.evaluation.*
|
||||
import com.intellij.cce.evaluation.step.ActionsGenerationStep
|
||||
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
|
||||
import com.intellij.cce.filter.EvaluationFilter
|
||||
import com.intellij.cce.filter.EvaluationFilterReader
|
||||
@@ -58,44 +57,52 @@ internal class ContextCollectionEvaluationCommand : CompletionEvaluationStarter.
|
||||
val config = loadConfig(Paths.get(configPath), feature.getStrategySerializer())
|
||||
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
val evaluationRootInfo = EvaluationRootInfo(true)
|
||||
loadAndApply(config.projectPath) { project ->
|
||||
val stepFactory = object : StepFactory by BackgroundStepFactory(
|
||||
feature = feature,
|
||||
config = config,
|
||||
project = project,
|
||||
inputWorkspacePaths = null,
|
||||
evaluationRootInfo = evaluationRootInfo
|
||||
feature.prepareEnvironment(config).use { environment ->
|
||||
val dataset = environment.dataset
|
||||
|
||||
check(dataset is ProjectActionsDataset)
|
||||
|
||||
val actions = dataset.config
|
||||
val newDataset = object : ProjectActionsDataset(
|
||||
config.strategy,
|
||||
actions,
|
||||
config.interpret.filesLimit,
|
||||
config.interpret.sessionsLimit,
|
||||
evaluationRootInfo,
|
||||
dataset.project,
|
||||
dataset.processor,
|
||||
feature.name
|
||||
) {
|
||||
override fun generateActionsStep(): EvaluationStep {
|
||||
return object : ActionsGenerationStep(
|
||||
config = config,
|
||||
language = config.language,
|
||||
evaluationRootInfo = evaluationRootInfo,
|
||||
project = project,
|
||||
processor = feature.getGenerateActionsProcessor(config.strategy),
|
||||
featureName = feature.name
|
||||
) {
|
||||
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
|
||||
val files = runReadAction {
|
||||
FilesHelper.getFilesOfLanguage(project, config.actions.evaluationRoots, config.actions.ignoreFileNames, language)
|
||||
}.sortedBy { it.name }
|
||||
val strategy = config.strategy as CompletionContextCollectionStrategy
|
||||
val sampled = files.shuffled(Random(strategy.samplingSeed)).take(strategy.samplesCount).sortedBy { it.name }
|
||||
generateActions(workspace, language, sampled, evaluationRootInfo, progress)
|
||||
return workspace
|
||||
}
|
||||
}
|
||||
override fun prepare(datasetContext: DatasetContext, progress: Progress) {
|
||||
val files = runReadAction {
|
||||
FilesHelper.getFilesOfLanguage(project, actions.evaluationRoots, actions.ignoreFileNames, actions.language)
|
||||
}.sortedBy { it.name }
|
||||
val strategy = config.strategy as CompletionContextCollectionStrategy
|
||||
val sampled = files.shuffled(Random(strategy.samplingSeed)).take(strategy.samplesCount).sortedBy { it.name }
|
||||
generateActions(datasetContext, actions.language, sampled, evaluationRootInfo, progress)
|
||||
}
|
||||
}
|
||||
val process = EvaluationProcess.build(
|
||||
init = {
|
||||
shouldGenerateActions = true
|
||||
shouldInterpretActions = true
|
||||
shouldGenerateReports = false
|
||||
shouldReorderElements = config.reorder.useReordering
|
||||
},
|
||||
stepFactory = stepFactory
|
||||
|
||||
val newEnvironment = object : EvaluationEnvironment by environment {
|
||||
override val dataset: EvaluationDataset = newDataset
|
||||
}
|
||||
|
||||
val datasetContext = DatasetContext(workspace, workspace, null)
|
||||
|
||||
val stepFactory = BackgroundStepFactory(
|
||||
feature = feature,
|
||||
environment = environment,
|
||||
config = config,
|
||||
inputWorkspacePaths = null,
|
||||
datasetContext = datasetContext
|
||||
)
|
||||
|
||||
val process = EvaluationProcess.build(newEnvironment, stepFactory) {
|
||||
shouldGenerateActions = true
|
||||
shouldInterpretActions = true
|
||||
shouldGenerateReports = false
|
||||
shouldReorderElements = config.reorder.useReordering
|
||||
}
|
||||
process.start(workspace)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.intellij.cce.actions
|
||||
|
||||
import com.intellij.cce.core.Session
|
||||
import com.intellij.cce.core.SimpleTokenProperties
|
||||
import com.intellij.cce.core.SymbolLocation
|
||||
import com.intellij.cce.core.TypeProperty
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.interpreter.*
|
||||
import com.intellij.cce.util.Progress
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.readLines
|
||||
|
||||
class CsvDataset(
|
||||
private val datasetRef: DatasetRef,
|
||||
private val chunkSize: Int,
|
||||
private val targetField: String
|
||||
) : EvaluationDataset {
|
||||
override val setupSdk: EvaluationStep? = null
|
||||
override val checkSdk: EvaluationStep? = null
|
||||
|
||||
override val preparationDescription: String = "Checking that CSV file exists"
|
||||
|
||||
override fun prepare(datasetContext: DatasetContext, progress: Progress) {
|
||||
val datasetPath = datasetContext.path(datasetRef)
|
||||
|
||||
require(datasetPath.extension == "csv") {
|
||||
"Csv dataset should have the appropriate extension: $datasetRef"
|
||||
}
|
||||
|
||||
datasetRef.prepare(datasetContext)
|
||||
|
||||
check(datasetPath.isRegularFile()) {
|
||||
"$datasetRef didn't create a file: $datasetPath"
|
||||
}
|
||||
}
|
||||
|
||||
override fun sessionCount(datasetContext: DatasetContext): Int = datasetContext.path(datasetRef).readLines().size - 1
|
||||
|
||||
override fun chunks(datasetContext: DatasetContext): Iterator<EvaluationDatasetChunk> {
|
||||
val lines = datasetContext.path(datasetRef).readLines()
|
||||
val dataLines = lines.subList(1, lines.size)
|
||||
val names = lines.first().split(',').map { it.trim() }
|
||||
|
||||
var offset = 0
|
||||
val result = mutableListOf<EvaluationDatasetChunk>()
|
||||
for (rows in dataLines.chunked(chunkSize)) {
|
||||
val presentationText = StringBuilder()
|
||||
val calls = mutableListOf<CallFeature>()
|
||||
for (row in rows) {
|
||||
if (presentationText.isNotBlank()) {
|
||||
presentationText.append("\n")
|
||||
}
|
||||
|
||||
val values = names.zip(row.split(',').map { it.trim() }).toMap()
|
||||
val features = values.filterNot { it.key == targetField }
|
||||
val target = values[targetField]!!
|
||||
|
||||
calls += callFeature(target, presentationText.length, features)
|
||||
|
||||
presentationText.append("$target <- ${features.toList().joinToString(", ") { "${it.first} = ${it.second}" }}")
|
||||
|
||||
offset += 1
|
||||
}
|
||||
|
||||
result += object : EvaluationDatasetChunk {
|
||||
override val datasetName: String = this@CsvDataset.datasetRef.name
|
||||
override val name: String = "$datasetName:${offset - rows.size + 1}-${offset}"
|
||||
override val sessionCount: Int = calls.size
|
||||
override val presentationText: String = presentationText.toString()
|
||||
|
||||
override fun evaluate(
|
||||
featureInvoker: FeatureInvoker,
|
||||
handler: InterpretationHandler,
|
||||
filter: InterpretFilter,
|
||||
order: InterpretationOrder,
|
||||
sessionHandler: (Session) -> Unit
|
||||
): List<Session> {
|
||||
val sessions = mutableListOf<Session>()
|
||||
|
||||
for (call in calls.reorder(order)) {
|
||||
if (!filter.shouldCompleteToken()) {
|
||||
continue
|
||||
}
|
||||
|
||||
handler.onActionStarted(call)
|
||||
val session = featureInvoker.callFeature(call.expectedText, call.offset, call.nodeProperties)
|
||||
sessions += session
|
||||
sessionHandler(session)
|
||||
if (handler.onSessionFinished(name)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
handler.onFileProcessed(name)
|
||||
|
||||
return sessions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.iterator()
|
||||
}
|
||||
|
||||
private fun callFeature(target: String, offset: Int, features: Map<String, String>): CallFeature {
|
||||
val actions = ActionsBuilder().run {
|
||||
session {
|
||||
val properties = SimpleTokenProperties.create(TypeProperty.UNKNOWN, SymbolLocation.UNKNOWN) {
|
||||
features.forEach { put(it.key, it.value) }
|
||||
}
|
||||
callFeature(target, offset, properties)
|
||||
}
|
||||
|
||||
build()
|
||||
}
|
||||
|
||||
val action = actions.first()
|
||||
|
||||
check(action is CallFeature)
|
||||
|
||||
return action
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.intellij.cce.actions
|
||||
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.cce.workspace.storages.FileErrorsStorage
|
||||
import com.intellij.cce.workspace.storages.storage.ActionsStorage
|
||||
import com.intellij.cce.workspace.storages.storage.ActionsStorageFactory
|
||||
import com.intellij.cce.workspace.storages.storage.getActionsStorageTypeFromEnv
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
* Provides a context for working with datasets.
|
||||
* It is supposed to have everything needed to generate and store data.
|
||||
*/
|
||||
class DatasetContext(
|
||||
private val outputWorkspace: EvaluationWorkspace,
|
||||
private val actionWorkspace: EvaluationWorkspace?,
|
||||
internal val configPath: String?,
|
||||
) {
|
||||
private val datasetDir = Paths.get("ml-eval-datasets")
|
||||
|
||||
val actionsStorage: ActionsStorage by lazy {
|
||||
check(actionWorkspace != null) { "It seems that workspace with actions wasn't configured" }
|
||||
|
||||
val directory = actionWorkspace.path().resolve("actions")
|
||||
Files.createDirectories(directory)
|
||||
ActionsStorageFactory.create(directory.toString(), getActionsStorageTypeFromEnv())
|
||||
}
|
||||
|
||||
|
||||
val errorsStorage: FileErrorsStorage = outputWorkspace.errorsStorage
|
||||
|
||||
fun saveAdditionalStats(name: String, stats: Map<String, Any>) {
|
||||
outputWorkspace.saveAdditionalStats(name, stats)
|
||||
}
|
||||
|
||||
fun path(name: String): Path {
|
||||
Files.createDirectories(datasetDir)
|
||||
return datasetDir.resolve(name)
|
||||
}
|
||||
|
||||
fun path(ref: DatasetRef): Path = path(ref.name)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.intellij.cce.actions
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import kotlin.io.path.exists
|
||||
|
||||
sealed interface DatasetRef {
|
||||
|
||||
val name: String
|
||||
|
||||
fun prepare(datasetContext: DatasetContext)
|
||||
|
||||
companion object {
|
||||
private const val EXISTING_PROTOCOL = "existing:"
|
||||
|
||||
fun parse(ref: String): DatasetRef {
|
||||
if (ref.startsWith(EXISTING_PROTOCOL)) {
|
||||
return ExistingRef(ref.substring(EXISTING_PROTOCOL.length))
|
||||
}
|
||||
|
||||
if (ref.contains(":")) {
|
||||
throw IllegalArgumentException("Protocol is not supported: $ref")
|
||||
}
|
||||
|
||||
return ConfigRelativeRef(ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal data class ConfigRelativeRef(val relativePath: String) : DatasetRef {
|
||||
override val name: String = Path.of(relativePath).normalize().toString()
|
||||
|
||||
override fun prepare(datasetContext: DatasetContext) {
|
||||
val targetPath = datasetContext.path(this)
|
||||
|
||||
if (targetPath.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
val configPath = checkNotNull(datasetContext.configPath) {
|
||||
"Path $relativePath supposed to be relative to config, but there is no config explicitly provided. " +
|
||||
"Note that this option is only for test purposes and not supposed to be used in production."
|
||||
}
|
||||
|
||||
val sourcePath = Path.of(configPath).parent.resolve(relativePath)
|
||||
|
||||
check(sourcePath.exists()) {
|
||||
"Config-relative path $relativePath does not exist: $sourcePath"
|
||||
}
|
||||
|
||||
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
}
|
||||
|
||||
internal data class ExistingRef(override val name: String) : DatasetRef {
|
||||
override fun prepare(datasetContext: DatasetContext) {
|
||||
val path = datasetContext.path(name)
|
||||
|
||||
check(path.exists()) {
|
||||
"Dataset $name does not exist: $path"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.intellij.cce.actions
|
||||
|
||||
import com.intellij.cce.core.Session
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.interpreter.InterpretFilter
|
||||
import com.intellij.cce.interpreter.InterpretationHandler
|
||||
import com.intellij.cce.interpreter.InterpretationOrder
|
||||
import com.intellij.cce.util.Progress
|
||||
|
||||
/**
|
||||
* Represents data which will be used for evaluation.
|
||||
*/
|
||||
interface EvaluationDataset {
|
||||
val setupSdk: EvaluationStep?
|
||||
val checkSdk: EvaluationStep?
|
||||
|
||||
val preparationDescription: String
|
||||
|
||||
fun prepare(datasetContext: DatasetContext, progress: Progress)
|
||||
|
||||
fun sessionCount(datasetContext: DatasetContext): Int
|
||||
|
||||
// TODO should return something closeable for large files
|
||||
fun chunks(datasetContext: DatasetContext): Iterator<EvaluationDatasetChunk>
|
||||
}
|
||||
|
||||
interface EvaluationDatasetChunk {
|
||||
val datasetName: String
|
||||
val name: String
|
||||
val sessionCount: Int
|
||||
val presentationText: String
|
||||
|
||||
fun evaluate(
|
||||
featureInvoker: FeatureInvoker,
|
||||
handler: InterpretationHandler,
|
||||
filter: InterpretFilter,
|
||||
order: InterpretationOrder,
|
||||
sessionHandler: (Session) -> Unit
|
||||
): List<Session>
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
// 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.cce.evaluation.step
|
||||
package com.intellij.cce.actions
|
||||
|
||||
import com.intellij.cce.actions.ActionsGenerator
|
||||
import com.intellij.cce.actions.CallFeature
|
||||
import com.intellij.cce.actions.FileActions
|
||||
import com.intellij.cce.core.JvmProperties
|
||||
import com.intellij.cce.core.PropertyAdapters
|
||||
import com.intellij.cce.core.SymbolLocation
|
||||
import com.intellij.cce.core.TokenProperties
|
||||
import com.intellij.cce.core.*
|
||||
import com.intellij.cce.evaluable.EvaluationStrategy
|
||||
import com.intellij.cce.evaluable.common.CommonActionsInvoker
|
||||
import com.intellij.cce.evaluation.EvaluationRootInfo
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.evaluation.SetupSdkStep
|
||||
import com.intellij.cce.evaluation.step.CheckProjectSdkStep
|
||||
import com.intellij.cce.interpreter.*
|
||||
import com.intellij.cce.processor.DefaultEvaluationRootProcessor
|
||||
import com.intellij.cce.processor.EvaluationRootByRangeProcessor
|
||||
import com.intellij.cce.processor.GenerateActionsProcessor
|
||||
@@ -16,44 +16,59 @@ import com.intellij.cce.util.ExceptionsUtil.stackTraceToString
|
||||
import com.intellij.cce.util.FilesHelper
|
||||
import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.util.Summary
|
||||
import com.intellij.cce.util.text
|
||||
import com.intellij.cce.visitor.CodeFragmentBuilder
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.cce.workspace.info.FileErrorInfo
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import kotlin.random.Random
|
||||
|
||||
open class ActionsGenerationStep(
|
||||
protected val config: Config,
|
||||
protected val language: String,
|
||||
protected val evaluationRootInfo: EvaluationRootInfo,
|
||||
project: Project,
|
||||
protected val processor: GenerateActionsProcessor,
|
||||
protected val featureName: String
|
||||
) : BackgroundEvaluationStep(project) {
|
||||
override val name: String = "Generating actions"
|
||||
open class ProjectActionsDataset(
|
||||
private val strategy: EvaluationStrategy,
|
||||
val config: Config.ActionsGeneration,
|
||||
private val filesLimit: Int?, // TODO dataset generation could be lazy
|
||||
private val sessionsLimit: Int?,
|
||||
private val evaluationRootInfo: EvaluationRootInfo,
|
||||
val project: Project,
|
||||
val processor: GenerateActionsProcessor,
|
||||
private val featureName: String
|
||||
) : EvaluationDataset {
|
||||
override val setupSdk: EvaluationStep? = SetupSdkStep.forLanguage(project, Language.resolve(config.language))
|
||||
override val checkSdk: EvaluationStep? = CheckProjectSdkStep(project, config.language)
|
||||
|
||||
override val description: String = "Generating actions by selected files"
|
||||
override val preparationDescription: String = "Generating actions by selected files"
|
||||
|
||||
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
|
||||
override fun prepare(datasetContext: DatasetContext, progress: Progress) {
|
||||
val filesForEvaluation = ReadAction.compute<List<VirtualFile>, Throwable> {
|
||||
FilesHelper.getFilesOfLanguage(project, config.actions.evaluationRoots, config.actions.ignoreFileNames, language)
|
||||
FilesHelper.getFilesOfLanguage(project, config.evaluationRoots, config.ignoreFileNames, config.language)
|
||||
}
|
||||
generateActions(
|
||||
workspace,
|
||||
language,
|
||||
datasetContext,
|
||||
config.language,
|
||||
filesForEvaluation,
|
||||
evaluationRootInfo,
|
||||
progress,
|
||||
filesLimit = config.interpret.filesLimit ?: Int.MAX_VALUE,
|
||||
sessionsLimit = config.interpret.sessionsLimit ?: Int.MAX_VALUE,)
|
||||
return workspace
|
||||
filesLimit = this.filesLimit ?: Int.MAX_VALUE,
|
||||
sessionsLimit = this.sessionsLimit ?: Int.MAX_VALUE,
|
||||
)
|
||||
}
|
||||
|
||||
override fun sessionCount(datasetContext: DatasetContext): Int = datasetContext.actionsStorage.computeSessionsCount()
|
||||
|
||||
override fun chunks(datasetContext: DatasetContext): Iterator<EvaluationDatasetChunk> {
|
||||
val files = datasetContext.actionsStorage.getActionFiles()
|
||||
return files.shuffled(FILES_RANDOM).asSequence().map { file ->
|
||||
val fileActions = datasetContext.actionsStorage.getActions(file)
|
||||
val fileText = FilesHelper.getFile(project, fileActions.path).text()
|
||||
FileActionsChunk(fileActions, fileText)
|
||||
}.iterator()
|
||||
}
|
||||
|
||||
protected fun generateActions(
|
||||
workspace: EvaluationWorkspace,
|
||||
datasetContext: DatasetContext,
|
||||
languageName: String,
|
||||
files: Collection<VirtualFile>,
|
||||
evaluationRootInfo: EvaluationRootInfo,
|
||||
@@ -62,7 +77,7 @@ open class ActionsGenerationStep(
|
||||
sessionsLimit: Int = Int.MAX_VALUE,
|
||||
) {
|
||||
val actionsGenerator = ActionsGenerator(processor)
|
||||
val codeFragmentBuilder = CodeFragmentBuilder.create(project, languageName, featureName, config.strategy)
|
||||
val codeFragmentBuilder = CodeFragmentBuilder.create(project, languageName, featureName, strategy)
|
||||
|
||||
val errors = mutableListOf<FileErrorInfo>()
|
||||
var totalSessions = 0
|
||||
@@ -96,7 +111,7 @@ open class ActionsGenerationStep(
|
||||
val codeFragment = codeFragmentBuilder.build(file, rootVisitor, featureName)
|
||||
val fileActions = actionsGenerator.generate(codeFragment)
|
||||
actionsSummarizer.update(fileActions)
|
||||
workspace.actionsStorage.saveActions(fileActions)
|
||||
datasetContext.actionsStorage.saveActions(fileActions)
|
||||
totalSessions += fileActions.sessionsCount
|
||||
if (fileActions.sessionsCount > 0) {
|
||||
totalFiles++
|
||||
@@ -106,7 +121,7 @@ open class ActionsGenerationStep(
|
||||
catch (e: Throwable) {
|
||||
indicator.setProgress(filename, "error: ${e.message} | $filename", progress)
|
||||
try {
|
||||
workspace.errorsStorage.saveError(
|
||||
datasetContext.errorsStorage.saveError(
|
||||
FileErrorInfo(FilesHelper.getRelativeToProjectPath(project, file.path), e.message
|
||||
?: "No Message", stackTraceToString(e))
|
||||
)
|
||||
@@ -120,7 +135,7 @@ open class ActionsGenerationStep(
|
||||
LOG.info("Generating actions for file ${file.path} completed. Done: $i/${files.size}. With error: ${errors.size}")
|
||||
}
|
||||
|
||||
actionsSummarizer.save(workspace)
|
||||
actionsSummarizer.save(datasetContext)
|
||||
}
|
||||
|
||||
private class ActionsSummarizer {
|
||||
@@ -164,8 +179,8 @@ open class ActionsGenerationStep(
|
||||
}
|
||||
}
|
||||
|
||||
fun save(workspace: EvaluationWorkspace) {
|
||||
workspace.saveAdditionalStats("actions", rootSummary.asSerializable())
|
||||
fun save(datasetContext: DatasetContext) {
|
||||
datasetContext.saveAdditionalStats("actions", rootSummary.asSerializable())
|
||||
}
|
||||
|
||||
private enum class CompletionKind {
|
||||
@@ -181,6 +196,31 @@ open class ActionsGenerationStep(
|
||||
|
||||
private fun TokenProperties.java(): JvmProperties? = PropertyAdapters.Jvm.adapt(this)
|
||||
}
|
||||
|
||||
private inner class FileActionsChunk(
|
||||
private val fileActions: FileActions,
|
||||
override val presentationText: String,
|
||||
) : EvaluationDatasetChunk {
|
||||
override val datasetName: String = config.projectName
|
||||
override val name: String = fileActions.path
|
||||
override val sessionCount: Int = fileActions.sessionsCount
|
||||
|
||||
override fun evaluate(
|
||||
featureInvoker: FeatureInvoker,
|
||||
handler: InterpretationHandler,
|
||||
filter: InterpretFilter,
|
||||
order: InterpretationOrder,
|
||||
sessionHandler: (Session) -> Unit
|
||||
): List<Session> {
|
||||
val factory = object : InvokersFactory {
|
||||
override fun createActionsInvoker(): ActionsInvoker = CommonActionsInvoker(project)
|
||||
override fun createFeatureInvoker(): FeatureInvoker = featureInvoker
|
||||
}
|
||||
val actionInterpreter = ActionInvokingInterpreter(factory, handler, filter, order)
|
||||
return actionInterpreter.interpret(fileActions, sessionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(ProjectActionsDataset::class.java)
|
||||
private val FILES_RANDOM = Random(42)
|
||||
@@ -1,17 +1,15 @@
|
||||
// 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.cce.evaluable
|
||||
|
||||
import com.intellij.cce.core.Language
|
||||
import com.intellij.cce.evaluation.EvaluationEnvironment
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.metric.Metric
|
||||
import com.intellij.cce.processor.GenerateActionsProcessor
|
||||
import com.intellij.cce.report.FileReportGenerator
|
||||
import com.intellij.cce.report.GeneratorDirectories
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.storages.FeaturesStorage
|
||||
import com.intellij.cce.workspace.storages.FullLineLogsStorage
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
/**
|
||||
* Represents a feature that can be evaluated in IDE.
|
||||
@@ -26,14 +24,9 @@ interface EvaluableFeature<T : EvaluationStrategy> {
|
||||
fun getStrategySerializer(): StrategySerializer<T>
|
||||
|
||||
/**
|
||||
* how to prepare the context before the feature invocation
|
||||
* @return initialized environment which will be used during evaluation
|
||||
*/
|
||||
fun getGenerateActionsProcessor(strategy: T): GenerateActionsProcessor
|
||||
|
||||
/**
|
||||
* how to call the feature
|
||||
*/
|
||||
fun getFeatureInvoker(project: Project, language: Language, strategy: T): FeatureInvoker
|
||||
fun prepareEnvironment(config: Config): EvaluationEnvironment
|
||||
|
||||
/**
|
||||
* how to render the results of evaluation
|
||||
@@ -52,11 +45,11 @@ interface EvaluableFeature<T : EvaluationStrategy> {
|
||||
/**
|
||||
* additional steps to set up evaluation
|
||||
*/
|
||||
fun getEvaluationSteps(language: Language, strategy: T): List<EvaluationStep>
|
||||
fun getEvaluationSteps(config: Config): List<EvaluationStep>
|
||||
|
||||
|
||||
/**
|
||||
* additional steps to set up evaluation before project is opened
|
||||
* additional steps to set up evaluation before environment is initialized
|
||||
*/
|
||||
fun getPreliminaryEvaluationSteps(): List<EvaluationStep>
|
||||
|
||||
|
||||
@@ -1,15 +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.intellij.cce.evaluable
|
||||
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.actions.ProjectActionsDataset
|
||||
import com.intellij.cce.core.Language
|
||||
import com.intellij.cce.evaluation.*
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.processor.GenerateActionsProcessor
|
||||
import com.intellij.cce.report.BasicFileReportGenerator
|
||||
import com.intellij.cce.report.FileReportGenerator
|
||||
import com.intellij.cce.report.GeneratorDirectories
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.storages.FeaturesStorage
|
||||
import com.intellij.cce.workspace.storages.FullLineLogsStorage
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
abstract class EvaluableFeatureBase<T : EvaluationStrategy>(override val name: String) : EvaluableFeature<T> {
|
||||
|
||||
/**
|
||||
* how to prepare the context before the feature invocation
|
||||
*/
|
||||
abstract fun getGenerateActionsProcessor(strategy: T): GenerateActionsProcessor
|
||||
|
||||
/**
|
||||
* how to call the feature
|
||||
*/
|
||||
abstract fun getFeatureInvoker(project: Project, language: Language, strategy: T): FeatureInvoker
|
||||
|
||||
abstract fun getEvaluationSteps(language: Language, strategy: T): List<EvaluationStep>
|
||||
|
||||
override fun getEvaluationSteps(config: Config): List<EvaluationStep> =
|
||||
getEvaluationSteps(Language.resolve(actions(config).language), config.strategy())
|
||||
|
||||
override fun getFileReportGenerator(filterName: String,
|
||||
comparisonFilterName: String,
|
||||
featuresStorages: List<FeaturesStorage>,
|
||||
@@ -18,4 +39,27 @@ abstract class EvaluableFeatureBase<T : EvaluationStrategy>(override val name: S
|
||||
BasicFileReportGenerator(filterName, comparisonFilterName, featuresStorages, dirs)
|
||||
|
||||
override fun getPreliminaryEvaluationSteps(): List<EvaluationStep> = emptyList()
|
||||
|
||||
override fun prepareEnvironment(config: Config): EvaluationEnvironment {
|
||||
val actions = actions(config)
|
||||
val strategy = config.strategy<T>()
|
||||
return ProjectEnvironment.open(actions.projectPath) { project ->
|
||||
StandaloneEnvironment(
|
||||
dataset = ProjectActionsDataset(
|
||||
strategy,
|
||||
actions,
|
||||
config.interpret.filesLimit,
|
||||
config.interpret.sessionsLimit,
|
||||
EvaluationRootInfo(true),
|
||||
project,
|
||||
getGenerateActionsProcessor(strategy),
|
||||
name
|
||||
),
|
||||
featureInvoker = getFeatureInvoker(project, Language.resolve(actions.language), strategy)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun actions(config: Config) =
|
||||
config.actions ?: throw IllegalStateException("Configuration missing project description (actions)")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.intellij.cce.evaluable
|
||||
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.evaluation.EvaluationEnvironment
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.evaluation.StandaloneEnvironment
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.report.BasicFileReportGenerator
|
||||
import com.intellij.cce.report.FileReportGenerator
|
||||
import com.intellij.cce.report.GeneratorDirectories
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.storages.FeaturesStorage
|
||||
import com.intellij.cce.workspace.storages.FullLineLogsStorage
|
||||
|
||||
abstract class StandaloneFeature<T : EvaluationStrategy>(
|
||||
override val name: String
|
||||
) : EvaluableFeature<T> {
|
||||
|
||||
abstract fun getDataset(config: Config): EvaluationDataset
|
||||
|
||||
abstract fun getFeatureInvoker(strategy: T): FeatureInvoker
|
||||
|
||||
override fun getPreliminaryEvaluationSteps(): List<EvaluationStep> = emptyList()
|
||||
|
||||
override fun getEvaluationSteps(config: Config): List<EvaluationStep> = emptyList()
|
||||
|
||||
override fun getFileReportGenerator(
|
||||
filterName: String,
|
||||
comparisonFilterName: String,
|
||||
featuresStorages: List<FeaturesStorage>,
|
||||
fullLineStorages: List<FullLineLogsStorage>,
|
||||
dirs: GeneratorDirectories
|
||||
): FileReportGenerator = BasicFileReportGenerator(filterName, comparisonFilterName, featuresStorages, dirs)
|
||||
|
||||
override fun prepareEnvironment(config: Config): EvaluationEnvironment {
|
||||
return StandaloneEnvironment(
|
||||
getDataset(config),
|
||||
getFeatureInvoker(config.strategy())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.testFramework.TestModeFlags
|
||||
import com.intellij.util.progress.sleepCancellable
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
class CommonActionsInvoker(private val project: Project) : ActionsInvoker {
|
||||
init {
|
||||
@@ -103,24 +104,24 @@ class CommonActionsInvoker(private val project: Project) : ActionsInvoker {
|
||||
|
||||
override fun openFile(file: String): String = readActionInSmartMode(project) {
|
||||
LOG.info("Open file: $file")
|
||||
val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(File(file))
|
||||
val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(File(fullPath(file)))
|
||||
val descriptor = OpenFileDescriptor(project, virtualFile!!)
|
||||
spaceStrippingEnabled = TrailingSpacesStripper.isEnabled(virtualFile)
|
||||
TrailingSpacesStripper.setEnabled(virtualFile, false)
|
||||
val fileEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true)
|
||||
?: throw Exception("Can't open text editor for file: $file")
|
||||
?: throw Exception("Can't open text editor for file: ${fullPath(file)}")
|
||||
return@readActionInSmartMode fileEditor.document.text
|
||||
}
|
||||
|
||||
override fun closeFile(file: String) = onEdt {
|
||||
LOG.info("Close file: $file")
|
||||
val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(File(file))!!
|
||||
val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(File(fullPath(file)))!!
|
||||
TrailingSpacesStripper.setEnabled(virtualFile, spaceStrippingEnabled)
|
||||
FileEditorManager.getInstance(project).closeFile(virtualFile)
|
||||
}
|
||||
|
||||
override fun isOpen(file: String): Boolean = readActionInSmartMode(project) {
|
||||
FileEditorManager.getInstance(project).openFiles.any { it.path == file }
|
||||
FileEditorManager.getInstance(project).openFiles.any { it.path == fullPath(file) }
|
||||
}
|
||||
|
||||
override fun save() = writeAction {
|
||||
@@ -140,6 +141,8 @@ class CommonActionsInvoker(private val project: Project) : ActionsInvoker {
|
||||
action()
|
||||
}
|
||||
|
||||
private fun fullPath(subPath: String) = project.basePath?.let { Paths.get(it).resolve(subPath).toString() } ?: subPath
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<CommonActionsInvoker>()
|
||||
private const val LOG_MAX_LENGTH = 50
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.intellij.cce.evaluable.standaloneExample
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.intellij.cce.actions.CsvDataset
|
||||
import com.intellij.cce.actions.DatasetRef
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.evaluable.EvaluationStrategy
|
||||
import com.intellij.cce.evaluable.StandaloneFeature
|
||||
import com.intellij.cce.evaluable.StrategySerializer
|
||||
import com.intellij.cce.filter.EvaluationFilter
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.metric.Metric
|
||||
import com.intellij.cce.metric.PrecisionMetric
|
||||
import com.intellij.cce.metric.SessionsCountMetric
|
||||
import com.intellij.cce.workspace.Config
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class StandaloneExampleFeature : StandaloneFeature<DatasetStrategy>("standalone-example") {
|
||||
|
||||
override fun getStrategySerializer(): StrategySerializer<DatasetStrategy> = object : StrategySerializer<DatasetStrategy> {
|
||||
override fun serialize(src: DatasetStrategy, typeOfSrc: Type, context: JsonSerializationContext): JsonObject = JsonObject()
|
||||
override fun deserialize(map: Map<String, Any>, language: String): DatasetStrategy = DatasetStrategy()
|
||||
}
|
||||
|
||||
override fun getDataset(config: Config): EvaluationDataset {
|
||||
val fileDataset = config.fileDataset ?: throw IllegalStateException("Required dataset config")
|
||||
return CsvDataset(
|
||||
datasetRef = DatasetRef.parse(fileDataset.url),
|
||||
chunkSize = fileDataset.chunkSize,
|
||||
targetField = "Type"
|
||||
)
|
||||
}
|
||||
|
||||
override fun getFeatureInvoker(strategy: DatasetStrategy): FeatureInvoker = StandaloneExampleInvoker()
|
||||
|
||||
override fun getMetrics(): List<Metric> = listOf(
|
||||
SessionsCountMetric(),
|
||||
PrecisionMetric()
|
||||
)
|
||||
}
|
||||
|
||||
class DatasetStrategy : EvaluationStrategy {
|
||||
override val filters: Map<String, EvaluationFilter> = emptyMap()
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.intellij.cce.evaluable.standaloneExample
|
||||
|
||||
import com.intellij.cce.core.*
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
|
||||
class StandaloneExampleInvoker : FeatureInvoker {
|
||||
override fun callFeature(expectedText: String, offset: Int, properties: TokenProperties): Session {
|
||||
val session = Session(
|
||||
offset = offset,
|
||||
expectedText = expectedText,
|
||||
completableLength = expectedText.length,
|
||||
properties,
|
||||
)
|
||||
|
||||
val letter = properties.additionalProperty("Letter")!!.first()
|
||||
val text = if (letter.code % 2 == 1) "Vowel" else "Consonant"
|
||||
|
||||
val suggestions = listOf(
|
||||
Suggestion(
|
||||
text = text,
|
||||
presentationText = text,
|
||||
source = SuggestionSource.INTELLIJ,
|
||||
details = emptyMap(),
|
||||
isRelevant = text == expectedText
|
||||
)
|
||||
)
|
||||
|
||||
val lookup = Lookup(
|
||||
"",
|
||||
offset,
|
||||
suggestions,
|
||||
latency = 0,
|
||||
features = null,
|
||||
selectedPosition = suggestions.indexOfFirst { it.isRelevant },
|
||||
isNew = false,
|
||||
additionalInfo = emptyMap()
|
||||
)
|
||||
|
||||
session.addLookup(lookup)
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
override fun comparator(generated: String, expected: String): Boolean = generated == expected
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
// 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.cce.evaluation
|
||||
|
||||
import com.intellij.cce.actions.DatasetContext
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
|
||||
import com.intellij.cce.interpreter.*
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.interpreter.InterpretFilter
|
||||
import com.intellij.cce.interpreter.InterpretationHandlerImpl
|
||||
import com.intellij.cce.util.ExceptionsUtil
|
||||
import com.intellij.cce.util.FilesHelper
|
||||
import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.util.text
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.cce.workspace.info.FileErrorInfo
|
||||
@@ -16,16 +18,14 @@ import com.intellij.cce.workspace.storages.LogsSaver
|
||||
import com.intellij.cce.workspace.storages.asCompositeLogsSaver
|
||||
import com.intellij.cce.workspace.storages.logsSaverIf
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.random.Random
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class ActionsInterpretationHandler(
|
||||
private val config: Config,
|
||||
private val language: String,
|
||||
private val invokersFactory: InvokersFactory,
|
||||
private val project: Project) : TwoWorkspaceHandler {
|
||||
private val datasetContext: DatasetContext,
|
||||
private val featureInvoker: FeatureInvoker,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val LOG = Logger.getInstance(ActionsInterpretationHandler::class.java)
|
||||
@@ -36,44 +36,57 @@ class ActionsInterpretationHandler(
|
||||
logsSaverIf(config.interpret.saveFusLogs) { workspace.fusLogsSaver }
|
||||
).asCompositeLogsSaver()
|
||||
|
||||
override fun invoke(workspace1: EvaluationWorkspace, workspace2: EvaluationWorkspace, indicator: Progress) {
|
||||
fun invoke(dataset: EvaluationDataset, workspace: EvaluationWorkspace, indicator: Progress) {
|
||||
var sessionsCount: Int
|
||||
val computingTime = measureTimeMillis {
|
||||
sessionsCount = workspace1.actionsStorage.computeSessionsCount()
|
||||
sessionsCount = dataset.sessionCount(datasetContext)
|
||||
}
|
||||
LOG.info("Computing of sessions count took $computingTime ms")
|
||||
val interpretationConfig = config.interpret
|
||||
val logsSaver = createLogsSaver(workspace2)
|
||||
val logsSaver = createLogsSaver(workspace)
|
||||
val handler = InterpretationHandlerImpl(indicator, sessionsCount, interpretationConfig.sessionsLimit)
|
||||
val filter =
|
||||
if (interpretationConfig.sessionProbability < 1)
|
||||
RandomInterpretFilter(interpretationConfig.sessionProbability, interpretationConfig.sessionSeed)
|
||||
else InterpretFilter.default()
|
||||
val interpreter = ActionInvokingInterpreter(invokersFactory, handler, filter, config.interpret.order, project.basePath)
|
||||
.wrapLogging(logsSaver)
|
||||
val featuresStorage = if (interpretationConfig.saveFeatures) workspace2.featuresStorage else FeaturesStorage.EMPTY
|
||||
val featuresStorage = if (interpretationConfig.saveFeatures) workspace.featuresStorage else FeaturesStorage.EMPTY
|
||||
LOG.info("Start interpreting actions")
|
||||
if (interpretationConfig.sessionProbability < 1) {
|
||||
val skippedSessions = (sessionsCount * (1.0 - interpretationConfig.sessionProbability)).roundToInt()
|
||||
println("During actions interpretation will be skipped about $skippedSessions sessions")
|
||||
}
|
||||
val files = workspace1.actionsStorage.getActionFiles()
|
||||
for (file in files.shuffled(FILES_RANDOM)) {
|
||||
val fileActions = workspace1.actionsStorage.getActions(file)
|
||||
workspace2.fullLineLogsStorage.enableLogging(fileActions.path)
|
||||
var fileCount = 0
|
||||
for (chunk in dataset.chunks(datasetContext)) {
|
||||
if (config.interpret.filesLimit?.let { it <= fileCount } == true) {
|
||||
break
|
||||
}
|
||||
|
||||
workspace.fullLineLogsStorage.enableLogging(chunk.name)
|
||||
try {
|
||||
val sessions = interpreter.interpret(fileActions) { session -> featuresStorage.saveSession(session, fileActions.path) }
|
||||
val sessions = logsSaver.invokeRememberingLogs {
|
||||
chunk.evaluate(featureInvoker, handler, filter, interpretationConfig.order) { session ->
|
||||
featuresStorage.saveSession(session, chunk.name)
|
||||
}
|
||||
}
|
||||
|
||||
if (sessions.isNotEmpty()) {
|
||||
val fileText = FilesHelper.getFile(project, fileActions.path).text()
|
||||
workspace2.sessionsStorage.saveSessions(FileSessionsInfo(config.projectName, fileActions.path, fileText, sessions))
|
||||
} else {
|
||||
LOG.warn("No sessions collected from file: $file")
|
||||
val sessionsInfo = FileSessionsInfo(
|
||||
projectName = chunk.datasetName,
|
||||
filePath = chunk.name,
|
||||
text = chunk.presentationText,
|
||||
sessions = sessions
|
||||
)
|
||||
workspace.sessionsStorage.saveSessions(sessionsInfo)
|
||||
fileCount += 1
|
||||
}
|
||||
else {
|
||||
LOG.warn("No sessions collected from file: ${chunk.name}")
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
try {
|
||||
workspace2.errorsStorage.saveError(
|
||||
FileErrorInfo(fileActions.path, e.message ?: "No Message", ExceptionsUtil.stackTraceToString(e))
|
||||
workspace.errorsStorage.saveError(
|
||||
FileErrorInfo(chunk.name, e.message ?: "No Message", ExceptionsUtil.stackTraceToString(e))
|
||||
)
|
||||
}
|
||||
catch (e2: Throwable) {
|
||||
@@ -83,11 +96,9 @@ class ActionsInterpretationHandler(
|
||||
}
|
||||
if (handler.isCancelled() || handler.isLimitExceeded()) break
|
||||
}
|
||||
logsSaver.save(language, config.interpret.trainTestSplit)
|
||||
logsSaver.save(config.actions?.language, config.interpret.trainTestSplit)
|
||||
SetupStatsCollectorStep.deleteLogs()
|
||||
workspace2.saveMetadata()
|
||||
workspace.saveMetadata()
|
||||
LOG.info("Interpreting actions completed")
|
||||
}
|
||||
}
|
||||
|
||||
private val FILES_RANDOM = Random(42)
|
||||
|
||||
@@ -1,49 +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.intellij.cce.evaluation
|
||||
|
||||
import com.intellij.cce.core.Language
|
||||
import com.intellij.cce.actions.DatasetContext
|
||||
import com.intellij.cce.evaluable.EvaluableFeature
|
||||
import com.intellij.cce.evaluable.EvaluationStrategy
|
||||
import com.intellij.cce.evaluable.common.CommonActionsInvoker
|
||||
import com.intellij.cce.evaluation.step.*
|
||||
import com.intellij.cce.interpreter.ActionsInvoker
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.interpreter.InvokersFactory
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
class BackgroundStepFactory(
|
||||
private val feature: EvaluableFeature<EvaluationStrategy>,
|
||||
private val config: Config,
|
||||
private val project: Project,
|
||||
private val environment: EvaluationEnvironment,
|
||||
private val inputWorkspacePaths: List<String>?,
|
||||
private val evaluationRootInfo: EvaluationRootInfo
|
||||
private val datasetContext: DatasetContext
|
||||
) : StepFactory {
|
||||
|
||||
private val invokersFactory = object : InvokersFactory {
|
||||
override fun createActionsInvoker(): ActionsInvoker = CommonActionsInvoker(project)
|
||||
override fun createFeatureInvoker(): FeatureInvoker = feature.getFeatureInvoker(project, Language.resolve(config.language), config.strategy)
|
||||
}
|
||||
|
||||
override fun generateActionsStep(): EvaluationStep {
|
||||
return ActionsGenerationStep(config, config.language, evaluationRootInfo,
|
||||
project, feature.getGenerateActionsProcessor(config.strategy), feature.name)
|
||||
}
|
||||
override fun generateActionsStep(): EvaluationStep = DatasetPreparationStep(environment.dataset, datasetContext)
|
||||
|
||||
override fun interpretActionsStep(): EvaluationStep =
|
||||
ActionsInterpretationStep(config, config.language, invokersFactory, project)
|
||||
ActionsInterpretationStep(config, environment.dataset, datasetContext, environment.featureInvoker, newWorkspace = false)
|
||||
|
||||
override fun generateReportStep(): EvaluationStep =
|
||||
ReportGenerationStep(inputWorkspacePaths?.map { EvaluationWorkspace.open(it, SetupStatsCollectorStep.statsCollectorLogsDirectory) },
|
||||
config.reports.sessionsFilters, config.reports.comparisonFilters, project, feature)
|
||||
config.reports.sessionsFilters, config.reports.comparisonFilters, feature)
|
||||
|
||||
override fun interpretActionsOnNewWorkspaceStep(): EvaluationStep =
|
||||
ActionsInterpretationOnNewWorkspaceStep(config, invokersFactory, project)
|
||||
ActionsInterpretationStep(config, environment.dataset, datasetContext, environment.featureInvoker, newWorkspace = true)
|
||||
|
||||
override fun reorderElements(): EvaluationStep =
|
||||
ReorderElementsStep(config, project)
|
||||
ReorderElementsStep(config)
|
||||
|
||||
override fun setupStatsCollectorStep(): EvaluationStep? =
|
||||
if ((config.interpret.saveLogs || config.interpret.saveFeatures || config.interpret.experimentGroup != null)
|
||||
@@ -52,14 +39,13 @@ class BackgroundStepFactory(
|
||||
SetupStatsCollectorStep(config.interpret.experimentGroup, config.interpret.logLocationAndItemText)
|
||||
else null
|
||||
|
||||
override fun setupSdkStep(): EvaluationStep? = SetupSdkStep.forLanguage(project, Language.resolve(config.language))
|
||||
override fun setupSdkStep(): EvaluationStep? = environment.dataset.setupSdk
|
||||
|
||||
override fun checkSdkConfiguredStep(): EvaluationStep = CheckProjectSdkStep(project, config.language)
|
||||
override fun checkSdkConfiguredStep(): EvaluationStep? = environment.dataset.checkSdk
|
||||
|
||||
override fun finishEvaluationStep(): FinishEvaluationStep = HeadlessFinishEvaluationStep(project)
|
||||
override fun finishEvaluationStep(): FinishEvaluationStep = HeadlessFinishEvaluationStep()
|
||||
|
||||
override fun featureSpecificSteps(): List<EvaluationStep> =
|
||||
feature.getEvaluationSteps(Language.resolve(config.language), config.strategy)
|
||||
override fun featureSpecificSteps(): List<EvaluationStep> = feature.getEvaluationSteps(config)
|
||||
|
||||
override fun featureSpecificPreliminarySteps(): List<EvaluationStep> = feature.getPreliminaryEvaluationSteps()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.intellij.cce.evaluation
|
||||
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.evaluation.step.runInIntellij
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
|
||||
/**
|
||||
* Environment represents resources needed for an evaluation.
|
||||
* For example, it can be IntelliJ project like in [ProjectEnvironment] if an evaluation needs an opened project.
|
||||
* It should be initialized before an evaluation process and closed right after finish.
|
||||
*/
|
||||
interface EvaluationEnvironment : AutoCloseable {
|
||||
val dataset: EvaluationDataset
|
||||
val featureInvoker: FeatureInvoker
|
||||
|
||||
fun execute(step: EvaluationStep, workspace: EvaluationWorkspace): EvaluationWorkspace?
|
||||
}
|
||||
|
||||
/**
|
||||
* A special type of environment which doesn't imply any associated resources and can be treated like a simple data class.
|
||||
*/
|
||||
class StandaloneEnvironment(
|
||||
override val dataset: EvaluationDataset,
|
||||
override val featureInvoker: FeatureInvoker
|
||||
) : EvaluationEnvironment {
|
||||
override fun execute(step: EvaluationStep, workspace: EvaluationWorkspace): EvaluationWorkspace? =
|
||||
step.runInIntellij(null, workspace)
|
||||
|
||||
override fun close() {
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,19 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class EvaluationProcess private constructor(private val steps: List<EvaluationStep>,
|
||||
private val finalStep: FinishEvaluationStep?) {
|
||||
class EvaluationProcess private constructor (
|
||||
private val environment: EvaluationEnvironment,
|
||||
private val steps: List<EvaluationStep>,
|
||||
private val finalStep: FinishEvaluationStep?
|
||||
) {
|
||||
companion object {
|
||||
fun build(init: Builder.() -> Unit, stepFactory: StepFactory): EvaluationProcess {
|
||||
fun build(environment: EvaluationEnvironment, stepFactory: StepFactory, init: Builder.() -> Unit): EvaluationProcess {
|
||||
val builder = Builder()
|
||||
builder.init()
|
||||
return builder.build(stepFactory)
|
||||
return builder.build(environment, stepFactory)
|
||||
}
|
||||
}
|
||||
|
||||
fun startAsync(workspace: EvaluationWorkspace) = ApplicationManager.getApplication().executeOnPooledThread {
|
||||
start(workspace)
|
||||
}
|
||||
|
||||
fun start(workspace: EvaluationWorkspace): EvaluationWorkspace {
|
||||
val stats = mutableMapOf<String, Long>()
|
||||
var currentWorkspace = workspace
|
||||
@@ -28,7 +27,7 @@ class EvaluationProcess private constructor(private val steps: List<EvaluationSt
|
||||
if (hasError && step !is UndoableEvaluationStep.UndoStep) continue
|
||||
println("Starting step: ${step.name} (${step.description})")
|
||||
val duration = measureTimeMillis {
|
||||
val result = step.start(currentWorkspace)
|
||||
val result = environment.execute(step, workspace)
|
||||
if (result == null) {
|
||||
hasError = true
|
||||
} else {
|
||||
@@ -48,7 +47,7 @@ class EvaluationProcess private constructor(private val steps: List<EvaluationSt
|
||||
var shouldGenerateReports: Boolean = false
|
||||
var shouldReorderElements: Boolean = false
|
||||
|
||||
fun build(factory: StepFactory): EvaluationProcess {
|
||||
fun build(environment: EvaluationEnvironment, factory: StepFactory): EvaluationProcess {
|
||||
val steps = mutableListOf<EvaluationStep>()
|
||||
val isTestingEnvironment = ApplicationManager.getApplication().isUnitTestMode
|
||||
|
||||
@@ -56,7 +55,9 @@ class EvaluationProcess private constructor(private val steps: List<EvaluationSt
|
||||
factory.setupSdkStep()?.let { steps.add(it) }
|
||||
|
||||
if (!Registry.`is`("evaluation.plugin.disable.sdk.check")) {
|
||||
steps.add(factory.checkSdkConfiguredStep())
|
||||
factory.checkSdkConfiguredStep()?.let {
|
||||
steps.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ class EvaluationProcess private constructor(private val steps: List<EvaluationSt
|
||||
steps.add(step.undoStep())
|
||||
}
|
||||
|
||||
return EvaluationProcess(steps, factory.finishEvaluationStep().takeIf { !isTestingEnvironment })
|
||||
return EvaluationProcess(environment, steps, factory.finishEvaluationStep().takeIf { !isTestingEnvironment })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ class FilteredSessionsStorage(private val filter: SessionsFilter, private val st
|
||||
override fun getSessions(path: String): FileSessionsInfo {
|
||||
val sessionsInfo = storage.getSessions(path)
|
||||
val filteredSessions = filter.apply(sessionsInfo.sessions)
|
||||
return FileSessionsInfo(sessionsInfo.projectName, sessionsInfo.filePath, sessionsInfo.text, filteredSessions)
|
||||
return sessionsInfo.copy(sessions = filteredSessions)
|
||||
}
|
||||
|
||||
override fun getSessionFiles(): List<Pair<String, String>> = storage.getSessionFiles()
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.intellij.cce.evaluation
|
||||
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.actions.OpenProjectArgsData
|
||||
import com.intellij.cce.actions.ProjectOpeningUtils
|
||||
import com.intellij.cce.evaluation.step.runInIntellij
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.ide.impl.runUnderModalProgressIfIsEdt
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.warmup.util.importOrOpenProjectAsync
|
||||
import java.nio.file.FileSystems
|
||||
|
||||
class ProjectEnvironment(
|
||||
val project: Project,
|
||||
override val dataset: EvaluationDataset,
|
||||
override val featureInvoker: FeatureInvoker,
|
||||
) : EvaluationEnvironment {
|
||||
|
||||
override fun execute(step: EvaluationStep, workspace: EvaluationWorkspace): EvaluationWorkspace? =
|
||||
step.runInIntellij(project, workspace)
|
||||
|
||||
override fun close() {
|
||||
ProjectOpeningUtils.closeProject(project)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun open(projectPath: String, init: (Project) -> StandaloneEnvironment): ProjectEnvironment {
|
||||
println("Open and load project $projectPath. Operation may take a few minutes.")
|
||||
@Suppress("DEPRECATION")
|
||||
val project = runUnderModalProgressIfIsEdt {
|
||||
importOrOpenProjectAsync(OpenProjectArgsData(FileSystems.getDefault().getPath(projectPath)))
|
||||
}
|
||||
println("Project loaded!")
|
||||
|
||||
val environment = try {
|
||||
init(project)
|
||||
}
|
||||
catch (exception: Exception) {
|
||||
ProjectOpeningUtils.closeProject(project)
|
||||
throw RuntimeException("Failed to initialize project environment: $exception", exception)
|
||||
}
|
||||
|
||||
return ProjectEnvironment(
|
||||
project,
|
||||
environment.dataset,
|
||||
environment.featureInvoker
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ interface StepFactory {
|
||||
fun generateReportStep(): EvaluationStep
|
||||
fun setupStatsCollectorStep(): EvaluationStep?
|
||||
fun setupSdkStep(): EvaluationStep?
|
||||
fun checkSdkConfiguredStep(): EvaluationStep
|
||||
fun checkSdkConfiguredStep(): EvaluationStep?
|
||||
fun finishEvaluationStep(): FinishEvaluationStep
|
||||
fun featureSpecificSteps(): List<EvaluationStep>
|
||||
fun featureSpecificPreliminarySteps(): List<EvaluationStep>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.cce.evaluation.step
|
||||
|
||||
import com.intellij.cce.evaluation.ActionsInterpretationHandler
|
||||
import com.intellij.cce.interpreter.InvokersFactory
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
class ActionsInterpretationOnNewWorkspaceStep(config: Config,
|
||||
invokersFactory: InvokersFactory,
|
||||
project: Project) :
|
||||
CreateWorkspaceStep(
|
||||
config,
|
||||
ActionsInterpretationHandler(config, config.language, invokersFactory, project),
|
||||
project) {
|
||||
|
||||
override val name: String = "Actions interpreting"
|
||||
|
||||
override val description: String = "Interpretation of generated actions"
|
||||
}
|
||||
@@ -1,24 +1,30 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.cce.evaluation.step
|
||||
|
||||
import com.intellij.cce.actions.DatasetContext
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.evaluation.ActionsInterpretationHandler
|
||||
import com.intellij.cce.interpreter.InvokersFactory
|
||||
import com.intellij.cce.interpreter.FeatureInvoker
|
||||
import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
class ActionsInterpretationStep(
|
||||
private val config: Config,
|
||||
private val language: String,
|
||||
private val invokersFactory: InvokersFactory,
|
||||
project: Project) : BackgroundEvaluationStep(project) {
|
||||
private val dataset: EvaluationDataset,
|
||||
private val datasetContext: DatasetContext,
|
||||
private val featureInvoker: FeatureInvoker,
|
||||
private val newWorkspace: Boolean
|
||||
) : BackgroundEvaluationStep {
|
||||
override val name: String = "Actions interpreting"
|
||||
|
||||
override val description: String = "Interpretation of generated actions"
|
||||
|
||||
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
|
||||
ActionsInterpretationHandler(config, language, invokersFactory, project).invoke(workspace, workspace, progress)
|
||||
return workspace
|
||||
val resultWorkspace =
|
||||
if (newWorkspace) EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
else workspace
|
||||
ActionsInterpretationHandler(config, datasetContext, featureInvoker).invoke(dataset, resultWorkspace, progress)
|
||||
return resultWorkspace
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
package com.intellij.cce.evaluation.step
|
||||
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.evaluation.ForegroundEvaluationStep
|
||||
import com.intellij.cce.evaluation.HeadlessEvaluationAbortHandler
|
||||
import com.intellij.cce.util.CommandLineProgress
|
||||
import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.util.TeamcityProgress
|
||||
import com.intellij.cce.util.isUnderTeamCity
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.progress.Task
|
||||
@@ -16,40 +16,42 @@ import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.concurrency.FutureResult
|
||||
|
||||
abstract class BackgroundEvaluationStep(val project: Project) : EvaluationStep {
|
||||
protected companion object {
|
||||
val LOG = Logger.getInstance(BackgroundEvaluationStep::class.java)
|
||||
}
|
||||
interface BackgroundEvaluationStep : EvaluationStep {
|
||||
fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace
|
||||
}
|
||||
|
||||
abstract fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace
|
||||
fun EvaluationStep.runInIntellij(project: Project?, workspace: EvaluationWorkspace): EvaluationWorkspace? {
|
||||
return when (this) {
|
||||
is ForegroundEvaluationStep -> start(workspace)
|
||||
is BackgroundEvaluationStep -> {
|
||||
val result = FutureResult<EvaluationWorkspace?>()
|
||||
val task = object : Task.Backgroundable(project, name, true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
createProgress(title).wrapWithProgress {
|
||||
result.set(runInBackground(workspace, it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun start(workspace: EvaluationWorkspace): EvaluationWorkspace? {
|
||||
val result = FutureResult<EvaluationWorkspace?>()
|
||||
val task = object : Task.Backgroundable(project, name, true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
createProgress(title).wrapWithProgress {
|
||||
result.set(runInBackground(workspace, it))
|
||||
override fun onCancel() {
|
||||
evaluationAbortedHandler.onCancel(this.title)
|
||||
result.set(null)
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
evaluationAbortedHandler.onError(error, this.title)
|
||||
result.set(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
evaluationAbortedHandler.onCancel(this.title)
|
||||
result.set(null)
|
||||
}
|
||||
|
||||
override fun onThrowable(error: Throwable) {
|
||||
evaluationAbortedHandler.onError(error, this.title)
|
||||
result.set(null)
|
||||
}
|
||||
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))
|
||||
return result.get()
|
||||
}
|
||||
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))
|
||||
return result.get()
|
||||
}
|
||||
|
||||
private val evaluationAbortedHandler = HeadlessEvaluationAbortHandler()
|
||||
|
||||
private fun createProgress(title: String) = when {
|
||||
isUnderTeamCity -> TeamcityProgress(title)
|
||||
else -> CommandLineProgress(title)
|
||||
else -> throw IllegalStateException("Unexpected type of `$this`")
|
||||
}
|
||||
}
|
||||
|
||||
private val evaluationAbortedHandler = HeadlessEvaluationAbortHandler()
|
||||
|
||||
private fun createProgress(title: String) = when {
|
||||
isUnderTeamCity -> TeamcityProgress(title)
|
||||
else -> CommandLineProgress(title)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
package com.intellij.cce.evaluation.step
|
||||
|
||||
import com.intellij.cce.core.Language
|
||||
import com.intellij.cce.evaluation.EvaluationStep
|
||||
import com.intellij.cce.evaluation.ForegroundEvaluationStep
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
|
||||
class CheckProjectSdkStep(private val project: Project, private val language: String) : EvaluationStep {
|
||||
class CheckProjectSdkStep(private val project: Project, private val language: String) : ForegroundEvaluationStep {
|
||||
override val name: String = "Check Project SDK"
|
||||
override val description: String = "Checks that project SDK was configured properly"
|
||||
|
||||
|
||||
@@ -5,12 +5,11 @@ import com.intellij.cce.evaluation.TwoWorkspaceHandler
|
||||
import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
abstract class CreateWorkspaceStep(
|
||||
private val config: Config,
|
||||
private val handler: TwoWorkspaceHandler,
|
||||
project: Project) : BackgroundEvaluationStep(project) {
|
||||
private val handler: TwoWorkspaceHandler
|
||||
) : BackgroundEvaluationStep {
|
||||
|
||||
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
|
||||
val newWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.intellij.cce.evaluation.step
|
||||
|
||||
import com.intellij.cce.actions.DatasetContext
|
||||
import com.intellij.cce.actions.EvaluationDataset
|
||||
import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
|
||||
class DatasetPreparationStep(
|
||||
private val dataset: EvaluationDataset,
|
||||
private val datasetContext: DatasetContext,
|
||||
) : BackgroundEvaluationStep {
|
||||
override val name: String = "Preparing dataset"
|
||||
|
||||
override val description: String = dataset.preparationDescription
|
||||
|
||||
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
|
||||
dataset.prepare(datasetContext, progress)
|
||||
return workspace
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,13 @@
|
||||
// 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.cce.evaluation.step
|
||||
|
||||
import com.intellij.cce.actions.ProjectOpeningUtils
|
||||
import com.intellij.cce.evaluation.FinishEvaluationStep
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.openapi.application.ex.ApplicationEx.FORCE_EXIT
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class HeadlessFinishEvaluationStep(private val project: Project) : FinishEvaluationStep {
|
||||
class HeadlessFinishEvaluationStep : FinishEvaluationStep {
|
||||
override fun start(workspace: EvaluationWorkspace, withErrors: Boolean) {
|
||||
if (withErrors) {
|
||||
println("Evaluation completed with errors.")
|
||||
ProjectOpeningUtils.closeProject(project)
|
||||
exit(exitCode = 1)
|
||||
throw FinishEvaluationStep.EvaluationCompletedWithErrorsException()
|
||||
} else {
|
||||
print("Evaluation completed. ")
|
||||
if (workspace.getReports().isEmpty()) {
|
||||
@@ -24,14 +17,6 @@ class HeadlessFinishEvaluationStep(private val project: Project) : FinishEvaluat
|
||||
println("Reports:")
|
||||
workspace.getReports().forEach { println("${it.key}: ${it.value}") }
|
||||
}
|
||||
ProjectOpeningUtils.closeProject(project)
|
||||
exit(exitCode = 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exit(exitCode: Int) = try {
|
||||
ApplicationManagerEx.getApplicationEx().exit(FORCE_EXIT, exitCode)
|
||||
} catch (t: Throwable) {
|
||||
exitProcess(exitCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,23 +10,29 @@ import com.intellij.cce.util.Progress
|
||||
import com.intellij.cce.workspace.Config
|
||||
import com.intellij.cce.workspace.EvaluationWorkspace
|
||||
import com.intellij.cce.workspace.FeaturesSerializer
|
||||
import com.intellij.cce.workspace.info.FileSessionsInfo
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
class ReorderElementsStep(config: Config, project: Project) :
|
||||
class ReorderElementsStep(private val config: Config) :
|
||||
CreateWorkspaceStep(
|
||||
Config.buildFromConfig(config) { evaluationTitle = config.reorder.title },
|
||||
ReorderElementsHandler(config.reorder.features),
|
||||
project) {
|
||||
ReorderElementsHandler(config.reorder.features, config.actions != null)
|
||||
) {
|
||||
|
||||
override val name: String = "Reorder elements"
|
||||
|
||||
override val description: String = "Reorder elements by features values"
|
||||
|
||||
private class ReorderElementsHandler(private val featuresForReordering: List<String>) : TwoWorkspaceHandler {
|
||||
private class ReorderElementsHandler(
|
||||
private val featuresForReordering: List<String>,
|
||||
private val isActionProjectDataset: Boolean
|
||||
) : TwoWorkspaceHandler {
|
||||
|
||||
override fun invoke(workspace1: EvaluationWorkspace, workspace2: EvaluationWorkspace, indicator: Progress) {
|
||||
if (featuresForReordering.isEmpty()) return
|
||||
|
||||
check(isActionProjectDataset) {
|
||||
"Reorder is available only for action-based dataset"
|
||||
}
|
||||
|
||||
val files = workspace1.sessionsStorage.getSessionFiles()
|
||||
for ((counter, file) in files.withIndex()) {
|
||||
indicator.setProgress(file.first, file.first, counter.toDouble() / files.size)
|
||||
@@ -54,7 +60,7 @@ class ReorderElementsStep(config: Config, project: Project) :
|
||||
workspace2.featuresStorage.saveSession(newSession, fileSessionsInfo.filePath)
|
||||
}
|
||||
workspace2.sessionsStorage.saveSessions(
|
||||
FileSessionsInfo(fileSessionsInfo.projectName, fileSessionsInfo.filePath, fileSessionsInfo.text, resultSessions)
|
||||
fileSessionsInfo.copy(sessions = resultSessions)
|
||||
)
|
||||
}
|
||||
workspace2.sessionsStorage.saveMetadata()
|
||||
|
||||
@@ -18,16 +18,15 @@ import com.intellij.cce.workspace.info.FileSessionsInfo
|
||||
import com.intellij.cce.workspace.storages.FileErrorsStorage
|
||||
import com.intellij.cce.workspace.storages.SessionsStorage
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import java.nio.file.Path
|
||||
|
||||
class ReportGenerationStep<T : EvaluationStrategy>(
|
||||
private val inputWorkspaces: List<EvaluationWorkspace>?,
|
||||
filters: List<SessionsFilter>,
|
||||
comparisonFilters: List<CompareSessionsFilter>,
|
||||
project: Project,
|
||||
private val feature: EvaluableFeature<T>
|
||||
) : BackgroundEvaluationStep(project) {
|
||||
) : BackgroundEvaluationStep {
|
||||
override val name: String = "Report generation"
|
||||
|
||||
override val description: String = "Generation of HTML-report"
|
||||
@@ -134,8 +133,8 @@ class ReportGenerationStep<T : EvaluationStrategy>(
|
||||
}
|
||||
if (sessionsInfo == null) throw IllegalStateException("Sessions file doesn't exist")
|
||||
for (file in sessionFile.value) {
|
||||
val sessionsEvaluation = FileSessionsInfo(
|
||||
sessionsInfo.projectName, sessionsInfo.filePath, sessionsInfo.text, comparisonStorage.get(file.evaluationType)
|
||||
val sessionsEvaluation = sessionsInfo.copy(
|
||||
sessions = comparisonStorage.get(file.evaluationType)
|
||||
)
|
||||
val metricsEvaluation = title2evaluator.getValue(file.evaluationType).evaluate(
|
||||
sessionsEvaluation.sessions)
|
||||
@@ -155,3 +154,5 @@ class ReportGenerationStep<T : EvaluationStrategy>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(ReportGenerationStep::class.java)
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.intellij.cce.workspace
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.intellij.cce.evaluable.EvaluationStrategy
|
||||
import com.intellij.cce.evaluable.StrategySerializer
|
||||
import com.intellij.cce.filter.EvaluationFilter
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class ConfigFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `test legacy format deserialization`() {
|
||||
|
||||
deserialize(
|
||||
"""
|
||||
{
|
||||
"outputDir": "outputDir",
|
||||
"strategy": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
).also {
|
||||
assertNull(it.actions)
|
||||
}
|
||||
|
||||
assertThrows<IllegalStateException> {
|
||||
deserialize(
|
||||
"""
|
||||
{
|
||||
"outputDir": "outputDir",
|
||||
"strategy": {},
|
||||
"language": "LANG"
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
assertThrows<IllegalStateException> {
|
||||
deserialize(
|
||||
"""
|
||||
{
|
||||
"outputDir": "outputDir",
|
||||
"strategy": {},
|
||||
"projectPath": "projectPath"
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
assertThrows<IllegalStateException> {
|
||||
deserialize(
|
||||
"""
|
||||
{
|
||||
"outputDir": "outputDir",
|
||||
"strategy": {},
|
||||
"projectName": "projectName"
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
deserialize(
|
||||
"""
|
||||
{
|
||||
"outputDir": "outputDir",
|
||||
"strategy": {},
|
||||
"language": "LANG",
|
||||
"projectPath": "projectPath",
|
||||
"projectName": "projectName",
|
||||
"actions": {
|
||||
"evaluationRoots": []
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
).also {
|
||||
assertEquals("LANG", it.actions?.language)
|
||||
assertEquals("projectPath", it.actions?.projectPath)
|
||||
assertEquals("projectName", it.actions?.projectName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserialize(text: String) =
|
||||
ConfigFactory.deserialize(ConfigFactory.createGson(TestStrategySerializer), text, TestStrategySerializer)
|
||||
|
||||
private object TestStrategy : EvaluationStrategy {
|
||||
override val filters: Map<String, EvaluationFilter> = emptyMap()
|
||||
}
|
||||
|
||||
private object TestStrategySerializer : StrategySerializer<TestStrategy> {
|
||||
override fun serialize(src: TestStrategy, typeOfSrc: Type, context: JsonSerializationContext): JsonObject = JsonObject()
|
||||
override fun deserialize(map: Map<String, Any>, language: String): TestStrategy = TestStrategy
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user