Skip to content

Commit

Permalink
feat: add filtering by workflow, processor and date range to timeline…
Browse files Browse the repository at this point in the history
… view (#85)

* feat: filter by workflow and processor

* feat: filter by date range

* feat: implement multi-selectable workflow steps filtering

* fix: make only related workflows visible while filtering by processors

* fix: set all checkboxes of processor filter to selected on page load

* ref: highlight selected processors

* fix: refactor to own component and prepare visual filtering

* fix: show filter also when no data is selected

* fix: workflow filter label

* feat: hide workflow when neither workflow or processor in workflow are selected

* refactor: remove unused imports/vars, style

* fix: remove search from workflow filter

* feat: change date range dropdown to multiselect

* refactor: change order

* feat: add i18n translations

* fix: adjust filter position for smaller screens

* fix: changed faulty imported that failed build

* fix: workflow filter not working

---------

Co-authored-by: amtul.noor <[email protected]>
Co-authored-by: noornoorie <[email protected]>
Co-authored-by: jfrer <[email protected]>
  • Loading branch information
4 people authored Jun 25, 2024
1 parent a4bbdac commit 084933b
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 56 deletions.
34 changes: 17 additions & 17 deletions src/components/workflows/WorkflowsTimeline.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<script setup lang="ts">
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 { computed, onMounted, ref, watch} from "vue"
import { EvaluationMetrics, getMaxValueByMetric} from '@/helpers/metrics'
import { useI18n } from "vue-i18n"
import type { DropdownOption, EvaluationResultsDocumentWide, Workflow, GroundTruth } from "@/types"
import { DropdownPassThroughStyles } from '@/helpers/pt'
import workflowsStore from '@/store/workflows-store'
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"
const { t } = useI18n()
const gtList = computed<GroundTruth[]>(() => workflowsStore.gt.filter(({ id }) => filtersStore.gt.findIndex(({ value }) => value === id) > -1))
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]) })))
Expand All @@ -34,20 +35,19 @@ watch(selectedMetric,

<template>
<div class="flex flex-col">
<template v-if="gtList.length > 0">
<div class="flex w-full mb-4">
<Dropdown
v-model="selectedMetric"
:options="metrics"
:pt="DropdownPassThroughStyles"
optionLabel="label"
placeholder="Select a metric"
class="ml-auto md:w-14rem"
unstyled
/>
</div>
<TrendLegend class="ml-auto mb-4"/>
</template>
<div class="flex w-full mb-4">
<Dropdown
v-model="selectedMetric"
:options="metrics"
:pt="DropdownPassThroughStyles"
optionLabel="label"
placeholder="Select a metric"
class="ml-auto md:w-14rem"
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" />
Expand Down
3 changes: 2 additions & 1 deletion src/components/workflows/filters/Filters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import workflowsStore from "@/store/workflows-store"
import { computed, onMounted } from "vue"
import MultiSelect from "primevue/multiselect"
import type { FilterOption } from "@/types"
const gtOptions = computed(() => workflowsStore.gt.map(({ id, label }) => ({ value: id, label })))
onMounted(() => {
Expand All @@ -21,7 +22,7 @@ onMounted(() => {
optionLabel="label"
placeholder="Select Ground Truth"
panel-class="max-w-[500px]"
max-selected-labels="1"
:max-selected-labels="1"
filter
class="relative flex-1"
>
Expand Down
156 changes: 156 additions & 0 deletions src/components/workflows/timeline/TimelineFilters.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<script setup lang="ts">
import filtersStore from "@/store/filters-store"
import workflowsStore from "@/store/workflows-store"
import { computed, onMounted, ref } from "vue"
import MultiSelect from "primevue/multiselect"
import type { DropdownOption, GroundTruth } from "@/types"
import { deduplicateStepIds, mapGtId } from '@/helpers/utils'
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const dateRangeOptions = computed(() => {
const values = workflowsStore.gt.reduce((acc, cur) => {
acc.add(`${cur.metadata.time.notBefore}-${cur.metadata.time.notAfter}`)
return acc
}, new Set<string>())
return Array.from(values).sort().map(value => ({ value, label: value }))
})
const selectedDateRange = ref<DropdownOption[]>([])
const workflowOptions = computed(() => workflowsStore.workflows.map(({ id, label }) => ({ value: id, label })))
const selectedWorkflows = ref<DropdownOption[]>([])
const workflowStepOptions = ref<DropdownOption[]>([])
const selectedWorkflowSteps = ref<DropdownOption[]>([])
const dateRangeDropdownLabel = computed(() => {
if (dateRangeOptions.value.length === selectedDateRange.value.length) {
return t('filter_by_date_range')
}
return null
})
const workflowDropdownLabel = computed(() => {
if (workflowOptions.value.length === selectedWorkflows.value.length) {
return t('filter_by_workflow')
}
return null
})
const workflowStepDropdownLabel = computed(() => {
if (workflowStepOptions.value.length === selectedWorkflowSteps.value.length) {
return t('filter_by_processor')
}
return null
})
const onDateRangeChange = (event: any) => {
selectedDateRange.value = event
selectGTs()
}
const onWorkflowChange = (event: any) => {
selectedWorkflows.value = event
selectGTs()
selectWorkflows()
}
const onWorkflowStepChange = (event: any) => {
selectedWorkflowSteps.value = event
selectGTs()
selectWorkflows()
}
const selectGTs = () => {
filtersStore.gtTimeline = filtersStore.gt.filter(({ value }) => {
const gt = workflowsStore.getGtById(value)
if(!gt) return false
return hasSomeSelectedProcessor(gt) && hasSomeSelectedDateRange(gt) && hasSomeSelectedWorkflow(gt)
})
}
const selectWorkflows = () => {
filtersStore.workflow = selectedWorkflows.value.filter(({ value }) => (workflowhasSomeSelectedWorkflowStep(value)))
}
const hasSomeSelectedProcessor = (gt: GroundTruth) => {
const gtRuns = workflowsStore.getRuns(gt.id)
return selectedWorkflowSteps.value.some((selectedStep) => {
return gtRuns.some((gtRun) => {
return gtRun.metadata.workflow_steps.findIndex(({ id }) => id === selectedStep.value) > -1
})
})
}
const hasSomeSelectedDateRange = (gt: GroundTruth) => {
return selectedDateRange.value.some(({ value }) => {
const splitDateRange = value.split('-')
const from = splitDateRange[0]
const to = splitDateRange[1]
return gt.metadata.time.notBefore === from && gt.metadata.time.notAfter === to
})
}
const hasSomeSelectedWorkflow = (gt: GroundTruth) => {
const gtRuns = workflowsStore.getRuns(gt.id)
return gtRuns.some((gtRun) => {
return selectedWorkflows.value.findIndex(({ value }) => (value === mapGtId(gtRun.metadata.ocr_workflow.id))) > -1
})
}
const workflowhasSomeSelectedWorkflowStep = (workflowId: string) => {
const workflow = workflowsStore.getWorkflowById(workflowId)
if (workflow == null) return false
return workflow.steps.some((step) => selectedWorkflowSteps.value.findIndex(({ value }) => (value === step.id)) > -1)
}
onMounted(() => {
workflowStepOptions.value = deduplicateStepIds(workflowsStore.workflows).map(id => ({ value: id, label: t(id) }))
selectedDateRange.value = dateRangeOptions.value
selectedWorkflows.value = workflowOptions.value
selectedWorkflowSteps.value = workflowStepOptions.value
selectGTs()
selectWorkflows()
})
</script>

<template>
<div class="flex flex-wrap mb-4 justify-start lg:justify-end">
<MultiSelect
v-model="selectedDateRange"
@update:model-value="onDateRangeChange($event)"
:max-selected-labels="1"
:options="dateRangeOptions"
optionLabel="label"
:placeholder="t('select_a_date_range')"
:selected-items-label="dateRangeDropdownLabel"
class="mr-4 mb-2 lg:m-0 lg:ml-auto md:w-14rem"
/>
<MultiSelect
v-model="selectedWorkflows"
@update:model-value="onWorkflowChange($event)"
:max-selected-labels="1"
:options="workflowOptions"
optionLabel="label"
:placeholder="t('select_a_workflow')"
:selected-items-label="workflowDropdownLabel"
class="mr-4 mb-2 lg:m-0 lg:ml-4 md:w-14rem"
/>
<MultiSelect
v-model="selectedWorkflowSteps"
@update:model-value="onWorkflowStepChange($event)"
filter
:max-selected-labels="1"
:options="workflowStepOptions"
optionLabel="label"
:placeholder="t('select_a_processor')"
:selected-items-label="workflowStepDropdownLabel"
class="lg:ml-4 md:w-14rem"
/>
</div>

</template>
9 changes: 3 additions & 6 deletions src/components/workflows/timeline/TimelineItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import MetricChart from "@/components/workflows/timeline/MetricChart.vue"
import type { EvaluationResultsDocumentWide, GroundTruth, Workflow, WorkflowStep } from "@/types"
import MetricAverageChart from "@/components/workflows/timeline/MetricAverageChart.vue"
import { Icon } from '@iconify/vue'
import { onMounted, nextTick, ref } from "vue"
import { computed, nextTick, ref } from "vue"
import { OverlayPanelDropdownStyles } from "@/helpers/pt"
import workflowsStore from "@/store/workflows-store"
import TimelineItemMetadata from "@/components/workflows/timeline/TimelineItemMetadata.vue"
import filtersStore from "@/store/filters-store"
const props = defineProps<{
Expand All @@ -22,12 +23,8 @@ const isOpVisible = ref(false)
const selectedStep = ref<WorkflowStep | null>(null)
const startDate = ref<Date>(new Date('2023-10-01'))
const endDate = ref<Date>(new Date())
const workflows = ref<Workflow[]>([])
onMounted(() => {
workflows.value = workflowsStore.workflows
})
const workflows = computed<Workflow[]>(() => workflowsStore.workflows.filter(({ id }) => filtersStore.workflow.findIndex(({ value }) => value === id ) > - 1))
function getStepAcronym(stepId) {
return StepsAcronyms[stepId]
Expand Down
14 changes: 12 additions & 2 deletions src/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ref } from 'vue'
import type { EvaluationResultsDocumentWide, EvaluationRun } from "@/types"
import type { EvaluationResultsDocumentWide, EvaluationRun, Workflow, WorkflowStep } from "@/types"

const utils = ref({})

Expand Down Expand Up @@ -72,9 +72,19 @@ const mapGtId = (id: string) => {
return arr[arr.length - 1].split('.')[0]
}

const deduplicateStepIds = (workflows: Workflow[]) => {
return Array.from(workflows.reduce((acc, cur) => {
cur.steps.forEach(({ id }) => {
acc.add(id)
})
return acc
}, new Set<string>()))
}

export {
getEvalColor,
setEvalColors,
createReadableMetricValue,
mapGtId
mapGtId,
deduplicateStepIds,
}
8 changes: 7 additions & 1 deletion src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,11 @@
"volume": "Umfang",
"labelling": "Kennzeichnung",
"no_labels_for_this_entry": "Keine Kennzeichnung für diesen Eintrag.",
"average_timeline": "Chronik (Durchschnitt)"
"average_timeline": "Chronik (Durchschnitt)",
"filter_by_date_range": "Nach Zeitspanne filtern",
"filter_by_workflow": "Nach Workflow filtern",
"filter_by_processor": "Nach Prozessor filtern",
"select_a_date_range": "Zeitraum auswählen",
"select_a_workflow": "Workflow auswählen",
"select_a_processor": "Prozessor auswählen"
}
9 changes: 7 additions & 2 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@
"volume": "Volume",
"labelling": "Labelling",
"no_labels_for_this_entry": "No labels for this entry.",
"average_timeline": "Average Timeline"

"average_timeline": "Average Timeline",
"filter_by_date_range": "Filter by date range",
"filter_by_workflow": "Filter by workflow",
"filter_by_processor": "Filter by processor",
"select_a_date_range": "Select a date range",
"select_a_workflow": "Select a workflow",
"select_a_processor": "Select a processor"
}
8 changes: 6 additions & 2 deletions src/store/filters-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import type { FilterOption } from "@/types"

export default reactive<{
gt: FilterOption[],
metric: FilterOption[]
gtTimeline: FilterOption[],
metric: FilterOption[],
workflow: FilterOption[],
}>({
gt: [],
metric: []
gtTimeline: [],
metric: [],
workflow: []
})
Loading

0 comments on commit 084933b

Please sign in to comment.