diff --git a/backend/src/monarch_py/api/entity.py b/backend/src/monarch_py/api/entity.py index 500f56897..353fd119b 100644 --- a/backend/src/monarch_py/api/entity.py +++ b/backend/src/monarch_py/api/entity.py @@ -1,13 +1,15 @@ +from io import StringIO from typing import List, Union from fastapi import APIRouter, Depends, HTTPException, Path, Query +from fastapi.responses import StreamingResponse from monarch_py.api.additional_models import PaginationParams from monarch_py.api.config import solr from monarch_py.api.additional_models import OutputFormat from monarch_py.datamodels.model import AssociationTableResults, Node from monarch_py.datamodels.category_enums import AssociationCategory -from monarch_py.utils.format_utils import to_tsv +from monarch_py.utils.format_utils import to_json, to_tsv router = APIRouter(tags=["entity"], responses={404: {"description": "Not Found"}}) @@ -69,6 +71,11 @@ def _association_table( title="Output format for the response", examples=["json", "tsv"], ), + download=Query( + default=False, + title="Download the results as a file", + examples=[True, False], + ), ) -> Union[AssociationTableResults, str]: """ Retrieves association table data for a given entity and association type @@ -85,6 +92,18 @@ def _association_table( response = solr().get_association_table( entity=id, category=category.value, q=query, sort=sort, offset=pagination.offset, limit=pagination.limit ) + if download: + string_response = ( + to_tsv(response, print_output=False) + if format == OutputFormat.tsv + else to_json(response, print_output=False) + ) + stream = StringIO(string_response) + response = StreamingResponse( + stream, media_type="text/csv" if format == OutputFormat.tsv else "application/json" + ) + response.headers["Content-Disposition"] = f"attachment; filename=assoc-table-{id}.{format.value}" + return response if format == OutputFormat.json: return response elif format == OutputFormat.tsv: diff --git a/frontend/src/api/associations.ts b/frontend/src/api/associations.ts index dc2f814de..7c8a4d6c6 100644 --- a/frontend/src/api/associations.ts +++ b/frontend/src/api/associations.ts @@ -28,8 +28,37 @@ export const getAssociations = async ( return response; }; +/** max rows supported in single download */ +export const maxDownload = 500; + +/** get associations between a node and a category */ +export const downloadAssociations = async ( + nodeId = "", + associationCategory = "", + search?: string, + sort: Sort = null, +) => { + /** make query params */ + const params = { + limit: maxDownload, + query: search || "", + sort: sort + ? `${sort.key} ${sort.direction === "up" ? "asc" : "desc"}` + : null, + download: true, + format: "tsv", + }; + + /** make query */ + const url = `${apiUrl}/entity/${nodeId}/${associationCategory}`; + await request(url, params, {}, "text", true); +}; + /** get top few associations */ export const getTopAssociations = async ( nodeId = "", associationCategory = "", ) => await getAssociations(nodeId, associationCategory, 0, 5); + +/** maximum associations downloadable at once */ +export const downloadLimit = 500; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 1824fd95d..b774fac87 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -72,10 +72,12 @@ export const request = async ( * { id: [1,2,3] } -> ?id=1&id=2&id=3 */ params: Params = {}, - /** fetch options */ + /** fetch/request options */ options: RequestInit = {}, /** parse response mode */ parse: "text" | "json" = "json", + /** whether open request url in new tab */ + newTab = false, ): Promise => { /** get string of url parameters/options */ const paramsObject = new URLSearchParams(); @@ -96,6 +98,8 @@ export const request = async ( const paramsString = "?" + paramsObject.toString(); const url = path + paramsString; + if (newTab) window.open(url); + /** make request object */ const request = new Request(url, options); diff --git a/frontend/src/pages/node/AssociationsTable.vue b/frontend/src/pages/node/AssociationsTable.vue index bc5df3448..2845d06d3 100644 --- a/frontend/src/pages/node/AssociationsTable.vue +++ b/frontend/src/pages/node/AssociationsTable.vue @@ -93,7 +93,11 @@