Select Git revision
StructureDb.vue 10.60 KiB
<script setup lang="ts">
import type { SortItem, AutocompleteMeiliFacetProps } from "@/components/ServerDbTable.vue"
import { useNumericalFilter } from "@/composables/useNumericalfilter"
import { useRefinedUrl } from "@/composables/useRefinedUrl"
import { ServerDbTable } from "#components"
import { withQuery, joinURL, withTrailingSlash } from 'ufo'
interface Item {
Foldseek_name: string
System_name_ok: string
}
const sortBy: Ref<SortItem[]> = ref([{ key: 'System', order: "asc" }])
const itemValue = ref("id");
const dbName = ref("structure")
const client = useMeiliSearchRef()
onMounted(async () => {
try {
const data = await client.index(toValue(dbName)).search("", {
facets: ["*"],
filter: [],
page: 1,
hitsPerPage: 25,
})
autocompleteMeiliFacetsProps.value.facetDistribution = data?.facetDistribution
} catch (error) {
throw createError("Unable to get structures")
}
})
const autocompleteMeiliFacetsProps = ref<AutocompleteMeiliFacetProps>({
db: toValue(dbName),
facets: [
{ title: "Defense System", type: "subheader" },
{ title: "System", value: "System", type: "facet", icon: "i-tabler:virus-off", },
{ title: "Subsystem", value: "subtype", type: "facet", icon: "i-tabler:virus-off" },
{ type: "divider" },
{ title: "Gene name", value: "gene_name", type: "facet", icon: "mdi-dna" },
{ title: "Completed", value: "completed", type: "facet", icon: "md:done" },
{ title: "Prediction type", value: "prediction_type", type: "facet", icon: "i-gravity-ui:molecule" },
],
facetDistribution: undefined
})
const computedAutocompleteMeiliFacetsProps = computed(() => {
const toValFacetDistribution = toValue(autocompleteMeiliFacetsProps).facetDistribution
const toValFacets = toValue(autocompleteMeiliFacetsProps).facets
if (toValFacetDistribution !== undefined && toValFacets !== undefined) {
return {
...toValue(autocompleteMeiliFacetsProps), facets: toValFacets.map(facet => {
if (facet.type === "facet") {
const count = toValFacetDistribution?.[facet.value] ? Object.keys(toValFacetDistribution[facet.value]).length : undefined
return count ? { ...facet, count } : { ...facet }
}
else {
return { ...facet }
}
})
}
}
else {
return toValue(autocompleteMeiliFacetsProps)
}
})
const headers: Ref<Object[]> = ref([
{ title: 'Structure', key: 'structure', sortable: false, removable: false, fixed: true, minWidth: "110px" },
{ title: 'Foldseek', key: 'Foldseek_name', sortable: false },
{ title: "System", key: "System", removable: false },
{ title: "Gene name", key: "gene_name", removable: false },
{ title: "Subtype", key: "subtype", removable: false },
// { title: "pdb file", key: "pdb" },
// { title: "fasta", key: "fasta_file" },
{ title: "Proteins in structure", key: 'proteins_in_the_prediction', sortable: false, removable: true },
{ title: "System genes", key: "system_genes", sortable: false, removable: true },
{ title: "Sys id", key: "nb_sys", removable: true },
{ title: "Completed", key: "completed", removable: true },
{ title: "Prediction type", key: "prediction_type", removable: true },
{ title: "N genes in sys", key: "system_number_of_genes", removable: true },
{ title: "pLDDT", key: "plddts", removable: true },
{ title: "iptm+ptm", key: "iptm+ptm", removable: true },
{ title: "pDockQ", key: "pDockQ", removable: true },
// { title: "Type", key: "type", removable: true },
])
const { range: plddtsRange, stringifyFilter: plddtsFilter, reset: plddtsReset } = useNumericalFilter("plddts", 0, 100)
const { range: iptmRange, stringifyFilter: iptmFilter, reset: iptmReset } = useNumericalFilter("iptm+ptm", 0, 1)
const { range: pdockqRange, stringifyFilter: pdockqFilter, reset: pdockqReset } = useNumericalFilter("pDockQ", 0, 1)
function isString(item: Ref<string | undefined>): item is Ref<string> {
return toValue(item) !== undefined
}
const numericalFilters = computed(() => {
const listFilters = [plddtsFilter, iptmFilter, pdockqFilter].filter(isString).map(f => toValue(f))
return listFilters.length > 0 ? listFilters : undefined
})
const defaultDataTableServerProps = ref({
showExpand: false
})
const dataTableServerProps = computed(() => {
return {
...toValue(defaultDataTableServerProps),
headers: toValue(headers),
itemValue: toValue(itemValue)
}
})
function toFolseekUrl(item: Item) {
const url = joinURL("/" + item.System_name_ok, item.Foldseek_name)
const { refinedUrl } = useRefinedUrl(url)
return toValue(refinedUrl)
}
function namesToCollapsibleChips(names: string[], systemDir: string, file: string | null = null) {
if (file === null) {
return names.filter((it) => it !== "").map(it => ({ title: it.split("__")[1] }))
} else {
return names.filter((it) => it !== "").map(it => ({ title: it.split("__")[1], href: `/wiki/${systemDir}/${file}` }))
}
}
function pdbNameToCif(pdbPath: string) {
const cifPath = pdbPath.split(".").slice(0, -1).join(".")
return `${cifPath}.cif`
}
const { refinedUrl: downloadAllPdbUrl } = useRefinedUrl("/df-all-pdbs.tar.gz")
const { refinedUrl: downloadAllCifUrl } = useRefinedUrl("/df-all-cifs.tar.gz")
</script>
<template>
<ServerDbTable title="Predicted Structures" :sortBy="sortBy" :data-table-server-props="dataTableServerProps"
:autocomplete-meili-facets-props="computedAutocompleteMeiliFacetsProps"
:numerical-filters="toRef(numericalFilters)">
<template #numerical-filters="{ search }">
<v-list>
<v-list-item>
<v-list-item-title class="text-subtitle-1 text-medium-emphasis">
pLDDT
</v-list-item-title>
<v-row>
<v-col class="pt-8 pl-8" :lg="8">
<v-range-slider v-model="plddtsRange" strict density="compact" hide-details="auto"
step="0.5" :min="0" :max="100" thumb-label="always" @update:modelValue="search()">
<template #append>
<v-btn variant="text" icon="md:restart_alt" @click="plddtsReset()"></v-btn>
</template>
</v-range-slider>
</v-col></v-row>
</v-list-item>
<v-list-item>
<v-list-item-title class="text-subtitle-1 text-medium-emphasis">
iptm+ptm
</v-list-item-title>
<v-row>
<v-col class="pt-8 pl-8" :lg="8">
<v-range-slider v-model="iptmRange" strict density="compact" hide-details="auto" step="0.1"
:min="0" :max="1" thumb-label="always" @update:modelValue="search()">
<template #append>
<v-btn variant="text" icon="md:restart_alt" @click="iptmReset()"></v-btn>
</template>
</v-range-slider>
</v-col></v-row>
</v-list-item>
<v-list-item>
<v-list-item-title class="text-subtitle-1 text-medium-emphasis">pDockQ</v-list-item-title>
<v-row>
<v-col class="pt-8 pl-8" :lg="8">
<v-range-slider v-model="pdockqRange" density="compact" strict hide-details="auto"
step="0.1" :min="0" :max="1" thumb-label="always" @update:modelValue="search()">
<template #append>
<v-btn variant="text" icon="md:restart_alt" @click="pdockqReset()"></v-btn>
</template>
</v-range-slider>
</v-col></v-row>
</v-list-item>
</v-list>
</template>
<template #[`item.Foldseek_name`]="{ item }">
<FoldseekDialog v-if="item.Foldseek_name !== 'na'" :foldseek-path="toFolseekUrl(item)"></FoldseekDialog>
</template>
<template #[`item.proteins_in_the_prediction`]="{ item }">
<CollapsibleChips
:items="namesToCollapsibleChips(item.proteins_in_the_prediction, item.System_name_ok, item.fasta_file)">
</CollapsibleChips>
</template>
<template #[`item.system_genes`]="{ item }">
<CollapsibleChips :items="namesToCollapsibleChips(item.system_genes, item.System_name_ok)">
</CollapsibleChips>
</template>
<template #[`item.structure`]="{ item }">
<v-row no-gutters align="center">
<MolstarPdbePlugin v-if="item?.pdb && item.pdb !== 'na'"
:data-urls="[`/${item.System_name_ok}/${pdbNameToCif(item.pdb)}`, `/${item.System_name_ok}/${item.pdb}`]"
uniq format="cif">
</MolstarPdbePlugin>
</v-row>
<!-- <v-row no-gutters align="center">
<MolstarPdbePlugin v-if="item?.pdb && item.pdb !== 'na'"
:data-urls="[`/${item.System_name_ok}/${item.pdb}`]" uniq format="pdb">
</MolstarPdbePlugin>
</v-row> -->
<!-- <v-icon v-else color="warning" icon="md:dangerous"></v-icon> -->
</template>
<template #[`item.completed`]="{ item }">
<v-icon v-if="item.completed" color="success" icon="md:check"></v-icon>
<v-icon v-else color="warning" icon="md:dangerous"></v-icon>
</template>
<template #toolbar-items>
<v-menu>
<template v-slot:activator="{ props }">
<!-- <v-tooltip activator="parent" location="top">Download all structures</v-tooltip> -->
<v-btn class="align-self-end" v-bind="props" icon="md:download">
</v-btn>
</template>
<v-list>
<v-list-item value="pdb" :href="downloadAllPdbUrl">
<v-list-item-title>all pdbs</v-list-item-title>
</v-list-item>
<v-list-item value="cif" :href="downloadAllCifUrl">
<v-list-item-title>all cifs</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</template>
</ServerDbTable>
</template>