From 7e9f161046c993fb2a773fdec46eebf39edf9c28 Mon Sep 17 00:00:00 2001 From: Remi PLANEL <rplanel@pasteur.fr> Date: Thu, 21 Dec 2023 12:54:52 +0100 Subject: [PATCH] Can download data server --- components/ServerDbTable.vue | 69 ++++++++++--------- components/content/RefseqDb.vue | 14 ++-- components/content/StructureDb.vue | 4 +- components/content/SystemDb.vue | 13 ++-- composables/useCsvDownload.ts | 43 +++++++++--- content/5.structure.md | 2 +- .../df_wiki_cli/meilisearch/__init__.py | 2 +- 7 files changed, 84 insertions(+), 63 deletions(-) diff --git a/components/ServerDbTable.vue b/components/ServerDbTable.vue index cb80b6d8..b6acb9c4 100644 --- a/components/ServerDbTable.vue +++ b/components/ServerDbTable.vue @@ -18,6 +18,7 @@ export interface Props { sortBy?: SortItem[] facets: MaybeRef<string[]> dataTableServerProps: Record<string, any> + columnsToDownload: MaybeRef<string[]> } export interface FilterItem { @@ -324,44 +325,20 @@ const { pending: pendingDownloadData, downloadCsv } = useCsvDownload(props.db) // }) - +function downloadData() { + downloadCsv( + props.db, + toValue(search), { ...toValue(notPaginatedParams), filter: toValue(computedFilter), sort: toValue(msSortBy) }, + props.columnsToDownload + + ) +} </script> <template> <v-card flat color="transparent"> <v-card-text> </v-card-text> <v-card-text> - <v-row> - <v-col :cols="mobile ? 12 : 5"> - <v-text-field v-model="search" label="Search..." hide-details prepend-inner-icon="mdi-magnify" - single-line clearable></v-text-field> - </v-col> - <v-col :cols="mobile ? 12 : 6"> - <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-col> - <v-col :cols="mobile ? 'auto' : 1"> - <v-badge :content="totalHits" color="primary"> - <v-btn icon="md:download" - @click="downloadCsv(props.db, toValue(search), { ...notPaginatedParams.value, filter: toValue(computedFilter), sort: msSortBy.value })"> - </v-btn> - </v-badge> - </v-col> - </v-row> <v-row v-if="props.db === 'structure'"> <v-col> @@ -386,7 +363,35 @@ const { pending: pendingDownloadData, downloadCsv } = useCsvDownload(props.db) :loading="loading" :items="msResult?.hits ?? []" :items-length="totalHits" density="compact" :items-per-page-options="itemsPerPage" :height="computedTableHeight" class="elevation-1 mt-2"> + <template #top> + <v-toolbar floating><v-toolbar-title class="text-capitalize"> + <v-badge :content="totalHits" color="primary" inline> + <v-btn prepend-icon="md:download" variant="text" color="primary" @click="downloadData()">{{ + props.title }} + </v-btn> + </v-badge> + </v-toolbar-title> + <v-text-field v-model="search" label="Search..." hide-details="auto" prepend-inner-icon="mdi-magnify" + single-line clearable class="mr-2"></v-text-field> + <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-toolbar> + </template> <template v-for="(slot, index) of Object.keys(slots)" :key="index" v-slot:[slot]="data"> <slot :name="slot" v-bind="data"></slot> diff --git a/components/content/RefseqDb.vue b/components/content/RefseqDb.vue index fc3fa610..38260b1f 100644 --- a/components/content/RefseqDb.vue +++ b/components/content/RefseqDb.vue @@ -206,7 +206,8 @@ const binPlotOptions = ref({ }) const binPlotDataOptions = computed(() => { - return allHits.value?.hits?.length > 0 ? { + const toValueAllHits = toValue(allHits) + return toValueAllHits?.hits?.length > 0 ? { ...binPlotOptions.value, color: { ...binPlotOptions.value.color, @@ -214,7 +215,7 @@ const binPlotDataOptions = computed(() => { }, // 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" } })), + Plot.cell(toValueAllHits?.hits ?? [], Plot.group({ fill: "count" }, { x: "type", y: selectedTaxoRank.value, tip: true, inset: 0.5, sort: { y: "fill" } })), ] } : null @@ -280,13 +281,8 @@ const scaleType = ref("linear") </v-expansion-panels> <ServerDbTable title="RefSeq" db="refseq" :sortBy="sortBy" :facets="facets" - :data-table-server-props="dataTableServerProps"> - <template #top> - <v-toolbar><v-toolbar-title class="text-capitalize"> - RefSeq - </v-toolbar-title><v-spacer></v-spacer> - </v-toolbar> - </template> + :data-table-server-props="dataTableServerProps" @refresh:search="getAllHits"> + <template #[`item.accession_in_sys`]="{ item }"> <CollapsibleChips :items="namesToAccessionChips(item.accession_in_sys)"> </CollapsibleChips> diff --git a/components/content/StructureDb.vue b/components/content/StructureDb.vue index 6fc5a9c8..a02fc2c5 100644 --- a/components/content/StructureDb.vue +++ b/components/content/StructureDb.vue @@ -76,13 +76,13 @@ function remove(key) { <ServerDbTable title="Predicted Structures" db="structure" :sortBy="sortBy" :facets="facets" :data-table-server-props="dataTableServerProps"> - <template #top> + <!-- <template #top> <v-toolbar><v-toolbar-title class="text-capitalize"> Predicted Structures </v-toolbar-title><v-spacer></v-spacer> </v-toolbar> - </template> + </template> --> <template #[`item.proteins_in_the_prediction`]="{ item }"> <CollapsibleChips :items="namesToCollapsibleChips(item.proteins_in_the_prediction, item.fasta_file)"> diff --git a/components/content/SystemDb.vue b/components/content/SystemDb.vue index 72d1642f..65e3c152 100644 --- a/components/content/SystemDb.vue +++ b/components/content/SystemDb.vue @@ -3,7 +3,7 @@ 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 facets: Ref<string[]> = ref(["title", "Sensor", "Effector", "Activator", "PFAM.AC", "PFAM.DE"]) const headers: Ref<Object[]> = ref([ { title: "System", key: "title", removable: false }, { title: "Article", key: "doi", removable: false }, @@ -30,16 +30,13 @@ const dataTableServerProps = computed(() => { } }) +const columnsToDownload = ref(['title', 'doi', 'Sensor', 'Activator', 'Effector', 'PFAM', 'contributors',]) + </script> <template> - <ServerDbTable title="systems" db="systems" :sortBy="sortBy" :facets="facets" - :data-table-server-props="dataTableServerProps"> + <ServerDbTable title="List Systems" db="systems" :sortBy="sortBy" :facets="facets" + :data-table-server-props="dataTableServerProps" :columns-to-download="columnsToDownload"> - <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()}`">{{ diff --git a/composables/useCsvDownload.ts b/composables/useCsvDownload.ts index 88589b8f..3ce2d7cd 100644 --- a/composables/useCsvDownload.ts +++ b/composables/useCsvDownload.ts @@ -1,6 +1,7 @@ import { ref } from 'vue'; import Papa from 'papaparse'; import { saveAs } from "file-saver"; +import { column } from '@observablehq/plot'; export function useCsvDownload(baseName: MaybeRef<string> = "df" ) { @@ -12,33 +13,55 @@ export function useCsvDownload(baseName: MaybeRef<string> = "df" index: MaybeRef<string>, query: MaybeRef<string>, params: MaybeRef<Record<string, any>>, + columns: MaybeRef<string[] | undefined> = undefined ) => { - filename.value = `${toValue(baseName)}-${toValue(params).filter}.csv` + const toValueParams = toValue(params) + const filterName = toValueParams?.filter ? toValueParams.filter.replaceAll('\"', "") : '' + filename.value = `${toValue(baseName)}-${filterName}.csv` pending.value = true + try { const { data, error } = await useAsyncMeiliSearch({ index: toValue(index), - params: toValue(params), + params: toValueParams, query: toValue(query) }) - const csvContent = Papa.unparse(toValue(data).hits); - // console.log(csvContent) - const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); - saveAs(blob, `${toValue(filename)}`); + if (toValue(data)?.hits?.length > 0) { + const sanitizedData = toValue(data).hits.map(row => { + let sanitizedRow = { ...row } + if (sanitizedRow?.PFAM?.length > 0) { + sanitizedRow = { + ...sanitizedRow, + PFAM: sanitizedRow.PFAM.map(({ AC }) => AC).join(", ") + } + + } + if (sanitizedRow?.contributors?.length > 0) { + sanitizedRow = { + ...sanitizedRow, + contributors: sanitizedRow.contributors.join(", ") + } + } + return sanitizedRow + + }) + + + console.log(sanitizedData) + const csvContent = Papa.unparse(sanitizedData, { columns: toValue(columns) }); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + saveAs(blob, `${toValue(filename)}`); + } } finally { pending.value = false } } - // watch(msResult, (newRes) => { - // console.log("save file !!!!!!") - - // }) return { pending, downloadCsv, diff --git a/content/5.structure.md b/content/5.structure.md index 923ce4a9..a21e7e49 100644 --- a/content/5.structure.md +++ b/content/5.structure.md @@ -5,7 +5,7 @@ navigation: icon: "mdi-database" --- -# Structures' prediction DB +# Structure's prediction DB In the following tables are various structures that were generated by Alphafold for all monomers, hetero- and homo-dimers for a given system. In the page for each system is the structure for the monomers and real structure when it exists. diff --git a/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py b/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py index b3ec461a..eb98e990 100644 --- a/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py +++ b/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py @@ -193,7 +193,7 @@ def update_systems( ) print(pagination_settings_task) attr_task = index.update_filterable_attributes( - body=["title", "Sensor", "Activator", "Effector", "PFAM.AC"] + body=["title", "Sensor", "Activator", "Effector", "PFAM.AC" , "PFAM.DE"] ) params = { "maxValuesPerFacet": 1000000, -- GitLab