Merge pull request #17847 from ahmedhamidawan/tool_panel_slow_computed

This commit is contained in:
Marius van den Beek
2024-03-28 21:13:58 +01:00
committed by GitHub
5 changed files with 52 additions and 28 deletions
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, type ComputedRef, onMounted, onUnmounted, type PropType, type Ref, ref, watch } from "vue";
import { computed, type ComputedRef, onMounted, onUnmounted, type PropType, watch } from "vue";
import { useRouter } from "vue-router/composables";
import { type Tool, type ToolSection, useToolStore } from "@/stores/toolStore";
@@ -12,6 +13,7 @@ import { type ToolSearchKeys } from "../utilities";
import DelayedInput from "@/components/Common/DelayedInput.vue";
import FilterMenu from "@/components/Common/FilterMenu.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
const router = useRouter();
@@ -67,8 +69,6 @@ const emit = defineEmits<{
(e: "onQuery", query: string): void;
}>();
const searchWorker: Ref<Worker | undefined> = ref(undefined);
const localFilterText = computed({
get: () => {
return props.query !== null ? props.query : "";
@@ -112,6 +112,7 @@ const ToolFilters: ComputedRef<Filtering<string>> = computed(() => new Filtering
const { currentFavorites } = storeToRefs(useUserStore());
const toolStore = useToolStore();
const { searchWorker } = storeToRefs(toolStore);
const sectionNames = toolStore.sectionDatalist("default").map((option: { value: string; text: string }) => option.text);
const ontologyList = toolStore
@@ -119,8 +120,10 @@ const ontologyList = toolStore
.concat(toolStore.sectionDatalist("ontology:edam_operations"));
onMounted(() => {
searchWorker.value = new Worker(new URL("../toolSearch.worker.js", import.meta.url));
// initialize worker
if (!searchWorker.value) {
searchWorker.value = new Worker(new URL("components/Panels/toolSearch.worker.js", import.meta.url));
}
searchWorker.value.onmessage = ({ data }) => {
const { type, payload, sectioned, query, closestTerm } = data;
if (type === "searchToolsByKeysResult" && query === props.query) {
@@ -134,7 +137,10 @@ onMounted(() => {
});
onUnmounted(() => {
searchWorker.value?.terminate();
// The worker is not terminated but it will not be listening to messages
if (searchWorker.value?.onmessage) {
searchWorker.value.onmessage = null;
}
});
watch(
@@ -178,7 +184,7 @@ function onAdvancedSearch(filters: any) {
</script>
<template>
<div>
<div v-if="searchWorker">
<FilterMenu
v-if="props.enableAdvanced"
:class="!propShowAdvanced && 'mb-3'"
@@ -245,4 +251,7 @@ function onAdvancedSearch(filters: any) {
:placeholder="placeholder"
@change="checkQuery" />
</div>
<BAlert v-else class="mb-3" variant="info" show>
<LoadingSpan message="Loading Tool Search" />
</BAlert>
</template>
+3 -5
View File
@@ -135,8 +135,6 @@ const localPanel: ComputedRef<Record<string, Tool | ToolSectionType> | null> = c
}
});
const sectionIds = computed(() => Object.keys(localPanel.value || {}));
const favWorkflows = computed(() => {
const Galaxy = getGalaxyInstance();
const storedWorkflowMenuEntries = Galaxy && Galaxy.config.stored_workflow_menu_entries;
@@ -298,10 +296,10 @@ function setButtonText() {
:query-filter="queryFilter || undefined"
:disable-filter="true"
@onClick="onToolClick" />
<div v-for="(sectionId, key) in sectionIds" :key="key">
<div v-for="(panel, key) in localPanel" :key="key">
<ToolSection
v-if="localPanel[sectionId]"
:category="localPanel[sectionId] || {}"
v-if="panel"
:category="panel || {}"
:query-filter="queryFilter || undefined"
@onClick="onToolClick" />
</div>
+5 -1
View File
@@ -34,7 +34,8 @@ const emit = defineEmits<{
const arePanelsFetched = ref(false);
const toolStore = useToolStore();
const { currentPanelView, defaultPanelView, isPanelPopulated, loading, panel, panelViews } = storeToRefs(toolStore);
const { currentPanelView, defaultPanelView, isPanelPopulated, loading, panel, panelViews, currentPanel } =
storeToRefs(toolStore);
const loadingView = ref<string | undefined>(undefined);
const query = ref("");
@@ -192,6 +193,9 @@ function onInsertWorkflowSteps(workflowId: string, workflowStepCount: number | u
</b-badge>
</div>
</div>
<b-alert v-else-if="currentPanel" class="m-2" variant="info" show>
<LoadingSpan message="Loading Toolbox" />
</b-alert>
</template>
<style lang="scss" scoped>
+23 -13
View File
@@ -158,16 +158,22 @@ export function getValidToolsInCurrentView(
isWorkflowPanel = false,
excludedSectionIds: string[] = []
) {
const toolEntries = Object.entries(toolsById).filter(([, tool]) => {
// filter on non-hidden, non-disabled, and workflow compatibile (based on props.workflow)
return (
!tool.hidden &&
tool.disabled !== true &&
!(isWorkflowPanel && !tool.is_workflow_compatible) &&
!excludedSectionIds.includes(tool.panel_section_id)
);
});
return Object.fromEntries(toolEntries);
const excludeSet = new Set(excludedSectionIds);
const validTools: Record<string, Tool> = {};
for (const [toolId, tool] of Object.entries(toolsById)) {
const { panel_section_id, hidden, disabled, is_workflow_compatible } = tool;
if (
!excludeSet.has(panel_section_id) &&
!hidden &&
disabled !== true &&
!(isWorkflowPanel && !is_workflow_compatible)
) {
validTools[toolId] = tool;
}
}
return validTools;
}
/** Looks in each section of `currentPanel` and filters `section.tools` on `validToolIdsInCurrentView` */
@@ -175,12 +181,16 @@ export function getValidToolsInEachSection(
validToolIdsInCurrentView: string[],
currentPanel: Record<string, Tool | ToolSection>
) {
// use a set for fast membership lookup
const idSet = new Set(validToolIdsInCurrentView);
return Object.entries(currentPanel).map(([id, section]) => {
const validatedSection = { ...section } as ToolSection;
if (validatedSection.tools && Array.isArray(validatedSection.tools)) {
// assign sectionTools to avoid repeated getter access
const sectionTools = validatedSection.tools;
if (sectionTools && Array.isArray(sectionTools)) {
// filter on valid tools and panel labels in this section
validatedSection.tools = validatedSection.tools.filter((toolId) => {
if (typeof toolId === "string" && validToolIdsInCurrentView.includes(toolId)) {
validatedSection.tools = sectionTools.filter((toolId) => {
if (typeof toolId === "string" && idSet.has(toolId)) {
return true;
} else if (typeof toolId !== "string") {
// is a special case where there is a label within a section
+5 -2
View File
@@ -4,7 +4,7 @@
import axios from "axios";
import { defineStore } from "pinia";
import Vue, { computed, Ref, ref } from "vue";
import Vue, { computed, Ref, ref, shallowRef } from "vue";
import { createWhooshQuery, filterTools, types_to_icons } from "@/components/Panels/utilities";
import { useUserLocalStorage } from "@/composables/userLocalStorage";
@@ -75,12 +75,14 @@ export interface PanelView {
export const useToolStore = defineStore("toolStore", () => {
const currentPanelView: Ref<string> = useUserLocalStorage("tool-store-view", "");
const defaultPanelView: Ref<string> = ref("");
const toolsById = ref<Record<string, Tool>>({});
const toolsById = shallowRef<Record<string, Tool>>({});
const toolResults = ref<Record<string, string[]>>({});
const panel = ref<Record<string, Record<string, Tool | ToolSection>>>({});
const panelViews = ref<Record<string, PanelView>>({});
const loading = ref(false);
const searchWorker = ref<Worker | undefined>(undefined);
const getToolForId = computed(() => {
return (toolId: string) => toolsById.value[toolId];
});
@@ -278,6 +280,7 @@ export const useToolStore = defineStore("toolStore", () => {
currentPanel,
isPanelPopulated,
sectionDatalist,
searchWorker,
fetchToolForId,
fetchTools,
fetchPanelViews,