Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add associations tsv download to frontend #537

Merged
merged 8 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion backend/src/monarch_py/api/entity.py
Original file line number Diff line number Diff line change
@@ -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"}})

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
29 changes: 29 additions & 0 deletions frontend/src/api/associations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
6 changes: 5 additions & 1 deletion frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ export const request = async <Response>(
* { 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<Response> => {
/** get string of url parameters/options */
const paramsObject = new URLSearchParams();
Expand All @@ -96,6 +98,8 @@ export const request = async <Response>(
const paramsString = "?" + paramsObject.toString();
const url = path + paramsString;

if (newTab) window.open(url);

/** make request object */
const request = new Request(url, options);

Expand Down
30 changes: 15 additions & 15 deletions frontend/src/pages/node/AssociationsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@

<script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue";
import { getAssociations } from "@/api/associations";
import {
downloadAssociations,
getAssociations,
maxDownload,
} from "@/api/associations";
import { getCategoryLabel } from "@/api/categories";
import {
AssociationDirectionEnum,
Expand All @@ -108,7 +112,6 @@ import type { Cols, Sort } from "@/components/AppTable.vue";
import { snackbar } from "@/components/TheSnackbar.vue";
import { getBreadcrumbs } from "@/pages/node/AssociationsSummary.vue";
import { useQuery } from "@/util/composables";
import { downloadJson } from "@/util/download";

type Props = {
/** current node */
Expand Down Expand Up @@ -276,26 +279,23 @@ const {

/** download table data */
async function download() {
/** max rows to try to query */
const max = 100;
const total = associations.value.total;

/** warn user */
snackbar(
`Downloading data for ${total > max ? "first " : ""}${Math.min(
total,
max,
)} table entries.` + (total >= 100 ? " This may take a minute." : ""),
`Downloading data for ${
associations.value.total > maxDownload ? "first " : ""
}${Math.min(
associations.value.total,
maxDownload,
)} table rows. This may take a minute.`,
);

/** attempt to request all rows */
const response = await getAssociations(
/** download as many rows as possible */
await downloadAssociations(
props.node.id,
props.category.id,
0,
max,
search.value,
sort.value,
);
downloadJson(response, "associations");
}

/** get associations when category or table state changes */
Expand Down
1 change: 0 additions & 1 deletion frontend/src/pages/node/SectionOverview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@
</template>

<script setup lang="ts">
import type { info } from "console";
import { computed } from "vue";
import { omit } from "lodash";
import type { Node } from "@/api/model";
Expand Down