Skip to content

Commit

Permalink
feat(download): add UI components and polling for zip downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
mcannalte committed Aug 12, 2021
1 parent 2f2c200 commit 3df9c66
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 2 deletions.
8 changes: 8 additions & 0 deletions src/Discovery/Discovery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
const [permalinkCopied, setPermalinkCopied] = useState(false);
const [exportingToWorkspace, setExportingToWorkspace] = useState(false);
const [advSearchFilterHeight, setAdvSearchFilterHeight] = useState('100vh');
const [downloadInProgress, setDownloadInProgress] = useState(false);
const [downloadStatusMessage, setDownloadStatusMessage] = useState({"message":"", "title": "", "active": false});

const handleSearchChange = (ev) => {
const { value } = ev.currentTarget;
Expand All @@ -208,6 +210,8 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
setSearchFilteredResources(results);
};



useEffect(() => {
// Load studies into JS Search.
const search = new JsSearch.Search(config.minimalFieldMapping.uid);
Expand Down Expand Up @@ -508,6 +512,10 @@ const Discovery: React.FunctionComponent<Props> = (props: Props) => {
setExportingToWorkspace={setExportingToWorkspace}
filtersVisible={filtersVisible}
setFiltersVisible={setFiltersVisible}
downloadInProgress={downloadInProgress}
setDownloadInProgress={setDownloadInProgress}
downloadStatusMessage={downloadStatusMessage}
setDownloadStatusMessage={setDownloadStatusMessage}
/>

{/* Advanced search panel */}
Expand Down
148 changes: 146 additions & 2 deletions src/Discovery/DiscoveryActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import {
RightOutlined,
ExportOutlined,
DownloadOutlined,
FileTextOutlined
} from '@ant-design/icons';
import FileSaver from 'file-saver';
import { DiscoveryConfig } from './DiscoveryConfig';
import { fetchWithCreds } from '../actions';
import { manifestServiceApiPath, hostname } from '../localconf';
import { manifestServiceApiPath, hostname, discoveryConfig } from '../localconf';
import Popup from '../components/Popup';

interface User {
username: string
Expand All @@ -27,8 +29,109 @@ interface Props {
filtersVisible: boolean;
setFiltersVisible: (boolean) => void;
user: User,
downloadInProgress: boolean,
setDownloadInProgress: (boolean) => void,
downloadStatusMessage: {
title: string,
message: string,
active: boolean
},
setDownloadStatusMessage: (Object) => void;
}

const handleDownloadZipClick = async (
selectedResources: any[],
setDownloadInProgress: (boolean) => void,
setDownloadStatusMessage: (object) => void,
manifestFieldName: string,
maxDownloadSizeBytes: number
) => {
const studyIDs = selectedResources.map(study => study.project_number)
let downloadSize = 0;
for (const study of selectedResources) {
for (const file of study[manifestFieldName]) {
downloadSize += file.file_size;
}
}

if (downloadSize > maxDownloadSizeBytes) {
const maxSizeMB = (maxDownloadSizeBytes / 1000000).toFixed(1);
const downloadSizeMb = (downloadSize / 1000000).toFixed(1);
setDownloadStatusMessage(
{
"title": "Download limit exceeded",
"message": `
The selected studies contain ${downloadSizeMb} MB of data, which exceeds the download limit of ${maxSizeMB} MB.
Please deselect some studies and try again, or use the gen3 client.
`,
"active": true
}
);
return
}

setDownloadInProgress(true);
const triggerDownloadResponse = await fetch(
"/job/dispatch",
{
method: "POST",
body: JSON.stringify({
"action": "batch-export",
"input": {
"study_ids": studyIDs
}
})
}
)
const initialialJobState = await triggerDownloadResponse.json()
const downloadFailedMessage = {
"title": "Download failed",
"message": "There was a problem preparing your download." +
"Please consider using the gen3 client to download these files via a manifest.",
"active": true
};

if (initialialJobState === null) {
console.warn("Sower is not configured for batch-export job.")
setDownloadInProgress(false);
setDownloadStatusMessage(downloadFailedMessage);
}
else {
const uid = initialialJobState.uid;
const pollForUpdate = async () => {

const statusResponse = await fetch(`/job/status?UID=${uid}`);
const statusObject = await statusResponse.json();
const status = statusObject.status;

if (status === "Failed") {
setDownloadStatusMessage(downloadFailedMessage);
setDownloadInProgress(false);
}
else if (status === "Completed") {
const outputResponse = await fetch(`/job/output?UID=${uid}`);
const outputJSON = await outputResponse.json();
const url = outputJSON.output;
const message = "Your download has been prepared. If your download doesn't start automatically, please copy and paste this url into your browser:\n\n" + url
setDownloadStatusMessage(
{
"title": "Download prepared",
"message": message,
"active": true
}
)
setDownloadInProgress(false);
setTimeout(() => window.open(url), 2000);
}
else {
setTimeout(pollForUpdate, 5000);
}
}
setTimeout(pollForUpdate, 5000);
}
}


const handleDownloadManifestClick = (config: DiscoveryConfig, selectedResources: any[]) => {
const { manifestFieldName } = config.features.exportToWorkspace;
if (!manifestFieldName) {
Expand Down Expand Up @@ -129,6 +232,32 @@ const DiscoveryActionBar = (props: Props) => {
&& (
<Space>
<span className='discovery-export__selected-ct'>{props.selectedResources.length} selected</span>
{
props.config.features.exportToWorkspace.enableDownloadZip &&
<Button
onClick={(props.user.username) ? () => {
handleDownloadZipClick(
props.selectedResources,
props.setDownloadInProgress,
props.setDownloadStatusMessage,
props.config.features.exportToWorkspace.manifestFieldName,
props.config.features.exportToWorkspace.downloadZipMaxSizeBytes || 250000000
);
}
: () => { handleRedirectToLoginClick(); }}
type='text'
disabled={props.selectedResources.length === 0 || props.downloadInProgress === true}
icon={<DownloadOutlined />}
>
{(props.user.username) ?
(
(props.downloadInProgress === true) ? "Preparing download..."
: `${props.config.features.exportToWorkspace.downloadZipButtonText || 'Download Zip'}`
)
:
`Login to ${props.config.features.exportToWorkspace.downloadZipButtonText || 'Download Zip'}`}
</Button>
}
{ props.config.features.exportToWorkspace.enableDownloadManifest
&& (
<Popover
Expand All @@ -155,11 +284,12 @@ const DiscoveryActionBar = (props: Props) => {
: () => { handleRedirectToLoginClick(); }}
type='text'
disabled={props.selectedResources.length === 0}
icon={<DownloadOutlined />}
icon={<FileTextOutlined />}
>
{(props.user.username) ? `${props.config.features.exportToWorkspace.downloadManifestButtonText || 'Download Manifest'}`
: `Login to ${props.config.features.exportToWorkspace.downloadManifestButtonText || 'Download Manifest'}`}
</Button>

</Popover>
)}
<Popover
Expand Down Expand Up @@ -188,6 +318,20 @@ const DiscoveryActionBar = (props: Props) => {
{(props.user.username) ? 'Open In Workspace' : 'Login to Open In Workspace'}
</Button>
</Popover>
{
props.downloadStatusMessage.active &&
<Popup
message={props.downloadStatusMessage.message}
title={props.downloadStatusMessage.title}
leftButtons={[
{
caption: 'Close',
fn: () => props.setDownloadStatusMessage({"title": "", "message": "", "active": false}),
},
]}
/>
}

</Space>
)}
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/Discovery/DiscoveryConfig.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export interface DiscoveryConfig {
enableDownloadManifest: boolean
downloadManifestButtonText?: string
manifestFieldName: string
enableDownloadZip: boolean
downloadZipButtonText: string
downloadZipMaxSizeBytes: number
}
// explorationIntegration: {
// enabled: boolean // not supported
Expand Down

0 comments on commit 3df9c66

Please sign in to comment.