add autosave

This commit is contained in:
bkmartinjr
2021-01-22 14:07:34 -08:00
parent 023976c992
commit e0a0b77c5b
4 changed files with 134 additions and 18 deletions
+79
View File
@@ -387,3 +387,82 @@ export const saveObsAnnotationsAction = () => async (dispatch, getState) => {
}); });
} }
}; };
export const saveGenesetsAction = () => async (dispatch, getState) => {
const state = getState();
const { config, genesets, annotations } = state;
// bail if gene sets not available, or in readonly mode.
const genesetsAreAvailable =
config?.parameters?.["annotations_genesets"] ?? false;
const genesetsReadonly =
config?.parameters?.["annotations_genesets_readonly"] ?? true;
if (!genesetsAreAvailable || genesetsReadonly) {
// our non-save was completed!
dispatch({
type: "autosave: genesets complete",
lastSavedGenesets: genesets,
});
}
/*
JSON data structure is an array of arrays, where the first
element is gene set name, remainder are the genes. Eg,
{
"genesets": [
[ "gs1", ["TNFRSF4","SUMO3","BRWD1"]],
[ "gs2", ["DSCR3", "BRWD1", "BACE2", "SIK1", "C21orf33", "ICOSLG", "SUMO3"]]
]
}
Order of gene sets and genes is significant
*/
const gsArr = [];
for (const [gsName, gsGenes] of genesets.genesets) {
gsArr.push([gsName, Array.from(gsGenes)]);
}
try {
const { dataCollectionNameIsReadOnly, dataCollectionName } = annotations;
const queryString =
!dataCollectionNameIsReadOnly && !!dataCollectionName
? `?annotation-collection-name=${encodeURIComponent(
dataCollectionName
)}`
: "";
const res = await fetch(
`${globals.API.prefix}${globals.API.version}genesets${queryString}`,
{
method: "PUT",
headers: new Headers({
Accept: "application/json",
"Content-Type": "application/json",
}),
body: JSON.stringify({
genesets: gsArr,
}),
credentials: "include",
}
);
if (res.ok) {
dispatch({
type: "autosave: genesets complete",
lastSavedGenesets: genesets,
});
} else {
dispatch({
type: "autosave: genesets error",
message: `HTTP error ${res.status} - ${res.statusText}`,
res,
});
}
} catch (error) {
dispatch({
type: "autosave: genesets error",
message: error.toString(),
error,
});
}
};
+15 -6
View File
@@ -52,14 +52,21 @@ async function userInfoFetch(dispatch) {
}); });
} }
async function genesetsFetch(dispatch) { async function genesetsFetch(dispatch, config) {
return fetchJson("genesets").then((response) => { if (config?.parameters?.["annotations_genesets"] ?? false) {
const genesets = response?.genesets ?? {}; fetchJson("genesets").then((response) => {
const genesets = response?.genesets ?? {};
dispatch({
type: "geneset: initial load",
init: genesets,
});
});
} else {
dispatch({ dispatch({
type: "geneset: initial load", type: "geneset: initial load",
init: genesets, init: [],
}); });
}) }
} }
function prefetchEmbeddings(annoMatrix) { function prefetchEmbeddings(annoMatrix) {
@@ -84,9 +91,10 @@ const doInitialDataLoad = () =>
schemaFetch(dispatch), schemaFetch(dispatch),
userColorsFetchAndLoad(dispatch), userColorsFetchAndLoad(dispatch),
userInfoFetch(dispatch), userInfoFetch(dispatch),
genesetsFetch(dispatch),
]); ]);
genesetsFetch(dispatch, config);
const baseDataUrl = `${globals.API.prefix}${globals.API.version}`; const baseDataUrl = `${globals.API.prefix}${globals.API.version}`;
const annoMatrix = new AnnoMatrixLoader(baseDataUrl, schema.schema); const annoMatrix = new AnnoMatrixLoader(baseDataUrl, schema.schema);
const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix); const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix);
@@ -253,6 +261,7 @@ export default {
annotationRenameLabelInCategory: annoActions.annotationRenameLabelInCategory, annotationRenameLabelInCategory: annoActions.annotationRenameLabelInCategory,
annotationLabelCurrentSelection: annoActions.annotationLabelCurrentSelection, annotationLabelCurrentSelection: annoActions.annotationLabelCurrentSelection,
saveObsAnnotationsAction: annoActions.saveObsAnnotationsAction, saveObsAnnotationsAction: annoActions.saveObsAnnotationsAction,
saveGenesetsAction: annoActions.saveGenesetsAction,
needToSaveObsAnnotations: annoActions.needToSaveObsAnnotations, needToSaveObsAnnotations: annoActions.needToSaveObsAnnotations,
layoutChoiceAction: embActions.layoutChoiceAction, layoutChoiceAction: embActions.layoutChoiceAction,
setCellSetFromSelection: selnActions.setCellSetFromSelection, setCellSetFromSelection: selnActions.setCellSetFromSelection,
@@ -17,6 +17,9 @@ import {
auth: state.config?.authentication, auth: state.config?.authentication,
userInfo: state.userInfo, userInfo: state.userInfo,
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false, writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
writableGenesetsEnabled: !(
state.config?.parameters?.["annotations_genesets_readonly"] ?? true
),
})) }))
class FilenameDialog extends React.Component { class FilenameDialog extends React.Component {
constructor(props) { constructor(props) {
@@ -101,7 +104,7 @@ class FilenameDialog extends React.Component {
} = this.props; } = this.props;
const { filenameText } = this.state; const { filenameText } = this.state;
return writableCategoriesEnabled && return (writableCategoriesEnabled || writableGenesetsEnabled) &&
annotations.promptForFilename && annotations.promptForFilename &&
!annotations.dataCollectionNameIsReadOnly && !annotations.dataCollectionNameIsReadOnly &&
!annotations.dataCollectionName && !annotations.dataCollectionName &&
+36 -11
View File
@@ -5,11 +5,18 @@ import FilenameDialog from "./filenameDialog";
@connect((state) => ({ @connect((state) => ({
annotations: state.annotations, annotations: state.annotations,
saveInProgress: state.autosave?.saveInProgress ?? false, obsAnnotationSaveInProgress:
state.autosave?.obsAnnotationSaveInProgress ?? false,
genesetSaveInProgress: state.autosave?.genesetSaveInProgress ?? false,
error: state.autosave?.error, error: state.autosave?.error,
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false, writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
writableGenesetsEnabled: !(
state.config?.parameters?.annotations_genesets_readonly ?? true
),
annoMatrix: state.annoMatrix, annoMatrix: state.annoMatrix,
genesets: state.genesets,
lastSavedAnnoMatrix: state.autosave?.lastSavedAnnoMatrix, lastSavedAnnoMatrix: state.autosave?.lastSavedAnnoMatrix,
lastSavedGenesets: state.autosave?.lastSavedGenesets,
})) }))
class Autosave extends React.Component { class Autosave extends React.Component {
constructor(props) { constructor(props) {
@@ -38,18 +45,40 @@ class Autosave extends React.Component {
} }
tick = () => { tick = () => {
const { dispatch, saveInProgress } = this.props; const {
if (this.needToSave() && !saveInProgress) { dispatch,
obsAnnotationSaveInProgress,
genesetSaveInProgress,
} = this.props;
if (!obsAnnotationSaveInProgress && this.needToSaveObsAnnotations()) {
dispatch(actions.saveObsAnnotationsAction()); dispatch(actions.saveObsAnnotationsAction());
} }
if (!genesetSaveInProgress && this.needToSaveGenesets()) {
dispatch(actions.saveGenesetsAction());
}
}; };
needToSave = () => { saveInProgress() {
/* return true if we need to save, false if we don't */ const { obsAnnotationSaveInProgress, genesetSaveInProgress } = this.props;
return obsAnnotationSaveInProgress || genesetSaveInProgress;
}
needToSaveObsAnnotations = () => {
/* return true if we need to save obs cell labels, false if we don't */
const { annoMatrix, lastSavedAnnoMatrix } = this.props; const { annoMatrix, lastSavedAnnoMatrix } = this.props;
return actions.needToSaveObsAnnotations(annoMatrix, lastSavedAnnoMatrix); return actions.needToSaveObsAnnotations(annoMatrix, lastSavedAnnoMatrix);
}; };
needToSaveGenesets = () => {
/* return true if we need to save gene ses, false if we do not */
const { genesets, lastSavedGenesets } = this.props;
return genesets.initialized && genesets !== lastSavedGenesets;
};
needToSave() {
return this.needToSaveGenesets() || this.needToSaveObsAnnotations();
}
statusMessage() { statusMessage() {
const { error } = this.props; const { error } = this.props;
if (error) { if (error) {
@@ -59,11 +88,7 @@ class Autosave extends React.Component {
} }
render() { render() {
const { const { writableCategoriesEnabled, lastSavedAnnoMatrix } = this.props;
writableCategoriesEnabled,
saveInProgress,
lastSavedAnnoMatrix,
} = this.props;
const initialDataLoadComplete = lastSavedAnnoMatrix; const initialDataLoadComplete = lastSavedAnnoMatrix;
if (!writableCategoriesEnabled) return null; if (!writableCategoriesEnabled) return null;
@@ -74,7 +99,7 @@ class Autosave extends React.Component {
data-testclass={ data-testclass={
!initialDataLoadComplete !initialDataLoadComplete
? "autosave-init" ? "autosave-init"
: this.needToSave() || saveInProgress : this.saveInProgress() || this.needToSave()
? "autosave-incomplete" ? "autosave-incomplete"
: "autosave-complete" : "autosave-complete"
} }