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 (29)
Showing
with 1577 additions and 398 deletions
data/ filter=lfs diff=lfs merge=lfs -text
data/**/*.csv filter=lfs diff=lfs merge=lfs -text
data/**/*.tsv filter=lfs diff=lfs merge=lfs -text
......@@ -203,6 +203,15 @@ lint:
--dir content/3.defense-systems/
--pfam public/pfam-a-hmm.csv
--output data/list-systems.json
- df-wiki-cli meilisearch delete-all-documents refseq
- >
df-wiki-cli
meilisearch
--host ${MEILI_HOST}
--key ${MEILI_MASTER_KEY}
index-update
refseq
sys_id
- >
df-wiki-cli
meilisearch
......@@ -486,7 +495,7 @@ build:prod:wiki:
deploy:dev:
extends: .deploy
rules:
- if: $CI_COMMIT_BRANCH == "dev"
- if: $CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "refactor-facet-autocomplete"
needs:
- "build:dev:wiki"
when: manual
......
This diff is collapsed.
<script setup lang="ts">
import { filter } from '@observablehq/plot'
export interface FilterItem {
type: 'facet' | 'innerOperator' | 'outerOperator' | 'value'
value: string
title: string
count?: number
deletable: boolean
props: Record<string, any>
}
export interface FacetItem {
title: string
value: string
type: "facet"
icon?: string
count?: number
}
export interface OperatorItem {
title: string,
type: 'operator'
}
export interface FacetCategory {
title: string
type: "subheader"
}
export interface FacetDivider {
type: "divider"
}
export type FacetInputItem = FacetItem | FacetCategory | FacetDivider
export interface Props {
db: string
modelValue: FilterItem[] | undefined
facets: MaybeRef<FacetInputItem[] | undefined>
facetDistribution: MaybeRef<Record<string, Record<string, number>> | undefined>
isValidFilters?: MaybeRef<boolean>
autocompleteProps?: Record<string, any>
}
const emit = defineEmits(['update:modelValue', "meiliFilters"])
const filterId = ref<number>(0)
const props = withDefaults(defineProps<Props>(), {
modelValue: undefined,
autocompleteProps: () => {
return {
chips: true,
clearable: true,
multiple: true,
"auto-select-first": true,
"return-object": true,
"prepend-inner-icon": "md:filter_alt",
"hide-details": "auto",
"item-value": "value", "item-title": "title",
label: "Filter results...",
"single-line": true,
}
},
isValidFilters: false
});
// const { result: msResult } = useMeiliSearch(props.db)
const isAutocompleteFocused = ref<boolean>(false)
// const facetDistribution: Ref<Record<string, Record<string, number>>> = useState('facetDistribution')
const autocompleteProps = computed(() => {
return {
...props.autocompleteProps,
items: toValue(autocompleteItems)
}
})
const filterStep = computed(() => {
const toValFilterItems = toValue(props.modelValue)
if (toValFilterItems !== undefined) {
return toValFilterItems.length % 4
}
})
const innerOperatorItems = ref<FilterItem[]>([
{
type: "innerOperator", value: '=', title: "is", deletable: false, props: {
type: "innerOperator", deletable: false
}
}, {
type: "innerOperator", value: '!=', title: "is not", deletable: false, props: {
type: "innerOperator",
deletable: false
}
},
])
const outerOperatorItems = ref<FilterItem[]>([
{
type: "outerOperator", value: 'AND', title: "AND", deletable: false, props: {
type: "outerOperator", deletable: false
}
}, {
type: "outerOperator", value: 'OR', title: "OR", deletable: false, props: {
type: "outerOperator",
deletable: false
}
},
])
const autocompleteItems = computed(() => {
const toValFilterItems = toValue(props.modelValue)
// const index = toValFilterItems?.length ?? 0
if (filterStep.value === undefined || filterStep.value === 0) {
filterId.value++
return toValue(props.facets)?.map(facetItem => {
switch (facetItem.type) {
case "facet":
return {
type: "facet",
value: `${facetItem.value}-${filterId.value}`,
title: facetItem.title,
deletable: false,
icon: facetItem?.icon,
count: facetItem?.count,
props: {
deletable: false,
type: "facet"
}
}
case "subheader":
return {
type: "subheader",
title: facetItem.title,
deletable: false,
props: {
type: "subheader"
}
}
case "divider":
return { type: "divider" }
default:
break;
}
})
}
if (filterStep.value === 1) {
filterId.value++
return innerOperatorItems.value.map(it => { return { ...it, value: `${it.value}-${filterId.value}`, } })
}
if (filterStep.value === 2) {
filterId.value++
// get the facet value
if (Array.isArray(toValFilterItems)) {
const { type, value } = toValFilterItems?.slice(-2, -1)[0]
const sanitizedValue = value.split("-")[0]
const facetDistri = toValue(props.facetDistribution)
return facetDistri?.[sanitizedValue] ? Object.entries(facetDistri[sanitizedValue]).map(([key, val]) => {
return {
type: "value", value: `${key}-${filterId.value}`, title: key, count: val, deletable: true, props: {
type: "value", count: val, deletable: true
}
}
}) : []
}
}
if (filterStep.value === 3) {
filterId.value++
return outerOperatorItems.value.map(it => { return { ...it, value: `${it.value}-${filterId.value}`, } })
}
})
const hasFacetDistribution = computed(() => {
const toValFacetDistribution = toValue(props.facetDistribution)
return toValFacetDistribution !== undefined && Object.keys(toValFacetDistribution).length > 0
})
function updateAutocompleteFocused(isFocused: boolean) {
isAutocompleteFocused.value = isFocused
}
function emitUpdateModelValue(filters: MaybeRef<FilterItem[] | undefined>) {
emit('update:modelValue', toValue(filters))
}
function clearFilters() {
emitUpdateModelValue(undefined)
}
function deleteOneFilter(index: number) {
const toValFilterItems = toValue(props.modelValue)
// check if the next item is an outeroperator
const nextFilterItem = toValFilterItems?.slice(index + 1, index + 2)
if (index + 1 === toValFilterItems?.length && toValFilterItems?.length >= 7) {
// need to remove the previous outer operator
toValFilterItems?.splice(index - 3, 4)
}
else if (nextFilterItem?.length === 1 && nextFilterItem[0].type === 'outerOperator') {
toValFilterItems?.splice(index - 2, 4)
}
else {
toValFilterItems?.splice(index - 2, 3)
}
emitUpdateModelValue(toValFilterItems)
}
function isItemFilter(type: string | undefined) {
return type === "facet" || type === "innerOperator" || type === "outerOperator" || type === "value"
}
const hint = ref<string>('All <span class="font-weight-bold">OR</span> in a row are grouped together. Example: <span class="font-weight-bold">brex OR avs AND Archea</span> &rArr; <span class="font-weight-bold">(brex OR avs) AND Archea</span>')
</script>
<template>
<v-autocomplete :model-value="props.modelValue" @click:clear="clearFilters" v-bind="autocompleteProps" :hint="hint"
persistent-hint @update:focused="updateAutocompleteFocused" @update:modelValue="emitUpdateModelValue"
:loading="!hasFacetDistribution" :disabled="!hasFacetDistribution">
<template #message="{ message }">
<span v-html="message"></span>
</template>
<template #item="{ props, item }">
<v-list-item v-if="isItemFilter(item?.raw?.type)" v-bind="{ ...props, active: false }" :title="item.title"
:prepend-icon="item?.raw?.icon ? item.raw.icon : undefined"
:subtitle="item.raw?.count ? item.raw.count : ''" :value="props.value">
</v-list-item>
<v-divider v-if="item.raw.type === 'divider'"></v-divider>
<v-list-subheader v-if="item.raw.type === 'subheader'" :title="item.raw.title"></v-list-subheader>
</template>
<template #chip="{ props, item, index }">
<v-chip v-bind="props" :text="item.raw.title" :closable="item.props.deletable"
@click:close="item.props.type === deleteOneFilter(index)"></v-chip>
</template>
<!-- <template #append>
<v-btn variant="text" icon="md:filter_alt" @click="emitUpdateModelValue(props.modelValue)"
:disabled="!isValidFilters"></v-btn>
</template> -->
</v-autocomplete>
</template>
\ No newline at end of file
......@@ -32,7 +32,7 @@ watchEffect(() => {
const sections = ref([
{
id: "webservice",
label: "webservice",
label: "Webservice",
href: runtimeConfig.public.defenseFinderWebservice,
},
{ id: "wiki", label: "Wiki", to: '/', },
......
<script setup lang="ts">
// import type { FacetDistribution } from "meilisearch";
import { useCsvDownload } from "@/composables/useCsvDownload"
import { useMeiliFilters } from "@/composables/useMeiliFilters"
import { useSlots } from 'vue'
import { useDisplay } from "vuetify";
import { useThrottleFn } from '@vueuse/core'
import type { FacetInputItem, FilterItem } from '@/components/AutocompleteMeiliFacets.vue'
import { useMeiliSearch } from "#imports"
import type { Filter } from "meilisearch";
// import { saveAs } from "file-saver";
import { useFileSystemAccess } from '@vueuse/core'
export interface SortItem {
key: string,
order: boolean | 'asc' | 'desc'
......@@ -22,47 +23,57 @@ export interface NumericalFilter {
export interface NumericalFilterModel extends NumericalFilter {
model: [number, number]
}
export interface AutocompleteMeiliFacetProps {
db: string
facets: FacetInputItem[] | undefined
facetDistribution: Record<string, Record<string, number>> | undefined
}
export interface Props {
title?: string
db?: string
sortBy?: SortItem[]
facets: MaybeRef<string[]>
numericalFilters?: MaybeRef<string | undefined>
numericalFilters?: Ref<string[] | undefined>
dataTableServerProps: Record<string, any>
columnsToDownload?: MaybeRef<string[] | undefined>
autocompleteMeiliFacetsProps: AutocompleteMeiliFacetProps
}
export interface FilterItem {
type: 'facet' | 'operator' | 'value' | 'text'
value: string
title: string
count?: number
deletable: boolean
props: Record<string, any>
}
const props = withDefaults(defineProps<Props>(), {
title: '',
db: 'refseq',
columnsToDownload: undefined,
sortBy: () => [{ key: "type", order: "asc" }],
numericalFilters: undefined
numericalFilters: () => ref(undefined),
autocompleteMeiliFacetsProps: () => {
return {
db: 'refseq',
facetDistribution: undefined,
facets: undefined
}
}
});
// const facetDistribution: Ref<Record<string, Record<string, number>> | undefined> = useState(`refseqFacetDistribution`)
const slots = useSlots()
const sortByRef = toRef(props.sortBy)
const facetsRef = toRef(props.facets)
const emit = defineEmits(["refresh:search"])
const { search: msSearch, result: msResult } = useMeiliSearch(props.db)
const { search: msSearch, result: msResult } = useMeiliSearch(props.autocompleteMeiliFacetsProps.db)
const search: Ref<string> = ref("");
const filterOrSearch: Ref<FilterItem[] | null> = ref(null)
const hitsPerPage: Ref<number> = ref(25)
const itemsPerPage: Ref<number[]> = ref([25, 50, 100])
const filterError: Ref<string | null> = ref(null)
const msFilter: Ref<string | undefined> = ref(undefined)
// const msFilter: Ref<string | undefined> = ref(undefined)
const page = ref(1)
let loading = ref(false)
const expanded = ref([])
......@@ -75,6 +86,9 @@ const computedTableHeight = computed(() => {
const pendingDownloadData = ref(false)
const toRefNumericalFilters = toRef(props.numericalFilters)
// const meiliFilters = ref<string | undefined>(undefined)
const filterInputValues = computed(() => {
if (filterOrSearch.value != null) {
return filterOrSearch.value.filter(({ props }) => props.type !== 'text')
......@@ -83,9 +97,6 @@ const filterInputValues = computed(() => {
}
})
const isFilter = computed(() => {
return Array.isArray(filterOrSearch.value)
})
const msSortBy = computed(() => {
if (sortByRef.value.length > 0) {
......@@ -114,26 +125,67 @@ const notPaginatedParams = computed(() => {
watch([paginationParams, msSortBy, page], ([newParams, newSort, newPage]) => {
searchOrFilter()
if (toValue(isValidFilters)) {
searchOrFilter()
}
})
onMounted(async () => {
onBeforeMount(async () => {
searchOrFilter()
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 computedFilter = computed(() => {
return [toValue(msFilter), props.numericalFilters].filter(f => f !== undefined).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
// }
// })
watch(computedFilter, () => {
searchOrFilter()
emitRefreshRes()
console.log(toValue(computedFilter))
if (toValue(isValidFilters) && (toValue(computedFilter) !== undefined || toValue(filterInputValues) === null)) {
searchOrFilter()
emitRefreshRes()
}
})
......@@ -147,154 +199,86 @@ const throttleSearch = useThrottleFn(async () => {
searchOrFilter()
emitRefreshRes()
}, 300)
async function searchOrFilter() {
// do something, it will be called at most 1 time per second
try {
loading.value = true
// const q = queryInputValue.value === null ? "" : queryInputValue.value
const q = search.value
await msSearch(q, { ...paginationParams.value, filter: toValue(computedFilter), sort: msSortBy.value })
} catch (error: any) {
filterError.value = error
console.log(error)
const lastFilterItem = computed(() => {
const toValFilterItems = toValue(msFilterCompo)
if (toValFilterItems !== undefined && Array.isArray(toValFilterItems)) {
return toValFilterItems.slice(-1)[0]
}
})
const isValidFilters = computed(() => {
const toValFilterItems = toValue(msFilterCompo)
if (toValFilterItems === undefined || Array.isArray(toValFilterItems) && toValFilterItems?.length === 0) {
return true
}
finally {
loading.value = false
else {
const toValLastFilterItem = toValue(lastFilterItem)
if (toValLastFilterItem !== undefined) {
console.log(toValLastFilterItem.type)
console.log(toValLastFilterItem.type === 'value')
return toValLastFilterItem.type === 'value'
// && isAutocompleteFocused.value === false
// || (toValFilterStep === 0 && toValLastFilterItem.type === "outerOperator" && toValLastFilterItem.value.split("-")[0] === "AND")
}
}
return false
})
async function searchOrFilter() {
if (toValue(isValidFilters)) {
// do something, it will be called at most 1 time per second
try {
loading.value = true
// const q = queryInputValue.value === null ? "" : queryInputValue.value
const q = search.value
await msSearch(q, { ...paginationParams.value, filter: toValue(computedFilter), sort: msSortBy.value })
} catch (error: any) {
filterError.value = error
console.log(error)
}
finally {
loading.value = false
}
}
}
function emitRefreshRes() {
console.log("emit refresh:search")
const q = search.value
emit("refresh:search", {
index: props.db,
index: props.autocompleteMeiliFacetsProps.db,
query: q,
params: { ...notPaginatedParams.value, filter: toValue(computedFilter), sort: msSortBy.value }
})
}
function clearFilterOrSearch() {
filterOrSearch.value = null
// searchOrFilter()
// emitRefreshRes()
}
// watch(msFilter, async (fos) => {
// searchOrFilter()
// emitRefreshRes()
// search.value = ''
// })
const totalHits = computed(() => {
return toValue(msResult)?.totalHits ?? toValue(msResult)?.estimatedTotalHits ?? 0
})
watch(filterInputValues, (newSoF) => {
if (isFilter.value && filterInputValues.value !== null && filterInputValues.value?.length % 3 === 0) {
msFilter.value = filterInputValues.value.map((it, index) => {
const sanitizedValue = it.value.split("-").slice(0, -1).join("-")
if (index >= 1 && (index + 1) % 3 === 1) {
return ` AND ${sanitizedValue}`
} else if ((index + 1) % 3 === 0) {
return `"${sanitizedValue}"`
} else {
return `${sanitizedValue}`
}
}).join("")
}
})
watch(search, () => {
searchOrFilter()
emitRefreshRes()
// emitRefreshRes()
})
const filterStep = computed(() => {
return filterInputValues.value !== null && filterInputValues.value.length > 0 ? filterInputValues.value?.length % 3 : null
})
const operatorItems = ref([
{
type: "operator", value: '=', title: "is", deletable: false, props: {
type: "operator", deletable: false
}
}, {
type: "operator", value: '!=', title: "is not", deletable: false, props: {
type: "operator",
deletable: false
}
},
])
const autocompleteItems = computed(() => {
const index = filterOrSearch.value?.length ?? 0
// console.log(index)
if (filterStep.value === null || filterStep.value === 0) {
return toValue(facetsRef).map(value => {
return {
type: "facet",
value: `${value}-${index}`,
title: value,
deletable: false,
props: {
deletable: false,
type: "facet"
}
}
})
}
if (filterStep.value === 1) {
return operatorItems.value.map(it => { return { ...it, value: `${it.value}-${index}`, } })
}
if (filterStep.value === 2) {
// get the facet value
if (Array.isArray(filterOrSearch.value)) {
const { type, value } = filterOrSearch.value?.slice(-2, -1)[0]
const sanitizedValue = value.split("-")[0]
// console.log("compute new facets")
const facetDistri = msResult.value?.facetDistribution
// console.log(facetDistri)
return facetDistri?.[sanitizedValue] ? Object.entries(facetDistri[sanitizedValue]).map(([key, val]) => {
return {
type: "value", value: `${key}-${index}`, title: key, count: val, deletable: true, props: {
type: "value", count: val, deletable: true
}
}
}) : []
}
}
})
function selectItem(item) {
filterOrSearch.value = Array.isArray(filterOrSearch.value) ? [...filterOrSearch.value, item] : [item]
}
function deleteOneFilter(index: number) {
if (isFilter.value) {
filterOrSearch.value?.splice(index - 2, 2)
}
}
// watch(msFilterCompo, () => {
// searchOrFilter()
function clearSearch() {
search.value = ""
}
// })
async function downloadData() {
pendingDownloadData.value = true
try {
const { data } = await useAsyncMeiliSearch({
index: props.db,
index: props.autocompleteMeiliFacetsProps.db,
params: { ...toValue(notPaginatedParams), filter: toValue(computedFilter), sort: toValue(msSortBy) },
query: toValue(search),
})
......@@ -304,84 +288,50 @@ async function downloadData() {
pendingDownloadData.value = false
}
}
function focusedOrBlur(isFocused: boolean) {
if (!isFocused) {
emitRefreshRes()
}
}
</script>
<template>
<v-card flat color="transparent">
<v-card-text>
</v-card-text>
<v-card-text>
<slot name="numerical-filters" :search="throttleSearch"></slot>
</v-card-text>
<slot name="numerical-filters" :search="throttleSearch"></slot>
<v-data-table-server v-if="!msError" v-model:page="page" color="primary" v-bind="dataTableServerProps"
v-model:items-per-page="hitsPerPage" v-model:sortBy="sortByRef" v-model:expanded="expanded" fixed-header
:loading="loading" :items="msResult?.hits ?? []" :items-length="totalHits" density="compact"
:items-per-page-options="itemsPerPage" :height="computedTableHeight" class="elevation-1 mt-2">
<template #top>
<template v-if="mobile">
<v-toolbar> <v-badge :content="totalHits" color="primary" class="mx-2">
<v-btn prepend-icon="md:download" :loading="pendingDownloadData" variant="text" color="primary"
@click="downloadData()">{{
props.title }}
</v-btn>
</v-badge></v-toolbar>
<v-toolbar><v-text-field v-model="search" label="Search..." hide-details="auto"
prepend-inner-icon="mdi-magnify" single-line clearable class="mx-2"></v-text-field></v-toolbar>
<v-toolbar><v-autocomplete ref="autocompleteInput" hide-details v-model:model-value="filterOrSearch"
auto-select-first chips clearable label="Filter results..." :items="autocompleteItems"
single-line item-value="value" item-title="title" multiple return-object
prepend-inner-icon="md:search" @click:appendInner="searchOrFilter" class="mx-2"
@click:clear="clearFilterOrSearch" @update:modelValue="() => clearSearch()">
<template #chip="{ props, item, index }">
<v-chip v-bind="props" :text="item.raw.title" :closable="item.props.deletable"
@click:close="item.props.type === deleteOneFilter(index)"></v-chip>
</template>
<template #item="{ props, item }">
<v-list-item v-bind="{ ...props, active: false, onClick: () => selectItem(item) }"
:title="item.title" :subtitle="item.raw?.count ? item.raw.count : ''"
:value="props.value">
</v-list-item>
</template>
</v-autocomplete></v-toolbar>
</template>
<template v-else>
<v-toolbar>
<v-card variant="flat" color="transparent">
<v-card-title>
<v-badge :content="totalHits" color="primary" class="mr-3">
<v-btn prepend-icon="md:download" :loading="pendingDownloadData" variant="text" color="primary"
@click="downloadData()">{{
props.title }}
</v-btn>
</v-badge>
<v-spacer></v-spacer>
<v-card variant="flat" color="transparent" :min-width="400" class="mx-2" :rounded="false">
<v-text-field v-model="search" label="Search..." hide-details="auto"
prepend-inner-icon="mdi-magnify" single-line clearable></v-text-field>
</v-card>
<v-card variant="flat" color="transparent" :min-width="500" class="mx-2" :rounded="false">
<v-autocomplete ref="autocompleteInput" hide-details v-model:model-value="filterOrSearch"
auto-select-first chips clearable label="Filter results..." :items="autocompleteItems"
single-line item-value="value" item-title="title" multiple return-object
prepend-inner-icon="md:search" @click:appendInner="searchOrFilter"
@click:clear="clearFilterOrSearch" @update:modelValue="() => clearSearch()">
<template #chip="{ props, item, index }">
<v-chip v-bind="props" :text="item.raw.title" :closable="item.props.deletable"
@click:close="item.props.type === deleteOneFilter(index)"></v-chip>
</template>
<template #item="{ props, item }">
<v-list-item v-bind="{ ...props, active: false, onClick: () => selectItem(item) }"
:title="item.title" :subtitle="item.raw?.count ? item.raw.count : ''"
:value="props.value">
</v-list-item>
</template>
</v-autocomplete>
</v-card>
</v-toolbar>
</template>
</v-card-title>
<v-card-title>
<v-text-field v-model="search" label="Search..." hide-details="auto" :disabled="pendingDownloadData"
prepend-inner-icon="mdi-magnify" single-line clearable
@update:focused="focusedOrBlur"></v-text-field>
</v-card-title>
<v-card-title>
<AutocompleteMeiliFacets v-model="msFilterCompo" v-bind="props.autocompleteMeiliFacetsProps"
:is-valid-filters="isValidFilters">
</AutocompleteMeiliFacets>
</v-card-title>
</v-card>
</template>
<template v-for="(slot, index) of Object.keys(slots)" :key="index" v-slot:[slot]="data">
......
......@@ -107,7 +107,7 @@ const pdbeMolstarComponent = ref(null)
const selectedPdb: Ref<string | null> = ref(null)
const structureToDownload: Ref<string | null> = ref(null)
const selectedPaePath = computed(() => {
return selectedPdb.value ? `${selectedPdb.value.split(".").slice(0, -1).join('.')}.png` : null
return selectedPdb.value ? `${selectedPdb.value.split(".").slice(0, -1).join('.')}.png` : undefined
})
watch(selectedPdb, (newSelectedPdb, prevSelectPdb) => {
......@@ -190,10 +190,9 @@ const moleculeFormat: Ref<string> = ref("pdb")
<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" position="relative">
<pdbe-molstar ref="pdbeMolstarComponent" landscape="true" hide-controls="true"
:custom-data-url="selectedPdb" alphafold-view="true"
:custom-data-format="moleculeFormat"></pdbe-molstar>
: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 :cols="mobile ? 12 : undefined">
......@@ -272,4 +271,4 @@ div.msp-plugin-content.msp-layout-expanded {
height: 16px;
width: 16px;
}
</style>
\ No newline at end of file
</style>
......@@ -2,36 +2,98 @@
import * as Plot from "@observablehq/plot";
import PlotFigure from "~/components/PlotFigure";
import { useDisplay } from "vuetify";
import type { SortItem } from "@/components/ServerDbTable.vue"
import { ServerDbTable } from "#components"
import { useSerialize } from "@/composables/useSerialize";
import { useRasterize } from "@/composables/useRasterize";
import { useDownloadBlob } from '@/composables/useDownloadBlob';
import type { SortItem, AutocompleteMeiliFacetProps } from "@/components/ServerDbTable.vue"
import type { ComponentPublicInstance } from 'vue'
const sortBy: Ref<SortItem[]> = ref([{ key: 'type', order: "asc" }])
const itemValue = ref("id");
const { width } = useDisplay();
const scaleTransform: Ref<string[]> = ref([])
const dbName = ref("refseq")
onBeforeMount(async () => {
const { data } = await useAsyncMeiliSearch({
index: toValue(dbName), query: "", params: {
facets: ["*"],
filter: [],
page: 1,
hitsPerPage: 25,
}
})
autocompleteMeiliFacetsProps.value.facetDistribution = toValue(data)?.facetDistribution
})
onMounted(async () => {
const { data } = await useAsyncMeiliSearch({
index: toValue(dbName), query: "", params: {
facets: ["*"],
filter: [],
page: 1,
hitsPerPage: 25,
}
})
autocompleteMeiliFacetsProps.value.facetDistribution = toValue(data)?.facetDistribution
})
const { serialize } = useSerialize()
const { rasterize } = useRasterize()
const { download } = useDownloadBlob()
const facets = ref([
"replicon",
"type",
"subtype",
"Superkingdom",
"phylum",
"order",
"family",
"genus",
"species",
])
const autocompleteMeiliFacetsProps = ref<AutocompleteMeiliFacetProps>({
db: toValue(dbName),
facets: [
{ title: "Defense System", type: "subheader" },
{ title: "System", value: "type", type: "facet", icon: "mdi-virus-outline", },
{ title: "Subsystem", value: "subtype", type: "facet", icon: "mdi-virus-outline" },
{ type: "divider" },
{ title: "Taxonomy", type: "subheader" },
{ title: "Superkingdom", value: "Superkingdom", type: "facet", icon: "mdi-family-tree" },
{ title: "Phylum", value: "phylum", type: "facet", icon: "mdi-family-tree" },
{ title: "Order", value: "order", type: "facet", icon: "mdi-family-tree" },
{ title: "Family", value: "family", type: "facet", icon: "mdi-family-tree" },
{ title: "Genus", value: "genus", type: "facet", icon: "mdi-family-tree" },
{ title: "Species", value: "species", type: "facet", icon: "mdi-family-tree" },
{ type: "divider" },
{ title: "Replicon", value: "replicon", type: "facet", icon: "mdi-dna", },
],
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 availableTaxo: Ref<string[]> = ref([
"species",
"genus",
......@@ -47,11 +109,11 @@ const selectedTaxoRank = ref("phylum");
const headers = ref([
{ title: "Replicon", key: "replicon" },
{
title: "Type",
title: "System",
key: "type",
},
{
title: "Subtype",
title: "Subsystem",
key: "subtype",
},
{
......@@ -72,7 +134,7 @@ const computedHeaders = computed(() => {
}
})]
})
const { result: msResult } = useMeiliSearch('refseq')
const { result: msResult } = useMeiliSearch(toValue(dbName))
const computedWidth = computed(() => {
const currentWidth = fullWidth.value ? width.value : width.value / 2
......@@ -82,20 +144,11 @@ const computedWidth = computed(() => {
const allHits: Ref<Record<string, any> | undefined> = ref(undefined)
// onMounted(async () => {
// console.log("on mounted get all hits")
// const params = {
// facets: ["*"],
// filter: undefined,
// sort: ["type:asc"],
// limit: 500000
// }
// getAllHits({ index: "refseq", params, query: "" })
// })
const pendingAllHits = ref(false)
async function getAllHits(params: { index: string, params: Record<string, any>, query: string }) {
console.log(params.index)
if (params.index === 'refseq') {
if (params.index === toValue(dbName)) {
console.log("get all hits in function ")
console.log(params)
pendingAllHits.value = true
......@@ -112,10 +165,6 @@ async function getAllHits(params: { index: string, params: Record<string, any>,
}
const plotHeight = computed(() => {
return computedWidth.value / 3;
// return 500
});
const defaultDataTableServerProps = ref({
showExpand: false
......@@ -222,7 +271,7 @@ function namesToAccessionChips(names: string[]) {
return namesToCollapsibleChips(names).map(it => {
return {
...it,
// href: new URL(it.title, "http://toto.pasteur.cloud").toString()
href: new URL(it.title, "https://www.ncbi.nlm.nih.gov/protein/").toString()
}
})
}
......@@ -249,10 +298,7 @@ const binPlotDataOptions = computed(() => {
type: scaleType.value,
tickFormat: '~s',
ticks: scaleType.value === 'symlog' ? 3 : 5,
// width: 350
},
// fy: { domain: groupSortDomain.value },
marks: [
Plot.cell(toValueAllHits?.hits ?? [], Plot.group({ fill: "count" }, { x: "type", y: selectedTaxoRank.value, tip: true, inset: 0.5, sort: { y: "fill" } })),
]
......@@ -419,13 +465,25 @@ async function downloadPng(component: ComponentPublicInstance | null, filename:
</v-expansion-panel>
</v-expansion-panels>
<ServerDbTable title="RefSeq" db="refseq" :sortBy="sortBy" :facets="facets"
<ServerDbTable title="RefSeq" :sortBy="sortBy"
:autocomplete-meili-facets-props="computedAutocompleteMeiliFacetsProps"
:data-table-server-props="dataTableServerProps" @refresh:search="(params) => getAllHits(params)">
<template #[`item.accession_in_sys`]="{ item }">
<CollapsibleChips :items="namesToAccessionChips(item.accession_in_sys)">
</CollapsibleChips>
</template>
<template #[`item.type`]="{ item }">
<v-chip color="info" link size="small" :to="`/defense-systems/${item.type.toLowerCase()}`" target="_blank"> {{
item.type }}
</v-chip>
</template>
<template #[`item.subtype`]="{ item }">
<v-chip color="info" link size="small" :to="`/defense-systems/${item.type.toLowerCase()}`" target="_blank"> {{
item.subtype }}
</v-chip>
</template>
</ServerDbTable>
......
<script setup lang="ts">
import * as Plot from "@observablehq/plot";
import PlotFigure from "~/components/PlotFigure";
import type { SortItem } from "@/components/ServerDbTable.vue"
import type { SortItem, AutocompleteMeiliFacetProps } from "@/components/ServerDbTable.vue"
import { useNumericalFilter } from "@/composables/useNumericalfilter"
import type { FacetInputItem } from '@/components/AutocompleteMeiliFacets.vue'
import { ServerDbTable } from "#components"
import { ServerDbTable } from "#components"
const sortBy: Ref<SortItem[]> = ref([{ key: 'System', order: "asc" }])
const itemValue = ref("id");
const facets: Ref<string[]> = ref(["System", "subtype", "gene_name", "completed", "prediction_type",])
const dbName = ref("structure")
onBeforeMount(async () => {
console.log("dans le mounted refseq")
const { data } = await useAsyncMeiliSearch({
index: toValue(dbName), query: "", params: {
facets: ["*"],
filter: [],
page: 1,
hitsPerPage: 25,
}
})
autocompleteMeiliFacetsProps.value.facetDistribution = toValue(data)?.facetDistribution
})
const autocompleteMeiliFacetsProps = ref<AutocompleteMeiliFacetProps>({
db: toValue(dbName),
facets: [
{ title: "Defense System", type: "subheader" },
{ title: "System", value: "System", type: "facet", icon: "mdi-virus-outline", },
{ title: "Subsystem", value: "subtype", type: "facet", icon: "mdi-virus-outline" },
{ 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: "mdi-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 },
{ title: "System", key: "System", removable: false },
......@@ -30,14 +88,21 @@ const headers: Ref<Object[]> = ref([
])
const { search: msSearch, result: msResult } = useMeiliSearch('structure')
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].map(f => toValue(f)).filter(f => f !== undefined)
return listFilters.length > 0 ? listFilters.join(" AND ") : undefined
console.log("je compute mes numerical filters")
const listFilters = [plddtsFilter, iptmFilter, pdockqFilter].filter(isString).map(f => toValue(f))
console.log(listFilters)
return listFilters.length > 0 ? listFilters : undefined
})
const defaultDataTableServerProps = ref({
......@@ -69,61 +134,55 @@ function pdbNameToCif(pdbPath: string) {
return `${cifPath}.cif`
}
function toSystemName(rawName: string) {
// Does it work if it's a list of system genes ?
// split on __ for systeme_vgenes
return rawName.split("__")[0].toLocaleLowerCase()
}
const plddtDistribution = computed(() => {
if (toValue(msResult)?.facetDistribution?.plddts) {
return Object.entries(toValue(msResult).facetDistribution.plddts).map(([key, value]) => { })
}
})
</script>
<template>
<ServerDbTable title="Predicted Structures" db="structure" :sortBy="sortBy" :facets="facets"
:data-table-server-props="dataTableServerProps" :numerical-filters="numericalFilters">
<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-row>
<v-col cols="12" md="12" lg="4">
<v-range-slider v-model="plddtsRange" strict density="compact" hide-details="auto" label="pLDDT"
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-col cols="12" md="12" lg="4">
<v-range-slider v-model="iptmRange" strict density="compact" hide-details="auto" label="iptm+ptm"
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>
<!-- pdockqReset -->
<v-col cols="12" md="12" lg="4">
<v-range-slider v-model="pdockqRange" strict density="compact" hide-details="auto" label="pDockQ"
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>
<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">
<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">
<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">
<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.proteins_in_the_prediction`]="{ item }">
......
<script setup lang="ts">
import type { SortItem } from "@/components/ServerDbTable.vue"
import type { SortItem, AutocompleteMeiliFacetProps } from "@/components/ServerDbTable.vue"
import { ServerDbTable } from "#components"
const sortBy: Ref<SortItem[]> = ref([{ key: 'title', order: "asc" }])
const itemValue = ref("title");
const facets: Ref<string[]> = ref(["title", "Sensor", "Effector", "Activator", "PFAM.AC", "PFAM.DE"])
const dbName = ref("systems")
onBeforeMount(async () => {
const { data } = await useAsyncMeiliSearch({
index: toValue(dbName), query: "", params: {
facets: ["*"],
filter: [],
page: 1,
hitsPerPage: 25,
}
})
autocompleteMeiliFacetsProps.value.facetDistribution = toValue(data)?.facetDistribution
})
const autocompleteMeiliFacetsProps = ref<AutocompleteMeiliFacetProps>({
db: toValue(dbName),
facets: [
{ title: "Defense System", type: "subheader" },
{ title: 'System', value: "title", type: "facet", icon: "mdi-virus-outline" },
{ type: "divider" },
{ title: "Mechanism", type: "subheader" },
{ title: 'Sensor', value: "Sensor", type: "facet", icon: "mdi-cog" },
{ title: 'Effector', value: "Effector", type: "facet", icon: "mdi-cog" },
{ title: 'Activator', value: "Activator", type: "facet", icon: "mdi-cog" },
{ type: "divider" },
{ title: "PFAM", type: "subheader" },
{ title: 'Acession', value: "PFAM.AC", type: "facet", icon: "mdi-key" },
{ title: 'Description', value: "PFAM.DE", type: "facet", icon: "md:description" },
{ type: "divider" },
{ title: 'Contributor', value: "contributors", type: "facet", icon: "md:person" },
],
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: "System", key: "title", removable: false },
{ title: "Article", key: "doi", removable: false },
......@@ -16,7 +80,6 @@ const headers: Ref<Object[]> = ref([
])
const { search: msSearch, result: msResult } = useMeiliSearch('systems')
const defaultDataTableServerProps = ref({
showExpand: false
......@@ -34,7 +97,8 @@ const columnsToDownload = ref(['title', 'doi', 'Sensor', 'Activator', 'Effector'
</script>
<template>
<ServerDbTable title="List Systems" db="systems" :sortBy="sortBy" :facets="facets"
<ServerDbTable title="List Systems" :db="dbName" :sortBy="sortBy"
:autocomplete-meili-facets-props="computedAutocompleteMeiliFacetsProps"
:data-table-server-props="dataTableServerProps" :columns-to-download="columnsToDownload">
......@@ -48,8 +112,10 @@ const columnsToDownload = ref(['title', 'doi', 'Sensor', 'Activator', 'Effector'
<ArticleDoi v-if="item?.doi" :doi="item.doi" :abstract="item?.abstract" :divider="false" :enumerate="false" />
</template>
<template #[`item.PFAM`]="{ item }">
<pfam-chips v-if="item?.PFAM" :pfams="item.PFAM"></pfam-chips>
</template>
</ServerDbTable>
</template>
\ No newline at end of file
<template #[`item.contributors`]="{ item }">
<CollapsibleChips v-if="item?.contributors" :items="item.contributors.map(it => ({ title: it }))">
</CollapsibleChips>
</template>
</ServerDbTable></template>
\ No newline at end of file
export interface FilterItem {
type: 'facet' | 'operator' | 'value' | 'text'
value: string
title: string
count?: number
deletable: boolean
props: Record<string, any>
}
export function useFacetFilters(inputFilters: MaybeRef<FilterItem[] | undefined>) {
}
\ No newline at end of file
import type { FilterItem } from '@/components/AutocompleteMeiliFacets.vue'
import { filter } from '@observablehq/plot'
import type { Filter } from "meilisearch"
export function useMeiliFilters(filters: MaybeRef<FilterItem[] | undefined>, numericalFilters: Ref<string[] | undefined>) {
const arrayFilters = ref<Filter | undefined>(undefined)
const nf = toRef(numericalFilters)
watch(nf, () => {
console.log("mon nf", nf)
})
watch(numericalFilters,() => {
console.log("je watch numerical filter", numericalFilters)
})
watchEffect(() => {
console.log("dans le computed de useMeilisFilter")
const toValFilters = toValue(filters)
const toValNumericalFilter = toValue(nf)
console.log(toValNumericalFilter)
let categoricalFilters: FilterItem[] | undefined = undefined
if (toValFilters !== undefined && toValFilters.length >= 3) {
const cachedFilters = [...toValFilters]
// Remove last element if it is an outerOperator
if (cachedFilters.length % 4 === 0 && cachedFilters.slice(-1)[0].type === 'outerOperator') {
cachedFilters.splice(-1)
}
let previousOperator: 'AND' | 'OR' | undefined = undefined
const arrayFilters = cachedFilters.reduce<FilterItem[]>((acc, curr, index) => {
const sanitizedValue = curr.value.split("-").slice(0, -1).join("-")
const position = index + 1
switch (position % 4) {
case 0:
// if this is the first time pass by an outeroperator
if (previousOperator === undefined) {
const newFilter = acc.splice(-3).join("")
console.log("newFilter: ", newFilter)
if (sanitizedValue === 'OR') {
acc = [[newFilter]]
}
else {
acc.push(newFilter)
}
}
previousOperator = sanitizedValue
break;
case 3:
acc.push(`'${sanitizedValue}'`)
const newFilter = acc.splice(-3).join("")
if (previousOperator === 'AND') {
acc.push(newFilter)
}
else {
const previousElem = acc.slice(-1)[0]
if (Array.isArray(previousElem)) {
acc.slice(-1)[0].push(newFilter)
}
else if (previousElem !== undefined) {
const previousElem = acc.splice(-1)[0]
console.log("previousElem: ", previousElem)
console.log("newFilter: ", newFilter)
acc.push([previousElem, newFilter])
}
else {
acc.push(newFilter)
}
}
break
default:
acc.push(sanitizedValue)
break;
}
return acc
}, [])
categoricalFilters = arrayFilters.length > 0 ? arrayFilters : undefined
}
function isFilterItem(item: FilterItem[] | string[] | undefined): item is FilterItem[] | string[] {
return item !== undefined
}
console.log(categoricalFilters)
const filterItemArray = [categoricalFilters, toValNumericalFilter].filter(isFilterItem).flat()
console.log(filterItemArray)
// const returnArr = filterItemArray.reduce<FilterItem | string | undefined>((acc, curr) => {
// const toValCurr = toValue(curr)
// if (toValCurr === undefined) {
// return acc
// }
// else {
// if (acc !== undefined) {
// return [...acc, ...toValCurr]
// }
// else { return [...toValCurr] }
// }
// }, undefined)
// console.log(returnArr)
// return returnArr
arrayFilters.value = filterItemArray
})
// const arrayFilters: ComputedRef<Filter | undefined> = computed(() => {
// })
return { arrayFilters }
}
......@@ -6,17 +6,11 @@ export function useNumericalFilter(
const range: Ref<[number, number]> = ref([toValue(min), toValue(max)])
const stringifyFilter: Ref<string | undefined> = ref(`${toValue(id)} ${toValue(min)} TO ${toValue(max)}`)
const stringifyFilter: Ref<string | undefined> = ref(undefined)
watchEffect(() => {
console.log("watch reange")
console.log(range.value)
if (range.value[0] === toValue(min) && range.value[1] === toValue(max)) {
stringifyFilter.value = undefined
} else {
......
......@@ -44,35 +44,35 @@ The system was detected in 35 different species.
Proportion of genome encoding the AbiA system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiA_large
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abia/AbiA_large__AbiA_large-plddts_90.82916.pdb
dataUrls:
- /abia/AbiA_large__AbiA_large.cif
---
::
### AbiA_small
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abia/AbiA_small,AbiA_small__AbiA_SLATT,0,DF-plddts_94.13374.pdb
---
::
dataUrls:
- /abia/AbiA_small.AbiA_small__AbiA_small.0.DF.cif
- /abia/AbiA_small.AbiA_small__AbiA_SLATT.0.DF.cif
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abia/AbiA_small,AbiA_small__AbiA_small,0,DF-plddts_89.82347.pdb
---
::
## Experimental validation
<mermaid>
graph LR;
Chopin_2005[<a href='https://doi.org/10.1016/j.mib.2005.06.006'>Chopin et al., 2005</a>] --> Origin_0
......@@ -98,3 +98,4 @@ end
style Title3 fill:none,stroke:none,stroke-width:none
style Title4 fill:none,stroke:none,stroke-width:none
</mermaid>
......@@ -47,18 +47,22 @@ The system was detected in 5 different species.
Proportion of genome encoding the AbiB system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiB
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abib/AbiB__AbiB-plddts_74.5545.pdb
dataUrls:
- /abib/AbiB__AbiB.cif
---
::
## Experimental validation
<mermaid>
graph LR;
Chopin_2005[<a href='https://doi.org/10.1016/j.mib.2005.06.006'>Chopin et al., 2005</a>] --> Origin_0
......@@ -84,3 +88,4 @@ end
</mermaid>
......@@ -10,6 +10,9 @@ tableColumns:
Activator: Unknown
Effector: Unknown
PFAM: PF16872
relevantAbstracts:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
---
# AbiC
......@@ -35,18 +38,22 @@ The system was detected in 57 different species.
Proportion of genome encoding the AbiC system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiC
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abic/AbiC__AbiC-plddts_83.80335.pdb
dataUrls:
- /abic/AbiC__AbiC.cif
---
::
## Experimental validation
<mermaid>
graph LR;
Fillol-Salom_2022[<a href='https://doi.org/10.1016/j.cell.2022.07.014'>Fillol-Salom et al., 2022</a>] --> Origin_0
......@@ -102,14 +109,4 @@ end
style Title3 fill:none,stroke:none,stroke-width:none
style Title4 fill:none,stroke:none,stroke-width:none
</mermaid>
## Relevant abstracts
::relevant-abstracts
---
items:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
---
::
......@@ -10,6 +10,9 @@ tableColumns:
Activator: Unknown
Effector: Unknown
PFAM: PF07751
relevantAbstracts:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
---
# AbiD
......@@ -35,18 +38,22 @@ The system was detected in 964 different species.
Proportion of genome encoding the AbiD system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiD
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abid/AbiD__AbiD-plddts_91.87407.pdb
dataUrls:
- /abid/AbiD__AbiD.cif
---
::
## Experimental validation
<mermaid>
graph LR;
Chopin_2005[<a href='https://doi.org/10.1016/j.mib.2005.06.006'>Chopin et al., 2005</a>] --> Origin_0
......@@ -72,14 +79,4 @@ end
style Title3 fill:none,stroke:none,stroke-width:none
style Title4 fill:none,stroke:none,stroke-width:none
</mermaid>
## Relevant abstracts
::relevant-abstracts
---
items:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
---
::
......@@ -10,6 +10,10 @@ tableColumns:
Activator: Unknown
Effector: Unknown
PFAM: PF08843, PF09407, PF09952, PF11459, PF13338, PF17194
relevantAbstracts:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
- doi: 10.1093/nar/gkt1419
---
# AbiE
......@@ -44,25 +48,23 @@ The system was detected in 1107 different species.
Proportion of genome encoding the AbiE system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiE
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abie/AbiE,AbiE__AbiEi,0,V-plddts_85.81224.pdb
---
::
dataUrls:
- /abie/AbiE.AbiE__AbiEi.0.V.cif
- /abie/AbiE.AbiE__AbiEii.0.V.cif
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abie/AbiE,AbiE__AbiEii,0,V-plddts_90.73768.pdb
---
::
## Experimental validation
<mermaid>
graph LR;
Chopin_2005[<a href='https://doi.org/10.1016/j.mib.2005.06.006'>Chopin et al., 2005</a>] --> Origin_0
......@@ -86,15 +88,4 @@ end
style Title3 fill:none,stroke:none,stroke-width:none
style Title4 fill:none,stroke:none,stroke-width:none
</mermaid>
## Relevant abstracts
::relevant-abstracts
---
items:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
- doi: 10.1093/nar/gkt1419
---
::
......@@ -10,6 +10,9 @@ tableColumns:
Activator: Unknown
Effector: Unknown
PFAM: PF10899, PF16873
relevantAbstracts:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
---
# AbiG
......@@ -35,25 +38,23 @@ The system was detected in 22 different species.
Proportion of genome encoding the AbiG system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiG
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abig/AbiG,AbiG__AbiGi,0,V-plddts_92.08021.pdb
---
::
dataUrls:
- /abig/AbiG.AbiG__AbiGi.0.V.cif
- /abig/AbiG.AbiG__AbiGii.0.V.cif
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abig/AbiG,AbiG__AbiGii,0,V-plddts_88.01846.pdb
---
::
## Experimental validation
<mermaid>
graph LR;
Chopin_2005[<a href='https://doi.org/10.1016/j.mib.2005.06.006'>Chopin et al., 2005</a>] --> Origin_0
......@@ -79,14 +80,4 @@ end
style Title3 fill:none,stroke:none,stroke-width:none
style Title4 fill:none,stroke:none,stroke-width:none
</mermaid>
## Relevant abstracts
::relevant-abstracts
---
items:
- doi: 10.1023/A:1002027321171
- doi: 10.1016/j.mib.2005.06.006
---
::
......@@ -46,18 +46,22 @@ The system was detected in 439 different species.
Proportion of genome encoding the AbiH system for the 14 phyla with more than 50 genomes in the RefSeq database.
## Structure
## Structure
### AbiH
##### Example 1
::molstar-pdbe-plugin
---
height: 700
dataUrl: /abih/AbiH__AbiH-plddts_91.3485.pdb
dataUrls:
- /abih/AbiH__AbiH.cif
---
::
## Experimental validation
<mermaid>
graph LR;
Chopin_2005[<a href='https://doi.org/10.1016/j.mib.2005.06.006'>Chopin et al., 2005</a>] --> Origin_0
......@@ -85,3 +89,4 @@ end