Rename Label->Version, add version meta field
This commit is contained in:
@@ -98,7 +98,7 @@ object CLI {
|
||||
sealed interface IProjectIdentification
|
||||
sealed interface ILabelIdentification
|
||||
data class ProjectName(val user: String, val project: String) : IProjectIdentification
|
||||
data class LabelName(val name: String): ILabelIdentification
|
||||
data class VersionName(val name: String): ILabelIdentification
|
||||
data class ID(val id: Uuid) : IProjectIdentification, ILabelIdentification
|
||||
|
||||
sealed interface IData<T> {
|
||||
@@ -168,22 +168,23 @@ object CLI {
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
class AddLabel : CliktCommand(name = "add-label") {
|
||||
class AddVersion : CliktCommand(name = "add-version") {
|
||||
val parent by requireObject<Project>()
|
||||
val name by option("--name", help = "Set the label's name (*)")
|
||||
val color by option("--color", help = "Set the label's color (*)").check("Color must be specified in RGB-hex-format (#ABCDEF)") {
|
||||
val name by option("--name", help = "Set the version's name (*)")
|
||||
val color by option("--color", help = "Set the version's color (*)").check("Color must be specified in RGB-hex-format (#ABCDEF)") {
|
||||
it.length == 7 && it[0] == '#' && it.substring(1, it.length).all { c -> c.isDigit() || c in "ABCDEF" }
|
||||
}
|
||||
val meta by option("--meta", help = "Add metadata to the version (*)")
|
||||
|
||||
override fun run() = ProjectHandlers.newLabel(name, color, parent.finder)
|
||||
override fun run() = ProjectHandlers.newVersion(name, color, parent.finder, meta)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
class AddData : CliktCommand(name = "add-data") {
|
||||
val parent by requireObject<Project>()
|
||||
val label by mutuallyExclusiveOptions<ILabelIdentification>(
|
||||
option("--label-id", help = "Set the label by UUID.").convert { ID(Uuid.parse(it)) },
|
||||
option("--label", help = "Set the label by name.").convert { LabelName(it) }
|
||||
val version by mutuallyExclusiveOptions<ILabelIdentification>(
|
||||
option("--version-id", help = "Set the version by UUID.").convert { ID(Uuid.parse(it)) },
|
||||
option("--version", help = "Set the version by name.").convert { VersionName(it) }
|
||||
).single()
|
||||
val warmup by mutuallyExclusiveOptions<IData<Float>>(
|
||||
option("--warmup", help = "Set warmup data directly.").float().split(",").transformAll { DirectData(it.flatten()) },
|
||||
@@ -204,7 +205,7 @@ object CLI {
|
||||
val input by option("--input", help = "Set the input used to benchmark")
|
||||
val hardware by option("--hardware", "--hw", help = "Set the hardware on which the benchmark was run")
|
||||
|
||||
override fun run() = ProjectHandlers.newData(parent.finder, label, warmup, measurement, unit)
|
||||
override fun run() = ProjectHandlers.newData(parent.finder, version, warmup, measurement, unit, input, hardware)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package com.jaytux.phoebench.clients.cli
|
||||
|
||||
import com.github.ajalt.clikt.parameters.options.convert
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import com.jaytux.phoebench.clients.cli.CLI.Commands.Project.ProjectName
|
||||
import com.jaytux.phoebench.common.ApiRoute
|
||||
import com.jaytux.phoebench.common.Either
|
||||
import com.jaytux.phoebench.common.EmptyRequest
|
||||
import com.jaytux.phoebench.common.EntryRequest
|
||||
import com.jaytux.phoebench.common.ErrorResponse
|
||||
import com.jaytux.phoebench.common.LabelRequest
|
||||
import com.jaytux.phoebench.common.LabelResponse
|
||||
import com.jaytux.phoebench.common.VersionRequest
|
||||
import com.jaytux.phoebench.common.ProjectRequest
|
||||
import com.jaytux.phoebench.common.Routes
|
||||
import com.jaytux.phoebench.common.TimeUnit
|
||||
@@ -19,9 +14,6 @@ import com.jaytux.phoebench.common.error
|
||||
import com.jaytux.phoebench.common.fold
|
||||
import com.jaytux.phoebench.common.map
|
||||
import com.jaytux.phoebench.common.value
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import java.nio.file.OpenOption
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.inputStream
|
||||
@@ -100,7 +92,7 @@ object ProjectHandlers {
|
||||
}
|
||||
}
|
||||
|
||||
fun newLabel(name: String?, color: String?, project: CLI.Commands.Project.IProjectIdentification?) {
|
||||
fun newVersion(name: String?, color: String?, project: CLI.Commands.Project.IProjectIdentification?, version: String?) {
|
||||
val id = ensureProjectIdentification(project)
|
||||
val useName = name.maybePrompt("label name") { it }
|
||||
val useColor = color.maybePrompt("color") {
|
||||
@@ -111,10 +103,11 @@ object ProjectHandlers {
|
||||
exitProcess(-1)
|
||||
}
|
||||
}
|
||||
val useVersion = version.maybePrompt("version") { it }
|
||||
|
||||
tryAuthenticated {
|
||||
id.toId().bind { projectId ->
|
||||
Client.callRoute(Routes.Label.new, LabelRequest(useName, useColor, projectId))
|
||||
Client.callRoute(Routes.Version.new, VersionRequest(useName, useColor, useVersion, projectId))
|
||||
}
|
||||
}.fold({
|
||||
System.err.println("Could not create label: ${it.msg}")
|
||||
@@ -139,11 +132,11 @@ object ProjectHandlers {
|
||||
label: CLI.Commands.Project.ILabelIdentification?,
|
||||
warmup: CLI.Commands.Project.IData<Float>?,
|
||||
measurement: CLI.Commands.Project.IData<Float>?,
|
||||
unit: TimeUnit?
|
||||
unit: TimeUnit?, input: String?, hardware: String?
|
||||
) {
|
||||
val projectId = ensureProjectIdentification(project)
|
||||
val labelId = label.maybePrompt("label name") {
|
||||
CLI.Commands.Project.LabelName(it)
|
||||
val versionId = label.maybePrompt("version name") {
|
||||
CLI.Commands.Project.VersionName(it)
|
||||
}
|
||||
val warmupData = warmup.ensure("warmup").toList()
|
||||
val measureData = measurement.ensure("measurements").toList()
|
||||
@@ -162,20 +155,22 @@ object ProjectHandlers {
|
||||
}
|
||||
}
|
||||
}
|
||||
val useInput = input.maybePrompt("benchmark input") { it }
|
||||
val useHardware = hardware.maybePrompt("hardware description") { it }
|
||||
|
||||
tryAuthenticated {
|
||||
projectId.toId().bind { pId ->
|
||||
when(labelId) {
|
||||
is CLI.Commands.Project.ID -> labelId.id.value()
|
||||
is CLI.Commands.Project.LabelName -> Client.callRoute(Routes.Project.get, pId).bind {
|
||||
it.usedLabels.firstOrNull { l -> l.name == labelId.name }?.id?.value()
|
||||
?: ErrorResponse("Label ${labelId.name} does not exist in this project.").error()
|
||||
when(versionId) {
|
||||
is CLI.Commands.Project.ID -> versionId.id.value()
|
||||
is CLI.Commands.Project.VersionName -> Client.callRoute(Routes.Project.get, pId).bind {
|
||||
it.usedLabels.firstOrNull { l -> l.name == versionId.name }?.id?.value()
|
||||
?: ErrorResponse("Label ${versionId.name} does not exist in this project.").error()
|
||||
}
|
||||
}.map { pId to it }
|
||||
}.bind { (pId, lId) ->
|
||||
}.bind { (pId, vId) ->
|
||||
Client.callRoute(Routes.Entry.new, EntryRequest(
|
||||
lId, Clock.System.now(), pId,
|
||||
warmupData, measureData, timeUnit
|
||||
vId, Clock.System.now(), pId,
|
||||
warmupData, measureData, timeUnit, useInput, useHardware
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
+10
-12
@@ -8,15 +8,13 @@ import com.jaytux.phoebench.common.Either
|
||||
import com.jaytux.phoebench.common.EntryRequest
|
||||
import com.jaytux.phoebench.common.EntryResponse
|
||||
import com.jaytux.phoebench.common.ErrorResponse
|
||||
import com.jaytux.phoebench.common.LabelRequest
|
||||
import com.jaytux.phoebench.common.LabelResponse
|
||||
import com.jaytux.phoebench.common.PartialEntryRequest
|
||||
import com.jaytux.phoebench.common.PartialLabelRequest
|
||||
import com.jaytux.phoebench.common.VersionRequest
|
||||
import com.jaytux.phoebench.common.VersionResponse
|
||||
import com.jaytux.phoebench.common.PartialProjectRequest
|
||||
import com.jaytux.phoebench.common.PartialVersionRequest
|
||||
import com.jaytux.phoebench.common.ProjectResponse
|
||||
import com.jaytux.phoebench.common.Routes
|
||||
import com.jaytux.phoebench.common.TimeUnit
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlin.time.Instant
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@@ -25,8 +23,8 @@ interface IProjectRepo {
|
||||
suspend fun update(name: String? = null, isPublic: Boolean? = null): Either<ErrorResponse, Unit>
|
||||
suspend fun delete(): Either<ErrorResponse, Unit>
|
||||
|
||||
suspend fun newLabel(name: String, color: Color): Either<ErrorResponse, LabelResponse>
|
||||
suspend fun updateLabel(id: Uuid, name: String? = null, color: Color? = null): Either<ErrorResponse, Unit>
|
||||
suspend fun newVersion(name: String, color: Color, meta: String): Either<ErrorResponse, VersionResponse>
|
||||
suspend fun updateVersion(id: Uuid, name: String? = null, color: Color? = null, meta: String? = null): Either<ErrorResponse, Unit>
|
||||
suspend fun deleteLabel(id: Uuid): Either<ErrorResponse, Unit>
|
||||
|
||||
suspend fun newEntry(label: Uuid, timestamp: Instant, warmups: List<Float>, measurements: List<Float>,
|
||||
@@ -44,14 +42,14 @@ interface IProjectRepo {
|
||||
override suspend fun delete(): Either<ErrorResponse, Unit> =
|
||||
_client.callRoute(Routes.Project.delete, _projectId).ignoreValue()
|
||||
|
||||
override suspend fun newLabel(name: String, color: Color): Either<ErrorResponse, LabelResponse> =
|
||||
_client.callRoute(Routes.Label.new, LabelRequest(name, color.hexString(), _projectId))
|
||||
override suspend fun newVersion(name: String, color: Color, meta: String): Either<ErrorResponse, VersionResponse> =
|
||||
_client.callRoute(Routes.Version.new, VersionRequest(name, color.hexString(), meta, _projectId))
|
||||
|
||||
override suspend fun updateLabel(id: Uuid, name: String?, color: Color?): Either<ErrorResponse, Unit> =
|
||||
_client.callRoute(Routes.Label.update, id to PartialLabelRequest(name, color?.hexString())).ignoreValue()
|
||||
override suspend fun updateVersion(id: Uuid, name: String?, color: Color?, meta: String?): Either<ErrorResponse, Unit> =
|
||||
_client.callRoute(Routes.Version.update, id to PartialVersionRequest(name, color?.hexString(), meta)).ignoreValue()
|
||||
|
||||
override suspend fun deleteLabel(id: Uuid): Either<ErrorResponse, Unit> =
|
||||
_client.callRoute(Routes.Label.delete, id).ignoreValue()
|
||||
_client.callRoute(Routes.Version.delete, id).ignoreValue()
|
||||
|
||||
override suspend fun newEntry(label: Uuid, timestamp: Instant, warmups: List<Float>,
|
||||
measurements: List<Float>, unit: TimeUnit, input: String, hardware: String
|
||||
|
||||
+20
-22
@@ -11,13 +11,11 @@ import com.jaytux.phoebench.clients.SnackProvider.Companion.snackOr
|
||||
import com.jaytux.phoebench.clients.hexString
|
||||
import com.jaytux.phoebench.clients.immutable
|
||||
import com.jaytux.phoebench.clients.insort
|
||||
import com.jaytux.phoebench.clients.systemTz
|
||||
import com.jaytux.phoebench.clients.withScope
|
||||
import com.jaytux.phoebench.common.EntryResponse
|
||||
import com.jaytux.phoebench.common.LabelResponse
|
||||
import com.jaytux.phoebench.common.VersionResponse
|
||||
import com.jaytux.phoebench.common.ProjectEvent
|
||||
import com.jaytux.phoebench.common.TimeUnit
|
||||
import com.jaytux.phoebench.common.foldSuspend
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Instant
|
||||
@@ -32,7 +30,7 @@ class ProjectVM(
|
||||
private val _sseRepo: ISSERepo = ISSERepo.default(_client),
|
||||
private val _forceBack: () -> Unit
|
||||
) : ViewModel() {
|
||||
data class Label(val id: Uuid, val name: String, val colorStr: String, val uiColor: Color = parseColor(colorStr)) {
|
||||
data class Version(val id: Uuid, val name: String, val colorStr: String, val meta: String, val uiColor: Color = parseColor(colorStr)) {
|
||||
companion object {
|
||||
private val _snack by lazy { SnackProvider.get() }
|
||||
private val _errColor = Color(red = 252, green = 20, blue = 182)
|
||||
@@ -50,17 +48,17 @@ class ProjectVM(
|
||||
return Color(red = r, green = g, blue = b)
|
||||
}
|
||||
|
||||
fun fromResponse(it: LabelResponse) = Label(it.id, it.name, it.color)
|
||||
fun fromResponse(it: VersionResponse) = Version(it.id, it.name, it.color, it.meta)
|
||||
|
||||
val invalid = Label(Uuid.fromLongs(0L, 0L), "<invalid>", _errColor.hexString(), _errColor)
|
||||
val invalid = Version(Uuid.fromLongs(0L, 0L), "<invalid>", _errColor.hexString(), "", _errColor)
|
||||
}
|
||||
}
|
||||
|
||||
data class Entry(val id: Uuid, val label: Label, val timeStamp: Instant, val warmups: List<Float>,
|
||||
data class Entry(val id: Uuid, val label: Version, val timeStamp: Instant, val warmups: List<Float>,
|
||||
val measurements: List<Float>, val nativeUnit: TimeUnit, val input: String, val hardware: String) {
|
||||
companion object {
|
||||
fun fromResponse(it: EntryResponse, map: Map<Uuid, Label>) = Entry(
|
||||
it.id, map[it.labelId] ?: Label.invalid, it.timestamp,
|
||||
fun fromResponse(it: EntryResponse, map: Map<Uuid, Version>) = Entry(
|
||||
it.id, map[it.labelId] ?: Version.invalid, it.timestamp,
|
||||
it.warmups, it.measurements, it.unit, it.input, it.hardware
|
||||
)
|
||||
}
|
||||
@@ -70,7 +68,7 @@ class ProjectVM(
|
||||
private val _owner = mutableStateOf<String?>(null)
|
||||
private val _public = mutableStateOf(false)
|
||||
private val _editable = mutableStateOf(false)
|
||||
private val _labels = mutableStateOf(mapOf<Uuid, Label>())
|
||||
private val _labels = mutableStateOf(mapOf<Uuid, Version>())
|
||||
private val _entries = mutableStateOf(listOf<Entry>())
|
||||
private val _inputs = mutableStateOf(setOf<String>())
|
||||
private val _hardware = mutableStateOf(setOf<String>())
|
||||
@@ -79,7 +77,7 @@ class ProjectVM(
|
||||
val owner = _owner.immutable()
|
||||
val public = _public.immutable()
|
||||
val editable = _editable.immutable()
|
||||
val labels = _labels.immutable()
|
||||
val versions = _labels.immutable()
|
||||
val entries = _entries.immutable()
|
||||
val inputs = _inputs.immutable()
|
||||
val hardware = _hardware.immutable()
|
||||
@@ -104,7 +102,7 @@ class ProjectVM(
|
||||
_owner.value = it.owner.name
|
||||
_public.value = it.isPublic
|
||||
_editable.value = it.isEditable
|
||||
_labels.value = it.usedLabels.associate { l -> l.id to Label.fromResponse(l) }
|
||||
_labels.value = it.usedLabels.associate { l -> l.id to Version.fromResponse(l) }
|
||||
val localInputs = mutableSetOf<String>()
|
||||
val localHardware = mutableSetOf<String>()
|
||||
_entries.value = it.entries.map { e ->
|
||||
@@ -122,14 +120,14 @@ class ProjectVM(
|
||||
when(event) {
|
||||
ProjectEvent.Deleted -> back()
|
||||
is ProjectEvent.EntryDeleted -> _entries.value = _entries.value.filter { it.id != event.id }
|
||||
is ProjectEvent.LabelChanged -> _labels.value += (event.label.id to Label.fromResponse(event.label))
|
||||
is ProjectEvent.LabelDeleted -> _labels.value = _labels.value.filter { it.value.id != event.id }
|
||||
is ProjectEvent.VersionChanged -> _labels.value += (event.label.id to Version.fromResponse(event.label))
|
||||
is ProjectEvent.VersionDeleted -> _labels.value = _labels.value.filter { it.value.id != event.id }
|
||||
is ProjectEvent.NewEntry -> {
|
||||
_entries.value = _entries.value.insort(Entry.fromResponse(event.entry, _labels.value), Entry::timeStamp)
|
||||
_inputs.value += event.entry.input
|
||||
_hardware.value += event.entry.hardware
|
||||
}
|
||||
is ProjectEvent.NewLabel -> _labels.value += (event.label.id to Label.fromResponse(event.label))
|
||||
is ProjectEvent.NewVersion -> _labels.value += (event.label.id to Version.fromResponse(event.label))
|
||||
is ProjectEvent.Updated -> {
|
||||
_name.value = event.changes.name
|
||||
_owner.value = event.changes.owner.name
|
||||
@@ -157,21 +155,21 @@ class ProjectVM(
|
||||
_forceBack()
|
||||
}
|
||||
|
||||
fun mkLabel(name: String, color: Color) {
|
||||
fun mkLabel(name: String, color: Color, meta: String) {
|
||||
withScope {
|
||||
_repo.newLabel(name, color).snackOr {
|
||||
_repo.newVersion(name, color, meta).snackOr {
|
||||
if(_job == null)
|
||||
_labels.value += it.id to Label(it.id, it.name, it.color)
|
||||
_labels.value += it.id to Version(it.id, it.name, it.color, it.meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateLabel(id: Uuid, name: String?, color: Color?) {
|
||||
fun updateLabel(id: Uuid, name: String?, color: Color?, meta: String?) {
|
||||
withScope {
|
||||
val old = _labels.value[id] ?: return@withScope
|
||||
_repo.updateLabel(id, name, color).snackOr {
|
||||
_repo.updateVersion(id, name, color).snackOr {
|
||||
if(_job == null)
|
||||
_labels.value += id to Label(id, name ?: old.name, color?.hexString() ?: old.colorStr, color ?: old.uiColor)
|
||||
_labels.value += id to Version(id, name ?: old.name, color?.hexString() ?: old.colorStr, meta ?: old.meta, color ?: old.uiColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +183,7 @@ class ProjectVM(
|
||||
}
|
||||
}
|
||||
|
||||
fun mkEntry(label: Label, warmups: List<Float>, measurements: List<Float>, unit: TimeUnit, input: String, hardware: String) {
|
||||
fun mkEntry(label: Version, warmups: List<Float>, measurements: List<Float>, unit: TimeUnit, input: String, hardware: String) {
|
||||
withScope {
|
||||
_repo.newEntry(label.id, Clock.System.now(), warmups, measurements, unit, input, hardware).snackOr {
|
||||
if(_job == null) {
|
||||
|
||||
+50
-41
@@ -20,14 +20,11 @@ import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.toLowerCase
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.composables.icons.lucide.*
|
||||
import com.jaytux.phoebench.clients.darken
|
||||
import com.jaytux.phoebench.clients.data.ProjectVM
|
||||
import com.jaytux.phoebench.clients.data.mutableStateSetFrom
|
||||
import com.jaytux.phoebench.clients.data.mutableStateSetOf
|
||||
@@ -52,8 +49,6 @@ import io.github.koalaplot.core.xygraph.AxisContent
|
||||
import io.github.koalaplot.core.xygraph.DefaultPoint
|
||||
import io.github.koalaplot.core.xygraph.XYGraph
|
||||
import io.github.koalaplot.core.xygraph.autoScaleRange
|
||||
import io.github.koalaplot.core.xygraph.autoScaleXRange
|
||||
import io.github.koalaplot.core.xygraph.autoScaleYRange
|
||||
import io.github.koalaplot.core.xygraph.rememberAxisStyle
|
||||
import io.github.koalaplot.core.xygraph.rememberFloatLinearAxisModel
|
||||
import io.github.koalaplot.core.xygraph.rememberGridStyle
|
||||
@@ -70,7 +65,7 @@ fun ProjectView(id: Uuid, forceBack: () -> Unit) {
|
||||
val owner by vm.owner
|
||||
val public by vm.public
|
||||
val editable by vm.editable
|
||||
val labels by vm.labels
|
||||
val labels by vm.versions
|
||||
|
||||
var editing by remember { mutableStateOf(false) }
|
||||
var deleting by remember { mutableStateOf(false) }
|
||||
@@ -152,8 +147,8 @@ fun ProjectView(id: Uuid, forceBack: () -> Unit) {
|
||||
vm.delete()
|
||||
}
|
||||
|
||||
if(addingLabel) AddLabelDialog({ addingLabel = false; addOpen = false }) { name, color ->
|
||||
vm.mkLabel(name, color)
|
||||
if(addingLabel) AddVersionDialog({ addingLabel = false; addOpen = false }) { name, color, meta ->
|
||||
vm.mkLabel(name, color, meta)
|
||||
addOpen = false
|
||||
}
|
||||
|
||||
@@ -162,7 +157,7 @@ fun ProjectView(id: Uuid, forceBack: () -> Unit) {
|
||||
|
||||
if(addingData) AddEntryDialog(labels, usedInputs, usedHardware,
|
||||
onCancel = { addingData = false; addOpen = false },
|
||||
onAddLbl = { name, lbl -> vm.mkLabel(name, lbl) }
|
||||
onAddLbl = { name, lbl, meta -> vm.mkLabel(name, lbl, meta) }
|
||||
) { label, warmups, measurements, unit, input, hardware ->
|
||||
vm.mkEntry(label, warmups, measurements, unit, input, hardware)
|
||||
addOpen = false
|
||||
@@ -212,29 +207,32 @@ fun ConfirmDeleteProjectDialog(name: String, onCancel: () -> Unit, onDelete: ()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddLabelDialog(onCancel: () -> Unit, onAdd: (name: String, color: Color) -> Unit) {
|
||||
fun AddVersionDialog(onCancel: () -> Unit, onAdd: (name: String, color: Color, meta: String) -> Unit) {
|
||||
Dialog(onDismissRequest = onCancel) {
|
||||
var name by remember { mutableStateOf("") }
|
||||
var color by remember { mutableStateOf(randomColor()) }
|
||||
var meta by remember { mutableStateOf("") }
|
||||
|
||||
Surface(Modifier.padding(15.dp).widthIn(400.dp), shape = MaterialTheme.shapes.medium) {
|
||||
Column(Modifier.padding(8.dp).width(IntrinsicSize.Min)) {
|
||||
Text("Create label", Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.headlineMedium)
|
||||
Text("Create version", Modifier.align(Alignment.CenterHorizontally), style = MaterialTheme.typography.headlineMedium)
|
||||
OutlinedTextField(name, { name = it }, Modifier.fillMaxWidth(), label = { Text("Name") })
|
||||
Spacer(Modifier.height(10.dp))
|
||||
KolorPicker(color, { color = it }, alphaTrackVisible = false, modifier = Modifier.aspectRatio(1f))
|
||||
Spacer(Modifier.height(10.dp))
|
||||
OutlinedTextField(meta, { meta = it }, Modifier.fillMaxWidth(), label = { Text("Additional information") })
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
CancelConfirmXRow(onCancel, {
|
||||
onAdd(name.trim(), color)
|
||||
}, confirmText = "Create", canConfirm = name.trim().isNotBlank())
|
||||
onAdd(name.trim(), color, meta.trim())
|
||||
}, confirmText = "Create", canConfirm = name.trim().isNotBlank() && meta.trim().isNotBlank())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun QuickLabel(lbl: ProjectVM.Label, hollow: Boolean = false) {
|
||||
fun QuickVersion(lbl: ProjectVM.Version, hollow: Boolean = false) {
|
||||
Row(Modifier.height(IntrinsicSize.Min).padding(vertical = 3.dp), verticalAlignment = Alignment.Bottom) {
|
||||
Box(Modifier.fillMaxHeight().aspectRatio(1f).background(lbl.uiColor)) {
|
||||
if(hollow) {
|
||||
@@ -242,20 +240,29 @@ fun QuickLabel(lbl: ProjectVM.Label, hollow: Boolean = false) {
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.width(15.dp))
|
||||
Text(lbl.name)
|
||||
Text(" ${lbl.colorStr}", style = MaterialTheme.typography.bodySmall, color = LocalContentColor.current.copy(alpha = 0.75f))
|
||||
Column {
|
||||
Row {
|
||||
Text(lbl.name)
|
||||
Text(" ${lbl.colorStr}", Modifier.align(Alignment.Bottom),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = LocalContentColor.current.copy(alpha = 0.75f),
|
||||
)
|
||||
}
|
||||
Text(lbl.meta, maxLines = 1, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.bodySmall,
|
||||
color = LocalContentColor.current.copy(0.75f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddEntryDialog(
|
||||
labels: Map<Uuid, ProjectVM.Label>, usedInputs: Set<String>, usedHardware: Set<String>,
|
||||
onCancel: () -> Unit, onAddLbl: (name: String, color: Color) -> Unit,
|
||||
onAdd: (label: ProjectVM.Label, warmups: List<Float>, measurements: List<Float>, unit: TimeUnit, input: String, hardware: String) -> Unit
|
||||
labels: Map<Uuid, ProjectVM.Version>, usedInputs: Set<String>, usedHardware: Set<String>,
|
||||
onCancel: () -> Unit, onAddLbl: (name: String, color: Color, meta: String) -> Unit,
|
||||
onAdd: (label: ProjectVM.Version, warmups: List<Float>, measurements: List<Float>, unit: TimeUnit, input: String, hardware: String) -> Unit
|
||||
) {
|
||||
Dialog(onDismissRequest = onCancel) {
|
||||
var label by remember { mutableStateOf<ProjectVM.Label?>(null) }
|
||||
var label by remember { mutableStateOf<ProjectVM.Version?>(null) }
|
||||
var warmups by remember { mutableStateOf("") }
|
||||
var measurements by remember { mutableStateOf("") }
|
||||
var unit by remember { mutableStateOf(TimeUnit.SECONDS) }
|
||||
@@ -295,12 +302,12 @@ fun AddEntryDialog(
|
||||
tonalElevation = 2.dp
|
||||
) {
|
||||
Box(Modifier.fillMaxWidth().clickable { dropDownOpen = true }.padding(8.dp)) {
|
||||
label?.let { QuickLabel(it) } ?: Text("Select label...", fontStyle = FontStyle.Italic)
|
||||
label?.let { QuickVersion(it) } ?: Text("Select version...", fontStyle = FontStyle.Italic)
|
||||
}
|
||||
}
|
||||
DropdownMenu(dropDownOpen, { dropDownOpen = false }) {
|
||||
labels.values.forEach { lbl ->
|
||||
DropdownMenuItem({ QuickLabel(lbl) }, { label = lbl; dropDownOpen = false })
|
||||
DropdownMenuItem({ QuickVersion(lbl) }, { label = lbl; dropDownOpen = false })
|
||||
}
|
||||
HorizontalDivider(Modifier.height(1.dp))
|
||||
|
||||
@@ -378,14 +385,14 @@ fun AddEntryDialog(
|
||||
}
|
||||
}
|
||||
|
||||
if(addingLabel) AddLabelDialog({ addingLabel = false }, onAddLbl)
|
||||
if(addingLabel) AddVersionDialog({ addingLabel = false }, onAddLbl)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ProjectPlotArea(vm: ProjectVM){
|
||||
val entries by vm.entries
|
||||
val labels by vm.labels
|
||||
val versions by vm.versions
|
||||
val editable by vm.editable
|
||||
|
||||
var displayWarmups by remember { mutableStateOf(false) }
|
||||
@@ -400,8 +407,8 @@ fun ProjectPlotArea(vm: ProjectVM){
|
||||
var timeFilter by remember { mutableStateOf(0f..1f) }
|
||||
var timeFilterString by remember { mutableStateOf("") }
|
||||
|
||||
val labelFilter = remember(labels) { mutableStateSetFrom(labels.values) }
|
||||
val labelFilterKey by labelFilter.revision
|
||||
val versionFilter = remember(versions) { mutableStateSetFrom(versions.values) }
|
||||
val labelVersionFilter by versionFilter.revision
|
||||
|
||||
val allInputs by vm.inputs
|
||||
val allHardware by vm.hardware
|
||||
@@ -410,7 +417,7 @@ fun ProjectPlotArea(vm: ProjectVM){
|
||||
var filterInput by remember { mutableStateOf<String?>(null) }
|
||||
var filterHardware by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
LaunchedEffect(entries, displayWarmups, currentUnit, timeFilter, timeMin, timeMax, labelFilterKey, filterInput, filterHardware) {
|
||||
LaunchedEffect(entries, displayWarmups, currentUnit, timeFilter, timeMin, timeMax, labelVersionFilter, filterInput, filterHardware) {
|
||||
var maxX = 0f
|
||||
var minY = 0f
|
||||
var maxY = 0f
|
||||
@@ -421,7 +428,7 @@ fun ProjectPlotArea(vm: ProjectVM){
|
||||
renderableEntries = entries.mapNotNull { entry ->
|
||||
val use = if(displayWarmups) entry.warmups else entry.measurements
|
||||
|
||||
if(entry.label !in labelFilter) return@mapNotNull null
|
||||
if(entry.label !in versionFilter) return@mapNotNull null
|
||||
if(!(entry.timeStamp inRange timeRange)) return@mapNotNull null
|
||||
if(filterInput != null && entry.input != filterInput) return@mapNotNull null
|
||||
if(filterHardware != null && entry.hardware != filterHardware) return@mapNotNull null
|
||||
@@ -547,32 +554,32 @@ fun ProjectPlotArea(vm: ProjectVM){
|
||||
|
||||
Row(Modifier.weight(0.33f).fillMaxHeight()) {
|
||||
Column(Modifier.weight(0.5f)) {
|
||||
Text("Labels", style = MaterialTheme.typography.headlineSmall)
|
||||
Text("Click a label to toggle its visibility.", fontStyle = FontStyle.Italic,
|
||||
Text("Versions", style = MaterialTheme.typography.headlineSmall)
|
||||
Text("Click a version to toggle its visibility.", fontStyle = FontStyle.Italic,
|
||||
color = LocalContentColor.current.copy(alpha = 0.5f), style = MaterialTheme.typography.bodySmall)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
if(labels.isEmpty()) {
|
||||
if(versions.isEmpty()) {
|
||||
Box(Modifier.fillMaxWidth().fillMaxHeight(0.25f)) {
|
||||
Text("No labels yet.", Modifier.align(Alignment.Center), fontStyle = FontStyle.Italic)
|
||||
Text("No versions yet.", Modifier.align(Alignment.Center), fontStyle = FontStyle.Italic)
|
||||
}
|
||||
}
|
||||
else {
|
||||
LazyColumn(Modifier.padding(start = 5.dp)) {
|
||||
items(labels.toList().sortedBy { it.second.name }) { (_, lbl) ->
|
||||
items(versions.toList().sortedBy { it.second.name }) { (_, lbl) ->
|
||||
Box(Modifier.fillMaxWidth().clickable {
|
||||
if(labelFilter.size == labels.size) {
|
||||
labelFilter.clear()
|
||||
labelFilter.add(lbl)
|
||||
if(versionFilter.size == versions.size) {
|
||||
versionFilter.clear()
|
||||
versionFilter.add(lbl)
|
||||
}
|
||||
else {
|
||||
labelFilter.toggle(lbl)
|
||||
if(labelFilter.isEmpty()) labelFilter.addAll(labels.values)
|
||||
versionFilter.toggle(lbl)
|
||||
if(versionFilter.isEmpty()) versionFilter.addAll(versions.values)
|
||||
}
|
||||
}) {
|
||||
Box {
|
||||
QuickLabel(lbl, lbl !in labelFilter)
|
||||
QuickVersion(lbl, lbl !in versionFilter)
|
||||
|
||||
if(lbl !in labelFilter) {
|
||||
if(lbl !in versionFilter) {
|
||||
Box(Modifier.matchParentSize()) {
|
||||
HorizontalDivider(
|
||||
Modifier.fillMaxWidth().align(Alignment.Center),
|
||||
@@ -607,7 +614,9 @@ fun ProjectPlotArea(vm: ProjectVM){
|
||||
Text(it, Modifier.padding(start = 5.dp, top = 2.dp, bottom = 2.dp), color = LocalContentColor.current.copy(0.66f))
|
||||
}
|
||||
|
||||
item { Spacer(Modifier.height(15.dp)) }
|
||||
item { Spacer(Modifier.height(10.dp)) }
|
||||
item { HorizontalDivider(Modifier.height(1.dp)) }
|
||||
item { Spacer(Modifier.height(10.dp)) }
|
||||
|
||||
item { Text("Hardware", style = MaterialTheme.typography.headlineSmall) }
|
||||
item {
|
||||
|
||||
Reference in New Issue
Block a user