experimental re-embedding (#1186)

* first cut at re-embedding route and back-end support

* update and expand config route tests

* add scanpy_umap

* add reembedding to config route parameters

* front-end support for reembedding fetch and UI

* remove unused imports

* add loading state

* save reembedding in reducer state

* improve withColsFrom

* transmit reembed schema to client; pick unique embedding names

* display embeddings

* format

* lint

* spaces, tab size 2

* lint

* test hack for smoke-test race

* back out hack sleep

* add check for backed mode

* add unit test for reembedding

* lint

* hide re-embedding CLI param from help
This commit is contained in:
Bruce Martin
2020-03-09 16:53:30 -07:00
committed by GitHub
parent b3e9719602
commit 144b19c449
25 changed files with 928 additions and 159 deletions
+4 -1
View File
@@ -32,6 +32,7 @@ class AppConfig(object):
self.max_category_items = 100
self.diffexp_lfc_cutoff = 0.01
self.disable_diffexp = False
self.enable_reembedding = False
self.anndata_backed = False
# TODO these options may not apply to all datasets in the multi dataset.
@@ -56,6 +57,7 @@ class AppConfig(object):
"var_names",
"anndata_backed",
"disable_diffexp",
"enable_reembedding",
]
self.update(inputs, kw)
@@ -80,7 +82,7 @@ class AppConfig(object):
# we have camalCase, hyphen-text, and underscore_text
# features
features = [f.todict() for f in data_adaptor.get_features().values()]
features = [f.todict() for f in data_adaptor.get_features(annotation)]
# display_names
title = self.get_title(data_adaptor)
@@ -105,6 +107,7 @@ class AppConfig(object):
"diffexp_lfc_cutoff": self.diffexp_lfc_cutoff,
"backed": self.anndata_backed,
"disable-diffexp": self.disable_diffexp,
"enable-reembedding": self.enable_reembedding,
"annotations": False,
"annotations_file": None,
"annotations_output_dir": None,
+31 -1
View File
@@ -165,7 +165,7 @@ def diffexp_obs_post(request, data_adaptor):
try:
diffexp = data_adaptor.diffexp_topN(set1_filter, set2_filter, count)
return make_response(diffexp, HTTPStatus.OK, {"Content-Type": "application/json"})
except (ValueError, FilterError) as e:
except (ValueError, DisabledFeatureError, FilterError) as e:
return make_response(str(e), HTTPStatus.BAD_REQUEST)
except JSONEncodingValueError as e:
# JSON encoding failure, usually due to bad data
@@ -188,3 +188,33 @@ def layout_obs_get(request, data_adaptor):
return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR)
except ValueError as e:
return make_response(str(e), HTTPStatus.INTERNAL_SERVER_ERROR)
def layout_obs_put(request, data_adaptor):
preferred_mimetype = request.accept_mimetypes.best_match(["application/octet-stream"])
if preferred_mimetype != "application/octet-stream":
return make_response(f"Unsupported MIME type '{request.accept_mimetypes}'", HTTPStatus.NOT_ACCEPTABLE)
if not data_adaptor.config.enable_reembedding:
return make_response(f"Computed embedding not supported.", HTTPStatus.BAD_REQUEST)
args = request.get_json()
filter = args["filter"] if args else None
if not filter:
return make_response("Error: obs filter is required", HTTPStatus.BAD_REQUEST)
method = args["method"] if args else "umap"
try:
schema, fbs = data_adaptor.compute_embedding(method, filter)
return make_response(
fbs,
HTTPStatus.OK,
{
"Content-Type": "application/octet-stream",
"CxG-Schema": json.dumps(schema),
"Access-Control-Expose-Headers": "CxG-Schema",
},
)
except NotImplementedError as e:
return make_response(str(e), HTTPStatus.NOT_IMPLEMENTED)
except (ValueError, DisabledFeatureError, FilterError) as e:
return make_response(str(e), HTTPStatus.BAD_REQUEST)