-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #458 from biomage-ltd/rework-project-details
[BIOMAGE-1273] Refactor ProjectDetails component
- Loading branch information
Showing
27 changed files
with
915 additions
and
35,828 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/__test__/redux/actions/projects/deleteMetadataTrack.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/__test__/redux/actions/projects/updateMetadataTrack.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* eslint-disable import/no-unresolved */ | ||
import React, { useEffect, useState } from 'react'; | ||
import _ from 'lodash'; | ||
import { | ||
Menu, Tooltip, Dropdown, Button, | ||
} from 'antd'; | ||
|
||
import { useSelector, useDispatch } from 'react-redux'; | ||
import PropTypes from 'prop-types'; | ||
import { saveAs } from 'file-saver'; | ||
import downloadTypes from 'utils/data-management/downloadTypes'; | ||
import { getFromApiExpectOK } from 'utils/getDataExpectOK'; | ||
import pushNotificationMessage from 'utils/pushNotificationMessage'; | ||
import endUserMessages from 'utils/endUserMessages'; | ||
import pipelineStatus from '../../utils/pipelineStatusValues'; | ||
import { exportQCParameters, filterQCParameters } from '../../utils/data-management/exportQCParameters'; | ||
import { loadBackendStatus } from '../../redux/actions/backendStatus/index'; | ||
|
||
const DownloadData = (props) => { | ||
const { | ||
activeProjectUuid, | ||
} = props; | ||
const dispatch = useDispatch(); | ||
const activeProject = useSelector((state) => state.projects[activeProjectUuid]); | ||
const experimentSettings = useSelector((state) => state.experimentSettings); | ||
const backendStatus = useSelector((state) => state.backendStatus); | ||
const samples = useSelector((state) => state.samples); | ||
const projects = useSelector((state) => state.projects); | ||
const [qcHasRun, setQcHasRun] = useState(false); | ||
const [gem2sHasRun, setGem2sHasRun] = useState(false); | ||
const [allSamplesAnalysed, setAllSamplesAnalysed] = useState(false); | ||
// Change if we have more than one experiment per project | ||
const experimentId = activeProject?.experiments[0]; | ||
|
||
useEffect(() => { | ||
if (experimentId && !backendStatus[experimentId]) { | ||
dispatch(loadBackendStatus(experimentId)); | ||
} | ||
}, [experimentId]); | ||
|
||
useEffect(() => { | ||
setQcHasRun(experimentId | ||
&& (backendStatus[experimentId]?.status.pipeline?.status === pipelineStatus.SUCCEEDED)); | ||
setGem2sHasRun(experimentId | ||
&& (backendStatus[experimentId]?.status?.gem2s?.status === pipelineStatus.SUCCEEDED)); | ||
}, [backendStatus]); | ||
|
||
useEffect(() => { | ||
setAllSamplesAnalysed(getAllSamplesAnalysed()); | ||
}, [activeProject, experimentSettings]); | ||
|
||
const getAllSamplesAnalysed = () => { | ||
// Returns true only if there is at least one sample in the currently active | ||
// project AND all samples in the project have been analysed. | ||
if (!activeProject?.samples?.length) { | ||
return false; | ||
} | ||
const steps = Object.values(_.omit(experimentSettings?.processing, ['meta'])); | ||
return steps.length > 0 | ||
// eslint-disable-next-line no-prototype-builtins | ||
&& activeProject?.samples?.every((s) => steps[0].hasOwnProperty(s)); | ||
}; | ||
|
||
const downloadExperimentData = async (type) => { | ||
try { | ||
if (!experimentId) throw new Error('No experimentId specified'); | ||
if (!downloadTypes.has(type)) throw new Error('Invalid download type'); | ||
|
||
const { signedUrl } = await getFromApiExpectOK(`/v1/experiments/${experimentId}/download/${type}`); | ||
const link = document.createElement('a'); | ||
link.style.display = 'none'; | ||
link.href = signedUrl; | ||
|
||
document.body.appendChild(link); | ||
link.click(); | ||
|
||
setTimeout(() => { | ||
URL.revokeObjectURL(link.href); | ||
link.parentNode.removeChild(link); | ||
}, 0); | ||
} catch (e) { | ||
pushNotificationMessage('error', endUserMessages.ERROR_DOWNLOADING_DATA); | ||
} | ||
}; | ||
|
||
return ( | ||
<Dropdown | ||
overlay={() => ( | ||
<Menu> | ||
<Menu.Item | ||
key='download-raw-seurat' | ||
disabled={!gem2sHasRun} | ||
onClick={() => { | ||
downloadExperimentData('raw_seurat_object'); | ||
}} | ||
> | ||
<Tooltip | ||
title={ | ||
gem2sHasRun | ||
? 'Samples have been merged' | ||
: 'Launch analysis to merge samples' | ||
} | ||
placement='left' | ||
> | ||
Raw Seurat object (.rds) | ||
</Tooltip> | ||
</Menu.Item> | ||
<Menu.Item | ||
key='download-processed-seurat' | ||
disabled={ | ||
!qcHasRun | ||
} | ||
onClick={() => { | ||
// Change if we have more than one experiment per project | ||
downloadExperimentData('processed_seurat_object'); | ||
}} | ||
> | ||
<Tooltip | ||
title={ | ||
qcHasRun | ||
? 'With Data Processing filters and settings applied' | ||
: 'Launch analysis to process data' | ||
} | ||
placement='left' | ||
> | ||
Processed Seurat object (.rds) | ||
</Tooltip> | ||
</Menu.Item> | ||
<Menu.Item | ||
disabled={!allSamplesAnalysed} | ||
key='download-processing-settings' | ||
onClick={() => { | ||
const config = _.omit(experimentSettings.processing, ['meta']); | ||
const filteredConfig = filterQCParameters(config, activeProject.samples, samples); | ||
const blob = exportQCParameters(filteredConfig); | ||
saveAs(blob, `${activeProjectUuid.split('-')[0]}_settings.txt`); | ||
}} | ||
> | ||
{ | ||
allSamplesAnalysed | ||
? 'Data Processing settings (.txt)' | ||
: ( | ||
<Tooltip title='One or more of your samples has yet to be analysed' placement='left'> | ||
Data Processing settings (.txt) | ||
</Tooltip> | ||
) | ||
} | ||
</Menu.Item> | ||
</Menu> | ||
)} | ||
trigger={['click']} | ||
placement='bottomRight' | ||
disabled={ | ||
projects.ids.length === 0 | ||
|| activeProject?.samples?.length === 0 | ||
} | ||
> | ||
<Button> | ||
Download | ||
</Button> | ||
</Dropdown> | ||
|
||
); | ||
}; | ||
|
||
DownloadData.propTypes = { | ||
activeProjectUuid: PropTypes.string.isRequired, | ||
}; | ||
export default React.memo(DownloadData); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React from 'react'; | ||
import { Space, Input } from 'antd'; | ||
import { useDispatch } from 'react-redux'; | ||
import PropTypes from 'prop-types'; | ||
import { rules } from '../../utils/validateInputs'; | ||
import EditableField from '../EditableField'; | ||
import MetadataEditor from './MetadataEditor'; | ||
import { DEFAULT_NA } from '../../redux/reducers/projects/initialState'; | ||
|
||
import { | ||
updateMetadataTrack, | ||
} from '../../redux/actions/projects'; | ||
|
||
const MetadataColumn = (props) => { | ||
const dispatch = useDispatch(); | ||
const { | ||
name, validateInput, setCells, deleteMetadataColumn, key, activeProjectUuid, | ||
} = props; | ||
const metadataNameValidation = [ | ||
rules.MIN_1_CHAR, | ||
rules.ALPHANUM_SPACE, | ||
rules.START_WITH_ALPHABET, | ||
rules.UNIQUE_NAME_CASE_INSENSITIVE, | ||
]; | ||
return ( | ||
<Space> | ||
<EditableField | ||
deleteEnabled | ||
onDelete={(e, currentName) => deleteMetadataColumn(currentName)} | ||
onAfterSubmit={(newName) => dispatch( | ||
updateMetadataTrack(name, newName, activeProjectUuid), | ||
)} | ||
value={name} | ||
validationFunc={ | ||
(newName) => validateInput(newName, metadataNameValidation) | ||
} | ||
/> | ||
<MetadataEditor | ||
onReplaceEmpty={(value) => setCells(value, key, 'REPLACE_EMPTY')} | ||
onReplaceAll={(value) => setCells(value, key, 'REPLACE_ALL')} | ||
onClearAll={() => setCells(DEFAULT_NA, key, 'CLEAR_ALL')} | ||
massEdit | ||
> | ||
<Input /> | ||
</MetadataEditor> | ||
</Space> | ||
); | ||
}; | ||
MetadataColumn.propTypes = { | ||
name: PropTypes.string.isRequired, | ||
validateInput: PropTypes.func.isRequired, | ||
setCells: PropTypes.func.isRequired, | ||
deleteMetadataColumn: PropTypes.func.isRequired, | ||
key: PropTypes.string.isRequired, | ||
activeProjectUuid: PropTypes.string.isRequired, | ||
}; | ||
export default MetadataColumn; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.