Skip to content

Commit

Permalink
feat: implement gt list sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
amtul.noor authored and noornoorie committed Apr 23, 2024
1 parent d799240 commit 49fdbad
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 9 deletions.
75 changes: 72 additions & 3 deletions src/components/workflows/WorkflowsTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import TimelineItem from "@/components/workflows/timeline/TimelineItem.vue"
import Dropdown from 'primevue/dropdown'
import {computed, onMounted, ref, watch} from "vue"
import {EvaluationMetrics, getMaxValueByMetric} from '@/helpers/metrics'
import { SortOptions } from '@/helpers/sort'
import { useI18n } from "vue-i18n"
import type { DropdownOption, EvaluationResultsDocumentWide, Workflow, GroundTruth } from "@/types"
import { DropdownPassThroughStyles } from '@/helpers/pt'
Expand All @@ -12,11 +13,26 @@ import timelineStore from "@/store/timeline-store"
import TrendLegend from "@/components/workflows/TrendLegend.vue";
const { t } = useI18n()
const gtList = computed<GroundTruth[]>(() => workflowsStore.gt.filter(({ id }) => filtersStore.gt.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>(() => <keyof EvaluationResultsDocumentWide>selectedMetric.value?.value || EvaluationMetrics.CER_MEAN)
const gtList = computed<GroundTruth[]>(() => (
workflowsStore.gt.filter(({ id }) => filtersStore.gt.findIndex(({ value }) => value === id) > -1)
)
)
const metrics = computed<DropdownOption[]>(() => (
Object.keys(EvaluationMetrics).map(key => ({ value: EvaluationMetrics[key], label: t(EvaluationMetrics[key]) }))
)
)
const sortOpts = computed<DropdownOption[]>(() => (
Object.keys(SortOptions).map(key => (
{
value: SortOptions[key],
label: t(SortOptions[key]),
}
))
))
const selectedSortType = ref<DropdownOption | null>(null)
onMounted(async () => {
selectedMetric.value = metrics.value[0]
Expand All @@ -30,11 +46,53 @@ watch(selectedMetric,
),
{ immediate: true }
)
watch(
selectedSortType,
(type) => {
switch (type?.value) {
case 'year_asc':
workflowsStore.sortGtbyYearAsc()
break
case 'year_desc':
workflowsStore.sortGtbyYearDesc()
break
case 'label_asc':
workflowsStore.sortGtbyLabelAsc()
break
case 'label_desc':
workflowsStore.sortGtbyLabelDesc()
break
case 'metric_asc':
workflowsStore.sortGtbyMetricAsc(selectedMetricValue.value)
break
case 'metric_desc':
workflowsStore.sortGtbyMetricDesc(selectedMetricValue.value)
break
}
}
)
watch(
selectedMetricValue,
(metric) => {
if (selectedSortType.value) {
switch (selectedSortType.value.value) {
case 'metric_asc':
workflowsStore.sortGtbyMetricAsc(metric)
break
case 'metric_desc':
workflowsStore.sortGtbyMetricDesc(metric)
break
}
}
},
)
</script>

<template>
<div class="flex flex-col">
<div class="flex w-full mb-4">
<div class="flex w-full mb-2">
<Dropdown
v-model="selectedMetric"
:options="metrics"
Expand All @@ -45,6 +103,17 @@ watch(selectedMetric,
unstyled
/>
</div>
<div class="flex w-full mb-4">
<Dropdown
v-model="selectedSortType"
:options="sortOpts"
:pt="DropdownPassThroughStyles"
optionLabel="label"
placeholder="Sort by"
class="ml-auto md:w-14rem"
unstyled
/>
</div>
<TrendLegend class="ml-auto mb-4"/>
<div class="flex flex-col space-y-6">
<template v-if="gtList.length > 0">
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const SortOptions = {
LABEL_ASC: 'label_asc',
LABEL_DESC: 'label_desc',
YEAR_ASC: 'year_asc',
YEAR_DESC: 'year_desc',
METRIC_ASC: 'metric_asc',
METRIC_DESC: 'metric_desc',
}

export {
SortOptions,
}
8 changes: 7 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,11 @@
"negative": "negative",
"better": "better",
"equal": "equal",
"worse": "worse"
"worse": "worse",
"year_asc": "Year Oldest",
"year_desc": "Year Newest",
"label_asc": "Label (a-z)",
"label_desc": "Label (z-a)",
"metric_asc": "Selected Metric (ascending)",
"metric_desc": "Selected Metric (descending)",
}
142 changes: 137 additions & 5 deletions src/store/workflows-store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { reactive } from "vue"
import type { EvaluationRun, GroundTruth, ReleaseInfo, Workflow } from "@/types"
import type { EvaluationRun, EvaluationResultsDocumentWide, GroundTruth, ReleaseInfo, Workflow } from "@/types"
import { mapGtId } from "@/helpers/utils"
import { getMaxValueByMetric } from "@/helpers/metrics"

function normalizeDate(dateString: string): string {
return new Date(new Date(dateString).setHours(0, 0, 0, 0)).toDateString()
Expand All @@ -12,9 +13,15 @@ export default reactive<{
runs: 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
sortGtbyYearAsc: () => void
sortGtbyYearDesc: () => void
sortGtbyLabelAsc: () => void
sortGtbyLabelDesc: () => void
sortGtbyMetricAsc: (metric: keyof EvaluationResultsDocumentWide) => void
sortGtbyMetricDesc: (metric: keyof EvaluationResultsDocumentWide) => void
}>({
gt: [],
workflows: [],
Expand All @@ -31,18 +38,143 @@ export default reactive<{
}
)
},
getLatestRuns() {
getLatestRuns(gtId?: string) {
const dates = Object.keys(this.runs.reduce((acc, cur) => {
acc[normalizeDate(cur.metadata.timestamp)] = null
return acc
}, <{ [key: string]: null}>{}))

return this.runs.filter(({ metadata }) => normalizeDate(metadata.timestamp) === dates[dates.length - 1])
return this.runs.filter(
({ metadata }) => {
const matchGt = gtId ? mapGtId(metadata.gt_workspace.id) === gtId : true
return matchGt && normalizeDate(metadata.timestamp) === dates[dates.length - 1]
}
)
},
getGtById(id: string): GroundTruth | null {
return this.gt.find((item) => item.id === id) ?? null
},
getWorkflowById(id: string): Workflow | null {
return this.workflows.find((item) => item.id === id) ?? null
}
},
sortGtbyYearAsc() {
this.gt.sort((left, right) => {
const leftSplit = left.id.split('_')
const rightSplit = right.id.split('_')
if (leftSplit.length < 1 || rightSplit.length < 1) {
console.warn("[SORT GT] GTID couldn't be split properly")
return 0
}

const leftYear = Number(leftSplit[leftSplit.length-1])
const rightYear = Number(rightSplit[rightSplit.length-1])
if (isNaN(leftYear) || isNaN(rightYear)) {
console.warn("[SORT GT] Year couldn't be parsed properly from GTID ")
return 0
}
return leftYear - rightYear
})
},
sortGtbyYearDesc() {
this.gt.sort((left, right) => {
const leftSplit = left.id.split('_')
const rightSplit = right.id.split('_')
if (leftSplit.length < 1 || rightSplit.length < 1) {
console.warn("[SORT GT] GTID couldn't be split properly")
return 0
}

const leftYear = Number(leftSplit[leftSplit.length-1])
const rightYear = Number(rightSplit[rightSplit.length-1])
if (isNaN(leftYear) || isNaN(rightYear)) {
console.warn("[SORT GT] Year couldn't be parsed properly from GTID ")
return 0
}
return rightYear - leftYear
})
},
sortGtbyLabelAsc() {
this.gt.sort((left, right) => {
const leftLabel = left.label.toUpperCase()
const rightLabel = right.label.toUpperCase()
if (leftLabel < rightLabel) {
return -1
} else if (leftLabel > rightLabel) {
return 1
} else {
return 0
}
})
},
sortGtbyLabelDesc() {
this.gt.sort((left, right) => {
const leftLabel = left.label.toUpperCase()
const rightLabel = right.label.toUpperCase()
if (rightLabel < leftLabel) {
return -1
} else if (rightLabel > leftLabel) {
return 1
} else {
return 0
}
})
},
sortGtbyMetricAsc(metric: keyof EvaluationResultsDocumentWide) {
this.gt.sort((left, right) => {
let leftValue = 0
let rightValue = 0

const leftRuns = this.getLatestRuns(left.id)
const rightRuns = this.getLatestRuns(right.id)
//
// TODO: Decide what to do for empty (missed) runs
// currently placed all the way down unsorted
if (leftRuns.length < 1) {
return 1
}
if (rightRuns.length < 1) {
return -1
}

leftValue = leftRuns.reduce((acc, cur) => {
const value = <number | null>cur.evaluation_results.document_wide[<keyof EvaluationResultsDocumentWide>metric]
return acc += value ?? 0
}, 0) / leftRuns.length
rightValue = rightRuns.reduce((acc, cur) => {
const value = <number | null>cur.evaluation_results.document_wide[<keyof EvaluationResultsDocumentWide>metric]
return acc += value ?? 0
}, 0) / rightRuns.length

return leftValue - rightValue
})
},
sortGtbyMetricDesc(metric: keyof EvaluationResultsDocumentWide) {
this.gt.sort((left, right) => {
let leftValue = 0
let rightValue = 0

const leftRuns = this.getLatestRuns(left.id)
const rightRuns = this.getLatestRuns(right.id)

// TODO: Decide what to do for empty (missed) runs
// currently placed all the way down unsorted
if (leftRuns.length < 1) {
return 1
}
if (rightRuns.length < 1) {
return -1
}

leftValue = leftRuns.reduce((acc, cur) => {
const value = <number | null>cur.evaluation_results.document_wide[<keyof EvaluationResultsDocumentWide>metric]
return acc += value ?? 0
}, 0) / leftRuns.length
rightValue = rightRuns.reduce((acc, cur) => {
const value = <number | null>cur.evaluation_results.document_wide[<keyof EvaluationResultsDocumentWide>metric]
return acc += value ?? 0
}, 0) / rightRuns.length

return rightValue - leftValue
})
},
})

0 comments on commit 49fdbad

Please sign in to comment.