-
Notifications
You must be signed in to change notification settings - Fork 2
Austenem/CAT-983 MVP Bulk File Download #3604
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
Changes from all commits
99c1736
2d11c5d
2bd6009
f50fb2e
a368f43
5ccddb5
d616b20
c139813
c51dc1e
cdd6d69
47099a3
f4e7f15
7eb079c
e6ec0ba
eb6c5dd
34a14aa
73b15c6
fce7a12
8fc7d14
6ff2a12
3b60355
26b0356
fb38840
37f1776
5a00860
38591b4
c3cbcc9
0cfbedb
b3f362c
a7f4703
3fa2492
c9264fc
1b044af
4fb7c15
bb0e905
61559df
c0c156e
2a1538a
8b30019
7d057d6
a704aea
f08177c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
- Add dialog option to the Search, Publication, and Collection pages that generates a download manifest for files from selected datasets. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from 'react'; | ||
import { ButtonProps } from '@mui/material/Button'; | ||
import SvgIcon from '@mui/material/SvgIcon'; | ||
import Download from '@mui/icons-material/Download'; | ||
import { useBulkDownloadDialog } from 'js/components/bulkDownload/hooks'; | ||
import BulkDownloadDialog from 'js/components/bulkDownload/BulkDownloadDialog'; | ||
import { WhiteBackgroundIconTooltipButton } from 'js/shared-styles/buttons'; | ||
|
||
interface BulkDownloadButtonProps extends ButtonProps { | ||
tooltip: string; | ||
uuids: Set<string>; | ||
deselectRows?: (uuids: string[]) => void; | ||
} | ||
function BulkDownloadButton({ tooltip, uuids, deselectRows, ...rest }: BulkDownloadButtonProps) { | ||
const { openDialog } = useBulkDownloadDialog(); | ||
|
||
return ( | ||
<> | ||
<WhiteBackgroundIconTooltipButton tooltip={tooltip} onClick={() => openDialog(uuids)} {...rest}> | ||
<SvgIcon color="primary" component={Download} /> | ||
</WhiteBackgroundIconTooltipButton> | ||
<BulkDownloadDialog deselectRows={deselectRows} /> | ||
</> | ||
); | ||
} | ||
|
||
export default BulkDownloadButton; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import BulkDownloadButton from './BulkDownloadButton'; | ||
|
||
export default BulkDownloadButton; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React from 'react'; | ||
import BulkDownloadButton from 'js/components/bulkDownload/BulkDownloadButton/BulkDownloadButton'; | ||
import { useSelectableTableStore } from 'js/shared-styles/tables/SelectableTableProvider'; | ||
|
||
const tooltip = | ||
'Bulk download files for selected datasets. If no datasets are selected, all datasets given the current filters will be selected.'; | ||
|
||
interface BulkDownloadButtonFromSearchProps { | ||
type: string; | ||
allResultsUUIDs: string[]; | ||
} | ||
function BulkDownloadButtonFromSearch({ type, allResultsUUIDs }: BulkDownloadButtonFromSearchProps) { | ||
const { selectedRows, deselectRows } = useSelectableTableStore(); | ||
|
||
const disabled = allResultsUUIDs.length === 0; | ||
const isDatasetSearch = type.toLowerCase() === 'dataset'; | ||
|
||
if (!isDatasetSearch) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<BulkDownloadButton | ||
tooltip={disabled ? 'Loading datasets...' : tooltip} | ||
deselectRows={deselectRows} | ||
uuids={selectedRows.size > 0 ? selectedRows : new Set(allResultsUUIDs)} | ||
disabled={disabled} | ||
/> | ||
); | ||
} | ||
|
||
export default BulkDownloadButtonFromSearch; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import BulkDownloadButtonFromSearch from './BulkDownloadButtonFromSearch'; | ||
|
||
export default BulkDownloadButtonFromSearch; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
import React from 'react'; | ||
import { Control } from 'react-hook-form'; | ||
import Button from '@mui/material/Button'; | ||
import Stack from '@mui/material/Stack'; | ||
import Box from '@mui/material/Box'; | ||
import Skeleton from '@mui/material/Skeleton'; | ||
import Typography from '@mui/material/Typography'; | ||
|
||
import { LINKS, PAGES } from 'js/components/bulkDownload/constants'; | ||
import { BulkDownloadFormTypes, useBulkDownloadDialog } from 'js/components/bulkDownload/hooks'; | ||
import RemoveProtectedDatasetsFormField from 'js/components/workspaces/RemoveProtectedDatasetsFormField'; | ||
import BulkDownloadOptionsField from 'js/components/bulkDownload/BulkDownloadOptionsField'; | ||
import BulkDownloadMetadataField from 'js/components/bulkDownload/BulkDownloadMetadataField'; | ||
import { DatasetAccessLevelHits } from 'js/hooks/useProtectedDatasets'; | ||
import SummaryPaper from 'js/shared-styles/sections/SectionPaper'; | ||
import DialogModal from 'js/shared-styles/DialogModal'; | ||
import { SectionDescription } from 'js/shared-styles/sections/SectionDescription'; | ||
import Step from 'js/shared-styles/surfaces/Step'; | ||
import { OutboundLink } from 'js/shared-styles/Links'; | ||
import RelevantPagesSection from 'js/shared-styles/sections/RelevantPagesSection'; | ||
import LabelledSectionText from 'js/shared-styles/sections/LabelledSectionText'; | ||
import { Alert } from 'js/shared-styles/alerts'; | ||
import ErrorOrWarningMessages from 'js/shared-styles/alerts/ErrorOrWarningMessages'; | ||
|
||
function DownloadDescription() { | ||
return ( | ||
<SectionDescription> | ||
<Stack spacing={2}> | ||
<Typography> | ||
Choose download options to bulk download files from your selected datasets. Your selection of files will | ||
generate a manifest file, which can be used with the{' '} | ||
<OutboundLink href={LINKS.documentation}>HuBMAP Command Line Transfer (CLT)</OutboundLink> tool for | ||
downloading. An option to download a tsv file of the metadata is also available. | ||
</Typography> | ||
<Typography> | ||
To download the files included in the manifest file,{' '} | ||
<OutboundLink href={LINKS.installation}>install the HuBMAP CLT</OutboundLink> (if not already installed) and | ||
follow <OutboundLink href={LINKS.documentation}>instructions</OutboundLink> for how to use it with the | ||
manifest file. | ||
{/* TODO: uncomment once tutorial is created */} | ||
{/* A <OutboundLink href={LINKS.tutorial}>tutorial</OutboundLink> is available to guide you through | ||
the entire process. */} | ||
</Typography> | ||
<RelevantPagesSection pages={PAGES} /> | ||
</Stack> | ||
</SectionDescription> | ||
); | ||
} | ||
|
||
interface ProtectedDatasetsSectionProps { | ||
control: Control<BulkDownloadFormTypes>; | ||
errorMessages: string[]; | ||
protectedHubmapIds: string; | ||
protectedRows: DatasetAccessLevelHits; | ||
removeProtectedDatasets: () => void; | ||
} | ||
function ProtectedDatasetsSection({ | ||
control, | ||
errorMessages, | ||
protectedHubmapIds, | ||
protectedRows, | ||
removeProtectedDatasets, | ||
}: ProtectedDatasetsSectionProps) { | ||
if (errorMessages.length === 0) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<Stack paddingY={1}> | ||
<ErrorOrWarningMessages errorMessages={errorMessages} /> | ||
<RemoveProtectedDatasetsFormField | ||
control={control} | ||
protectedHubmapIds={protectedHubmapIds} | ||
removeProtectedDatasets={removeProtectedDatasets} | ||
protectedRows={protectedRows} | ||
/> | ||
</Stack> | ||
); | ||
} | ||
|
||
function DownloadOptionsDescription() { | ||
return ( | ||
<SectionDescription> | ||
<Stack spacing={2}> | ||
<Box>Select raw and/or processed files to download.</Box> | ||
<LabelledSectionText label="Raw Data" spacing={1}> | ||
Raw data consists of files as originally submitted by the data submitters. Individuals files for raw data | ||
cannot be previewed or selected for the manifest download. You must download all raw files, and the total | ||
download size cannot be estimated. | ||
</LabelledSectionText> | ||
<LabelledSectionText label="Processed Data" spacing={1}> | ||
Processed data includes files associated with data generated by HuBMAP using uniform processing pipelines or | ||
by an external processing approach. Only files centrally processed by HuBMAP are available for individual | ||
selection, which can be done in the Advanced Selections section below. | ||
</LabelledSectionText> | ||
</Stack> | ||
</SectionDescription> | ||
); | ||
} | ||
|
||
interface DownloadOptionsSectionProps { | ||
control: Control<BulkDownloadFormTypes>; | ||
downloadOptions: { | ||
key: string; | ||
label: string; | ||
}[]; | ||
isLoading: boolean; | ||
errorMessages: string[]; | ||
protectedHubmapIds: string; | ||
protectedRows: DatasetAccessLevelHits; | ||
removeProtectedDatasets: () => void; | ||
} | ||
function DownloadOptionsSection({ | ||
control, | ||
downloadOptions, | ||
isLoading, | ||
errorMessages, | ||
protectedHubmapIds, | ||
protectedRows, | ||
removeProtectedDatasets, | ||
}: DownloadOptionsSectionProps) { | ||
if (isLoading) { | ||
return ( | ||
<> | ||
<Skeleton variant="rectangular" height={50} /> | ||
<Skeleton variant="rectangular" height={300} /> | ||
<Skeleton variant="rectangular" height={200} /> | ||
</> | ||
); | ||
} | ||
|
||
if (downloadOptions.length === 0) { | ||
return ( | ||
<Alert severity="warning"> | ||
<Typography>Files are not available for any of the selected datasets.</Typography> | ||
</Alert> | ||
); | ||
} | ||
|
||
return ( | ||
<Box> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above regarding the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
<Step title="Download Options" hideRequiredText> | ||
<ProtectedDatasetsSection | ||
control={control} | ||
errorMessages={errorMessages} | ||
protectedHubmapIds={protectedHubmapIds} | ||
protectedRows={protectedRows} | ||
removeProtectedDatasets={removeProtectedDatasets} | ||
/> | ||
<DownloadOptionsDescription /> | ||
<SummaryPaper> | ||
<Stack direction="column" spacing={2}> | ||
<BulkDownloadOptionsField control={control} name="bulkDownloadOptions" /> | ||
<BulkDownloadMetadataField control={control} name="bulkDownloadMetadata" /> | ||
</Stack> | ||
</SummaryPaper> | ||
</Step> | ||
</Box> | ||
); | ||
} | ||
|
||
const formId = 'bulk-download-form'; | ||
|
||
interface BulkDownloadDialogProps { | ||
deselectRows?: (uuids: string[]) => void; | ||
} | ||
function BulkDownloadDialog({ deselectRows }: BulkDownloadDialogProps) { | ||
const { | ||
handleSubmit, | ||
onSubmit, | ||
handleClose, | ||
isOpen, | ||
errors, | ||
control, | ||
isLoading, | ||
downloadOptions, | ||
errorMessages, | ||
...rest | ||
} = useBulkDownloadDialog(deselectRows); | ||
|
||
return ( | ||
<DialogModal | ||
title="Bulk Download Files" | ||
maxWidth="lg" | ||
content={ | ||
// eslint-disable-next-line @typescript-eslint/no-misused-promises | ||
<form id={formId} onSubmit={handleSubmit(onSubmit)}> | ||
<Stack spacing={2}> | ||
<DownloadDescription /> | ||
<DownloadOptionsSection | ||
control={control} | ||
downloadOptions={downloadOptions} | ||
isLoading={isLoading} | ||
errorMessages={errorMessages} | ||
{...rest} | ||
/> | ||
</Stack> | ||
</form> | ||
} | ||
isOpen={isOpen} | ||
handleClose={handleClose} | ||
actions={ | ||
<Stack direction="row" spacing={2} alignItems="end"> | ||
<Button type="button" onClick={handleClose}> | ||
Cancel | ||
</Button> | ||
<Button | ||
type="submit" | ||
variant="contained" | ||
form={formId} | ||
disabled={Object.keys(errors).length > 0 || errorMessages.length > 0} | ||
> | ||
Generate Download Manifest | ||
</Button> | ||
</Stack> | ||
} | ||
withCloseButton | ||
/> | ||
); | ||
} | ||
|
||
export default BulkDownloadDialog; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import BulkDownloadDialog from './BulkDownloadDialog'; | ||
|
||
export default BulkDownloadDialog; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from 'react'; | ||
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'; | ||
import Stack from '@mui/material/Stack'; | ||
import { PrimarySwitch } from 'js/shared-styles/switches'; | ||
import { StyledFormLabel } from 'js/components/bulkDownload/style'; | ||
|
||
type BulkDownloadMetadataFieldProps<FormType extends FieldValues> = Pick< | ||
UseControllerProps<FormType>, | ||
'name' | 'control' | ||
>; | ||
function BulkDownloadMetadataField<FormType extends FieldValues>({ | ||
control, | ||
name, | ||
}: BulkDownloadMetadataFieldProps<FormType>) { | ||
const { field } = useController({ | ||
name, | ||
control, | ||
}); | ||
|
||
return ( | ||
<Stack> | ||
<StyledFormLabel id="bulk-download-metadata">Download Metadata File (TSV)</StyledFormLabel> | ||
<PrimarySwitch | ||
checked={field.value} | ||
onChange={(e) => field.onChange(!!e.target.checked)} | ||
inputProps={{ 'aria-labelledby': 'bulk-download-metadata' }} | ||
sx={(theme) => ({ marginLeft: theme.spacing(-1) })} | ||
/> | ||
</Stack> | ||
); | ||
} | ||
|
||
export default BulkDownloadMetadataField; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import BulkDownloadMetadataField from './BulkDownloadMetadataField'; | ||
|
||
export default BulkDownloadMetadataField; |
Uh oh!
There was an error while loading. Please reload this page.