Skip to content
Snippets Groups Projects
Commit 512d7fbe authored by Paul Pestov's avatar Paul Pestov
Browse files

feat: render timeline preview charts with dynamic max values

parent 5358ed25
No related branches found
No related tags found
No related merge requests found
......@@ -5,7 +5,7 @@ import Dropdown from 'primevue/dropdown'
import { computed, onMounted, ref } from "vue"
import { EvaluationMetrics } from '@/helpers/metrics'
import { useI18n } from "vue-i18n"
import type { DropdownOption, GroundTruth, Workflow } from "@/types"
import type {DropdownOption, EvaluationResultsDocumentWide, GroundTruth, Workflow} from "@/types"
import { DropdownPassThroughStyles } from '@/helpers/pt'
import { store } from '@/helpers/store'
......@@ -14,7 +14,7 @@ const gtList = ref<GroundTruth[]>([])
const workflows = ref<Workflow[]>([])
const selectedMetric = ref<DropdownOption | null>(null)
const metrics = computed<DropdownOption[]>(() => Object.keys(EvaluationMetrics).map(key => ({ value: EvaluationMetrics[key], label: t(EvaluationMetrics[key]) })))
const selectedMetricValue = computed<string>(() => selectedMetric.value?.value || EvaluationMetrics.CER_MEAN)
const selectedMetricValue = computed<keyof EvaluationResultsDocumentWide>(() => <keyof EvaluationResultsDocumentWide>selectedMetric.value?.value || EvaluationMetrics.CER_MEAN)
onMounted(async () => {
selectedMetric.value = metrics.value[0]
......
......@@ -18,13 +18,12 @@ interface Props {
const props = defineProps<Props>()
const height = props.height || 60
const height = props.height || 45
const marginTop = 10
const marginRight = 10
const marginBottom = 30
const marginBottom = 5
const marginLeft = 40
const _width = computed(() => props.width ?? 300)
const _maxY = computed(() => props.maxY ?? 2)
const container = ref<HTMLDivElement>()
......@@ -42,11 +41,14 @@ function isUp(data: TimelineChartDataPoint[], higherIsUp = true) {
else return -1
}
function render([data, startDate, endDate]) {
function render([data, startDate, endDate, maxY]) {
if (!data || !startDate || !endDate) return
if (data.length === 0) return
if (!container.value) return
container.value.replaceChildren()
// Declare the x (horizontal position) scale.
const x = d3.scaleTime()
.domain([startDate, endDate])
......@@ -54,8 +56,10 @@ function render([data, startDate, endDate]) {
// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0, _maxY.value])
.domain([0, maxY])
.range([height - marginBottom, marginTop])
.nice()
// Create the SVG container.
const svg = d3.create("svg")
......@@ -75,7 +79,7 @@ function render([data, startDate, endDate]) {
svg.append("g")
.classed('y-axis-group', true)
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y).ticks(1).tickSize(0).tickPadding(5))
.call(d3.axisLeft(y).tickValues([0, maxY]).tickSize(0).tickPadding(5))
svg.select('.y-axis-group .domain').attr('stroke', colors.gray['400'])
svg.selectAll('.y-axis-group .tick text').attr('fill', colors.gray['400'])
......@@ -126,18 +130,14 @@ function render([data, startDate, endDate]) {
setEventListeners(svg.selectAll('.chart-point'), tooltip, { useData: props.tooltipContent })
// Append the SVG element.
if (!container.value) return
container.value.replaceChildren()
container.value.append(svg.node())
}
onMounted(() => {
render([props.data, props.startDate, props.endDate])
render([props.data, props.startDate, props.endDate, props.maxY])
})
watch([() => props.data, () => props.startDate, () => props.endDate], render)
watch([() => props.data, () => props.startDate, () => props.endDate, () => props.maxY], render)
</script>
<template>
......
<script setup lang="ts">
import { onMounted, ref, watch } from "vue"
import {computed, onMounted, ref, watch} from "vue"
import api from "@/helpers/api"
import BaseTimelineChart from "@/components/timeline/BaseTimelineChart.vue"
import type {EvaluationResultsDocumentWide, EvaluationRun, TimelineChartDataPoint, Workflow} from "@/types"
import { getMaxValueOfMetric } from '@/helpers/metrics'
import type {EvaluationResultsDocumentWide, EvaluationRun, TimelineChartDataPoint} from "@/types"
import {useI18n} from "vue-i18n";
import { metricChartTooltipContent } from "@/helpers/metric-chart-tooltip-content";
import OverlayPanel from "primevue/overlaypanel";
import BaseTimelineDetailedChart from "@/components/timeline/BaseTimelineDetailedChart.vue";
import timelineStore from "@/store/timeline-store";
const { t } = useI18n()
const props = defineProps<{
gtId: string,
metric: string,
metric: keyof EvaluationResultsDocumentWide,
startDate: Date,
endDate: Date
}>()
const data = ref<TimelineChartDataPoint[]>([])
const maxY = ref(2)
const workflows = ref<Workflow[] | null>(null)
const maxY = computed(() => timelineStore.maxValues[props.metric] ?? 0)
const runs = ref<EvaluationRun[]>([])
const op = ref<OverlayPanel | null>(null)
onMounted(async () => {
const { gtId, metric } = props
workflows.value = await api.getWorkflows()
const { gtId } = props
runs.value = await api.getRuns(gtId)
data.value = getTimelineData(runs.value, metric)
maxY.value = getMaxValueOfMetric(metric)
init()
})
watch(() => props.metric, async (value) => {
data.value = getTimelineData(runs.value, value)
maxY.value = getMaxValueOfMetric(value)
})
watch(() => props.metric, init)
function init() {
if (!runs.value) return
data.value = getTimelineData(runs.value, props.metric)
}
function getTimelineData(runs: EvaluationRun[], metric: string): TimelineChartDataPoint[] {
const datesValues = runs.reduce((acc, cur) => {
......
<script setup lang="ts">
import { onMounted, ref, watch } from "vue"
import { computed, onMounted, ref, watch } from "vue"
import api from "@/helpers/api"
import BaseTimelineChart from "@/components/timeline/BaseTimelineChart.vue"
import { getMaxValueOfMetric } from '@/helpers/metrics'
import { extendMaxValue, getMaxValueByMetric } from '@/helpers/metrics'
import type { EvaluationResultsDocumentWide, EvaluationRun, TimelineChartDataPoint } from "@/types"
import { metricChartTooltipContent } from "@/helpers/metric-chart-tooltip-content"
import OverlayPanel from 'primevue/overlaypanel'
import BaseTimelineDetailedChart from "@/components/timeline/BaseTimelineDetailedChart.vue"
import timelineStore from "@/store/timeline-store"
const props = defineProps(['gtId', 'workflowId', 'metric', 'startDate', 'endDate'])
const runs = ref<EvaluationRun[]>([])
const data = ref([])
const maxY = ref(2)
const maxY = computed(() => timelineStore.maxValues[props.metric] ?? 0 )
const op = ref<OverlayPanel | null>(null)
onMounted(async () => {
const { gtId, workflowId, metric } = props
const { gtId, workflowId } = props
runs.value = await api.getRuns(gtId, workflowId)
data.value = getTimelineData(runs.value, metric)
maxY.value = getMaxValueOfMetric(metric)
init()
})
watch(() => props.metric,
(value) => {
if (!runs.value) return
data.value = getTimelineData(runs.value, value)
maxY.value = getMaxValueOfMetric(value)
}, { immediate: true }
)
watch(() => props.metric, init)
function init() {
if (!runs.value) return
data.value = getTimelineData(runs.value, props.metric)
const maxValueByMetric = getMaxValueByMetric(props.metric, runs.value)
if (maxValueByMetric > maxY.value) {
timelineStore.setMaxValue(props.metric, extendMaxValue(maxValueByMetric))
}
}
function getTimelineData(runs: EvaluationRun[], metric: keyof EvaluationResultsDocumentWide) {
return runs.map(({ metadata, evaluation_results }) => {
......@@ -44,6 +47,8 @@ function tooltipContent(d: TimelineChartDataPoint) {
return metricChartTooltipContent(d, props.metric)
}
</script>
<template>
......
......@@ -3,7 +3,7 @@ import Panel from "primevue/panel"
import OverlayPanel from 'primevue/overlaypanel'
import StepsAcronyms from '@/helpers/workflow-steps-acronyms'
import MetricChart from "@/components/timeline/MetricChart.vue"
import type { GroundTruth, Workflow, WorkflowStep } from "@/types"
import type { EvaluationResultsDocumentWide, GroundTruth, Workflow, WorkflowStep } from "@/types"
import MetricAverageChart from "@/components/timeline/MetricAverageChart.vue"
import { Icon } from '@iconify/vue'
import { ref } from "vue"
......@@ -12,7 +12,7 @@ import { OverlayPanelDropdownStyles } from "@/helpers/pt"
const props = defineProps<{
gt: GroundTruth,
workflows: Workflow[],
metric: string
metric: keyof EvaluationResultsDocumentWide
}>()
const op = ref<OverlayPanel>()
......@@ -67,7 +67,7 @@ function hideParametersOverlay() {
</div>
</template>
<template v-slot:default>
<div class="flex border-t border-gray-300 pt-4 px-4">
<div class="flex border-t border-gray-300 py-4 px-4">
<table class="table-fixed w-full">
<tr v-for="workflow in workflows" :key="workflow.id">
<td class="font-semibold pe-2">{{ workflow.label }}</td>
......
import type { EvaluationResultsDocumentWide, EvaluationRun } from "@/types"
const EvaluationMetrics = {
CER_MEAN: 'cer_mean',
CER_MEDIAN: 'cer_median',
......@@ -8,7 +10,7 @@ const EvaluationMetrics = {
CPU_TIME: 'cpu_time'
}
function getMaxValueOfMetric(metric: string): number {
function getDefaultMaxValueOfMetric(metric: string): number {
if (metric === EvaluationMetrics.CER_MEAN) return 2
if (metric === EvaluationMetrics.CER_MEDIAN) return 2
if (metric === EvaluationMetrics.CER_STANDARD_DEVIATION) return 2
......@@ -20,7 +22,22 @@ function getMaxValueOfMetric(metric: string): number {
else return 1
}
function getMaxValueByMetric(metric: keyof EvaluationResultsDocumentWide, runs: EvaluationRun[] = []): number {
const values = runs.map((run) => {
const value = run.evaluation_results.document_wide[metric]
return Array.isArray(value) ? Math.max(...value) : value ?? 0
}) ?? []
return Math.max(...values)
}
function extendMaxValue(value: number): number {
return Math.floor(value + value * 0.2)
}
export {
EvaluationMetrics,
getMaxValueOfMetric
getMaxValueByMetric,
getDefaultMaxValueOfMetric,
extendMaxValue
}
import { reactive, ref } from "vue"
export default reactive<{
maxValues: {[metric: string]: number },
setMaxValue: (metric: string, value: number) => void,
getMaxValue: (metric: string) => number
}>({
maxValues:{},
setMaxValue(metric: string, value: number) {
this.maxValues[metric] = value
},
getMaxValue(metric: string) {
return this.maxValues[metric] ?? 0
}
})
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment