Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add sorting of gts by label, selected metric and time span to timeline view #90

Merged
merged 7 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/components/workflows/WorkflowsTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,26 @@ import filtersStore from '@/store/filters-store'
import timelineStore from "@/store/timeline-store"
import TrendLegend from "@/components/workflows/TrendLegend.vue"
import TimelineFilters from "./timeline/TimelineFilters.vue"
import TimelineSorting from "./timeline/TimelineSorting.vue"

const { t } = useI18n()
const gtList = computed<GroundTruth[]>(() => workflowsStore.gt.filter(({ id }) => filtersStore.gtTimeline.findIndex(({ value }) => value === id) > -1))
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<keyof EvaluationResultsDocumentWide>(() => selectedMetric.value?.value as keyof EvaluationResultsDocumentWide || EvaluationMetrics.CER_MEAN)
const sortedGtList = ref<GroundTruth[]>([])


onMounted(async () => {
selectedMetric.value = metrics.value[0]
workflows.value = workflowsStore.workflows
})

watch(gtList, () => {
sortedGtList.value = gtList.value
})

watch(selectedMetric,
() => timelineStore.setMaxValue(
selectedMetricValue.value,
Expand All @@ -35,22 +42,23 @@ watch(selectedMetric,

<template>
<div class="flex flex-col">
<div class="flex w-full mb-4">
<div class="flex mb-4 space-x-4 items-end justify-between">
<TimelineSorting v-model="sortedGtList" :selectedMetric="selectedMetricValue"/>
<Dropdown
v-model="selectedMetric"
:options="metrics"
:pt="DropdownPassThroughStyles"
optionLabel="label"
placeholder="Select a metric"
class="ml-auto md:w-14rem"
class="grow-0"
unstyled
/>
</div>
<TrendLegend class="ml-auto mb-4"/>
<TimelineFilters></TimelineFilters>
<div class="flex flex-col space-y-6">
<template v-if="gtList.length > 0">
<TimelineItem v-for="gt in gtList" :key="gt.id" :gt="gt" :metric="selectedMetricValue" />
<template v-if="sortedGtList.length > 0">
<TimelineItem v-for="gt in sortedGtList" :key="gt.id" :gt="gt" :metric="selectedMetricValue" />
</template>
<template v-else-if="workflowsStore.gt.length > 0 ">
<div class="my-6">{{ $t('no_documents_selected') }}</div>
Expand Down
55 changes: 55 additions & 0 deletions src/components/workflows/timeline/TimelineSorting.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script setup lang="ts">
import type { DropdownOption, EvaluationResultsDocumentWide, GroundTruth } from "@/types"
import { computed, ref, watch } from "vue"
import Dropdown from "primevue/dropdown"
import { GTTimelineSortingOptions, sortByOption } from "@/helpers/sorting"
import { useI18n } from "vue-i18n"
import { DropdownPassThroughStyles } from "@/helpers/pt"

const { t } = useI18n()

const props = defineProps<{
modelValue: GroundTruth[],
selectedMetric: keyof EvaluationResultsDocumentWide
}>()

const emit = defineEmits<{
(event: 'update:modelValue', payload: GroundTruth[]): void
}>()

const sortOptions = computed<DropdownOption[]>(() =>
Object.keys(GTTimelineSortingOptions).map(key =>
({ value: GTTimelineSortingOptions[key as keyof typeof GTTimelineSortingOptions],
label: t(GTTimelineSortingOptions[key as keyof typeof GTTimelineSortingOptions])
})
)
)
const selectedSortOption = ref<DropdownOption>(sortOptions.value[0])

watch(() => props.modelValue, () => {
updateSortedList(selectedSortOption.value)
})

watch(() => props.selectedMetric, () => {
updateSortedList(selectedSortOption.value)
})

function updateSortedList(event: any) {
const sortedGtList = sortByOption(props.modelValue, event.value, props.selectedMetric)
emit('update:modelValue', sortedGtList)
}

</script>
<template>
<div>
<p class="font-semibold mb-2">{{ t('sort_by')}}:</p>
<Dropdown
v-model="selectedSortOption"
@update:model-value="updateSortedList($event)"
:options="sortOptions"
:pt="DropdownPassThroughStyles"
option-label="label"
unstyled
/>
</div>
</template>
64 changes: 64 additions & 0 deletions src/helpers/sorting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import workflowsStore from "@/store/workflows-store"
import type { EvaluationResultsDocumentWide, EvaluationRun, GroundTruth, TimeSpan } from "@/types"

const GTTimelineSortingOptions = {
LABEL_ASC: 'label_asc',
LABEL_DESC: 'label_desc',
METRIC_DESC: 'metric_desc',
METRIC_ASC: 'metric_asc',
YEAR_ASC: 'year_asc',
YEAR_DESC: 'year_desc'
}

function sortByOption(gtList: GroundTruth[], sortingOption: string, metric: keyof EvaluationResultsDocumentWide): GroundTruth[] {
if (sortingOption === GTTimelineSortingOptions.METRIC_DESC) return sortByMetric(gtList, true, metric)
if (sortingOption === GTTimelineSortingOptions.METRIC_ASC) return sortByMetric(gtList, false, metric)
if (sortingOption === GTTimelineSortingOptions.LABEL_DESC) return sortByLabel(gtList, true)
if (sortingOption === GTTimelineSortingOptions.LABEL_ASC) return sortByLabel(gtList, false)
if (sortingOption === GTTimelineSortingOptions.YEAR_DESC) return sortByYear(gtList, true)
if (sortingOption === GTTimelineSortingOptions.YEAR_ASC) return sortByYear(gtList, false)
return gtList
}

function sortByMetric(gtList: GroundTruth[], desc: boolean, metric: keyof EvaluationResultsDocumentWide): GroundTruth[] {
return gtList.sort((left, right) => {
const compareMetric = (left: GroundTruth, right: GroundTruth) => {
const leftRuns = workflowsStore.getLatestRuns(left.id)
const rightRuns = workflowsStore.getLatestRuns(right.id)

const getAverageValue = (runs: EvaluationRun[]) => {
if (runs.length === 0) return 0
return runs.reduce((acc, curr) => {
const value = <number | null>curr.evaluation_results.document_wide[metric]
return acc += value ?? 0
}, 0) / runs.length
}

return getAverageValue(leftRuns) - getAverageValue(rightRuns)
}
return desc ? compareMetric(right, left) : compareMetric(left, right)
})
}

function sortByLabel(gtList: GroundTruth[], desc: boolean): GroundTruth[] {
return gtList.sort((left, right) => {
const leftLabel = left.label.toLocaleLowerCase()
const rightLabel = right.label.toLocaleLowerCase()
return desc ? rightLabel.localeCompare(leftLabel) : leftLabel.localeCompare(rightLabel)
})
}

function sortByYear(gtList: GroundTruth[], desc: boolean): GroundTruth[] {
return gtList.sort((left, right) => {
const compareTimeSpan = (leftTime: TimeSpan, rightTime: TimeSpan) => {
return (leftTime.notBefore > rightTime.notBefore) ? 1 : ((rightTime.notBefore > leftTime.notBefore) ? -1 : 0)
}
return desc ? compareTimeSpan(right.metadata.time, left.metadata.time) : compareTimeSpan(left.metadata.time, right.metadata.time)
})
}



export {
GTTimelineSortingOptions, sortByOption
}
8 changes: 7 additions & 1 deletion src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,11 @@
"select_a_date_range": "Zeitraum auswählen",
"select_a_workflow": "Workflow auswählen",
"select_a_processor": "Prozessor auswählen",
"keep_grouping_when_sorting": "Gruppierung beim Sortieren beibehalten"
"keep_grouping_when_sorting": "Gruppierung beim Sortieren beibehalten",
"label_asc": "Titel (a - z)",
"label_desc": "Titel (z - a)",
"metric_desc": "Ausgewählte Metrik (absteigend)",
"metric_asc": "Ausgewählte Metrik (aufsteigend)",
"year_asc": "Zeitraum (ältester - neuster)",
"year_desc": "Zeitraum (neuster - ältester)"
}
8 changes: 7 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,11 @@
"select_a_date_range": "Select a date range",
"select_a_workflow": "Select a workflow",
"select_a_processor": "Select a processor",
"keep_grouping_when_sorting": "Keep grouping when sorting"
"keep_grouping_when_sorting": "Keep grouping when sorting",
"label_asc": "Label (a - z)",
"label_desc": "Label (z - a)",
"metric_desc": "Selected metric (descending)",
"metric_asc": "Selected metric (ascending)",
"year_asc": "Time period (oldest - latest)",
"year_desc": "Time period (latest - oldest)"
}
12 changes: 10 additions & 2 deletions src/store/workflows-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default reactive<{
latestRuns: EvaluationRun[],
releases: ReleaseInfo[],
getRuns: (gtId: string, workflowId?: string) => EvaluationRun[]
getLatestRuns: () => EvaluationRun[],
getLatestRuns: (gtId?: string) => EvaluationRun[],
getGtById: (id: string) => GroundTruth | null
getWorkflowById: (id: string) => Workflow | null
}>({
Expand All @@ -33,8 +33,16 @@ export default reactive<{
}
)
},
getLatestRuns() {
getLatestRuns(gtId?: string) {
if (gtId === undefined) {
return this.latestRuns
}
return this.latestRuns
.filter(
({ metadata }) => {
return mapGtId(metadata.gt_workspace.id) === gtId
}
)
},
getGtById(id: string): GroundTruth | null {
return this.gt.find((item) => item.id === id) ?? null
Expand Down
10 changes: 6 additions & 4 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ export interface GroundTruthMetadata {
reference: string,
link: string
}[],
time: {
notBefore: string,
notAfter: string
},
time: TimeSpan
title: string,
'transcription-guidelines': string,
url: string,
Expand All @@ -54,6 +51,11 @@ export interface GroundTruthMetadata {
}[],
}

export interface TimeSpan {
notBefore: string,
notAfter: string
}

export interface Workflow {
id: string,
label: string,
Expand Down
Loading