From 9d06dd7515b52a63f4c5f9463cdee45f2face663 Mon Sep 17 00:00:00 2001 From: Remi PLANEL <rplanel@pasteur.fr> Date: Tue, 23 Jul 2024 15:02:59 +0200 Subject: [PATCH] Anti defense systems --- backend/analysis/api.py | 13 ++-- backend/analysis/model_schemas.py | 4 ++ backend/analysis/models.py | 1 + backend/analysis/schemas.py | 2 + backend/poetry.lock | 12 ++-- backend/pyproject.toml | 2 +- .../components/AnalysisResultDataTable.vue | 6 +- frontend/components/SubmitAnalysis.vue | 27 +++++-- frontend/components/UppyGenomeUpload.vue | 72 ++++++++++++++----- .../components/tableResults/GenesTable.vue | 1 + .../components/tableResults/SystemsTable.vue | 1 + 11 files changed, 104 insertions(+), 37 deletions(-) diff --git a/backend/analysis/api.py b/backend/analysis/api.py index 0b5ad35..2bbe663 100644 --- a/backend/analysis/api.py +++ b/backend/analysis/api.py @@ -2,7 +2,7 @@ import os from tempfile import mkstemp from django.conf import settings -from ninja import Router, File +from ninja import Router, File, Form from ninja.files import UploadedFile from django_to_galaxy.models import GalaxyInstance @@ -23,6 +23,7 @@ from django.http import JsonResponse, FileResponse from analysis.model_schemas import ( AnalysisOutSchema, AnalysisInSchema, + AnalysisPayload, Error, GeneOutSchema, HmmerOutSchema, @@ -63,11 +64,13 @@ def login(request): @router.post("add", response={200: AnalysisOutSchema, 503: Error}) def add( request, + payload: AnalysisPayload = Form(...), genomefile: UploadedFile = File(...), ): fd, genome_path = mkstemp( prefix="defense-finder-", suffix="-sequences-input.fasta", dir="/uploaded-files" ) + with open(genome_path, "wb+") as f: for chunk in genomefile.chunks(): f.write(chunk) @@ -91,7 +94,8 @@ def add( input_files = [ genome_path, ] - params = {} + + params = {"1": payload.dict()} analysis = aw.invoke(session, input_files, params, f"{genomefile.name}") return analysis @@ -100,6 +104,7 @@ def add( @router.post("add-example", response={200: AnalysisOutSchema, 503: Error}) def add_example( request, + payload: AnalysisPayload, ): request.session.set_expiry(session_expiry) if request.session.session_key: @@ -117,12 +122,12 @@ def add_example( is_galaxy_online = aw.analysis_owner.galaxy_instance.is_online() if not is_galaxy_online: return 503, {"message": "The Galaxy instance is offline"} - print(settings.BASE_DIR) input_files = [ f"{settings.BASE_DIR}/analysis/data/GCF_000005845.faa", ] - params = {} + params = {"1": payload.dict()} + analysis = aw.invoke(session, input_files, params, "GCF_000005845.faa") return analysis diff --git a/backend/analysis/model_schemas.py b/backend/analysis/model_schemas.py index 7df65d0..ecfe7c1 100644 --- a/backend/analysis/model_schemas.py +++ b/backend/analysis/model_schemas.py @@ -62,6 +62,10 @@ class Error(Schema): message: str +class AnalysisPayload(Schema): + antidefensefinder: bool + + class GeneOutSchema(ModelSchema): genes: List[GeneEntry] diff --git a/backend/analysis/models.py b/backend/analysis/models.py index a3c831b..6c0ed6c 100644 --- a/backend/analysis/models.py +++ b/backend/analysis/models.py @@ -75,6 +75,7 @@ class AnalysisWorkflow(Workflow): history, datamap, params = self.prepare_invocation( session, input_files, params, history_name ) + print(params) galaxy_inv = self.galaxy_workflow.invoke( datamap, history=history.galaxy_history, params=params ) diff --git a/backend/analysis/schemas.py b/backend/analysis/schemas.py index db4ce9d..85c1b9f 100644 --- a/backend/analysis/schemas.py +++ b/backend/analysis/schemas.py @@ -4,6 +4,7 @@ from ninja import Schema class GeneEntry(Schema): replicon: str + activity: Optional[str] hit_id: str gene_name: str hit_pos: int @@ -30,6 +31,7 @@ class GeneEntry(Schema): class SystemEntry(Schema): sys_id: str type: str + activity: Optional[str] subtype: str sys_beg: str sys_end: str diff --git a/backend/poetry.lock b/backend/poetry.lock index cadcff4..5f8d3b9 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "appdirs" @@ -359,13 +359,13 @@ test = ["black", "django-stubs", "flake8", "isort", "mypy (==0.931)", "psycopg2- [[package]] name = "django-to-galaxy" -version = "0.6.4" +version = "0.6.9" description = "Django extension that eases communication with Galaxy instance to execute workflows." optional = false -python-versions = ">=3.9,<4.0" +python-versions = "<4.0,>=3.9" files = [ - {file = "django_to_galaxy-0.6.4-py3-none-any.whl", hash = "sha256:5e813d45eba6cbb5dab4737c2d5b464f46f33119e2828148860b842a4a272ef8"}, - {file = "django_to_galaxy-0.6.4.tar.gz", hash = "sha256:27e7026b276c8b18fb84715b3a8a6b6f06d63d9994b6d00bc118ca7d53127722"}, + {file = "django_to_galaxy-0.6.9-py3-none-any.whl", hash = "sha256:12fde5b9067ae967bbdf412f177f5e67e6b25a1f995639b3ed31170f77282826"}, + {file = "django_to_galaxy-0.6.9.tar.gz", hash = "sha256:2f9506905069a093fb76adea8b5de57bc64a12f438dbce88179918997e0f7f0e"}, ] [package.dependencies] @@ -979,4 +979,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "0178257066e14753b45a43635af1b8fb9ab5f4773148e50f2ae63573f08e98b7" +content-hash = "096a9a4ca3a3446ca43aa8ac13a60c8f9856b9395e379397167f50e66d7c6587" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 684756c..5ade092 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.md" python = "^3.10" django-ninja = "^0.22.2" psycopg = {extras = ["binary"], version = "^3.1.9"} -django-to-galaxy = "^0.6.4" +django-to-galaxy = "^0.6.9" django-environ = "^0.10.0" gunicorn = "^21.2.0" biopython = "^1.83" diff --git a/frontend/components/AnalysisResultDataTable.vue b/frontend/components/AnalysisResultDataTable.vue index 423921a..4a1f5f8 100644 --- a/frontend/components/AnalysisResultDataTable.vue +++ b/frontend/components/AnalysisResultDataTable.vue @@ -64,7 +64,11 @@ const dataTableProps = computed(() => { <v-card-text> <v-data-table v-model:items-per-page="itemsPerPage" v-bind="dataTableProps"> <template #[`item.type`]="{ item }"> - <v-chip :href="item?.href ? item.href : undefined" target="_blank"> + <v-chip v-if="item.activity === 'Defense'" :href="item?.href ? item.href : undefined" + target="_blank"> + {{ item.type }} + </v-chip> + <v-chip v-else> {{ item.type }} </v-chip> </template> diff --git a/frontend/components/SubmitAnalysis.vue b/frontend/components/SubmitAnalysis.vue index b284b94..6df871c 100644 --- a/frontend/components/SubmitAnalysis.vue +++ b/frontend/components/SubmitAnalysis.vue @@ -6,6 +6,7 @@ const uppyComponentRef = ref(null) const tab = ref<"local" | "paste">("null") const maxSequenceLength = ref(10000) const { csrfToken } = await useCsrfToken() +const antiDefenseSystem = ref(false) const { handleSubmit } = useForm({ validationSchema: { // files(value) { @@ -32,6 +33,9 @@ async function addExample() { try { await $fetch('/dfapi/analysis/add-example', { method: 'POST', + body: { + antidefensefinder: toValue(antiDefenseSystem) + }, headers: { "X-CSRFToken": csrfToken.value } }) await navigateTo("/analyses/") @@ -44,10 +48,12 @@ async function addExample() { } const submit = handleSubmit(async (values) => { + console.log("dans le submit") loading.value = true const formData = new FormData(); const blob = new Blob([toValue(sequence)], { type: 'text/plain' }) formData.append('genomefile', blob, 'pasted-sequence.fasta') + formData.append('antidefensefinder', toValue(antiDefenseSystem).toString()) try { await $fetch('/dfapi/analysis/add', { method: 'POST', @@ -80,18 +86,27 @@ const submit = handleSubmit(async (values) => { <UppyGenomeUpload v-model="files.value.value" ref="uppyComponentRef" class="mb-2" /> </v-window-item> <v-window-item value="paste"> - <v-form @submit.prevent="submit"> - <v-textarea v-model="sequence" :error-messages="sequenceErrorMessage" :count="maxSequenceLength" - label="Fasta sequences" variant="filled" auto-grow></v-textarea> - <v-btn type="submit" :loading="loading">Submit</v-btn> - </v-form> + <v-card flat> + <v-card-text> + <v-form @submit.prevent="submit"> + <v-switch v-model="antiDefenseSystem" color="primary" label="anti defense system" + hide-details="auto" class="mx-0"></v-switch> + <v-textarea v-model="sequence" :error-messages="sequenceErrorMessage" :count="maxSequenceLength" + label="Fasta sequences" variant="filled" auto-grow></v-textarea> + <v-btn type="submit" :loading="loading">Submit</v-btn> + </v-form> + </v-card-text> + </v-card> </v-window-item> <v-window-item value="example"> <v-card flat color="transparent"> <v-card-text> You can try an example with Escherichia coli str. K-12 substr. MG1655 proteins (<a target="_blank" href="https://www.ncbi.nlm.nih.gov/datasets/genome/GCF_000005845.2">GCF_000005845.2</a>) - + </v-card-text> + <v-card-text> + <v-switch v-model="antiDefenseSystem" color="primary" label="anti defense system" + hide-details="auto"></v-switch> </v-card-text> <v-card-actions> <v-btn variant="elevated" density="default" size="default" :slim="false" rounded="xl" diff --git a/frontend/components/UppyGenomeUpload.vue b/frontend/components/UppyGenomeUpload.vue index 848b5b9..7013867 100644 --- a/frontend/components/UppyGenomeUpload.vue +++ b/frontend/components/UppyGenomeUpload.vue @@ -28,11 +28,13 @@ const router = useRouter(); const { width } = useDisplay(); const emit = defineEmits(['update:modelValue']) const { csrfToken } = await useCsrfToken() - +const antiDefenseSystem = ref(false) +const hasAddFiles = ref(false) +const uploading = ref(false) const uppyCovar = new Uppy({ - autoProceed: true, + autoProceed: false, restrictions: { maxFileSize: 10000000 }, - // meta: { name: props.analysisName } + meta: { antidefensefinder: toValue(antiDefenseSystem) } }) // .use(DragDrop, { target: '#drag-drop' }) .use(XHR, { @@ -48,21 +50,43 @@ const uppyCovar = new Uppy({ // router.push() } }) + .on('file-added', (file) => { + const files = uppyCovar.getFiles() + if (files.length > 0) hasAddFiles.value = true + if (files.length === 0) hasAddFiles.value = false + }) + .on("upload", () => { + uploading.value = true + }) + // complete + .on("complete", () => { + uploading.value = false + }) + .on('error', (error) => { + uploading.value = false + }) .on("file-removed", (file, reason) => { const files = uppyCovar.getFiles() const emitObject = (files.length === 0) ? undefined : files + if (emitObject === undefined) hasAddFiles.value = false emit('update:modelValue', emitObject) }) - // .on("error", (error) => { - // console.log(error) - // throw createError("uppy error") - // }); + const themeUppy = computed(() => { return theme.global.current.value.dark ? 'dark' : "light" }) +watch(antiDefenseSystem, () => { + uppyCovar.setMeta({ antidefensefinder: toValue(antiDefenseSystem) }) +}) + +async function runAnalysis() { + console.log('run analsysis') + await uppyCovar.upload() + +} defineExpose({ uppyCovar, @@ -71,17 +95,27 @@ defineExpose({ </script> <template> <v-card flat> - <Dashboard :uppy="uppyCovar" :props="{ - inline: true, - hideUploadButton: false, - theme: themeUppy, - width, - height: 300, - metaFields: [{ - id: 'uppy-genome-upload', - name: 'genome', - placeholder: 'Genomes' - }], - }" /> + <v-card-text> + <v-form @submit.prevent="runAnalysis"> + + <v-switch v-model="antiDefenseSystem" color="primary" label="anti defense system" + hide-details="auto" class="ml-1"></v-switch> + + <Dashboard :uppy="uppyCovar" :props="{ + inline: true, + hideUploadButton: true, + theme: themeUppy, + width, + height: 300, + metaFields: [{ + id: 'uppy-genome-upload', + name: 'genome', + placeholder: 'Genomes' + }], + }" /> + + <v-btn type="submit" :disabled="!hasAddFiles" :loading="uploading">Submit</v-btn> + </v-form> + </v-card-text> </v-card> </template> \ No newline at end of file diff --git a/frontend/components/tableResults/GenesTable.vue b/frontend/components/tableResults/GenesTable.vue index 4c3ea83..4ac0598 100644 --- a/frontend/components/tableResults/GenesTable.vue +++ b/frontend/components/tableResults/GenesTable.vue @@ -28,6 +28,7 @@ const headers = ref([ { title: "Replicon", align: "end", key: "replicon" }, { title: "Position", align: "end", key: "hit_pos" }, { title: "Model fqn", align: "end", key: "model_fqn" }, + { title: "Activity", align: "end", key: "activity"}, { title: "System id", align: "end", key: "sys_id" }, { title: "System loci", align: "end", key: "sys_loci" }, { title: "locus_num", align: "end", key: "locus_num" }, diff --git a/frontend/components/tableResults/SystemsTable.vue b/frontend/components/tableResults/SystemsTable.vue index 1a8ed3d..621a404 100644 --- a/frontend/components/tableResults/SystemsTable.vue +++ b/frontend/components/tableResults/SystemsTable.vue @@ -31,6 +31,7 @@ const groupBy = ref([ ]) const headers = ref([ { title: "System id", align: "end", key: "sys_id", fixed: true, }, + { title: "Activity", align: "end", key: "activity"}, { title: "type", align: "end", key: "type", }, { title: "subtype", align: "end", key: "subtype" }, { title: "sys_beg", align: "end", key: "sys_beg" }, -- GitLab