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

Upload dataset feature #839

Merged
merged 9 commits into from
Jan 21, 2021
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
4 changes: 2 additions & 2 deletions backend/config/corpora-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ paths:
- collection
summary: Publish a collection
security:
- cxguserCookie: [ ]
- cxguserCookie: []
description: >-
Update status of specified collection to PUBLIC. This will make it
visible on the public sites.
Expand Down Expand Up @@ -496,7 +496,7 @@ paths:
Cancels the download of a dataset to the data portal and cleans up (removes) any artifacts created in the download process
operationId: corpora.lambdas.api.v1.dataset.delete_dataset
security:
- cxguserCookie: [ ]
- cxguserCookie: []
parameters:
- $ref: "#/components/parameters/path_dataset_uuid"
responses:
Expand Down
1 change: 1 addition & 0 deletions frontend/src/common/API.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum API {
DATASET = "/dp/v1/datasets/{dataset_uuid}",
DATASET_ASSET_DOWNLOAD_LINK = "/dp/v1/datasets/{dataset_uuid}/asset/{asset_uuid}",
DATASET_STATUS = "/dp/v1/datasets/{dataset_uuid}/status",
COLLECTIONS = "/dp/v1/collections",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/common/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export interface Dataset {
dataset_deployments: DatasetDeployment[];
dataset_assets: DatasetAsset[];
processing_status: DatasetUploadStatus;
collection_id: Collection["id"];
// contributors: Contributor[];
// preprint_doi: DOI;
// publication_doi: DOI;
Expand Down Expand Up @@ -136,7 +137,7 @@ export enum UPLOAD_STATUS {
UPLOADING = "UPLOADING",
UPLOADED = "UPLOADED",
FAILED = "FAILED",
CANCEL = "CANCEL",
CANCEL_PENDING = "CANCEL_PENDING",
PENDING = "PENDING",
CANCELED = "CANCELED",
NA = "NA",
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/common/queries/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
export const DEFAULT_FETCH_OPTIONS: RequestInit = {
credentials: "include",
};

export const DELETE_FETCH_OPTIONS: RequestInit = {
credentials: "include",
method: "DELETE",
};
49 changes: 46 additions & 3 deletions frontend/src/common/queries/datasets.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useQuery } from "react-query";
import { useMutation, useQuery, useQueryCache } from "react-query";
import { API_URL } from "src/configs/configs";
import { API } from "../API";
import { DatasetUploadStatus } from "../entities";
import { DatasetUploadStatus, VISIBILITY_TYPE } from "../entities";
import { apiTemplateToUrl } from "../utils/apiTemplateToUrl";
import { DEFAULT_FETCH_OPTIONS } from "./common";
import { USE_COLLECTION } from "./collections";
import { DEFAULT_FETCH_OPTIONS, DELETE_FETCH_OPTIONS } from "./common";
import { ENTITIES } from "./entities";

export const USE_DATASET_STATUS = {
Expand All @@ -16,6 +17,7 @@ async function fetchDatasetStatus(
dataset_uuid: string
): Promise<DatasetUploadStatus> {
const url = apiTemplateToUrl(API_URL + API.DATASET_STATUS, { dataset_uuid });

return await (await fetch(url, DEFAULT_FETCH_OPTIONS)).json();
}

Expand All @@ -28,3 +30,44 @@ export function useDatasetStatus(dataset_uuid: string, shouldFetch: boolean) {
{ enabled: shouldFetch, refetchInterval: REFETCH_INTERVAL_MS }
);
}

export const USE_DELETE_DATASET = {
entities: [ENTITIES.DATASET],
id: "dataset",
};

async function deleteDataset(dataset_uuid = ""): Promise<DatasetUploadStatus> {
if (!dataset_uuid) throw new Error("No dataset id provided");

const url = apiTemplateToUrl(API_URL + API.DATASET, { dataset_uuid });
const response = await fetch(url, DELETE_FETCH_OPTIONS);

if (response.ok) return await response.json();

throw Error(response.statusText);
}

export function useDeleteDataset(collection_uuid = "") {
if (!collection_uuid) {
throw new Error("No collection id given");
}

const queryCache = useQueryCache();

return useMutation(deleteDataset, {
onSuccess: (uploadStatus: DatasetUploadStatus) => {
queryCache.invalidateQueries([
USE_COLLECTION,
collection_uuid,
VISIBILITY_TYPE.PRIVATE,
]);

queryCache.cancelQueries([USE_DATASET_STATUS, uploadStatus.dataset_id]);

queryCache.setQueryData(
[USE_DATASET_STATUS, uploadStatus.dataset_id],
uploadStatus
);
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ interface Props {
handleChange: (format: DATASET_ASSET_FORMAT) => void;
isDisabled: boolean;
format: DATASET_ASSET_FORMAT | "";
availableFormats: DATASET_ASSET_FORMAT[];
}

const DataFormat: FC<Props> = ({
handleChange: handleChangeRaw,
isDisabled = false,
format,
availableFormats,
}) => {
const handleChange = (event: React.FormEvent<HTMLElement>) => {
const value = (event.target as HTMLInputElement)
Expand All @@ -31,9 +33,21 @@ const DataFormat: FC<Props> = ({
onChange={handleChange}
selectedValue={format}
>
<Radio label=".h5ad (AnnData v0.7)" value={DATASET_ASSET_FORMAT.H5AD} />
<Radio label=".loom" value={DATASET_ASSET_FORMAT.LOOM} />
<Radio label=".rds (Seurat v3)" value={DATASET_ASSET_FORMAT.RDS} />
<Radio
disabled={!availableFormats.includes(DATASET_ASSET_FORMAT.H5AD)}
label=".h5ad (AnnData v0.7)"
value={DATASET_ASSET_FORMAT.H5AD}
/>
<Radio
disabled={!availableFormats.includes(DATASET_ASSET_FORMAT.LOOM)}
label=".loom"
value={DATASET_ASSET_FORMAT.LOOM}
/>
<Radio
disabled={!availableFormats.includes(DATASET_ASSET_FORMAT.RDS)}
label=".rds (Seurat v3)"
value={DATASET_ASSET_FORMAT.RDS}
/>
</RadioGroup>
</Section>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Classes } from "@blueprintjs/core";
import { Classes, Intent } from "@blueprintjs/core";
import React, { FC, useEffect, useState } from "react";
import { API } from "src/common/API";
import { Dataset, DATASET_ASSET_FORMAT } from "src/common/entities";
Expand All @@ -8,7 +8,7 @@ import CurlLink from "./components/CurlLink";
import DataFormat from "./components/DataFormat";
import Details from "./components/Details";
import Name from "./components/Name";
import { Cancel, DisabledDownload, Download, Wrapper } from "./style";
import { CancelButton, DownloadButton, Wrapper } from "./style";

interface Props {
onClose: () => void;
Expand All @@ -20,7 +20,7 @@ const Content: FC<Props> = ({ onClose, name, dataAssets }) => {
const [format, setFormat] = useState<DATASET_ASSET_FORMAT | "">("");
const [fileSize, setFileSize] = useState<number>(0);
const [fileName, setFileName] = useState<string>("");
const [downloadLink, setDownloadLink] = useState<string | null>(null);
const [downloadLink, setDownloadLink] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);

useEffect(() => {
Expand Down Expand Up @@ -50,20 +50,20 @@ const Content: FC<Props> = ({ onClose, name, dataAssets }) => {
};

const renderDownload = () => {
if (!downloadLink || isLoading) {
return <DisabledDownload>Download</DisabledDownload>;
}

return (
<Download
<DownloadButton
disabled={!downloadLink || isLoading}
data-test-id="download-asset-download-button"
href={downloadLink}
intent={Intent.PRIMARY}
>
Download
</Download>
</DownloadButton>
);
};

const availableFormats = dataAssets.map((dataAsset) => dataAsset.filetype);

return (
<>
<div className={Classes.DIALOG_BODY}>
Expand All @@ -73,6 +73,7 @@ const Content: FC<Props> = ({ onClose, name, dataAssets }) => {
handleChange={handleChange}
isDisabled={isLoading}
format={format}
availableFormats={availableFormats}
/>
<Details
isLoading={isLoading}
Expand All @@ -85,7 +86,9 @@ const Content: FC<Props> = ({ onClose, name, dataAssets }) => {
</Wrapper>
</div>
<div className={Classes.DIALOG_FOOTER}>
<Cancel onClick={onClose}>Cancel</Cancel>
<CancelButton onClick={onClose} minimal>
Cancel
</CancelButton>
{renderDownload()}
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,16 @@
import { OLD_BLUE, OLD_GRAY } from "src/components/common/theme";
import styled, { css } from "styled-components";
import { AnchorButton, Button } from "@blueprintjs/core";
import { PT_GRID_SIZE_PX } from "src/components/common/theme";
import styled from "styled-components";

export const Wrapper = styled.div`
width: 625px;
height: 300px;
`;

const buttonStyle = css`
margin-right: 10px;
border: none;
cursor: pointer;
export const DownloadButton = styled(AnchorButton)`
margin-right: ${PT_GRID_SIZE_PX}px;
`;

export const Cancel = styled.button`
${buttonStyle}
background-color: transparent;
color: ${OLD_GRAY.DARK};
font-size: 14px;
`;

const sharedDownloadStyle = css`
${buttonStyle}
background-color: ${OLD_BLUE};
color: white;
font-size: 14px;
width: 80px;
height: 37px;
border-radius: 4px;
margin-right: 10px;
`;

export const DisabledDownload = styled.button`
${sharedDownloadStyle}

cursor: default;
opacity: 0.6;
filter: unset;
`;

export const Download = styled.a`
${sharedDownloadStyle}

display: flex;
align-items: center;
justify-content: center;

:hover {
filter: brightness(1.1);
color: white;
cursor: pointer;
text-decoration: none;
}
export const CancelButton = styled(Button)`
margin-right: ${PT_GRID_SIZE_PX}px;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,29 @@ import { StyledButton } from "./style";
interface Props {
name: string;
dataAssets: Dataset["dataset_assets"];
isDisabled?: boolean;
Button?: React.ElementType;
}

const DownloadDataset: FC<Props> = ({ name, dataAssets }) => {
const DownloadDataset: FC<Props> = ({
name,
dataAssets,
isDisabled = false,
Button = StyledButton,
}) => {
const [isOpen, setIsOpen] = React.useState(false);

const toggleOpen = () => setIsOpen(!isOpen);

return (
<SmallColumn>
<StyledButton onClick={toggleOpen} data-test-id="dataset-download-button">
<Button
disabled={isDisabled}
onClick={toggleOpen}
data-test-id="dataset-download-button"
>
Download
</StyledButton>
</Button>
<Modal title="Download Dataset" isOpen={isOpen} onClose={toggleOpen}>
<Content name={name} dataAssets={dataAssets} onClose={toggleOpen} />
</Modal>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HTMLTable } from "@blueprintjs/core";
import { PT_GRID_SIZE_PX } from "src/components/common/theme";
import styled, { css } from "styled-components";

export const textClippingCSS = css`
Expand All @@ -13,24 +14,38 @@ const titleColWidthCSS = css`
width: calc(3 / 8 * 100%);
`;

const datasetTitleColWidthCSS = css`
width: calc(2 / 8 * 100%);
`;

export const StyledCollectionsGrid = styled(HTMLTable)`
grid-column: 1/9;
margin-top: 16px;
margin-top: ${PT_GRID_SIZE_PX * 2}px;
`;

export const CollectionHeaderCell = styled.th`
${titleColWidthCSS}
text-align: left !important;
padding: 0 !important;
`;

export const DatasetHeaderCell = styled.th`
${datasetTitleColWidthCSS}
text-align: left !important;
padding: 0 !important;
padding-left: ${PT_GRID_SIZE_PX * 2}px !important;
`;

export const LeftAlignedHeaderCell = styled.th`
${detailsColWidthCSS}
text-align: left !important;
margin-left: 16px;
padding: 0 !important;
padding-left: ${PT_GRID_SIZE_PX * 2}px !important;
`;

export const RightAlignedHeaderCell = styled.th`
width: calc(1 / 8 * 100%);
${datasetTitleColWidthCSS}
text-align: right !important;
margin-left: 16px;
padding: 0 !important;
padding-left: ${PT_GRID_SIZE_PX * 2}px !important;
`;
Loading