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:
Colin Megill
2020-01-23 17:04:17 -05:00
committed by GitHub
co-authored by Bruce Martin Sidney Bell
parent 8d725b1ad9
commit d48647a655
20 changed files with 964 additions and 532 deletions
-1
View File
@@ -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 =
+7
View File
@@ -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);
+5 -3
View File
@@ -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"
],
+29
View File
@@ -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;
+55 -18
View File
@@ -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;
+41 -14
View File
@@ -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;