mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-03 09:18:12 +08:00
Ontologies (#1110)
* add sample ontologies file * add ontologies reducer * Move select category to own component * Dialog and Input factored out * refactoring categorical, partway * validationn * anno * suggest populates input * frontend for ontology working * initial implementation of back-end support for ontologies * edit is now dialog again * autosuggest working on edit * part way through create arbitrary label * handle choice in function * pass duplicate cat prop * editing works * update test to match new CLI params * fix occupancy alignment * edit category as dialogue * secondary button * remove stubbed out ontologies * add label setting upon new label creation * Update legal characters for labels (#1119) * Allow any term in the ontology (bypass legal name check) * Add hyphens and parens to legal characters in names * improve performance for large ontologies * correctly handle case where ontologies are disabled * fix logic error in CLI Co-authored-by: Bruce Martin <bruce@chanzuckerberg.com> * PR cleanup 1 * lint * validate user generated labels * finish hooking up connected suggest component * protect against undefined callbacks * Fix illegal characters error message * break out npm run commands * fix error detection on label edit Co-authored-by: Bruce Martin <bruce@chanzuckerberg.com> Co-authored-by: Sidney Bell <sidneymbell@users.noreply.github.com>
This commit is contained in:
co-authored by
Bruce Martin
Sidney Bell
parent
8d725b1ad9
commit
d48647a655
@@ -31,7 +31,6 @@ const Annotations = (
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case "configuration load complete": {
|
||||
const DefaultDataCollectionName = null;
|
||||
const dataCollectionName =
|
||||
action.config.parameters?.["annotations-data-collection-name"] ?? null;
|
||||
const dataCollectionNameIsReadOnly =
|
||||
|
||||
@@ -165,9 +165,16 @@ const CrossfilterReducerBase = (
|
||||
return state.delDimension(obsAnnoDimensionName(action.metadataField));
|
||||
}
|
||||
|
||||
case "annotation: add new label to category":
|
||||
case "annotation: label current cell selection":
|
||||
case "annotation: label edited":
|
||||
case "annotation: delete label": {
|
||||
if (
|
||||
action.type === "annotation: add new label to category" &&
|
||||
!action.assignSelectedCells
|
||||
)
|
||||
return state;
|
||||
|
||||
/* we need to reindex the dimension. For now, just drop it and add another */
|
||||
const name = action.metadataField;
|
||||
const dimName = obsAnnoDimensionName(name);
|
||||
|
||||
@@ -18,6 +18,7 @@ import controls from "./controls";
|
||||
import resetCache from "./resetCache";
|
||||
import annotations from "./annotations";
|
||||
import autosave from "./autosave";
|
||||
import ontology from "./ontology";
|
||||
import centroidLabels from "./centroidLabels";
|
||||
import pointDialation from "./pointDilation";
|
||||
|
||||
@@ -28,6 +29,7 @@ const Reducer = undoable(
|
||||
["config", config],
|
||||
["universe", universe],
|
||||
["world", world],
|
||||
["ontology", ontology],
|
||||
["annotations", annotations],
|
||||
["layoutChoice", layoutChoice],
|
||||
["categoricalSelection", categoricalSelection],
|
||||
@@ -45,15 +47,15 @@ const Reducer = undoable(
|
||||
]),
|
||||
[
|
||||
"universe",
|
||||
"categoricalSelection",
|
||||
"world",
|
||||
"categoricalSelection",
|
||||
"continuousSelection",
|
||||
"graphSelection",
|
||||
"crossfilter",
|
||||
"layoutChoice",
|
||||
"colors",
|
||||
"controls",
|
||||
"differential",
|
||||
"colors",
|
||||
"layoutChoice",
|
||||
"centroidLabels",
|
||||
"annotations"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// jshint esversion: 6
|
||||
const Ontology = (
|
||||
state = {
|
||||
enabled: false, // are ontology terms enabled?
|
||||
terms: null, // an array of term names, eg, ['cell', 'lung cell', ...]
|
||||
termSet: null, // a Set object containing all terms, for fast lookup
|
||||
loading: true
|
||||
},
|
||||
action
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case "configuration load complete":
|
||||
const enabled =
|
||||
action.config?.parameters?.annotations_cell_ontology_enabled ?? false;
|
||||
const terms = action.config?.parameters?.annotations_cell_ontology_terms;
|
||||
const termSet = new Set(terms);
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
enabled,
|
||||
terms,
|
||||
termSet
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default Ontology;
|
||||
@@ -136,13 +136,27 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
|
||||
"user annotations require a non-zero length string name"
|
||||
);
|
||||
|
||||
/* add the new label to the annotation */
|
||||
/* add the new label to the annotation schema */
|
||||
const schema = AnnotationsHelpers.addObsAnnoCategory(
|
||||
state.schema,
|
||||
annotationName,
|
||||
newLabelName
|
||||
);
|
||||
return { ...state, schema };
|
||||
|
||||
/* if so requested, label the current selection */
|
||||
const { world, crossfilter } = prevSharedState;
|
||||
const { metadataField, newLabelText } = action;
|
||||
const obsAnnotations = !action.assignSelectedCells
|
||||
? state.obsAnnotations
|
||||
: setLabelOnCurrentSelection(
|
||||
state,
|
||||
world,
|
||||
crossfilter,
|
||||
metadataField,
|
||||
newLabelText
|
||||
);
|
||||
|
||||
return { ...state, schema, obsAnnotations };
|
||||
}
|
||||
|
||||
case "annotation: label edited": {
|
||||
@@ -208,23 +222,11 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
|
||||
case "annotation: label current cell selection": {
|
||||
const { metadataField, label } = action;
|
||||
const { world, crossfilter } = prevSharedState;
|
||||
|
||||
/*
|
||||
selection state is relative to world. We need to convert it
|
||||
to a mask for Universe before applying it.
|
||||
*/
|
||||
const worldMask = crossfilter.allSelectedMask();
|
||||
const mask = World.worldEqUniverse(world, state)
|
||||
? worldMask
|
||||
: AnnotationsHelpers.worldToUniverseMask(
|
||||
worldMask,
|
||||
world.obsAnnotations,
|
||||
state.nObs
|
||||
);
|
||||
const obsAnnotations = AnnotationsHelpers.setLabelByMask(
|
||||
state.obsAnnotations,
|
||||
const obsAnnotations = setLabelOnCurrentSelection(
|
||||
state,
|
||||
world,
|
||||
crossfilter,
|
||||
metadataField,
|
||||
mask,
|
||||
label
|
||||
);
|
||||
return { ...state, obsAnnotations };
|
||||
@@ -236,4 +238,39 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
|
||||
}
|
||||
};
|
||||
|
||||
function setLabelOnCurrentSelection(
|
||||
universe,
|
||||
world,
|
||||
crossfilter,
|
||||
metadataField,
|
||||
label
|
||||
) {
|
||||
/*
|
||||
Set category `metadataField` to value `label` for anything currently selected.
|
||||
Used by several action type reducers.
|
||||
|
||||
Returns the new obsAnnotations dataframe.
|
||||
*/
|
||||
|
||||
/*
|
||||
selection state is relative to world. We need to convert it
|
||||
to a mask for Universe before applying it.
|
||||
*/
|
||||
const worldMask = crossfilter.allSelectedMask();
|
||||
const mask = World.worldEqUniverse(world, universe)
|
||||
? worldMask
|
||||
: AnnotationsHelpers.worldToUniverseMask(
|
||||
worldMask,
|
||||
world.obsAnnotations,
|
||||
universe.nObs
|
||||
);
|
||||
const obsAnnotations = AnnotationsHelpers.setLabelByMask(
|
||||
universe.obsAnnotations,
|
||||
metadataField,
|
||||
mask,
|
||||
label
|
||||
);
|
||||
return obsAnnotations;
|
||||
}
|
||||
|
||||
export default Universe;
|
||||
|
||||
@@ -196,6 +196,21 @@ const WorldReducer = (
|
||||
case "annotation: add new label to category": {
|
||||
/* add a new label to the schema - schema updated by universe reducer, we just need to note it */
|
||||
const { schema } = nextSharedState.universe;
|
||||
const { metadataField, newLabelText } = action;
|
||||
const { crossfilter } = prevSharedState;
|
||||
|
||||
if (action.assignSelectedCells) {
|
||||
return {
|
||||
...state,
|
||||
schema,
|
||||
...setLabelOnCurrentSelection(
|
||||
state,
|
||||
crossfilter,
|
||||
metadataField,
|
||||
newLabelText
|
||||
)
|
||||
};
|
||||
}
|
||||
return { ...state, schema };
|
||||
}
|
||||
|
||||
@@ -246,21 +261,10 @@ const WorldReducer = (
|
||||
case "annotation: label current cell selection": {
|
||||
const { metadataField, label } = action;
|
||||
const { crossfilter } = prevSharedState;
|
||||
const mask = crossfilter.allSelectedMask();
|
||||
const unclipped = {
|
||||
...state.unclipped,
|
||||
obsAnnotations: AnnotationsHelpers.setLabelByMask(
|
||||
state.unclipped.obsAnnotations,
|
||||
metadataField,
|
||||
mask,
|
||||
label
|
||||
)
|
||||
return {
|
||||
...state,
|
||||
...setLabelOnCurrentSelection(state, crossfilter, metadataField, label)
|
||||
};
|
||||
const obsAnnotations = state.obsAnnotations.replaceColData(
|
||||
metadataField,
|
||||
unclipped.obsAnnotations.col(metadataField).asArray()
|
||||
);
|
||||
return { ...state, obsAnnotations, unclipped };
|
||||
}
|
||||
|
||||
default: {
|
||||
@@ -269,4 +273,27 @@ const WorldReducer = (
|
||||
}
|
||||
};
|
||||
|
||||
function setLabelOnCurrentSelection(world, crossfilter, metadataField, label) {
|
||||
/*
|
||||
Set category `metadataField` to value `label` for anything currently selected.
|
||||
Used by several action type reducers.
|
||||
*/
|
||||
const mask = crossfilter.allSelectedMask();
|
||||
const unclipped = {
|
||||
...world.unclipped,
|
||||
obsAnnotations: AnnotationsHelpers.setLabelByMask(
|
||||
world.unclipped.obsAnnotations,
|
||||
metadataField,
|
||||
mask,
|
||||
label
|
||||
)
|
||||
};
|
||||
const obsAnnotations = world.obsAnnotations.replaceColData(
|
||||
metadataField,
|
||||
unclipped.obsAnnotations.col(metadataField).asArray()
|
||||
);
|
||||
|
||||
return { obsAnnotations, unclipped };
|
||||
}
|
||||
|
||||
export default WorldReducer;
|
||||
|
||||
Reference in New Issue
Block a user