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 (77)
Showing
with 621 additions and 315 deletions
......@@ -39,6 +39,7 @@ stages:
- update-meilisearch-indexes
- get-meili-key
- build
- build-wiki
- deploy
......@@ -211,6 +212,14 @@ lint:
update
--file data/all_predictions_statistics.csv
--document structure
- >
df-wiki-cli
meilisearch
--host ${MEILI_HOST}
--key ${MEILI_MASTER_KEY}
update
--file data/list-systems.json
--document systems
allow_failure: false
......@@ -344,6 +353,29 @@ build:prod:wiki:
- if: $CI_COMMIT_BRANCH == "main"
build-wiki:
stage: build-wiki
image: node:21.1-bookworm-slim
variables:
NODE_OPTIONS: --max_old_space_size=12288
NUXT_APP_BASE_URL: /wiki/
NUXT_PUBLIC_MEILISEARCH_CLIENT_HOST_URL: ${MEILI_HOST}
NUXT_PUBLIC_MEILISEARCH_CLIENT_SEARCH_API_KEY: ${MEILI_API_KEY}
NUXT_PUBLIC_MEILI_HOST: ${MEILI_HOST}
NUXT_PUBLIC_MEILI_API_KEY: ${MEILI_API_KEY}
before_script:
- npm install
script:
- npm run generate
artifacts:
paths:
- .output/public
################ DEPLOY ##########################
.deploy:
stage: deploy
......
......@@ -38,7 +38,9 @@ function onScroll() {
<v-row justify="center">
<v-col cols="auto">
<v-card flat color="transparent" :min-width="mobile ? undefined : 900" :max-width="fluid ? undefined : 1500">
<slot />
<v-card-text>
<slot />
</v-card-text>
<EditGitlab v-if="edit" />
<NavPrevNext v-if="edit" />
</v-card>
......
......@@ -18,7 +18,7 @@ const props = withDefaults(defineProps<Props>(), {
density: "prominent",
drawer: true,
drawerEnabled: true,
title: "Knowledge database of all known anti-phage systems"
title: "Knowledge database of all known defense systems"
});
const emit = defineEmits(['update:drawer'])
function toggleTheme() {
......@@ -38,7 +38,7 @@ const sections = ref([
{ id: "wiki", label: "Wiki", to: '/', },
{ id: "refseq", label: "RefSeq DB", to: '/refseq/' },
{ id: "structure", label: "Structures DB", to: '/structure/' },
{ id: "help", label: "Help", to: '/help/' },
{ id: "help", label: "Help", to: '/help/defensefinder' },
]);
......
......@@ -3,21 +3,21 @@ import { usePfamStore } from "@/stores/pfam";
const { pfam: pfamStore } = usePfamStore();
export interface Props {
pfamString: string | null;
pfams: Record<string, any>[];
itemsToDisplay?: number;
}
const props = withDefaults(defineProps<Props>(), {
pfamString: null,
pfams: [],
itemsToDisplay: 2,
});
const pfams = computed(() => {
if (props.pfamString === null) {
return [];
} else {
return props.pfamString.split(",").map((pfam) => pfam.trim());
}
});
// const pfams = computed(() => {
// if (props.pfamString === null) {
// return [];
// } else {
// return props.pfamString.split(",").map((pfam) => pfam.trim());
// }
// });
const show = ref(false);
const pfamBaseUrl = ref(new URL("https://www.ebi.ac.uk/interpro/entry/pfam/"));
......@@ -31,11 +31,11 @@ function constructPfamUrl(pfam: string) {
<v-col>
<v-card flat color="transparent" density="compact" rounded="false">
<template v-for="(pfam, index) in pfams" :key="pfam">
<v-chip v-if="index < itemsToDisplay || itemsToDisplay < 0" :href="constructPfamUrl(pfam)" target="_blank"
<v-chip v-if="index < itemsToDisplay || itemsToDisplay < 0" :href="constructPfamUrl(pfam.AC)" target="_blank"
color="info" class="mr-1 mb-1">
{{ pfam }}
{{ pfam.DE }}
<v-tooltip activator="parent" location="top">{{
pfamStore.get(pfam)?.DE ?? "none"
pfam.AC
}}</v-tooltip></v-chip>
<template v-if="index === itemsToDisplay">
......@@ -51,11 +51,11 @@ function constructPfamUrl(pfam: string) {
</template>
<template v-if="pfams.length > itemsToDisplay && show">
<template v-for="(pfam, index) in pfams" :key="pfam">
<v-chip v-if="index >= itemsToDisplay" :href="constructPfamUrl(pfam)" target="_blank" color="info"
<v-chip v-if="index >= itemsToDisplay" :href="constructPfamUrl(pfam.AC)" target="_blank" color="info"
class="mr-1 mb-1">
{{ pfam }}
{{ pfam.DE }}
<v-tooltip activator="parent" location="top">{{
pfamStore.get(pfam)?.DE ?? "none"
pfam.AC
}}</v-tooltip></v-chip>
</template>
</template>
......
......@@ -2,9 +2,10 @@
// import type { FacetDistribution } from "meilisearch";
import { useSlots } from 'vue'
import { useDisplay } from "vuetify";
import { useFacetsStore } from '~~/stores/facets'
import * as Plot from "@observablehq/plot";
import PlotFigure from "~/components/PlotFigure";
import * as d3 from "d3";
import { useThrottleFn } from '@vueuse/core'
import { useMeiliSearch } from "#imports"
export interface SortItem {
key: string,
......@@ -36,17 +37,15 @@ const props = withDefaults(defineProps<Props>(), {
console.log(props.dataTableServerProps)
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 facetStore = useFacetsStore()
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 page = ref(1)
......@@ -58,6 +57,7 @@ const computedTableHeight = computed(() => {
const computedHeight = height.value - 350
return computedHeight > minTableHeight.value ? computedHeight : minTableHeight.value
})
const plddtRange = ref([0, 100])
// const { pending: pendingDownloadData, downloadCsv } = useCsvDownload(props.db, `df-${props.db}`)
......@@ -126,7 +126,40 @@ watch([paginationParams, msSortBy, page], ([newParams, newSort, newPage]) => {
onMounted(async () => {
searchOrFilter()
})
const hasPlddt = computed(() => props.db === 'structure')
// Fetch results
const plddtFilter = computed(() => {
const plddtRangeValue = plddtRange.value
if (hasPlddt.value && plddtRangeValue?.length === 2 && (plddtRangeValue[0] !== 0 || plddtRangeValue[1] !== 100)) {
return `plddts ${plddtRangeValue[0]} TO ${plddtRangeValue[1]}`
} else {
return undefined
}
})
const computedFilter = computed(() => {
if (toValue(msFilter)) {
if (toValue(plddtFilter)) {
return `${toValue(msFilter)} AND ${toValue(plddtFilter)}`
}
else {
return toValue(msFilter)
}
} else {
if (toValue(plddtFilter)) {
return `${toValue(plddtFilter)}`
}
else {
return undefined
}
}
})
const msError = computed(() => {
if (filterError.value?.type && filterError.value?.message) {
......@@ -134,16 +167,26 @@ const msError = computed(() => {
} else { return false }
})
const throttleSearch = useThrottleFn(async () => { searchOrFilter() }, 300)
async function searchOrFilter(pagination = true) {
// 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
emit("refresh:search", {
index: props.db,
query: q,
params: { ...notPaginatedParams.value, filter: toValue(computedFilter), sort: msSortBy.value }
})
if (pagination) {
await msSearch(q, { ...paginationParams.value, filter: msFilter.value, sort: msSortBy.value })
await msSearch(q, { ...paginationParams.value, filter: toValue(computedFilter), sort: msSortBy.value })
}
else {
await msSearch(q, { ...notPaginatedParams.value, filter: msFilter.value, sort: msSortBy.value })
await msSearch(q, { ...notPaginatedParams.value, filter: toValue(computedFilter), sort: msSortBy.value })
}
} catch (error: any) {
......@@ -153,9 +196,11 @@ async function searchOrFilter(pagination = true) {
finally {
loading.value = false
}
}
function clearFilterOrSearch() {
filterOrSearch.value = null
searchOrFilter()
......@@ -167,11 +212,6 @@ watch(msFilter, async (fos) => {
})
watch(msResult, (newRes) => {
facetStore.setFacets({ facetDistribution: newRes.facetDistribution, facetStat: newRes.facetStat })
}, { deep: true })
const totalHits = computed(() => {
return toValue(msResult)?.totalHits ?? toValue(msResult)?.estimatedTotalHits ?? 0
......@@ -195,7 +235,7 @@ watch(filterInputValues, (newSoF) => {
})
watch(search, () => { searchOrFilter() })
// watch(plddtRange, () => { searchOrFilter() })
const filterStep = computed(() => {
return filterInputValues.value !== null && filterInputValues.value.length > 0 ? filterInputValues.value?.length % 3 : null
})
......@@ -239,7 +279,7 @@ const autocompleteItems = computed(() => {
const { type, value } = filterOrSearch.value?.slice(-2, -1)[0]
const sanitizedValue = value.split("-")[0]
// console.log("compute new facets")
const facetDistri = facetStore.facets?.facetDistribution
const facetDistri = msResult.value?.facetDistribution
// console.log(facetDistri)
return facetDistri?.[sanitizedValue] ? Object.entries(facetDistri[sanitizedValue]).map(([key, val]) => {
return {
......@@ -276,32 +316,19 @@ function clearSearch() {
// function runTextSearch() {
// if (canAddTextSearch) {
// const item: FilterItem = reactive({
// type: 'text', title: search.value, value: search.value, deletable: true, props: { type: "text", deletable: true, }
// })
// if (Array.isArray(filterOrSearch.value)) {
// filterOrSearch.value = [
// ...filterOrSearch.value, item
// ]
// } else {
// filterOrSearch.value = [item]
// }
// search.value = ""
// searchOrFilter()
// }
// }
// const groupSortDomain = computed(() => {
// console.log(msResult.value)
// return msResult.value ? d3.groupSort(msResult.value?.hits?.filter((d) => d.phylum), (g) => d3.median(g, (d) => d.phylum), (d) => d.type) : []
// })
// function downloadData() {
// downloadCsv(search, msFilter, msSortBy, notPaginatedParams)
// }
</script>
<template>
<v-card flat color="transparent">
<v-card-text>
</v-card-text>
<v-card-text>
<v-row>
<v-col cols="5">
......@@ -327,23 +354,28 @@ function clearSearch() {
</v-autocomplete>
</v-col>
</v-row>
<v-row v-if="props.db === 'structure'">
<v-col>
<v-range-slider v-model="plddtRange" density="compact" hide-details="auto" label="pLDDT" step="0.1"
@update:modelValue="throttleSearch()">
<template v-slot:prepend>
<span hide-details single-line type="number" variant="outlined" density="compact"
style="width: 70px">{{ plddtRange[0] }}</span>
</template>
<template v-slot:append>
<span hide-details single-line type="number" variant="outlined" style="width: 70px"
density="compact">{{ plddtRange[1] }}</span>
</template>
</v-range-slider>
</v-col>
</v-row>
</v-card-text>
<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"
:height="computedTableHeight" class="elevation-1 mt-2">
<template #top>
<v-toolbar><v-toolbar-title class="text-capitalize">
{{ props.db }}
</v-toolbar-title><v-spacer></v-spacer>
<!-- <v-btn :loading="pendingDownloadData" :disabled="totalHits > 10000" @click="downloadData" icon
variant="text" class="text-none mr-15">
<v-badge :content="totalHits" color="info" floating>
<v-icon>md:download</v-icon></v-badge></v-btn> -->
</v-toolbar>
</template>
:items-per-page-options="itemsPerPage" :height="computedTableHeight" class="elevation-1 mt-2">
<template v-for="(slot, index) of Object.keys(slots)" :key="index" v-slot:[slot]="data">
<slot :name="slot" v-bind="data"></slot>
</template>
......
......@@ -4,15 +4,18 @@ 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
uniq?: boolean
}
const { mobile } = useDisplay()
// const selectedPdb = ref('')
const refinedDataUrls = computed(() => {
......@@ -31,10 +34,15 @@ const refinedDataUrls = computed(() => {
if (props?.dataUrls && props?.dataUrls?.length > 0) {
urls = [...props.dataUrls.map((dataUrl) => {
return refinedUrl(dataUrl)
// return dataUrl
})]
}
if (props?.dataUrl) {
urls = [...urls, refinedUrl(props.dataUrl)]
urls = [
...urls,
// props.dataUrl
refinedUrl(props.dataUrl)
]
}
return urls
......@@ -44,6 +52,7 @@ const refinedDataUrls = computed(() => {
// const selectedPdb = ref(refinedDataUrls.value?.length > 0 ? refinedDataUrls.value[0] : null)
const props = withDefaults(defineProps<Props>(), {
height: 600,
uniq: false
})
const { width, height } = useDisplay()
......@@ -95,88 +104,93 @@ useHead({
const pdbeMolstarComponent = ref(null)
// const selectedPdb = ref("/wiki/avs/AVAST_I,AVAST_I__Avs1A,0,V-plddts_85.07081.pdb")
const selectedPdb = ref(null)
const selectedPdb: Ref<string | null> = ref(null)
const selectedPaePath = computed(() => {
return selectedPdb.value ? `${selectedPdb.value.split(".").slice(0, -1).join('.')}.tsv` : null
return selectedPdb.value ? `${selectedPdb.value.split(".").slice(0, -1).join('.')}.png` : null
})
const paeData = ref([])
watch(selectedPaePath, async (newPaePath) => {
if (newPaePath !== null) {
try {
const data = await d3.tsv(newPaePath);
console.log(data.length)
console.log(data[0][0])
console.log(data?.[0]?.[0] !== undefined)
if (data.length > 500) {
paeError.value = `The PAE is too large to be displayed (${data.length} residus)`
paeData.value = []
}
else if (data?.[0]?.[0] === undefined) {
paeError.value = "The PAE cannot be downloaded"
paeData.value = []
}
else {
paeData.value = data
paeError.value = null
}
// const paeData = ref([])
// watch(selectedPaePath, async (newPaePath) => {
// if (newPaePath !== null) {
// try {
// const data = await d3.tsv(newPaePath);
// if (data.length > 500) {
// paeError.value = `The PAE is too large to be displayed (${data.length} residus)`
// paeData.value = []
// }
// else if (data?.[0]?.[0] === undefined) {
// paeError.value = "The PAE cannot be downloaded"
// paeData.value = []
// }
// else {
// paeData.value = data
// paeError.value = null
// }
// }
// catch (error) {
// console.log(error)
// }
// } else {
// paeData.value = []
// }
}
catch (error) {
console.log(error)
}
} else {
paeData.value = []
}
})
// })
const sanitizedPaeData = computed(() => {
return paeData.value.reduce((acc, curr, index) => {
const scoredResidue = index
// let newAcc = [...acc]
for (const [alignedResidue, value] of Object.entries(curr)) {
// console.log(value)
acc.push({ alignedResidue: parseInt(alignedResidue), scoredResidue: parseInt(scoredResidue), value: parseFloat(value) })
// newAcc = [...newAcc, ...[{ alignedResidue: parseInt(alignedResidue), scoredResidue: parseInt(scoredResidue), value: parseFloat(value) }]]
}
// const sanitizedPaeData = computed(() => {
// return paeData.value.reduce((acc, curr, index) => {
// const scoredResidue = index
// // let newAcc = [...acc]
// for (const [alignedResidue, value] of Object.entries(curr)) {
// // console.log(value)
// acc.push({ alignedResidue: parseInt(alignedResidue), scoredResidue: parseInt(scoredResidue), value: parseFloat(value) })
// // newAcc = [...newAcc, ...[{ alignedResidue: parseInt(alignedResidue), scoredResidue: parseInt(scoredResidue), value: parseFloat(value) }]]
// }
// return acc
// }, [])
// })
return acc
}, [])
})
// const plotPaeOptions = computed(() => {
// return {
// width: 640,
// height: 640,
// color: { scheme: "Greens", legend: true, reverse: true, label: "Expected position error (Ångströms)" },
// y: { reverse: true },
// marks: [
// Plot.dot(sanitizedPaeData.value, { x: "scoredResidue", y: "alignedResidue", stroke: "value" })
// ]
// }
// })
const plotPaeOptions = computed(() => {
return {
width: 640,
height: 640,
color: { scheme: "Greens", legend: true, reverse: true, label: "Expected position error (Ångströms)" },
y: { reverse: true },
marks: [
Plot.dot(sanitizedPaeData.value, { x: "scoredResidue", y: "alignedResidue", stroke: "value" })
]
}
watch(selectedPdb, (newSelectedPdb, prevSelectPdb) => {
viewPdb(newSelectedPdb)
})
watch(selectedPdb, (newSelectedPdb, prevSelectPdb) => {
if (newSelectedPdb !== null) {
function viewPdb(pdbPath: string | null) {
if (pdbPath !== null) {
dialog.value = true
const format = toValue(newSelectedPdb)?.split(".").slice(-1)[0]?.toLowerCase() ?? "pdb"
const format = toValue(pdbPath)?.split(".").slice(-1)[0]?.toLowerCase() ?? "pdb"
moleculeFormat.value = format
if (pdbeMolstarComponent.value?.viewerInstance) {
console.log(pdbeMolstarComponent.value)
const viewerInstance = pdbeMolstarComponent.value.viewerInstance
// show.value = true
const customData = { url: newSelectedPdb, format: format, binary: false }
const customData = { url: pdbPath, format: format, binary: false }
viewerInstance.visual.update({ customData })
}
}
}
function setSelectedPdbToFirst() {
const urls = toValue(refinedDataUrls)
if (urls.length >= 1) {
selectedPdb.value = urls[0]
}
})
}
// const moleculeFormat = computed(() => {
// return toValue(selectedPdb)?.split(".")?.[-1]?.toLowerCase() ?? "pdb"
......@@ -186,10 +200,13 @@ const moleculeFormat: Ref<string> = ref("pdb")
<template>
<v-row><v-col><v-select v-model="selectedPdb" label="Select PDB" :items="refinedDataUrls"
hide-details="auto"></v-select></v-col></v-row>
<span class="d-flex flex-wrap align-center justify-center">
<v-col>
<v-btn v-if="uniq" size="x-small" variant="tonal" icon="md:visibility" @click="setSelectedPdbToFirst()"></v-btn>
<v-select v-else v-model="selectedPdb" label="Select PDB" :items="refinedDataUrls" hide-details="auto">
</v-select>
</v-col>
<v-row justify="center">
<v-dialog v-model="dialog" transition="dialog-bottom-transition" fullscreen :scrim="false">
<v-card flat :rounded="false">
<v-toolbar>
......@@ -206,7 +223,7 @@ const moleculeFormat: Ref<string> = ref("pdb")
</v-toolbar>
<v-card-text>
<v-row>
<v-col cols="auto">
<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">
......@@ -215,12 +232,13 @@ const moleculeFormat: Ref<string> = ref("pdb")
:custom-data-format="moleculeFormat"></pdbe-molstar>
</v-sheet>
</v-col>
<v-col>
<PlotFigure v-if="sanitizedPaeData?.length > 0 && paeError === null" defer
<v-col :cols="mobile ? 12 : undefined">
<v-img :src="selectedPaePath"></v-img>
<!-- <PlotFigure v-if="sanitizedPaeData?.length > 0 && paeError === null" defer
:options="plotPaeOptions"></PlotFigure>
<v-alert v-else type="warning">{{ paeError }}</v-alert>
<v-alert v-else type="warning" variant="tonal">{{ paeError }}</v-alert> -->
<v-card flat color="transparent">
<v-card-title>Model Confidence</v-card-title>
<v-card-text>
AlphaFold produces a per-residue model
......@@ -269,35 +287,12 @@ const moleculeFormat: Ref<string> = ref("pdb")
</v-list-item>
</v-list>
</v-card>
<!-- <div _ngcontent-ykv-c96="" class="confidenceWrapper">
<div _ngcontent-ykv-c96="" class="column legendRow ng-star-inserted"><span
_ngcontent-ykv-c96="" class="legendColor"
style="background-color: rgb(0, 83, 214);">&nbsp;</span><span _ngcontent-ykv-c96=""
class="legendlabel">Very high (pLDDT &gt; 90)</span></div>
<div _ngcontent-ykv-c96="" class="column legendRow ng-star-inserted"><span
_ngcontent-ykv-c96="" class="legendColor"
style="background-color: rgb(101, 203, 243);">&nbsp;</span><span
_ngcontent-ykv-c96="" class="legendlabel">High (90 &gt; pLDDT &gt; 70)</span></div>
<div _ngcontent-ykv-c96="" class="column legendRow ng-star-inserted"><span
_ngcontent-ykv-c96="" class="legendColor"
style="background-color: rgb(255, 219, 19);">&nbsp;</span><span
_ngcontent-ykv-c96="" class="legendlabel">Low (70 &gt; pLDDT &gt; 50)</span></div>
<div _ngcontent-ykv-c96="" class="column legendRow ng-star-inserted"><span
_ngcontent-ykv-c96="" class="legendColor"
style="background-color: rgb(255, 125, 69);">&nbsp;</span><span
_ngcontent-ykv-c96="" class="legendlabel">Very low (pLDDT &lt; 50)</span></div> -->
<!---->
<!-- </div> -->
<!-- <div _ngcontent-ykv-c96="" class="column legendDesc"> AlphaFold produces a per-residue model
confidence score (pLDDT) between 0 and 100. Some regions below 50 pLDDT may be unstructured
in isolation. </div> -->
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
</v-row>
</span>
</template>
......
<script setup lang="ts">
import { useFacetsStore } from '~~/stores/facets'
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"
const facetStore = useFacetsStore()
const sortBy: Ref<SortItem[]> = ref([{ key: 'type', order: "asc" }])
const itemValue = ref("id");
const { width } = useDisplay();
const distriTool: Ref<string[]> = ref([])
const scaleTransform: Ref<string[]> = ref([])
const facets = ref([
"replicon",
......@@ -50,11 +48,9 @@ const headers = ref([
sortable: false
}
])
const logTransform = computed(() => {
return distriTool.value.includes('log')
})
const fullWidth = computed(() => {
return distriTool.value.includes('fullwidth')
return layoutPlot.value === 'fullwidth'
})
const computedHeaders = computed(() => {
return [...headers.value, ...availableTaxo.value.map(taxo => {
......@@ -64,12 +60,35 @@ const computedHeaders = computed(() => {
}
})]
})
const { result: msResult } = useMeiliSearch('refseq')
const computedWidth = computed(() => {
const currentWidth = fullWidth.value ? width.value : width.value / 2
return Math.max(currentWidth, 550);
});
const allHits = ref([])
onMounted(async () => {
const params = {
facets: ["*"],
filter: [],
sort: ["type:asc"],
limit: 500000
}
await getAllHits({ index: "refseq", params, query: "" })
})
async function getAllHits(params) {
if (params.index === 'refseq') {
console.log(params.index)
const { data, error } = await useAsyncMeiliSearch(params)
console.log(error.value)
console.log(data.value)
allHits.value = data.value
}
}
const plotHeight = computed(() => {
return computedWidth.value / 3;
// return 500
......@@ -87,11 +106,10 @@ const dataTableServerProps = computed(() => {
}
})
const defaultBarPlotOptions = computed(() => {
const y = logTransform.value ? { nice: true, grid: true, type: 'symlog' } : { nice: true, grid: true, type: "linear" }
// const y = { nice: true, grid: true }
return {
x: { label: null, tickRotate: 45, ticks: 10 },
y,
y: { grid: true, type: scaleType.value },
color: { legend: true },
width: computedWidth.value,
height: plotHeight.value + 100,
......@@ -99,11 +117,10 @@ const defaultBarPlotOptions = computed(() => {
})
const computedSystemDistribution = computed(() => {
if (facetStore.facets?.facetDistribution?.type) {
return Object.entries(facetStore.facets.facetDistribution.type).map(([key, value]) => {
if (toValue(msResult)?.facetDistribution?.type) {
return Object.entries(toValue(msResult).facetDistribution.type).map(([key, value]) => {
return {
type: key,
// count: logTransform.value ? Math.log(value) : value
count: value
}
}).sort()
......@@ -129,8 +146,8 @@ const computedDistriSystemOptions = computed(() => {
};
});
const computedTaxonomyDistribution = computed(() => {
if (facetStore.facets?.facetDistribution?.[selectedTaxoRank.value]) {
return Object.entries(facetStore.facets.facetDistribution[selectedTaxoRank.value]).map(([key, value]) => {
if (toValue(msResult)?.facetDistribution?.[selectedTaxoRank.value]) {
return Object.entries(toValue(msResult).facetDistribution[selectedTaxoRank.value]).map(([key, value]) => {
return {
[selectedTaxoRank.value]: key,
count: value
......@@ -169,67 +186,117 @@ function namesToCollapsibleChips(names: string[]) {
function namesToAccessionChips(names: string[]) {
return namesToCollapsibleChips(names).map(it => {
return { ...it, href: new URL(it.title, "http://toto.pasteur.cloud").toString() }
return {
...it,
// href: new URL(it.title, "http://toto.pasteur.cloud").toString()
}
})
}
const taxoPanel: Ref<number> = ref(0)
const systemPanel: Ref<number> = ref(0)
const systemPanel: Ref<number> = ref(["table"])
const layoutPlot: Ref<string[]> = ref("grid")
const binPlotOptions = ref({
marginLeft: 150,
marginBottom: 200,
padding: 0,
width: 1920,
grid: true,
x: { tickRotate: 90, tip: true, },
color: { scheme: "turbo", legend: true },
})
const binPlotDataOptions = computed(() => {
return allHits.value?.hits?.length > 0 ? {
...binPlotOptions.value,
color: {
...binPlotOptions.value.color,
type: scaleType.value
},
// fy: { domain: groupSortDomain.value },
marks: [
Plot.cell(allHits.value?.hits ?? [], Plot.group({ fill: "count" }, { x: "type", y: selectedTaxoRank.value, tip: true, inset: 0.5, sort: { y: "fill" } })),
]
} : null
})
const scaleType = ref("linear")
</script>
<template>
<v-card flat class="mb-2" color="transparent">
<v-toolbar density="compact">
<v-toolbar-title>Distributions</v-toolbar-title>
<v-btn-toggle v-model="distriTool" multiple density="compact" rounded="false" variant="text" color="primary"
class="mx-2">
<v-btn icon="md:fullscreen" value="fullwidth"></v-btn>
<v-btn icon="mdi-math-log" value="log"></v-btn>
</v-btn-toggle>
</v-toolbar>
<v-row align="start" class="my-2">
<v-col :cols="fullWidth ? 12 : 6">
<v-card color="transparent" flat>
<v-expansion-panels v-model="systemPanel">
<v-expansion-panel elevation="3">
<v-expansion-panel-title color="grey-lighten-4">Systems</v-expansion-panel-title>
<v-expansion-panel-text>
<PlotFigure :options="unref(computedDistriSystemOptions)" defer></PlotFigure>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</v-card>
</v-col>
<v-col :cols="fullWidth ? 12 : 6">
<v-card flat color="transparent">
<v-expansion-panels v-model="taxoPanel">
<v-expansion-panel elevation="3" :value="true">
<v-expansion-panel-title color="grey-lighten-4">
Taxonomic
</v-expansion-panel-title>
<v-expansion-panel-text>
<v-card color="transparent" flat>
<v-expansion-panels v-model="systemPanel" class="my-2" density="compact" multiple>
<v-expansion-panel elevation="3" value="barplot">
<v-expansion-panel-title color="grey-lighten-4"><v-icon color="primary"
class="mr-2">mdi-chart-bar</v-icon>Systems - Taxonomic</v-expansion-panel-title>
<v-expansion-panel-text>
<v-card flat color="transparent">
<v-toolbar flat color="transparent" density="compact">
<v-btn-toggle v-model="layoutPlot" density="compact" rounded="false" variant="text"
color="primary" mandatory class="mx-2">
<v-btn icon="md:grid_view" value="grid"></v-btn>
<v-btn icon="md:view_agenda" value="fullwidth"></v-btn>
</v-btn-toggle>
<v-select v-model="scaleType" class="mx-2" density="compact"
:items="['linear', 'sqrt', 'symlog']" label="Scale Type" hide-details="auto"></v-select>
<v-select v-model="selectedTaxoRank" :items="availableTaxo" density="compact"
label="Select taxonomic rank"></v-select>
label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select>
</v-toolbar>
<v-row align="start">
<v-col :cols="fullWidth ? 12 : 6">
<PlotFigure :options="unref(computedDistriSystemOptions)" defer></PlotFigure>
</v-col>
<v-col :cols="fullWidth ? 12 : 6">
<PlotFigure defer :options="unref(computedDistriTaxoOptions)"></PlotFigure>
</v-col>
</v-row>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
<v-expansion-panel elevation="3" value="matrix">
<v-expansion-panel-title color="grey-lighten-4"><v-icon color="primary"
class="mr-2">mdi-data-matrix</v-icon>Heatmap</v-expansion-panel-title>
<v-expansion-panel-text>
<v-card flat color="transparent">
<v-toolbar flat color="transparent" density="compact">
<v-select v-model="scaleType" class="mx-2" density="compact"
:items="['linear', 'sqrt', 'symlog']" label="Scale Type" hide-details="auto"></v-select>
<v-select v-model="selectedTaxoRank" :items="availableTaxo" density="compact"
label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select>
</v-toolbar>
<PlotFigure v-if="toValue(binPlotDataOptions) !== null" :options="unref(binPlotDataOptions)"
defer>
</PlotFigure>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
<ServerDbTable title="RefSeq" db="refseq" :sortBy="sortBy" :facets="facets"
:data-table-server-props="dataTableServerProps" @refresh:search="getAllHits">
<template #top>
<v-toolbar><v-toolbar-title class="text-capitalize">
RefSeq
</v-toolbar-title><v-spacer></v-spacer>
</v-toolbar>
</template>
<template #[`item.accession_in_sys`]="{ item }">
<CollapsibleChips :items="namesToAccessionChips(item.accession_in_sys)">
</CollapsibleChips>
</template>
</ServerDbTable>
<PlotFigure defer :options="unref(computedDistriTaxoOptions)"></PlotFigure>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</v-card>
</v-col>
</v-row>
</v-card>
<ServerDbTable title="RefSeq" db="refseq" :sortBy="sortBy" :facets="facets"
:data-table-server-props="dataTableServerProps">
<template #[`item.accession_in_sys`]="{ item }">
<CollapsibleChips :items="namesToAccessionChips(item.accession_in_sys)"></CollapsibleChips>
</template>
</ServerDbTable>
</template>
\ No newline at end of file
<script setup lang="ts">
import * as Plot from "@observablehq/plot";
import PlotFigure from "~/components/PlotFigure";
import type { SortItem } from "@/components/ServerDbTable.vue"
import { ServerDbTable } from "#components"
const sortBy: Ref<SortItem[]> = ref([{ key: 'system', order: "asc" }])
const itemValue = ref("id");
const facets: Ref<string[]> = ref(["system", "completed"])
const facets: Ref<string[]> = ref(["system", "completed", "prediction_type", ])
const headers: Ref<Object[]> = ref([
{ title: "System", key: "system" },
{ title: 'Structure', key: 'structure', sortable: false, removable: false },
{ title: "Type", key: "system", removable: false },
// { title: "pdb file", key: "pdb" },
// { title: "fasta", key: "fasta_file" },
{ title: "Proteins in structure", key: 'proteins_in_the_prediction', sortable: false },
{ title: "System genes", key: "system_genes", sortable: false },
{ title: "Sys id", key: "nb_sys" },
{ title: "Completed", key: "completed" },
{ title: "Prediction type", key: "prediction_type" },
{ title: "Num of genes", key: "system_number_of_genes" },
{ title: "pLDDT", key: "plddts" },
{ title: "iptm+ptm", key: "iptm+ptm" },
{ title: "pDockQ", key: "pDockQ" },
{ title: "Type", key: "type" }
{ 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 { search: msSearch, result: msResult } = useMeiliSearch('structure')
const defaultDataTableServerProps = ref({
showExpand: true
showExpand: false
})
const dataTableServerProps = computed(() => {
......@@ -33,6 +38,9 @@ const dataTableServerProps = computed(() => {
}
})
function namesToCollapsibleChips(names: string[], file: string | null = null) {
if (file === null) {
return names.filter((it) => it !== "").map(it => ({ title: it.split("__").pop() }))
......@@ -51,10 +59,31 @@ function toSystemName(rawName: string) {
return rawName.split(/_|-0/)[0].toLocaleLowerCase()
}
const plddtDistribution = computed(() => {
if (toValue(msResult)?.facetDistribution?.plddts) {
return Object.entries(toValue(msResult).facetDistribution.plddts).map(([key, value]) => { })
}
})
function remove(key) {
headers.value = headers.value.filter(header => header.key !== key)
}
</script>
<template>
<ServerDbTable title="Predicted Structures" db="structure" :sortBy="sortBy" :facets="facets"
:data-table-server-props="dataTableServerProps">
<template #top>
<v-toolbar><v-toolbar-title class="text-capitalize">
Predicted Structures
</v-toolbar-title><v-spacer></v-spacer>
</v-toolbar>
</template>
<template #[`item.proteins_in_the_prediction`]="{ item }">
<CollapsibleChips :items="namesToCollapsibleChips(item.proteins_in_the_prediction, item.fasta_file)">
</CollapsibleChips>
......@@ -62,17 +91,13 @@ function toSystemName(rawName: string) {
<template #[`item.system_genes`]="{ item }">
<CollapsibleChips :items="namesToCollapsibleChips(item.system_genes)"></CollapsibleChips>
</template>
<template #expanded-row="{ columns, item }">
<tr>
<td :colspan="columns.length">
<v-card flat color="transparent">
<v-card-text>
<MolstarPdbePlugin :data-urls="[`/${toSystemName(item.system)}/${pdbNameToCif(item.pdb)}`]">
</MolstarPdbePlugin>
</v-card-text>
</v-card>
</td>
</tr>
<template #[`item.structure`]="{ item }">
<MolstarPdbePlugin v-if="item?.pdb && item.pdb !== 'na'"
:data-urls="[`/${toSystemName(item.system)}/${pdbNameToCif(item.pdb)}`]" uniq>
</MolstarPdbePlugin>
<span v-else class="d-flex flex-wrap align-center justify-center">
<v-icon color="warning" icon="md:dangerous"></v-icon>
</span>
</template>
<template #[`item.completed`]="{ item }">
<v-icon v-if="item.completed" color="success" icon="md:check"></v-icon>
......
<script setup lang="ts">
import type { SortItem } 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"])
const headers: Ref<Object[]> = ref([
{ title: "System", key: "title", removable: false },
{ title: "Article", key: "doi", removable: false },
{ title: "Sensor", key: "Sensor", removable: false },
{ title: "Activator", key: "Activator", removable: false },
{ title: "Effector", key: "Effector", removable: false },
{ title: "PFAM", key: "PFAM", removable: false },
{ title: "Contributors", key: "contributors", removable: false },
])
const { search: msSearch, result: msResult } = useMeiliSearch('systems')
const defaultDataTableServerProps = ref({
showExpand: false
})
const dataTableServerProps = computed(() => {
return {
...toValue(defaultDataTableServerProps),
headers: toValue(headers),
itemValue: toValue(itemValue)
}
})
</script>
<template>
<ServerDbTable title="systems" db="systems" :sortBy="sortBy" :facets="facets"
:data-table-server-props="dataTableServerProps">
<template #top>
<v-toolbar><v-toolbar-title class="text-capitalize">
List Systems </v-toolbar-title><v-spacer></v-spacer>
</v-toolbar>
</template>
<template #[`item.title`]="{ item }">
<v-chip color="info" link :to="`/defense-systems/${item.title.toLowerCase()}`">{{
item.title
}}</v-chip>
</template>
<template #[`item.doi`]="{ item }">
<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
......@@ -5,7 +5,7 @@ navigation:
icon: 'md:home'
relevantAbstracts:
- doi: 10.1128/jb.64.4.557-569.1952
- doi: 10.1128/jb.65.2.113-121.19
- doi: 10.1128/jb.65.2.113-121.1953
- doi: 10.1128/JB.05535-11
- doi: 10.1126/science.aar4120
- doi: 10.1038/s41579-023-00934-x
......@@ -14,17 +14,18 @@ relevantAbstracts:
- doi: 10.1016/j.cell.2021.12.029
- doi: 10.1038/nmicrobiol.2017.92
- doi: 10.1038/s41586-019-1894-8
---
## Introduction
Bacteria and their phages have co-existed for billions of years. The pressure of phage infection is thought to be a major driver of bacterial evolution and has favored the development of a diversity of anti-phage weapons. These weapons, namely anti-phage defense systems can be defined as single genes or groups of genes that partially or fully inhibit phage infection. For reviews on anti-phage systems, see : :ref{doi=10.1038/s41586-019-1894-8, 10.1146/annurev-micro-020722-013730, 10.1016/j.mib.2005.06.006, 10.1038/s41579-023-00934-x}.
Bacteria and their phages have co-existed for billions of years. The pressure of phage infection is thought to be a major driver of bacterial evolution and has favored the development of a diversity of anti-phage weapons. These weapons, namely anti-phage defense systems can be defined as single genes or groups of genes that partially or fully inhibit phage infection. For reviews on anti-phage systems, see : :ref{doi=10.1038/s41586-019-1894-8,10.1146/annurev-micro-020722-013730,10.1016/j.mib.2005.06.006,10.1038/s41579-023-00934-x}.
## A brief history of anti-phage systems
The first discovered anti-phage system, a Restriction-Modification (RM) system, was described in the early 1950s ref{doi=10.1128/jb.64.4.557-569.1952, 10.1128/jb.65.2.113-121.1953}. In the following decades, a handful of other systems were discovered :ref{doi=10.1016/j.mib.2005.06.006}. In 2007, CRISPR-Cas systems were discovered to be anti-phage systems :ref{doi=10.1126/science.1138140}. As CRISPR-Cas systems and RM systems are extremely prevalent in bacteria, it was thought for some years that the antiviral immune system of bacteria had been mostly elucidated.
The first discovered anti-phage system, a Restriction-Modification (RM) system, was described in the early 1950s :ref{doi=10.1128/jb.64.4.557-569.1952,10.1128/jb.65.2.113-121.1953}. In the following decades, a handful of other systems were discovered :ref{doi=10.1016/j.mib.2005.06.006}. In 2007, CRISPR-Cas systems were discovered to be anti-phage systems :ref{doi=10.1126/science.1138140}. As CRISPR-Cas systems and RM systems are extremely prevalent in bacteria, it was thought for some years that the antiviral immune system of bacteria had been mostly elucidated.
Following these two major breakthroughs, knowledge of anti-phage systems remained scarce for some years. Yet, in 2011, it was revealed that anti-phage systems tend to colocalize on the bacterial genome in defense-islands :ref{doi=10.1128/JB.05535-11}. This led to a guilt-by-association hypothesis: if a gene or a set of genes is frequently found in bacterial genomes in close proximity to known defense systems, such as RM or CRISPR-Cas systems, then it might constitute a new defense system. This hypothesis was tested systematically in a landarmark study in 2018 :ref{doi=10.1126/science.aar4120} leading to the discovery of 10 novel anti-phage systems. This started the uncovering of an impressive diversity of defense systems in a very short amount of time :ref{10.1038/s41579-023-00934-x}.
Following these two major breakthroughs, knowledge of anti-phage systems remained scarce for some years. Yet, in 2011, it was revealed that anti-phage systems tend to colocalize on the bacterial genome in defense-islands :ref{doi=10.1128/JB.05535-11}. This led to a guilt-by-association hypothesis: if a gene or a set of genes is frequently found in bacterial genomes in close proximity to known defense systems, such as RM or CRISPR-Cas systems, then it might constitute a new defense system. This hypothesis was tested systematically in a landarmark study in 2018 :ref{doi=10.1126/science.aar4120} leading to the discovery of 10 novel anti-phage systems. This started the uncovering of an impressive diversity of defense systems in a very short amount of time :ref{doi=10.1038/s41579-023-00934-x}.
To date over 150 types of defense systems have been described, unveiling an unsuspected diversity of molecular mechanisms. The antiviral immune systems of bacteria therefore appear much more complex than previously envisioned, and new discoveries do not seem to be slowing down.
......
---
title: DefenseFinder Webservice
layout: article
---
# Documentation
## DefenseFinder
[DefenseFinder](https://github.com/mdmparis/defense-finder) is a software to detect defense systems in bacterial genomes.
It takes as input a fasta file, be it nucleic or protein (it will guess which).
On the web service page, users can upload (1) their fasta file(s). One can upload multiple fasta files at once, as many jobs will run.
Job are automatically launched once the upload is done. Jobs can be found in the analysis panel (2), and are named with the name of the input file. The job name can be changed.
Analyses are kept for 6 months, or with a maximum of 10 jobs.
![webservice_interface](/help/webservice_interface.jpg){max-width=750px}
In the Analyses panel, each past job is kept for 6 months. Next to the name of the input file (1) there is a rolling circle until the job finishes to run, which become a number. One can edit the job name (by default it's the file's name) by clicking on the small pen (2), or can delete a job (3). To visualize the results, one can click on Results (4) or on the job's name.
![analyses_interface](/help/analyses_interface.jpg){max-width=750px}
The result consists in 3 tables :
- Systems table : Shown by default. One system per line. On the column type, there is the name of the system, and one can click on it to be redirected to the corresponding wiki page (1).
- Genes table (2): One gene per line. Those are genes from the aforementioned system, with some addition information on the quality of the hit. The key between both table is `sys_id`
- HMMER table (3): One gene per line. Here it's all the genes hit by a hmm profile, even when the gene is not part of a defense system.
![results_interface](/help/results_interface.jpg){max-width=750px}
---
title: Help
title: Contributing to the Wiki
layout: article
navigation:
icon: 'md:help'
---
# Documentation
# Contributing to the Wiki
## DefenseFinder
[DefenseFinder](https://github.com/mdmparis/defense-finder) is a software to detect defense systems from bacterial genomes.
It takes as input a fasta file, nucleic or proteic (it will guess which).
On the web service page, users can upload (1) their fasta file. One can upload multiple fasta files at once, as many jobs will be run.
Users need to provide a name for the job (2) being submitted before clicking on the submit button (3).
Once a job is submitted, the page is redirected to the "Analyses" panel (4) where results of the past runs can be found.
![webservice_interface](/help/webservice_interface.jpg){max-width=750px}
The result consists in 3 tables :
- Systems table : One system per line. On the column type, there is the name of the system, and one can click on it to be redirected to the corresponding wiki page.
- Genes table : One gene per line. Those are genes from the aforementioned system, with some addition information on the quality of the hit. The key between both table is `sys_id`
- HMMER table : One gene per line. Here it's all the genes hit by a hmm profile, even when the gene is not part of a defense system.
## Contributing to the Wiki
### 1/ Create an account
## 1/ Create an account
The wiki is based on gitlab pages, and we are using the gitlab's instance of the Pasteur Institute. To contribute, users need to be part of the project.
......@@ -43,7 +22,7 @@ Once your account is created, you need to request access to the project, on the
Click, and wait for an admin approval.
### 2/ Edit a page
## 2/ Edit a page
Once you have access to the project (the previous step is done once), you can edit easily each page of the wiki, and post [issues](https://gitlab.pasteur.fr/mdm-lab/wiki/-/issues) (if you have question about something or remarks with anything from content to design).
......@@ -65,7 +44,7 @@ To do so, just fill in the description (1) of what you did, or anything that you
![Create MR](/help/Create_MR.png){max-width=750px}
### 3/ Tips to write Markdown
## 3/ Tips to write Markdown
As a general advice, check an already written file to see how other pages are written.<br><br>
......
navigation.icon: "md:help"
......@@ -9,11 +9,11 @@ In the following pages are presented different general concepts that are useful
You'll find information on :
1. [Abortive infection](/general-concepts/abortive-infection/)
2. [Defense Islands ](/general-concepts/defense-islands/)
3. [Triggers of defense systems](/general-concepts/defense-systems_trigger/)
4. [Effectors of defense systems](/general-concepts/defense-systems_effector/)
5. [How defense systems were and are discovered](/general-concepts/defense-systems-discovery/)
6. [Defensive domains](/general-concepts/defensive-domains/)
7. [MGE and defense systems](/general-concepts/mge-defense-systems/)
8. [Anti defense systems](/general-concepts/anti-defense-systems/)
\ No newline at end of file
1. [Abortive infection](/general-concepts/abortive-infection)
2. [Defense Islands](/general-concepts/defense-islands)
3. [Triggers of defense systems](/general-concepts/defense-systems_trigger)
4. [Effectors of defense systems](/general-concepts/defense-systems_effector)
5. [How defense systems were and are discovered](/general-concepts/defense-systems-discovery)
6. [Defensive domains](/general-concepts/defensive-domains)
7. [MGE and defense systems](/general-concepts/mge-defense-systems)
8. [Anti defense systems](/general-concepts/anti-defense-systems)
\ No newline at end of file
---
title: Abortive Infection
layout: article
contributors:
- Lucas Paoli
relevantAbstracts:
- doi: 10.1128/jb.68.1.36-42.1954
- doi: 10.1016/0006-3002(61)90455-3
- doi: 10.1016/0022-2836(68)90078-8
- doi: 10.1128/jvi.4.2.162-168.1969
- doi: 10.1128/jvi.13.4.870-880.1974
- doi: 10.1128/mr.45.1.52-71.1981
- doi: 10.3168/jds.S0022-0302(90)78904-7
- doi: 10.1016/S0960-9822(00)00124-X
- doi: 10.1111/j.1365-2958.1995.tb02255.x
- doi: 10.1016/j.mib.2005.06.006
- doi: 10.1146/annurev-virology-011620-040628
- doi: 10.1016/j.mib.2023.102312
- doi: 10.1038/s41579-023-00934-x
---
This section is empty. You can help by adding to it.
The term abortive infection was coined in the 1950s :ref{doi=10.1128/jb.68.1.36-42.1954}
to describe the observations that a fraction of the bacterial population did not support phage replication.
This phenomenon, also called phage exclusion, was identified in multiple systems across the following decades
:ref{doi=10.1016/0006-3002(61)90455-3,10.1016/0022-2836(68)90078-8,10.1128/jvi.4.2.162-168.1969,10.1128/jvi.13.4.870-880.1974}
and reviewed extensively :ref{doi=10.1128/mr.45.1.52-71.1981,10.3168/jds.S0022-0302(90)78904-7,10.1111/j.1365-2958.1995.tb02255.x}.
In the following years, and through the resolution of molecular mechanisms of key defense systems such as Rex or Lit, abortive infection became synonymous with infection-induced controlled cell-death.
Controlled cell death upon detection of the phage infection stops the propagation of the phage and protects the rest of the bacterial population
:ref{doi=10.1016/S0960-9822(00)00124-X,10.1016/j.mib.2005.06.006}.
Abortive infection can thus be thought of as a form of bacterial altruism.
With the recent developments in phage-defense systems and microbial immunity (see :ref{doi=10.1038/s41579-023-00934-x} for a review), many newly identifed anti-phage defense systems are thought to function through abortive infection. Abortive defense systems often detect the phage infection at the later stage through protein sensing or the monitoring of host integrity but can also be based on nucleic acid sensing. Upon sensing, a diverse set of effectors can be used to reduce metabolism or induce cell-death (e.g., NAD+ depletion, translation interruption or membrane depolarisation). The diversity of and mechanisms of abortive infection were recently reviewd here :ref{doi=10.1146/annurev-virology-011620-040628}, while the evolutionary success of this paradoxical altruistic form of immunity has recently been discussed here :ref{doi=10.1016/j.mib.2023.102312}.
Although abortive infection is currently often understood as leading to cell-death, it should be noted that its original definition appeared to be broader and that some mechanisms currently included as abortive infection may only lead to metabolic stalling or dormancy.
---
title: Defense Islands
layout: article
relevantAbstracts:
- doi: 10.1128/JB.05535-11
- doi: 10.1126/science.aar4120
- doi: 10.1016/j.chom.2022.09.017
contributors:
- Ernest Mordret
---
# Defense Islands
**Defense islands** are regions of prokaryotic genomes enriched in defense systems. Their existence first described in Makarova *et al.* :ref{doi=10.1128/JB.05535-11}, who observed that genes encoding defense systems (mainly Restriction Modification enzymes, Toxin-Antitoxin systems, but notably not CRISPR) tended to cluster preferentially on specific portions of bacterial genomes. They postulated that unknown genes commonly found associated to these regions would likely have a defensive role themselves, and confirmed bioinformatically that many of them were indeed diverged versions of classical defense systems. Other systems of genes commonly found in defense islands were later isolated and heterologously expressed to experimentally confirm to have a defensive role (BREX, DISARM). Doron *et al.* :ref{doi=10.1126/science.aar4120}, later followed by Millmann *et al.* :ref{doi=10.1016/j.chom.2022.09.017}, used the colocalization of genes in defense islands to generate many candidate systems and test them experimentally in high throughput screens, leading to the discovery of a large number of new defense systems.
The reasons leading to the formation and maintenance of defense islands are still unclear. Makarova *et al.* :ref{doi=10.1128/JB.05535-11} observed a that defense islands often associated with mobile genetic elements, suggesting that defense systems travel through horizontal gene transfer, taking advantage of the MGEs' mobility. This observation in itself could explain the non-random localization of defense systems in the preferred "landing pads" (=*sinks*) of mobile genetic elements. Whether the colocalization of defense systems into these islands is purely due to there horizontal transmission, or whether they reflect a deeper functional implication such as coregulation and coordination, remains debated.
This section is empty. You can help by adding to it.
\ No newline at end of file
......@@ -2,29 +2,23 @@
title: Defense system triggers
contributors:
- Avigail Stokar-Avihail
- Alba Herrero del Valle
layout: article
---
# How anti-phage systems sense invading phages
Upon phage infection, the bacterial immune system senses a specific phage component or modification that the phage exerts on the cell to elicit the bacterial immune response. Understanding how bacteria sense phage infection is a fundamental question, which remains unanswered for the majority of recently discovered immune systems. There are dozens of cases in which the mechanism of immunity has been elucidated, but the phage trigger remains elusive. Understanding how antiphage systems are activated is key for a full understanding of bacterial immunity and for repurposing them as molecular tools as has been done for restriction enzymes and CRISPR-Cas.
## Diversity
Various determinants of the phage can elicit bacterial immunity either in a direct or indirect manner. The most common and well known prokaryotic anti-phage systems, restriction enzymes and CRISPR-Cas, recognize and cleave phage DNA or RNA. More recently, a CBASS system has been found to directly bind to a structured phage RNA that triggers immune activation1. In other cases, defense systems are activated by protein coding phage genes. In some cases, the phage protein is directly sensed by the defense systems, as has been beautifully demonstrated for the Avs systems that directly bind either the phage terminase or portal protein2. In other cases, the phage protein can be sensed indirectly by the defense system, for example by detecting its activity in the cell. Such an indirect mechanism has been found for example in the case of some retron defense systems that are triggered by phage tampering with the RecBCD protein complex3,4. For a comprehensive coverage of all recent phage detection mechanisms the recent review by Huiting and Bondy-Denomy5 is highly recommended.
Various determinants of the phage can elicit bacterial immunity either in a direct or indirect manner. The most common and well known prokaryotic anti-phage systems, restriction enzymes and CRISPR-Cas, recognize and cleave phage DNA or RNA. More recently, a CBASS system has been found to directly bind to a structured phage RNA that triggers immune activation :ref{doi=10.1101/2023.03.07.531596}. In other cases, defense systems are activated by protein coding phage genes. In some cases, the phage protein is directly sensed by the defense systems, as has been beautifully demonstrated for the Avs systems that directly bind either the phage terminase or portal protein :ref{doi=10.1126/science.abm4096}. In other cases, the phage protein can be sensed indirectly by the defense system, for example by detecting its activity in the cell. Such an indirect mechanism has been found for example in the case of some retron defense systems that are triggered by phage tampering with the RecBCD protein complex :ref{doi=10.1016/j.cell.2020.09.065,10.1016/j.cell.2023.02.029}. For a comprehensive coverage of all recent phage detection mechanisms the recent review by Huiting and Bondy-Denomy :ref{doi=10.1016/j.mib.2023.102325} is highly recommended.
## Method of discovery:
The main method used to pinpoint phage components that trigger a specific defense system of interest has been through a simple classic genetics approach, whereby mutant phages that overcome the defense system are examined. Such mutants often occur spontaneously and can thus be selected for by simply picking phage plaques that are able to form on a lawn of bacteria expressing the defense system4,5. The hypothesis is that the phage mutant escapes bacterial immunity due to a mutation in the component sensed by the system. Thus, sequencing these phage mutants and identification of the mutated locus is the first required step. To validate that the mutated phage component is indeed the actual trigger of the defense system, follow up experiments are required. For example, in some cases expression of this phage component without any other phage genes is sufficient to elicit the activity of bacterial immune system. This approach was used to identify Borvo activation by expression of the phage DNA polymerase4, Dazbog activation by expression of a phage DNA methylase4, retron activation by either phage SSB proteins4 or by proteins that inhibit the host RecBCD3, CapRel triggering by the phage Capsid protein6 and many more5. Additional biochemical pulldown assays can be used to assess binding of the defense system to the suspected phage trigger.
One major caveat in the above approach is that in some cases mutant phages that escape the immune system cannot be isolated. This can occur for example if the defense system senses a general fold of a highly conserved and essential phage protein. In this case a simple mutation in the protein will not suffice for the phage to escape detection. In such cases, an alternative approach can be used that does not rely on isolation of escape mutants. An overexpression library of all phage genes can be co-expressed with the defense system of interest, and then assayed for immune activation. This approach was successfully applied for identification phage components that trigger diverse Avs systems2.
## General concepts:
Although much is still unknown regarding how bacterial immune systems sense phage infection, by combining the data observed so far, several general concepts in immune sensing are beginning to come to light. First, mechanistically diverse immune systems appear to have converged to sense common conserved phage components4. These include the phage core replication machinery, host takeover machinery and structural components. Second, several studies have found cases in which defense occurs in a multi-layered fashion, whereby a second system is activated when the first one fails3,7,8. Research in upcoming years is expected to reveal additional guiding principles in the ways bacteria detect phages.
## References:
1. Banh D V, Roberts C G, Amador A M, Brady S F, & Marraffini L A. (2023) Bacterial cGAS senses a viral RNA to initiate immunity. bioRxiv 2023.03.07.531596 doi:10.1101/2023.03.07.531596.
2. Gao L A, Wilkinson M E, Strecker J, Makarova K S, Macrae R K, Koonin E V, & Zhang F. (2022) Prokaryotic innate immunity through pattern recognition of conserved viral proteins. Science 377: eabm4096.
3. Millman A, Bernheim A, Stokar-Avihail A, Fedorenko T, Voichek M, Leavitt A, Oppenheimer-Shaanan Y, & Sorek R. (2020) Bacterial Retrons Function In Anti-Phage Defense. Cell 183: 1551–1561.
4. Stokar-Avihail A, Fedorenko T, Hör J, Garb J, Leavitt A, Millman A, Shulman G, Wojtania N, Melamed S, Amitai G, & Sorek R. (2023) Discovery of phage determinants that confer sensitivity to bacterial immune systems. Cell 186: 1863-1876.e16.
5. Huiting E & Bondy-Denomy J. (2023) Defining the expanding mechanisms of phage-mediated activation of bacterial immunity. Curr. Opin. Microbiol. 74: 102325.
6. Zhang T, Tamman H, Coppieters ’t Wallant K, Kurata T, LeRoux M, Srikant S, Brodiazhenko T, Cepauskas A, Talavera A, Martens C, Atkinson G C, Hauryliuk V, Garcia-Pino A, & Laub M T. (2022) Direct activation of a bacterial innate immune system by a viral capsid protein. Nature 612: 132–140.
7. Rousset F, Depardieu F, Miele S, Dowding J, Laval A-L, Lieberman E, Garry D, Rocha E P C, Bernheim A, & Bikard D. (2022) Phages and their satellites encode hotspots of antiviral systems. Cell Host Microbe 30: 740–753.
8. Penner M, Morad I, Snyder L, & Kaufmann G. (1995) Phage T4-coded Stp: Double-edged effector of coupled DNA and tRNA-restriction systems. J. Mol. Biol. 249: 857–68.
The main method used to pinpoint phage components that trigger a specific defense system of interest has been through a simple classic genetics approach, whereby mutant phages that overcome the defense system are examined. Such mutants often occur spontaneously and can thus be selected for by simply picking phage plaques that are able to form on a lawn of bacteria expressing the defense system :ref{doi=10.1016/j.cell.2023.02.029,10.1016/j.mib.2023.102325}. The hypothesis is that the phage mutant escapes bacterial immunity due to a mutation in the component sensed by the system. Thus, sequencing these phage mutants and identification of the mutated locus is the first required step. To validate that the mutated phage component is indeed the actual trigger of the defense system, follow up experiments are required. For example, in some cases expression of this phage component without any other phage genes is sufficient to elicit the activity of bacterial immune system. This approach was used to identify Borvo activation by expression of the phage DNA polymerase, Dazbog activation by expression of a phage DNA methylase, retron activation by either phage SSB proteins :ref{doi=10.1016/j.cell.2023.02.029} or by proteins that inhibit the host RecBCD3, CapRel triggering by the phage Capsid protein :ref{doi=10.1038/s41586-022-05444-z} and many more :ref{doi=10.1016/j.mib.2023.102325}. Additional biochemical pulldown assays can be used to assess binding of the defense system to the suspected phage trigger.
One major caveat in the above approach is that in some cases mutant phages that escape the immune system cannot be isolated. This can occur for example if the defense system senses a general fold of a highly conserved and essential phage protein. In this case a simple mutation in the protein will not suffice for the phage to escape detection. In such cases, an alternative approach can be used that does not rely on isolation of escape mutants. An overexpression library of all phage genes can be co-expressed with the defense system of interest, and then assayed for immune activation. This approach was successfully applied for identification phage components that trigger diverse Avs systems :ref{doi=10.1126/science.abm4096}.
## General concepts
Although much is still unknown regarding how bacterial immune systems sense phage infection, by combining the data observed so far, several general concepts in immune sensing are beginning to come to light. First, mechanistically diverse immune systems appear to have converged to sense common conserved phage components4. These include the phage core replication machinery, host takeover machinery and structural components. Second, several studies have found cases in which defense occurs in a multi-layered fashion, whereby a second system is activated when the first one fails :ref{doi=10.1016/j.cell.2020.09.065,10.1016/j.chom.2022.02.018,10.1006/jmbi.1995.0343}. Research in upcoming years is expected to reveal additional guiding principles in the ways bacteria detect phages.
......@@ -2,6 +2,22 @@
title: Defensive Domains
layout: article
toc: true
contributors:
- Hugo Vaysset
---
Among proteins involved in defense systems, a few domains appear to be frequent.
# What are protein domains ?
Proteins can typically be decomposed into a set of structural or functional units called "domains" where each individual domain has a specific biological function (e.g. catalyzing a chemical reaction or binding to another protein). The combination of one or several protein domains within a protein determines its biological function.
![illustration_thsa](/defensive_domain/ThsA.png){max-width=500px}
To examplify this idea, the figure is a depiction of the ThsA protein involved in the [Thoeris](/defense-systems/thoeris) defense system in *Bacillus cereus*. The protein is composed of two domains : a SIR2-like domain (blue) and a SLOG domain (green). The SLOG domain of ThsA is able to bind to cyclic Adenosine Diphosphate Ribose (cADPR), a signalling molecule produced by ThsB upon phage infection. Binding of cADPR activates the Nicotinamide Adenine Dinucleotide (NAD) depletion activity of the SIR2-like domain which causes abortive infection. This shows how the presence of two domains in this protein allows it to be activated by the sensor component of the system (ThsB) and to trigger the immune response mechanism :ref{doi=10.1038/s41586-021-04098-7}.
# Domain characterization helps to understand the biological function of a protein
Although a considerable diversity of molecular mechanisms have been described for defense systems, it is striking to observe that some functional domains are recurrently involved in antiphage defense :ref{doi=10.1038/s41586-021-04098-7}. When studying the presence of a new defense system, the *in silico* characterization of the domains present in the system can provide valuable information regarding the molecular mechanism of the system. If one protein of the system contains for example a TerB domain, this might indicate that the system is involved in membrane integrity surveillance as this domain was previously shown to be associated with the periplasmic membrane :ref{doi=10.1016/j.chom.2022.09.017}. If a protein of the system contains a TIR domain this might indicate that the system possess a NAD degradation activity or that the protein could multimerize as both functions have been shown for this domain in the past :ref{doi=10.3389/fimmu.2021.784484}.
# Domains can be conserved throughout evolution
It is clear that some defense systems can be conserved among different clades of bacteria but it was also observed that the unit of evolutionary conservation can be the protein domain :ref{doi=10.1038/s41467-022-30269-9}. As a consequence, it is frequent to find the same domain associated with a wide range of distinct other domains in different defense systems :ref{doi=10.1016/j.mib.2023.102312}. This is well illustrated by defense systems such [Avs](/defense-systems/avs) or [CBASS](/defense-systems/cbass) that can be constituted of diverse effector proteins which differ from each other based on the specific domains that compose them :ref{doi=10.1126/science.aba0372}, :ref{doi=10.1038/s41564-022-01239-0}, :ref{doi=10.1038/s41564-020-0777-y}. The modular aspect of protein domains fits with the concept of "evolution as tinkering" stating that already existing objects (here protein domains) can often be repurposed in new manners, allowing the efficient development of novel functions :ref{doi=10.1126/science.860134}.
---
title: Defense Systems and MGE
layout: article-no-toc
contributors:
- Marian Dominguez-Mirazo
layout: article
---
Defense systems help bacteria against mobile genetic elements (MGE), such as phage or plasmids.
Yet, some defense systems favor certain MGE, or some MGE carry defense systems.
\ No newline at end of file
Mobile genetic elements (MGEs), such as plasmids, bacteriophages, and phage satellites, facilitate horizontal gene transfer (HGT) within microbial populations, playing a crucial role in the genetic diversity and genomic evolution of bacteria :ref{doi=10.1098/rstb.2020.0460}. These elements expedite the exchange of genetic material among bacterial cells, promoting the dissemination of advantageous traits like antibiotic resistance, virulence factors, and metabolic capabilities, allowing bacteria to adapt to dynamic environments :ref{doi=10.1098/rstb.2020.0460}. However, the presence of MGEs can impose a substantial fitness cost on the bacterial host, as in the case of lytic phage infections. To counteract parasitic genomic elements, including viruses and other MGEs, bacteria have evolved defense systems. These defense systems are often disadvantageous under low parasite pressure, leading to their occasional loss. However, as the pressure from parasites increases, these defense systems become advantageous. Consequently, defense systems in bacteria exhibit high mobility and transfer rates :ref{doi=10.1038/s41576-019-0172-9}. Interestingly, a large fraction of defense systems in bacteria are encoded by MGEs :ref{doi=10.1038/s41467-022-30269-9,10.1371/journal.pbio.3001514}. While sometimes the fitness interests of MGEs and the bacterial host are aligned, these systems are likely to be selected because they benefit the MGE encoding it rather than the host cell who :ref{doi=10.1371/journal.pbio.3001514,10.1038/s41576-019-0172-9}. This benefit may include preventing other mobile elements from infecting the same cell and competing for essential resources. The presence of defense systems can, in turn, have an effect in gene flow who :ref{doi=10.1371/journal.pbio.3001514}.
......@@ -2,7 +2,38 @@
title: Anti Defense Systems
layout: article
toc: true
contributors:
- Nathalie Bechon
---
Anti defense systems are systems that act against defense system, and thus help bypass the defense mechanism.
# Anti-defense systems:
This article is non-exhaustive but introduces the topic of an-defense systems. Several reviews mentioned here did a great in-depth characterization of the known anti-defense phage mechanism :ref{doi=10.3389/fmicb.2023.1211793,10.1016/j.jmb.2023.167974,10.1038/nrmicro3096}.
Several strategies allow phages to avoid bacterial defenses to successfully complete an infectious cycle. In particular, anti-defense proteins are bacteriophage proteins that specifically act against a bacterial defense system, and thus allow bacteriophages to bypass the bacterial immune system. The most well-described category of anti-defense proteins are the anti-CRISPR proteins (Acr), that have been thoroughly reviewed previously :ref{doi=10.1038/nrmicro.2017.120,10.1016/j.jmb.2023.167974}. However, concomitant with the renewed interest of the field to identify new bacterial defense systems, many anti-defense proteins targeting diverse defense systems have recently been described. Using a non-exhaustive list of anti-defense proteins as examples, I will outline several general categories of anti-defense mechanism. However, I will not focus on another common phage anti-defense strategy that relies on modifying their components, such as mutating the proteins that trigger the defense to escape, or changing their DNA to avoid targeting by restriction-modification or CRISPR systems.
Anti-defense proteins are crucial to understand the evolutionary arms race between bacteria and their phages, as they likely drive the diversification of bacterial defense systems. Some defense system even evolved to recognize anti-defense proteins as activators, providing multiple lines of defense during phage infection :ref{doi=10.1016/j.cell.2023.02.029}. Moreover, these proteins are also important to mediate phage/phage interactions. Indeed, anti CRISPR proteins were suggested to be involved in phage/phage collaboration, in which a primo-infection by a phage carrying an anti-CRISPR protein is unsuccessful but leaves the bacteria immunosuppressed and therefore sensitive to a second phage infection :ref{doi=10.1016/j.jmb.2023.167974}. Considering the importance of overcoming bacterial defenses for phages, it is likely that a significant part of the phage proteins of unknown function currently found in phage sequenced genomes act as anti-defense. Some anti-defense proteins were shown to colocalize in phage genomes, suggesting comparative genomics could be used to identify now anti-defense proteins, similar to what has been done very successfully for bacteria :ref{doi=10.1038/s41467-020-19415-3}. In general, recent studies have used a range of screening methods to identify new anti-defense proteins, and it is expected that many new anti-defense proteins will be described in the coming years.
## Anti-defense proteins target all stages of bacterial defenses
Most anti-defense proteins described to date directly bind a bacterial defense protein to block its activity. However, several other strategies have been described such as post-translational modification of a target, spatial segregation or signaling molecule degradation :ref{doi=10.3389/fmicb.2023.1211793}. They have been described to target all stages of bacterial defense.
Bacterial defenses can be separated in two broad categories: external and internal defenses.
### External defenses
Bacteria can hide receptors behind surface structures such as extracellular polysaccharides or capsular polysaccharides. Conversely, phages can produce various depolymerases to degrade the protective extracellular polysaccharides :ref{doi=10.1038/nrmicro3096}.
### Internal defenses
Bacteria encode a variety of defense systems that prevent phage infection from progressing in various ways. Despite all this variability, all bacterial defense systems are schematically composed of three parts: a sensor recognizing the infection, an effector that achieves protection and a way to transmit the information between the sensor and the effector, either through signaling molecules or protein-protein interactions. Phage anti-defense proteins can target all three of these components.
- Sensor targeting:
- Competitive binding to the sensor: an anti-DSR2 protein from phages phi3T and SPbeta can bind the bacterial DSR2 protein and prevent the physical interaction between DSR2 and its phage activator, the tail tube protein :ref{doi=10.1038/s41564-022-01207-8}. Moreover, Ocr protein from T7 can mimic a B-form DNA oligo and acts as a competitive inhibitor of bacterial type I restriction modification systems :ref{doi=10.1016/s1097-2765(02)00435-5}.
- Masking the activator: some jumbo phages are able to produce a nucleus-like proteinaceous structure that hides phage DNA and replication machinery away from DNA-targeted systems such as type I CRISPR system :ref{doi=10.1038/s41564-019-0612-5}.
- Transmission targeting:
- Degradation of signaling molecules: many systems rely on the production of a nucleotidic signaling molecule after phage sensing to activate the effector such as Pycsar, CBASS, and Thoeris systems. Phages possess proteins that can degrade these molecules to prevent effector activation, such as the anti CBASS Acb1 from phage T4 and the anti Pycsar Apyc1 from phage SBSphiJ :ref{doi=10.1038/s41586-022-04716-y}.
- Sequestration of signaling molecules: an alternative strategy is to bind the signaling molecule very tightly without degrading it, which still prevents effector activation but is presumably easier to evolve than a catalysis-dependent degradation. These phage proteins are called sponges, and two were identified as anti-Thoeris: Tad1 from phage SBSphiJ7 and Tad2 from phage SPO1 and SPO1L3 :ref{doi=10.1038/s41586-022-05375-9,10.1038/s41586-023-06869-w}.
- Effector targeting:
- Direct binding to block activity: Multiple anti-CRISPR protein have been described that can directly bind all the different components of the Cas complex to prevent DNA degradation :ref{doi=10.3389/fmicb.2023.1211793,10.1146/annurev-genet-120417-031321}. So far, this is the most abundant category of anti-defense protein described, and it is not restricted to only anti-CRISPR proteins.
- Antitoxin mimicking: toxin-antitoxin defense systems rely on a toxin effector and an antitoxin that will toxin-mediated toxicity in absence of phage infection. Phages can highjack this process by mimicking the antitoxin to prevent toxin activity even during infection. For instance, phage ϕTE can produce a short repetitive RNA that mimics the ToxI RNA antitoxin of type III toxin-antitoxin system ToxIN and evade defense mediated by this system :ref{doi=10.1371/journal.pgen.1003023}.