-
Notifications
You must be signed in to change notification settings - Fork 47
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
(PXP-6544) Limited PFB Export from the Files Tab #729
Changes from 13 commits
0f919d6
24d1b62
588baac
2bbcf9d
e1b9517
23f1f66
b7099ad
38ebc57
376aa8e
c2b7a70
1200fdc
425e92a
7b8c61c
fb8dd05
c55a4e9
49f5dd7
5beb15e
540c8f3
0f6fc7b
5ba5a17
1b7254d
571ad86
d104636
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 |
---|---|---|
|
@@ -33,6 +33,8 @@ class ExplorerButtonGroup extends React.Component { | |
pfbStartText: 'Your export is in progress.', | ||
pfbWarning: 'Please do not navigate away from this page until your export is finished.', | ||
pfbSuccessText: 'Your cohort has been exported to PFB! The URL is displayed below.', | ||
// for export to PFB in Files tab | ||
sourceNodesInCohort: [], | ||
// for export to workspace | ||
exportingToWorkspace: false, | ||
exportWorkspaceFileName: null, | ||
|
@@ -42,6 +44,12 @@ class ExplorerButtonGroup extends React.Component { | |
} | ||
|
||
componentWillReceiveProps(nextProps) { | ||
if (nextProps.job && nextProps.job.status === 'Failed' && this.props.job.status !== 'Failed') { | ||
this.setState({ | ||
toasterOpen: true, | ||
toasterHeadline: this.state.toasterErrorText, | ||
}); | ||
} | ||
if (nextProps.job && nextProps.job.status === 'Completed' && this.props.job.status !== 'Completed') { | ||
this.fetchJobResult() | ||
.then((res) => { | ||
|
@@ -74,6 +82,14 @@ class ExplorerButtonGroup extends React.Component { | |
&& nextProps.totalCount) { | ||
this.refreshManifestEntryCount(); | ||
} | ||
if (this.props.buttonConfig.enableLimitedFilePFBExport | ||
&& nextProps.filter !== this.props.filter) { | ||
const sourceNodeField = this.props.buttonConfig.enableLimitedFilePFBExport.sourceNodeField; | ||
if (!sourceNodeField) { | ||
throw new Error('Limited File PFB Export is enabled, but \'sourceNodeField\' has not been specified. Check the portal config.'); | ||
} | ||
this.refreshSourceNodes(nextProps.filter, sourceNodeField); | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
|
@@ -101,12 +117,28 @@ class ExplorerButtonGroup extends React.Component { | |
clickFunc = this.exportToTerra; | ||
} | ||
} | ||
if (buttonConfig.type === 'export-files') { | ||
// REMOVE THIS CODE WHEN TERRA EXPORT WORKS | ||
// ======================================= | ||
if (terraExportWarning) { | ||
clickFunc = this.exportFilesToTerraWithWarning; | ||
} else { | ||
// ======================================= | ||
clickFunc = this.exportFilesToTerra; | ||
} | ||
} | ||
if (buttonConfig.type === 'export-to-seven-bridges') { | ||
clickFunc = this.exportToSevenBridges; | ||
} | ||
if (buttonConfig.type === 'export-files-to-seven-bridges') { | ||
clickFunc = this.exportFilesToSevenBridges; | ||
} | ||
if (buttonConfig.type === 'export-to-pfb') { | ||
clickFunc = this.exportToPFB; | ||
} | ||
if (buttonConfig.type === 'export-files-to-pfb') { | ||
clickFunc = this.exportFilesToPFB; | ||
} | ||
if (buttonConfig.type === 'export-to-workspace') { | ||
clickFunc = this.exportToWorkspace; | ||
} | ||
|
@@ -115,7 +147,6 @@ class ExplorerButtonGroup extends React.Component { | |
} | ||
return clickFunc; | ||
}; | ||
|
||
getManifest = async (indexType) => { | ||
if (!this.props.guppyConfig.manifestMapping | ||
|| !this.props.guppyConfig.manifestMapping.referenceIdFieldInDataIndex) { | ||
|
@@ -320,6 +351,16 @@ class ExplorerButtonGroup extends React.Component { | |
this.exportToTerra(); | ||
} | ||
} | ||
exportFilesToTerraWithWarning = () => { | ||
// If the number of subjects is over the threshold, warn the user that their | ||
// export to Terra job might fail. | ||
if (this.props.totalCount >= terraExportWarning.subjectThreshold) { | ||
this.setState({ enableTerraWarningPopup: true }); | ||
} else { | ||
// If the number is below the threshold, proceed as normal | ||
this.exportFilesToTerra(); | ||
} | ||
} | ||
// ========================================== | ||
|
||
exportToTerra = () => { | ||
|
@@ -328,12 +369,24 @@ class ExplorerButtonGroup extends React.Component { | |
}); | ||
}; | ||
|
||
exportFilesToTerra = () => { | ||
this.setState({ exportingToTerra: true }, () => { | ||
this.exportFilesToPFB(); | ||
}); | ||
}; | ||
|
||
exportToSevenBridges = () => { | ||
this.setState({ exportingToSevenBridges: true }, () => { | ||
this.exportToPFB(); | ||
}); | ||
} | ||
|
||
exportFilesToSevenBridges = () => { | ||
this.setState({ exportingToSevenBridges: true }, () => { | ||
this.exportFilesToPFB(); | ||
}); | ||
} | ||
|
||
sendPFBToTerra = () => { | ||
const url = encodeURIComponent(this.state.exportPFBURL); | ||
let templateParam = ''; | ||
|
@@ -360,6 +413,31 @@ class ExplorerButtonGroup extends React.Component { | |
}); | ||
}; | ||
|
||
exportFilesToPFB = () => { | ||
if (this.props.buttonConfig.enableLimitedFilePFBExport) { | ||
if (!this.state.sourceNodesInCohort || this.state.sourceNodesInCohort.length !== 1) { | ||
return; | ||
} | ||
const rootNode = this.state.sourceNodesInCohort[0]; | ||
this.props.submitJob({ | ||
action: 'export-files', | ||
input: { | ||
filter: getGQLFilter(this.props.filter), | ||
root_node: rootNode, | ||
}, | ||
}); | ||
this.props.checkJobStatus(); | ||
this.setState({ | ||
toasterOpen: true, | ||
toasterHeadline: this.state.pfbStartText, | ||
}); | ||
} else { | ||
/* eslint-disable no-console */ | ||
console.error(`Error: Missing \`enableLimitedFilePFBExport\` in the portal config. | ||
Currently, in order to export a File PFB, \`enableLimitedFilePFBExport\` must be set in the portal config.`); | ||
} | ||
}; | ||
|
||
exportToWorkspace = async (indexType) => { | ||
this.setState({ exportingToWorkspace: true }); | ||
const resultManifest = await this.getManifest(indexType); | ||
|
@@ -462,6 +540,43 @@ class ExplorerButtonGroup extends React.Component { | |
} | ||
}; | ||
|
||
refreshSourceNodes = async (filter, sourceNodeField) => { | ||
try { | ||
const indexType = this.props.guppyConfig.dataType; | ||
const query = `query ($filter: JSON) { | ||
_aggregation { | ||
${indexType} (filter: $filter) { | ||
${sourceNodeField} { | ||
histogram { | ||
key | ||
count | ||
} | ||
} | ||
} | ||
} | ||
}`; | ||
const body = { query, variables: { filter: getGQLFilter(filter) } }; | ||
const res = await fetchWithCreds({ | ||
path: guppyGraphQLUrl, | ||
method: 'POST', | ||
body: JSON.stringify(body), | ||
}); | ||
// eslint-disable-next-line no-underscore-dangle | ||
const sourceNodesHistogram = res.data.data._aggregation[indexType][sourceNodeField].histogram; | ||
const sourceNodes = []; | ||
sourceNodesHistogram.forEach(({ key, count }) => { | ||
if (count > 0) { | ||
sourceNodes.push(key); | ||
} | ||
}); | ||
this.setState({ | ||
sourceNodesInCohort: sourceNodes, | ||
}); | ||
} catch (err) { | ||
throw Error(`Error when getting data types: ${err}`); | ||
} | ||
}; | ||
|
||
// check if the user has access to this resource | ||
isButtonDisplayed = (buttonConfig) => { | ||
if (buttonConfig.type === 'export-to-workspace' || buttonConfig.type === 'export-files-to-workspace') { | ||
|
@@ -483,6 +598,19 @@ class ExplorerButtonGroup extends React.Component { | |
// disable the pfb export button if any other pfb export jobs are running | ||
return !(this.state.exportingToTerra || this.state.exportingToSevenBridges); | ||
} | ||
if (buttonConfig.type === 'export-files-to-pfb') { | ||
// disable the pfb export button if any other pfb export jobs are running | ||
const otherPFBJobsRunning = this.state.exportingToTerra | ||
|| this.state.exportingToSevenBridges; | ||
if (otherPFBJobsRunning) { | ||
return false; | ||
} | ||
// If limited file PFB export is enabled, disable the button if the selected | ||
// data files are on more than one source node. (See https://github.com/uc-cdis/data-portal/pull/729) | ||
if (this.props.buttonConfig.enableLimitedFilePFBExport) { | ||
return this.state.sourceNodesInCohort.length === 1; | ||
} | ||
} | ||
if (buttonConfig.type === 'export') { | ||
if (!this.props.buttonConfig.terraExportURL) { | ||
console.error('Export to Terra button is present, but there is no `terraExportURL` specified in the portal config. Disabling the export to Terra button.'); // eslint-disable-line no-console | ||
|
@@ -494,9 +622,28 @@ class ExplorerButtonGroup extends React.Component { | |
|| this.state.exportingToSevenBridges | ||
|| this.isPFBRunning()); | ||
} | ||
if (buttonConfig.type === 'export-files') { | ||
if (!this.props.buttonConfig.terraExportURL) { | ||
console.error('Export to Terra button is present, but there is no `terraExportURL` specified in the portal config. Disabling the export to Terra button.'); // eslint-disable-line no-console | ||
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. something worth noting: each re-render (like clicking a checkbox) produces this error print 12 times :O 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. if it's too much work, no worries, but -- the constructor might be a better place to do these error prints 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. Ya good idea, I just stuck these checks in the constructor! |
||
return false; | ||
} | ||
// disable the terra export button if any of the | ||
// pfb export operations are running. | ||
const otherPFBJobsRunning = this.state.exportingToTerra | ||
|| this.state.exportingToSevenBridges | ||
|| this.isPFBRunning(); | ||
if (otherPFBJobsRunning) { | ||
return false; | ||
} | ||
// If limited file PFB export is enabled, disable the button if the selected | ||
// data files are on more than one source node. (See https://github.com/uc-cdis/data-portal/pull/729) | ||
if (this.props.buttonConfig.enableLimitedFilePFBExport) { | ||
return this.state.sourceNodesInCohort.length === 1; | ||
} | ||
} | ||
if (buttonConfig.type === 'export-to-seven-bridges') { | ||
if (!this.props.buttonConfig.sevenBridgesExportURL) { | ||
console.error('Export to Terra button is present, but there is no `terraExportURL` specified in the portal config. Disabling the export to Terra button.'); // eslint-disable-line no-console | ||
console.error('Export to Seven Bridges button is present, but there is no `sevenBridgesExportURL` specified in the portal config. Disabling the export to Seven Bridges button.'); // eslint-disable-line no-console | ||
return false; | ||
} | ||
// disable the seven bridges export buttons if any of the | ||
|
@@ -505,6 +652,25 @@ class ExplorerButtonGroup extends React.Component { | |
|| this.state.exportingToSevenBridges | ||
|| this.isPFBRunning()); | ||
} | ||
if (buttonConfig.type === 'export-files-to-seven-bridges') { | ||
if (!this.props.buttonConfig.sevenBridgesExportURL) { | ||
console.error('Export to Seven Bridges button is present, but there is no `sevenBridgesExportURL` specified in the portal config. Disabling the export to Seven Bridges button.'); // eslint-disable-line no-console | ||
return false; | ||
} | ||
// disable the seven bridges export buttons if any of the | ||
// pfb export operations are running. | ||
const otherPFBJobsRunning = this.state.exportingToTerra | ||
|| this.state.exportingToSevenBridges | ||
|| this.isPFBRunning(); | ||
if (otherPFBJobsRunning) { | ||
return false; | ||
} | ||
// If limited file PFB export is enabled, disable the button if the selected | ||
// data files are on more than one source node. (See https://github.com/uc-cdis/data-portal/pull/729) | ||
if (this.props.buttonConfig.enableLimitedFilePFBExport) { | ||
return this.state.sourceNodesInCohort.length === 1; | ||
} | ||
} | ||
if (buttonConfig.type === 'export-to-workspace') { | ||
return this.state.manifestEntryCount > 0; | ||
} | ||
|
@@ -519,19 +685,19 @@ class ExplorerButtonGroup extends React.Component { | |
if (buttonConfig.type === 'export-to-workspace' || buttonConfig.type === 'export-files-to-workspace') { | ||
return this.state.exportingToWorkspace; | ||
} | ||
if (buttonConfig.type === 'export-to-pfb') { | ||
if (buttonConfig.type === 'export-to-pfb' || buttonConfig.type === 'export-files-to-pfb') { | ||
// export to pfb button is pending if a pfb export job is running and it's | ||
// neither an export to terra job or an export to seven bridges job. | ||
return this.isPFBRunning() | ||
&& !(this.state.exportingToTerra || this.state.exportingToSevenBridges); | ||
} | ||
if (buttonConfig.type === 'export') { | ||
if (buttonConfig.type === 'export' || buttonConfig.type === 'export-files') { | ||
// export to terra button is pending if a pfb export job is running and | ||
// it's an exporting to terra job. | ||
return this.isPFBRunning() | ||
&& this.state.exportingToTerra; | ||
} | ||
if (buttonConfig.type === 'export-to-seven-bridges') { | ||
if (buttonConfig.type === 'export-to-seven-bridges' || buttonConfig.type === 'export-files-to-seven-bridges') { | ||
// export to seven bridges button is pending if a pfb export job is running | ||
// and it's an export to seven bridges job. | ||
return this.isPFBRunning() | ||
|
@@ -553,7 +719,25 @@ class ExplorerButtonGroup extends React.Component { | |
} else if (buttonConfig.type === 'manifest' && this.state.manifestEntryCount > 0) { | ||
buttonTitle = `${buttonConfig.title} (${humanizeNumber(this.state.manifestEntryCount)})`; | ||
} | ||
const btnTooltipText = (this.props.isLocked) ? 'You only have access to summary data' : buttonConfig.tooltipText; | ||
|
||
let tooltipEnabled = false; | ||
let btnTooltipText = ''; | ||
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. nitpicking: seems you can set the default value for these 2 the same from L728-729 and get rid of the |
||
|
||
// If limited file PFB export is enabled, PFB export buttons will be disabled | ||
// if the user selects multiple files that are on different nodes in the graph. | ||
// (See https://github.com/uc-cdis/data-portal/pull/729). | ||
// If the user has selected multiple files on different nodes, display a | ||
// tooltip explaining that the user can only export files of the same type. | ||
const isFilePFBButton = buttonConfig.type === 'export-files' || buttonConfig.type === 'export-files-to-pfb' || buttonConfig.type === 'export-files-to-seven-bridges'; | ||
if (this.props.buttonConfig.enableLimitedFilePFBExport | ||
&& isFilePFBButton | ||
&& this.state.sourceNodesInCohort.length > 1) { | ||
tooltipEnabled = true; | ||
btnTooltipText = 'Currently you cannot export files with different Data Types. Please choose a single Data Type from the Data Type filter on the left.'; | ||
} else { | ||
tooltipEnabled = buttonConfig.tooltipText ? !this.isButtonEnabled(buttonConfig) : false; | ||
btnTooltipText = (this.props.isLocked) ? 'You only have access to summary data' : buttonConfig.tooltipText; | ||
} | ||
|
||
return ( | ||
<Button | ||
|
@@ -565,7 +749,7 @@ class ExplorerButtonGroup extends React.Component { | |
className='explorer-button-group__download-button' | ||
buttonType='primary' | ||
enabled={this.isButtonEnabled(buttonConfig)} | ||
tooltipEnabled={buttonConfig.tooltipText ? !this.isButtonEnabled(buttonConfig) : false} | ||
tooltipEnabled={tooltipEnabled} | ||
tooltipText={btnTooltipText} | ||
isPending={this.isButtonPending(buttonConfig)} | ||
/> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we don't check for
isPFBRunning()
in here (also inexport-to-pfb
)?and there are so many trios of
this.state.exportingToTerra || this.state.exportingToSevenBridges || this.isPFBRunning()
, maybe we should consolidate them 🤔There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure why and I was scared to change it 😆. But I can't imagine how adding that extra check for
isPFBRunning()
would break anything, so I'll consolidate the check forthis.state.exportingToTerra || this.state.exportingToSevenBridges || this.isPFBRunning()
into a single function.