mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-25 02:18:12 +08:00
add autosave
This commit is contained in:
@@ -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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,14 +52,21 @@ async function userInfoFetch(dispatch) {
|
||||
});
|
||||
}
|
||||
|
||||
async function genesetsFetch(dispatch) {
|
||||
return fetchJson("genesets").then((response) => {
|
||||
const genesets = response?.genesets ?? {};
|
||||
async function genesetsFetch(dispatch, config) {
|
||||
if (config?.parameters?.["annotations_genesets"] ?? false) {
|
||||
fetchJson("genesets").then((response) => {
|
||||
const genesets = response?.genesets ?? {};
|
||||
dispatch({
|
||||
type: "geneset: initial load",
|
||||
init: genesets,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
dispatch({
|
||||
type: "geneset: initial load",
|
||||
init: genesets,
|
||||
init: [],
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function prefetchEmbeddings(annoMatrix) {
|
||||
@@ -84,9 +91,10 @@ const doInitialDataLoad = () =>
|
||||
schemaFetch(dispatch),
|
||||
userColorsFetchAndLoad(dispatch),
|
||||
userInfoFetch(dispatch),
|
||||
genesetsFetch(dispatch),
|
||||
]);
|
||||
|
||||
genesetsFetch(dispatch, config);
|
||||
|
||||
const baseDataUrl = `${globals.API.prefix}${globals.API.version}`;
|
||||
const annoMatrix = new AnnoMatrixLoader(baseDataUrl, schema.schema);
|
||||
const obsCrossfilter = new AnnoMatrixObsCrossfilter(annoMatrix);
|
||||
@@ -253,6 +261,7 @@ export default {
|
||||
annotationRenameLabelInCategory: annoActions.annotationRenameLabelInCategory,
|
||||
annotationLabelCurrentSelection: annoActions.annotationLabelCurrentSelection,
|
||||
saveObsAnnotationsAction: annoActions.saveObsAnnotationsAction,
|
||||
saveGenesetsAction: annoActions.saveGenesetsAction,
|
||||
needToSaveObsAnnotations: annoActions.needToSaveObsAnnotations,
|
||||
layoutChoiceAction: embActions.layoutChoiceAction,
|
||||
setCellSetFromSelection: selnActions.setCellSetFromSelection,
|
||||
|
||||
@@ -17,6 +17,9 @@ import {
|
||||
auth: state.config?.authentication,
|
||||
userInfo: state.userInfo,
|
||||
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
|
||||
writableGenesetsEnabled: !(
|
||||
state.config?.parameters?.["annotations_genesets_readonly"] ?? true
|
||||
),
|
||||
}))
|
||||
class FilenameDialog extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -101,7 +104,7 @@ class FilenameDialog extends React.Component {
|
||||
} = this.props;
|
||||
const { filenameText } = this.state;
|
||||
|
||||
return writableCategoriesEnabled &&
|
||||
return (writableCategoriesEnabled || writableGenesetsEnabled) &&
|
||||
annotations.promptForFilename &&
|
||||
!annotations.dataCollectionNameIsReadOnly &&
|
||||
!annotations.dataCollectionName &&
|
||||
|
||||
@@ -5,11 +5,18 @@ import FilenameDialog from "./filenameDialog";
|
||||
|
||||
@connect((state) => ({
|
||||
annotations: state.annotations,
|
||||
saveInProgress: state.autosave?.saveInProgress ?? false,
|
||||
obsAnnotationSaveInProgress:
|
||||
state.autosave?.obsAnnotationSaveInProgress ?? false,
|
||||
genesetSaveInProgress: state.autosave?.genesetSaveInProgress ?? false,
|
||||
error: state.autosave?.error,
|
||||
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
|
||||
writableGenesetsEnabled: !(
|
||||
state.config?.parameters?.annotations_genesets_readonly ?? true
|
||||
),
|
||||
annoMatrix: state.annoMatrix,
|
||||
genesets: state.genesets,
|
||||
lastSavedAnnoMatrix: state.autosave?.lastSavedAnnoMatrix,
|
||||
lastSavedGenesets: state.autosave?.lastSavedGenesets,
|
||||
}))
|
||||
class Autosave extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -38,18 +45,40 @@ class Autosave extends React.Component {
|
||||
}
|
||||
|
||||
tick = () => {
|
||||
const { dispatch, saveInProgress } = this.props;
|
||||
if (this.needToSave() && !saveInProgress) {
|
||||
const {
|
||||
dispatch,
|
||||
obsAnnotationSaveInProgress,
|
||||
genesetSaveInProgress,
|
||||
} = this.props;
|
||||
if (!obsAnnotationSaveInProgress && this.needToSaveObsAnnotations()) {
|
||||
dispatch(actions.saveObsAnnotationsAction());
|
||||
}
|
||||
if (!genesetSaveInProgress && this.needToSaveGenesets()) {
|
||||
dispatch(actions.saveGenesetsAction());
|
||||
}
|
||||
};
|
||||
|
||||
needToSave = () => {
|
||||
/* return true if we need to save, false if we don't */
|
||||
saveInProgress() {
|
||||
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;
|
||||
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() {
|
||||
const { error } = this.props;
|
||||
if (error) {
|
||||
@@ -59,11 +88,7 @@ class Autosave extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
writableCategoriesEnabled,
|
||||
saveInProgress,
|
||||
lastSavedAnnoMatrix,
|
||||
} = this.props;
|
||||
const { writableCategoriesEnabled, lastSavedAnnoMatrix } = this.props;
|
||||
const initialDataLoadComplete = lastSavedAnnoMatrix;
|
||||
|
||||
if (!writableCategoriesEnabled) return null;
|
||||
@@ -74,7 +99,7 @@ class Autosave extends React.Component {
|
||||
data-testclass={
|
||||
!initialDataLoadComplete
|
||||
? "autosave-init"
|
||||
: this.needToSave() || saveInProgress
|
||||
: this.saveInProgress() || this.needToSave()
|
||||
? "autosave-incomplete"
|
||||
: "autosave-complete"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user