From 30485906273efae95b4fb2ca4c117e12c0cc938a Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 28 Jun 2022 11:58:29 -0300 Subject: [PATCH 01/55] Add metadataKeys and handle some of the project actions in the experiment slice instead --- src/redux/actions/projects/deleteProject.js | 16 +++++++++- .../reducers/experiments/experimentsCreate.js | 1 + .../reducers/experiments/experimentsDelete.js | 4 +++ .../experiments/experimentsMetadataCreate.js | 16 ++++++++++ .../experiments/experimentsMetadataDelete.js | 15 ++++++++++ .../experiments/experimentsMetadataUpdate.js | 16 ++++++++++ .../reducers/experiments/experimentsSaving.js | 2 +- .../experiments/experimentsSetActive.js | 12 ++++++++ src/redux/reducers/experiments/index.js | 30 ++++++++++++++++--- .../reducers/experiments/initialState.js | 1 + 10 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/redux/reducers/experiments/experimentsMetadataCreate.js create mode 100644 src/redux/reducers/experiments/experimentsMetadataDelete.js create mode 100644 src/redux/reducers/experiments/experimentsMetadataUpdate.js create mode 100644 src/redux/reducers/experiments/experimentsSetActive.js diff --git a/src/redux/actions/projects/deleteProject.js b/src/redux/actions/projects/deleteProject.js index cabdad8ddd..42ca345152 100644 --- a/src/redux/actions/projects/deleteProject.js +++ b/src/redux/actions/projects/deleteProject.js @@ -11,7 +11,7 @@ import { } from 'redux/actionTypes/projects'; import { - EXPERIMENTS_DELETED, + EXPERIMENTS_DELETED, EXPERIMENTS_ERROR, EXPERIMENTS_SAVING, } from 'redux/actionTypes/experiments'; import { SAMPLES_DELETE } from 'redux/actionTypes/samples'; @@ -30,6 +30,13 @@ const deleteProject = ( }, }); + dispatch({ + type: EXPERIMENTS_SAVING, + payload: { + message: endUserMessages.DELETING_PROJECT, + }, + }); + try { await fetchAPI( `/v2/experiments/${projectUuid}`, @@ -83,6 +90,13 @@ const deleteProject = ( error: errorMessage, }, }); + + dispatch({ + type: EXPERIMENTS_ERROR, + payload: { + message: endUserMessages.DELETING_PROJECT, + }, + }); } }; diff --git a/src/redux/reducers/experiments/experimentsCreate.js b/src/redux/reducers/experiments/experimentsCreate.js index ab959303db..94ff193dd5 100644 --- a/src/redux/reducers/experiments/experimentsCreate.js +++ b/src/redux/reducers/experiments/experimentsCreate.js @@ -22,6 +22,7 @@ const experimentCreate = (state, action) => { [newExperiment.id]: newExperiment, meta: { ...state.meta, + activeExperimentId: newExperiment.id, saving: false, }, }; diff --git a/src/redux/reducers/experiments/experimentsDelete.js b/src/redux/reducers/experiments/experimentsDelete.js index 0c05351b9d..c19909bf3a 100644 --- a/src/redux/reducers/experiments/experimentsDelete.js +++ b/src/redux/reducers/experiments/experimentsDelete.js @@ -14,6 +14,10 @@ const experimentsDelete = (state, action) => { return { ids: newIds, ...remainingExperiments, + meta: { + ...state.meta, + saving: false, + }, }; }; diff --git a/src/redux/reducers/experiments/experimentsMetadataCreate.js b/src/redux/reducers/experiments/experimentsMetadataCreate.js new file mode 100644 index 0000000000..4cc03d6fb0 --- /dev/null +++ b/src/redux/reducers/experiments/experimentsMetadataCreate.js @@ -0,0 +1,16 @@ +const experimentsMetadataCreate = (state, action) => { + const { key, projectUuid } = action.payload; + + return { + ...state, + [projectUuid]: { + ...state[projectUuid], + metadataKeys: [ + ...state[projectUuid].metadataKeys, + key, + ], + }, + }; +}; + +export default experimentsMetadataCreate; diff --git a/src/redux/reducers/experiments/experimentsMetadataDelete.js b/src/redux/reducers/experiments/experimentsMetadataDelete.js new file mode 100644 index 0000000000..c3629e810f --- /dev/null +++ b/src/redux/reducers/experiments/experimentsMetadataDelete.js @@ -0,0 +1,15 @@ +const projectsMetadataDelete = (state, action) => { + const { key, projectUuid } = action.payload; + + return { + ...state, + [projectUuid]: { + ...state[projectUuid], + metadataKeys: [ + ...state[projectUuid].metadataKeys.filter((value) => value !== key), + ], + }, + }; +}; + +export default projectsMetadataDelete; diff --git a/src/redux/reducers/experiments/experimentsMetadataUpdate.js b/src/redux/reducers/experiments/experimentsMetadataUpdate.js new file mode 100644 index 0000000000..c18d82b881 --- /dev/null +++ b/src/redux/reducers/experiments/experimentsMetadataUpdate.js @@ -0,0 +1,16 @@ +const projectsMetadataUpdate = (state, action) => { + const { oldKey, newKey, projectUuid } = action.payload; + + return { + ...state, + [projectUuid]: { + ...state[projectUuid], + metadataKeys: [ + ...state[projectUuid].metadataKeys.filter((value) => value !== oldKey), + newKey, + ], + }, + }; +}; + +export default projectsMetadataUpdate; diff --git a/src/redux/reducers/experiments/experimentsSaving.js b/src/redux/reducers/experiments/experimentsSaving.js index 008a537af3..bffac2363b 100644 --- a/src/redux/reducers/experiments/experimentsSaving.js +++ b/src/redux/reducers/experiments/experimentsSaving.js @@ -2,7 +2,7 @@ const experimentSaving = (state) => ({ ...state, meta: { ...state.meta, - saving: false, + saving: true, error: false, }, }); diff --git a/src/redux/reducers/experiments/experimentsSetActive.js b/src/redux/reducers/experiments/experimentsSetActive.js new file mode 100644 index 0000000000..83dfae6932 --- /dev/null +++ b/src/redux/reducers/experiments/experimentsSetActive.js @@ -0,0 +1,12 @@ +const projectsSetActive = (state, action) => { + const { projectUuid } = action.payload; + return { + ...state, + meta: { + ...state.meta, + activeExperimentId: projectUuid, + }, + }; +}; + +export default projectsSetActive; diff --git a/src/redux/reducers/experiments/index.js b/src/redux/reducers/experiments/index.js index 0db1fd671c..6fcdc79484 100644 --- a/src/redux/reducers/experiments/index.js +++ b/src/redux/reducers/experiments/index.js @@ -1,4 +1,4 @@ -import initialState from './initialState'; +import initialState from 'redux/reducers/experiments/initialState'; import { EXPERIMENTS_CREATED, EXPERIMENTS_UPDATED, @@ -7,19 +7,25 @@ import { EXPERIMENTS_ERROR, EXPERIMENTS_SAVING, EXPERIMENTS_DELETED, -} from '../../actionTypes/experiments'; - +} from 'redux/actionTypes/experiments'; +import { + PROJECTS_METADATA_CREATE, PROJECTS_METADATA_DELETE, PROJECTS_METADATA_UPDATE, PROJECTS_SET_ACTIVE, +} from 'redux/actionTypes/projects'; import { SAMPLES_CREATE, SAMPLES_DELETE, -} from '../../actionTypes/samples'; +} from 'redux/actionTypes/samples'; import experimentsCreate from './experimentsCreate'; import experimentsUpdate from './experimentsUpdate'; import experimentsDelete from './experimentsDelete'; +import experimentsSetActive from './experimentsSetActive'; import experimentsLoading from './experimentsLoading'; import experimentsLoaded from './experimentsLoaded'; import experimentsError from './experimentsError'; import experimentsSaving from './experimentsSaving'; +import experimentsMetadataCreate from './experimentsMetadataCreate'; +import experimentsMetadataUpdate from './experimentsMetadataUpdate'; +import experimentsMetadataDelete from './experimentsMetadataDelete'; import samplesCreate from './samplesCreate'; import samplesDelete from './samplesDelete'; @@ -38,6 +44,10 @@ const experimentsReducer = (state = initialState, action) => { return experimentsDelete(state, action); } + case PROJECTS_SET_ACTIVE: { + return experimentsSetActive(state, action); + } + case EXPERIMENTS_LOADING: { return experimentsLoading(state, action); } @@ -62,6 +72,18 @@ const experimentsReducer = (state = initialState, action) => { return samplesDelete(state, action); } + case PROJECTS_METADATA_CREATE: { + return experimentsMetadataCreate(state, action); + } + + case PROJECTS_METADATA_UPDATE: { + return experimentsMetadataUpdate(state, action); + } + + case PROJECTS_METADATA_DELETE: { + return experimentsMetadataDelete(state, action); + } + default: { return state; } diff --git a/src/redux/reducers/experiments/initialState.js b/src/redux/reducers/experiments/initialState.js index fd85388cd1..0a37841aeb 100644 --- a/src/redux/reducers/experiments/initialState.js +++ b/src/redux/reducers/experiments/initialState.js @@ -7,6 +7,7 @@ const experimentTemplate = { notifyByEmail: true, meta: { organism: null, type: '10x' }, sampleIds: [], + metadataKeys: [], }; const initialState = { From 1e45d34469700987642000275c7e6ba080cfe489 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 28 Jun 2022 12:23:47 -0300 Subject: [PATCH 02/55] Make LaunchAnalysisButton stop using project --- .../data-management/LaunchAnalysisButton.jsx | 30 +++++++++---------- .../calculateGem2sRerunStatus.js | 5 ++-- .../generateGem2sParamsHash.js | 10 +++---- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/components/data-management/LaunchAnalysisButton.jsx b/src/components/data-management/LaunchAnalysisButton.jsx index 7f07dc1e2f..1bd58537c5 100644 --- a/src/components/data-management/LaunchAnalysisButton.jsx +++ b/src/components/data-management/LaunchAnalysisButton.jsx @@ -40,10 +40,8 @@ const LaunchAnalysisButton = () => { const samples = useSelector((state) => state.samples); const backendStatus = useSelector((state) => state.backendStatus); - const projects = useSelector((state) => state.projects); - const { activeProjectUuid } = projects.meta; - const activeProject = projects[activeProjectUuid]; - const experimentId = activeProject.experiments[0]; + const { activeExperimentId } = experiments.meta; + const activeExperiment = experiments[activeExperimentId]; const [gem2sRerunStatus, setGem2sRerunStatus] = useState( { rerun: true, paramsHash: null, reasons: [] }, @@ -51,34 +49,34 @@ const LaunchAnalysisButton = () => { const launchAnalysis = () => { if (gem2sRerunStatus.rerun) { - dispatch(runGem2s(experimentId, gem2sRerunStatus.paramsHash)); + dispatch(runGem2s(activeExperimentId, gem2sRerunStatus.paramsHash)); } - navigateTo(modules.DATA_PROCESSING, { experimentId }); + navigateTo(modules.DATA_PROCESSING, { experimentId: activeExperimentId }); }; useEffect(() => { // The value of backend status is null for new projects that have never run - const gem2sBackendStatus = backendStatus[experimentId]?.status?.gem2s; + const gem2sBackendStatus = backendStatus[activeExperimentId]?.status?.gem2s; if ( !gem2sBackendStatus - || !experiments[experimentId]?.sampleIds?.length > 0 + || !experiments[activeExperimentId]?.sampleIds?.length > 0 ) return; const gem2sStatus = calculateGem2sRerunStatus( - gem2sBackendStatus, activeProject, samples, experiments[experimentId], + gem2sBackendStatus, activeExperiment, samples, ); setGem2sRerunStatus(gem2sStatus); - }, [backendStatus, activeProjectUuid, samples, activeProject]); + }, [backendStatus, activeExperimentId, samples, activeExperiment]); const canLaunchAnalysis = useCallback(() => { - if (activeProject.samples.length === 0) return false; + if (activeExperiment.sampleIds.length === 0) return false; // Check that samples is loaded - const testSampleUuid = activeProject.samples[0]; + const testSampleUuid = activeExperiment.sampleIds[0]; if (samples[testSampleUuid] === undefined) return false; - const metadataKeysAvailable = activeProject.metadataKeys.length; + const metadataKeysAvailable = activeExperiment.metadataKeys.length; const allSampleFilesUploaded = (sample) => { // Check if all files for a given tech has been uploaded @@ -111,7 +109,7 @@ const LaunchAnalysisButton = () => { .every((value) => value.length > 0); }; - const canLaunch = activeProject.samples.every((sampleUuid) => { + const canLaunch = activeExperiment.sampleIds.every((sampleUuid) => { if (!samples[sampleUuid]) return false; const checkedSample = samples[sampleUuid]; @@ -119,12 +117,12 @@ const LaunchAnalysisButton = () => { && allSampleMetadataInserted(checkedSample); }); return canLaunch; - }, [samples, activeProject.samples, activeProject.metadataKeys]); + }, [samples, activeExperiment.sampleIds, activeExperiment.metadataKeys]); const renderLaunchButton = () => { const buttonText = !gem2sRerunStatus.rerun ? 'Go to Data Processing' : 'Process project'; - if (!backendStatus[experimentId] || backendStatus[experimentId]?.loading) { + if (!backendStatus[activeExperimentId] || backendStatus[activeExperimentId]?.loading) { return ; } diff --git a/src/utils/data-management/calculateGem2sRerunStatus.js b/src/utils/data-management/calculateGem2sRerunStatus.js index a0d1df795f..cd95d26c07 100644 --- a/src/utils/data-management/calculateGem2sRerunStatus.js +++ b/src/utils/data-management/calculateGem2sRerunStatus.js @@ -2,15 +2,14 @@ import pipelineStatus from 'utils/pipelineStatusValues'; import generateGem2sParamsHash from './generateGem2sParamsHash'; const calculateGem2sRerunStatus = ( - gem2sBackendStatus, activeProject, samples, experiment, + gem2sBackendStatus, activeExperiment, samples, ) => { const gem2sStatus = gem2sBackendStatus?.status; const existingParamsHash = gem2sBackendStatus?.paramsHash; const newParamsHash = generateGem2sParamsHash( - activeProject, + activeExperiment, samples, - experiment, ); const projectHashEqual = existingParamsHash === newParamsHash; diff --git a/src/utils/data-management/generateGem2sParamsHash.js b/src/utils/data-management/generateGem2sParamsHash.js index 1c8f494b24..32dcee5b9b 100644 --- a/src/utils/data-management/generateGem2sParamsHash.js +++ b/src/utils/data-management/generateGem2sParamsHash.js @@ -2,13 +2,13 @@ import objectHash from 'object-hash'; import { DEFAULT_NA } from 'redux/reducers/projects/initialState'; -const generateGem2sParamsHash = (project, samples, experiment) => { - if (!project || !samples || !experiment) { +const generateGem2sParamsHash = (experiment, samples) => { + if (!experiment || !samples) { return false; } const projectSamples = Object.entries(samples) .sort() - .filter(([key]) => project?.samples?.includes(key)); + .filter(([key]) => experiment?.sampleIds?.includes(key)); const existingSampleIds = projectSamples.map(([, sample]) => sample.uuid); // Different sample order should not change the hash. @@ -21,8 +21,8 @@ const generateGem2sParamsHash = (project, samples, experiment) => { sampleNames: orderInvariantSampleIds.map((sampleId) => samples[sampleId].name), }; - if (project.metadataKeys.length) { - const orderInvariantProjectMetadataKeys = [...project.metadataKeys].sort(); + if (experiment.metadataKeys.length) { + const orderInvariantProjectMetadataKeys = [...experiment.metadataKeys].sort(); hashParams.metadata = orderInvariantProjectMetadataKeys.reduce((acc, key) => { // Make sure the key does not contain '-' as it will cause failure in GEM2S From 22241a9be9943e56a0427091f9cf6b698f267e50 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 29 Jun 2022 08:41:38 -0300 Subject: [PATCH 03/55] Update comment --- src/components/data-management/LaunchAnalysisButton.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/data-management/LaunchAnalysisButton.jsx b/src/components/data-management/LaunchAnalysisButton.jsx index 1bd58537c5..f5858afa9a 100644 --- a/src/components/data-management/LaunchAnalysisButton.jsx +++ b/src/components/data-management/LaunchAnalysisButton.jsx @@ -55,7 +55,7 @@ const LaunchAnalysisButton = () => { }; useEffect(() => { - // The value of backend status is null for new projects that have never run + // The value of backend status is null for new experiments that have never run const gem2sBackendStatus = backendStatus[activeExperimentId]?.status?.gem2s; if ( From fb4953bcdfaf4d5075c0b1ea102a0e0458c22758 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 29 Jun 2022 08:42:07 -0300 Subject: [PATCH 04/55] Cleanup --- src/components/data-management/LaunchAnalysisButton.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/data-management/LaunchAnalysisButton.jsx b/src/components/data-management/LaunchAnalysisButton.jsx index f5858afa9a..54cb226aa9 100644 --- a/src/components/data-management/LaunchAnalysisButton.jsx +++ b/src/components/data-management/LaunchAnalysisButton.jsx @@ -140,7 +140,6 @@ const LaunchAnalysisButton = () => { ); } - // Popconfirm if (gem2sRerunStatus.rerun) { return ( Date: Wed, 29 Jun 2022 08:53:02 -0300 Subject: [PATCH 05/55] Change from using active project to using active experiemnt in contentwrapper --- src/components/ContentWrapper.jsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/ContentWrapper.jsx b/src/components/ContentWrapper.jsx index a28be5033d..15a1e13c9f 100644 --- a/src/components/ContentWrapper.jsx +++ b/src/components/ContentWrapper.jsx @@ -51,26 +51,23 @@ const ContentWrapper = (props) => { const { navigateTo, currentModule } = useAppRouter(); const currentExperimentIdRef = useRef(routeExperimentId); - const activeProjectUuid = useSelector((state) => state?.projects?.meta?.activeProjectUuid); - const activeProjectExperimentID = useSelector((state) => ( - state?.projects[activeProjectUuid]?.experiments[0])); + const activeExperimentId = useSelector((state) => state?.experiments?.meta?.activeExperimentId); + const activeExperiment = useSelector((state) => state.experiments[activeExperimentId]); - const activeProject = useSelector((state) => state.projects[activeProjectUuid]); const samples = useSelector((state) => state.samples); - // Use the project's experiment ID in data management useEffect(() => { - if (!activeProjectExperimentID && !routeExperimentId) return; + if (!activeExperimentId && !routeExperimentId) return; if (currentModule === modules.DATA_MANAGEMENT) { - currentExperimentIdRef.current = activeProjectExperimentID; + currentExperimentIdRef.current = activeExperimentId; return; } if (currentExperimentIdRef.current === routeExperimentId) return; currentExperimentIdRef.current = routeExperimentId; - }, [currentModule, activeProjectExperimentID, routeExperimentId]); + }, [currentModule, activeExperimentId, routeExperimentId]); const currentExperimentId = currentExperimentIdRef.current; const experiment = useSelector((state) => state?.experiments[currentExperimentId]); @@ -132,11 +129,11 @@ const ContentWrapper = (props) => { useEffect(() => { const gem2sStatus = calculateGem2sRerunStatus( - gem2sBackendStatus, activeProject, samples, experiment, + gem2sBackendStatus, activeExperiment, samples, experiment, ); setGem2sRerunStatus(gem2sStatus); - }, [gem2sBackendStatus, activeProject, samples, experiment]); + }, [gem2sBackendStatus, activeExperiment, samples, experiment]); useEffect(() => { Auth.currentAuthenticatedUser() From e1de202ea564a1b27eec1f9fc1f54ccbd8d7c2be Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 29 Jun 2022 08:54:35 -0300 Subject: [PATCH 06/55] Update NotifyByEmail --- src/components/NotifyByEmail.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/NotifyByEmail.jsx b/src/components/NotifyByEmail.jsx index 4dadf003c7..01a73dcea4 100644 --- a/src/components/NotifyByEmail.jsx +++ b/src/components/NotifyByEmail.jsx @@ -13,19 +13,19 @@ const NotifyByEmail = (props) => { const changeEmailNotification = (value) => { dispatch(updateExperiment(experimentId, { notifyByEmail: value })); }; - const { activeProjectUuid } = useSelector((state) => state?.projects?.meta) || false; + const { activeExperimentId } = useSelector((state) => state?.experiments?.meta) || false; useEffect(() => { - if (!activeProjectUuid) { + if (!activeExperimentId) { dispatch(loadProjects()); } }, []); useEffect(() => { - if (!experiment && activeProjectUuid) { - dispatch(loadExperiments(activeProjectUuid)); + if (!experiment && activeExperimentId) { + dispatch(loadExperiments(activeExperimentId)); } - }, [activeProjectUuid]); + }, [activeExperimentId]); return ( From 9fef7a64d8caacdbb44d9ddb79b3cb1d92515c42 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 29 Jun 2022 10:25:11 -0300 Subject: [PATCH 07/55] Some fixes --- src/components/ContentWrapper.jsx | 2 + .../data-management/LaunchAnalysisButton.jsx | 7 ++- .../actions/experiments/loadExperiments.js | 30 +++++++----- src/redux/actions/projects/loadProjects.js | 8 +++ .../reducers/experiments/experimentsLoaded.js | 49 +++++++------------ .../generateGem2sParamsHash.js | 5 +- 6 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/components/ContentWrapper.jsx b/src/components/ContentWrapper.jsx index 15a1e13c9f..0730db2cfe 100644 --- a/src/components/ContentWrapper.jsx +++ b/src/components/ContentWrapper.jsx @@ -128,6 +128,8 @@ const ContentWrapper = (props) => { const [gem2sRerunStatus, setGem2sRerunStatus] = useState(null); useEffect(() => { + if (!activeExperiment) return; + const gem2sStatus = calculateGem2sRerunStatus( gem2sBackendStatus, activeExperiment, samples, experiment, ); diff --git a/src/components/data-management/LaunchAnalysisButton.jsx b/src/components/data-management/LaunchAnalysisButton.jsx index 54cb226aa9..6792800058 100644 --- a/src/components/data-management/LaunchAnalysisButton.jsx +++ b/src/components/data-management/LaunchAnalysisButton.jsx @@ -117,11 +117,16 @@ const LaunchAnalysisButton = () => { && allSampleMetadataInserted(checkedSample); }); return canLaunch; - }, [samples, activeExperiment.sampleIds, activeExperiment.metadataKeys]); + }, [samples, activeExperiment?.sampleIds, activeExperiment?.metadataKeys]); const renderLaunchButton = () => { const buttonText = !gem2sRerunStatus.rerun ? 'Go to Data Processing' : 'Process project'; + console.log('activeExperimentIdDebug'); + console.log(activeExperimentId); + + console.log('backendStatusDebug'); + console.log(backendStatus[activeExperimentId]); if (!backendStatus[activeExperimentId] || backendStatus[activeExperimentId]?.loading) { return ; } diff --git a/src/redux/actions/experiments/loadExperiments.js b/src/redux/actions/experiments/loadExperiments.js index e6944a519f..44bba745f9 100644 --- a/src/redux/actions/experiments/loadExperiments.js +++ b/src/redux/actions/experiments/loadExperiments.js @@ -2,9 +2,9 @@ import fetchAPI from 'utils/http/fetchAPI'; import handleError from 'utils/http/handleError'; import { - EXPERIMENTS_LOADED, + // EXPERIMENTS_LOADED, EXPERIMENTS_ERROR, - EXPERIMENTS_LOADING, + // EXPERIMENTS_LOADING, } from 'redux/actionTypes/experiments'; const toApiV1 = (experimentV2) => { @@ -17,6 +17,7 @@ const toApiV1 = (experimentV2) => { processingConfig, createdAt, pipelines, + metadataKeys, } = experimentV2; const experimentV1 = { @@ -28,6 +29,7 @@ const toApiV1 = (experimentV2) => { createdDate: createdAt, notifyByEmail, sampleIds: samplesOrder, + metadataKeys, meta: { // This is always 10x and organism so we can just generate them here organism: null, @@ -43,21 +45,27 @@ const toApiV1 = (experimentV2) => { const loadExperiments = ( projectUuid, ) => async (dispatch) => { - dispatch({ - type: EXPERIMENTS_LOADING, - }); + // dispatch({ + // type: EXPERIMENTS_LOADING, + // }); try { let data = await fetchAPI(`/v2/experiments/${projectUuid}`); + console.log('dataDebgu1'); + console.log(data); + data = [toApiV1(data)]; - dispatch({ - type: EXPERIMENTS_LOADED, - payload: { - experiments: data, - }, - }); + console.log('dataDebug'); + console.log(data); + + // dispatch({ + // type: EXPERIMENTS_LOADED, + // payload: { + // experiments: data, + // }, + // }); } catch (e) { const errorMessage = handleError(e); diff --git a/src/redux/actions/projects/loadProjects.js b/src/redux/actions/projects/loadProjects.js index fce64b2e30..a4067a8b6d 100644 --- a/src/redux/actions/projects/loadProjects.js +++ b/src/redux/actions/projects/loadProjects.js @@ -2,6 +2,7 @@ import fetchAPI from 'utils/http/fetchAPI'; import handleError from 'utils/http/handleError'; import endUserMessages from 'utils/endUserMessages'; import { PROJECTS_ERROR, PROJECTS_LOADED, PROJECTS_LOADING } from 'redux/actionTypes/projects'; +import { EXPERIMENTS_LOADED } from 'redux/actionTypes/experiments'; const toApiV1 = (experimentListV2) => { const projectsListV1 = experimentListV2.map((experimentData) => { @@ -37,6 +38,13 @@ const loadProjects = () => async (dispatch) => { try { let data = await fetchAPI('/v2/experiments'); + dispatch({ + type: EXPERIMENTS_LOADED, + payload: { + experiments: data, + }, + }); + data = toApiV1(data); const ids = data.map((project) => project.uuid); diff --git a/src/redux/reducers/experiments/experimentsLoaded.js b/src/redux/reducers/experiments/experimentsLoaded.js index 23bf4c1043..13337c5232 100644 --- a/src/redux/reducers/experiments/experimentsLoaded.js +++ b/src/redux/reducers/experiments/experimentsLoaded.js @@ -1,40 +1,27 @@ -const convertedToUIModel = (experiment) => ({ - ...experiment, - id: experiment.experimentId, - name: experiment.experimentName, - projectUuid: experiment.projectId, -}); +/* eslint-disable no-param-reassign */ +import produce from 'immer'; -const experimentsLoaded = (state, action) => { +const experimentsLoaded = produce((draft, action) => { const { experiments } = action.payload; - const newExperiments = experiments.reduce((acc, curr) => { - if (!acc.ids.includes(curr.experimentId)) acc.ids.push(curr.experimentId); + console.log('experimentsDebug'); + console.log(experiments); - const uiModelExp = convertedToUIModel(curr); + // const newActiveExperimentId = state.meta.activeExperimentId; - acc[curr.experimentId] = { - projectUuid: uiModelExp.projectUuid, - name: uiModelExp.name, - description: uiModelExp.description, - id: uiModelExp.id, - createdDate: uiModelExp.createdDate, - meta: uiModelExp.meta, - sampleIds: uiModelExp.sampleIds, - notifyByEmail: curr.notifyByEmail, - }; + // // If the current active experiment no longer exists, change it + // if (!Object.keys(state).includes(newActiveExperimentId)) { + // newActiveExperimentId = experiments[0]?.id; + // } + draft.meta.activeExperimentId = experiments[0].id; + draft.meta.loading = false; - return acc; - }, { ids: [...state.ids] }); + experiments.forEach((experiment) => { + // WIP, deal with this before emrging + experiment.sampleIds = experiment.samplesOrder; - return { - ...state, - ...newExperiments, - meta: { - ...state.meta, - loading: false, - }, - }; -}; + draft[experiment.id] = experiment; + }); +}); export default experimentsLoaded; diff --git a/src/utils/data-management/generateGem2sParamsHash.js b/src/utils/data-management/generateGem2sParamsHash.js index 32dcee5b9b..b0b79a7194 100644 --- a/src/utils/data-management/generateGem2sParamsHash.js +++ b/src/utils/data-management/generateGem2sParamsHash.js @@ -3,6 +3,7 @@ import objectHash from 'object-hash'; import { DEFAULT_NA } from 'redux/reducers/projects/initialState'; const generateGem2sParamsHash = (experiment, samples) => { + // WIP samples has meta, so check on that before merging if (!experiment || !samples) { return false; } @@ -15,8 +16,8 @@ const generateGem2sParamsHash = (experiment, samples) => { const orderInvariantSampleIds = [...existingSampleIds].sort(); const hashParams = { - organism: experiment.meta.organism, - input: { type: experiment.meta.type }, + organism: null, + input: { type: '10x' }, sampleIds: orderInvariantSampleIds, sampleNames: orderInvariantSampleIds.map((sampleId) => samples[sampleId].name), }; From 27a209a129dbd31ef7ca24630d4f550449d1adfa Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 29 Jun 2022 12:25:15 -0300 Subject: [PATCH 08/55] Update DownloadDataButton --- .../data-management/DownloadDataButton.jsx | 44 +++++++++---------- .../reducers/experiments/experimentsLoaded.js | 8 ++++ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/components/data-management/DownloadDataButton.jsx b/src/components/data-management/DownloadDataButton.jsx index ba5264f5dc..ab998407fa 100644 --- a/src/components/data-management/DownloadDataButton.jsx +++ b/src/components/data-management/DownloadDataButton.jsx @@ -21,56 +21,56 @@ import handleError from 'utils/http/handleError'; const DownloadDataButton = () => { const dispatch = useDispatch(); - const { activeProjectUuid } = useSelector((state) => state.projects.meta); const experimentSettings = useSelector((state) => state.experimentSettings); - const activeProject = useSelector((state) => state.projects[activeProjectUuid]); - // Change if we have more than one experiment per project - const experimentId = activeProject?.experiments[0]; + const experiments = useSelector((state) => state.experiments); + const { activeExperimentId } = experiments.meta; + const activeExperiment = experiments[activeExperimentId]; const { status: backendStatuses, loading: backendLoading, - } = useSelector(getBackendStatus(experimentId)); + } = useSelector(getBackendStatus(activeExperimentId)); 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); useEffect(() => { - if (experimentId && !backendLoading && !backendStatuses) { - dispatch(loadBackendStatus(experimentId)); + if (activeExperimentId && !backendLoading && !backendStatuses) { + dispatch(loadBackendStatus(activeExperimentId)); } - }, [experimentId]); + }, [activeExperimentId]); useEffect(() => { - setQcHasRun(experimentId - && (backendStatuses?.pipeline?.status === pipelineStatus.SUCCEEDED)); - setGem2sHasRun(experimentId - && (backendStatuses?.gem2s?.status === pipelineStatus.SUCCEEDED)); + setQcHasRun( + activeExperimentId && (backendStatuses?.pipeline?.status === pipelineStatus.SUCCEEDED), + ); + setGem2sHasRun( + activeExperimentId && (backendStatuses?.gem2s?.status === pipelineStatus.SUCCEEDED), + ); }, [backendStatuses]); useEffect(() => { setAllSamplesAnalysed(getAllSamplesAnalysed()); - }, [activeProject, experimentSettings]); + }, [activeExperiment, 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) { + if (!activeExperiment?.sampleIds?.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)); + && activeExperiment?.sampleIds?.every((s) => steps[0].hasOwnProperty(s)); }; const downloadExperimentData = async (type) => { try { - if (!experimentId) throw new Error('No experimentId specified'); + if (!activeExperimentId) throw new Error('No experimentId specified'); if (!downloadTypes.has(type)) throw new Error('Invalid download type'); - const signedUrl = await fetchAPI(`/v2/experiments/${experimentId}/download/${type}`); + const signedUrl = await fetchAPI(`/v2/experiments/${activeExperimentId}/download/${type}`); downloadFromUrl(signedUrl); } catch (e) { @@ -123,9 +123,9 @@ const DownloadDataButton = () => { key='download-processing-settings' onClick={() => { const config = _.omit(experimentSettings.processing, ['meta']); - const filteredConfig = filterQCParameters(config, activeProject.samples, samples); + const filteredConfig = filterQCParameters(config, activeExperiment.sampleIds, samples); const blob = exportQCParameters(filteredConfig); - saveAs(blob, `${activeProjectUuid.split('-')[0]}_settings.txt`); + saveAs(blob, `${activeExperimentId.split('-')[0]}_settings.txt`); }} > { @@ -143,8 +143,8 @@ const DownloadDataButton = () => { trigger={['click']} placement='bottomRight' disabled={ - projects.ids.length === 0 - || activeProject.samples.length === 0 + experiments.ids.length === 0 + || activeExperiment.sampleIds.length === 0 } >