Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • mdm-lab/wiki
  • hvaysset/wiki
  • jsousa/wiki
  • tclabby/wiki
4 results
Show changes
Commits on Source (2)
......@@ -14,4 +14,5 @@ scripts/Pfam-A.hmm.dat
meili_data
#refseq_res.csv
*.pyc
# pfam-a-hmm.csv
\ No newline at end of file
# pfam-a-hmm.csv
.vscode
......@@ -492,7 +492,7 @@ build:prod:wiki:
deploy:dev:
extends: .deploy
rules:
- if: $CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "distri-system-section"
- if: $CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "uniq-molstar-plugin-per-page"
needs:
- "build:dev:wiki"
when: manual
......
......@@ -193,6 +193,7 @@ function updateAutocompleteFocused(isFocused: boolean) {
function emitUpdateModelValue(filters: MaybeRef<FilterItem[] | undefined>) {
console.log(filters)
emit('update:modelValue', toValue(filters))
}
......
......@@ -4,7 +4,7 @@ import { useCsvDownload } from "@/composables/useCsvDownload"
import { useMeiliFilters } from "@/composables/useMeiliFilters"
import { useSlots } from 'vue'
import { useDisplay } from "vuetify";
import { useThrottleFn } from '@vueuse/core'
import { toValue, useThrottleFn } from '@vueuse/core'
import type { FacetInputItem, FilterItem } from '@/components/AutocompleteMeiliFacets.vue'
import { useMeiliSearch } from "#imports"
// import { saveAs } from "file-saver";
......@@ -39,7 +39,7 @@ export interface Props {
autocompleteMeiliFacetsProps: AutocompleteMeiliFacetProps
}
const msFilters = defineModel('msFilters')
const props = withDefaults(defineProps<Props>(), {
title: '',
columnsToDownload: undefined,
......@@ -52,7 +52,9 @@ const props = withDefaults(defineProps<Props>(), {
facets: undefined
}
}
},
msFilters: undefined
});
const slots = useSlots()
const sortByRef = toRef(props.sortBy)
......@@ -123,45 +125,10 @@ onBeforeMount(async () => {
emitRefreshRes()
})
const msFilterCompo = ref<FilterItem[] | undefined>(undefined)
const computedFilterStr = computed(() => {
const toValFilters = toValue(msFilterCompo)
let filtersStr: string | undefined = undefined
if (toValFilters !== undefined && toValFilters.length > 0) {
const tmpFilterItems = [...toValFilters]
if (tmpFilterItems.length % 4 === 0) {
tmpFilterItems.splice(-1)
}
filtersStr = "(" + tmpFilterItems.map((it, index) => {
const sanitizedValue = it.value.split("-").slice(0, -1).join("-")
if ((index + 1) % 4 === 3) {
return `"${sanitizedValue}"`
} else if ((index + 1) % 4 === 0) {
return ` ${sanitizedValue} `
}
else {
return `${sanitizedValue}`
}
}).join("") + ")"
}
return [filtersStr, props.numericalFilters].filter(f => f !== undefined && f !== null).join(" AND ")
})
const computedF = computed(() => toValue(props.numericalFilters))
const { arrayFilters: computedFilter } = useMeiliFilters(msFilterCompo, computedF)
// const computedFilter = computed(() => {
// const toValFilters = toValue(msFilterCompo)
// if (toValFilters !== undefined && toValFilters.length > 0) {
// meiliFilterAsArray
// }
// })
const { arrayFilters: computedFilter } = useMeiliFilters(msFilters, computedF)
watch(computedFilter, () => {
......@@ -185,14 +152,14 @@ const throttleSearch = useThrottleFn(async () => {
const lastFilterItem = computed(() => {
const toValFilterItems = toValue(msFilterCompo)
const toValFilterItems = toValue(msFilters)
if (toValFilterItems !== undefined && Array.isArray(toValFilterItems)) {
return toValFilterItems.slice(-1)[0]
}
})
const isValidFilters = computed(() => {
const toValFilterItems = toValue(msFilterCompo)
const toValFilterItems = toValue(msFilters)
if (toValFilterItems === undefined || Array.isArray(toValFilterItems) && toValFilterItems?.length === 0) {
return true
}
......@@ -249,10 +216,7 @@ watch(search, () => {
// emitRefreshRes()
})
// watch(msFilterCompo, () => {
// searchOrFilter()
// })
async function downloadData() {
pendingDownloadData.value = true
......@@ -305,7 +269,7 @@ function focusedOrBlur(isFocused: boolean) {
@update:focused="focusedOrBlur"></v-text-field>
</v-card-text>
<v-card-text>
<AutocompleteMeiliFacets v-model="msFilterCompo" v-bind="props.autocompleteMeiliFacetsProps"
<AutocompleteMeiliFacets v-model="msFilters" v-bind="props.autocompleteMeiliFacetsProps"
:is-valid-filters="isValidFilters">
</AutocompleteMeiliFacets>
</v-card-text>
......
<script setup lang="ts">
import { toValue } from '@vueuse/core';
import MolstarPdbePlugin from './MolstarPdbePlugin.vue';
const { page } = useContent();
// get the structures
const msIndexName = ref<string>("structure")
const stuctureUrls = ref<string[] | undefined>(undefined)
const headers = ref<Record<string, any>[]>([
{ title: 'Structure', key: 'structure', sortable: false, removable: false, fixed: true, minWidth: "110px" },
{ title: " System", key: "System" },
{ title: "Gene name", key: "gene_name", removable: false },
{ title: "Subtype", key: "subtype", removable: false },
{ title: "Proteins in structure", key: 'proteins_in_the_prediction', sortable: false, removable: true },
{ title: "System genes", key: "system_genes", sortable: false, 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 },
])
const groupBy = ref([
{
key: 'subtype',
order: 'asc',
},
])
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`
}
function displayStructure(item) {
stuctureUrls.value = [`/${item.System_name_ok}/${pdbNameToCif(item.pdb)}`, `/${item.System_name_ok}/${item.pdb}`]
}
// ==================================================================================
// ASYNC PART
const { data: structures, error, refresh } = await useAsyncMeiliSearch({
index: toValue(msIndexName),
query: "",
params: {
facets: ["*"],
filter: [`System='${toValue(page).title}'`, "completed='true'"],
}
})
if (error.value) {
throw createError(`Cannot get structure for system: ${page.title}`)
}
// watch(page() => {
// refresh()
// })
console.log(structures)
</script>
<template>
<v-card flat>
<v-data-table :headers="headers" :items="structures.hits" :group-by="groupBy">
<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 v-slot:group-header="{ item, columns, toggleGroup, isGroupOpen }">
<tr>
<td :colspan="columns.length">
<VBtn :icon="isGroupOpen(item) ? '$expand' : '$next'" size="small" variant="text"
@click="toggleGroup(item)"></VBtn>
{{ item.value === 'na' ? 'No subtype' : item.value }}
</td>
</tr>
</template>
<template #[`item.structure`]="{ item }">
<v-row no-gutters align="center">
<v-btn v-if="item?.pdb && item.pdb !== 'na'" @click="displayStructure(item)">view</v-btn>
</v-row>
</template>
</v-data-table>
<PdbeMolstarPlugin v-model="stuctureUrls" />
</v-card>
</template>
\ No newline at end of file
......@@ -2,21 +2,19 @@
import { withTrailingSlash, withLeadingSlash, joinURL } from 'ufo'
import { useRuntimeConfig, computed } from '#imports'
import * as d3 from "d3";
import * as Plot from "@observablehq/plot";
import PlotFigure from "~/components/PlotFigure";
import { useDisplay } from "vuetify";
export interface Props {
height?: number
dataUrls?: string[]
dataUrl?: string
dataUrl?: string | undefined
uniq?: boolean
format?: "pdb" | "cif"
}
const { mobile } = useDisplay()
const { mobile, width, height } = useDisplay()
const refinedDataUrls = computed(() => {
function refinedUrl(url: string) {
......@@ -53,10 +51,10 @@ const refinedDataUrls = computed(() => {
const props = withDefaults(defineProps<Props>(), {
height: 600,
uniq: false,
format: 'pdb'
format: 'pdb',
dataUrl: undefined
})
const { width, height } = useDisplay()
const maxWidth = ref(1500)
......@@ -72,7 +70,6 @@ const computedHeight = computed(() => {
return height.value - 250
})
const paeError: Ref<string | null> = ref(null)
function closeStructure() {
selectedPdb.value = null
......
<script setup lang="ts">
import { withTrailingSlash, withLeadingSlash, joinURL } from 'ufo'
import { useRuntimeConfig, computed, toValue } from '#imports'
import { useDisplay } from "vuetify";
import type { MaybeRef } from "vue"
import FoldseekDialog from '../FoldseekDialog.vue'
const { mobile, width, height } = useDisplay()
useHead({
link: [
{
rel: 'stylesheet',
href: 'https://www.ebi.ac.uk/pdbe/pdb-component-library/css/pdbe-molstar-3.1.3.css'
},
],
script: [
// Required for IE11
{
src: "https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"
},
{
src: "https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/webcomponents-lite.js"
},
{
type: "text/javascript",
src: "https://www.ebi.ac.uk/pdbe/pdb-component-library/js/pdbe-molstar-component-3.1.3.js",
// tagPosition: 'bodyClose'
}
]
})
export interface Props {
height?: number
// dataUrls?: MaybeRef<string[] | undefined>
format?: "pdb" | "cif"
}
const props = withDefaults(defineProps<Props>(), {
height: 600,
format: 'pdb',
// dataUrls: undefined
})
const dataUrls = defineModel()
const pdbeMolstarComponent = ref(null)
const dialog = ref(false)
const moleculeFormat: Ref<string> = ref("pdb")
const selectedPdb: Ref<string | null> = ref(null)
const structureToDownload: Ref<string | null> = ref(null)
const computedWidth = computed(() => {
// if (toValue(width) > toValue(maxWidth)) return toValue(maxWidth) / 1.5
return toValue(width) / 1.5
})
const computedHeight = computed(() => {
return height.value - 250
})
function refinedUrl(url: string) {
if (url?.startsWith('/') && !url.startsWith('//')) {
const _base = withLeadingSlash(withTrailingSlash(useRuntimeConfig().app.baseURL))
if (_base !== '/' && !url.startsWith(_base)) {
return joinURL(_base, url)
}
}
return url
}
function viewPdb(pdbPath: string | null) {
if (pdbPath !== null) {
dialog.value = true
const format = toValue(pdbPath)?.split(".").slice(-1)[0]?.toLowerCase() ?? "pdb"
moleculeFormat.value = format
if (pdbeMolstarComponent.value?.viewerInstance) {
const viewerInstance = pdbeMolstarComponent.value.viewerInstance
const customData = { url: pdbPath, format: format, binary: false }
viewerInstance.visual.update({ customData })
}
}
}
const refinedDataUrls = computed(() => {
const toValDataUrls = toValue(dataUrls)
if (toValDataUrls !== undefined && toValDataUrls.length > 0) {
return toValDataUrls.map((dataUrl) => {
return refinedUrl(dataUrl)
// return dataUrl
})
}
return toValDataUrls
})
function closeStructure() {
selectedPdb.value = null
dialog.value = false
}
watch(selectedPdb, (newSelectedPdb, prevSelectPdb) => {
viewPdb(newSelectedPdb)
structureToDownload.value = newSelectedPdb
})
watchEffect(() => {
const toValUrl = toValue(refinedDataUrls)
if (toValUrl?.length > 0) {
dialog.value = true
}
else {
dialog.value = false
}
})
</script>
<template>
<v-dialog v-model="dialog" transition="dialog-bottom-transition" fullscreen :scrim="false">
<v-card flat :rounded="false">
<v-toolbar>
<v-toolbar-title>Structures</v-toolbar-title>
<v-select v-model="selectedPdb" label="Select PDB" :items="refinedDataUrls"
hide-details="auto"></v-select>
<v-spacer></v-spacer>
<v-btn :disabled="!selectedPdb" icon="md:download" :href="structureToDownload"></v-btn>
<v-btn icon @click="closeStructure">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-row>
<v-col :cols="mobile ? 12 : 'auto'">
<v-sheet v-if="selectedPdb"
class="d-flex align-center justify-center flex-wrap text-center mx-auto px-4 my-3"
:height="computedHeight" :width="computedWidth" style="position:relative;">
<pdbe-molstar ref="pdbeMolstarComponent" :custom-data-url="selectedPdb" alphafold-view
sequence-panel="true" landscape="false"
:custom-data-format="moleculeFormat"></pdbe-molstar>
</v-sheet>
</v-col>
<v-col v-if="moleculeFormat === 'cif'" :cols="mobile ? 12 : undefined">
<v-img :src="selectedPaePath"></v-img>
<v-card flat color="transparent">
<v-card-title>Model Confidence</v-card-title>
<v-card-text>
AlphaFold produces a per-residue model
confidence score (pLDDT) between 0 and 100. Some regions below 50 pLDDT may be
unstructured
in isolation.
</v-card-text>
<v-list>
<v-list-item>
<template #prepend>
<div class="legendColor mr-2" style="background-color: rgb(0, 83, 214);">
&nbsp;</div>
</template>
<v-list-item-title>
Very high (pLDDT > 90)
</v-list-item-title>
</v-list-item>
<v-list-item>
<template #prepend>
<div class="legendColor mr-2" style="background-color: rgb(101, 203, 243);">
&nbsp;</div>
</template>
<v-list-item-title>
High (90 > pLDDT > 70)
</v-list-item-title>
</v-list-item>
<v-list-item>
<template #prepend>
<div class="legendColor mr-2" style="background-color: rgb(255, 219, 19);">
&nbsp;</div>
</template>
<v-list-item-title>
Low (70 > pLDDT > 50) </v-list-item-title>
</v-list-item>
<v-list-item>
<template #prepend>
<div class="legendColor mr-2" style="background-color: rgb(255, 125, 69);">
&nbsp;</div>
</template>
<v-list-item-title>
Very low (pLDDT &lt; 50) </v-list-item-title>
</v-list-item>
</v-list>
</v-card>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
</template>
\ No newline at end of file
......@@ -6,13 +6,12 @@ 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")
......@@ -163,49 +162,60 @@ const { refinedUrl: downloadAllCifUrl } = useRefinedUrl("/df-all-cifs.tar.gz")
: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"
<v-row>
<v-col cols="12" md="4">
<v-card flat variant="tonal">
<v-card-item class="mb-4">
<v-card-title> pLDDT
</v-card-title>
</v-card-item>
<v-card-text class="pr-0">
<v-range-slider v-model="plddtsRange" strict density="compact" hide-details="auto" class=""
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>
<v-btn variant="text" density="compact" 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-card-text>
</v-card>
</v-col>
<v-col cols="12" md="4">
<v-card flat variant="tonal">
<v-card-item class="mb-4">
<v-card-title> iptm+ptm
</v-card-title>
</v-card-item>
<v-card-text class="pr-0">
<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>
<v-btn variant="text" density="compact" 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-card-text></v-card>
</v-col>
<v-col cols="12" md="4">
<v-card flat variant="tonal">
<v-card-item class="mb-4">
<v-card-title> pDockQ
</v-card-title>
</v-card-item>
<v-card-text class="pr-0">
<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>
<v-btn variant="text" density="compact" icon="md:restart_alt"
@click="pdockqReset()"></v-btn>
</template>
</v-range-slider>
</v-col></v-row>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-col>
</v-row>
</template>
<template #[`item.Foldseek_name`]="{ item }">
<FoldseekDialog v-if="item.Foldseek_name !== 'na'" :foldseek-path="toFolseekUrl(item)"></FoldseekDialog>
......@@ -227,16 +237,6 @@ const { refinedUrl: downloadAllCifUrl } = useRefinedUrl("/df-all-cifs.tar.gz")
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>
......
import { withTrailingSlash, withLeadingSlash, joinURL } from "ufo";
import { withoutTrailingSlash, withLeadingSlash, joinURL } from "ufo";
export function useRefinedUrl(url: string | Ref<string>) {
export function useRefinedUrl(url: MaybeRef<string>) {
const refinedUrl = computed(() => {
const sanitzedUrl = toValue(url)
if (sanitzedUrl.startsWith("/") && !sanitzedUrl.startsWith("//")) {
const _base = withLeadingSlash(
withTrailingSlash(useRuntimeConfig().app.baseURL)
withoutTrailingSlash(useRuntimeConfig().app.baseURL)
);
if (_base !== "/" && !sanitzedUrl.startsWith(_base)) {
return joinURL(_base, sanitzedUrl);
}
}
return toValue(url);
return sanitzedUrl;
});
return { refinedUrl }
}
\ No newline at end of file
......@@ -73,65 +73,12 @@ The system was detected in 366 different species.
Proportion of genome encoding the Avs system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
### AVAST_I
##### Example 1
## Structures
::molstar-pdbe-plugin
---
height: 700
dataUrls:
- /avs/AVAST_I.AVAST_I__Avs1A.0.V.cif
- /avs/AVAST_I.AVAST_I__Avs1C.0.V.cif
- /avs/AVAST_I.AVAST_I__Avs1B.0.V.cif
---
::article-structure
::
### AVAST_II
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrls:
- /avs/AVAST_II__Avs2A.cif
---
::
### AVAST_III
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrls:
- /avs/AVAST_III.AVAST_III__Avs3B.0.V.cif
- /avs/AVAST_III.AVAST_III__Avs3A.0.V.cif
---
::
### AVAST_IV
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrls:
- /avs/AVAST_IV__Avs4A.cif
---
::
### AVAST_V
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrls:
- /avs/AVAST_V__Avs5A.cif
---
::
## Experimental validation
......
This diff is collapsed.
......@@ -24,7 +24,7 @@
"@unocss/preset-icons": "^0.58.3",
"@vueuse/core": "^10.7.1",
"@vueuse/nuxt": "^10.7.1",
"nuxt": "^3.9.1",
"nuxt": "^3.11.1",
"nuxt-meilisearch": "^1.1.0",
"vuetify-nuxt-module": "^0.9.0"
},
......